Inform 7 Home Page / Documentation
§7.12. Characters Following a Script
So far we've seen characters who will answer questions whenever the player feels like asking, and characters who will use some reasoning procedure to direct the conversation. There is a third option, often useful in IF with a fast-paced narrative: the character follows a conversational script, making sure to cover a series of points before the scene ends.
There are more and less tedious ways to implement this kind of scene. The worst case is one in which the player is not allowed to interrupt or ask any questions; he must merely wait until the character runs out of things to say. This can be useful and plausible in very small doses - say, two or three turns - but if the character has more information than that to impart, we may want to make the scene more interactive.
Pine 2 partly addresses this challenge: the character has a line of conversation that she wants to follow to its conclusion; we may ask questions along the way, but if we're silent, she'll take up the slack, and the scene won't end until she's done with what she has to say.
Another kind of script is a series of actions for the character to perform. Robo demonstrates a programmable robot that will observe what the player does, then try to emulate the actions later when switched into play-back mode. Robo 2 extends this capacity to allow the robot to contain fifteen different scripts which the player can store, list, run, and erase.
Your Mother Doesn't Work Here offers a character with a list of tasks but whose plans can be interrupted by more urgent demands. This verges on not being a simple script any more: if we carry the idea to its natural conclusion, we get characters capable of planning scripts for themselves to accomplish their aims. This is conventionally called "goal-seeking".
See Goal-Seeking Characters for characters that work out plans for themselves in order to accomplish various outcomes
Start of Chapter 7: Other Characters | |
Back to §7.11. Character Knowledge and Reasoning | |
Onward to §7.13. Traveling Characters |
ExampleRobo 1 |
The Experimentation Chamber is a room. Robo is a man in the Experimentation Chamber. "Robo, your prototype tin companion, stands awkwardly beside you. In the middle of his chest is a red enamel button[if the red button is switched on], currently depressed[otherwise], currently un-depressed[end if]."
The red button is a device. It is part of Robo. Instead of pushing the red button: if the red button is switched off, try switching on the red button; otherwise try switching off the red button.
After switching on the red button:
say "CLICK! Robo is now in play-back mode."
After switching off the red button:
say "CLACK! Robo is now in observation mode."
Definition: Robo is watching if the red button is switched off.
The current instruction set is a list of stored actions that varies.
After doing something when Robo is watching and Robo can see the player:
now the actor is Robo;
add the current action to the current instruction set;
now the actor is the player;
say "Robo watches you [the current action][one of], his yellow eyes lamp-like and observant[or]. In his metal head, gears whirr[or], his brushed-copper handlebar moustaches twitching[or] impassively[at random].";
continue the action.
Every turn when Robo is not watching:
if the number of entries in the current instruction set is 0:
say "Robo has run out of behavior and grinds to an (expectant) halt.";
now the red button is switched off;
otherwise:
let the next task be entry 1 of the current instruction set;
try the next task;
remove entry 1 from the current instruction set.
The red block and the blue cylinder are things in the Experimentation Chamber. The counter is a supporter in the Experimentation Chamber. The counter is scenery.
Report Robo examining Robo:
say "Robo examines each of his hands in turn, then each of his legs (bending over mostly double in the middle to do this)." instead.
Report Robo examining the player:
say "Robo stares at you, unblinkingly, for several seconds together[if a random chance of 1 in 7 succeeds]. His left moustache-bar twitches infinitesimally upward[end if]." instead.
Report Robo taking the cylinder:
say "[one of][Robo] needs several attempts to get his metal fingers around [the cylinder] -- they are not designed for grasping small objects elegantly. But at last he succeeds[or]Once again, Robo struggles a bit before picking up [the cylinder][stopping]." instead.
Test me with "z / take cylinder / take block / put cylinder on counter / put block on counter / x robo / x me / get block / drop block / press red button / z / z / z / z / z / z / z / z / z / z".
ExampleYour Mother Doesn't Work Here |
Suppose the player's mother is supposed to be cleaning the living room, but she can be interrupted by the need to pick up things the player has dropped. New tasks are added to the end of her "current plan" list; every turn, she attempts to do whatever is the last entry on that list.
A person has a list of stored actions called the current plan.
Every turn:
repeat with culprit running through people who are not the player:
if the number of entries in current plan of the culprit is greater than 0:
let N be the number of entries in the current plan of the culprit;
try entry N of the current plan of the culprit;
remove entry N from the current plan of the culprit.
The Living Room is a room. It contains a somewhat muddy Persian rug. Your mother is a woman in the Living Room.
Instead of your mother rubbing the rug:
say "Your mother scrubs the stained areas of the rug, muttering to herself."
Report your mother taking something:
say "Your mother picks up [the noun][one of], sighing deeply[or], jaw tight[or], with assorted comments on your manners[or]; to judge from her comments, she is also indulging in a pleasant fantasy about Swiss boarding schools[stopping]." instead.
When play begins:
add mother going west to the current plan of mother;
add mother rubbing the rug to the current plan of mother.
Every turn:
if mother is not in the Living Room, end the story finally.
Carry out dropping something:
add mother taking the noun to the current plan of mother.
The player carries some dirty socks, some dirty shoes, a dirty hat, a pair of dirty trousers, and a backpack.
Test me with "drop socks / z / drop shoes / drop hat / drop all / z / z".
As goal-seeking goes, this is fairly rudimentary; "Boston Cream" provides an alternative (and slightly more sophisticated approach), but for really complex goal-seeking characters, it is probably best to turn to the character extensions designed for Inform.
ExamplePine 2 |
A person can be asleep or awake. A person can be active or passive.
The Spinning Tower is a room. "A remote corner of the old castle, reserved for spinning and weaving tasks."
Sleeping Beauty is an asleep woman in the Spinning Tower. "[if asleep]Sleeping Beauty lies here, oblivious to your presence[otherwise]Sleeping Beauty stands beside you, looking a little confused[end if]." The description is "She is even more magnificent than the rumors suggested." Understand "woman" or "girl" or "princess" or "lady" as Sleeping Beauty.
Discovery is a scene. Discovery begins when play begins. Discovery ends when Sleeping Beauty is awake. Marriage Proposal is a scene. Marriage Proposal begins when Discovery ends.
When Discovery ends: say "Throughout the palace you can hear the other sounds of stirring and movement as the spell of centuries is broken."
Instead of attacking an asleep person:
now the noun is awake;
say "[The noun] sits bolt upright. 'Hey! Ow!' So much for that true love's kiss nonsense."
Instead of kissing an asleep person:
now the noun is awake;
say "[The noun] slowly stirs to wakefulness!"
Instead of throwing water at an asleep person:
now the second noun is awake;
now the noun is nowhere;
say "You pour out [the noun] on [the second noun].
[The second noun] wakes, shuddering. 'Agh! I had a terrible dream about drowning and then-- Hey!'"
The player carries a jug of water. Understand "pour [something] on [something]" or "splash [something] at/on [something]" as throwing it at.
So much, we had before. Now, suppose we want a conversation style which allows the player to move conversation forward by asking appropriate questions, but which will keep moving forward even if he doesn't. To this end, we provide a table -- a borrowing from a later chapter. In the table, we record two ways of performing each conversation bit, one which reflects the player's participation, and one in which the character moves things onward:
topic |
reply |
quip |
"dream/dreams/nightmare/nightmares/sleep" |
"'Sleep well?' you ask solicitously. |
'Not really,' she replies, edging away from you. So much for that angle." |
"'Ghastly nightmares,' she remarks. You nod politely." |
"marriage/love/wedding/boyfriend/beau/lover" |
"'So,' you say. 'This is a little weird since we just met, but, um. Would you like to get married?' |
She looks at you nervously. 'Do I have to?'" |
"'I, er,' she says. 'I hope I'm not supposed to marry you or something.'" |
"marriage/love/wedding/boyfriend/beau/lover" |
"'I was told I was going to marry you and inherit the kingdom,' you say, apologetically. 'Would that be very bad?' |
'Oh, it's not you -- I'm seeing someone,' she says, smiling quickly.
You try to think how to point out that it's been a hundred years since she last saw her boyfriend." |
"'Do you think I could go look for someone? I'm seeing him, you see, and I think I've been... sick... for a while, so he might be worried.' |
You try to think how to point out that it's been a hundred years since she last saw her boyfriend."
"marriage/love/wedding/boyfriend/beau/lover" |
"'You've been up here for a hundred years,' you say. An unpleasant thought occurs to you. 'Was your young man in the castle somewhere?' |
She shakes her head mutely." |
"She goes to the window and looks out at the now-fading thicket of briar. 'That took a while to grow,' she observes. 'I've been up here longer than I thought.' |
Instead of asking an awake beauty about a topic listed in the Table of Conversation:
now Beauty is passive;
say "[reply entry][paragraph break]";
blank out the whole row.
The "now Beauty is passive" line prevents her from making any conversation of her own on a turn when we've spoken to her. This keeps the conversation from progressing too quickly.
Instead of telling an awake beauty about something: try asking the noun about it.
Instead of asking an asleep person about something:
say "[The noun] snores."
Marriage Proposal ends when the number of filled rows in the Table of Conversation is 0.
Every turn during Marriage Proposal:
if Beauty is active:
repeat through Table of Conversation:
say "[quip entry][paragraph break]";
blank out the whole row;
make no decision.
After we've generated any spontaneous conversation, we return her to her regular active state.
When Marriage Proposal ends: end the story saying "This is going to take some explaining."
Test me with "x beauty / wake beauty / pour water on beauty / ask beauty about sleep / z / ask beauty about marriage".
Now we have a scenario in which the player can ask her some questions out of order if he really wants to, but the scene will not end until the basic conversation topics have been exhausted. If we wanted to add some other chit-chat, not as part of the main conversation strand, but by way of optional enrichment, we might make a second conversation table and record alternative outcomes in it.
ExampleRobo 2 |
We have seen how we can make a robot that watches the player, then plays back the same actions again. A slightly more adventurous implementation would be to let the player create a whole series of named scripts which the robot will run on command.
To do this, we'll need each program to have a command that sets it off (stored as text, since this is the best way to capture and preserve arbitrary text entered by the player) and then the script of actions that must result:
The hard drive is a container. A program is a kind of thing. 15 programs are in hard drive. A program has some text called the starter command. A program has a list of stored actions called the script. Understand the starter command property as describing a program.
Rule for printing the name of a program (called the target) which is not blank:
say "[starter command of the target in upper case]".
Definition: a program is blank if the number of entries in its script is 0.
The current instruction name is some text that varies. The current instruction set is a list of stored actions that varies.
Now, we want to let Robo learn new programs; for this purpose, we'll emulate the code from our previous implementation, so that Robo watches what the player does and stores those actions in his script:
Understand "learn [text]" as learning. Learning is an action applying to one topic.
Check learning:
say "You have already learned all you need to know. Robo, however, remains to be trained." instead.
Check Robo learning:
if Robo is watching, say "Robo is already recording '[the current instruction name]'." instead.
Carry out Robo learning:
truncate the current instruction set to 0 entries;
now the current instruction name is the topic understood;
now Robo is watching.
Report Robo learning:
say "'Learning [the current instruction name in upper case],' Robo replies."
After doing something when Robo is watching and Robo can see the player:
now the actor is Robo;
add the current action to the current instruction set;
now the actor is the player;
say "Robo watches you [the current action][one of], his yellow eyes lamp-like and observant[or]. In his metal head, gears whirr[or], his brushed-copper handlebar moustaches twitching[or] impassively[at random].";
continue the action.
Of course, we also need to be able to switch learning mode off, and store any script learned this way. We'll also use the same STOP command to make Robo terminate a program he's in the middle of running.
Understand "stop" as stopping. Stopping is an action applying to nothing.
Check stopping:
say "The command is useful only for Robo." instead.
Check Robo stopping:
if Robo is standing by, stop the action.
Carry out Robo stopping when Robo is watching:
let N be a random blank program;
if N is a program:
now the starter command of N is the current instruction name;
now the script of N is the current instruction set;
say "'Stored [current instruction name in upper case].'";
otherwise:
say "FAILURE: no program slots remaining."
Next, we need to be able to play these programs back again. We'll give Robo a "current program" to store which program he's currently working on, and a number called "stage" which will record where he is in the script. Our previous implementation simply had Robo erase entries from his script list as he performed them, but this time we would like Robo to be able to remember and rerun the same scripts over and over, so we need something a little more subtle.
Understand "run [any program]" as running. Running is an action applying to one visible thing.
Check running:
say "Only Robo can perform Robo's programs." instead.
Check Robo running:
if Robo is not standing by, stop the action.
Unsuccessful attempt by Robo running:
say "'ERROR: Robo can launch new programs only when on standby.'"
Carry out Robo running:
now the current program of Robo is the noun;
now the stage of Robo is 1;
now Robo is replaying.
Report Robo running:
say "'Running [the starter command in upper case],' Robo confirms."
Every turn when Robo is replaying:
let the chosen script be the script of the current program of Robo;
let maximum be the number of entries in the chosen script;
let N be the stage of Robo;
let the next step be entry N of the chosen script;
try the next step;
increment the stage of Robo;
if the stage of Robo is greater than the maximum:
say "Robo's program ends, and he reverts to stand-by mode.";
now Robo is standing by;
now the stage of Robo is 1.
For the player's sanity, we should also provide a way to find out which programs Robo has stored in memory and what they do, so we design two listing commands:
Understand "list programs" as requesting program list. Requesting program list is an action applying to nothing.
Check requesting program list:
say "You will have to ask Robo to list programs." instead.
Carry out Robo requesting program list:
say "'The available program[if more than one program is not blank]s[end if] [is-are list of programs which are not blank].'".
Understand "describe [any program]" or "list [any program]" as requesting script of. Requesting script of is an action applying to one visible thing.
Check requesting script of:
say "You will have to ask Robo to give you the script." instead.
Carry out Robo requesting script of:
say "The script of [noun] is: [script of the noun]."
And to complete the suite, in case the player runs into Robo's fifteen-program limit:
Understand "delete [any program]" as deleting. Deleting is an action applying to one visible thing. Understand the command "erase" as "delete".
Check deleting:
say "You will have to instruct Robo to delete [the noun]." instead.
Check Robo deleting (this is the can't delete except in standby rule):
if Robo is not standing by, stop the action.
Unsuccessful attempt by Robo deleting:
say "'ERROR: programs may only be deleted while Robo is in stand-by mode.'" instead.
Carry out Robo deleting:
truncate the script of the noun to 0 entries;
now the starter command of the noun is "".
Now we use pretty much the same set-up as before to test Robo's abilities:
Robo is a man in the Experimentation Chamber. "Robo, your prototype tin companion, stands awkwardly beside you[if watching], watching[end if]." Robo can be watching, replaying, or standing by. Robo is standing by. Robo has a program called the current program. Robo has a number called the stage.
The red block and the blue cylinder are things in the Experimentation Chamber. The counter is a supporter in the Experimentation Chamber. The counter is scenery.
Report Robo examining Robo:
say "Robo examines each of his hands in turn, then each of his legs (bending over mostly double in the middle to do this)." instead.
Report Robo examining the player:
say "Robo stares at you, unblinkingly, for several seconds together[if a random chance of 1 in 7 succeeds]. His left moustache-bar twitches infinitesimally upward[end if]." instead.
Report Robo taking the cylinder:
say "[one of][Robo] needs several attempts to get his metal fingers around [the cylinder] -- they are not designed for grasping small objects elegantly. But at last he succeeds[or]Once again, Robo struggles a bit before picking up [the cylinder][stopping]." instead.
Test chocolate with "learn chocolate / stop / list programs / Robo, learn chocolate / take red / put all on counter / Robo, stop / Robo, list programs / Robo, run chocolate / z / Robo, run chocolate / Robo, stop / z".
Test vanilla with "Robo, learn vanilla / take all / i / drop all / x robo / x me / Robo, stop / Robo, list programs / Robo, list vanilla / Robo, run vanilla / z / z / robo, delete vanilla / robo, stop / robo, list vanilla / robo, delete vanilla / robo, list programs".
We could also have written this so that Robo learns new scripts by accepting the player's instructions, so that
>ROBO, LEARN LIBRARY THEFT
>ROBO, TAKE BOOK
>ROBO, EAST
>ROBO, STOP
...would store the script 'library theft' with the actions taking the book and going east. The implementation there would have been mostly identical, except that instead of an "after doing something..." rule, we would have captured commands as we asked Robo to perform them, and added those to the command list in progress. The alternative code might look something like this:
Before Robo doing something other than stopping when Robo is watching:
add the current action to the current instruction set;
say "'CHECK: [current action] added to script,' says Robo." instead.
Unsuccessful attempt by Robo doing something when Robo is watching:
say "He does not actually perform the action."