25.29. To say one of

| Chapter 25: Extensions |

| 25.29. To say one of

Many of the invocation syntaxes described in the previous section are used in the definition by the Standard Rules of the "[one of] ... [or] ... [purely at random]" construction, so it makes a good example of how they can be used.

First, this is a segmented substitution with a single possible beginning ("[one of]"), a single possible middle ("[or]") but a choice of many possible endings. Almost everything is compiled by the invocation of the beginning:

> To say one of -- beginning say_one_of:
> (- {-allocate-storage:say_one_of}I7_ST_say_one_of-->{-counter:say_one_of}
> = {-final-segment-marker}(I7_ST_say_one_of-->{-counter:say_one_of}, {-segment-count});
> switch((I7_ST_say_one_of-->{-advance-counter:say_one_of})%({-segment-count}+1) - 1) {
> 0: -).
> To say or -- continuing say_one_of:
> (- {-segment-count}: -).
> To say purely at random -- ending say_one_of with marker I7_SOO_PAR:
> (- {-close-brace} -).

The 3rd invocation of this (say) might compile the following:

> I7_ST_say_one_of-->2 = I7_SOO_PAR(I7_ST_say_one_of-->2, 4);
> switch((I7_ST_say_one_of-->2)%5 - 1) {
> 0: ... first text ...
> 1: ... second text ...
> 2: ... third text ...
> 3: ... fourth text ...
> }

First, we notified Inform that it needs to allocate an array (I7_ST_say_one_of) providing storage associated with the counter "say_one_of". This we used to count off individual invocations of "[one of]", so that each would have its own word of storage - for the 3rd invocation, I7_ST_say_one_of-->2. We then call a state-changing routine, in this case I7_SOO_PAR, which is allowed to know the previous state and also the number of options available, and which returns the new state. The state is supposed to be the option chosen last time, but that means that there are not 4, but 5 possibilities: 0 for "there was no last time", then 1 to 4 for the possible outcomes. We reduce the state mod 5 to obtain the decision this time, and subtract 1 because it happens to be convenient to make the switch statement run from 0 to 3 rather than 1 to 4. (The reason we reduce the state mod 5 is to allow the state-changer to squirrel away secret information in the upper bits of the state, if it wants to. Note that subtracting one means that the switch value might be -1, which results in no text being printed: thus if the state-changer chooses 0, it can decide on none of the above.)

In this design, the marker attached to the choice of ending substitution is the name of the I6 state-changer: here is the I7_SOO_PAR routine.

> [ I7_SOO_PAR oldval count; if (count <= 1) return count; return random(count); ];

As it happens, this ignores the old value: after all, it is meant to be purely at random, and nothing could be less pure than taking the last outcome into consideration when choosing the next.

Note that the counter say_one_of is advanced in invocation of the head. It might seem that the tidier design, somehow, would be to advance the counter in the invocation of the tails, but this is not a good idea. In general it is not safe to assume that the counter will have the same value when the tail is invoked that it had when the head was invoked, because segmented say constructions can legally be nested in Inform strings. Because of this, it is best to deal with a counter entirely in a single invocation, either of the beginning or the ending.

Because "[one of] ... [or] ..." is such a useful construction - switching between alternative forms of text, which writers of IF very often do - the above implementation is intentionally left open for new endings to be added, and the examples below show how easily this can be done.

444
| Example Blink Making a "by atmosphere" token, allowing us to design our own text variations such as "[one of]normal[or]gloomy[or]scary[by atmosphere]". |
RB

445
| Example Uncommon Ground Making a "by viewpoint" token, allowing us to design our own text variations such as "[show to yourself]quaint[to Lolita]thrilling[to everyone else]squalid[end show]" depending on the identity of the player at the moment. |
RB

A slightly more challenging case than the "by atmosphere" example is one in which we want to create text variations depending on the identity of our player character. What we want to do is build a switch statement in I6, one that looks something like switch(player) { yourself: print "quaint"; Lolita: print "thrilling"; default: print "squalid"; } out of I7 that looks like this: say "[show to yourself]quaint[to Lolita]thrilling[to everyone else]squalid[end show]". "Uncommon Ground" The Mud Village is a room. "You stand at the center of a [show to yourself]quaint[to Lolita]thrilling[to everyone else]squalid[end show] mud village." Leforge is a man in the Mud Village. Lolita is a woman in the Mud Village. Instead of waiting: if the player is Lolita, now the player is Leforge; if the player is yourself, now the player is Lolita; say "You jump bodies. Whoops!" To say show to (N - a person) -- beginning say_seen_by: (- switch(player) {-open-brace} {N}: -). To say to (N - a person) -- continuing say_seen_by: (- {N}: -). To say to everyone else -- continuing say_seen_by: (- default: -) To say end show -- ending say_seen_by: (- {-close-brace} -) Test me with "look / z / look / z / look".

Previous | Contents | Next