The turn sequence and action-processing rules

I’ve been sitting on this for a long time but never finished writing up the Parser so I never posted it. But what the heck, the rest of it is still useful. It’s for 10.1 (I had written it for the current dev version of Inform, but have, I hope, edited out the small number of differences).

The Turn Sequence

1. Parse command rule
   Parser__parse (Parser.i6t)
     if EarlyInTurnSequence is true, return immediately (doing nothing)
     begin the reading a command activity
       Before reading a command
       For reading a command (if present, *or*...)
       Keyboard (in Parser.i6t)
       PrintPrompt (prints command prompt)
       DrawStatusLine (you guessed it)
       KeyboardPrimitive
         TestKeyboardPrimitive (if we're running, e.g., TEST ME, or...)
         VM_ReadKeyboard (Glulx.i6t *or* ZMachine.i6t)
           VM_Tokenise (split command into tokens)
         handle Oops
         handle Undo
     [ one of:
     - generate a parser error and loop back to top of parser loop
     - ask a disambiguation question
     - accept command and identify action (stored in parser_results)
     [...]

2. declare everything initially unmentioned (unmention *all* the things!)
3. generate action rule (GENERATE_ACTION_R in OrderOfPlay.i6t)
     if EarlyInTurnSequence is true, return immediately (doing nothing)
     set EarlyInTurnSequence to false
     assign the basic action globals
     if this is a multiple action
       follow multiple action processing rules
       GenerateMultipleActions (OrderOfPlay.i6t)
         loop through multiple object list, calling BeginAction with each
     otherwise, if singleton action
       BeginAction (Actions.i6t)
         ChronologyPoint (Chronology.i6t)
         save existing action variables
         ActionPrimitive (Actions.i6t)
           Setting action variables rulebook
           if out of world action,
              descend to specific action rule (see below)
           Follow action processing rulebook (see separate chart)
         restore previous action variables
         call TrackActions to update `if we have...`
     if out of world action, rule succeeds [ skip rest of turn sequence and start again from the top ]
   [ beyond this point, we should no longer count on previous values of current action, noun, etc ]
4. early scene changing stage rule
     follow the scene changing rules
       follow appropriate when <scene> begins, when <scene> ends rulebooks
5. every turn stage rule
     follow the every turn rules
6. timed events rule (e.g., `at 9:15 am:`, `at the time when...`)
7. advance time rule (increment time, turn count)
8. update chronological records rule
     loop through past tense conditions, testing them
     ChronologyPoint
9. late scene changing rule
     follow the scene changing rules
       follow appropriate when <scene> begins, when <scene> ends rulebooks
10. adjust light rule
11. note object acquisitions rule (any undescribed things enclosed by player become described)
12. notify score changes rule

Action Processing

tl; dr: saying that it goes Before, Instead, Check, Carry Out, After, Report elides some things:

  • before
  • visibility, accessibility, carrying requirements
  • instead
  • persuasion
  • check
  • carry out
  • after
  • report
action processing rules
  announce items from multiple object lists rule
  set pronouns from items from multiple object lists rule
  before stage rule
    follow the before rules
  basic visibility rule (Actions.i6t)
    if action requires light and actor is the player (NPCs don't need light)
      follow the visibility rules
        can't act in the dark rule (standard rules) [only rule in rulebook]
          if in darkness, rule succeeds
      if rulebook succeeds (success/failure is backwards of what you might expect)
        carry out the refusal to act in darkness activity
        stop the action
  basic accessibility rule (Actions.i6t)
    if the action requires a touchable noun
      if the noun is a direction, stop the action in failure
      otherwise
         test touchability (ObjectIsUntouchable in Light.i6t)
           follow the accessibility rules (Standard Rules)
             access through barriers rule (access_through_barriers_r in Light.i6t ) [only rule in rulebook]
               find the common ancestor of the actor and noun
               if there's a barrier between actor and common ancestor
                 follow the reaching outside rules
                   can't reach outside closed containers rule
                     if actor's in closed container, stop the action in failure
               if there's a barrier between noun and common ancestor
                 follow the reaching inside rules
                   can't reach inside closed containers rule
                     if noun's in closed container, stop the action in failure
    if the action requires a touchable second noun, proceed as above for the second noun
  carrying requirements rule
    if the action requires a carried noun and the actor isn't carrying it
      carry out the implicitly taking activity with the noun
        for implicitly taking
          standard implicit taking rule
      if player *still* isn't carrying it, stop the action in failure
    if the action requires a carried second noun and the actor isn't carrying it
      proceed as above for the second noun
  instead stage rule
    follow the instead rules
    (if an instead rule applies, unless it explicitly *continues the action*, stop the action in failure)
  requested actions require persuasion rule (Actions.i6t)
    if player is asking someone to do something
      follow the persuasion rules
      unless a persuasion rule succeeds, stop the action in failure ("<npc> has better things to do.")
  carry out requested actions rule (Actions.i6t)
    if this as an asking someone to action and persuasion succeeded
      store value of act requester (on stack)
      set act requester to nothing (converts this to a regular, unrequested action with an NPC actor)
      call BeginAction for this new action
      if deadflag is false and the action failed (the rule as written says action failed or it's out of world, but actionPrimitive would already have failed it if the player had asked someone to do an out-of-world action)
        follow the unsuccessful attempt by rules
        unless an unsuccessful attempt by rule succeeds, output "<npc> is unable to do that"
      restore previous value of act requester (from stack), i.e., we convert back to the original `ask someone to...` action with player as actor
      follow the after rules *for the `ask someone to...` action*
      (*don't* try to follow report rules, because `ask someone to...` can't have them)
      stop the action *in success*, regardless of whether the NPC's action succeeded: `ask someone to...` succeeded
  descend to specific action-processing rule (Glulx.i6t or Zmachine.i6t)
    work out details of specific action rule (creates some data structures)
    investigate player's awareness before action rule
      follow the player's action awareness rules
    check stage rule
      specific action check rules
        (typically, test conditions and stop the action in failure if condition isn't met, silently for NPCs or with a message for players. Sometimes, try
        implicit actions and stop the action in failure if that action fails.)
    carry out stage rule
      specific action carry out rules
    after stage rule
      unless it's an out-of-world action
        follow after rules
        (if an after rule applies, unless the rule explicitly *continues* the action, stop the action in success)
    investigate player's awareness after action rule (even for out-of-world)
      follow the player's action awareness rules;
    report stage rule
      if awareness after action succeded,
        follow the specific report rules
    default action success rule
      action succeeds (the end for out-of-world actions)
  end action-processing in success
    action succeeds

Startup

initialise memory rule
virtual machine startup rule
seed random number generator rule
update chronological records rule
declare everything initially unmentioned rule
position player in model world rule
start in the correct scenes rule
  follow the scene changing rules
when play begins stage rule
  follow the when play begins rulebook
fix baseline scoring rule
display banner rule 
initial room description rule
  try looking

Main

[ the Main function in OrderOfPlay.i6t ]

Follow the Startup rules
loop forever (until proven otherwise)
  while deadflag is 0 [ we haven't encountered an `end the story` phrase ]
    set EarlyInTurnSequence to true
    initialize action variables
    follow the Turn Sequence rules
    [ we *did* encounter `end the story`... ]
  follow the Shutdown rules
    When Play Ends Stage rule
      follow the When Play Ends rules
      Resurrect Player If Asked rule
        if `resurrect_please` is true [ a rule invoked `resume the story` ]
          set deadflag to 0
          set resurrect_please to false
          stop the Shutdown rulebook (...returning to looping forever)
      Print Player's Obituary rule
        carry out the Printing the Player's Obituary activity
      Ask the Final Question rule
        carry out the Handling the Final Question Activity
        if resurrect_please is true, return true
  unless the Shutdown Rules returned true, break out of loop (ending game)

An I6 quit statement ends things immediately. This is what happens with the quit command (if you say yes to its confirmation question) or when the immediately quit rule is followed (which is what happens if you choose “quit” during the handling the final question activity).

end the story sets an I6 global called deadflag. resume the story sets an I6 global called resurrect_please but doesn’t alter deadflag So if an author adds a Final Question Option that invokes resume the story (and a player chooses it) we don’t dive right back into the loop that follows the turn sequence rules because deadflag is still non-zero: we end up following the Shutdown rules again and thus follow the When Play Ends rules again. When we get back to the Resurrect Player If Asked rule, deadflag is restored and, so, we end up following the turn sequence again.

7 Likes

The startup rules have been overhauled after version 10. It won’t make a difference to most authors, but will be more flexible for Glk extension authors.

3 Likes

I modified the original to mention EarlyInTurnSequence. OrderOfPlay.i6t tells us:

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.

When it says “explained in the Standard Rules”, it means explained in the comments on the turn sequence in the literate source that generates the Standard Rules.

An unusual point here is that the “parse command rule” and the “generate action rule” are written such that they do nothing unless the turn sequence rulebook is being followed at the top level (by Main, that is). This prevents them from being used recursively, which would not work properly, and enables a popular trick from the time before the 2008 reform to keep working: we can simulate six turns going by in which the player does nothing by running “follow the turn sequence rules” six times in a row. Everything happens exactly as it should — the turn count, the time of day, timed events, and so on — except that no commands are read and no consequent actions generated.

I hadn’t been aware of this idiom, though it’s used in the 9 AM Appointment and Delayed Gratification examples. It’s sort of the opposite of acting fast in the Timeless example. In Timeless, an action happens, but the rest of the turn sequence is aborted. In these, an action happens but in the middle of that action, during its carry out rules, it runs through the whole turn sequence (except that the parse command and generate action rules are skipped), ultimately running (conventionally) through the post-action part of the turn sequence as it concludes the original command.

2 Likes