Actions contents
Summary.
To review: an action is an impulse to do something by a person in the model world. Commands such as DROP POTATO are converted into actions ("dropping the Idaho potato"); sometimes they succeed, sometimes they fail. While they run, the fairly complicated details are stored in a suite of I6 global variables such as actor, noun, inp1, and so on (see OrderOfPlay.i6t for details); the running of an action is mainly a matter of processing many rulebooks, chief among them they "action processing rules".
In general, actions can come from five different sources:
(i) As a result of parsing the player's command: there are actually two ways this can happen, one if the command calls for a single action, and another if it calls for a whole run of them (like TAKE ALL). See the rules in OrderOfPlay.i6t. (ii) From an I7 "try" phrase, in which case TryAction is called. (iii) From an I6 angle-bracket-notation such as <Wait>, though this is a syntax which is deprecated now, and is never normally used in I7. The I6 compiler converts such a syntax into a call to the R_Process below. (iv) Through conversion of an existing action. For instance, "removing the cup from the table" is converted in the Standard Rules to "taking the cup". This is done via a routine called GVS_Convert. (v) When a request is successful, the "carry out requested actions" rule turns the original action – a request by the player, such as is produced by CHOPIN, PLAY POLONAISE – into an action by the person asked, such as "Chopin playing the Polonaise".
Certain exceptional cases can arise for other reasons:
(vi) Implicit taking actions are generated by the "carrying requirements rule" when the actor tries something which requires him to be holding an item which he can see, but is not currently holding. (vii) A partial but intentionally incomplete form of the "looking" action is generated when describing the new location at the end of a "going" action.
In every case except (vii), the action is carried out by BeginAction, the single routine which unifies all of these approaches. Except the last one.
This segment of the template is divided into two: first, the I6 code needed for (i) to (vii), the alternative ways for actions to begin; and secondly the common machinery into which all actions eventually pass.
Action Data.
This is perhaps a good place to document the ActionData array, a convenient table of metadata about the actions. Since this is compiled by NI, the following structure can't be modified here without making matching changes in NI. ActionData is an I6 table containing a series of fixed-length records, one on each action.
The routine FindAction locates the record in this table for a given action number, returning its word offset within the table: the argument -1 means "the current action".
63Constant AD_ACTION = 0;
64Constant AD_REQUIREMENTS = 1;
65Constant AD_NOUN_KOV = 2;
66Constant AD_SECOND_KOV = 3;
67Constant AD_VARIABLES_CREATOR = 4;
68Constant AD_VARIABLES_ID = 5;
69
70Constant AD_RECORD_SIZE = 6;
71
72[ FindAction fa t;
73 if (fa == -1) fa = action;
74 t = 1;
75 while (t <= ActionData-->0) {
76 if (fa == ActionData-->t) return t;
77 t = t + AD_RECORD_SIZE;
78 }
79 rfalse;
80];
81
82[ ActionNumberIndexed i;
83 if ((i>=0) && (i < AD_RECORDS)) return ActionData-->(i*AD_RECORD_SIZE + AD_ACTION + 1);
84 return 0;
85];
Requirements Bitmap.
As noted above, the AD_REQUIREMENTS field is a bitmap of flags for various possible action requirements:
92Constant TOUCH_NOUN_ABIT = $$00000001;
93Constant TOUCH_SECOND_ABIT = $$00000010;
94Constant LIGHT_ABIT = $$00000100;
95Constant NEED_NOUN_ABIT = $$00001000;
96Constant NEED_SECOND_ABIT = $$00010000;
97Constant OUT_OF_WORLD_ABIT = $$00100000;
98Constant CARRY_NOUN_ABIT = $$01000000;
99Constant CARRY_SECOND_ABIT = $$10000000;
100
101[ NeedToCarryNoun; return TestActionMask(CARRY_NOUN_ABIT); ];
102[ NeedToCarrySecondNoun; return TestActionMask(CARRY_SECOND_ABIT); ];
103[ NeedToTouchNoun; return TestActionMask(TOUCH_NOUN_ABIT); ];
104[ NeedToTouchSecondNoun; return TestActionMask(TOUCH_SECOND_ABIT); ];
105[ NeedLightForAction; return TestActionMask(LIGHT_ABIT); ];
106
107[ TestActionMask match mask at;
108 at = FindAction(-1);
109 if (at == 0) rfalse;
110 mask = ActionData-->(at+AD_REQUIREMENTS);
111 if (mask & match) rtrue;
112 rfalse;
113];
Try Action.
This is method (ii) in the summary above.
119[ TryAction req by ac n s stora smeta tbits saved_command text_of_command;
120 if (stora) return STORED_ACTION_TY_New(ac, n, s, by, req, stora);
121 tbits = req & (16+32);
122 req = req & 1;
123 @push actor; @push act_requester; @push inp1; @push inp2;
124 @push parsed_number; smeta = meta;
125 actor = by; if (req) act_requester = player; else act_requester = 0;
126
127 by = FindAction(ac);
128 if (by) {
129 if (ActionData-->(by+AD_NOUN_KOV) == OBJECT_TY) inp1 = n;
130 else { inp1 = 1; parsed_number = n; }
131 if (ActionData-->(by+AD_SECOND_KOV) == OBJECT_TY) inp2 = s;
132 else { inp2 = 1; parsed_number = s; }
133 if (((ActionData-->(by+AD_NOUN_KOV) == UNDERSTANDING_TY) ||
134 (ActionData-->(by+AD_SECOND_KOV) == UNDERSTANDING_TY)) && (tbits)) {
135 saved_command = BlkValueCreate(TEXT_TY);
136 BlkValueCast(saved_command, SNIPPET_TY, players_command);
137 text_of_command = BlkValueCreate(TEXT_TY);
138 BlkValueCopy(text_of_command, parsed_number);
139 SetPlayersCommand(text_of_command);
140 if (tbits == 16) {
141 n = players_command; inp1 = 1; parsed_number = players_command;
142 } else {
143 s = players_command; inp2 = 1; parsed_number = players_command;
144 }
145 BlkValueFree(text_of_command);
146 @push consult_from; @push consult_words;
147 consult_from = 1; consult_words = parsed_number - 100;
148 }
149 }
150
151 BeginAction(ac, n, s, 0, true);
152
153 if (saved_command) {
154 @pull consult_words; @pull consult_from;
155 SetPlayersCommand(saved_command);
156 BlkValueFree(saved_command);
157 }
158
159 meta = smeta; @pull parsed_number;
160 @pull inp2; @pull inp1; @pull act_requester; @pull actor;
161 TrackActions(true, smeta);
162];
I6 Angle Brackets.
This is method (iii) in the summary above. The routine here has slightly odd conventions and a curious name which would take too long to explain: neither can be changed without amending the veneer code within the I6 compiler.
171[ R_Process a i j;
172 @push inp1; @push inp2;
173 inp1 = i; inp2 = j; BeginAction(a, i, j);
174 @pull inp2; @pull inp1;
175];
Conversion.
This is method (iv) in the summary above.
181Global converted_action_outcome = -1;
182[ GVS_Convert ac n s;
183 converted_action_outcome = BeginAction(ac, n, s);
184 if (converted_action_outcome == true) FollowRulebook( (+ after rules +), nothing, true );
185 rtrue;
186];
187
188[ ConvertToRequest X AN Y Z;
189 WORK_OUT_DETAILS_OF_SPECIFIC_R();
190 if (X == player) TryAction(false, X, AN, Y, Z);
191 else TryAction(true, X, AN, Y, Z);
192 rtrue;
193];
194
195[ ConvertToGoingWithPush i oldrm newrm infl;
196 i=noun;
197 if (IndirectlyContains(noun, actor) == false) { move i to actor; infl = true; }
198 move_pushing = i;
199 oldrm = LocationOf(noun);
200 BeginAction(##Go, second);
201 newrm = LocationOf(actor);
202 move_pushing = nothing; move i to newrm;
203 if (newrm ~= oldrm) {
204 if (IndirectlyContains(i, player)) TryAction(0, player, ##Look, 0, 0);
205 RulebookSucceeds();
206 } else RulebookFails();
207 rtrue;
208];
Implicit Take.
This is method (vi) in the summary above.
214[ ImplicitTake obj ks;
215 if (actor == player) { STANDARD_IMPLICIT_TAKING_RM('A', obj); }
216 else {
217 if (TestVisibility(player, actor))
218 STANDARD_IMPLICIT_TAKING_RM('B', obj, actor);
219 }
220 ClearParagraphing(3);
221 @push keep_silent; keep_silent = true;
222 @push say__p; @push say__pc; ClearParagraphing(4);
223 if (act_requester) TryAction(true, actor, ##Take, obj, nothing);
224 else TryAction(false, actor, ##Take, obj, nothing);
225 DivideParagraphPoint(); @pull say__pc; @pull say__p; AdjustParagraphPoint(); @pull keep_silent;
226 if (obj in actor) rtrue;
227 rfalse;
228];
Look After Going.
This is method (vii) in the summary above.
Fundamentally, room descriptions arise through looking actions, but they are also printed after successful going actions, with a special form of paragraph break (see Printing.i6t for an explanation of this). Room descriptions through looking are always given in full, unless we have SUPERBRIEF mode set.
240[ LookAfterGoing;
241 GoingLookBreak();
242 AbbreviatedRoomDescription();
243];
Abbreviated Room Description.
This is used when we want a room description with the same abbreviation conventions as after a going action, and we don't quite want a looking action fully to take place. We nevertheless want to be sure that the action variables for looking exist, and in particular, we want to set the "room-describing action" variable to the action which was prevailing when the room description was called for. We also set "abbreviated form allowed" to "true": when the ordinary looking action is running, this is "false".
The actual description occurs during LookSub, which is the specific action processing stage for the "looking" action: thus, we use the check, carry out, after and report rules as if we were "looking", but are unaffected by before or instead rules.
Uniquely, this pseudo-action does not use BeginAction: it works only through the specific action processing rules, not the main action-processing ones, though that is not easy to see from the code below because it is hidden in the call to LookSub. The -Sub suffix is an I6 usage identifying this as the routine to go along with the action ##Look, and so it is, but it looks nothing like the LookSub of the old I6 library. NI compiles -Sub routines like so:
[ LookSub; return GenericVerbSub(153,154,155); ];
(with whatever rulebook numbers are appropriate). GenericVerbSub then runs through the specific action processing stage.
274[ AbbreviatedRoomDescription prior_action pos frame_id;
275 prior_action = action;
276
277 action = ##Look;
278 pos = FindAction(##Look);
279 if ((pos) && (ActionData-->(pos+AD_VARIABLES_CREATOR))) {
280 frame_id = ActionData-->(pos+AD_VARIABLES_ID);
281 Mstack_Create_Frame(ActionData-->(pos+AD_VARIABLES_CREATOR), frame_id);
282 FollowRulebook(SETTING_ACTION_VARIABLES_RB);
283 (MStack-->MstVO(frame_id, 0)) = prior_action;
284 (MStack-->MstVO(frame_id, 1)) = true;
285 }
286 LookSub();
287 if (frame_id) Mstack_Destroy_Frame(ActionData-->(pos+AD_VARIABLES_CREATOR), frame_id);
288
289 action = prior_action;
290];
Begin Action.
We now begin the second half of the segment: the machinery which handles all actions.
The significance of 4096 here is that this is how I6 distinguishes genuine actions – numbered upwards in order of creation – from what I6 calls "fake actions" – numbered upwards from 4096. Fake actions are hardly used at all in I7, and certainly shouldn't get here, but it's possible nonetheless using I6 angled-brackets, so... In other respects all we do is to save details of whatever current action is happening onto the stack, and then call ActionPrimitive.
305[ BeginAction a n s moi notrack rv;
306 ChronologyPoint();
307
308 @push action; @push noun; @push second; @push self; @push multiple_object_item;
309
310 action = a; noun = n; second = s; self = noun; multiple_object_item = moi;
311 if (action < 4096) rv = ActionPrimitive();
312
313 @pull multiple_object_item; @pull self; @pull second; @pull noun; @pull action;
314
315 if (notrack == false) TrackActions(true, meta);
316 return rv;
317];
Action Primitive.
This is somewhat different from the I6 library counterpart which gives it its name, but the idea is the same. It has no arguments at all: everything it needs to know is now stored in global variables. The routine looks long, but really contains little: it's all just book-keeping, printing debugging information if ACTIONS is in force, etc., with all of the actual work delegated to the action processing rulebook.
We use a rather sneaky device to handle out-of-world actions, those for which the meta flag is set: we make it look to the system as if the "action processing rulebook" is being followed, so that all its variables are created and placed in scope, but at the crucial moment we descend to the specific action processing rules directly instead of processing the main rulebook. This is what short-circuits out of world actions and protects them from before and instead rules: see the Standard Rules for more discussion of this.
337[ ActionPrimitive rv p1 p2 p3 p4 p5 frame_id;
338 MStack_CreateRBVars(ACTION_PROCESSING_RB);
339
340 if ((keep_silent == false) && (multiflag == false)) DivideParagraphPoint();
341 reason_the_action_failed = 0;
342
343 frame_id = -1;
344 p1 = FindAction(action);
345 if ((p1) && (ActionData-->(p1+AD_VARIABLES_CREATOR))) {
346 frame_id = ActionData-->(p1+AD_VARIABLES_ID);
347 Mstack_Create_Frame(ActionData-->(p1+AD_VARIABLES_CREATOR), frame_id);
348 }
349 if (ActionVariablesNotTypeSafe()) {
350 if (actor ~= player) { ACTION_PROCESSING_INTERNAL_RM('K'); new_line; }
351 if (frame_id ~= -1)
352 Mstack_Destroy_Frame(ActionData-->(p1+AD_VARIABLES_CREATOR), frame_id);
353 MStack_DestroyRBVars(ACTION_PROCESSING_RB);
354 return;
355 }
356
357 FollowRulebook(SETTING_ACTION_VARIABLES_RB);
358
359 #IFDEF DEBUG;
360 if ((trace_actions) && (FindAction(-1))) {
361 print "["; p1=actor; p2=act_requester; p3=action; p4=noun; p5=second;
362 DB_Action(p1,p2,p3,p4,p5);
363 print "]^"; ClearParagraphing(5);
364 }
365 ++debug_rule_nesting;
366 #ENDIF;
367 TrackActions(false, meta);
368 if ((meta) && (actor ~= player)) {
369 ACTION_PROCESSING_INTERNAL_RM('A', actor); new_line; rv = RS_FAILS; }
370 else if (meta) { DESCEND_TO_SPECIFIC_ACTION_R(); rv = RulebookOutcome(); }
371 else { FollowRulebook(ACTION_PROCESSING_RB); rv = RulebookOutcome(); }
372 #IFDEF DEBUG;
373 --debug_rule_nesting;
374 if ((trace_actions) && (FindAction(-1))) {
375 print "["; DB_Action(p1,p2,p3,p4,p5); print " - ";
376 switch (rv) {
377 RS_SUCCEEDS: print "succeeded";
378 RS_FAILS: print "failed";
379 #IFNDEF MEMORY_ECONOMY;
380 if (reason_the_action_failed)
381 print " the ",
382 (RulePrintingRule) reason_the_action_failed;
383 #ENDIF;
384 default: print "ended without result";
385 }
386 print "]^"; say__p = 1;
387 SetRulebookOutcome(rv);
388 }
389 #ENDIF;
390 if (rv == RS_SUCCEEDS) UpdateActionBitmap();
391 if (frame_id ~= -1) {
392 p1 = FindAction(action);
393 Mstack_Destroy_Frame(ActionData-->(p1+AD_VARIABLES_CREATOR), frame_id);
394 }
395 MStack_DestroyRBVars(ACTION_PROCESSING_RB);
396 if ((keep_silent == false) && (multiflag == false)) DivideParagraphPoint();
397 if (rv == RS_SUCCEEDS) rtrue;
398 rfalse;
399];
Internal Rule.
Provided only as a hook on which to hang responses.
405[ ACTION_PROCESSING_INTERNAL_R; ];
Type Safety.
Some basic action requirements have to be met before we can go any further: if they aren't, then it isn't type-safe even to run the action processing rulebook.
(i) For an out of world action, we set the meta flag. Otherwise: (ii) If either the noun or second noun is a topic, then this is an action arising from parsing (such actions do not arise through the "try" phrase, unless by stored actions in which case this has all happened before and doesn't need to be done again) – the parser places details of which words make up the topic in the I6 global variables consult_words and consult_from. We convert them to a valid I7 snippet value. (iii) If either the first or second noun is supposed to be an object but seems here to be a value, or vice versa, we stop with a parser error. (This should be fairly difficult to provoke: NI's type-checking will make it difficult to arrange without I6 subterfuges.) (iv) If either the first or second noun is supposed to be an object and required to exist, yet is missing, we use the "supplying a missing noun" or "supplying a missing second noun" activities to fill the void.
We return true if type safety is violated, false if all is well.
430[ ActionVariablesNotTypeSafe mask noun_kova second_kova at;
431 at = FindAction(-1); if (at == 0) rfalse;
432
433 noun_kova = ActionData-->(at+AD_NOUN_KOV);
434 second_kova = ActionData-->(at+AD_SECOND_KOV);
435
436
437
438
439
440
441
442 if (noun_kova == SNIPPET_TY or UNDERSTANDING_TY) {
443 if (inp1 ~= 1) { inp2 = inp1; second = noun; }
444 parsed_number = 100*consult_from + consult_words;
445 inp1 = 1; noun = nothing;
446 }
447 if (second_kova == SNIPPET_TY or UNDERSTANDING_TY) {
448 parsed_number = 100*consult_from + consult_words;
449 inp2 = 1; second = nothing;
450 }
451
452 mask = ActionData-->(at+AD_REQUIREMENTS);
453 if (mask & OUT_OF_WORLD_ABIT) { meta = 1; rfalse; }
454 meta = 0;
455
456 if (inp1 == 1) {
457 if (noun_kova == OBJECT_TY) {
458 if (actor == player) { ACTION_PROCESSING_INTERNAL_RM('B'); new_line; }
459 rtrue;
460 }
461 } else {
462 if (noun_kova ~= OBJECT_TY) {
463 if (actor == player) { ACTION_PROCESSING_INTERNAL_RM('C'); new_line; }
464 rtrue;
465 }
466 if ((mask & NEED_NOUN_ABIT) && (noun == nothing)) {
467 @push act_requester; act_requester = nothing;
468 CarryOutActivity(SUPPLYING_A_MISSING_NOUN_ACT);
469 @pull act_requester;
470 if (noun == nothing) {
471 if (say__p) rtrue;
472 if (actor == player) { ACTION_PROCESSING_INTERNAL_RM('D'); new_line; }
473 rtrue;
474 }
475 }
476 if (((mask & NEED_NOUN_ABIT) == 0) && (noun ~= nothing)) {
477 if (actor == player) { ACTION_PROCESSING_INTERNAL_RM('E'); new_line; }
478 rtrue;
479 }
480 }
481
482 if (inp2 == 1) {
483 if (second_kova == OBJECT_TY) {
484 if (actor == player) { ACTION_PROCESSING_INTERNAL_RM('F'); new_line; }
485 rtrue;
486 }
487 } else {
488 if (second_kova ~= OBJECT_TY) {
489 if (actor == player) { ACTION_PROCESSING_INTERNAL_RM('G'); new_line; }
490 rtrue;
491 }
492 if ((mask & NEED_SECOND_ABIT) && (second == nothing)) {
493 @push act_requester; act_requester = nothing;
494 CarryOutActivity(SUPPLYING_A_MISSING_SECOND_ACT);
495 @pull act_requester;
496 if (second == nothing) {
497 if (say__p) rtrue;
498 if (actor == player) { ACTION_PROCESSING_INTERNAL_RM('H'); new_line; }
499 rtrue;
500 }
501 }
502 if (((mask & NEED_SECOND_ABIT) == 0) && (second ~= nothing)) {
503 if (actor == player) { ACTION_PROCESSING_INTERNAL_RM('I'); new_line; }
504 rtrue;
505 }
506 }
507
508 rfalse;
509];
Basic Visibility Rule.
This is one of the I6 primitive rules in the action processing rulebook: see the account in the Standard Rules for details.
Note that this rule only blocks the player from acting in darkness: this is because light is only reckoned from the player's perspective in any case, so that it would be unfair to apply the rule to any other person.
520[ BASIC_VISIBILITY_R;
521 if (act_requester) rfalse;
522 if ((NeedLightForAction()) &&
523 (actor == player) &&
524 (FollowRulebook(VISIBLE_RB)) &&
525 (RulebookSucceeded())) {
526 BeginActivity(REFUSAL_TO_ACT_IN_DARK_ACT);
527 if (ForActivity(REFUSAL_TO_ACT_IN_DARK_ACT)==false) {
528 BASIC_VISIBILITY_RM('A'); new_line;
529 }
530 EndActivity(REFUSAL_TO_ACT_IN_DARK_ACT);
531 reason_the_action_failed = BASIC_VISIBILITY_R;
532 RulebookFails();
533 rtrue;
534 }
535 rfalse;
536];
Basic Accessibility Rule.
This is one of the I6 primitive rules in the action processing rulebook: see the account in the Standard Rules for details.
543[ BASIC_ACCESSIBILITY_R mask at;
544 if (act_requester) rfalse;
545 at = FindAction(-1);
546 if (at == 0) rfalse;
547 mask = ActionData-->(at+AD_REQUIREMENTS);
548
549 if ((mask & TOUCH_NOUN_ABIT) && noun && (inp1 ~= 1)) {
550 if (noun ofclass K3_direction) {
551 RulebookFails();
552 reason_the_action_failed = BASIC_ACCESSIBILITY_R;
553 if (actor~=player) rtrue;
554 BASIC_ACCESSIBILITY_RM('A'); new_line;
555 RulebookFails();
556 reason_the_action_failed = BASIC_ACCESSIBILITY_R;
557 rtrue;
558 }
559 if (ObjectIsUntouchable(noun, (actor~=player), actor)) {
560 RulebookFails();
561 reason_the_action_failed = BASIC_ACCESSIBILITY_R;
562 rtrue;
563 }
564 }
565
566 if ((mask & TOUCH_SECOND_ABIT) && second && (inp2 ~= 1)) {
567 if (second ofclass K3_direction) {
568 RulebookFails();
569 reason_the_action_failed = BASIC_ACCESSIBILITY_R;
570 if (actor~=player) rtrue;
571 BASIC_ACCESSIBILITY_RM('A'); new_line;
572 RulebookFails();
573 reason_the_action_failed = BASIC_ACCESSIBILITY_R;
574 rtrue;
575 }
576 if (ObjectIsUntouchable(second, (actor~=player), actor)) {
577 RulebookFails();
578 reason_the_action_failed = BASIC_ACCESSIBILITY_R;
579 rtrue;
580 }
581 }
582 rfalse;
583];
Carrying Requirements Rule.
This is one of the I6 primitive rules in the action processing rulebook: see the account in the Standard Rules for details.
590[ CARRYING_REQUIREMENTS_R mask at;
591
592 at = FindAction(-1);
593 if (at == 0) rfalse;
594 mask = ActionData-->(at+AD_REQUIREMENTS);
595
596 if ((mask & TOUCH_NOUN_ABIT) && noun && (inp1 ~= 1)) {
597 if ((mask & CARRY_NOUN_ABIT) && (noun notin actor)) {
598 CarryOutActivity(IMPLICITLY_TAKING_ACT, noun);
599 if (noun notin actor) {
600 RulebookFails();
601 reason_the_action_failed = CARRYING_REQUIREMENTS_R;
602 rtrue;
603 }
604 }
605 }
606
607 if ((mask & TOUCH_SECOND_ABIT) && second && (inp2 ~= 1)) {
608 if ((mask & CARRY_SECOND_ABIT) && (second notin actor)) {
609 CarryOutActivity(IMPLICITLY_TAKING_ACT, second);
610 if (second notin actor) {
611 RulebookFails();
612 reason_the_action_failed = CARRYING_REQUIREMENTS_R;
613 rtrue;
614 }
615 }
616 }
617 rfalse;
618];
Standard Implicit Taking Rule.
623[ STANDARD_IMPLICIT_TAKING_R;
624 ImplicitTake(parameter_value);
625 rfalse;
626];
Requested Actions Require Persuasion Rule.
This is one of the I6 primitive rules in the action processing rulebook: see the account in the Standard Rules for details.
633[ REQUESTED_ACTIONS_REQUIRE_R rv;
634 if ((actor ~= player) && (act_requester)) {
635 @push say__p;
636 say__p = 0;
637 rv = FollowRulebook(PERSUADE_RB);
638 if (RulebookSucceeded() == false) {
639 if ((deadflag == false) && (say__p == FALSE)) {
640 REQUESTED_ACTIONS_REQUIRE_RM('A', actor);
641 new_line;
642 }
643 ActRulebookFails(rv); rtrue;
644 }
645 @pull say__p;
646 }
647 rfalse;
648];
Carry Out Requested Actions Rule.
This is one of the I6 primitive rules in the action processing rulebook: see the account in the Standard Rules for details.
655[ CARRY_OUT_REQUESTED_ACTIONS_R rv;
656 if ((actor ~= player) && (act_requester)) {
657 @push act_requester; act_requester = nothing;
658 rv = BeginAction(action, noun, second);
659 if (((meta) || (rv == false)) && (deadflag == false)) {
660 if (FollowRulebook(UNSUCCESSFUL_ATTEMPT_RB) == false) {
661 CARRY_OUT_REQUESTED_ACTIONS_RM('A', actor); new_line;
662 }
663 }
664 @pull act_requester;
665 FollowRulebook(AFTER_RB);
666 ActRulebookSucceeds();
667 rtrue;
668 }
669 rfalse;
670];
Generic Verb Subroutine.
In I6, actions are carried out by routines with names like TakeSub, consisting of -Sub tacked on to the action name Take. Sub stands for "subroutine": this is all a convention going back to Inform 1, which was in 1993 practically an assembler. In the I6 code generated by I7, every -Sub routine corresponding to an I7 action consists only of a call to GenericVerbSub which specifies the three rulebooks it owns: its check, carry out and report rulebooks.
682Array Details_of_Specific_Action-->5;
683
684[ GenericVerbSub ch co re vis rv;
685 @push converted_action_outcome;
686 converted_action_outcome = -1;
687
688 Details_of_Specific_Action-->0 = true;
689 if (meta) Details_of_Specific_Action-->0 = false;
690 Details_of_Specific_Action-->1 = keep_silent;
691 Details_of_Specific_Action-->2 = ch;
692 Details_of_Specific_Action-->3 = co;
693 Details_of_Specific_Action-->4 = re;
694
695 FollowRulebook(SPECIFIC_ACTION_PROCESSING_RB, 0, true);
696 if ((RulebookFailed()) && (converted_action_outcome == 1)) ActRulebookSucceeds();
697
698 @pull converted_action_outcome;
699 rtrue;
700];
Work Out Details Of Specific Action Rule.
This is one of the I6 primitive rules in the specific action processing rulebook, and it's basically a trick to allow information known to the GenericVerbSub routine to be passed down as rulebook variables for the specific action-processing rules – in effect allowing us to pass not one but five parameters to the rulebook: the out-of-world and silence flags, plus the three specific rulebooks needed to process the action.
711[ WORK_OUT_DETAILS_OF_SPECIFIC_R;
712 MStack-->MstVO(SPECIFIC_ACTION_PROCESSING_RB, 0) = Details_of_Specific_Action-->0;
713 MStack-->MstVO(SPECIFIC_ACTION_PROCESSING_RB, 1) = Details_of_Specific_Action-->1;
714 MStack-->MstVO(SPECIFIC_ACTION_PROCESSING_RB, 2) = Details_of_Specific_Action-->2;
715 MStack-->MstVO(SPECIFIC_ACTION_PROCESSING_RB, 3) = Details_of_Specific_Action-->3;
716 MStack-->MstVO(SPECIFIC_ACTION_PROCESSING_RB, 4) = Details_of_Specific_Action-->4;
717 rfalse;
718];
Actions Bitmap.
This is a fairly large bitmap recording which actions have succeeded thus far on which nouns. It was to some extent an early attempt at implementing a past-tense system; I'm not at all sure it was successful, since it is hindered by certain restrictions – it only records action/noun combinations, for instance, and the notion of "success" is a vexed one for actions anyway. There is a clearly defined meaning, but it doesn't always correspond to what the user might expect, which is unfortunate.
730[ TestActionBitmap obj act i j k bitmap;
731 if (obj == nothing) bitmap = ActionHappened;
732 else {
733 if (~~(obj provides action_bitmap)) rfalse;
734 bitmap = obj.&action_bitmap;
735 }
736 if (act == -1) return (((bitmap->0) & 1) ~= 0);
737 for (i=0, k=2: i<ActionCount: i++) {
738 if (act == ActionCoding-->i) {
739 return (((bitmap->j) & k) ~= 0);
740 }
741 k = k*2; if (k == 256) { k = 1; j++; }
742 }
743 rfalse;
744];
745
746[ UpdateActionBitmap;
747 SetActionBitmap(noun, action);
748 if (action == ##Go) SetActionBitmap(location, ##Enter);
749];
750
751[ SetActionBitmap obj act i j k bitmap;
752 for (i=0, k=2: i<ActionCount: i++) {
753 if (act == ActionCoding-->i) {
754 if (obj provides action_bitmap) {
755 bitmap = obj.&action_bitmap;
756 bitmap->0 = (bitmap->0) | 1;
757 bitmap->j = (bitmap->j) | k;
758 }
759 ActionHappened->0 = (ActionHappened->0) | 1;
760 ActionHappened->j = (ActionHappened->j) | k;
761 }
762 k = k*2; if (k == 256) { k = 1; j++; }
763 }
764];
Printing Actions.
This is really for debugging purposes, but also provides us with a way to print a stored action, for instance, or to print an action name value. (For instance, printing an action name might result in "taking"; printing a whole action might produce "Henry taking the grapefruit".)
773[ SayActionName act; DB_Action(0, 0, act, 0, 0, 2); ];
774
775[ DA_Name n; if (n ofclass K3_direction) print (name) n; else print (the) n; ];
776[ DA_Topic x a b c d i cf cw;
777 cw = x%100; cf = x/100;
778 print "~";
779 for (a=cf:d<cw:d++,a++) {
780 wn = a; b = WordAddress(a); c = WordLength(a);
781 for (i=b:i<b+c:i++) {
782 print (char) 0->i;
783 }
784 if (d<cw-1) print " ";
785 }
786 print "~";
787];
788[ DA_Number n; print n; ];
789[ DA_TruthState n; if (n==0) print "false"; else print "true"; ];
790[ DB_Action ac acr act n s for_say t at l j v c clc;
791 if ((for_say == 0) && (debug_rule_nesting > 0))
792 print "(", debug_rule_nesting, ") ";
793 if ((ac ~= player) && (for_say ~= 2)) {
794 if (acr) print "asking ", (the) ac, " to try ";
795 else print (the) ac, " ";
796 }
797 DB_Action_Details(act, n, s, for_say);
798 if ((keep_silent) && (for_say == 0)) print " - silently";
799];