I6 Template Layer

Inform 7 6M62ContentsIntroductionFunction IndexRules Index

Actions.i6t

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; ! The I6 action number (0 to 4095) 64Constant AD_REQUIREMENTS = 1; ! Such as requiring light; a bitmap, see below 65Constant AD_NOUN_KOV = 2; ! Kind of value of the first noun 66Constant AD_SECOND_KOV = 3; ! Kind of value of the second noun 67Constant AD_VARIABLES_CREATOR = 4; ! Routine to initialise variables owned 68Constant AD_VARIABLES_ID = 5; ! Frame ID for variables owned by action 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; ! "room-describing action" 284        (MStack-->MstVO(frame_id, 1)) = true; ! "abbreviated form allowed" 285    } 286    LookSub(); ! The I6 verb routine for "looking" 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); ! In case disturbed by printing activities 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; ! For any I6-defined actions 432 433    noun_kova = ActionData-->(at+AD_NOUN_KOV); 434    second_kova = ActionData-->(at+AD_SECOND_KOV); 435 436    !print "at = ", at, " nst = ", noun_kova, "^"; 437    !print "consult_from = ", consult_from, " consult_words = ", consult_from, "^"; 438    !print "inp1 = ", inp1, " noun = ", noun, "^"; 439    !print "inp2 = ", inp2, " second = ", second, "^"; 440    !print "sst = ", second_kova, "^"; 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; ! noun = parsed_number; 446    } 447    if (second_kova == SNIPPET_TY or UNDERSTANDING_TY) { 448        parsed_number = 100*consult_from + consult_words; 449        inp2 = 1; second = nothing; ! second = parsed_number; 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; ! Check rules for the action 692    Details_of_Specific_Action-->3 = co; ! Carry out rules for the action 693    Details_of_Specific_Action-->4 = re; ! Report rules for the action 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];