I6 Template Layer

Inform 7 6M62ContentsIntroductionFunction IndexRules Index

StoredAction.i6t

StoredAction contents

Block Format.

The short block of a stored action is simply a pointer to a long block. The long block always has a length of 6 words.

An action which involves a topic – such as the one produced by the command LOOK UP JIM MCDIVITT IN ENCYCLOPAEDIA – cannot be tried without the text of that topic (JIM MCDIVITT) being available. That's no problem if the action is tried in the same turn in which it is generated, because the text will still be in the command buffer. But once we store actions up for future use it becomes an issue. So when we store an action involving a topic, we record the actual text typed at the time when it is stored, and this goes into array entry 5 of the block. Because that in turn is text, and therefore a block value on the heap in its own right, we have to be a little more careful about destroying and copying stored actions than we otherwise would be.

Note that entries 1 and 2 are values whose kind depends on the action in entry 0: but they are never block values, because actions are not allowed to apply to block values. This simplifies matters considerably.

28Constant STORA_ACTION_F = 0; 29Constant STORA_NOUN_F = 1; 30Constant STORA_SECOND_F = 2; 31Constant STORA_ACTOR_F = 3; 32Constant STORA_REQUEST_F = 4; 33Constant STORA_COMMAND_TEXT_F = 5;

KOV Support.

See the BlockValues.i6t segment for the specification of the following routines.

40[ STORED_ACTION_TY_Support task arg1 arg2 arg3; 41    switch(task) { 42        CREATE_KOVS: return STORED_ACTION_TY_Create(arg2); 43        DESTROY_KOVS: STORED_ACTION_TY_Destroy(arg1); 44        MAKEMUTABLE_KOVS: return 1; 45        COPYQUICK_KOVS: rtrue; 46        COPYSB_KOVS: BlkValueCopySB1(arg1, arg2); 47        KINDDATA_KOVS: return 0; 48        EXTENT_KOVS: return 6; 49        COPY_KOVS: STORED_ACTION_TY_Copy(arg1, arg2); 50        COMPARE_KOVS: return STORED_ACTION_TY_Compare(arg1, arg2); 51        HASH_KOVS: return STORED_ACTION_TY_Hash(arg1); 52        DEBUG_KOVS: print " = ", (STORED_ACTION_TY_Say) arg1; 53    } 54    ! We choose not to respond to: CAST_KOVS, COPYKIND_KOVS, READ_FILE_KOVS, WRITE_FILE_KOVS 55    rfalse; 56];

Creation.

A stored action block has fixed size, so this is a single-block KOV: its data consists of six words, laid out as shown in the following routine. Note that it initialises to the default value for this KOV, an action in which the player waits.

