I6 Template Layer

Inform 7 6M62ContentsIntroductionFunction IndexRules Index

OrderOfPlay.i6t

OrderOfPlay contents

Main.

This is where every I6 story file begins execution: it can end either by returning, or by a quit statement or equivalent opcode. (In I7 this does indeed happen when the quitting the game action is carried out, or when QUIT is typed as a reply to the final question; it's only if the user has altered the shutdown rulebook that we might ever actually return from Main.) The return value from Main is not meaningful.

The EarlyInTurnSequence flag is used to enforce the requirement that the "parse command rule" and "generate action rule" do nothing unless the turn sequence rulebook is being followed directly from Main, an anomaly explained in the Standard Rules.

23Global EarlyInTurnSequence; 24Global IterationsOfTurnSequence; 25 26[ Main; 27    #ifdef TARGET_ZCODE; max_z_object = #largest_object - 255; #endif; 28    ClearRTP(); 29    FollowRulebook(STARTUP_RB); 30    #ifdef DEBUG; InternalTestCases(); #endif; 31    while (true) { 32        while (deadflag == false) { 33            EarlyInTurnSequence = true; 34            action = ##Wait; meta = false; noun = nothing; second = nothing; 35            actor = player; 36            FollowRulebook(TURN_SEQUENCE_RB); 37            IterationsOfTurnSequence++; 38        } 39        if (FollowRulebook(SHUTDOWN_RB) == false) return; 40    } 41];

Virtual Machine Startup Rule.

We delegate to the appropriate VM-specific section of code for the real work. The printing of three blank lines at the start of play is traditional: on early Z-machine interpreters such as InfoTaskForce and Zip it was a necessity because of the way they buffered output. On modern windowed ones it still helps to space the opening text better.

51[ VIRTUAL_MACHINE_STARTUP_R; 52    CarryOutActivity(STARTING_VIRTUAL_MACHINE_ACT); 53    VM_Initialise(); 54    print "^^^"; 55    rfalse; 56];

Initial Situation.

The array InitialSituation is compiled by NI and contains:

(-0) The object number for the player, which is usually selfobj. (-1) The object in or on which the player begins, if he does. (This will always be an enterable container or supporter, or nothing.) (-2) The room in which the player begins, which is usually the first room created in the source text. (-3) The initial time of day, which is usually 9 AM.

The start object and start room are meaningful only if the player's object is compiled outside of the object tree (as can happen if the source text reads, say, "Mrs Bridges is a woman. The player is Mrs Bridges."): in other circumstances they are often correct, but this must not be relied on.

74Constant PLAYER_OBJECT_INIS = 0; 75Constant START_OBJECT_INIS = 1; 76Constant START_ROOM_INIS = 2; 77Constant START_TIME_INIS = 3; 78Constant DONE_INIS = 4; 79 80{-array:PL::Player::InitialSituation}

Initialise Memory Rule.

This rule amalgamates some minimal initialisations which all need to happen before we can risk using some of the more exotic I7 kinds:

(a) The language definition might call for initialisation, although the default language of play (English) does not.

(b) A handful of variables are filled in. I7_LOOKMODE is a constant created by the use options "use full-length room descriptions" or "use abbreviated room descriptions", but otherwise not existing. It is particularly important that player have the correct value, as the process of initialising the memory heap uses the player as the presumed actor when creating memory representations of literal stored actions where no actor was specified; this is why player is initialised here and not in the "position player in model world rule" below. The other interesting point here is that we explicitly set location and real_location to nothing, which is certainly incorrect, even though we know better. We do this so that the "update chronological records rule" cannot see where the player is: see the Standard Rules for an explanation of why this is, albeit perhaps dubiously, a good thing.

(c) We start the machinery needed to check that property accesses are valid during play.

(d) And we initialise the memory allocation heap, and expand the literal constants, as hinted above: these are called "block constants" since they occupy blocks of memory.

The not_yet_in_play flag, which is cleared when the first command is about to be read from the keyboard, suppresses the standard status line text: thus, if there is some long text to read before the player finds out where he is, the surprise will not be spoiled.

116[ INITIALISE_MEMORY_R; 117    #ifdef TARGET_GLULX; VM_PreInitialise(); #Endif; 118    #Ifdef LanguageInitialise; LanguageInitialise(); #Endif; 119 120    not_yet_in_play = true; 121    #ifdef I7_LOOKMODE; lookmode = I7_LOOKMODE; #endif; 122    player = InitialSituation-->PLAYER_OBJECT_INIS; 123    the_time = InitialSituation-->START_TIME_INIS; 124    real_location = nothing; 125    location = nothing; 126 127    CreatePropertyOffsets(); 128    HeapInitialise(); ! Create a completely unused memory allocation heap 129    StackFramingInitialise(); ! Create an empty stack 130    CreateDynamicRelations(); ! Create relation structures on the heap 131 132    rfalse; 133];

Seed Random Number Generator Rule.

Unless a seed is provided by NI, and it won't be for released story files, the VM's interpreter is supposed to start up with a good seed in its random number generator: something usually derived from, say, the milliseconds part of the current time of day, which is unlikely to repeat or show any pattern in real-world use. However, early Z-machine interpreters often did this quite badly, starting with poor seed values which meant that the first few random numbers always had something in common (being fairly small in their range, for instance). To obviate this we extract and throw away 100 random numbers to get the generator going, shaking out more obvious early patterns, but that cannot really help much if the VM interpreter's RNG is badly written. "Anyone who considers arithmetical methods of producing random digits is, of course, in a state of sin" (von Neumann).

150[ SEED_RANDOM_NUMBER_GENERATOR_R i; 151    if ({-value:rng_seed_at_start_of_play}) VM_Seed_RNG({-value:rng_seed_at_start_of_play}); 152    for (i=1: i<=100: i++) random(i); 153    rfalse; 154];

Position Player In Model World Rule.

This seems as good a place as any to write down the invariant we attempt to maintain for the player's position variables:

(1) The player variable is the object through which the player plays, which is always a person: its value is always set by starting from selfobj and then making a sequence of 0 or more ChangePlayer(new_value) calls. (This enables us to make sure it has the correct property values for printed name and so forth.)

(2) The location is always either the current room, a valid I7 room, or thedark, which is not a valid I7 object but is distinguishable both from all I7 objects and from nothing. The real_location is always the current room, which is always a valid I7 room. location equals thedark if and only if the player does not have light to see by; the routine SilentlyConsiderLight updates this without printing any messages to announce the fall or lifting of darkness (hence "silently").

(3) The player object is always in the subtree of real_location, and is always in a chain O1 < O2 < ... < On where O1 is the player, On is real_location, n ≥ 2 and O2, ..., On-1 are all either enterable containers, enterable supporters, or component parts of such. The routine LocationOf, applied to the player object, always agrees with real_location.

(4) "Floating" objects, such as backdrops and two-sided doors, are in theory present in more than one room at once. In practice they can only be in a single position in the I6 object tree at any one time. The rule is that if they are theoretically present in the real_location, then they are actually present in the subtree of real_location. The routine MoveFloatingObjects updates this, and must be called whenever the player moves from one room to another.

(5) Any objects carried by the player have the I6 moved attribute set. The SACK_OBJECT variable is always set to the object of kind "player's holdall" which the player has most recently been carrying, or had as a component part of himself. The "note object acquisitions" rule updates this.

These invariants are usually all false before the following rule is executed; they are all true once it has completed. In addition, because the global action variables usually hold details of the action most recently carried out, we initialise these as if the most recent action had been the player waiting. (Nobody ought to use these variables at this point, but in case they do use them by accident in a "when play begins" rule, we want Inform to behave predictably and without type-unsafe values entering code.)

204[ POSITION_PLAYER_IN_MODEL_R player_to_be; 205 206    player = selfobj; 207    player_to_be = InitialSituation-->PLAYER_OBJECT_INIS; 208     209    location = LocationOf(player_to_be); 210    if (location == 0) { 211        location = InitialSituation-->START_ROOM_INIS; 212        if (InitialSituation-->START_OBJECT_INIS) 213            move player_to_be to InitialSituation-->START_OBJECT_INIS; 214        else move player_to_be to location; 215    } 216 217    if (player_to_be ~= player) { remove selfobj; ChangePlayer(player_to_be); } 218    else { real_location = location; SilentlyConsiderLight(); } 219 220    NOTE_OBJECT_ACQUISITIONS_R(); MoveFloatingObjects(); 221     222    actor = player; act_requester = nothing; actors_location = real_location; action = ##Wait; 223 224    InitialSituation-->DONE_INIS = true; 225    rfalse; 226];

Parse Command Rule.

This section contains only two primitive rules from the turn sequence rulebook, the matched pair of the "parse command rule" and the "generate action rule"; the others are found in the sections on Light and Time.

We use almost identically the same parser as that in the I6 library, since it is a well-proven and understood algorithm. The I6 parser returns some of its results in a supplied array (here parser_results, though the I6 library used to call this inputobjs), but others are in global variables:

(1) The parser_results array holds four words, used as indexed by the constants below.

(-a) The action can be a valid I6 action number, or an I6 "fake action", a concept not used overtly in I7. Most valid I6 actions correspond exactly to I7 actions, but in principle it is possible to define (say) extra debugging commands entirely at the I6 level.

(-b) The count NO_INPS_PRES is always 0, 1 or 2, and then that many of the next two words are meaningful.

(-c) Each of the "inp" values is either 0, meaning "put the multiple object list here"; or 1, meaning "not an object but a value"; or a valid I6 object. (We use the scoping rules to ensure that any I6 object visible to the parser is also a valid I7 object, so – unlike with actions – we need not distinguish between the two.)

(2) The global variable actor is set to the person asked to carry out the command, or is the same as player if nobody was mentioned. Thus it will be the object for Floyd in the command FLOYD, GET PERMIT, but will be just player in the command EAST.

(3) The global variables special_number1 and, if necessary, special_number2 hold values corresponding to the first and second of the "inps" to be returned as 1. Thus, if one of the "inps" is a value and the other is an object, then special_number1 is that value; only if both are values rather than objects will special_number2 be used. There is no indication of the kind of these values: I6 is typeless.

(4) At most one of the "inps" is permitted to be 1, referring to a multiple object list. (And a multiple value list is forbidden.) If this happens, the list of objects is stored in an I6 table array (i.e., with the 0th word being the number of subsequent words) called multiple_object, and the parser will have set the toomany_flag if an overflow occurred – that is, if the list was truncated because it originally called for more than 63 objects.

(5) The global variable meta is set if the action is one marked as such in the I6 grammar. A confusion in the design of I6 is that being out of world, as we would say in I7 terms, is associated not with an action as such but with the command verb triggering it. (This in practice caused no trouble since we never used, say, the word SAVE for both saving the game and saving, I don't know, box top coupons.) The state of meta returned by the I6 parser does not quite correspond to I7's "out of world" concept, so we will alter it in a few cases.

Some of these conventions are a little odd-looking now: why not simply have a larger results array, rather than this pile of occasionally used variables? The reasons are purely historical: the I6 parser developed gradually over about a decade.

291Constant ACTION_PRES = 0; 292Constant NO_INPS_PRES = 1; 293Constant INP1_PRES = 2; 294Constant INP2_PRES = 3; ! Parser.i6t code assumes this is INP1_PRES + 1 295 296[ PARSE_COMMAND_R; 297    if (EarlyInTurnSequence == false) rfalse; ! Prevent use outside top level 298    not_yet_in_play = false; 299 300    Parser__parse(); 301    TreatParserResults(); 302    rfalse; 303];

Treat Parser Results.

We don't quite use the results exactly as they are returned by the parser: we make modifications in a few special cases.

(1) ##MistakeAction is a valid I6 action, but not an I7 one. It is used to implement "Understand ... as a mistake", which provides a short-cut way for I7 source text to specify responses to mistaken guesses at the syntax expected for commands. It can therefore result from a whole variety of different commands, some of which might be flagged meta, others not. We forcibly set the meta flag: a mistake in guessing the command always happens out of world.

(2) A command in the form PERSON, TELL ME ABOUT SOMETHING is altered to the action resulting from ASK PERSON ABOUT SOMETHING, so that ##Tell is converted to ##Ask in these cases.

322[ TreatParserResults; 323    if (parser_results-->ACTION_PRES == ##MistakeAction) meta = true; 324 325    if (parser_results-->ACTION_PRES == ##Tell && 326        parser_results-->INP1_PRES == player && actor ~= player) { 327        parser_results-->ACTION_PRES = ##Ask; 328        parser_results-->INP1_PRES = actor; actor = player; 329    } 330];

Generate Action Rule.

For what are, again, historical reasons to do with the development of I6, the current action is recorded in a slate of global variables:

(1) actor is as above; action is the I6 action number or fake action number, though in I7 usage no fake actions should ever reach this point.

(2) act_requester is the person requesting that the actor should perform the action, or nothing if the action is the actor's own choice. In the command FLOYD, MOP FLOOR, the act_requester is the player and the actor is Floyd; but when an action arises from a try phrase in I7, such as "try Floyd mopping the floor", act_requester is nothing because it is Floyd's own decision to do this. (The computer, of course, represents the will-power of all characters other than the player.)

(3) inp1 and inp2 are global variables whose contents mean the same as those of parser_results-->INP1_PRES and parser_results-->INP2_PRES. (This is not duplication, because actions also arise from "try" rather than the parser.)

(4) The variable multiflag is set during the processing of a multiple object list, and clear otherwise. (It is used for instance by the Standard Rules to give more concise reports of some successful actions.) Note that it remains set during any knock-on actions caused by actions in the multiple object list: the following rule is the only place where multiflag is set or cleared.

(5) noun and second are global variables which are equal to inp1 and inp2 when the latter hold valid object numbers, and are equal to nothing otherwise. (This is not duplication either, because it provides us with type-safe access to objects: there is no KOV which can safely represent inp1 and inp2, but noun and second are valid for the I7 kind of value "object".)

In the following rule, we create this set of variables for the action or multiple action(s) suggested by the parser: each action is sent on to BeginAction for processing. Once done, we reset the above variables in what might seem an odd way: we allow straightforward actions by the player to remain in the variables, but convert requests to other people to the neutral "waiting" action carried out by the player (which is the zero value for actions). Now, in a better world, we would always erase the action like this, because an action once completed ought to be forgotten. The value of noun ought to be visible only during the action's processing.

But in practice many I7 users write "every turn" rules which are predicated on what the turn's main action was: say, "Every turn when going: ..." The every turn stage is not until later in the turn sequence, so such rules can only work if we keep the main parser-generated action of the turn in the action variables when we finish up here: so that's what we do. (Note that BeginAction preserves the values of the action variables, storing copies on the stack, so whatever may have happened during processing, we finish this routine with the same action variable values that we set at the beginning.)

Finally, note that an out of world action stops the turn sequence early, at the end of action generation: this is what prevents the time of day advancing, every turn rules from firing, and so forth – see the Standard Rules.

391[ GENERATE_ACTION_R i; 392    if (EarlyInTurnSequence == false) rfalse; ! Prevent use outside top level 393    EarlyInTurnSequence = false; 394 395    action = parser_results-->ACTION_PRES; 396    act_requester = nothing; if (actor ~= player) act_requester = player; 397 398    inp1 = 0; inp2 = 0; multiflag = false; 399    if (parser_results-->NO_INPS_PRES >= 1) { 400        inp1 = parser_results-->INP1_PRES; if (inp1 == 0) multiflag = true; 401    } 402    if (parser_results-->NO_INPS_PRES >= 2) { 403        inp2 = parser_results-->INP2_PRES; if (inp2 == 0) multiflag = true; 404    } 405 406    if (inp1 == 1) noun = nothing; else noun = inp1; 407    if (inp2 == 1) second = nothing; else second = inp2; 408 409    if (multiflag) { 410        if (multiple_object-->0 == 0) { 411            if (actor == player) { GENERATE_ACTION_RM('B'); new_line; } 412            return; 413        } 414        if (toomany_flag) { 415            toomany_flag = false; 416            if (actor == player) { GENERATE_ACTION_RM('A'); } 417        } 418        i = multiple_object-->0; 419        FollowRulebook(MULTIPLE_ACTION_PROCESSING_RB); 420        if ((multiple_object-->0 == 1) && (i > 1)) { 421            multiflag = false; 422            if (inp1 == 0) noun = multiple_object-->1; 423            if ((inp2 == 0) && (parser_results-->NO_INPS_PRES >= 2)) 424                second = multiple_object-->1; 425        } 426        if (multiple_object-->0 == 0) rfalse; 427    } 428     429    if (multiflag) { 430        GenerateMultipleActions(); 431        multiflag = false; 432    } else BeginAction(action, noun, second); 433 434    if ((actor ~= player) || (act_requester)) action = ##Wait; 435    actor = player; act_requester = 0; 436 437    if (meta) { RulebookSucceeds(); rtrue; } 438    rfalse; 439];

Generate Multiple Actions.

So this routine is used to issue the individual actions necessary when a multiple object list has been supplied as either the noun or second noun part of an action generated by the parser. Note that we stop processing the list in the event of the game ending, or of the location variable changing its value, which can happen either through movement of the player, or through passage from darkness to light or vice versa.

We use RunParagraphOn to omit skipped lines as paragraph breaks between the results from any item in the list: this is both more condensed on screen in ordinary lists, and might allow the user to play tricks such as gathering up reports from a list and delivering them later in some processed way.

455[ GenerateMultipleActions initial_location k item; 456    initial_location = location; 457    for (k=1: k<=multiple_object-->0: k++) { 458        item = multiple_object-->k; 459        RunParagraphOn(); 460        if (inp1 == 0) { inp1 = item; BeginAction(action, item, second, item); inp1 = 0; } 461        else { inp2 = item; BeginAction(action, noun, item, item); inp2 = 0; } 462        if (deadflag) return; 463        if (location ~= initial_location) { 464            if (player == actor) { ACTION_PROCESSING_INTERNAL_RM('J'); new_line; } 465            return; 466        } 467    } 468];

Timed Events Rule.

A timed event is a rule stored in the TimedEventsTable, an I6 table array: zero entries in this table are ignored, and the sequence is significant only if more than one event goes off at the same moment, in which case earlier entries go off first. Each rule in the table has a corresponding timer value in TimedEventTimesTable. If this is negative, it represents a number of turns to go before the event happens – or properly speaking, the number of times the timed events rule is invoked. Otherwise the timer value must be a valid time of day at which the event happens (note that valid times are all non-negative integers). We allow a bracket of 30 minutes after the event time proper; this is designed to cope with situations in which the user sets some timed events, then advances the clock by hand (or uses a long step time, say in which each turn equates to 20 minutes).

Because an event is struck out of the table just before it is fired, it will not continue to go off the rest of the half-hour. Moreover, because the striking out happens before rather than after the rule fires, a rule can re-time itself to go off again later, somewhat like the snooze feature on an alarm clock, without the risk of it going off again immediately in the same use of the timed events rule: there is guaranteed to be a blank slot in the timer array at or before the current position because we have just blanked one.

494[ TIMED_EVENTS_R i d event_timer fire rule; 495    for (i=1: i<=(TimedEventsTable-->0): i++) 496        if ((rule=TimedEventsTable-->i) ~= 0) { 497            event_timer = TimedEventTimesTable-->i; fire = false; 498            if (event_timer<0) { 499                (TimedEventTimesTable-->i)++; 500                if (TimedEventTimesTable-->i == 0) fire = true; 501            } else { 502                d = (the_time - event_timer + TWENTY_FOUR_HOURS) % TWENTY_FOUR_HOURS; 503                if ((d >= 0) && (d < 30)) fire = true; 504            } 505            if (fire) { 506                TimedEventsTable-->i = 0; 507                FollowRulebook(rule); 508            } 509        } 510    rfalse; 511];

Setting Timed Events.

This is the corresponding routine which adds events to the timer tables, and is used to define phrases like "the cuckoo clock explodes in 7 turns from now" or "the cuckoo clock explodes at 4 PM". Here the rule would be "cuckoo clock explodes", and the event_time would either be 4 PM with absolute_time set, or simply 7 with absolute_time clear.

Note that the same event can occur only once in the timer tables: a new setting for its firing overwrites an old one. (This ensures that the table does not slowly balloon in size if the user has not been careful to ensure that events always fire.)

526[ SetTimedEvent rule event_time absolute_time i b; 527    for (i=1: i<=(TimedEventsTable-->0): i++) { 528        if (rule == TimedEventsTable-->i) { b=i; break; } 529        if ((b==0) && (TimedEventsTable-->i == 0)) b=i; 530    } 531    if (b==0) return RunTimeProblem(RTP_TOOMANYEVENTS); 532    TimedEventsTable-->b = rule; 533    if (absolute_time) TimedEventTimesTable-->b = event_time; 534    else TimedEventTimesTable-->b = -event_time; 535];

Setting Time Of Day.

This is the old I6 library routine SetTime, which is no longer used in I7 at present; but might be, some day.

542Global time_step; 543 544[ SetTime t s; 545    the_time = t; time_rate = s; time_step = 0; 546    if (s < 0) time_step = 0-s; 547];

Advance Time Rule.

This rule advances the two measures of the passing of time: the number of turns of play, and the_time of day.

554[ ADVANCE_TIME_R; 555    turns++; 556    if (the_time ~= NULL) { 557        if (time_rate >= 0) the_time = the_time+time_rate; 558        else { 559            time_step--; 560            if (time_step == 0) { 561                the_time++; 562                time_step = -time_rate; 563            } 564        } 565        the_time = the_time % TWENTY_FOUR_HOURS; 566    } 567    rfalse; 568];

Note Object Acquisitions Rule.

See the Standard Rules for comment on this.

574[ NOTE_OBJECT_ACQUISITIONS_R obj; 575    objectloop (obj in player) give obj moved; 576    objectloop (obj has concealed) 577        if (IndirectlyContains(player, obj)) give obj ~concealed; 578    #Ifdef RUCKSACK_CLASS; 579    objectloop (obj in player) 580        if (obj ofclass RUCKSACK_CLASS) 581            SACK_OBJECT = obj; 582    objectloop (obj ofclass RUCKSACK_CLASS && obj provides component_parent 583        && obj.component_parent == player) 584        SACK_OBJECT = obj; 585    #Endif; 586    rfalse; 587];

Resurrect Player If Asked Rule.

If a rule in the "when play ends" rulebook set resurrect_please, by executing the "resume the game" phrase, then this is where we notice that: making the shutdown rulebook succeed then tells Main to fall back into the turn sequence.

596[ RESURRECT_PLAYER_IF_ASKED_R; 597    if (resurrect_please) { 598        RulebookSucceeds(); resurrect_please = false; 599        deadflag = 0; story_complete = false; rtrue; 600    } 601    rfalse; 602];

Ask The Final Question Rule.

And so we come to the bittersweet end: we ask the final question endlessly, until the player gives a reply which takes drastic enough action to destroy the current execution context in the VM, for instance by typing QUIT, RESTART, UNDO or RESTORE. The question and answer are all managed by the activity, which is defined in I7 source text in the Standard Rules.

612[ ASK_FINAL_QUESTION_R; 613    print "^"; 614    while (true) { 615        CarryOutActivity(DEALING_WITH_FINAL_QUESTION_ACT); 616        DivideParagraphPoint(); 617        if (resurrect_please) rtrue; 618    } 619];

Read The Final Answer Rule.

This erases the current command, so is a technique we couldn't use during actual play, but here commands are but a distant memory. So we can use the same buffers for the final question as for game commands.

627[ READ_FINAL_ANSWER_R; 628    DrawStatusLine(); 629    KeyboardPrimitive(buffer, parse); 630    players_command = 100 + WordCount(); 631    num_words = WordCount(); 632    wn = 1; 633    rfalse; 634];

Immediately Restart VM Rule.

Now for four rules acting on typical responses to the final question.

640[ IMMEDIATELY_RESTART_VM_R; @restart; ];

Immediately Restore Saved Game Rule.

It is almost certainly unnecessary to set actor to player here, but we do so just in case, because RESTORE_THE_GAME_R is protected against doing anything when it thinks it might have been called erroneously through a command like "DAPHNE, RESTORE". (Out of world actions should never be carried out that way, but again, it's a precaution.)

650[ IMMEDIATELY_RESTORE_SAVED_R; actor = player; RESTORE_THE_GAME_R(); ];

Immediately Quit Rule.

655[ IMMEDIATELY_QUIT_R; @quit; ];

Immediately Undo Rule.

660[ IMMEDIATELY_UNDO_R; Perform_Undo(); ];

Print Obituary Headline Rule.

Finally, definitions of three primitive rules for the "printing the player's obituary" activity.

667[ PRINT_OBITUARY_HEADLINE_R; 668    print "^^ "; 669    VM_Style(ALERT_VMSTY); 670    print "***"; 671    if (deadflag == 1) PRINT_OBITUARY_HEADLINE_RM('A'); 672    if (deadflag == 2) PRINT_OBITUARY_HEADLINE_RM('B'); 673    if (deadflag == 3) PRINT_OBITUARY_HEADLINE_RM('C'); 674    if (deadflag ~= 0 or 1 or 2 or 3) { 675        print " "; 676        TEXT_TY_Say(deadflag); 677        print " "; 678    } 679    print "***"; 680    VM_Style(NORMAL_VMSTY); 681    print "^^"; #Ifndef NO_SCORING; print "^"; #Endif; 682    rfalse; 683];

Print Final Score Rule.

688[ PRINT_FINAL_SCORE_R; 689    #Iftrue USE_SCORING ~= 0; ANNOUNCE_SCORE_R(); #Endif; 690    rfalse; 691];

Display Final Status Line Rule.

696[ DISPLAY_FINAL_STATUS_LINE_R; 697    sline1 = score; sline2 = turns; 698    rfalse; 699];