65[ STORED_ACTION_TY_Create sb stora; 66    stora = FlexAllocate(6*WORDSIZE, STORED_ACTION_TY, BLK_FLAG_WORD); 67    BlkValueWrite(stora, STORA_ACTION_F, ##Wait, true); ! action 68    BlkValueWrite(stora, STORA_NOUN_F, 0, true); ! noun 69    BlkValueWrite(stora, STORA_SECOND_F, 0, true); ! second 70    BlkValueWrite(stora, STORA_ACTOR_F, player, true); ! actor 71    BlkValueWrite(stora, STORA_REQUEST_F, false, true); ! whether a request 72    BlkValueWrite(stora, STORA_COMMAND_TEXT_F, 0, true); ! text of command if necessary, 0 if not 73    return BlkValueCreateSB1(sb, stora); 74];

Setting Up.

In practice it's convenient for NI to have a routine which creates a stored action with a given slate of action variables, rather than have to set them all one at a time, so the following is provided as a shorthand form.

82[ STORED_ACTION_TY_New a n s ac req stora; 83    if (stora == 0) stora = BlkValueCreate(STORED_ACTION_TY); 84    BlkValueWrite(stora, STORA_ACTION_F, a); 85    BlkValueWrite(stora, STORA_NOUN_F, n); 86    BlkValueWrite(stora, STORA_SECOND_F, s); 87    BlkValueWrite(stora, STORA_ACTOR_F, ac); 88    BlkValueWrite(stora, STORA_REQUEST_F, req); 89    BlkValueWrite(stora, STORA_COMMAND_TEXT_F, 0); 90    return stora; 91];

Destruction.

Entries 0 to 4 are forgettable non-block values: only the optional text requires destruction.

98[ STORED_ACTION_TY_Destroy stora toc; 99    toc = BlkValueRead(stora, STORA_COMMAND_TEXT_F); 100    if (toc) BlkValueFree(toc); 101];

Copying.

The only entry needing attention is, again, entry 5: if this is non-zero in the source, then we need to create a new text block to hold a duplicate copy of the text.

109[ STORED_ACTION_TY_Copy storato storafrom tocfrom tocto; 110    tocfrom = BlkValueRead(storafrom, STORA_COMMAND_TEXT_F); 111    if (tocfrom == 0) return; 112    tocto = BlkValueCreate(TEXT_TY); 113    BlkValueCopy(tocto, tocfrom); 114    BlkValueWrite(storato, STORA_COMMAND_TEXT_F, tocto); 115];

Comparison.

There is no very convincing ordering on stored actions, but we need to devise a comparison which will exhaustively determine whether two actions are or are not different.

123[ STORED_ACTION_TY_Compare storaleft storaright delta itleft itright; 124    delta = BlkValueRead(storaleft, STORA_ACTION_F) - BlkValueRead(storaright, STORA_ACTION_F); 125    if (delta) return delta; 126    delta = BlkValueRead(storaleft, STORA_NOUN_F) - BlkValueRead(storaright, STORA_NOUN_F); 127    if (delta) return delta; 128    delta = BlkValueRead(storaleft, STORA_SECOND_F) - BlkValueRead(storaright, STORA_SECOND_F); 129    if (delta) return delta; 130    delta = BlkValueRead(storaleft, STORA_ACTOR_F) - BlkValueRead(storaright, STORA_ACTOR_F); 131    if (delta) return delta; 132    delta = BlkValueRead(storaleft, STORA_REQUEST_F) - BlkValueRead(storaright, STORA_REQUEST_F); 133    if (delta) return delta; 134    itleft = BlkValueRead(storaleft, STORA_COMMAND_TEXT_F); 135    itright = BlkValueRead(storaright, STORA_COMMAND_TEXT_F); 136    if ((itleft ~= 0) && (itright ~= 0)) 137        return TEXT_TY_Support(COMPARE_KOVS, itleft, itright); 138    return itleft - itright; 139]; 140 141[ STORED_ACTION_TY_Distinguish stora1 stora2; 142    if (STORED_ACTION_TY_Compare(stora1, stora2) == 0) rfalse; 143    rtrue; 144];

Hashing.

149[ STORED_ACTION_TY_Hash stora rv it; 150    rv = BlkValueRead(stora, STORA_ACTION_F); 151    rv = rv * 33 + BlkValueRead(stora, STORA_NOUN_F); 152    rv = rv * 33 + BlkValueRead(stora, STORA_SECOND_F); 153    rv = rv * 33 + BlkValueRead(stora, STORA_ACTOR_F); 154    rv = rv * 33 + BlkValueRead(stora, STORA_REQUEST_F); 155    it = BlkValueRead(stora, STORA_COMMAND_TEXT_F); 156    if (it ~= 0) 157        rv = rv * 33 + TEXT_TY_Support(HASH_KOVS, it); 158    return rv; 159];

Printing.

We share some code here with the routines originally written for the ACTIONS testing command. (The DB in DB_Action stands for "debugging".) When printing a topic, it prints the relevant words from the player's command: so if our stored action is one which contains an entry 5, then we have to temporarily adopt this as the player's command, and restore the old player's command once printing is done. To do this, we need to save the old player's command, and we do that by creating a text for the duration.

171[ STORED_ACTION_TY_Say stora text_of_command saved_command saved_pn saved_action K1 K2 at cf cw; 172    if ((stora==0) || (BlkValueWeakKind(stora) ~= STORED_ACTION_TY)) return; 173    text_of_command = BlkValueRead(stora, STORA_COMMAND_TEXT_F); 174    if (text_of_command) { 175        saved_command = BlkValueCreate(TEXT_TY); 176        BlkValueCast(saved_command, SNIPPET_TY, players_command); 177        SetPlayersCommand(text_of_command); 178    } 179    saved_pn = parsed_number; saved_action = action; 180    action = BlkValueRead(stora, STORA_ACTION_F); 181    cf = consult_from; cw = consult_words; 182    at = FindAction(-1); 183    K1 = ActionData-->(at+AD_NOUN_KOV); 184    K2 = ActionData-->(at+AD_SECOND_KOV); 185    if (K1 ~= OBJECT_TY) { 186        parsed_number = BlkValueRead(stora, STORA_NOUN_F); 187        if ((K1 == UNDERSTANDING_TY) && (text_of_command == 0)) { 188            if (saved_command == 0) saved_command = BlkValueCreate(TEXT_TY); 189            BlkValueCast(saved_command, SNIPPET_TY, players_command); 190            text_of_command = BlkValueCreate(TEXT_TY); 191            BlkValueCopy(text_of_command, parsed_number); 192            SetPlayersCommand(text_of_command); 193            parsed_number = players_command; 194            consult_from = parsed_number/100; consult_words = parsed_number%100; 195        } 196    } 197    if (K2 ~= OBJECT_TY) { 198        parsed_number = BlkValueRead(stora, STORA_SECOND_F); 199        if ((K2 == UNDERSTANDING_TY) && (text_of_command == 0)) { 200            if (saved_command == 0) saved_command = BlkValueCreate(TEXT_TY); 201            BlkValueCast(saved_command, SNIPPET_TY, players_command); 202            text_of_command = BlkValueCreate(TEXT_TY); 203            BlkValueCopy(text_of_command, parsed_number); 204            SetPlayersCommand(text_of_command); 205            parsed_number = players_command; 206            consult_from = parsed_number/100; consult_words = parsed_number%100; 207        } 208    } 209    DB_Action( 210        BlkValueRead(stora, STORA_ACTOR_F), 211        BlkValueRead(stora, STORA_REQUEST_F), 212        BlkValueRead(stora, STORA_ACTION_F), 213        BlkValueRead(stora, STORA_NOUN_F), 214        BlkValueRead(stora, STORA_SECOND_F), true); 215    parsed_number = saved_pn; action = saved_action; 216    consult_from = cf; consult_words = cw; 217    if (text_of_command) { 218        SetPlayersCommand(saved_command); 219        BlkValueFree(saved_command); 220    } 221];

Involvement.

That completes the compulsory services required for this KOV to function: from here on, the remaining routines provide definitions of stored action-related phrases in the Standard Rules.

An action "involves" an object if it appears as either the actor or the first or second noun.

232[ STORED_ACTION_TY_Involves stora item at; 233    at = FindAction(BlkValueRead(stora, STORA_ACTION_F)); 234    if (at) { 235        if ((ActionData-->(at+AD_NOUN_KOV) == OBJECT_TY) && 236            (BlkValueRead(stora, STORA_NOUN_F) == item)) rtrue; 237        if ((ActionData-->(at+AD_SECOND_KOV) == OBJECT_TY) && 238            (BlkValueRead(stora, STORA_SECOND_F) == item)) rtrue; 239    } 240    if (BlkValueRead(stora, STORA_ACTOR_F) == item) rtrue; 241    rfalse; 242];

Nouns.

Extracting the noun or second noun from an action is a delicate business because simply returning the values in entries 1 and 2 would not be type-safe; it would fail to be an object if the stored action did not apply to objects. So the following returns nothing if requested to produce noun or second noun for such an action.

252[ STORED_ACTION_TY_Part stora ind at ado; 253    if (ind == STORA_NOUN_F or STORA_SECOND_F) { 254        if (ind == STORA_NOUN_F) ado = AD_NOUN_KOV; else ado = AD_SECOND_KOV; 255        at = FindAction(BlkValueRead(stora, STORA_ACTION_F)); 256        if ((at) && (ActionData-->(at+ado) == OBJECT_TY)) return BlkValueRead(stora, ind); 257        return nothing; 258    } 259    return BlkValueRead(stora, ind); 260];

Pattern Matching.

In order to apply an action pattern such as "doing something with the kazoo" to a stored action, it needs to be the current action, because the code which compiles conditions like this looks at the action, noun, ..., variables. We don't want to do anything as disruptive as temporarily starting the stored action and then halting it again, so instead we simply "adopt" it, saving the slate of action variables and setting them from the stored action: almost immediately after – the moment the condition has been tested – we "unadopt" it again, restoring the stored values. Since the action pattern cannot itself refer to a stored action, the following code won't be nested, and we don't need to worry about stacking up saved copies of the action variables.

SAT_Tmp-->0 stores the outcome of the condition, and is set in code compiled by NI.

278Array SAT_Tmp-->7; 279[ STORED_ACTION_TY_Adopt stora at; 280    SAT_Tmp-->1 = action; 281    SAT_Tmp-->2 = noun; 282    SAT_Tmp-->3 = second; 283    SAT_Tmp-->4 = actor; 284    SAT_Tmp-->5 = act_requester; 285    SAT_Tmp-->6 = parsed_number; 286    action = BlkValueRead(stora, STORA_ACTION_F); 287    at = FindAction(-1); 288    if (ActionData-->(at+AD_NOUN_KOV) == OBJECT_TY) 289        noun = BlkValueRead(stora, STORA_NOUN_F); 290    else { 291        parsed_number = BlkValueRead(stora, STORA_NOUN_F); 292        noun = nothing; 293    } 294    if (ActionData-->(at+AD_SECOND_KOV) == OBJECT_TY) 295        second = BlkValueRead(stora, STORA_SECOND_F); 296    else { 297        parsed_number = BlkValueRead(stora, STORA_SECOND_F); 298        second = nothing; 299    } 300    actor = BlkValueRead(stora, STORA_ACTOR_F); 301    if (BlkValueRead(stora, STORA_REQUEST_F)) act_requester = player; else act_requester = nothing; 302]; 303 304[ STORED_ACTION_TY_Unadopt; 305    action = SAT_Tmp-->1; 306    noun = SAT_Tmp-->2; 307    second = SAT_Tmp-->3; 308    actor = SAT_Tmp-->4; 309    act_requester = SAT_Tmp-->5; 310    parsed_number = SAT_Tmp-->6; 311    return SAT_Tmp-->0; 312];

Current Action.

Although we never cast other values to stored actions, because none of them really imply an action (not even an action name, since that gives no help as to what the nouns might be), there is of course one action almost always present within a story file at run-time, even if it is not a single value as such: the action which is currently running. The following routine translates that into a stored action – thus allowing us to store it.

This is the place where we look to see if the action applies to a topic as either its noun or second noun, and if it does, we copy the player's command into a text block-value in entry 5.

327[ STORED_ACTION_TY_Current stora at text_of_command; 328    if ((stora==0) || (BlkValueWeakKind(stora) ~= STORED_ACTION_TY)) return 0; 329    BlkValueWrite(stora, STORA_ACTION_F, action); 330    at = FindAction(-1); 331 332    if (ActionData-->(at+AD_NOUN_KOV) == OBJECT_TY) 333        BlkValueWrite(stora, STORA_NOUN_F, noun); 334    else 335        BlkValueWrite(stora, STORA_NOUN_F, parsed_number); 336    if (ActionData-->(at+AD_SECOND_KOV) == OBJECT_TY) 337        BlkValueWrite(stora, STORA_SECOND_F, second); 338    else 339        BlkValueWrite(stora, STORA_SECOND_F, parsed_number); 340    BlkValueWrite(stora, STORA_ACTOR_F, actor); 341    if (act_requester) BlkValueWrite(stora, STORA_REQUEST_F, true); 342    else BlkValueWrite(stora, STORA_REQUEST_F, false); 343 344    if ((at) && ((ActionData-->(at+AD_NOUN_KOV) == UNDERSTANDING_TY) || 345            (ActionData-->(at+AD_SECOND_KOV) == UNDERSTANDING_TY))) { 346        text_of_command = BlkValueRead(stora, STORA_COMMAND_TEXT_F); 347        if (text_of_command == 0) { 348            text_of_command = BlkValueCreate(TEXT_TY); 349            BlkValueWrite(stora, STORA_COMMAND_TEXT_F, text_of_command); 350        } 351        BlkValueCast(text_of_command, SNIPPET_TY, players_command); 352    } else BlkValueWrite(stora, STORA_COMMAND_TEXT_F, 0); 353 354    return stora; 355];

Trying.

Finally: having stored an action for perhaps many turns, we now let it happen, either silently or not.

362[ STORED_ACTION_TY_Try stora ks text_of_command saved_command; 363    if ((stora==0) || (BlkValueWeakKind(stora) ~= STORED_ACTION_TY)) return; 364    if (ks) { @push keep_silent; keep_silent=1; } 365    text_of_command = BlkValueRead(stora, STORA_COMMAND_TEXT_F); 366    if (text_of_command) { 367        saved_command = BlkValueCreate(TEXT_TY); 368        BlkValueCast(saved_command, SNIPPET_TY, players_command); 369        SetPlayersCommand(text_of_command); 370    } 371    TryAction( 372        BlkValueRead(stora, STORA_REQUEST_F), 373        BlkValueRead(stora, STORA_ACTOR_F), 374        BlkValueRead(stora, STORA_ACTION_F), 375        BlkValueRead(stora, STORA_NOUN_F), 376        BlkValueRead(stora, STORA_SECOND_F)); 377    if (text_of_command) { 378        SetPlayersCommand(saved_command); 379        BlkValueFree(saved_command); 380    } 381    if (ks) { @pull keep_silent; } 382];