From 4381288eeb792adb3672ef747013c02815fa760a Mon Sep 17 00:00:00 2001 From: Glenn Hutchings Date: Fri, 15 Jul 2016 18:14:32 +0100 Subject: [PATCH] Add a bunch of autogenerated index entries. --- about.rst | 16 + appendices/c.rst | 8 + appendices/e.rst | 13 +- appendices/f.rst | 425 +++++++++++++------------ chapters/02.rst | 21 ++ chapters/03.rst | 36 +++ chapters/04.rst | 12 + chapters/05.rst | 166 ++++++---- chapters/06.rst | 51 ++- chapters/07.rst | 91 ++++-- chapters/08.rst | 80 +++-- chapters/09.rst | 250 ++++++++++----- chapters/10.rst | 525 +++++++++++++++--------------- chapters/11.rst | 754 ++++++++++++++++++++++++-------------------- chapters/12.rst | 506 +++++++++++++++-------------- chapters/13.rst | 700 ++++++++++++++++++++-------------------- chapters/14.rst | 18 ++ chapters/15.rst | 12 + chapters/16.rst | 15 + tools/Makefile | 24 ++ tools/autoindex.cfg | 29 ++ tools/autoindex.py | 216 +++++++++++++ 22 files changed, 2388 insertions(+), 1580 deletions(-) create mode 100644 tools/Makefile create mode 100644 tools/autoindex.cfg create mode 100644 tools/autoindex.py diff --git a/about.rst b/about.rst index aa6f5c9..0cbd9d3 100644 --- a/about.rst +++ b/about.rst @@ -30,6 +30,10 @@ and writing tutorials and reviews, these enthusiasts have led a revival responsible for many notable works, including some whose quality arguably surpasses that of the best commercial titles of the 1980s. +.. Generated by autoindex +.. index:: + single: TADS + Nowadays, IF is a hobby; almost everything that you need to begin writing your own text adventures is available, for free, on the Internet. While expert programmers may relish the considerable challenge of creating @@ -146,6 +150,10 @@ locations: * http://mirror.ifarchive.org/ + .. Generated by autoindex + .. index:: + single: IF Archive + The IF Archive (or actually a high-speed mirror copy of it), from which you can download almost anything that's free and in the public domain. For a clickable map of Inform-related parts of the Archive, see @@ -170,6 +178,10 @@ locations: * :newsgroup:`rec.arts.int-fiction` + .. Generated by autoindex + .. index:: + single: RAIF + The Usenet newsgroup for authors of IF, commonly known by the abbreviation RAIF. Here you'll find discussion on IF technology, criticism and game design issues, and fast, friendly and knowledgeable @@ -178,6 +190,10 @@ locations: * :newsgroup:`rec.games.int-fiction` + .. Generated by autoindex + .. index:: + single: RGIF + The complementary newsgroup for IF *players*, often known as RGIF. .. todo:: diff --git a/appendices/c.rst b/appendices/c.rst index b3277c2..bbd896d 100644 --- a/appendices/c.rst +++ b/appendices/c.rst @@ -93,6 +93,10 @@ you do, though, you'll get this:: ** The room "(street)" has no "description" property ** > +.. Generated by autoindex +.. index:: + pair: description; library property + Whoops! We've fallen foul of Inform's rule saying that every room must have a :prop:`description` property, to be displayed by the interpreter when you enter that room. Our ``street`` stub hasn't got a :prop:`description`, @@ -133,6 +137,10 @@ description is sufficient (and less trouble):: > +.. Generated by autoindex +.. index:: + pair: name; library property + You'll notice a couple of interesting points. Because we didn't supply external names with our ``street`` , ``bow`` and ``quiver`` stubs, the compiler has provided some for us -- ``(street)`` , ``(bow)`` and diff --git a/appendices/e.rst b/appendices/e.rst index 31d81bd..3f48f9e 100644 --- a/appendices/e.rst +++ b/appendices/e.rst @@ -68,9 +68,16 @@ to zero: | `Constant {const_id}`; | `Constant {const_id} = {expr}`; -Standard constants are `true` (1), `false` (0) and `nothing` (0), also -`NULL` (-1). Additionally, `WORDSIZE` is the number of bytes in a storage -word: 2 for the Z-machine, 4 for Glulx. +.. Generated by autoindex +.. index:: + pair: NULL; library constant + pair: false; library constant + pair: nothing; library constant + pair: true; library constant + +Standard constants are :const:`true` (1), :const:`false` (0) and +:const:`nothing` (0), also :const:`NULL` (-1). Additionally, `WORDSIZE` is +the number of bytes in a storage word: 2 for the Z-machine, 4 for Glulx. To define a constant (unless it already exists): diff --git a/appendices/f.rst b/appendices/f.rst index dd513c0..c33083e 100644 --- a/appendices/f.rst +++ b/appendices/f.rst @@ -2,7 +2,7 @@ Appendix F -- Inform library ============================== -.. |ADD| replace:: **+** +.. |ADD| replace:: **(+)** .. The ⊕ symbol doesn't work in LaTeX. @@ -21,12 +21,18 @@ properties and attributes, the verb grammars and actions. Library objects =============== +.. index:: + pair: compass; library object + :obj:`compass` A :attr:`container` object holding the twelve direction objects :obj:`d_obj`, :obj:`e_obj`, :obj:`in_obj`, :obj:`n_obj`, :obj:`ne_obj`, :obj:`nw_obj`, :obj:`out_obj`, :obj:`s_obj`, :obj:`se_obj`, :obj:`sw_obj`, :obj:`u_obj`, :obj:`w_obj`. +.. index:: + pair: LibraryMessages; library object + :obj:`LibraryMessages` If defined (between Includes of `Parser` and `VerbLib`), changes standard library messages: @@ -44,10 +50,16 @@ Library objects | `...` | `];` +.. index:: + pair: selfobj; library object + :obj:`selfobj` The default player object. Avoid: use instead the :var:`player` variable, which usually refers to :obj:`selfobj`. +.. index:: + pair: thedark; library object + :obj:`thedark` A pseudo-room which becomes the :var:`location` when there is no light (although the player object is not moved there). @@ -344,7 +356,7 @@ Library routines pair: MoveFloatingObjects; library routine `MoveFloatingObjects()` - Adjusts positions of game's `found_in` objects. + Adjusts positions of game's :prop:`found_in` objects. .. index:: pair: NextWord; library routine @@ -666,9 +678,9 @@ Object's value being considered first. pair: description; library property :prop:`description` - For an object: its description (output by `Examine`). + For an object: its description (output by :act:`Examine`). - For a room: its long description (output by `Look`). + For a room: its long description (output by :act:`Look`). The `{value}` can be a string, or a routine which outputs a string. @@ -718,9 +730,9 @@ Object's value being considered first. the :attr:`absent` attribute. The `{value}` can be * a space-separated list of `{rooms}` (where this object can be found) or - `{obj_ids}` (whose locations are tracked by this object); + `{obj_ids}` (whose locations are tracked by this object); * a routine which should return :const:`true` if this object can be found - in the current location, otherwise :const:`false`. + in the current location, otherwise :const:`false`. .. index:: pair: grammar; library property @@ -753,8 +765,8 @@ Object's value being considered first. pair: inside_description; library property :prop:`inside_description` - For an `enterable` object: its description, output as part of the room - description when the player is inside the object. + For an :prop:`enterable` object: its description, output as part of the + room description when the player is inside the object. The `{value}` can be a string, or a routine which outputs a string. @@ -776,14 +788,15 @@ Object's value being considered first. pair: life; library property :prop:`life` |ADD| - For an `animate` object: receives person-to-person actions (`Answer`, - `Ask`, `Attack`, `Give`, `Kiss`, `Order`, `Show`, `Tell`, `ThrowAt` and - `WakeOther`) for which this is the `{noun}`. The `{value}` is a routine - of structure similar to a `switch` statement, having cases for the - appropriate `{actions}` (and an optional default as well). The routine - should return :const:`false` to continue, telling the player what has - happened, or :const:`true` to stop processing the action and produce no - further output. + For an :prop:`animate` object: receives person-to-person actions + (:act:`Answer`, :act:`Ask`, :act:`Attack`, :act:`Give`, :act:`Kiss`, + :act:`Order`, :act:`Show`, :act:`Tell`, :act:`ThrowAt` and + :act:`WakeOther`) for which this is the `{noun}`. The `{value}` is a + routine of structure similar to a `switch` statement, having cases for + the appropriate `{actions}` (and an optional default as well). The + routine should return :const:`false` to continue, telling the player what + has happened, or :const:`true` to stop processing the action and produce + no further output. .. index:: pair: list_together; library property @@ -797,10 +810,10 @@ Object's value being considered first. the string; * a routine which is called twice. On the first call nothing has been output; :var:`inventory_stage` has the value 1, and the routine should - return :const:`false` to continue, or :const::const:`true` to stop - processing and produce no further output. On the second call the list - has been output; :var:`inventory_stage` has the value 2, and there is - no test on the return value. + return :const:`false` to continue, or :const:`true` to stop processing + and produce no further output. On the second call the list has been + output; :var:`inventory_stage` has the value 2, and there is no test on + the return value. .. index:: pair: n_to; library property @@ -943,24 +956,20 @@ Object's value being considered first. .. index:: pair: u_to; library property + pair: w_to; library property :prop:`u_to` See :prop:`d_to`. -.. index:: - pair: w_to; library property - :prop:`w_to` See :prop:`d_to`. .. index:: pair: when_closed; library property + pair: when_open; library property :prop:`when_closed` -.. index:: - pair: when_open; library property - :prop:`when_open` For a :attr:`container` or :attr:`door` object: used when including this object in a room's long description. The `{value}` can be a string, or a @@ -968,12 +977,10 @@ Object's value being considered first. .. index:: pair: when_off; library property + pair: when_on; library property :prop:`when_off` -.. index:: - pair: when_on; library property - :prop:`when_on` For a :attr:`switchable` object: used when including this object in a room's long description. The `{value}` can be a string, or a routine @@ -1213,7 +1220,7 @@ These routines, if you supply them, are called when shown. The player has gone from one dark room to another. `DeathMessage()` - The player has died; `deadflag` is 3 or more. + The player has died; :var:`deadflag` is 3 or more. `GamePostRoutine()` Called after all actions. @@ -1229,7 +1236,7 @@ These routines, if you supply them, are called when shown. Called during parsing. `LookRoutine()` - Called at the end of every `Look` description. + Called at the end of every :act:`Look` description. `NewRoom()` Called when room changes, before description is output. @@ -1266,54 +1273,54 @@ Group 1 actions Group 1 actions support the 'meta' verbs. These are the standard actions and their triggering verbs. -============== ===================================================== -`CommandsOff` "`RECORDING OFF`" -`CommandsOn` "`RECORDING [ON]`" -`CommandsRead` "`REPLAY`" -`FullScore` "`FULLSCORE`", "`FULL [SCORE]`" -`LMode1` "`BRIEF`", "`NORMAL`" -`LMode2` "`LONG`", "`VERBOSE`" -`LMode3` "`SHORT`", "`SUPERBRIEF`" -`NotifyOff` "`NOTIFY OFF`" -`NotifyOn` "`NOTIFY [ON]`" -`Objects` "`OBJECTS`" -`Places` "`PLACES`" -`Pronouns` "`[PRO]NOUNS`" -`Quit` "`DIE`", "`Q[UIT]`" -`Restart` "`RESTART`" -`Restore` "`RESTORE`" -`Save` "`CLOSE`" -`Score` "`SCORE`" -`ScriptOff` "`[TRAN]SCRIPT OFF`", "`NOSCRIPT`", "`UNSCRIPT`" -`ScriptOn` "`[TRAN]SCRIPT [ON]`" -`Verify` "`VERIFY`" -`Version` "`VERSION`" -============== ===================================================== +=================== ================================================== +:act:`CommandsOff` "`RECORDING OFF`" +:act:`CommandsOn` "`RECORDING [ON]`" +:act:`CommandsRead` "`REPLAY`" +:act:`FullScore` "`FULLSCORE`", "`FULL [SCORE]`" +:act:`LMode1` "`BRIEF`", "`NORMAL`" +:act:`LMode2` "`LONG`", "`VERBOSE`" +:act:`LMode3` "`SHORT`", "`SUPERBRIEF`" +:act:`NotifyOff` "`NOTIFY OFF`" +:act:`NotifyOn` "`NOTIFY [ON]`" +:act:`Objects` "`OBJECTS`" +:act:`Places` "`PLACES`" +:act:`Pronouns` "`[PRO]NOUNS`" +:act:`Quit` "`DIE`", "`Q[UIT]`" +:act:`Restart` "`RESTART`" +:act:`Restore` "`RESTORE`" +:act:`Save` "`CLOSE`" +:act:`Score` "`SCORE`" +:act:`ScriptOff` "`[TRAN]SCRIPT OFF`", "`NOSCRIPT`", "`UNSCRIPT`" +:act:`ScriptOn` "`[TRAN]SCRIPT [ON]`" +:act:`Verify` "`VERIFY`" +:act:`Version` "`VERSION`" +=================== ================================================== and the debug tools. -=============== =================================================== -`ActionsOff` "`ACTIONS OFF`" -`ActionsOn` "`ACTIONS [ON]`" -`ChangesOff` "`CHANGES OFF`" -`ChangesOn` "`CHANGES [ON]`" -`Gonear` "`GONEAR`" -`Goto` "`GOTO`" -`Predictable` "`RANDOM`" -`RoutinesOff` "`MESSAGES OFF`", "`ROUTINES OFF`" -`RoutinesOn` "`MESSAGES [ON]`", "`ROUTINES [ON]`" -`Scope` "`SCOPE`" -`Showobj` "`SHOWOBJ`" -`Showverb` "`SHOWVERB`" -`TimersOff` "`DAEMONS OFF`", "`TIMERS OFF`" -`TimersOn` "`DAEMONS [ON]`", "`TIMERS [ON]`" -`TraceLevel` "`TRACE number`" -`TraceOff` "`TRACE OFF`" -`TraceOn` "`TRACE [ON]`" -`XAbstract` "`ABSTRACT`" -`XPurloin` "`PURLOIN`" -`XTree` "`TREE`" -=============== =================================================== +================== =================================================== +:act:`ActionsOff` "`ACTIONS OFF`" +:act:`ActionsOn` "`ACTIONS [ON]`" +:act:`ChangesOff` "`CHANGES OFF`" +:act:`ChangesOn` "`CHANGES [ON]`" +:act:`Gonear` "`GONEAR`" +:act:`Goto` "`GOTO`" +:act:`Predictable` "`RANDOM`" +:act:`RoutinesOff` "`MESSAGES OFF`", "`ROUTINES OFF`" +:act:`RoutinesOn` "`MESSAGES [ON]`", "`ROUTINES [ON]`" +:act:`Scope` "`SCOPE`" +:act:`Showobj` "`SHOWOBJ`" +:act:`Showverb` "`SHOWVERB`" +:act:`TimersOff` "`DAEMONS OFF`", "`TIMERS OFF`" +:act:`TimersOn` "`DAEMONS [ON]`", "`TIMERS [ON]`" +:act:`TraceLevel` "`TRACE number`" +:act:`TraceOff` "`TRACE OFF`" +:act:`TraceOn` "`TRACE [ON]`" +:act:`XAbstract` "`ABSTRACT`" +:act:`XPurloin` "`PURLOIN`" +:act:`XTree` "`TREE`" +================== =================================================== .. _group-2-actions: @@ -1324,62 +1331,61 @@ Group 2 actions usually work, given the right circumstances. .. tabularcolumns:: |l|p{5in}| -============= ============================================================= -`Close` "`CLOSE [UP]`", "`COVER [UP]`", "`SHUT [UP]`" -`Disrobe` "`DISROBE`", "`DOFF`", "`REMOVE`", "`SHED`", "`TAKE OFF`" -`Drop` "`DISCARD`", "`DROP`", "`PUT DOWN`", "`THROW`" -`Eat` "`EAT`" -`Empty` "`EMPTY [OUT]`" -`EmptyT` "`EMPTY IN|INTO|ON|ONTO|TO`" -`Enter` "`CROSS`", "`ENTER`", "`GET IN|INTO|ON|ONTO`", - "`GO IN|INSIDE|INTO|THROUGH`", - "`LEAVE IN|INSIDE|INTO|THROUGH`", "`LIE IN|INSIDE|ON`", - "`LIE ON TOP OF`", - "`RUN IN|INSIDE|INTO|THROUGH`", "`SIT IN|INSIDE|ON`", - "`SIT ON TOP OF`", "`STAND ON`", - "`WALK IN|INSIDE|INTO|THROUGH`" -`Examine` "`CHECK,`" "`DESCRIBE`", "`EXAMINE`", - "`L[OOK] AT`", "`READ`", "`WATCH`", "`X`" -`Exit` "`EXIT`", "`GET OFF|OUT|UP`", - "`LEAVE`", "`OUT[SIDE]`", "`STAND [UP]`" -`GetOff` "`GET OFF`" -`Give` "`FEED [TO]`", "`GIVE [TO]`", "`OFFER [TO]`", "`PAY [TO]`" -`Go` "`GO`", "`LEAVE`", "`RUN`", "`WALK`" -`GoIn` "`CROSS`", "`ENTER`", "`IN[SIDE]`" -`Insert` "`DISCARD IN|INTO`", - "`DROP DOWN|IN|INTO`", - "`INSERT IN|INTO`", - "`PUT IN|INSIDE|INTO`", - "`THROW DOWN|IN|INTO`" -`Inv` "`I[NV]`", "`INVENTORY`", "`TAKE INVENTORY`" -`InvTall` "`I[NV] TALL`", "`INVENTORY TALL`" -`InvWide` "`I[NV] WIDE`", "`INVENTORY WIDE`" -`Lock` "`LOCK WITH`" -`Look` "`L[OOK]`" -`Open` "`OPEN`", "`UNCOVER`", "`UNDO`", "`UNWRAP`" -`PutOn` "`DISCARD ON|ONTO`", - "`DROP ON|ONTO`", - "`PUT ON|ONTO`", - "`THROW ON|ONTO`" -`Remove` "`GET FROM`", "`REMOVE FROM`", "`TAKE FROM|OFF`" -`Search` "`L[OOK] IN|INSIDE|INTO|THROUGH`", "`SEARCH`" -`Show` "`DISPLAY [TO]`", "`PRESENT [TO]`", "`SHOW [TO]`" -`SwitchOff` "`CLOSE OFF`", "`SCREW OFF`", - "`SWITCH OFF`", "`TURN OFF`", - "`TWIST OFF`" -`SwitchOn` "`SCREW ON`", "`SWITCH ON`", - "`TURN ON`", "`TWIST ON`" -`Take` "`CARRY`", "`GET`", "`HOLD`", - "`PEEL [OFF]`", "`PICK UP`", - "`REMOVE`", "`TAKE`" -`Transfer` "`CLEAR TO`", "`MOVE TO`", - "`PRESS TO`", "`PUSH TO`", - "`SHIFT TO`", "`TRANSFER TO`" -`Unlock` "`OPEN WITH`", "`UNDO WITH`", - "`UNLOCK WITH`" -`VagueGo` "`GO`", "`LEAVE`", "`RUN`", "`WALK`" -`Wear` "`DON`", "`PUT ON`", "`WEAR`" -============= ============================================================= +================ =========================================================== +:act:`Close` "`CLOSE [UP]`", "`COVER [UP]`", "`SHUT [UP]`" +:act:`Disrobe` "`DISROBE`", "`DOFF`", "`REMOVE`", "`SHED`", "`TAKE OFF`" +:act:`Drop` "`DISCARD`", "`DROP`", "`PUT DOWN`", "`THROW`" +:act:`Eat` "`EAT`" +:act:`Empty` "`EMPTY [OUT]`" +:act:`EmptyT` "`EMPTY IN|INTO|ON|ONTO|TO`" +:act:`Enter` "`CROSS`", "`ENTER`", "`GET IN|INTO|ON|ONTO`", + "`GO IN|INSIDE|INTO|THROUGH`", + "`LEAVE IN|INSIDE|INTO|THROUGH`", "`LIE IN|INSIDE|ON`", + "`LIE ON TOP OF`", + "`RUN IN|INSIDE|INTO|THROUGH`", "`SIT IN|INSIDE|ON`", + "`SIT ON TOP OF`", "`STAND ON`", + "`WALK IN|INSIDE|INTO|THROUGH`" +:act:`Examine` "`CHECK`", "`DESCRIBE`", "`EXAMINE`", + "`L[OOK] AT`", "`READ`", "`WATCH`", "`X`" +:act:`Exit` "`EXIT`", "`GET OFF|OUT|UP`", + "`LEAVE`", "`OUT[SIDE]`", "`STAND [UP]`" +:act:`GetOff` "`GET OFF`" +:act:`Give` "`FEED [TO]`", "`GIVE [TO]`", "`OFFER [TO]`", "`PAY [TO]`" +:act:`Go` "`GO`", "`LEAVE`", "`RUN`", "`WALK`" +:act:`GoIn` "`CROSS`", "`ENTER`", "`IN[SIDE]`" +:act:`Insert` "`DISCARD IN|INTO`", + "`DROP DOWN|IN|INTO`", + "`INSERT IN|INTO`", + "`PUT IN|INSIDE|INTO`", + "`THROW DOWN|IN|INTO`" +:act:`Inv` "`I[NV]`", "`INVENTORY`", "`TAKE INVENTORY`" +:act:`InvTall` "`I[NV] TALL`", "`INVENTORY TALL`" +:act:`InvWide` "`I[NV] WIDE`", "`INVENTORY WIDE`" +:act:`Lock` "`LOCK WITH`" +:act:`Look` "`L[OOK]`" +:act:`Open` "`OPEN`", "`UNCOVER`", "`UNDO`", "`UNWRAP`" +:act:`PutOn` "`DISCARD ON|ONTO`", + "`DROP ON|ONTO`", + "`PUT ON|ONTO`", + "`THROW ON|ONTO`" +:act:`Remove` "`GET FROM`", "`REMOVE FROM`", "`TAKE FROM|OFF`" +:act:`Search` "`L[OOK] IN|INSIDE|INTO|THROUGH`", "`SEARCH`" +:act:`Show` "`DISPLAY [TO]`", "`PRESENT [TO]`", "`SHOW [TO]`" +:act:`SwitchOff` "`CLOSE OFF`", "`SCREW OFF`", "`SWITCH OFF`", + "`TURN OFF`", "`TWIST OFF`" +:act:`SwitchOn` "`SCREW ON`", "`SWITCH ON`", + "`TURN ON`", "`TWIST ON`" +:act:`Take` "`CARRY`", "`GET`", "`HOLD`", + "`PEEL [OFF]`", "`PICK UP`", + "`REMOVE`", "`TAKE`" +:act:`Transfer` "`CLEAR TO`", "`MOVE TO`", + "`PRESS TO`", "`PUSH TO`", + "`SHIFT TO`", "`TRANSFER TO`" +:act:`Unlock` "`OPEN WITH`", "`UNDO WITH`", + "`UNLOCK WITH`" +:act:`VagueGo` "`GO`", "`LEAVE`", "`RUN`", "`WALK`" +:act:`Wear` "`DON`", "`PUT ON`", "`WEAR`" +================ =========================================================== .. _group-3-actions: @@ -1391,69 +1397,69 @@ Group 3 actions are by default stubs which output a message and stop at the .. tabularcolumns:: |l|p{5in}| -============= ============================================================= -`Answer` "`ANSWER TO`", "`SAY TO`", - "`SHOUT TO`", "`SPEAK TO`" -`Ask` "`ASK ABOUT`" -`AskFor` "`ASK FOR`" -`Attack` "`ATTACK`", "`BREAK`", "`CRACK`", - "`DESTROY`", "`FIGHT`", "`HIT`", - "`KILL`", "`MURDER`", "`PUNCH`", - "`SMASH`", "`THUMP`", "`TORTURE`", - "`WRECK`" -`Blow` "`BLOW`" -`Burn` "`BURN [WITH]`", "`LIGHT [WITH]`" -`Buy` "`BUY`" "`PURCHASE`" -`Climb` "`CLIMB [OVER|UP]`", "`SCALE`" -`Consult` "`CONSULT ABOUT|ON`", - "`LOOK UP IN`", - "`READ ABOUT IN`", "`READ IN`" -`Cut` "`CHOP,`" "`CUT`", "`PRUNE`", "`SLICE`" -`Dig` "`DIG [WITH]`" -`Drink` "`DRINK`", "`SIP`", "`SWALLOW`" -`Fill` "`FILL`" -`Jump` "`HOP`", "`JUMP`", "`SKIP`" -`JumpOver` "`HOP OVER`", "`JUMP OVER`", "`SKIP OVER`" -`Kiss` "`EMBRACE`", "`HUG`", "`KISS`" -`Listen` "`HEAR`", "`LISTEN [TO]`" -`LookUnder` "`LOOK UNDER`" -`Mild` Various mild swearwords. -`No` "`NO`" -`Pray` "`PRAY`" -`Pull` "`DRAG`" "`PULL`" -`Push` "`CLEAR`", "`MOVE`", "`PRESS`", - "`PUSH`", "`SHIFT`" -`PushDir` "`CLEAR`", "`MOVE`", "`PRESS`", - "`PUSH`", "`SHIFT`" -`Rub` "`CLEAN`", "`DUST`", "`POLISH`", - "`RUB`", "`SCRUB`", "`SHINE`", - "`SWEEP`", "`WIPE`" -`Set` "`ADJUST`", "`SET`" -`SetTo` "`ADJUST TO`", "`SET TO`" -`Sing` "`SING`" -`Sleep` "`NAP`", "`SLEEP`" -`Smell` "`SMELL`", "`SNIFF`" -`Sorry` "`SORRY`" -`Squeeze` "`SQUASH`", "`SQUEEZE`" -`Strong` Various strong swearwords. -`Swim` "`DIVE`", "`SWIM`" -`Swing` "`SWING [ON]`" -`Taste` "`TASTE`" -`Tell` "`TELL ABOUT`" -`Think` "`THINK`" -`ThrowAt` "`THROW AGAINST|AT|ON|ONTO`" -`Tie` "`ATTACH [TO]`", "`FASTEN [TO]`", - "`FIX [TO]`", "`TIE [TO]`" -`Touch` "`FEEL,`" "`FONDLE`", "`GROPE`", "`TOUCH`" -`Turn` "`ROTATE`", "`SCREW`", "`TURN`", - "`TWIST`", "`UNSCREW`" -`Wait` "`WAIT`" "`Z`" -`Wake` "`AWAKE[N]`", "`WAKE [UP]`" -`WakeOther` "`AWAKE[N]`", "`WAKE [UP]`" -`Wave` "`WAVE`" -`WaveHands` "`WAVE`" -`Yes` "`Y[ES]`" -============= ============================================================= +================ ========================================================== +:act:`Answer` "`ANSWER TO`", "`SAY TO`", + "`SHOUT TO`", "`SPEAK TO`" +:act:`Ask` "`ASK ABOUT`" +:act:`AskFor` "`ASK FOR`" +:act:`Attack` "`ATTACK`", "`BREAK`", "`CRACK`", + "`DESTROY`", "`FIGHT`", "`HIT`", + "`KILL`", "`MURDER`", "`PUNCH`", + "`SMASH`", "`THUMP`", "`TORTURE`", + "`WRECK`" +:act:`Blow` "`BLOW`" +:act:`Burn` "`BURN [WITH]`", "`LIGHT [WITH]`" +:act:`Buy` "`BUY`" "`PURCHASE`" +:act:`Climb` "`CLIMB [OVER|UP]`", "`SCALE`" +:act:`Consult` "`CONSULT ABOUT|ON`", + "`LOOK UP IN`", + "`READ ABOUT IN`", "`READ IN`" +:act:`Cut` "`CHOP`", "`CUT`", "`PRUNE`", "`SLICE`" +:act:`Dig` "`DIG [WITH]`" +:act:`Drink` "`DRINK`", "`SIP`", "`SWALLOW`" +:act:`Fill` "`FILL`" +:act:`Jump` "`HOP`", "`JUMP`", "`SKIP`" +:act:`JumpOver` "`HOP OVER`", "`JUMP OVER`", "`SKIP OVER`" +:act:`Kiss` "`EMBRACE`", "`HUG`", "`KISS`" +:act:`Listen` "`HEAR`", "`LISTEN [TO]`" +:act:`LookUnder` "`LOOK UNDER`" +:act:`Mild` Various mild swearwords. +:act:`No` "`NO`" +:act:`Pray` "`PRAY`" +:act:`Pull` "`DRAG`" "`PULL`" +:act:`Push` "`CLEAR`", "`MOVE`", "`PRESS`", + "`PUSH`", "`SHIFT`" +:act:`PushDir` "`CLEAR`", "`MOVE`", "`PRESS`", + "`PUSH`", "`SHIFT`" +:act:`Rub` "`CLEAN`", "`DUST`", "`POLISH`", + "`RUB`", "`SCRUB`", "`SHINE`", + "`SWEEP`", "`WIPE`" +:act:`Set` "`ADJUST`", "`SET`" +:act:`SetTo` "`ADJUST TO`", "`SET TO`" +:act:`Sing` "`SING`" +:act:`Sleep` "`NAP`", "`SLEEP`" +:act:`Smell` "`SMELL`", "`SNIFF`" +:act:`Sorry` "`SORRY`" +:act:`Squeeze` "`SQUASH`", "`SQUEEZE`" +:act:`Strong` Various strong swearwords. +:act:`Swim` "`DIVE`", "`SWIM`" +:act:`Swing` "`SWING [ON]`" +:act:`Taste` "`TASTE`" +:act:`Tell` "`TELL ABOUT`" +:act:`Think` "`THINK`" +:act:`ThrowAt` "`THROW AGAINST|AT|ON|ONTO`" +:act:`Tie` "`ATTACH [TO]`", "`FASTEN [TO]`", + "`FIX [TO]`", "`TIE [TO]`" +:act:`Touch` "`FEEL`", "`FONDLE`", "`GROPE`", "`TOUCH`" +:act:`Turn` "`ROTATE`", "`SCREW`", "`TURN`", + "`TWIST`", "`UNSCREW`" +:act:`Wait` "`WAIT`" "`Z`" +:act:`Wake` "`AWAKE[N]`", "`WAKE [UP]`" +:act:`WakeOther` "`AWAKE[N]`", "`WAKE [UP]`" +:act:`Wave` "`WAVE`" +:act:`WaveHands` "`WAVE`" +:act:`Yes` "`Y[ES]`" +================ ========================================================== Fake actions ============ @@ -1463,17 +1469,18 @@ the viewpoint of the second object. .. tabularcolumns:: |l|p{5in}| -================ ======================================================== -`LetGo` Generated by `Remove`. -`ListMiscellany` Outputs a range of inventory messages. -`Miscellany` Outputs a range of utility messages. -`NotUnderstood` Generated when parser fails to interpret some `orders`. -`Order` Receives things not handled by `orders`. -`PluralFound` Tells the parser that `parse_name()` has identified a - plural object. -`Prompt` Outputs the prompt, normally ">". -`Receive` Generated by `Insert` and `PutOn`. -`TheSame` Generated when parser can't distinguish between two - objects. -`ThrownAt` Generated by `ThrowAt`. -================ ======================================================== +===================== ====================================================== +:act:`LetGo` Generated by :act:`Remove`. +:act:`ListMiscellany` Outputs a range of inventory messages. +:act:`Miscellany` Outputs a range of utility messages. +:act:`NotUnderstood` Generated when parser fails to interpret some + :prop:`orders`. +:act:`Order` Receives things not handled by :prop:`orders`. +:act:`PluralFound` Tells the parser that `parse_name()` has identified a + plural object. +:act:`Prompt` Outputs the prompt, normally ">". +:act:`Receive` Generated by :act:`Insert` and :act:`PutOn`. +:act:`TheSame` Generated when parser can't distinguish between two + objects. +:act:`ThrownAt` Generated by :act:`ThrowAt`. +===================== ====================================================== diff --git a/chapters/02.rst b/chapters/02.rst index af3f045..1d40017 100644 --- a/chapters/02.rst +++ b/chapters/02.rst @@ -83,6 +83,11 @@ is a "text" (or "ASCII") file containing words and phrases which can be read -- admittedly after a little tuition, which is what this guide is all about -- by humans. +.. Generated by autoindex +.. index:: + single: IBM PC + pair: NotePad; text editor + How do you create that source file? Using a third software program: an :term:`editor`. However, unlike the compiler and interpreter, this program isn't dedicated to the Inform system -- or even to IF. An editor is an @@ -130,6 +135,10 @@ write an Inform adventure game: because it's a binary file not meaningful to human eyes, neatly discourages players from cheating). +.. Generated by autoindex +.. index:: + single: IF Archive + All of those, apart from the editor, can be downloaded for free from the IF Archive. One approach is to fetch them individually, following the guidance on Graham's page: visit http://www.inform-fiction.org/ and look @@ -334,6 +343,10 @@ can stay the same in each ``.bat`` file that you create. .. rubric:: Getting a better editor +.. Generated by autoindex +.. index:: + pair: NotePad; text editor + Although NotePad is adequate when you're getting started, you'll find life much easier if you obtain a more powerful editor program. We recommend TextPad, available as shareware from http://www.textpad.com/; in addition, @@ -524,6 +537,10 @@ Follow these steps: .. rubric:: Setting file associations +.. Generated by autoindex +.. index:: + single: Infocom + The business of first starting the interpreter, and then locating the story file that you want to play, is clumsy and inconvenient. Fortunately, when the system first "sees" the Zoom interpreter (which is a nice Aqua @@ -731,6 +748,10 @@ with these mistakes, and about controlling how the compiler behaves, in .. rubric:: More about the interpreter +.. Generated by autoindex +.. index:: + single: Infocom + One of the big advantages of the way Inform works is that a compiled game -- the Z-code story file -- is portable between different computers. That's not just from one PC to another: exactly the same story file will diff --git a/chapters/03.rst b/chapters/03.rst index 4fe5e33..a50db44 100644 --- a/chapters/03.rst +++ b/chapters/03.rst @@ -157,6 +157,10 @@ a clear (albeit sparse) map at the start will help us to keep things organised as the game evolves. We can infer several Inform rules just by looking at the source file. +.. Generated by autoindex +.. index:: + single: Strict mode + * If the *very first line* (or lines) of the source file begin with the characters "``!%``", then the compiler treats what follows on those lines as control instructions to itself rather than as part of the game's @@ -332,6 +336,10 @@ Again, we can infer some general principles from these four examples: * A keyword ``with`` follows, which simply tells the compiler what to expect next. +.. Generated by autoindex +.. index:: + pair: description; library property + * The word :prop:`description`, introducing another piece of text which gives more detail about the object: in the case of a room, it's the appearance of the surrounding environment when the player character is in @@ -341,6 +349,10 @@ Again, we can infer some general principles from these four examples: * Near the end, the keyword ``has`` appears, which again tells the compiler to expect a certain kind of information. +.. Generated by autoindex +.. index:: + pair: light; library attribute + * The word :attr:`light` says that this object is a source of illumination, and that therefore the player character can see what's happening here. There has to be at least one light source in every room (unless you want @@ -445,6 +457,21 @@ We've made two changes to the room objects. cottage), and the second defines a connection ``forest`` → ``clearing`` which heads off to the northeast. + .. Generated by autoindex + .. index:: + pair: d_to; library property + pair: e_to; library property + pair: in_to; library property + pair: n_to; library property + pair: ne_to; library property + pair: nw_to; library property + pair: out_to; library property + pair: s_to; library property + pair: se_to; library property + pair: sw_to; library property + pair: u_to; library property + pair: w_to; library property + Inform provides for eight "horizontal" connections (:prop:`n_to`, :prop:`ne_to`, :prop:`e_to`, :prop:`se_to`, :prop:`s_to`, :prop:`sw_to`, :prop:`w_to`, :prop:`nw_to`) two "vertical" ones (:prop:`u_to`, @@ -617,6 +644,11 @@ player can't interact with a room in the same way as with other objects; for example, she doesn't need to say EXAMINE THE FOREST -- just being there and typing LOOK is sufficient. +.. Generated by autoindex +.. index:: + pair: container; library attribute + pair: open; library attribute + The bird's definition is complete, but there's an additional complexity with the nest: we need to be able to put the bird into it. We do this by labelling the nest as a :attr:`container` -- able to hold other objects -- @@ -858,6 +890,10 @@ is one way of making it happen: several new concepts all at once. Later in the guide, we'll explain those concepts more clearly, so you can just skip this bit if you want. + .. Generated by autoindex + .. index:: + pair: deadflag; library variable + The variable :var:`deadflag`, part of the library, is normally 0. If you set its value to 2, the interpreter notices and ends the game with "You have won". The statement:: diff --git a/chapters/04.rst b/chapters/04.rst index 51454ab..46e1858 100644 --- a/chapters/04.rst +++ b/chapters/04.rst @@ -68,6 +68,10 @@ but you can change it at any time. For example, we used the statement:: location = before_cottage; +.. Generated by autoindex +.. index:: + pair: location; library variable + to reset the value of the :var:`location` variable to the ``before_cottage`` object, and we wrote:: @@ -435,6 +439,10 @@ into individual words, which it then looks up in the dictionary. If it finds all the words, and they seem to represent a sensible course of action, that's what happens next. +.. Generated by autoindex +.. index:: + pair: name; library property + So far, we've seen dictionary words used as the values of an object :prop:`name` property:: @@ -487,6 +495,10 @@ interpreter executes that statement: it performs an assignment:: deadflag = 2; +.. Generated by autoindex +.. index:: + pair: deadflag; library variable + which changes the value of the library variable :var:`deadflag` from its current value to 2. Incidentally, ``if`` statements are often written on two lines, with the "controlled" statement indented. This makes it diff --git a/chapters/05.rst b/chapters/05.rst index aabd40d..8e3fb54 100644 --- a/chapters/05.rst +++ b/chapters/05.rst @@ -74,6 +74,10 @@ how we do it: We'll go through this a step at a time: +.. Generated by autoindex +.. index:: + pair: before; library property + #. We've added a new :prop:`before` property to our bird object. The interpreter looks at the property *before* attempting to perform any action which is directed specifically at this object:: @@ -87,14 +91,19 @@ We'll go through this a step at a time: print "It sounds scared and in need of assistance.^"; return true; -#. The label is the name of an action, in this case ``Listen``. What we're - telling the interpreter is: if the action that you're about to perform - on the bird is a ``Listen``, execute these statements first; if it's any - other action, carry on as normal. So, if the player types EXAMINE BIRD, - PICK UP BIRD, PUT BIRD IN NEST, HIT BIRD or FONDLE BIRD, then she'll get - the standard response. If she types LISTEN TO BIRD, then our two - statements get executed before anything else happens. We call this - "trapping" or "intercepting" the action of Listening to the bird. +.. Generated by autoindex +.. index:: + pair: Listen; library action + +#. The label is the name of an action, in this case :act:`Listen`. What + we're telling the interpreter is: if the action that you're about to + perform on the bird is a :act:`Listen`, execute these statements first; + if it's any other action, carry on as normal. So, if the player types + EXAMINE BIRD, PICK UP BIRD, PUT BIRD IN NEST, HIT BIRD or FONDLE BIRD, + then she'll get the standard response. If she types LISTEN TO BIRD, + then our two statements get executed before anything else happens. We + call this "trapping" or "intercepting" the action of Listening to the + bird. #. The two statements that we execute are, first:: @@ -107,8 +116,8 @@ We'll go through this a step at a time: return true; which tells the interpreter that it doesn't need to do anything else, - because we've handled the ``Listen`` action ourselves. And the game now - behaves like this -- perfect: + because we've handled the :act:`Listen` action ourselves. And the game + now behaves like this -- perfect: .. code-block:: transcript @@ -251,13 +260,14 @@ solution we adopt is similar as well: ], has scenery; -We use a :prop:`before` property to intercept the ``Enter`` action applied to -the cottage object, so that we can display a more appropriate message. -This time, however, we've done it using one statement rather than two. It -turns out that the sequence "``print`` a string which ends with a newline -character, and then ``return true``" is so frequently needed that there's a -special statement which does it all. That is, this single statement (where -you'll note that the string *doesn't* need to end in ``^``):: +We use a :prop:`before` property to intercept the :act:`Enter` action +applied to the cottage object, so that we can display a more appropriate +message. This time, however, we've done it using one statement rather than +two. It turns out that the sequence "``print`` a string which ends with a +newline character, and then ``return true``" is so frequently needed that +there's a special statement which does it all. That is, this single +statement (where you'll note that the string *doesn't* need to end in +``^``):: print_ret "It's such a lovely day -- much too nice to go inside."; @@ -292,13 +302,13 @@ property, but now with a difference. ], has scenery; -This time, when we intercept the ``Climb`` action applied to the ``tree`` -object, it's not in order to display a better message; it's because we want -to move the player character to another room, just as if she'd typed UP. -Relocating the player character is actually quite a complex business, but -fortunately all of that complexity is hidden: there's a standard -:term:`library routine` to do the job, not one that we've written, but one -that's provided as part of the Inform system. +This time, when we intercept the :act:`Climb` action applied to the +``tree`` object, it's not in order to display a better message; it's +because we want to move the player character to another room, just as if +she'd typed UP. Relocating the player character is actually quite a +complex business, but fortunately all of that complexity is hidden: there's +a standard :term:`library routine` to do the job, not one that we've +written, but one that's provided as part of the Inform system. .. index:: single: arguments (of a routine) @@ -315,9 +325,9 @@ closing parentheses. That is, instead of just ``PlayerTo()`` we call :term:`argument`. Although we've moved the player character to another room, we're still in -the middle of the intercepted ``Climb`` action. As previously, we need to -tell the interpreter that we've dealt with the action, and so we don't want -the standard rejection message to be displayed. The ``return true`` +the middle of the intercepted :act:`Climb` action. As previously, we need +to tell the interpreter that we've dealt with the action, and so we don't +want the standard rejection message to be displayed. The ``return true`` statement does that, as usual. Dropping objects from the tree @@ -330,20 +340,24 @@ the top of the tree. Should she DROP something from up there, having it land nearby might seem a bit improbable; much more likely that it would fall to the clearing below. -It looks like we might want to intercept the ``Drop`` action, but not quite -in the way we've been doing up until now. For one thing, we don't want to -complicate the definitions of the ``bird`` and the ``nest`` and any other -objects we may introduce: much better to find a general solution that will -work for all objects. And second, we need to recognise that not all +.. Generated by autoindex +.. index:: + pair: Drop; library action + +It looks like we might want to intercept the :act:`Drop` action, but not +quite in the way we've been doing up until now. For one thing, we don't +want to complicate the definitions of the ``bird`` and the ``nest`` and any +other objects we may introduce: much better to find a general solution that +will work for all objects. And second, we need to recognise that not all objects are droppable; the player can't, for example, DROP THE BRANCH. -The best approach to the second problem is to intercept the ``Drop`` action -*after* it has occurred, rather than beforehand. That way, we let the -library take care of objects which aren't being held or which can't be -dropped, and only become involved once a ``Drop`` has been successful. And -the best approach to the first problem is to do this particular +The best approach to the second problem is to intercept the :act:`Drop` +action *after* it has occurred, rather than beforehand. That way, we let +the library take care of objects which aren't being held or which can't be +dropped, and only become involved once a :act:`Drop` has been successful. +And the best approach to the first problem is to do this particular interception not on an object-by-object basis, as we have been doing so -far, but instead for every ``Drop`` which takes place in our troublesome +far, but instead for every :act:`Drop` which takes place in our troublesome ``top_of_tree`` room. This is what we have to write: .. code-block:: inform @@ -360,6 +374,10 @@ far, but instead for every ``Drop`` which takes place in our troublesome Let's again take it a step at a time: +.. Generated by autoindex +.. index:: + pair: after; library property + #. We've added a new :prop:`after` property to our ``top_of_tree`` object. The interpreter looks at the property *subsequent to* performing any action in this room:: @@ -373,21 +391,22 @@ Let's again take it a step at a time: move noun to clearing; return false; -#. The label is the name of an action, in this case ``Drop``. What we're - telling the interpreter is: if the action that you've just performed - here is a ``Drop``, execute these statements before telling the player - what you've done; if it's any other action, carry on as normal. +#. The label is the name of an action, in this case :act:`Drop`. What + we're telling the interpreter is: if the action that you've just + performed here is a :act:`Drop`, execute these statements before telling + the player what you've done; if it's any other action, carry on as + normal. #. The two statements that we execute are first:: move noun to clearing; which takes the object which has just been moved from the :var:`player` - object to the ``top_of_tree`` object (by the successful ``Drop`` action) - and moves it again so that its parent becomes the ``clearing`` object. - That :var:`noun` is a library variable that always contains the internal - ID of the object which is the target of the current action. If the - player types DROP NEST, :var:`noun` contains the internal ID of the + object to the ``top_of_tree`` object (by the successful :act:`Drop` + action) and moves it again so that its parent becomes the ``clearing`` + object. That :var:`noun` is a library variable that always contains the + internal ID of the object which is the target of the current action. If + the player types DROP NEST, :var:`noun` contains the internal ID of the ``nest`` object; if she types DROP NESTLING then :var:`noun` contains the internal ID of the ``bird`` object. Second, we execute:: @@ -466,6 +485,10 @@ The extended ``if`` statement:: if (bird in nest && nest in branch) deadflag = 2; +.. Generated by autoindex +.. index:: + pair: deadflag; library variable + should now be read as: "Test whether the ``bird`` is currently in (or on) the ``nest``, *and* whether the ``nest`` is currently on (or in) the ``branch``; if both parts are :const:`true`, set the value of @@ -487,6 +510,10 @@ The new topics that we've encountered here include these: Object properties ----------------- +.. Generated by autoindex +.. index:: + pair: before; library property + Objects can have a :prop:`before` property -- if there is one, the interpreter looks at it *before* performing an action which in some way involves that object. Similarly, you can provide an :prop:`after` @@ -497,6 +524,10 @@ the ``bird``, ``cottage`` and ``tree`` (when they intercept actions aimed at that particular object) but also with rooms (when they intercept actions aimed at any object in that room). +.. Generated by autoindex +.. index:: + pair: after; library property + The value of each :prop:`before` and :prop:`after` property is an embedded routine. If such a routine ends with ``return false``, the interpreter then carries on with the next stage of the action which has been @@ -505,6 +536,10 @@ further for that action. By combining these possibilities, you can supplement the work done by a standard action with statements of your own, or you can replace a standard action completely. +.. Generated by autoindex +.. index:: + pair: cant_go; library property + Previously, we've seen connection properties used with the internal ID of the room to which they lead. In this chapter, we showed that the value could also be a string (explaining why movement in that direction isn't @@ -586,19 +621,29 @@ We encountered several new statements: .. rubric:: Actions -We've talked a lot about intercepting actions like ``Listen``, ``Enter``, -``Climb`` and ``Drop``. An action is a generalised representation of -something to be done, determined by the verb which the player types. For -example, the verbs HEAR and LISTEN are ways of saying much the same thing, -and so both result in the same action: ``Listen``. Similarly, verbs like -ENTER, GET INTO, SIT ON and WALK INSIDE all lead to an action of ``Enter``, -CLIMB and SCALE lead to Climb, and DISCARD, DROP, PUT DOWN and THROW all -lead to ``Drop``. This makes life much easier for the designer; although -Inform defines quite a lot of actions, there are many fewer than there are -ways of expressing those same actions using English verbs. - +.. Generated by autoindex +.. index:: + pair: Climb; library action + pair: Drop; library action + pair: Enter; library action + pair: Listen; library action + +We've talked a lot about intercepting actions like :act:`Listen`, +:act:`Enter`, :act:`Climb` and :act:`Drop`. An action is a generalised +representation of something to be done, determined by the verb which the +player types. For example, the verbs HEAR and LISTEN are ways of saying +much the same thing, and so both result in the same action: :act:`Listen`. +Similarly, verbs like ENTER, GET INTO, SIT ON and WALK INSIDE all lead to +an action of :act:`Enter`, CLIMB and SCALE lead to Climb, and DISCARD, +DROP, PUT DOWN and THROW all lead to :act:`Drop`. This makes life much +easier for the designer; although Inform defines quite a lot of actions, +there are many fewer than there are ways of expressing those same actions +using English verbs. + +.. Generated by autoindex .. index:: pair: action; library variable + pair: second; library variable Each action is represented internally by a number, and the value of the current action is stored in a library variable called, erm, :var:`action`. @@ -618,6 +663,11 @@ DROP THE NEST Drop nest nothing PUT NEST ON BRANCH PutOn nest branch =============================== ====== ======= ======= +.. Generated by autoindex +.. index:: + pair: false; library constant + pair: true; library constant + The value ``nothing`` is a built-in constant (like :const:`true` and :const:`false`) which means, well, there isn't any object to refer to. There's a list of standard library actions in :ref:`group-1-actions`, diff --git a/chapters/06.rst b/chapters/06.rst index 603663d..c0cd6da 100644 --- a/chapters/06.rst +++ b/chapters/06.rst @@ -155,9 +155,9 @@ pieces: Why not do that with the player? Because the object which represents the player is defined by the library (rather than as part of our - game), and actually has an internal ID of ``selfobj``; :var:`player` - is a variable whose value is that identifier. Rather than worry all - about this, it's easier to use the ``move`` statements. + game), and actually has an internal ID of :obj:`selfobj`; + :var:`player` is a variable whose value is that identifier. Rather + than worry all about this, it's easier to use the ``move`` statements. There's one other task associated with the quiver; it's an article of clothing which Wilhelm is "wearing", a state denoted by the attribute @@ -172,6 +172,10 @@ pieces: quiver ~worn`` -- read that as "give the quiver not-worn"; Inform often uses ``~`` to mean "not".) +.. Generated by autoindex +.. index:: + pair: description; library property + * If the player types EXAMINE ME, the interpreter displays the :prop:`description` property of the :var:`player` object. The default value is "As good-looking as ever", a bit of a cliché in the world of @@ -366,16 +370,22 @@ the first :prop:`before` we met looked like this:: return true; ], -The role of that original :prop:`before` was to intercept ``Listen`` +.. Generated by autoindex +.. index:: + pair: Examine; library action + pair: Listen; library action + +The role of that original :prop:`before` was to intercept :act:`Listen` actions, while leaving all others well alone. The role of the :prop:`before` in the ``Prop`` class is broader: to intercept (a) -``Examine`` actions, and (b) all the rest. If the action is ``Examine``, -then the ``return false`` statement means that the action carries on. If -the action is ``default`` -- none of those explicitly listed, which in this -instance means *every* action apart from ``Examine`` -- then the -``print_ret`` statement is executed, after which the interpreter does -nothing further. So, a ``Prop`` object can be EXAMINEd, but any other -action addressed to it results in a "no need to worry" message. +:act:`Examine` actions, and (b) all the rest. If the action is +:act:`Examine`, then the ``return false`` statement means that the action +carries on. If the action is ``default`` -- none of those explicitly +listed, which in this instance means *every* action apart from +:act:`Examine` -- then the ``print_ret`` statement is executed, after which +the interpreter does nothing further. So, a ``Prop`` object can be +EXAMINEd, but any other action addressed to it results in a "no need to +worry" message. That message is also more involved than anything we've so far displayed. The statement which produces it is:: @@ -410,6 +420,10 @@ The interesting things that this statement demonstrates are: display the following object's internal ID, is called a :term:`print rule`. +.. Generated by autoindex +.. index:: + pair: self; library variable + * There's a library variable :var:`self` which always contains the internal ID of the current object, and is really convenient when using a ``Class``. By using this variable in our ``print_ret`` statement, we @@ -436,6 +450,11 @@ would be a waste of time. A class for furniture --------------------- +.. Generated by autoindex +.. index:: + single: NPC + pair: static; library attribute + The last class for now -- we'll talk about the ``Arrow`` and ``NPC`` classes in the next chapter -- is for furniture-like objects. If you label an object with the :attr:`static` attribute, an attempt to TAKE it results @@ -464,10 +483,14 @@ intercepting four actions; we *could* have written the property like this:: PushDir: print_ret (The) self, " is too heavy for that."; ], +.. Generated by autoindex +.. index:: + pair: PushDir; library action + but since we're giving exactly the same response each time, it's better to -put all of those actions into one list, separated by commas. ``PushDir``, -if you were wondering, is the action triggered by a command like PUSH THE -TABLE NORTH. +put all of those actions into one list, separated by commas. +:act:`PushDir`, if you were wondering, is the action triggered by a command +like PUSH THE TABLE NORTH. Incidentally, another bonus of defining classes like these is that you can probably reuse them in your next game. diff --git a/chapters/07.rst b/chapters/07.rst index c7a8ad0..c4beedc 100644 --- a/chapters/07.rst +++ b/chapters/07.rst @@ -85,6 +85,10 @@ The line of dialogue is produced by the ``print`` statement, the ``print`` statement is controlled by the ``if`` statement, and the ``if`` statement is performing the test ``self hasnt visited``. In detail: +.. Generated by autoindex +.. index:: + pair: visited; library attribute + * :attr:`visited` is an attribute, but not one that you'd normally give to an object yourself. It's automatically applied to a room object by the interpreter, but only after that room has been visited for the first @@ -97,6 +101,10 @@ is performing the test ``self hasnt visited``. In detail: if object :samp:`{X}` currently does not have attribute :samp:`{Y}`, false if it does. +.. Generated by autoindex +.. index:: + pair: self; library variable + * :var:`self`, which we met in the previous chapter, is that useful variable which, within an object, always refers to that object. Since we're using it in the middle of the ``street`` object, that's what it refers to. @@ -185,6 +193,10 @@ for this:: :samp:`{external_names}`, because these are used by the ``Prop`` class's ``print_ret ... (the) self`` statement. +.. Generated by autoindex +.. index:: + pair: pluralname; library attribute + You'll see a couple of new attributes: :attr:`animate` marks an object as being "alive", while :attr:`pluralname` specifies that its external name is plural rather than singular. The interpreter uses these attributes to @@ -195,6 +207,10 @@ it's hard to be sure exactly what messages players may trigger; the best approach is to play safe and always give an object the relevant set of attributes, even when, as here, they probably won't be needed. +.. Generated by autoindex +.. index:: + pair: found_in; library property + You'll also see a new :prop:`found_in` property, which specifies the rooms -- and only the rooms; :prop:`found_in` shouldn't be used to place objects inside containers or supporters -- where this object is to appear. The @@ -285,13 +301,13 @@ well define Wilhelm's bow and arrows:: ], has container open clothing; -Both of these are straightforward objects, with the ``Drop``, ``Give`` and -``ThrowAt`` actions being intercepted to ensure that Wilhelm is never -without them. The :attr:`clothing` attribute makes its first appearance, -marking both the quiver and the bow as capable of being worn (as the result -of a WEAR BOW command, for instance); you'll remember that our -``Initialise`` routine goes on to add a :attr:`worn` attribute to the -quiver. +Both of these are straightforward objects, with the :act:`Drop`, +:act:`Give` and :act:`ThrowAt` actions being intercepted to ensure that +Wilhelm is never without them. The :attr:`clothing` attribute makes its +first appearance, marking both the quiver and the bow as capable of being +worn (as the result of a WEAR BOW command, for instance); you'll remember +that our ``Initialise`` routine goes on to add a :attr:`worn` attribute to +the quiver. An empty quiver is pretty useless, so here's the class used to define Wilhelm's stock of arrows. This class has some unusual features:: @@ -415,29 +431,42 @@ No surprises there, nor in most of the supporting scenery objects. :: description "Fine locally grown produce.", has pluralname; +.. Generated by autoindex +.. index:: + pair: before; library property + The only new thing here is the :prop:`before` property of the fruit'n'veg stall. The stall's description -- lots of items on a table -- may suggest to players that they can SEARCH through the produce, maybe finding a lucky beetroot or something else interesting. No such luck -- and we might as well trap the attempt. -Having intercepted a ``Search`` action, our plan is to respond with the +.. Generated by autoindex +.. index:: + pair: Examine; library action + pair: Search; library action + +Having intercepted a :act:`Search` action, our plan is to respond with the stall's description, as though the player has typed EXAMINE THE STALL. There isn't an easy way for us to stealthily slide those literal words into the interpreter, but we *can* simulate the effect which they'd cause: an -action of ``Examine`` applied to the object stall. This rather cryptic +action of :act:`Examine` applied to the object stall. This rather cryptic statement does the job:: ; -Having diverted the ``Search`` action into an ``Examine`` action, we must -tell the interpreter that it doesn't need to do anything else, because +Having diverted the :act:`Search` action into an :act:`Examine` action, we +must tell the interpreter that it doesn't need to do anything else, because we've handled the action ourselves. We've done that before -- using ``return true`` -- and so a first stab at the :prop:`before` action looks like this:: before [; Search: ; return true; ], +.. Generated by autoindex +.. index:: + pair: self; library variable + The two-statement sequence ``<...>; return true`` is so common that there's a single statement shortcut: ``<<...>>``. Also, for exactly the same reason as before, our code is clearer if we use :var:`self` instead of @@ -446,7 +475,7 @@ reason as before, our code is clearer if we use :var:`self` instead of before [; Search: <>; ], A couple of final observations before we leave this topic. The example -here is of an action (``Examine``) applied to an object (:var:`self`, +here is of an action (:act:`Examine`) applied to an object (:var:`self`, though ``stall`` or :var:`noun` would also work at this point). You can also use the ``<...>`` and ``<<...>>`` statements for actions which affect no objects:: @@ -461,6 +490,10 @@ command PUT THE BIRD IN THE NEST can be simulated with this statement:: Introducing Helga ================= +.. Generated by autoindex +.. index:: + single: NPC + One of the trickiest aspects of designing a good game is to provide satisfying interaction with other characters. It's hard enough to code inanimate objects which provoke appropriate responses to whatever actions @@ -487,15 +520,28 @@ what defines an object as an NPC, and causes the interpreter to treat it a little differently -- for example, TAKE HELGA results in "I don't suppose Helga would care for that". +.. Generated by autoindex +.. index:: + pair: Ask; library action + pair: Attack; library action + pair: Kiss; library action + pair: Order; library action + pair: Show; library action + pair: Tell; library action + pair: ThrowAt; library action + pair: WakeOther; library action + pair: life; library property + The :attr:`animate` attribute also brings into play nine extra actions -which can be applied only to animate objects: ``Answer``, ``Ask``, -``Order`` and ``Tell`` are all associated with speech, and ``Attack``, -``Kiss``, ``Show``, ``ThrowAt`` and ``WakeOther`` are associated with -non-verbal interaction. Additionally, a new :prop:`life` property -- very -similar to :prop:`before` -- can be defined to intercept them. Here we use -it to trap speech-related commands such as ASK HELGA ABOUT APPLE and TELL -WALTER ABOUT BABIES, telling players that in this game we've implemented -only a simpler TALK verb (which we describe in :ref:`verbs`). +which can be applied only to animate objects: :act:`Answer`, :act:`Ask`, +:act:`Order` and :act:`Tell` are all associated with speech, and +:act:`Attack`, :act:`Kiss`, :act:`Show`, :act:`ThrowAt` and +:act:`WakeOther` are associated with non-verbal interaction. Additionally, +a new :prop:`life` property -- very similar to :prop:`before` -- can be +defined to intercept them. Here we use it to trap speech-related commands +such as ASK HELGA ABOUT APPLE and TELL WALTER ABOUT BABIES, telling players +that in this game we've implemented only a simpler TALK verb (which we +describe in :ref:`verbs`). Based on the NPC class we've created, here's Helga:: @@ -540,6 +586,11 @@ incremented. This variable holds the number of points that the player has scored; when it changes like this, the interpreter tells the player that "Your score has just gone up by one point". +.. Generated by autoindex +.. index:: + pair: initial; library property + pair: times_spoken_to; library property + There are also :prop:`life` and :prop:`times_spoken_to` properties (which we'll talk about in :doc:`09`) and an :prop:`initial` property. diff --git a/chapters/08.rst b/chapters/08.rst index c71f5bd..db5715c 100644 --- a/chapters/08.rst +++ b/chapters/08.rst @@ -68,19 +68,30 @@ square from where he's standing, we need to provide a dummy hat on a pole, :prop:`found_in` both this room and the north side of the square, even if it's "too far away" for a detailed description. +.. Generated by autoindex +.. index:: + pair: Examine; library action + pair: before; library property + In fact, it's "too far away" for anything. We've replaced the standard -:prop:`before` action for the ``Prop`` class (which permits ``Examine``, but -rejects other actions with "You don't need to worry about...") with one +:prop:`before` action for the ``Prop`` class (which permits :act:`Examine`, +but rejects other actions with "You don't need to worry about...") with one rejecting *all* actions. Since Wilhelm's hatred of the vogt's activities is central to our plot, a message saying "You don't need to worry about the hat" would be unacceptably misleading. +.. Generated by autoindex +.. index:: + pair: FireAt; library action + pair: Talk; library action + The obnoxious soldiers are also implemented very sketchily; they need to be there, but they don't do much. Their most interesting characteristic is -probably that they trap two actions -- ``FireAt`` and ``Talk`` -- which are -*not* part of the library, but instead new actions that we've defined -specially for this game. We'll talk about those actions in :ref:`verbs`, -at which time the role of this :prop:`before` property will make more sense. +probably that they trap two actions -- :act:`FireAt` and :act:`Talk` -- +which are *not* part of the library, but instead new actions that we've +defined specially for this game. We'll talk about those actions in +:ref:`verbs`, at which time the role of this :prop:`before` property will +make more sense. The middle of the square ======================== @@ -100,10 +111,16 @@ on top. He can do three things: attempt, patience runs out, and Wilhelm is hauled off to perform his party piece. -So, there are two actions that we need to look out for: ``Salute`` (trapped -by the pole), and ``Go`` (which can be trapped by the room itself). ``Go`` -is a standard library action. ``Salute`` is one that we've devised; let's -deal with it first. Here's a first cut of the room:: +.. Generated by autoindex +.. index:: + pair: Go; library action + pair: Salute; library action + +So, there are two actions that we need to look out for: :act:`Salute` +(trapped by the pole), and :act:`Go` (which can be trapped by the room +itself). :act:`Go` is a standard library action. :act:`Salute` is one +that we've devised; let's deal with it first. Here's a first cut of the +room:: Room mid_square "Middle of the square" with description @@ -141,6 +158,10 @@ property which we've not met before: :prop:`has_been_saluted`. What a remarkable coincidence: the library provides a property with a name that's exactly right for our game; surely not? +.. Generated by autoindex +.. index:: + pair: has_been_saluted; library property + No, of course not. :prop:`has_been_saluted` isn't a standard library property; it's one that we've just invented. Notice how easily we did it -- we simply included the line:: @@ -209,8 +230,8 @@ that the salute was "gratefully" received. :ref:`reading-other-code`. Back to the ``mid_square`` room. We've said that we need to detect Wilhelm -trying to leave this room, which we can do by trapping the ``Go`` action in -a :prop:`before` property. Let's sketch the coding we'll need:: +trying to leave this room, which we can do by trapping the :act:`Go` action +in a :prop:`before` property. Let's sketch the coding we'll need:: before [; Go: @@ -218,14 +239,24 @@ a :prop:`before` property. Let's sketch the coding we'll need:: if (noun == n_obj) { Wilhelm is trying to move north } ]; -We can easily trap the ``Go`` action, but which direction is he moving? +.. Generated by autoindex +.. index:: + pair: Go; library action + pair: s_to; library property + +We can easily trap the :act:`Go` action, but which direction is he moving? Well, it turns out that the interpreter turns a command of GO SOUTH (or -just SOUTH) into an action of ``Go`` applied to an object ``s_obj``. This -object is defined by the library; so why isn't it called just "``south``"? -Well, because we already have another kind of south, the property :prop:`s_to` -used to say what lies in a southerly direction when defining a room. To -avoid confusing them, :prop:`s_to` means "south to" and ``s_obj`` means "south -when the player types it as the object of a verb". +just SOUTH) into an action of :act:`Go` applied to an object ``s_obj``. +This object is defined by the library; so why isn't it called just +"``south``"? Well, because we already have another kind of south, the +property :prop:`s_to` used to say what lies in a southerly direction when +defining a room. To avoid confusing them, :prop:`s_to` means "south to" +and ``s_obj`` means "south when the player types it as the object of a +verb". + +.. Generated by autoindex +.. index:: + pair: noun; library variable The identity of the object which is the target of the current action is stored in the :var:`noun` variable, so we can write the statement ``if (noun @@ -320,6 +351,11 @@ not). In effect, we're pretending that the soldier has such a short memory, he'll completely forget Wilhelm if our hero should move away from the pole. +.. Generated by autoindex +.. index:: + pair: has_been_saluted; library property + pair: warnings_count; library property + To do all this, we've added a new property and two statements. The property is :prop:`warnings_count`, and its value will count how many times Wilhelm has tried to go north without saluting the pole: 0 initially, 1 @@ -468,13 +504,17 @@ isn't the 1, 2 or 3 you'd anticipated. Here's the remainder of the code return true; } +.. Generated by autoindex +.. index:: + pair: Go; library action + The first part is really just displaying a lot of text, made slightly messier because we're adding emphasis to the word "Herr" by using underlining (which actually comes out as *italic type* on most interpreters). Then, we make sure that Walter has the apple (just in case we didn't give it to him earlier in the game), relocate to the final room using ``PlayerTo(marketplace)``, and finally ``return true`` to tell the -interpreter that we've handled this part of the ``Go`` action ourselves. +interpreter that we've handled this part of the :act:`Go` action ourselves. And so, at long last, here's the complete code for the ``mid_square``, the most complicated object in the whole game:: diff --git a/chapters/09.rst b/chapters/09.rst index 83d9f8d..ac5ccc8 100644 --- a/chapters/09.rst +++ b/chapters/09.rst @@ -53,14 +53,14 @@ has only one feature of interest:: ], has scenery; -The tree's :prop:`before` property is intercepting a ``FireAt`` action, which -we'll define in a few moments. This action is the result of a command like -SHOOT AT TREE WITH BOW -- we could simulate it with the statement -``<>`` -- and it needs extra care to ensure that the -:var:`second` object is a feasible weapon. To deal with silly commands like -SHOOT AT TREE WITH HELGA, we must test that :var:`second` is the bow, one of -the arrows, or ``nothing`` (from just SHOOT AT TREE). Since this is quite -a complex test, and one that we'll be making in several places, it's +The tree's :prop:`before` property is intercepting a :act:`FireAt` action, +which we'll define in a few moments. This action is the result of a +command like SHOOT AT TREE WITH BOW -- we could simulate it with the +statement ``<>`` -- and it needs extra care to ensure that +the :var:`second` object is a feasible weapon. To deal with silly commands +like SHOOT AT TREE WITH HELGA, we must test that :var:`second` is the bow, +one of the arrows, or ``nothing`` (from just SHOOT AT TREE). Since this is +quite a complex test, and one that we'll be making in several places, it's sensible to write a routine to do the job. Which we'll do shortly -- but first, a general introduction to working with routines. @@ -72,6 +72,10 @@ first, a general introduction to working with routines. A diversion: working with routines ================================== +.. Generated by autoindex +.. index:: + pair: each_turn; library property + A standalone routine, like the familiar routines embedded as the value of a property such as :prop:`before` or :prop:`each_turn`, is simply a set of statements to be executed. The major differences are in content, in timing, and in @@ -87,6 +91,11 @@ the default return value: control when it's called. A standalone routine, however, is completely under your control; it runs only when you explicitly call it. +.. Generated by autoindex +.. index:: + pair: false; library constant + pair: true; library constant + * If an embedded routine executes all of its statements and reaches the final ``];`` without encountering some form of ``return`` statement, it returns the value :const:`false`. In the same circumstances, a standalone @@ -155,6 +164,11 @@ we *could* write another routine to perform that check:: else return false; ]; +.. Generated by autoindex +.. index:: + pair: location; library variable + pair: self; library variable + However, the two routines are very similar; the only difference is the name of the variable -- :var:`self` or :var:`location` -- which is being checked. A better approach might be to rework our ``BeenHereBefore`` routine so that @@ -177,6 +191,10 @@ as a placeholder for a value (here, one of the variables :var:`self` or if (BeenToBefore(location) == true) ... +.. Generated by autoindex +.. index:: + pair: visited; library attribute + In the first line, we supply :var:`self` as the routine's argument. The routine doesn't care where the argument came from; it just sees a value which it knows as ``this_room``, and which it then uses to test for the @@ -210,6 +228,10 @@ Remember that: return false; ], + .. Generated by autoindex + .. index:: + pair: true; library constant + On the other hand, just because a routine returns a value doesn't mean you always *have* to use it; you can simply ignore the value if you want to. The ``TooFarAway`` routine that we showed you earlier in this @@ -239,7 +261,11 @@ and which return values are significant, see :ref:`object-props` and Return to the marketplace ========================= -After all that introduction, finally back to the ``FireAt`` action. We +.. Generated by autoindex +.. index:: + pair: FireAt; library action + +After all that introduction, finally back to the :act:`FireAt` action. We want to check on the characteristics of an object, possibly then displaying a message. We don't know exactly *which* object is to be checked, so we need to write our routine in a generalised way, capable of checking any @@ -275,6 +301,12 @@ The result is that we ask three questions: Is ``o`` the ``bow`` object? *Or* is it ``nothing``? Or, using the ``ofclass`` test, is it any object which is a member of the ``Arrow`` class? +.. Generated by autoindex +.. index:: + pair: before; library property + pair: false; library constant + pair: second; library variable + What this means is that the value returned by the call ``BowOrArrow(bow)`` is :const:`true`, while the value returned by the call ``BowOrArrow(tree)`` is :const:`false`. Or, more generally, the value returned by the call @@ -300,12 +332,16 @@ and the effect is either reacts to that value and ignores the following statements. Then * in both cases, the ``return true`` statement terminates the object's - interception of the ``FireAt`` action. + interception of the :act:`FireAt` action. + +.. Generated by autoindex +.. index:: + pair: deadflag; library variable That whole ``BowOrArrow()`` bit was rather complex, but the rest of the -``FireAt`` action is straightforward. Once the tree has determined that -it's being shot at by something sensible, it can just set :var:`deadflag` to 3 --- the "You have screwed up" ending, display a message, and be done. +:act:`FireAt` action is straightforward. Once the tree has determined that +it's being shot at by something sensible, it can just set :var:`deadflag` +to 3 -- the "You have screwed up" ending, display a message, and be done. Gessler the governor ==================== @@ -349,9 +385,9 @@ There's nothing in Gessler's definition that we haven't already encountered:: ], has male; -Like most NPCs, Gessler has a :prop:`life` property which deals with actions -applicable only to animate objects. This one responds merely to ``Talk`` -(as in TALK TO THE GOVERNOR). +Like most NPCs, Gessler has a :prop:`life` property which deals with +actions applicable only to animate objects. This one responds merely to +:act:`Talk` (as in TALK TO THE GOVERNOR). Walter and the apple ==================== @@ -413,6 +449,10 @@ carrying it. Without the :attr:`transparent` attribute, it would be as though the apple was in his pocket or otherwise out of sight; the interpreter would reply "You can't see any such thing". +.. Generated by autoindex +.. index:: + pair: found_in; library property + Walter has a :prop:`found_in` property which automatically moves him to the player's location on each turn. We can get away with this because in such a short and simple game, he does indeed follow you everywhere. In a more @@ -424,26 +464,46 @@ that is, is the player (and hence Walter) currently in that room? The events in the marketplace are such that specialised responses are more appropriate there than our standard ones. -Walter's :prop:`life` property responds to ``Give`` (as in GIVE APPLE TO -WALTER) and Talk (as in TALK TO YOUR SON); during ``Give``, we increment -the library variable :var:`score`, thus rewarding the player's generous good -nature. His :prop:`before` property is perhaps a little confusing. It's -saying: +.. Generated by autoindex +.. index:: + pair: Give; library action + pair: before; library property + pair: score; library variable -#. The ``Examine``, ``Listen``, ``Salute`` and ``Talk`` actions are always - available (a ``Talk`` action then gets passed to Walter's :prop:`life` - property). +Walter's :prop:`life` property responds to :act:`Give` (as in GIVE APPLE TO +WALTER) and Talk (as in TALK TO YOUR SON); during :act:`Give`, we increment +the library variable :var:`score`, thus rewarding the player's generous +good nature. His :prop:`before` property is perhaps a little confusing. +It's saying: + +.. Generated by autoindex +.. index:: + pair: Examine; library action + pair: Listen; library action + pair: Salute; library action -#. The ``FireAt`` action is permitted in the ``marketplace``, albeit with - unfortunate results. Elsewhere, it triggers the standard ``FireAt`` - response of "Unthinkable!" +#. The :act:`Examine`, :act:`Listen`, :act:`Salute` and :act:`Talk` actions + are always available (a :act:`Talk` action then gets passed to Walter's + :prop:`life` property). + +.. Generated by autoindex +.. index:: + pair: FireAt; library action + +#. The :act:`FireAt` action is permitted in the ``marketplace``, albeit + with unfortunate results. Elsewhere, it triggers the standard + :act:`FireAt` response of "Unthinkable!" #. All other actions are prevented in the ``marketplace``, and allowed to run their standard course (thanks to the ``return false``) elsewhere. -The apple's moment of glory has arrived! Its :prop:`before` property responds -to the ``FireAt`` action by setting :var:`deadflag` to 2. When that happens, -the game is over; the player has won. :: +.. Generated by autoindex +.. index:: + pair: deadflag; library variable + +The apple's moment of glory has arrived! Its :prop:`before` property +responds to the :act:`FireAt` action by setting :var:`deadflag` to 2. When +that happens, the game is over; the player has won. :: Object apple "apple" with name 'apple', @@ -488,6 +548,10 @@ verbs. That's the final task. Verbs, verbs, verbs =================== +.. Generated by autoindex +.. index:: + pair: Talk; library action + The Inform library delivers a standard set of nearly a hundred actions which players can perform; around twenty of those are "meta-actions" (like SAVE and QUIT) aimed at the interpreter itself, and the remainder operate @@ -496,8 +560,8 @@ blessing; it means that many of the actions which players might attempt are already catered for, either by the interpreter doing something useful, or by explaining why it's unable to. Nevertheless, most games find the need to define additional actions, and "William Tell" is no exception. We'll be -adding four actions of our own: ``Untie``, ``Salute``, ``FireAt`` and -``Talk``. +adding four actions of our own: :act:`Untie`, :act:`Salute`, :act:`FireAt` +and :act:`Talk`. .. rubric:: Untie @@ -531,9 +595,13 @@ The grammar is less complex than it perhaps at first appears: #. The ``->`` indicates an action to be triggered. +.. Generated by autoindex +.. index:: + pair: Untie; library action + #. If players type something that matches the pattern -- one of those five verbs followed by an object in scope -- the interpreter triggers an - ``Untie`` action, which by default is handled by a routine having the + :act:`Untie` action, which by default is handled by a routine having the same name as the action, with ``Sub`` appended. In this example, that's the ``UntieSub`` routine. @@ -569,17 +637,17 @@ We can illustrate how this works in the Altdorf street: The illustration shows four attempted usages of the new action. In the first, the player omits to mention an object; the interpreter knows (from -that :var:`noun` in the grammar which implies that the action needs a direct -object) that something is missing, so it issues a helpful prompt. In the -second, the player mentions an object that isn't in scope (in fact, there's -no dog anywhere in the game, but the interpreter isn't about to give *that* -away to the player). In the third, the object is in scope, but its -:prop:`before` property intercepts the ``Untie`` action (and indeed, since this -object is of the class ``Prop``, all actions apart from ``Examine``) to -display a customised rejection message. Finally, the fourth usage refers -to an object which *doesn't* intercept the action, so the interpreter calls -the default action handler -- ``UntieSub`` -- which displays a -general-purpose refusal to perform the action. +that :var:`noun` in the grammar which implies that the action needs a +direct object) that something is missing, so it issues a helpful prompt. +In the second, the player mentions an object that isn't in scope (in fact, +there's no dog anywhere in the game, but the interpreter isn't about to +give *that* away to the player). In the third, the object is in scope, but +its :prop:`before` property intercepts the :act:`Untie` action (and indeed, +since this object is of the class ``Prop``, all actions apart from +:act:`Examine`) to display a customised rejection message. Finally, the +fourth usage refers to an object which *doesn't* intercept the action, so +the interpreter calls the default action handler -- ``UntieSub`` -- which +displays a general-purpose refusal to perform the action. The principles presented here are those that you should generally employ: write a generic action handler which either refuses to do anything (see, @@ -589,23 +657,28 @@ that non-action (generally using a :prop:`before` property) for those objects which might make a legitimate target for the action, and instead provide a more specific response, either performing or rejecting the action. -In the case of ``Untie``, there are no objects which can be untied in this -game, so we always generate a refusal of some sort. +In the case of :act:`Untie`, there are no objects which can be untied in +this game, so we always generate a refusal of some sort. .. rubric:: Salute -The next action is ``Salute``, provided in case Wilhelm chooses to defer to -the hat on the pole. Here's the default action handler:: +.. Generated by autoindex +.. index:: + pair: Salute; library action + +The next action is :act:`Salute`, provided in case Wilhelm chooses to defer +to the hat on the pole. Here's the default action handler:: [ SaluteSub; if (noun has animate) print_ret (The) noun, " acknowledges you."; print_ret (The) noun, " takes no notice."; ]; -You'll notice that this is slightly more intelligent than our ``Untie`` +You'll notice that this is slightly more intelligent than our :act:`Untie` handler, since it produces different responses depending on whether the -object being saluted -- stored in the :var:`noun` variable -- is :attr:`animate` -or not. But it's basically doing the same job. And here's the grammar:: +object being saluted -- stored in the :var:`noun` variable -- is +:attr:`animate` or not. But it's basically doing the same job. And here's +the grammar:: Verb 'bow' 'nod' 'kowtow' 'genuflect' * 'at'/'to'/'towards' noun -> Salute; @@ -626,8 +699,8 @@ This grammar says that: the same room as the player. #. If players type something that matches one of those patterns, the - interpreter triggers a ``Salute`` action, which by default is dealt with - by the ``SaluteSub`` routine. + interpreter triggers a :act:`Salute` action, which by default is dealt + with by the ``SaluteSub`` routine. So, we're allowing BOW AT HAT and KOWTOW TOWARDS HAT, but not simply NOD HAT. We're allowing SALUTE HAT but not GREET TO HAT. It's not perfect, @@ -735,6 +808,11 @@ FIRE TREE). The third line:: accepts FIRE AT APPLE, FIRE AT TREE, and so on. Note that there's only one semicolon in all of the grammar, right at the very end. +.. Generated by autoindex +.. index:: + pair: noun; library variable + pair: second; library variable + The first two statements in ``FireAtSub`` deal with the first line of grammar: FIRE (or SHOOT, or AIM) by itself. If the player types just that, both :var:`noun` and :var:`second` will contain ``nothing``, so we reject the @@ -765,7 +843,12 @@ two objects back into the expected order by adding that ``reverse`` keyword at the end of the line, and then ``FireAtSub`` will work the same in all cases. -Before leaving the ``FireAt`` action, we'll add one more piece of grammar:: +.. Generated by autoindex +.. index:: + pair: FireAt; library action + +Before leaving the :act:`FireAt` action, we'll add one more piece of +grammar:: Extend 'attack' replace * noun -> FireAt; @@ -773,26 +856,38 @@ Before leaving the ``FireAt`` action, we'll add one more piece of grammar:: This uses the ``Extend`` directive which we've just met, this time with a ``replace`` keyword. The effect is to substitute the new grammar defined here for that contained in ``Grammar.h``, so that ATTACK, KILL, MURDER and -all the other violent synonyms now trigger our ``FireAt`` action instead of -the Library's standard ``Attack`` action. We're doing this so that, in the -Marketplace, KILL GESSLER and MURDER WALTER have the same unfortunate -results as FIRE AT GESSLER and SHOOT WALTER. +all the other violent synonyms now trigger our :act:`FireAt` action instead +of the Library's standard :act:`Attack` action. We're doing this so that, +in the Marketplace, KILL GESSLER and MURDER WALTER have the same +unfortunate results as FIRE AT GESSLER and SHOOT WALTER. .. rubric:: Talk -The final action that we define -- ``Talk`` -- provides a simple system of -canned conversation, a low-key replacement for the standard ``Answer``, -``Ask`` and ``Tell`` actions. The default ``TalkSub`` handler is closely -based on ``TellSub`` (defined in library file ``verblibm.h``, should you be -curious), and does three things: +.. Generated by autoindex +.. index:: + pair: Answer; library action + pair: Ask; library action + pair: Talk; library action + pair: Tell; library action + +The final action that we define -- :act:`Talk` -- provides a simple system +of canned conversation, a low-key replacement for the standard +:act:`Answer`, :act:`Ask` and :act:`Tell` actions. The default ``TalkSub`` +handler is closely based on ``TellSub`` (defined in library file +``verblibm.h``, should you be curious), and does three things: #. Deals with TALK TO ME or TALK TO MYSELF. +.. Generated by autoindex +.. index:: + pair: life; library property + pair: true; library constant + #. Checks (a) whether the creature being talked to has a :prop:`life` - property, (b) whether that property is prepared to process a ``Talk`` - action, and (c) if the ``Talk`` processing returns :const:`true`. If all - three checks succeed then ``TalkSub`` need do nothing more; if one or - more of them fails then ``TalkSub`` simply... + property, (b) whether that property is prepared to process a :act:`Talk` + action, and (c) if the :act:`Talk` processing returns :const:`true`. If + all three checks succeed then ``TalkSub`` need do nothing more; if one + or more of them fails then ``TalkSub`` simply... #. Displays a general "nothing to say" refusal to talk. :: @@ -806,12 +901,13 @@ curious), and does three things: That second condition ``(RunLife(noun,##Talk) ~= false)`` is a bit of a stumbling block, since it uses ``RunLife`` -- an undocumented - internal library routine -- to offer the ``Talk`` action to the NPC's - :prop:`life` property. We've decided to use it in exactly the same way - as the ``Tell`` action does, without worrying too much about how it - works (though it looks as though ``RunLife`` returns some :const:`true` - value if the :prop:`life` property has intercepted the action, :const:`false` - if it hasn't). The ``~=`` operator means "not equal to". + internal library routine -- to offer the :act:`Talk` action to the + NPC's :prop:`life` property. We've decided to use it in exactly the + same way as the :act:`Tell` action does, without worrying too much + about how it works (though it looks as though ``RunLife`` returns + some :const:`true` value if the :prop:`life` property has intercepted + the action, :const:`false` if it hasn't). The ``~=`` operator means + "not equal to". The grammar is straightforward:: @@ -826,15 +922,15 @@ prompts "Whom do you want to t to?" The fix for this involves enhancing an internal library routine called ``LanguageVerb`` -- not complex, but a little too heavy for our second game.) -Here's the simplest ``Talk`` handler that we've seen -- it's from Gessler -the governor. Any attempt to TALK TO GESSLER will provoke "You cannot -bring yourself to speak to him". :: +Here's the simplest :act:`Talk` handler that we've seen -- it's from +Gessler the governor. Any attempt to TALK TO GESSLER will provoke "You +cannot bring yourself to speak to him". :: life [; Talk: print_ret "You cannot bring yourself to speak to him."; ], -Walter's ``Talk`` handler is only slightly more involved:: +Walter's :act:`Talk` handler is only slightly more involved:: life [; Talk: diff --git a/chapters/10.rst b/chapters/10.rst index 6c785f9..fd9cbf5 100644 --- a/chapters/10.rst +++ b/chapters/10.rst @@ -14,51 +14,50 @@ Captain Fate: take 1 |S|\imple though they are, our two games have covered most of the basic functionality of Inform, providing enough solid ground underfoot for you to -start creating simple stories. Even if some of what you've encountered +start creating simple stories. Even if some of what you've encountered doesn't make sense yet, you should be able to browse a game's source code and form a general understanding of what is going on. -We'll now design a third game, to show you a few additional features and -give you some more sample code to analyse. In "Heidi" we tried to make -progress step by step, explaining every bit of code that went into the -game as we laid the objects sequentially; in "William Tell" you'll have -noticed that we took a few necessary explanatory detours, as the -concepts grew more interesting and complicated. Here we'll organise the -information in logical didactic chunks, defining some of the objects -minimally at first and then adding complexity as need arises. Again, -this means that you won't be able to compile for testing purposes after -the addition of every code snippet, so, if you're typing in the game as -you read, you’ll need to check the advice in :ref:`compile-as-you-go`. - -A lot of what goes into this game we have already seen; you may deduce -from this that the game design business is fairly repetitious and that -most games are, when you reach the programming bottom line, another -remake of the same old theme. Well, yes and no: you've got a camera and -have seen some short home videos in the making, but it’s a long way from -here to Casablanca. To stick with the analogy, we'll now construct the -opening sequence of an indie B-movie, a tribute to the style of -super-hero made famous by a childhood of comic books: +We'll now design a third game, to show you a few additional features and +give you some more sample code to analyse. In "Heidi" we tried to make +progress step by step, explaining every bit of code that went into the game +as we laid the objects sequentially; in "William Tell" you'll have noticed +that we took a few necessary explanatory detours, as the concepts grew more +interesting and complicated. Here we'll organise the information in +logical didactic chunks, defining some of the objects minimally at first +and then adding complexity as need arises. Again, this means that you +won't be able to compile for testing purposes after the addition of every +code snippet, so, if you're typing in the game as you read, you’ll need to +check the advice in :ref:`compile-as-you-go`. + +A lot of what goes into this game we have already seen; you may deduce from +this that the game design business is fairly repetitious and that most +games are, when you reach the programming bottom line, another remake of +the same old theme. Well, yes and no: you've got a camera and have seen +some short home videos in the making, but it’s a long way from here to +Casablanca. To stick with the analogy, we'll now construct the opening +sequence of an indie B-movie, a tribute to the style of super-hero made +famous by a childhood of comic books: .. pull-quote:: - "Impersonating mild mannered John Covarth, assistant help boy at an - insignificant drugstore, you suddenly STOP when your acute hearing - deciphers a stray radio call from the POLICE. There’s some MADMAN - attacking the population in Granary Park! You must change into your - Captain FATE costume fast...!" + "Impersonating mild mannered John Covarth, assistant help boy at an + insignificant drugstore, you suddenly STOP when your acute hearing + deciphers a stray radio call from the POLICE. There’s some MADMAN + attacking the population in Granary Park! You must change into your + Captain FATE costume fast...!" -which won't be so easy to do. In this short example, players will win -when they manage to change into their super-hero costume and fly away to -meet the foe. The confrontation will -- perhaps -- take place in some -other game, where we can but hope that Captain Fate will vanquish the -forces of evil, thanks to his mysterious (and here unspecified) -superpowers. +which won't be so easy to do. In this short example, players will win when +they manage to change into their super-hero costume and fly away to meet +the foe. The confrontation will -- perhaps -- take place in some other +game, where we can but hope that Captain Fate will vanquish the forces of +evil, thanks to his mysterious (and here unspecified) superpowers. Fade up on: a nondescript city street ===================================== -The game starts with meek John Covarth walking down the street. We set -up the game as usual: +The game starts with meek John Covarth walking down the street. We set up +the game as usual: .. code-block:: inform @@ -134,32 +133,36 @@ Almost everything is familar, apart from a few details: Constant OBJECT_SCORE 1; Constant ROOM_SCORE 1; -By default, Inform uses a system of automatic pronouns: as the player -character moves into a room, the library assigns pronouns like IT and -HIM to likely objects (if you play "Heidi" or "William Tell" and type -PRONOUNS, you can see how the settings change). There is another option. -If we declare the ``MANUAL_PRONOUNS`` onstant, we force the library to -assign pronouns to objects only as the player mentions them (that is, IT -will be unassigned until the player types, say, EXAMINE TREE, at which -point, IT becomes the TREE ). The behaviour of pronoun assignment is a -matter of personal taste; no system is objectively perfect. - -Apart from the constant ``MAX_SCORE`` that we have seen in "William -Tell", which defines the maximum number of points to be scored, we now -see two more constants: ``OBJECT_SCORE`` and ``ROOM_SCORE``. There are -several scoring systems predefined in Inform. In "William Tell" we've -seen how you can manually add (or subtract) points by changing the value -of the variable :var:`score`. Another approach is to award points to -players on the first occasion that they (a) enter a particular room, or -(b) pick up a particular object. To define that a room or object is -indeed “particular”, all you have to do is give it the attribute -:attr:`scored`; the library take cares of the rest. The predefined scores -are five points for finally reached rooms and four points for wondrous -acquisition of objects. With the constants ``OBJECT_SCORE`` and -``ROOM_SCORE`` we can change those defaults; for the sake of example, -we've decided to modestly award one point for each. By the way, the use -of an equals sign ``=`` is optional with ``Constant``; these two lines -have identical effect: +By default, Inform uses a system of automatic pronouns: as the player +character moves into a room, the library assigns pronouns like IT and HIM +to likely objects (if you play "Heidi" or "William Tell" and type PRONOUNS, +you can see how the settings change). There is another option. If we +declare the ``MANUAL_PRONOUNS`` constant, we force the library to assign +pronouns to objects only as the player mentions them (that is, IT will be +unassigned until the player types, say, EXAMINE TREE, at which point, IT +becomes the TREE ). The behaviour of pronoun assignment is a matter of +personal taste; no system is objectively perfect. + +.. Generated by autoindex +.. index:: + pair: score; library variable + pair: scored; library attribute + +Apart from the constant ``MAX_SCORE`` that we have seen in "William Tell", +which defines the maximum number of points to be scored, we now see two +more constants: ``OBJECT_SCORE`` and ``ROOM_SCORE``. There are several +scoring systems predefined in Inform. In "William Tell" we've seen how you +can manually add (or subtract) points by changing the value of the variable +:var:`score`. Another approach is to award points to players on the first +occasion that they (a) enter a particular room, or (b) pick up a particular +object. To define that a room or object is indeed “particular”, all you +have to do is give it the attribute :attr:`scored`; the library take cares +of the rest. The predefined scores are five points for finally reached +rooms and four points for wondrous acquisition of objects. With the +constants ``OBJECT_SCORE`` and ``ROOM_SCORE`` we can change those defaults; +for the sake of example, we've decided to modestly award one point for +each. By the way, the use of an equals sign ``=`` is optional with +``Constant``; these two lines have identical effect: .. code-block:: inform @@ -167,9 +170,8 @@ have identical effect: Constant ROOM_SCORE = 1; -Another difference has to do with a special short-hand method that -Inform provides for displaying strings of text. Until now, we have shown -you: +Another difference has to do with a special short-hand method that Inform +provides for displaying strings of text. Until now, we have shown you: .. code-block:: inform @@ -177,24 +179,28 @@ you: ... print_ret "And now for something completely different..."; -Both lines do the same thing: they display the quoted string, output a -newline character, and return true. As you have seen in the previous -example games, this happens quite a lot, so there is a yet shorter way -of achieving the same result: +Both lines do the same thing: they display the quoted string, output a +newline character, and return true. As you have seen in the previous +example games, this happens quite a lot, so there is a yet shorter way of +achieving the same result: .. code-block:: inform "And now for something completely different..."; -That is, *in a routine* (where the compiler is expecting to find a -collection of statements each terminated by a semicolon), a string in -double quotes by itself, without the need for any explicit keywords, -works exactly as if there were a ``print_ret`` in front of it. Remember -that this way of displaying text implies a ``return true`` at the end -(which therefore exits from the routine immediately). This detail -becomes important if we *don't* want to return true after the string -has been displayed on the screen -- we should use the explicit ``print`` -statement instead. +That is, *in a routine* (where the compiler is expecting to find a +collection of statements each terminated by a semicolon), a string in +double quotes by itself, without the need for any explicit keywords, works +exactly as if there were a ``print_ret`` in front of it. Remember that +this way of displaying text implies a ``return true`` at the end (which +therefore exits from the routine immediately). This detail becomes +important if we *don't* want to return true after the string has been +displayed on the screen -- we should use the explicit ``print`` statement +instead. + +.. Generated by autoindex +.. index:: + pair: name; library property You'll notice that -- unusually for a room -- our ``street`` object has a :prop:`name` property: @@ -205,21 +211,25 @@ a :prop:`name` property: with name 'city' 'buildings' 'skyscrapers' 'shops' 'apartments' 'cars', ... -Rooms aren't normally referenced by name, so this may seem odd. In fact, -we're illustrating a feature of Inform: the ability to define dictionary -words as "known but irrelevant" in this location. If the player types -EXAMINE CITY here, the interpreter will reply "That's not something you -need to refer to in order to SAVE the day", rather than the misleading -"You can't see any such thing". We mostly prefer to deal with such -scenic words using classes like ``Prop`` and ``Furniture``, but -sometimes a room's :prop:`name` property is a quick and convenient solution. - -In this game, we provide a class named ``Appliance`` to take care of -furniture and unmovable objects. You’ll notice that the starting room we -have defined has no connections yet. The description mentions a phone -booth and a café, so we might want to code those. While the café will be -a normal room, it would seem logical that the phone booth is actually a -big box on the sidewalk; therefore we define a :attr:`container` set in the +Rooms aren't normally referenced by name, so this may seem odd. In fact, +we're illustrating a feature of Inform: the ability to define dictionary +words as "known but irrelevant" in this location. If the player types +EXAMINE CITY here, the interpreter will reply "That's not something you +need to refer to in order to SAVE the day", rather than the misleading "You +can't see any such thing". We mostly prefer to deal with such scenic words +using classes like ``Prop`` and ``Furniture``, but sometimes a room's +:prop:`name` property is a quick and convenient solution. + +.. Generated by autoindex +.. index:: + pair: container; library attribute + +In this game, we provide a class named ``Appliance`` to take care of +furniture and unmovable objects. You’ll notice that the starting room we +have defined has no connections yet. The description mentions a phone +booth and a café, so we might want to code those. While the café will be a +normal room, it would seem logical that the phone booth is actually a big +box on the sidewalk; therefore we define a :attr:`container` set in the street, which players may enter: .. code-block:: inform @@ -242,28 +252,28 @@ street, which players may enter: ], has enterable container open; -What's interesting are the attributes at the end of the definition. -You'll recall from Heidi's ``nest`` object that a :attr:`container` is an -object capable of having other objects placed in it. If we make -something :attr:`enterable`, players count as one of those objects, so that -they may squeeze inside. Finally, ``containers`` are, by default, -supposed to be closed. You can make them :attr:`openable` if you wish -players to be able to OPEN and CLOSE them at will, but this doesn't seem -appropriate behaviour for a public cabin -- it would become tedious to -have to type OPEN BOOTH and CLOSE BOOTH when these actions provide -nothing special -- so we add instead the attribute :attr:`open` (as we did -with the nest), telling the interpreter that the container is open from -the word go. Players aren't aware of our design, of course; they may -indeed try to OPEN and CLOSE the booth, so we trap those actions in a -:prop:`before` property which just tells them that these are not relevant -options. The :prop:`after` property gives a customised message to override -the library’s default for commands like ENTER BOOTH or GO INSIDE BOOTH. - -Since in the street's description we have told players that the phone -booth is to the south, they might also try typing SOUTH. We must -intercept this attempt and redirect it (while we're at it, we add a -connection to the as-yet-undefined café room and a default message for -the movement which is not allowed): +What's interesting are the attributes at the end of the definition. You'll +recall from Heidi's ``nest`` object that a :attr:`container` is an object +capable of having other objects placed in it. If we make something +:attr:`enterable`, players count as one of those objects, so that they may +squeeze inside. Finally, ``containers`` are, by default, supposed to be +closed. You can make them :attr:`openable` if you wish players to be able +to OPEN and CLOSE them at will, but this doesn't seem appropriate behaviour +for a public cabin -- it would become tedious to have to type OPEN BOOTH +and CLOSE BOOTH when these actions provide nothing special -- so we add +instead the attribute :attr:`open` (as we did with the nest), telling the +interpreter that the container is open from the word go. Players aren't +aware of our design, of course; they may indeed try to OPEN and CLOSE the +booth, so we trap those actions in a :prop:`before` property which just +tells them that these are not relevant options. The :prop:`after` property +gives a customised message to override the library’s default for commands +like ENTER BOOTH or GO INSIDE BOOTH. + +Since in the street's description we have told players that the phone booth +is to the south, they might also try typing SOUTH. We must intercept this +attempt and redirect it (while we're at it, we add a connection to the +as-yet-undefined café room and a default message for the movement which is +not allowed): .. code-block:: inform @@ -279,12 +289,12 @@ the movement which is not allowed): "No time now for exploring! You'll move much faster in your Captain FATE costume."; -That takes care of entering the booth. But what about leaving it? -Players may type EXIT or OUT while they are inside an enterable -container and the interpreter will oblige but, again, they might type -NORTH. This is a problem, since we are actually in the street (albeit -inside the booth) and to the north we have the café. We may provide for -this condition in the room's :prop:`before` property: +That takes care of entering the booth. But what about leaving it? Players +may type EXIT or OUT while they are inside an enterable container and the +interpreter will oblige but, again, they might type NORTH. This is a +problem, since we are actually in the street (albeit inside the booth) and +to the north we have the café. We may provide for this condition in the +room's :prop:`before` property: .. code-block:: inform @@ -293,11 +303,11 @@ this condition in the room's :prop:`before` property: if (player in booth && noun == n_obj) <>; ], -Since we are outdoors and the booth provides a shelter, it's not -impossible that a player may try just IN, which is a perfectly valid -connection. However, that would be an ambiguous command, for it could -also refer to the café, so we express our bafflement and force the -player to try something else: +Since we are outdoors and the booth provides a shelter, it's not impossible +that a player may try just IN, which is a perfectly valid connection. +However, that would be an ambiguous command, for it could also refer to the +café, so we express our bafflement and force the player to try something +else: .. code-block:: inform @@ -305,9 +315,9 @@ player to try something else: s_to [; <>; ], in_to "But which way?", -Now everything seems to be fine, except for a tiny detail. We've said -that, while in the booth, the player character’s location is still the -``street`` room, regardless of being inside a :attr:`container`; if players +Now everything seems to be fine, except for a tiny detail. We've said +that, while in the booth, the player character’s location is still the +``street`` room, regardless of being inside a :attr:`container`; if players chanced to type LOOK, they'd get: .. code-block:: transcript @@ -317,19 +327,19 @@ chanced to type LOOK, they'd get: there's an open cafe now serving lunch. To the south, you can see a phone booth. -Hardly an adequate description while *inside* the booth. There are -several ways to fix the problem, depending on the result you wish to -achieve. The library provides a property called :prop:`inside_description` -which you can utilise with enterable containers. It works pretty much -like the normal :prop:`description` property, but it gets printed only when -the player is inside the container. The library makes use of this -property in a very clever way, because for every LOOK action it checks -whether we can see outside the container: if the container has the -:attr:`transparent` attribute set, or if it happens to be :attr:`open`, the -library displays the normal :prop:`description` of the room first and then -the :prop:`inside_description` of the container. If the library decides we -can’t see outside the container, only the inside_description is -displayed. Take for instance the following (simplified) example: +Hardly an adequate description while *inside* the booth. There are several +ways to fix the problem, depending on the result you wish to achieve. The +library provides a property called :prop:`inside_description` which you can +utilise with enterable containers. It works pretty much like the normal +:prop:`description` property, but it gets printed only when the player is +inside the container. The library makes use of this property in a very +clever way, because for every LOOK action it checks whether we can see +outside the container: if the container has the :attr:`transparent` +attribute set, or if it happens to be :attr:`open`, the library displays +the normal :prop:`description` of the room first and then the +:prop:`inside_description` of the container. If the library decides we +can’t see outside the container, only the :prop:`inside_description` is +displayed. Take for instance the following (simplified) example: .. code-block:: inform @@ -349,7 +359,7 @@ displayed. Take for instance the following (simplified) example: ... has container openable enterable light; -Now, the player would be able to OPEN BOX and ENTER BOX. A player who +Now, the player would be able to OPEN BOX and ENTER BOX. A player who tried a LOOK would get: .. code-block:: transcript @@ -368,14 +378,14 @@ If now the player closes the box and LOOKs: The inside panels of the magic box are covered with black velvet. There is a tiny switch by your right foot. -In our case, however, we don't wish the description of the street to be -displayed at all (even if a caller is supposedly able to see the street -while inside a booth). The problem is that we have made the booth an -:attr:`open` container, so the street's description would be displayed every -time. There is another solution. We can make the :prop:`description` -property of the ``street`` room a bit more complex, and change its -value: instead of a string, we write an embedded routine. Here's the -(almost) finished room: +In our case, however, we don't wish the description of the street to be +displayed at all (even if a caller is supposedly able to see the street +while inside a booth). The problem is that we have made the booth an +:attr:`open` container, so the street's description would be displayed +every time. There is another solution. We can make the +:prop:`description` property of the ``street`` room a bit more complex, and +change its value: instead of a string, we write an embedded routine. +Here's the (almost) finished room: .. code-block:: inform @@ -402,7 +412,7 @@ value: instead of a string, we write an embedded routine. Here's the Captain FATE costume."; The description while inside the booth mentions the sidewalk, which -might invite the player to EXAMINE it. No problem: +might invite the player to EXAMINE it. No problem: .. code-block:: inform @@ -415,7 +425,7 @@ might invite the player to EXAMINE it. No problem: the CITY!"; Unfortunately, both descriptions also mention the café, which will be a -room and therefore not, from the outside, an examinable object. The +room and therefore not, from the outside, an examinable object. The player may enter it and will get whatever description we code as the result of a LOOK action (which will have to do with the way the café looks from the *inside*); but while we are on the street we need @@ -444,10 +454,10 @@ something else to describe it: Although the text of our guide calls Benny's establishment a "café" -- note the acute "e" -- the game itself simplifies this to "cafe". We do this for clarity, not because Inform doesn't support accented - characters. The |DM4| explains in detail how to display these characters - in :dm4:`§1.11 ` "*How text is printed*" and provides the - whole Z-machine character set in Table 2. In our case, we could have - displayed this:: + characters. The |DM4| explains in detail how to display these + characters in :dm4:`§1.11 ` "*How text is printed*" and + provides the whole Z-machine character set in Table 2. In our case, we + could have displayed this:: The town's favourite for a quick snack, Benny's café has a 50's ROCKETSHIP look. @@ -455,39 +465,43 @@ something else to describe it: .. code-block:: inform - description - "The town's favourite for a quick snack, Benny's caf@'e has a 50's - ROCKETSHIP look.", + description + "The town's favourite for a quick snack, Benny's caf@'e has a 50's + ROCKETSHIP look.", - description - "The town's favourite for a quick snack, Benny's caf@@170 has a 50's - ROCKETSHIP look.", + description + "The town's favourite for a quick snack, Benny's caf@@170 has a 50's + ROCKETSHIP look.", - description - "The town's favourite for a quick snack, Benny's caf@{E9} has a 50's - ROCKETSHIP look.", + description + "The town's favourite for a quick snack, Benny's caf@{E9} has a 50's + ROCKETSHIP look.", - However, all three forms are harder to read than the vanilla "cafe", so + However, all three forms are harder to read than the vanilla "cafe", so we've opted for the simple life. -Unlike the sidewalk object, we offer more than a mere description. Since -the player may try ENTER CAFE as a reasonable way of access -- which -would have confused the interpreter immensely -- we take the opportunity -of making this object also :attr:`enterable`, and we cheat a little. The -attribute :attr:`enterable` has permitted the verb ENTER to be applied to -this object, but this is not a :attr:`container`; we want the player to be -sent into the *real* café room instead. The :prop:`before` property handles -this, intercepting the action, displaying a message and teleporting the -player into the café. We ``return true`` to inform the interpreter that -we have taken care of the ``Enter`` action ourselves, so it can stop -right there. - -As a final detail, note that we now have two ways of going into the -café: the :prop:`n_to` property of the ``street`` room and the ``Enter`` -action of the ``outside_of_cafe`` object. A perfectionist might point -out that it would be neater to handle the actual movement of the player -in just one place of our code, because this helps clarity. To achieve -this, we redirect the street's :prop:`n_to` property thus: +Unlike the sidewalk object, we offer more than a mere description. Since +the player may try ENTER CAFE as a reasonable way of access -- which would +have confused the interpreter immensely -- we take the opportunity of +making this object also :attr:`enterable`, and we cheat a little. The +attribute :attr:`enterable` has permitted the verb ENTER to be applied to +this object, but this is not a :attr:`container`; we want the player to be +sent into the *real* café room instead. The :prop:`before` property handles +this, intercepting the action, displaying a message and teleporting the +player into the café. We ``return true`` to inform the interpreter that we +have taken care of the :act:`Enter` action ourselves, so it can stop right +there. + +.. Generated by autoindex +.. index:: + pair: n_to; library property + +As a final detail, note that we now have two ways of going into the café: +the :prop:`n_to` property of the ``street`` room and the :act:`Enter` +action of the ``outside_of_cafe`` object. A perfectionist might point out +that it would be neater to handle the actual movement of the player in just +one place of our code, because this helps clarity. To achieve this, we +redirect the street's :prop:`n_to` property thus: .. code-block:: inform @@ -497,10 +511,10 @@ You may think that this is unnecessary madness, but a word to the wise: in a large game, you want action handling going on just in one place when possible, because it will help you to keep track of where things are a-happening if something goes *ploof* (as, believe us, it will; see -:doc:`16`). You don't need to be a perfectionist, just cautious. +:doc:`16`). You don't need to be a perfectionist, just cautious. A booth in this kind of situation is an open invitation for the player to -step inside and try to change into Captain Fate's costume. We won't let +step inside and try to change into Captain Fate's costume. We won't let this happen -- the player isn't Clark Kent, after all; later we'll explain how we forbid this action -- and that will force the player to go inside the café, looking for a discreet place to disrobe; but first, let's freeze @@ -511,29 +525,36 @@ A hero is not an ordinary person Which is to say, normal actions won't be the same for him. -As you have probably inferred from the previous chapters, some of the -library’s standard defined actions are less important than others in -making the game advance towards one of its conclusions. The library -defines PRAY and SING, for instance, which are of little consequence in -a normal gaming situation; each displays an all-purpose message, -sufficiently non-committal, and that's it. Of course, if your game -includes a magic portal that will reveal itself only if the player lets -rip with a snatch of Wagner, you may intercept the ``Sing`` action in a -:prop:`before` property and alter its default, pretty useless behaviour. If -not, it's "Your singing is abominable" for you. +.. Generated by autoindex +.. index:: + pair: Sing; library action + +As you have probably inferred from the previous chapters, some of the +library’s standard defined actions are less important than others in making +the game advance towards one of its conclusions. The library defines PRAY +and SING, for instance, which are of little consequence in a normal gaming +situation; each displays an all-purpose message, sufficiently +non-committal, and that's it. Of course, if your game includes a magic +portal that will reveal itself only if the player lets rip with a snatch of +Wagner, you may intercept the :act:`Sing` action in a :prop:`before` +property and alter its default, pretty useless behaviour. If not, it's +"Your singing is abominable" for you. All actions, useful or not, have a stock of messages associated with them (the messages are held in the ``english.h`` library file and listed in -:dm4:`Appendix 4 ` of the |DM4|). We have already seen one way of -altering the player character's description -- "As good looking as ever" -- -in "William Tell", but the other defaults may also be redefined to suit +:dm4:`Appendix 4 ` of the |DM4|). We have already seen one way +of altering the player character's description -- "As good looking as ever" +-- in "William Tell", but the other defaults may also be redefined to suit your tastes and circumstantial needs. -John Covarth, aka Captain Fate, could happily settle for most of these -default messages, but we deem it worthwhile to give him some customised -responses. If nothing else, this adds to the general atmosphere, a -nicety that many players regard as important. For this mission, we make -use of the ``LibraryMessages`` object. +.. index:: + pair: LibraryMessages; library object + +John Covarth, aka Captain Fate, could happily settle for most of these +default messages, but we deem it worthwhile to give him some customised +responses. If nothing else, this adds to the general atmosphere, a nicety +that many players regard as important. For this mission, we make use of +the :obj:`LibraryMessages` object. .. code-block:: inform @@ -571,38 +592,37 @@ use of the ``LibraryMessages`` object. Include "VerbLib"; -If you provide it, the ``LibraryMessages`` object must be defined -*between* the inclusion of ``Parser`` and ``VerbLib`` (it won't work -otherwise and you’ll get a compiler error). The object contains a single -property -- :prop:`before` -- which intercepts display of the default -messages that you want to change. An attempt to SING, for example, will -now result in "Alas! That is not one of your many superpowers" being +If you provide it, the :obj:`LibraryMessages` object must be defined +*between* the inclusion of ``Parser`` and ``VerbLib`` (it won't work +otherwise and you’ll get a compiler error). The object contains a single +property -- :prop:`before` -- which intercepts display of the default +messages that you want to change. An attempt to SING, for example, will +now result in "Alas! That is not one of your many superpowers" being displayed. -In addition to such verb-specific responses, the library defines other -messages not directly associated with an action, like the default -response when a verb is unrecognised, or if you refer to an object which -is not in scope, or indeed many other things. Most of these messages can -be accessed through the ``Miscellany entry``, which has a numbered list -of responses. The variable ``lm_n`` holds the current value of the -number of the message to be displayed, so you can change the default -with a test like this: +In addition to such verb-specific responses, the library defines other +messages not directly associated with an action, like the default response +when a verb is unrecognised, or if you refer to an object which is not in +scope, or indeed many other things. Most of these messages can be accessed +through the ``Miscellany entry``, which has a numbered list of responses. +The variable ``lm_n`` holds the current value of the number of the message +to be displayed, so you can change the default with a test like this: .. code-block:: inform if (lm_n == 39) "That's not something you need to refer to in order to SAVE the day."; -where 39 is the number for the standard message "That's not something -you need to refer to in the course of this game" -- displayed when the -player mentions a noun which is listed in a room's name property, as we -did for the ``street``. +where 39 is the number for the standard message "That's not something you +need to refer to in the course of this game" -- displayed when the player +mentions a noun which is listed in a room's name property, as we did for +the ``street``. .. note:: - Remember that when we are testing for different values of the - same variable, we can also use the switch statement. For the - Miscellany entry, the following code would work just as nicely: + Remember that when we are testing for different values of the same + variable, we can also use the switch statement. For the Miscellany + entry, the following code would work just as nicely: .. code-block:: inform @@ -624,21 +644,28 @@ did for the ``street``. "That's not something you need to refer to in order to SAVE the day."; } -Not surprisingly, the default message for self-examination: "As good -looking as ever" is a ``Miscellany`` entry -- it's number 19 -- so we -can change it through the ``LibraryMessages`` object instead of, as -before, assigning a new string to the ``player.description property``. -In our game, the description of the player character has two states: -with street clothes as John Covarth and with the super-hero outfit as -Captain Fate; hence the ``if (clothes has worn)`` clause. - -This discussion of changing our hero's appearance shows that there are -different ways of achieving the same result, which is a common situation -while designing a game. Problems may be approached from different -angles; why use one technique and not another? Usually, the context tips -the balance in favour of one solution, though it might happen that you -opt for the not-so-hot approach for some overriding reason. Don't feel -discouraged; choices like this become more common (and easier) as your +.. index:: + pair: LibraryMessages; library object + +.. Generated by autoindex +.. index:: + pair: Miscellany; library action + +Not surprisingly, the default message for self-examination: "As good +looking as ever" is a :act:`Miscellany` entry -- it's number 19 -- so we +can change it through the :obj:`LibraryMessages` object instead of, as +before, assigning a new string to the ``player.description`` property. In +our game, the description of the player character has two states: with +street clothes as John Covarth and with the super-hero outfit as Captain +Fate; hence the ``if (clothes has worn)`` clause. + +This discussion of changing our hero's appearance shows that there are +different ways of achieving the same result, which is a common situation +while designing a game. Problems may be approached from different angles; +why use one technique and not another? Usually, the context tips the +balance in favour of one solution, though it might happen that you opt for +the not-so-hot approach for some overriding reason. Don't feel +discouraged; choices like this become more common (and easier) as your experience grows. .. Ugh. Ghastly, but it does the job. @@ -652,18 +679,18 @@ experience grows. Going back to our example, an alternative approach would be to set the variable ``player.description`` in the ``Initialise`` routine (as we did with "William Tell") to the "ordinary clothes" string, and then later - change it as the need arises. It is a variable, after all, and you can + change it as the need arises. It is a variable, after all, and you can alter its value with another statement like ``player.description =`` - |WNL_LATEX| |WNL_HTML| anywhere in your code. This alternative solution + |WNL_LATEX| |WNL_HTML| anywhere in your code. This alternative solution might be better if we intended changing the description of the player - many times through the game. Since we plan to have only two states, the - ``LibraryMessages`` approach will do just fine. - -A final warning: as we explained when extending the standard verb -grammars, you *could* edit the appropriate library file and change all -the default messages, but that wouldn't be a sound practice, because -your library file will probably not be right for the next game. Use of -the ``LibraryMessages`` object is strongly advised. + many times through the game. Since we plan to have only two states, the + :obj:`LibraryMessages` approach will do just fine. + +A final warning: as we explained when extending the standard verb grammars, +you *could* edit the appropriate library file and change all the default +messages, but that wouldn't be a sound practice, because your library file +will probably not be right for the next game. Use of the +:obj:`LibraryMessages` object is strongly advised. If you're typing in the game, you'll probably want to read the brief section on :ref:`compile-as-you-go` prior to performing a test compile. diff --git a/chapters/11.rst b/chapters/11.rst index a82408e..ff082ec 100644 --- a/chapters/11.rst +++ b/chapters/11.rst @@ -13,7 +13,7 @@ Captain Fate: take 2 :align: left |V|\iewed from the inside, Benny's café is warm and welcoming, and packed -with lunchtime customers. We'll try to conjure up some appropriate images, +with lunchtime customers. We'll try to conjure up some appropriate images, but the main focus of the room isn't the decor: it's the door leading to the toilet -- and, perhaps, privacy? @@ -22,20 +22,19 @@ the toilet -- and, perhaps, privacy? A homely atmosphere =================== -Benny's café is populated with customers enjoying their lunch, so it -won't be a good place to change identities. However, the toilet to the -north looks promising, though Benny has strict rules about its use and -the door seems to be locked. +Benny's café is populated with customers enjoying their lunch, so it won't +be a good place to change identities. However, the toilet to the north +looks promising, though Benny has strict rules about its use and the door +seems to be locked. .. admonition:: Cultural Note :class: admonition note - Not for the first time, this guide betrays its origins. In - European countries the word "toilet" often refers not only to the - white porcelain artefact, but also to the room in which it can be - found (also, a "bathroom" is for taking a bath, a "restroom" for - taking a rest). Bear with us on this; the dual usage becomes - important a little later on. + Not for the first time, this guide betrays its origins. In European + countries the word "toilet" often refers not only to the white porcelain + artefact, but also to the room in which it can be found (also, a + "bathroom" is for taking a bath, a "restroom" for taking a rest). Bear + with us on this; the dual usage becomes important a little later on. We define the café room in simple form: @@ -51,7 +50,7 @@ We define the café room in simple form: s_to street, n_to toilet_door; -We'll elaborate on the last line (``n_to toilet_door``) later, when we +We'll elaborate on the last line (``n_to toilet_door``) later, when we define the door object which lies between the café and the toilet. We've mentioned a counter: @@ -73,33 +72,38 @@ We've mentioned a counter: has supporter; That :prop:`before` property, superficially normal, actually conceals a -little surprise. By now you should be entirely comfortable with using an +little surprise. By now you should be entirely comfortable with using an object's :prop:`before` property to intercept an action directed at that object; for example, if the player types HIT COUNTER then the counter's :prop:`before` property is potentially able to intercept the resulting -``Attack`` action. However, the command PUT KEY ON COUNTER generates *two* -actions. First, a ``PutOn`` action is offered to the key (effectively -saying, do you want to be placed on top of the counter?); that’s the normal -bit. And then the surprise: a ``Receive`` action is offered to the counter -(effectively saying, are you happy to have the key placed on you?) Both -actions have the same opportunity of returning :const:`false` to let the -action continue, :const:`true` to prevent it. - -The ``Receive`` action is generated by the library in the ``PutOnSub`` +:act:`Attack` action. However, the command PUT KEY ON COUNTER generates +*two* actions. First, a :act:`PutOn` action is offered to the key +(effectively saying, do you want to be placed on top of the counter?); +that’s the normal bit. And then the surprise: a :act:`Receive` action is +offered to the counter (effectively saying, are you happy to have the key +placed on you?) Both actions have the same opportunity of returning +:const:`false` to let the action continue, :const:`true` to prevent it. + +.. Generated by autoindex +.. index:: + pair: LetGo; library action + pair: Receive; library action + +The :act:`Receive` action is generated by the library in the ``PutOnSub`` action handler, and also in ``InsertSub`` (so a command like PUT BIRD IN -NEST sends a Receive to the nest object). There’s a matching ``LetGo``, +NEST sends a Receive to the nest object). There’s a matching :act:`LetGo`, generated by the library from commands like TAKE KEY OFF COUNTER and REMOVE -BIRD FROM NEST. ``Receive`` and ``LetGo`` are examples of what’s called a -:term:`fake action`. +BIRD FROM NEST. :act:`Receive` and :act:`LetGo` are examples of what’s +called a :term:`fake action`. .. note:: In "William Tell" we defined the ``quiver``, way back in - :ref:`possessions`, as an ``open container``. As things stand, the player - can put *any* held object, however inappropriate, into it. We could have - trapped the ``Receive`` action to ensure that arrows are the only - acceptable contents (recollect that ``~~``, to be read as "not", turns - true into false and vice versa): + :ref:`possessions`, as an ``open container``. As things stand, the + player can put *any* held object, however inappropriate, into it. We + could have trapped the :act:`Receive` action to ensure that arrows are + the only acceptable contents (recollect that ``~~``, to be read as "not", + turns true into false and vice versa): .. code-block:: inform @@ -111,14 +115,14 @@ BIRD FROM NEST. ``Receive`` and ``LetGo`` are examples of what’s called a print_ret "Only arrows -- clean arrows -- go in your quiver."; ], -Here, we intercept any attempt to place an item on the counter, and -translate it into an attempt to give that item to Benny. Part of the -game's plot depends on the player returning the toilet key to Benny, and -also paying him for his delicious cup of world-famous Cappuccino. -Putting the key and the money on the counter is a reasonable alternative -way for the player to accomplish this. +Here, we intercept any attempt to place an item on the counter, and +translate it into an attempt to give that item to Benny. Part of the +game's plot depends on the player returning the toilet key to Benny, and +also paying him for his delicious cup of world-famous Cappuccino. Putting +the key and the money on the counter is a reasonable alternative way for +the player to accomplish this. -We've also mentioned some customers. These are treated as NPCs, reacting +We've also mentioned some customers. These are treated as NPCs, reacting to our hero’s performance. .. code-block:: inform @@ -184,36 +188,53 @@ to our hero’s performance. ], has scenery animate pluralname; -Let's go step by step. Our hero enters the café dressed as John Covarth, -but will eventually manage to change clothes in the toilet, and he'll -have to cross back through the café to reach the street and win the -game. The customers' :prop:`description` takes into consideration which -outfit the player character is wearing. - -In "William Tell" we’ve seen a brief manifestation of the :prop:`life` -property, but here we'll extend it a little. As we explained, :prop:`life` -lets you intercept those actions particular to animate objects. Here we -trap ``Attack`` and ``Kiss`` to offer some customised messages for these -actions when applied to the customers. Also, we avoid conversation by -intercepting ``Ask``, ``Tell`` and ``Answer`` in order just to produce a -message which depends on the player character's attire. - -One other feature of :attr:`animate` objects is the possibility of giving -them orders: BILL, SHAKE THE SPEAR or ANNIE, GET YOUR GUN . These -actions are dealt with in the :prop:`orders` property and, as with the -:prop:`life` property, the embedded routine can become quite complex if you -want your NPCs to behave in an interesting way. In this case, we don't -need the customers to perform tasks for us, so instead we provide a -simple rejection message, just in case the player tries to order people -around. - -Which leaves us with the :prop:`daemon` bit. A daemon is a property +Let's go step by step. Our hero enters the café dressed as John Covarth, +but will eventually manage to change clothes in the toilet, and he'll have +to cross back through the café to reach the street and win the game. The +customers' :prop:`description` takes into consideration which outfit the +player character is wearing. + +.. Generated by autoindex +.. index:: + pair: Answer; library action + pair: Ask; library action + pair: Attack; library action + pair: Kiss; library action + pair: Tell; library action + pair: life; library property + +In "William Tell" we’ve seen a brief manifestation of the :prop:`life` +property, but here we'll extend it a little. As we explained, :prop:`life` +lets you intercept those actions particular to animate objects. Here we +trap :act:`Attack` and :act:`Kiss` to offer some customised messages for +these actions when applied to the customers. Also, we avoid conversation +by intercepting :act:`Ask`, :act:`Tell` and :act:`Answer` in order just to +produce a message which depends on the player character's attire. + +.. Generated by autoindex +.. index:: + pair: animate; library attribute + pair: orders; library property + +One other feature of :attr:`animate` objects is the possibility of giving +them orders: BILL, SHAKE THE SPEAR or ANNIE, GET YOUR GUN . These actions +are dealt with in the :prop:`orders` property and, as with the :prop:`life` +property, the embedded routine can become quite complex if you want your +NPCs to behave in an interesting way. In this case, we don't need the +customers to perform tasks for us, so instead we provide a simple rejection +message, just in case the player tries to order people around. + +.. Generated by autoindex +.. index:: + pair: daemon; library property + +Which leaves us with the :prop:`daemon` bit. A daemon is a property normally used to perform some timed or repetitive action without the need of the player’s direct interaction; for example, machines which work by themselves, animals that move on their own, or people going about their -business. More powerfully, a daemon may take notice of the player’s +business. More powerfully, a daemon may take notice of the player’s decisions at a particular moment, allowing for some interactive behaviour; -this is, however, an advanced feature that we won't use in this example. A +this is, however, an advanced feature that we won't use in this example. A daemon gets a chance of doing something at the end of every turn, typically to (or with) the object to which it’s associated. In our example, the daemon triggers some sneers and nasty comments from the customers once our @@ -221,37 +242,36 @@ hero comes out of the toilet dressed in Captain Fate’s costume. To code a daemon, you need to do three things: -#. First, define a daemon property in the object’s body; the value of - the property is always an embedded routine. +#. First, define a daemon property in the object’s body; the value of the + property is always an embedded routine. -#. However, daemons do nothing until you activate them. This is easily - achieved with the call :samp:`StartDaemon({obj_id})`, which may happen - anywhere (if you want some object's daemon to be active from the - beginning of the game,you can make the call in your Initialise - routine). +#. However, daemons do nothing until you activate them. This is easily + achieved with the call :samp:`StartDaemon({obj_id})`, which may happen + anywhere (if you want some object's daemon to be active from the + beginning of the game,you can make the call in your Initialise routine). -#. Once the daemon has finished its mission (if ever) you may stop it - with the call :samp:`StopDaemon({obj_id})`. +#. Once the daemon has finished its mission (if ever) you may stop it with + the call :samp:`StopDaemon({obj_id})`. -How does our particular daemon work? The appearance of our hero in full -crime-fighting wear will make the customers stare at him and make snarky -remarks. This must happen in the café room – the place where the -customers are -- so we need to make certain that the daemon does -something interesting only while the player stays in the right place -(and hasn’t wandered, say, back into the toilet): +How does our particular daemon work? The appearance of our hero in full +crime-fighting wear will make the customers stare at him and make snarky +remarks. This must happen in the café room – the place where the customers +are -- so we need to make certain that the daemon does something +interesting only while the player stays in the right place (and hasn’t +wandered, say, back into the toilet): .. code-block:: inform if (location ~= cafe) return; -So if the location is not the café room (remember ~= means "not equal -to"), return without doing anything else; on this turn, there’s nothing -for the daemon to do. We use a plain ``return`` statement because the -value returned from a daemon doesn’t matter. +So if the location is not the café room (remember ~= means "not equal to"), +return without doing anything else; on this turn, there’s nothing for the +daemon to do. We use a plain ``return`` statement because the value +returned from a daemon doesn’t matter. -We have defined a customised local property, ``number_of_comments``, to -control the sequence of customers' remarks. When the Captain enters the -café room from the toilet for the first time, the value of the property +We have defined a customised local property, ``number_of_comments``, to +control the sequence of customers' remarks. When the Captain enters the +café room from the toilet for the first time, the value of the property should be zero, so the statement block under the test: .. code-block:: inform @@ -262,46 +282,50 @@ should be zero, so the statement block under the test: curiosity.^"; } -will happen only this once. What we intend is to output the text "Nearby -customers..." right after the startling entrance of our hero, setting up -the scene for the comments which are about to happen. Since we assign a -value of 1 to the property, the message will not be printed again. -Notice how we use an explicit ``print`` statement; the execution of the -daemon will continue normally to the next line. +will happen only this once. What we intend is to output the text "Nearby +customers..." right after the startling entrance of our hero, setting up +the scene for the comments which are about to happen. Since we assign a +value of 1 to the property, the message will not be printed again. Notice +how we use an explicit ``print`` statement; the execution of the daemon +will continue normally to the next line. -We want the customers to indulge in witticisms once they see the -costumed Captain, but not on a completely predictable basis. +We want the customers to indulge in witticisms once they see the costumed +Captain, but not on a completely predictable basis. .. code-block:: inform if (random(2) == 1) ... -``random`` is an Inform routine used to generate random numbers or to -choose randomly between given choices; in the form -:samp:`random({expression})` it returns a random number between 1 and -``expression`` inclusive. So our condition is actually stating: if a -random choice between 1 and 2 happens to be 1 then perform some action. -Remember that a daemon is run once at the end of every turn, so the -condition is trying to squeeze a comment from a customer roughly once -every other turn. - -Next, we proceed as we have already seen in "William Tell", with a -switch statement to order the comments in a controlled sequence by -cunning use of our tailored local property, ``number_of_comments``. We -have written just five messages (could have been one or a hundred) and -then we reach the default case, which is a good place to stop the -daemon, since we have no more customers’ remarks to display. - -Ah, but when does the daemon *start* functioning? Well, as soon as our -protagonist comes out of the toilet dressed in his multicoloured -super-hero pyjamas. Since we want to minimise the possible game states, -we’ll make some general rules to avoid trouble: (a) players will be able -to change only in the toilet; (b) we won’t let players change back into -street clothes; and (c) once players manage to step into the street thus -dressed, the game is won. So, we can safely assume that if players enter -the café in their Captain’s outfit, they’ll be coming from the toilet. -As a consequence of all this, we add an :prop:`after` property to the café -room object: +``random`` is an Inform routine used to generate random numbers or to +choose randomly between given choices; in the form +:samp:`random({expression})` it returns a random number between 1 and +``expression`` inclusive. So our condition is actually stating: if a +random choice between 1 and 2 happens to be 1 then perform some action. +Remember that a daemon is run once at the end of every turn, so the +condition is trying to squeeze a comment from a customer roughly once every +other turn. + +Next, we proceed as we have already seen in "William Tell", with a switch +statement to order the comments in a controlled sequence by cunning use of +our tailored local property, ``number_of_comments``. We have written just +five messages (could have been one or a hundred) and then we reach the +default case, which is a good place to stop the daemon, since we have no +more customers’ remarks to display. + +.. Generated by autoindex +.. index:: + pair: after; library property + +Ah, but when does the daemon *start* functioning? Well, as soon as our +protagonist comes out of the toilet dressed in his multicoloured super-hero +pyjamas. Since we want to minimise the possible game states, we’ll make +some general rules to avoid trouble: (a) players will be able to change +only in the toilet; (b) we won’t let players change back into street +clothes; and (c) once players manage to step into the street thus dressed, +the game is won. So, we can safely assume that if players enter the café +in their Captain’s outfit, they’ll be coming from the toilet. As a +consequence of all this, we add an :prop:`after` property to the café room +object: .. code-block:: inform @@ -319,13 +343,13 @@ room object: s_to street, n_to toilet_door -There are two useful techniques to detect when the player is entering or -leaving a room. We'll later see in detail how to deal with a player -trying to go away and how to avoid it if need be. For now, let’s just -mention that, in both cases, you have to intercept the ``Go`` action in -a room object; if you trap it in a :prop:`before` property, you’re checking -for departure from the room; if you trap it in an :prop:`after` property, -you’re checking for arrivals into the room. Right now we wish to know if +There are two useful techniques to detect when the player is entering or +leaving a room. We'll later see in detail how to deal with a player trying +to go away and how to avoid it if need be. For now, let’s just mention +that, in both cases, you have to intercept the :act:`Go` action in a room +object; if you trap it in a :prop:`before` property, you’re checking for +departure from the room; if you trap it in an :prop:`after` property, +you’re checking for arrivals into the room. Right now we wish to know if the player just came from the toilet, so we use an :prop:`after` property. The first line: @@ -334,27 +358,32 @@ The first line: if (noun ~= s_obj) return false; -is telling the interpreter that we want to do something if the player -entered the room by typing a GO SOUTH command (this would normally mean -"coming from the north", but remember that nothing stops you from -connecting rooms without cardinal logic); the interpreter will apply -normal rules for the other available directions. - -Then we check whether the player character is wearing the costume, in -which case it starts the :prop:`daemon` of the ``customers`` object. The use -of the local ``first_time_out`` property ensures that the condition is -:const:`true` only once, so the statement block attached to it runs also +is telling the interpreter that we want to do something if the player +entered the room by typing a GO SOUTH command (this would normally mean +"coming from the north", but remember that nothing stops you from +connecting rooms without cardinal logic); the interpreter will apply normal +rules for the other available directions. + +.. Generated by autoindex +.. index:: + pair: daemon; library property + pair: true; library constant + +Then we check whether the player character is wearing the costume, in which +case it starts the :prop:`daemon` of the ``customers`` object. The use of +the local ``first_time_out`` property ensures that the condition is +:const:`true` only once, so the statement block attached to it runs also once. -We've finished with the customers in the café. Now, we have the toilet -to the north which, for reasons of gameplay *and* decency, is protected -by a door. +We've finished with the customers in the café. Now, we have the toilet to +the north which, for reasons of gameplay *and* decency, is protected by a +door. A door to adore =============== -Door objects require some specific properties and attributes. Let's -first code a simple door: +Door objects require some specific properties and attributes. Let's first +code a simple door: .. code-block:: inform @@ -369,49 +398,58 @@ first code a simple door: with_key toilet_key, has scenery door openable lockable locked; -We find this door in the café. We must specify the direction in which -the door leads and, as we have mentioned in the café's description, that -would be to the north. That’s what the :prop:`door_dir` property is for, and -in this case it takes the value of the north direction property -:prop:`n_to`. Then we must tell Inform the identity of the room to be found -behind the door, hence the :prop:`door_to` property, which takes the value -of the toilet room -- to be defined later. Remember the café's -connection to the north, ``n_to toilet_door``? Thanks to it, Inform will -know that the door is in the way, and thanks to the :prop:`door_to` -property, what lies beyond. - -Doors *must* have the attribute :attr:`door`, but beyond that we have a -stock of options to help us define exactly what kind of door we are -dealing with. As for containers, doors can be :attr:`openable` (which -activates the verbs OPEN and CLOSE so that they can be applied to this -object) and, since by default they are closed, you can give them the -attribute :attr:`open` if you wish otherwise. Additionally, doors can be -:attr:`lockable` (which sets up the LOCK/UNLOCK verbs) and you can make them -:attr:`locked` to override their default unlocked status. The verbs LOCK -and UNLOCK are expecting some kind of key object to operate the door. -This must be defined using the :prop:`with_key` property, whose value should -be the internal ID of the key; in our example, the soon-to-be-defined -``toilet_key`` . If you don't supply this property, players won't be -able to lock or unlock the door. - -This simple door definition has one problem, namely, that it exists only -in the café room. If you wish the door to be present also from the -toilet side, you can either (a) define another door to be found in the -``toilet room``, or (b) make this one a two-sided door. - -Solution (a) seems superficially straightforward, but then you have the -problem of keeping the states of the two doors – open/closed, -locked/unlocked -- in synch. In this scenario, where you can access the -toilet only through this door, that wouldn't be too complicated, since -you could leave the door object in the café room opened all the time, -regardless of what players do with the door object in the toilet room -and vice versa -- they are never going to see them at the same time. In -general terms, though, such inconsistencies lead to problems; solution +We find this door in the café. We must specify the direction in which the +door leads and, as we have mentioned in the café's description, that would +be to the north. That’s what the :prop:`door_dir` property is for, and in +this case it takes the value of the north direction property :prop:`n_to`. +Then we must tell Inform the identity of the room to be found behind the +door, hence the :prop:`door_to` property, which takes the value of the +toilet room -- to be defined later. Remember the café's connection to the +north, ``n_to toilet_door``? Thanks to it, Inform will know that the door +is in the way, and thanks to the :prop:`door_to` property, what lies +beyond. + +.. Generated by autoindex +.. index:: + pair: door; library attribute + pair: lockable; library attribute + pair: locked; library attribute + pair: open; library attribute + pair: openable; library attribute + pair: with_key; library property + +Doors *must* have the attribute :attr:`door`, but beyond that we have a +stock of options to help us define exactly what kind of door we are dealing +with. As for containers, doors can be :attr:`openable` (which activates +the verbs OPEN and CLOSE so that they can be applied to this object) and, +since by default they are closed, you can give them the attribute +:attr:`open` if you wish otherwise. Additionally, doors can be +:attr:`lockable` (which sets up the LOCK/UNLOCK verbs) and you can make +them :attr:`locked` to override their default unlocked status. The verbs +LOCK and UNLOCK are expecting some kind of key object to operate the door. +This must be defined using the :prop:`with_key` property, whose value +should be the internal ID of the key; in our example, the +soon-to-be-defined ``toilet_key`` . If you don't supply this property, +players won't be able to lock or unlock the door. + +This simple door definition has one problem, namely, that it exists only in +the café room. If you wish the door to be present also from the toilet +side, you can either (a) define another door to be found in the ``toilet +room``, or (b) make this one a two-sided door. + +Solution (a) seems superficially straightforward, but then you have the +problem of keeping the states of the two doors – open/closed, +locked/unlocked -- in synch. In this scenario, where you can access the +toilet only through this door, that wouldn't be too complicated, since you +could leave the door object in the café room opened all the time, +regardless of what players do with the door object in the toilet room and +vice versa -- they are never going to see them at the same time. In +general terms, though, such inconsistencies lead to problems; solution (a) is best ignored for most purposes. -Solution (b) is better, since you have only one door object to deal with -and its possible states affect both sides. However, the coding gets a -little bit complicated and you''ll have to define routines for most +Solution (b) is better, since you have only one door object to deal with +and its possible states affect both sides. However, the coding gets a +little bit complicated and you''ll have to define routines for most properties: .. code-block:: inform @@ -438,39 +476,39 @@ properties: with_key toilet_key, has scenery door openable lockable locked; -First of all, the door now needs a :prop:`found_in` property, since it's -going to be located both in the café and the toilet. The :prop:`description` -checks which side of the door we are looking at – testing the current -value of the variable :var:`location`, which holds the room the player is -in -- because we have a scribbled note stuck on one side, but not on the -other. And the :prop:`door_dir` and :prop:`door_to` properties must use the same -trick, because we travel north from the café into the toilet, but south -from the toilet into the café. - -Right now, the game will display "the toilet door" every time it needs -to refer to this object. It would be nice if we could somehow get the -game to distinguish between "the door to the toilet" and "the door to -the cafe", depending on the side we are facing. For this, a ``short_name -property`` is the thing. We have already talked about the external name -defined as part of an object's header information: +First of all, the door now needs a :prop:`found_in` property, since it's +going to be located both in the café and the toilet. The +:prop:`description` checks which side of the door we are looking at – +testing the current value of the variable :var:`location`, which holds the +room the player is in -- because we have a scribbled note stuck on one +side, but not on the other. And the :prop:`door_dir` and :prop:`door_to` +properties must use the same trick, because we travel north from the café +into the toilet, but south from the toilet into the café. + +Right now, the game will display "the toilet door" every time it needs to +refer to this object. It would be nice if we could somehow get the game to +distinguish between "the door to the toilet" and "the door to the cafe", +depending on the side we are facing. For this, a ``short_name property`` +is the thing. We have already talked about the external name defined as +part of an object's header information: .. code-block:: inform Object toilet_door "toilet door" -That ``toilet door`` will be the name displayed by the game at run-time -to refer to the door. With identical effect, this could also have been -coded thus: +That ``toilet door`` will be the name displayed by the game at run-time to +refer to the door. With identical effect, this could also have been coded +thus: .. code-block:: inform Object toilet_door with short_name "toilet door", -:prop:`short_name` is a property that supplies the external name of an -object, either as a string or an embedded routine. Normally, objects -retain the same external name throughout the game -- and the header -information method is perfect in that case -- but if it needs to change, +:prop:`short_name` is a property that supplies the external name of an +object, either as a string or an embedded routine. Normally, objects +retain the same external name throughout the game -- and the header +information method is perfect in that case -- but if it needs to change, it's easy to write a routine as the value of :prop:`short_name`: .. code-block:: inform @@ -485,52 +523,58 @@ it's easy to write a routine as the value of :prop:`short_name`: description ... -Notice the ``return true`` at the end of the routine. You''ll recall -that the standard rule says "return false to carry on, true to take over -and stop normal execution”. In the case of :prop:`short_name`, "carry on" -means "and now display the external name from the header information", -which is sometimes handy; for instance, you could write a :prop:`short_name` -routine to prefix an object's external name with one of a range of -adjectives -- perhaps a shining/flickering/fading/useless lantern. +Notice the ``return true`` at the end of the routine. You''ll recall that +the standard rule says "return false to carry on, true to take over and +stop normal execution". In the case of :prop:`short_name`, "carry on" +means "and now display the external name from the header information", +which is sometimes handy; for instance, you could write a +:prop:`short_name` routine to prefix an object's external name with one of +a range of adjectives -- perhaps a shining/flickering/fading/useless +lantern. .. note:: What's displayed if there isn't an external name in an object's header? If you've read the section :ref:`compile-as-you-go`, you'll recall that the interpreter simply uses the internal identifier within parentheses; - that is, with no external name and no :prop:`short_name` property, we might - see:: + that is, with no external name and no :prop:`short_name` property, we + might see:: You open the (toilet_door). - And the same principle applies if we were mistakenly to ``return - false`` from this short_name routine: we would get, first, the result - of our ``print`` statement, and then the standard rules would display - the internal ID:: + And the same principle applies if we were mistakenly to ``return false`` + from this short_name routine: we would get, first, the result of our + ``print`` statement, and then the standard rules would display the + internal ID:: You open the door to the toilet(toilet_door). -Doors can get more complicated than this (no, please, don't throw our -guide out of the window). Here comes some optional deluxe coding to make -the door object a bit friendlier in game play, so you can skip it if you +Doors can get more complicated than this (no, please, don't throw our guide +out of the window). Here comes some optional deluxe coding to make the +door object a bit friendlier in game play, so you can skip it if you foresee headaches. -Our door now behaves nicely at run-time. It can be locked and unlocked -if the player character has the right key; it can be opened and closed. -A sequence of commands to go into the toilet and lock the door behind -you would be: UNLOCK DOOR WITH KEY, OPEN DOOR, GO NORTH, CLOSE DOOR, -LOCK DOOR WITH KEY. After we are finished, let's go back to the café: -UNLOCK DOOR WITH KEY, OPEN DOOR, SOUTH. If the player is of the -fastidious kind: CLOSE DOOR, LOCK DOOR WITH KEY. This game features only -one door, but if it had three or four of them, players would grow -restless (at the very least) if they needed to type so many commands -just to go through a door. This is the kind of thing reportedly -considered as poor design, because the game is suddenly slowed down to -get over a simple action which involves no secrets or surprises. How -exciting can the crossing of an ordinary door be, after all? - -If a few lines of code can make the life of the player easier, it's -worth a shot. Let's provide a few improvements to our toilet door in +Our door now behaves nicely at run-time. It can be locked and unlocked if +the player character has the right key; it can be opened and closed. A +sequence of commands to go into the toilet and lock the door behind you +would be: UNLOCK DOOR WITH KEY, OPEN DOOR, GO NORTH, CLOSE DOOR, LOCK DOOR +WITH KEY. After we are finished, let's go back to the café: UNLOCK DOOR +WITH KEY, OPEN DOOR, SOUTH. If the player is of the fastidious kind: CLOSE +DOOR, LOCK DOOR WITH KEY. This game features only one door, but if it had +three or four of them, players would grow restless (at the very least) if +they needed to type so many commands just to go through a door. This is +the kind of thing reportedly considered as poor design, because the game is +suddenly slowed down to get over a simple action which involves no secrets +or surprises. How exciting can the crossing of an ordinary door be, after +all? + +.. Generated by autoindex +.. index:: + pair: after; library property + pair: before; library property + +If a few lines of code can make the life of the player easier, it's worth a +shot. Let's provide a few improvements to our toilet door in :prop:`before` and :prop:`after` properties: .. code-block:: inform @@ -558,39 +602,48 @@ worth a shot. Let's provide a few improvements to our toilet door in return true; ], -The basic idea here is to let the player who holds the key perform just -one action to both unlock *and* open the door (and, conversely, to close -*and* lock it). The relevant actions are ``Unlock`` and ``Open``, and -``Lock`` ( ``Close`` is not necessary; if players just close the door we -shouldn’t assume that they want to lock it as well). - -* **Open**: if the door isn't locked or the player doesn't hold the key, - keep going with the default ``Open`` action defined by the library. - That leaves a locked door and a player holding the key, so we - redirect processing to the ``Unlock`` action, giving as arguments the - door (self) and the toilet key. Since we are using single - angle-brackets ``<...>``, the action resumes after the unlocking is - done (note that the ``Unlock`` action also takes care of opening the - door). Finally, we ``return true`` to stop the library from trying to - open the door by itself. - -* **Lock**: if the door is already closed, keep going with the standard - library ``Lock`` action. If not, tell players that we are closing the - door for them, redirect the action briefly to actually close it, and - then ``return false`` to let the ``Lock`` action proceed as before. - -* **Unlock**: we place this action in the after property, so (let's - hope) the ``Unlock`` action has already happened. If the door is still - locked, something went wrong, so we ``return false`` to display the - standard message for an unsuccessful unlocking. Otherwise, the door is - now unlocked, so we inform the player that we are opening the door and - redirect the action to actually open it, returning :const:`true` to +The basic idea here is to let the player who holds the key perform just one +action to both unlock *and* open the door (and, conversely, to close *and* +lock it). The relevant actions are :act:`Unlock` and :act:`Open`, and +:act:`Lock` (:act:`Close` is not necessary; if players just close the door +we shouldn’t assume that they want to lock it as well). + +* **Open**: if the door isn't locked or the player doesn't hold the key, + keep going with the default :act:`Open` action defined by the library. + That leaves a locked door and a player holding the key, so we redirect + processing to the :act:`Unlock` action, giving as arguments the door + (self) and the toilet key. Since we are using single angle-brackets + ``<...>``, the action resumes after the unlocking is done (note that the + :act:`Unlock` action also takes care of opening the door). Finally, we + ``return true`` to stop the library from trying to open the door by + itself. + +* **Lock**: if the door is already closed, keep going with the standard + library :act:`Lock` action. If not, tell players that we are closing the + door for them, redirect the action briefly to actually close it, and then + ``return false`` to let the :act:`Lock` action proceed as before. + +.. Generated by autoindex +.. index:: + pair: true; library constant + +* **Unlock**: we place this action in the after property, so (let's hope) + the :act:`Unlock` action has already happened. If the door is still + locked, something went wrong, so we ``return false`` to display the + standard message for an unsuccessful unlocking. Otherwise, the door is + now unlocked, so we inform the player that we are opening the door and + redirect the action to actually open it, returning :const:`true` to suppress the standard message. -In all processes there is a library variable called :var:`keep_silent`, -which can be either :const:`false` (the normal state) or :const:`true`; when -:const:`true`, the interpreter does not display the associated message of an -action in progress, so we can avoid things like: +.. Generated by autoindex +.. index:: + pair: false; library constant + pair: keep_silent; library variable + +In all processes there is a library variable called :var:`keep_silent`, +which can be either :const:`false` (the normal state) or :const:`true`; +when :const:`true`, the interpreter does not display the associated message +of an action in progress, so we can avoid things like: .. code-block:: transcript @@ -598,58 +651,58 @@ action in progress, so we can avoid things like: You open the door to the toilet. You unlock the door to the toilet and open it. -Although we want to set :var:`keep_silent` to :const:`true` for the duration of -our extra processing, we need to reset it afterwards. In a case like -this, good design practice is to preserve its initial value (which was -probably :const:`false`, but you should avoid risky assumptions); we use a -local variable ``ks`` to remember that initial setting so that we can -safely restore it afterwards. You’ll remember that a local variable in a -standalone routine is declared between the routine’s name and the -semicolon: +Although we want to set :var:`keep_silent` to :const:`true` for the +duration of our extra processing, we need to reset it afterwards. In a +case like this, good design practice is to preserve its initial value +(which was probably :const:`false`, but you should avoid risky +assumptions); we use a local variable ``ks`` to remember that initial +setting so that we can safely restore it afterwards. You’ll remember that +a local variable in a standalone routine is declared between the routine’s +name and the semicolon: .. code-block:: inform [ BeenToBefore this_room; -In exactly the same way, a local variable in an embedded routine is -declared between the ``[`` starting marker of the routine and the +In exactly the same way, a local variable in an embedded routine is +declared between the ``[`` starting marker of the routine and the semicolon: .. code-block:: inform before [ ks; -You can declare up to fifteen variables this way -- just separated by -spaces -- which are usable only within the embedded routine. When we +You can declare up to fifteen variables this way -- just separated by +spaces -- which are usable only within the embedded routine. When we assign it thus: .. code-block:: inform ks = keep_silent; -we are actually making ``ks`` equal to whatever value :var:`keep_silent` -has (either :const:`true` or :const:`false`; we actually don't care). We then set -:var:`keep_silent` to :const:`true`, make the desired silent actions, and we -assign: +we are actually making ``ks`` equal to whatever value :var:`keep_silent` +has (either :const:`true` or :const:`false`; we actually don't care). We +then set :var:`keep_silent` to :const:`true`, make the desired silent +actions, and we assign: .. code-block:: inform keep_silent = ks; -which restores the value originally stored in ``ks`` to :var:`keep_silent`. -The effect is that we manage to leave it as it was before we tampered -with it. +which restores the value originally stored in ``ks`` to :var:`keep_silent`. +The effect is that we manage to leave it as it was before we tampered with +it. -Well, that's about everything about doors. Everything? Well, no, not +Well, that's about everything about doors. Everything? Well, no, not really; any object can grow as complex as your imagination allows, but -we’ll drop the subject here. If you care to see more sophisticated doors, +we’ll drop the subject here. If you care to see more sophisticated doors, check Exercises :dm4:`3 and 4 ` in the |DM4|, where an obliging door opens and unlocks by itself if the player simply walks in its direction. -So far, we have the player in front of a locked door leading to the -toilet. A dead end? No, the description mentions a scribbled note on its -surface. This one should offer no problem: +So far, we have the player in front of a locked door leading to the toilet. +A dead end? No, the description mentions a scribbled note on its surface. +This one should offer no problem: .. code-block:: inform @@ -679,17 +732,16 @@ surface. This one should offer no problem: ], has scenery; -Just notice how we change the description after the first time the -player examines the note, using the local property ``read_once`` created -just for this purpose. We don’t want the player to walk off with the -note, so we intercept the ``Take`` action and display something more in -character than the default message for scenery objects: "That's hardly -portable". - -We've talked a lot about the toilet key; it seems about time to code it. -Originally, the key is in Benny's possession, and the player will have -to ask for it, just as the note explains. Although we'll define Benny in -detail throughout the next chapter, here we present a basic definition, +Just notice how we change the description after the first time the player +examines the note, using the local property ``read_once`` created just for +this purpose. We don’t want the player to walk off with the note, so we +intercept the :act:`Take` action and display something more in character +than the default message for scenery objects: "That's hardly portable". + +We've talked a lot about the toilet key; it seems about time to code it. +Originally, the key is in Benny's possession, and the player will have to +ask for it, just as the note explains. Although we'll define Benny in +detail throughout the next chapter, here we present a basic definition, largely so that the key has a parent object. .. code-block:: inform @@ -721,17 +773,17 @@ largely so that the key has a parent object. "Benny is trusting you to look after that key."; ]; -While Benny has the key, there's logically no way to examine it (or -perform any other action involving it), but we want to prevent the -interpreter from objecting that ``You can't see any such thing``. We've -made the ``toilet_key`` a child of the ``benny`` object, and you can see -that Benny's got a :attr:`transparent` attribute; this means that the key is -in scope, and enables the player to refer to it without the interpreter -complaining. Because Benny also has an :attr:`animate` attribute, the -interpreter would normally intercept a TAKE KEY action with "That seems -to belong to Benny"; however, the same wouldn't apply to other commands -like TOUCH KEY and TASTE KEY. So, to prevent any interaction with the -key while it’s in Benny’s pockets, we define a :prop:`before` property. +While Benny has the key, there's logically no way to examine it (or perform +any other action involving it), but we want to prevent the interpreter from +objecting that ``You can't see any such thing``. We've made the +``toilet_key`` a child of the ``benny`` object, and you can see that +Benny's got a :attr:`transparent` attribute; this means that the key is in +scope, and enables the player to refer to it without the interpreter +complaining. Because Benny also has an :attr:`animate` attribute, the +interpreter would normally intercept a TAKE KEY action with "That seems to +belong to Benny"; however, the same wouldn't apply to other commands like +TOUCH KEY and TASTE KEY. So, to prevent any interaction with the key while +it’s in Benny’s pockets, we define a :prop:`before` property. .. code-block:: inform @@ -743,15 +795,15 @@ key while it’s in Benny’s pockets, we define a :prop:`before` property. "Benny is trusting you to look after that key."; ]; -All of the :prop:`before` properties that we've so far created have contained -one or more labels specifying the actions which they are to intercept; -you'll remember that in "William Tell" we introduced the ``default`` action -(see :ref:`props-class`) to mean "any value not already catered -for". There's one of those labels here, for the Drop action, but that's -preceded by a piece of code that will be executed at the start of *every* -action directed at the key. If it’s still in Benny’s possession, we display -a polite refusal; if the player has it then we prevent careless disposal; -otherwise, the action continues unhindered. +All of the :prop:`before` properties that we've so far created have +contained one or more labels specifying the actions which they are to +intercept; you'll remember that in "William Tell" we introduced the +``default`` action (see :ref:`props-class`) to mean "any value not already +catered for". There's one of those labels here, for the Drop action, but +that's preceded by a piece of code that will be executed at the start of +*every* action directed at the key. If it's still in Benny’s possession, +we display a polite refusal; if the player has it then we prevent careless +disposal; otherwise, the action continues unhindered. (In fact, the hat-on-a-pole ``Prop`` introduced in :ref:`south-side` had this all-exclusive :prop:`before` property: @@ -763,22 +815,26 @@ this all-exclusive :prop:`before` property: print_ret "You're too far away at the moment."; ], -It would have behaved exactly the same if we'd omitted the ``default`` +It would have behaved exactly the same if we'd omitted the ``default`` label, as we do here for Benny's key.) -Another small innovation here: the :prop:`invent` library property (we -didn’t make it up) which enables you to control how objects appear in -inventory listings, overriding the default. Left to itself, the -interpreter simply displays the object’s external name, preceded either -by a standard article like "a" or "some", or one specifically defined in -the object's :prop:`article` property. Here we replace "the toilet key" with -one of two more helpful descriptions, making it a most valuable object -in the eyes of John Covarth, and something to be despised haughtily by -Captain Fate once it's of no further use to him. - -When we had players in the street, we faced the problem that they might -choose to examine the café from the outside. While it's unlikely that -they'll try to examine the toilet room from the outside, it takes very +.. Generated by autoindex +.. index:: + pair: article; library property + +Another small innovation here: the :prop:`invent` library property (we +didn’t make it up) which enables you to control how objects appear in +inventory listings, overriding the default. Left to itself, the +interpreter simply displays the object's external name, preceded either by +a standard article like "a" or "some", or one specifically defined in the +object's :prop:`article` property. Here we replace "the toilet key" with +one of two more helpful descriptions, making it a most valuable object in +the eyes of John Covarth, and something to be despised haughtily by Captain +Fate once it's of no further use to him. + +When we had players in the street, we faced the problem that they might +choose to examine the café from the outside. While it's unlikely that +they'll try to examine the toilet room from the outside, it takes very little effort to offer a sensible output just in case: .. code-block:: inform @@ -813,17 +869,17 @@ little effort to offer a sensible output just in case: ], has scenery openable enterable; -As with the ``outside_of_cafe`` object, we intercept an ``Enter`` -action, to teleport players into the toilet room if they type ENTER -TOILET (or to display a refusal if the toilet door is closed). Players -may try to EXAMINE TOILET; they'll get a different message if the door -is open -- we invite them to enter it -- or if it's closed. OPEN TOILET -and CLOSE TOILET inputs are redirected to ``Open`` and ``Close`` actions -for the toilet door; remember that the double angle-brackets imply a -``return true``, so that the action stops there and the interpreter does -not attempt to ``Open`` or ``Close`` the ``outside_of_toilet`` object -itself after it has dealt with the door. - -You're right: the toilet looms large in this game (we blame it on early -maternal influences). We’ve introduced an ambiguity problem with the +As with the ``outside_of_cafe`` object, we intercept an :act:`Enter` +action, to teleport players into the toilet room if they type ENTER TOILET +(or to display a refusal if the toilet door is closed). Players may try to +EXAMINE TOILET; they'll get a different message if the door is open -- we +invite them to enter it -- or if it's closed. OPEN TOILET and CLOSE TOILET +inputs are redirected to :act:`Open` and :act:`Close` actions for the +toilet door; remember that the double angle-brackets imply a ``return +true``, so that the action stops there and the interpreter does not attempt +to :act:`Open` or :act:`Close` the ``outside_of_toilet`` object itself +after it has dealt with the door. + +You're right: the toilet looms large in this game (we blame it on early +maternal influences). We’ve introduced an ambiguity problem with the ``outside_of_toilet`` object, and we'll need some help in fixing it. diff --git a/chapters/12.rst b/chapters/12.rst index 0260f67..8321823 100644 --- a/chapters/12.rst +++ b/chapters/12.rst @@ -14,7 +14,7 @@ Captain Fate: take 3 |W|\e've given ourselves an interesting challenge by overusing that convenient word "toilet", and here we show you how we resolve the -ambiguities that have been introduced. Also, it's time for the eponymous +ambiguities that have been introduced. Also, it's time for the eponymous owner of Benny's café to be developed in full. Too many toilets @@ -23,87 +23,95 @@ Too many toilets .. index:: single: ambiguous objects -If you check the :prop:`name` properties of the toilet door, the toilet key -and the toilet room, you’ll see that the dictionary word ``'toilet'`` -occurs in all of them. There won't be any problems if players mention -the words DOOR or KEY, but we reach a strange impasse should they try to -perform some action with just the word TOILET. The interpreter has to -think fast: is the player talking about the key? About the door? Or -about the toilet? Unable to decide, it asks: "Which do you mean, the -door to the toilet, the toilet key or the toilet?" - -And guess what? Players will never be able to refer to the toilet object -(unless they type BATH ROOM or REST ROOM, not an obvious choice since we -haven't used those phrases anywhere visible). If the player answers -TOILET the parser will still have three objects with that dictionary -word as a possible name, so it will ask again, and again -- until we -give it some dictionary word which is not ambiguous. A human reader -would be able to understand that the word TOILET alone refers to the -room, but the interpreter won't -- unless we help it a little. - -We could work around this problem in more than one way, but we'll take -this opportunity of demonstrating the use of a third-party library -package. - -When experienced designers find a problem which is not easily solvable, -they may come up with a smart solution and then consider that others -could benefit from the effort. The product of this generosity takes the -form of a library extension: the solution neatly packaged as a file that -other designers can incorporate into their source code. These files can -be found in the IF Archive: go to -http://mirror.ifarchive.org/indexes/if-archive.html and then select -"``.../infocom``", "``.../compilers``", "``.../inform6``", -"``.../library``", and "``.../contributions``". All of these files -contain Inform code. To use a library extension (also known as a library -contribution), you should download it and read the instructions (usually -embedded as comments in the file, but occasionally supplied separately) -to discover what to do next. Normally, you ``Include`` it (as we have -already done with ``Parser``, ``VerbLib`` and ``Grammar``), but often -there are rules about where exactly this Include should be placed in -your source code. It is not unusual to find other suggestions and -warnings. +If you check the :prop:`name` properties of the toilet door, the toilet key +and the toilet room, you’ll see that the dictionary word ``'toilet'`` +occurs in all of them. There won't be any problems if players mention the +words DOOR or KEY, but we reach a strange impasse should they try to +perform some action with just the word TOILET. The interpreter has to +think fast: is the player talking about the key? About the door? Or about +the toilet? Unable to decide, it asks: "Which do you mean, the door to the +toilet, the toilet key or the toilet?" + +And guess what? Players will never be able to refer to the toilet object +(unless they type BATH ROOM or REST ROOM, not an obvious choice since we +haven't used those phrases anywhere visible). If the player answers TOILET +the parser will still have three objects with that dictionary word as a +possible name, so it will ask again, and again -- until we give it some +dictionary word which is not ambiguous. A human reader would be able to +understand that the word TOILET alone refers to the room, but the +interpreter won't -- unless we help it a little. + +We could work around this problem in more than one way, but we'll take this +opportunity of demonstrating the use of a third-party library package. + +.. |IFARCHIVE| replace:: http://mirror.ifarchive.org/indexes/if-archive.html + +.. Generated by autoindex +.. index:: + single: IF Archive + +When experienced designers find a problem which is not easily solvable, +they may come up with a smart solution and then consider that others could +benefit from the effort. The product of this generosity takes the form of +a library extension: the solution neatly packaged as a file that other +designers can incorporate into their source code. These files can be found +in the IF Archive: go to |IFARCHIVE| and then select "``.../infocom``", +"``.../compilers``", "``.../inform6``", "``.../library``", and +"``.../contributions``". All of these files contain Inform code. To use a +library extension (also known as a library contribution), you should +download it and read the instructions (usually embedded as comments in the +file, but occasionally supplied separately) to discover what to do next. +Normally, you ``Include`` it (as we have already done with ``Parser``, +``VerbLib`` and ``Grammar``), but often there are rules about where exactly +this Include should be placed in your source code. It is not unusual to +find other suggestions and warnings. To help us out of the disambiguation problem with the word TOILET, we are going to use Neil Cerutti's extension ``pname.h``, which is designed for -situations precisely like this. First, we follow the link to the IF archive -and download the compressed file ``pname.zip``, which contains two more -files: ``pname.h`` and ``pname.txt``. We place these files in the folder -where we are currently developing our game or, if using the environment we -proposed in :doc:`02`, in the ``Inform\Lib\Contrib`` folder. The text file -offers instructions about installation and usage. Here we find a warning: - - This version of pname.h is recommended for use only with version 6/10 - of the Inform Library. - -We're actually using a later version, but this doesn't seem to cause a -problem. Most extensions aren't so fussy, but ``pname.h`` fiddles with -some routines at the heart of the standard library; these may not be +situations precisely like this. First, we follow the link to the IF +archive and download the compressed file ``pname.zip``, which contains two +more files: ``pname.h`` and ``pname.txt``. We place these files in the +folder where we are currently developing our game or, if using the +environment we proposed in :doc:`02`, in the ``Inform\Lib\Contrib`` folder. +The text file offers instructions about installation and usage. Here we +find a warning: + + This version of pname.h is recommended for use only with version 6/10 of + the Inform Library. + +We're actually using a later version, but this doesn't seem to cause a +problem. Most extensions aren't so fussy, but ``pname.h`` fiddles with +some routines at the heart of the standard library; these may not be identical in other Inform versions. -The introduction explains what ``pname.h`` does for you; namely, it lets -you avoid using complicated :prop:`parse_name` routines to disambiguate the -player's input when the same dictionary word refers to more than one -item. A :prop:`parse_name` routine would have been the solution to our -problem before the existence of this file, and it qualifies as an -advanced programming topic, difficult to master on a first approach. -Fortunately, we don't need to worry. Neil Cerutti explains: - - The ``pname.h`` package defines a new object property, ``pname`` - (short for phrase name), with a similar look and feel to the standard - :prop:`name` property: both contain a list of dictionary words. However, - in a ``pname`` property the order of the words is significant, and - special operators ``'.p'`` ``'.or'`` and ``'.x'`` enable you to embed - some intelligence into the list. In most cases where the standard - :prop:`name` property isn't enough, you can now just replace it with a - ``pname`` property, rather than write a :prop:`parse_name` property +.. Generated by autoindex +.. index:: + pair: parse_name; library property + +The introduction explains what ``pname.h`` does for you; namely, it lets +you avoid using complicated :prop:`parse_name` routines to disambiguate the +player's input when the same dictionary word refers to more than one item. +A :prop:`parse_name` routine would have been the solution to our problem +before the existence of this file, and it qualifies as an advanced +programming topic, difficult to master on a first approach. Fortunately, +we don't need to worry. Neil Cerutti explains: + + The ``pname.h`` package defines a new object property, ``pname`` (short + for phrase name), with a similar look and feel to the standard + :prop:`name` property: both contain a list of dictionary words. However, + in a ``pname`` property the order of the words is significant, and + special operators ``'.p'`` ``'.or'`` and ``'.x'`` enable you to embed + some intelligence into the list. In most cases where the standard + :prop:`name` property isn't enough, you can now just replace it with a + ``pname`` property, rather than write a :prop:`parse_name` property routine. -We'll soon see how it works. Let's take a look at the installation +We'll soon see how it works. Let's take a look at the installation instructions: To incorporate this package into your program, do three things: - #. Add four lines near the head of the program (before you include + #. Add four lines near the head of the program (before you include ``Parser.h``). .. code-block:: inform @@ -123,9 +131,9 @@ instructions: #. Add ``pname`` properties to those objects which require phrase recognition. -It seems simple enough. So, following steps one and two, we add those -``Replace...`` lines before the inclusion of ``Parser``, and we include -``pname.h`` right after it. ``Replace`` tells the compiler that we're +It seems simple enough. So, following steps one and two, we add those +``Replace...`` lines before the inclusion of ``Parser``, and we include +``pname.h`` right after it. ``Replace`` tells the compiler that we're providing replacements for some standard routines. .. code-block:: inform @@ -147,12 +155,12 @@ providing replacements for some standard routines. Include "pname"; ... -Now our source code is ready to benefit from the library package. How -does it work? We have acquired a new property -- ``pname`` -- which can -be added to some of our objects, and which works pretty much like a -:prop:`name` property. In fact, it should be used *instead* of a :prop:`name` -property where we have a disambiguation problem. Let’s change the -relevant lines for the toilet door and the toilet key: +Now our source code is ready to benefit from the library package. How does +it work? We have acquired a new property -- ``pname`` -- which can be +added to some of our objects, and which works pretty much like a +:prop:`name` property. In fact, it should be used *instead* of a +:prop:`name` property where we have a disambiguation problem. Let’s change +the relevant lines for the toilet door and the toilet key: .. code-block:: inform @@ -175,52 +183,50 @@ while leaving the ``outside_of_toilet`` unchanged: before [; ... -We are now using a new operator -- ``'.x'`` -- in our ``pname`` word -lists. explains +We are now using a new operator -- ``'.x'`` -- in our ``pname`` word lists. +The text file explains - The first dictionary word to the right of a ``'.x'`` operator is + The first dictionary word to the right of a ``'.x'`` operator is interpreted as optional. -and this makes the dictionary word ``'toilet'`` of lesser importance for -these objects, so that at run-time players could refer to the DOOR or -TOILET DOOR or the KEY or TOILET KEY -- but not simply to the TOILET -- -when referring to either the door or the key. And, by leaving unchanged -the name property of the ``outside_of_toilet`` object – where there is also -another ``'toilet'`` entry -- the ``pname`` properties will tell the -interpreter to discard the key and the door as possible objects to be -considered when players refer just to TOILET. Looking at it in terms of -the English language, we've effectively said that "TOILET" is an -adjective in the phrases "TOILET DOOR" and "TOILET KEY", but a noun when -used on its own to refer to the room. - -The ``pname.h`` package has additional functionality to deal with more -complex phrases, but we don't need it in our example game. Feel free, -however, to read ``pname.txt`` and discover what this fine library -extension can do for you: it's an easy answer to many a disambiguation +and this makes the dictionary word ``'toilet'`` of lesser importance for +these objects, so that at run-time players could refer to the DOOR or +TOILET DOOR or the KEY or TOILET KEY -- but not simply to the TOILET -- +when referring to either the door or the key. And, by leaving unchanged +the name property of the ``outside_of_toilet`` object – where there is also +another ``'toilet'`` entry -- the ``pname`` properties will tell the +interpreter to discard the key and the door as possible objects to be +considered when players refer just to TOILET. Looking at it in terms of +the English language, we've effectively said that "TOILET" is an adjective +in the phrases "TOILET DOOR" and "TOILET KEY", but a noun when used on its +own to refer to the room. + +The ``pname.h`` package has additional functionality to deal with more +complex phrases, but we don't need it in our example game. Feel free, +however, to read ``pname.txt`` and discover what this fine library +extension can do for you: it's an easy answer to many a disambiguation headache. - Don't shoot! I'm only the barman ================================ -A lot of the action of the game happens around Benny, and his definition -needs a little care. Let's explain what we want to happen. - - So the door is locked and the player, after discovering what the note - stuck on the toilet door said, will eventually ask Benny for the key. - Sadly, Benny allows use of the toilet only to customers, a remark - he'll make looking pointedly at the menu board behind him. The player - will have to ask for a coffee first, thereby qualifying as a customer - in Benny's eyes and thus entitled to make use of the toilet. At last! - Rush inside, change into Captain Fate’s costume and fly away to save - the day! - -Except that the player neither paid for the coffee, nor returned the -toilet key. Benny will have to stop the player from leaving the café in -these circumstances. To prevent unnecessary complication, there will be -a coin near the lavatory, enough cash to pay for the coffee. And that -about sums it all up; pretty simple to describe -- not so simple to -code. Remember Benny's basic definition from the previous chapter: +A lot of the action of the game happens around Benny, and his definition +needs a little care. Let's explain what we want to happen. + + So the door is locked and the player, after discovering what the note + stuck on the toilet door said, will eventually ask Benny for the key. + Sadly, Benny allows use of the toilet only to customers, a remark he'll + make looking pointedly at the menu board behind him. The player will + have to ask for a coffee first, thereby qualifying as a customer in + Benny's eyes and thus entitled to make use of the toilet. At last! Rush + inside, change into Captain Fate’s costume and fly away to save the day! + +Except that the player neither paid for the coffee, nor returned the toilet +key. Benny will have to stop the player from leaving the café in these +circumstances. To prevent unnecessary complication, there will be a coin +near the lavatory, enough cash to pay for the coffee. And that about sums +it all up; pretty simple to describe -- not so simple to code. Remember +Benny's basic definition from the previous chapter: .. code-block:: inform @@ -232,7 +238,7 @@ code. Remember Benny's basic definition from the previous chapter: strikes him.", has scenery animate male proper transparent; -We can now add some complexity, beginning with a :prop:`life` property. In +We can now add some complexity, beginning with a :prop:`life` property. In generic form: .. code-block:: inform @@ -244,7 +250,7 @@ generic form: Ask,Tell,Answer: !... code to handle conversation ], -We have seen some of these actions before. We'll take care of the easier +We have seen some of these actions before. We'll take care of the easier ones: .. code-block:: inform @@ -270,20 +276,20 @@ ones: Ask,Tell,Answer: "Benny is too busy for idle chit-chat."; -Attacking Benny is not wise. If the player is still dressed as John -Covarth, the game displays a message refusing to use violence by reason -of staying in character as a worthless wimp. However, if Captain Fate -attempts the action, we'll find that there is more to Benny than meets -the eye, and the game is lost. Kissing and conversation are disallowed -by a couple of tailored responses. - -The Give action is a bit more complicated, since Benny reacts to certain -objects in a special and significant way. Bear in mind that Benny's -definition needs to keep track of whether the player has asked for a -coffee (thereby becoming a customer and thus worthy of the key), whether -the coffee has been paid for, and whether the toilet key has been -returned. The solution, yet again (this really is a most useful -capability), is more local property variables: +Attacking Benny is not wise. If the player is still dressed as John +Covarth, the game displays a message refusing to use violence by reason of +staying in character as a worthless wimp. However, if Captain Fate +attempts the action, we'll find that there is more to Benny than meets the +eye, and the game is lost. Kissing and conversation are disallowed by a +couple of tailored responses. + +The Give action is a bit more complicated, since Benny reacts to certain +objects in a special and significant way. Bear in mind that Benny's +definition needs to keep track of whether the player has asked for a coffee +(thereby becoming a customer and thus worthy of the key), whether the +coffee has been paid for, and whether the toilet key has been returned. +The solution, yet again (this really is a most useful capability), is more +local property variables: .. code-block:: inform @@ -299,10 +305,10 @@ capability), is more local property variables: live [; ... -Now we are ready to tackle the ``Give`` action of the :prop:`life` property, -which deals with commands like GIVE THE KEY TO BENNY (in a moment, we'll -come to the ``Give`` action of the :prop:`orders` property, which deals with -commands like BENNY, GIVE ME THE KEY): +Now we are ready to tackle the :act:`Give` action of the :prop:`life` +property, which deals with commands like GIVE THE KEY TO BENNY (in a +moment, we'll come to the :act:`Give` action of the :prop:`orders` +property, which deals with commands like BENNY, GIVE ME THE KEY): .. code-block:: inform @@ -329,8 +335,8 @@ commands like BENNY, GIVE ME THE KEY): back anytime,~ he says."; } -The Give action in the :prop:`life` property holds the variable :var:`noun` as -the object offered to the NPC. Remember that we can use the ``switch`` +The Give action in the :prop:`life` property holds the variable :var:`noun` +as the object offered to the NPC. Remember that we can use the ``switch`` statement as shorthand for: .. code-block:: inform @@ -340,39 +346,43 @@ statement as shorthand for: ... We won't let players give away their clothes or their costume (yes, an -improbable action, but you never know). The toilet key and the coin are -successfully transferred. The property ``key_not_returned`` will be set to +improbable action, but you never know). The toilet key and the coin are +successfully transferred. The property ``key_not_returned`` will be set to true when we receive the toilet key from Benny (we have not coded that bit -yet), and now, when we give it back, it's reset to :const:`false`. The ``move`` -statement is in charge of the actual transfer of the object from the -player's inventory to Benny, and we finally display a confirmation -message. With the coin, we find a new statement: ``remove``. This extracts -the object from the object tree, so that it now has no parent. The effect -is to make it disappear from the game (though you are not destroying the -object permanently -- and indeed you could return it to the object tree -using the ``move`` statement); as far as the player is concerned, there -isn’t a COIN to be found anywhere. The ``coffee_not_paid`` property will be -set to true when Benny serves us the cup of coffee (again, we’ll see that -in a moment); now we reset it to :const:`false`, which liberates the player from -debt. This culminates with the ``"..."`` print-and-return statement, -telling the player that the action was successful. In passing, remember -that in :ref:`homely-atmos` we defined the counter such that PUT KEY ON -COUNTER is automatically translated into GIVE KEY TO BENNY . - -Why move the key to Benny but remove the coin instead? Once players -qualify as customers by ordering a coffee, they will be able to ask for -the key and return it as many times as they like, so it seems sensible -to keep the key around. The coin, however, will be a one-shot. We won't -let players ask for more than one coffee, to prevent their debt from -growing ad infinitum -- besides, they came in here to change, not to -indulge in caffeine. Once the coin is paid, it disappears for good, -supposedly into Benny's greedy pockets. No need to worry about it any -more. - -The benny object needs also an :prop:`orders` property, just to take care of -the player's requests for coffee and the key, and to fend off any other -demands. The ``Give`` action in an :prop:`orders` property deals with inputs -like ASK BENNY FOR THE KEY or BENNY, GIVE ME THE KEY. The syntax is +yet), and now, when we give it back, it's reset to :const:`false`. The +``move`` statement is in charge of the actual transfer of the object from +the player's inventory to Benny, and we finally display a confirmation +message. With the coin, we find a new statement: ``remove``. This +extracts the object from the object tree, so that it now has no parent. +The effect is to make it disappear from the game (though you are not +destroying the object permanently -- and indeed you could return it to the +object tree using the ``move`` statement); as far as the player is +concerned, there isn’t a COIN to be found anywhere. The +``coffee_not_paid`` property will be set to true when Benny serves us the +cup of coffee (again, we’ll see that in a moment); now we reset it to +:const:`false`, which liberates the player from debt. This culminates with +the ``"..."`` print-and-return statement, telling the player that the +action was successful. In passing, remember that in :ref:`homely-atmos` we +defined the counter such that PUT KEY ON COUNTER is automatically +translated into GIVE KEY TO BENNY . + +Why move the key to Benny but remove the coin instead? Once players +qualify as customers by ordering a coffee, they will be able to ask for the +key and return it as many times as they like, so it seems sensible to keep +the key around. The coin, however, will be a one-shot. We won't let +players ask for more than one coffee, to prevent their debt from growing ad +infinitum -- besides, they came in here to change, not to indulge in +caffeine. Once the coin is paid, it disappears for good, supposedly into +Benny's greedy pockets. No need to worry about it any more. + +.. Generated by autoindex +.. index:: + pair: life; library property + +The benny object needs also an :prop:`orders` property, just to take care +of the player's requests for coffee and the key, and to fend off any other +demands. The :act:`Give` action in an :prop:`orders` property deals with +inputs like ASK BENNY FOR THE KEY or BENNY, GIVE ME THE KEY. The syntax is similar to that of the :prop:`life` property: .. code-block:: inform @@ -415,54 +425,53 @@ similar to that of the :prop:`life` property: } ], -* We test the value of :var:`second` in order to trap over-generous - gestures such as BENNY, GIVE COFFEE TO CUSTOMERS . Then we consider +* We test the value of :var:`second` in order to trap over-generous + gestures such as BENNY, GIVE COFFEE TO CUSTOMERS. Then we consider potential requests. -* **Toilet key:** first, we check whether players already have the key - or not, and complain if they do, stopping execution thanks to the - implicit ``return true`` of the ``"..."`` statement. If players don’t - have the key, we proceed to check whether they've asked for a coffee - yet, by testing the ``coffee_asked_for`` property. If this is true , - we should also check if the key is actually one of Benny’s - possessions -- a perverse player could get the key, then drop it - somewhere and ask for it again; if this should happen, we indicate - that Benny is nobody's fool with the message ``"~Last place I saw - that key..."``. Once all these fitting conditions are :const:`true`, - players will get the key, which means that they have to return it -- - the ``key_not_returned`` property becomes :const:`true` -- and we display - a suitable message. However, if the player didn't ask for a coffee, - Benny refuses to oblige, mentioning for the first time the menu board - where players will be able to see a picture of a cup of coffee when - they EXAMINE it. Take care to see how all the ``else`` clauses pair - up with the appropriate if statements, triggering responses for each - of the conditions that wasn't met. - -* **Coffee:** we check whether players have already asked for a coffee, - by testing the ``coffee_asked_for`` property, and refuse to serve - another one if :const:`true`. If :const:`false`, we place the coffee on the - counter, and set the properties ``coffee_asked_for`` and - ``coffee_not_paid`` to :const:`true`. The message bit you know about. - -* **Food:** we'll provide an object to deal with all of the delicious - comestibles to be found in the café, specifically those (such as - "pastries and sandwiches") mentioned in our descriptions. Although - that object is not yet defined, we code ahead to thwart player's - gluttony in case they choose to ask Benny for food. - -* **Menu:** our default response -- "I don’t think that’s on the menu, - sir" -- isn’t very appropriate if the player asks for a menu, so we - provide a better one. - -* **Default:** this takes care of anything else that the player asks - Benny for, displaying his curt response. +* **Toilet key:** first, we check whether players already have the key or + not, and complain if they do, stopping execution thanks to the implicit + ``return true`` of the ``"..."`` statement. If players don’t have the + key, we proceed to check whether they've asked for a coffee yet, by + testing the ``coffee_asked_for`` property. If this is true , we should + also check if the key is actually one of Benny’s possessions -- a + perverse player could get the key, then drop it somewhere and ask for it + again; if this should happen, we indicate that Benny is nobody's fool + with the message ``"~Last place I saw that key..."``. Once all these + fitting conditions are :const:`true`, players will get the key, which + means that they have to return it -- the ``key_not_returned`` property + becomes :const:`true` -- and we display a suitable message. However, if + the player didn't ask for a coffee, Benny refuses to oblige, mentioning + for the first time the menu board where players will be able to see a + picture of a cup of coffee when they EXAMINE it. Take care to see how + all the ``else`` clauses pair up with the appropriate if statements, + triggering responses for each of the conditions that wasn't met. + +* **Coffee:** we check whether players have already asked for a coffee, by + testing the ``coffee_asked_for`` property, and refuse to serve another + one if :const:`true`. If :const:`false`, we place the coffee on the + counter, and set the properties ``coffee_asked_for`` and + ``coffee_not_paid`` to :const:`true`. The message bit you know about. + +* **Food:** we'll provide an object to deal with all of the delicious + comestibles to be found in the café, specifically those (such as + "pastries and sandwiches") mentioned in our descriptions. Although that + object is not yet defined, we code ahead to thwart player's gluttony in + case they choose to ask Benny for food. + +* **Menu:** our default response -- "I don’t think that’s on the menu, sir" + -- isn’t very appropriate if the player asks for a menu, so we provide a + better one. + +* **Default:** this takes care of anything else that the player asks Benny + for, displaying his curt response. And before you know it, Benny's object is out of the way; however, don't -celebrate too soon. There’s still some Benny-related behaviour that, -curiously enough, doesn’t happen in Benny's object; we're talking about -Benny's reaction if the player tries to leave without paying or -returning the key. We promised you that Benny would stop the player, and -indeed he will. But where? +celebrate too soon. There’s still some Benny-related behaviour that, +curiously enough, doesn’t happen in Benny's object; we're talking about +Benny's reaction if the player tries to leave without paying or returning +the key. We promised you that Benny would stop the player, and indeed he +will. But where? We must revisit the café room object: @@ -517,33 +526,46 @@ We must revisit the café room object: s_to street, n_to toilet_door; -Once again, we find that the solution to a design problem is not -necessarily unique. Remember what we saw when dealing with the player's -description: we could have assigned a new value to the -``player.description`` variable, but opted to use the -``LibraryMessages`` object instead. This is a similar case. The code -causing Benny to intercept the forgetful player could have been added, -perhaps, to a :prop:`daemon` property in Benny’s definition. However, since -the action to be intercepted is always the same one and happens to be a -movement action when the player tries to leave the café room, it is also -possible to code it by trapping the ``Go`` action of the room object. +.. index:: + pair: LibraryMessages; library object + +.. Generated by autoindex +.. index:: + pair: Go; library action + pair: LibraryMessages; library object + pair: daemon; library property + +Once again, we find that the solution to a design problem is not +necessarily unique. Remember what we saw when dealing with the player's +description: we could have assigned a new value to the +``player.description`` variable, but opted to use the +:obj:`LibraryMessages` object instead. This is a similar case. The code +causing Benny to intercept the forgetful player could have been added, +perhaps, to a :prop:`daemon` property in Benny’s definition. However, +since the action to be intercepted is always the same one and happens to be +a movement action when the player tries to leave the café room, it is also +possible to code it by trapping the :act:`Go` action of the room object. Both would have been right, but this is somewhat simpler. -We have added a :prop:`before` property to the room object (albeit a longish -one), just dealing with the ``Go`` action. As we mentioned in an earlier -chapter, this technique lets you trap the player who is about to exit a -room before the movement actually takes place, a good moment to -interfere if we want to prevent escape. The first line: +.. Generated by autoindex +.. index:: + pair: before; library property + +We have added a :prop:`before` property to the room object (albeit a +longish one), just dealing with the :act:`Go` action. As we mentioned in +an earlier chapter, this technique lets you trap the player who is about to +exit a room before the movement actually takes place, a good moment to +interfere if we want to prevent escape. The first line: .. code-block:: inform if (noun ~= s_obj) return false; -is telling the interpreter that we want to tamper only with southwards -movement, allowing the interpreter to apply normal rules for the other +is telling the interpreter that we want to tamper only with southwards +movement, allowing the interpreter to apply normal rules for the other available directions. -From here on, it's only conditions and more conditions. The player may +From here on, it's only conditions and more conditions. The player may attempt to leave: * without paying for the coffee and without returning the key, @@ -552,8 +574,8 @@ attempt to leave: * having returned the key, but not paid for the coffee, or -* free of sin and accountable for nothing in the eyes of all men (well, - in the eye of Benny, at least). +* free of sin and accountable for nothing in the eyes of all men (well, in + the eye of Benny, at least). The first three are covered by the test: @@ -561,13 +583,13 @@ The first three are covered by the test: if (benny.coffee_not_paid == true || benny.key_not_returned == true) ... -that is, if either the coffee is not paid for *or* if the key is not -returned. When this condition is :const:`false`, it means that both -misdemeanours have been avoided and that the player is free to go. -However, when this condition is :const:`true`, the hand of Benny falls on the -player's shoulder and then the game displays a different message +that is, if either the coffee is not paid for *or* if the key is not +returned. When this condition is :const:`false`, it means that both +misdemeanours have been avoided and that the player is free to go. +However, when this condition is :const:`true`, the hand of Benny falls on +the player's shoulder and then the game displays a different message according to which fault or faults the player has committed. If the player is free to go, and is wearing the crime-fighting costume, -the game is won. We tell you how that's reported in the next chapter, +the game is won. We tell you how that's reported in the next chapter, where we finish off the design. diff --git a/chapters/13.rst b/chapters/13.rst index d4c8642..a36a32b 100644 --- a/chapters/13.rst +++ b/chapters/13.rst @@ -13,7 +13,7 @@ Captain Fate: the final cut :align: left |Y|\ou'll probably be pleased to hear that Captain Fate has almost run his -allotted span. There are some minor objects still to be defined -- the +allotted span. There are some minor objects still to be defined -- the toilet, our hero’s clothes, the all-important costume -- but first we need to decorate the café a little more. @@ -77,45 +77,48 @@ And a not-so-trivial object: it's Colombian."; ]; -There's nothing really new in this object (other than that the :prop:`name` -property caters for orthographically challenged players), but notice how -we don't ``remove`` it after the player drinks it. In an apparently -absurd whim, the coffee returns to Benny magically (although this is not -information that the player needs to know). Why? After you remove an -object from the game, if the player attempts, say, to EXAMINE it, the -interpreter will impertinently state that "You can't see any such -thing". Moreover, if the player asks Benny for a second coffee, once the -first one has been removed, Benny will complain "I don’t think that’s on -the menu, sir" -- a blatant lie -- which was the default in Benny’s -orders property. Since the removed coffee object does not belong to -Benny, it's not a noun that the player can ASK Benny FOR. By making it a -child of the barman (who has the :attr:`transparent` attribute set), the -coffee is still an object that players can refer to. We ensure that they -don't get more cups thanks to Benny's ``coffee_asked_for`` property, -which will remain :const:`true` after the first time. - -We also ensure that Benny doesn't ask for money from players who have -already paid, by first printing a "You pick up the cup..." message and -then testing Benny's ``coffee_not_paid`` property. If its value is -:const:`true`, we can finish the message with the "quidbuck" print-and-return -statement. If its value is :const:`false`, the player has previously paid, -and so there's nothing else to say. However, we still need to terminate -the incomplete message with a newline, and to return :const:`true` from the -property routine; we *could* have used the statements ``{ print "^"; -return true; }``, but an empty ``""`` statement does the same thing more -neatly. - +There's nothing really new in this object (other than that the :prop:`name` +property caters for orthographically challenged players), but notice how we +don't ``remove`` it after the player drinks it. In an apparently absurd +whim, the coffee returns to Benny magically (although this is not +information that the player needs to know). Why? After you remove an +object from the game, if the player attempts, say, to EXAMINE it, the +interpreter will impertinently state that "You can't see any such thing". +Moreover, if the player asks Benny for a second coffee, once the first one +has been removed, Benny will complain "I don’t think that’s on the menu, +sir" -- a blatant lie -- which was the default in Benny’s orders property. +Since the removed coffee object does not belong to Benny, it's not a noun +that the player can ASK Benny FOR. By making it a child of the barman (who +has the :attr:`transparent` attribute set), the coffee is still an object +that players can refer to. We ensure that they don't get more cups thanks +to Benny's ``coffee_asked_for`` property, which will remain :const:`true` +after the first time. + +.. Generated by autoindex +.. index:: + pair: false; library constant + +We also ensure that Benny doesn't ask for money from players who have +already paid, by first printing a "You pick up the cup..." message and +then testing Benny's ``coffee_not_paid`` property. If its value is +:const:`true`, we can finish the message with the "quidbuck" +print-and-return statement. If its value is :const:`false`, the player has +previously paid, and so there's nothing else to say. However, we still +need to terminate the incomplete message with a newline, and to return +:const:`true` from the property routine; we *could* have used the +statements ``{ print "^"; return true; }``, but an empty ``""`` statement +does the same thing more neatly. Toilet or dressing room? ======================== -Rather more of the latter, actually, since it's the only place away from -curious eyes where our hero will be able to metamorphose from weakling -into the bane of all evildoers. And we *really* don't want to become, -erm, bogged down with details of the room's function or plumbing. +Rather more of the latter, actually, since it's the only place away from +curious eyes where our hero will be able to metamorphose from weakling into +the bane of all evildoers. And we *really* don't want to become, erm, +bogged down with details of the room's function or plumbing. -There's not a lot about the toilet room and its contents, though there -will be some tricky side effects: +There's not a lot about the toilet room and its contents, though there will +be some tricky side effects: .. code-block:: inform @@ -159,32 +162,38 @@ will be some tricky side effects: ], has scored; -We initially place the coin as a child of the lavatory (just so that we -can easily make the ``if (coin in self)`` one-time test). Since the -lavatory does not have the :attr:`transparent` attribute set, the coin will -be invisible to players until they try to inspect the lavatory, an -action that will move the coin into the toilet room. Once taken, the -coin will remain in the inventory until the player gives it to Benny, -because we trap any ``Drop`` actions to help the player to Do the Right -Thing. - -The lavatory object includes a load of helpful synonyms in its name -property, including our favourite word ``'toilet'`` . That won't be a -problem: the other objects here which may have TOILET in their names -- -the key and the door -- both use the ``pname`` property to turn their -use of ``'toilet'`` into a lower-priority adjective. - -See that here we have the only two :attr:`scored` attributes of the game. -The player will be awarded one point for entering the toilet room, and +We initially place the coin as a child of the lavatory (just so that we can +easily make the ``if (coin in self)`` one-time test). Since the lavatory +does not have the :attr:`transparent` attribute set, the coin will be +invisible to players until they try to inspect the lavatory, an action that +will move the coin into the toilet room. Once taken, the coin will remain +in the inventory until the player gives it to Benny, because we trap any +:act:`Drop` actions to help the player to Do the Right Thing. + +The lavatory object includes a load of helpful synonyms in its name +property, including our favourite word ``'toilet'`` . That won't be a +problem: the other objects here which may have TOILET in their names -- the +key and the door -- both use the ``pname`` property to turn their use of +``'toilet'`` into a lower-priority adjective. + +.. Generated by autoindex +.. index:: + pair: scored; library attribute + +See that here we have the only two :attr:`scored` attributes of the game. +The player will be awarded one point for entering the toilet room, and another for finding and picking up the coin. -You might have noticed that we are forcefully clearing the :attr:`light` -attribute, inherited from the ``Room`` class. This will be a windowless -space and, to add a touch of realism, we'll make the room a dark one, -which will enable us to tell you about Inform's default behaviour when -there's no light to see by. However, let's define first the light switch -mentioned in the room's description to aid players in their dressing -duties. +.. Generated by autoindex +.. index:: + pair: light; library attribute + +You might have noticed that we are forcefully clearing the :attr:`light` +attribute, inherited from the ``Room`` class. This will be a windowless +space and, to add a touch of realism, we'll make the room a dark one, which +will enable us to tell you about Inform's default behaviour when there's no +light to see by. However, let's define first the light switch mentioned in +the room's description to aid players in their dressing duties. .. code-block:: inform @@ -208,10 +217,10 @@ duties. ], has switchable ~on; -Please notice the appearance of new attributes :attr:`switchable` and -:attr:`on`. switchable enables the object to be turned on and off, and is -typical of lanterns, computers, television sets, radios, and so on. The -library automatically extends the description of these objects by +Please notice the appearance of new attributes :attr:`switchable` and +:attr:`on`. :attr:`switchable` enables the object to be turned on and off, +and is typical of lanterns, computers, television sets, radios, and so on. +The library automatically extends the description of these objects by indicating if they are currently on or off: .. code-block:: transcript @@ -220,20 +229,19 @@ indicating if they are currently on or off: A notorious ACHIEVEMENT of technological SCIENCE, elegant yet EASY to use. The light switch is currently switched on. -Two new actions are ready to use, ``SwitchOn`` and ``SwitchOff``. Left -to themselves, they toggle the object's state between ON and OFF and +Two new actions are ready to use, :act:`SwitchOn` and :act:`SwitchOff`. +Left to themselves, they toggle the object's state between ON and OFF and display a message like: .. code-block:: transcript You switch the brass lantern on. -They also take care of checking if the player fumbled and tried to turn -on (or off) an object which was already on (or off). How does the -library know the state of the object? This is thanks to the :attr:`on` -attribute, which is set or cleared automatically as needed. You can, of -course, set or clear it manually like any other attribute, with the -``give`` statement: +They also take care of checking if the player fumbled and tried to turn on +(or off) an object which was already on (or off). How does the library +know the state of the object? This is thanks to the :attr:`on` attribute, +which is set or cleared automatically as needed. You can, of course, set +or clear it manually like any other attribute, with the ``give`` statement: .. code-block:: inform @@ -249,59 +257,62 @@ and check if a :attr:`switchable` object is on or off with the test: if (light_switch hasnt on) ... -A :attr:`switchable` object is OFF by default. However, you’ll notice that +A :attr:`switchable` object is OFF by default. However, you’ll notice that the has line of the object definition includes ``~on`` : .. code-block:: inform has switchable ~on; -Surely that’s saying "not-on"? Surely that's what would have happened +Surely that’s saying "not-on"? Surely that's what would have happened anyway if the line hadn't mentioned the attribute at all? .. code-block:: inform has switchable; -Absolutely true. Adding that ``~on`` attribute has no effect whatsoever -on the game -- but nevertheless it's a good idea. It's an aide-mémoire, -a way of reminding ourselves that we start with the attribute clear, and -that at some point we'll be setting it for some purpose. Trust us: it's -worthwhile taking tiny opportunities like this to help yourself. - -Let’s see how our light switch works. We trap the ``SwitchOn`` and -``SwitchOff`` actions in the :prop:`after` property (when the switching has -successfully taken place) and use them to give :attr:`light` to the light -switch. - -Uh, wait. To the light switch? Why not to the toilet room? Well, there's -a reason and we'll see it in a minute. For now, just remember that, in -order for players to see their surroundings, you need only one object in -a room with the :attr:`light` attribute set. It doesn't have to be the room +Absolutely true. Adding that ``~on`` attribute has no effect whatsoever on +the game -- but nevertheless it's a good idea. It's an aide-mémoire, a way +of reminding ourselves that we start with the attribute clear, and that at +some point we'll be setting it for some purpose. Trust us: it's worthwhile +taking tiny opportunities like this to help yourself. + +.. Generated by autoindex +.. index:: + pair: after; library property + +Let’s see how our light switch works. We trap the :act:`SwitchOn` and +:act:`SwitchOff` actions in the :prop:`after` property (when the switching +has successfully taken place) and use them to give :attr:`light` to the +light switch. + +Uh, wait. To the light switch? Why not to the toilet room? Well, there's +a reason and we'll see it in a minute. For now, just remember that, in +order for players to see their surroundings, you need only one object in a +room with the :attr:`light` attribute set. It doesn't have to be the room itself (though this is usually convenient). -After setting the :attr:`light` attribute, we display a customised message, +After setting the :attr:`light` attribute, we display a customised message, to avoid the default: .. code-block:: transcript You switch the light switch on. -which, given the name of the object, doesn't read very elegantly. We -foresee that players might try to PUSH SWITCH, so we trap this attempt -in a :prop:`before` property and redirect it to ``SwitchOn`` and -``SwitchOff`` actions, checking first which one is needed by testing the -:attr:`on` attribute. Finally, we have made the switch a member of the class -``Appliance``, so that the player doesn't walk away with it. +which, given the name of the object, doesn't read very elegantly. We +foresee that players might try to PUSH SWITCH, so we trap this attempt in a +:prop:`before` property and redirect it to :act:`SwitchOn` and +:act:`SwitchOff` actions, checking first which one is needed by testing the +:attr:`on` attribute. Finally, we have made the switch a member of the +class ``Appliance``, so that the player doesn't walk away with it. .. note:: - Remember what we said about class inheritance? No matter what you - define in the class, the object’s definition has priority. The class - ``Appliance`` defines a response for the ``Push`` action, but we + Remember what we said about class inheritance? No matter what you define + in the class, the object’s definition has priority. The class + ``Appliance`` defines a response for the :act:`Push` action, but we override it here with a new behaviour. - And there was light =================== @@ -312,13 +323,12 @@ So the player walks into the toilet and Darkness It is pitch dark, and you can't see a thing. -Oops! No toilet description, no mention of the light switch, nothing. It -is reasonable to think that if we have opened the toilet door to access -the toilet, some light coming from the café room will illuminate our -surroundings -- at least until the player decides to close the door. So -perhaps it would be a good idea to append a little code to the door -object to account for this. A couple of lines in the after property will -suffice: +Oops! No toilet description, no mention of the light switch, nothing. It +is reasonable to think that if we have opened the toilet door to access the +toilet, some light coming from the café room will illuminate our +surroundings -- at least until the player decides to close the door. So +perhaps it would be a good idea to append a little code to the door object +to account for this. A couple of lines in the after property will suffice: .. code-block:: inform @@ -336,19 +346,19 @@ suffice: ], -And this is the reason why the light switch didn't set the :attr:`light` -attribute of the toilet room, but did it to itself. We avoid running -into trouble if we let the open/closed states of the door control the -light of the room object, and the on/off states of the switch control -the light of the switch. So it is one shiny light switch. Fortunately, -players are never aware of this glowing artefact. +And this is the reason why the light switch didn't set the :attr:`light` +attribute of the toilet room, but did it to itself. We avoid running into +trouble if we let the open/closed states of the door control the light of +the room object, and the on/off states of the switch control the light of +the switch. So it is one shiny light switch. Fortunately, players are +never aware of this glowing artefact. .. note:: - Now, could they? Well, if players could TAKE the light switch (which - we have forbidden) and then did INVENTORY, the trick would be given - away, because all objects with the :attr:`light` attribute set are listed - as ``(providing light)`` . + Now, could they? Well, if players could TAKE the light switch (which we + have forbidden) and then did INVENTORY, the trick would be given away, + because all objects with the :attr:`light` attribute set are listed as + ``(providing light)`` . So the player walks into the toilet and @@ -361,7 +371,7 @@ So the player walks into the toilet and [Your score has just gone up by one point.] -Better. Now, suppose the player closes the door. +Better. Now, suppose the player closes the door. .. code-block:: transcript @@ -372,7 +382,7 @@ Better. Now, suppose the player closes the door. The player might try then to LOOK: -Well, no problem. We have mentioned that there is a light switch. Surely +Well, no problem. We have mentioned that there is a light switch. Surely the player will now try to: .. code-block:: transcript @@ -380,7 +390,7 @@ the player will now try to: >TURN ON LIGHT SWITCH You can't see any such thing. -Oops! Things are getting nasty here in the dark. It's probably time to +Oops! Things are getting nasty here in the dark. It's probably time to leave this place and try another approach: .. code-block:: transcript @@ -388,41 +398,40 @@ leave this place and try another approach: >OPEN DOOR You can't see any such thing. -And this illustrates one of the terrible things about darkness in a -game. You can't see anything; you can do very little indeed. All objects -except those in your inventory are out of scope, unreachable, as if -non-existent. Worse, if you DROP one of the objects you are carrying, it -will be swallowed by the dark, never to be found until there is light to -see by. - -The player, who is doubtless immersed in the fantasy of the game, will -now be a little annoyed. "I am in a small bathroom and I can't even -reach the door I have just closed?" The player's right, of -course [#dark]_. Darkened rooms are one cliché of traditional games. -Usually you move in one direction while looking for treasure in some -underground cave, and suddenly arrive at a pitch black place. It's good -behaviour of the game to disallow exploration of unknown dark territory, -and it's a convention to bar passage to players until they return with a -light source. However, if the scenario of the game features, say, the -player character's home, a little apartment with two rooms, and there’s -no light in the kitchen, we could expect the owner of the house to know -how to move around a little, perhaps groping for the light switch or -even going to the refrigerator in the dark. - -We are in a similar situation. The inner logic of the game demands that -blind players should be able to open the door and probably operate the -light switch they've just encountered. We have been telling you that an -object is in scope when it’s in the same room as the player. Darkness -changes that rule. All objects not directly carried by the player become +And this illustrates one of the terrible things about darkness in a game. +You can't see anything; you can do very little indeed. All objects except +those in your inventory are out of scope, unreachable, as if non-existent. +Worse, if you DROP one of the objects you are carrying, it will be +swallowed by the dark, never to be found until there is light to see by. + +The player, who is doubtless immersed in the fantasy of the game, will now +be a little annoyed. "I am in a small bathroom and I can't even reach the +door I have just closed?" The player's right, of course [#dark]_. +Darkened rooms are one cliché of traditional games. Usually you move in +one direction while looking for treasure in some underground cave, and +suddenly arrive at a pitch black place. It's good behaviour of the game to +disallow exploration of unknown dark territory, and it's a convention to +bar passage to players until they return with a light source. However, if +the scenario of the game features, say, the player character's home, a +little apartment with two rooms, and there’s no light in the kitchen, we +could expect the owner of the house to know how to move around a little, +perhaps groping for the light switch or even going to the refrigerator in +the dark. + +We are in a similar situation. The inner logic of the game demands that +blind players should be able to open the door and probably operate the +light switch they've just encountered. We have been telling you that an +object is in scope when it’s in the same room as the player. Darkness +changes that rule. All objects not directly carried by the player become out of scope. -One of the advantages of an advanced design system like Inform is the -flexibility to change all default behaviours to suit your particular -needs. Scope problems are no different. There is a set of routines and -functions to tamper with what's in scope when. We'll see just a tiny -example to fix our particular problem. In the section "``Entry point -routines``" of our game -- after the ``Initialise`` routine, for -instance -- include the following lines: +One of the advantages of an advanced design system like Inform is the +flexibility to change all default behaviours to suit your particular needs. +Scope problems are no different. There is a set of routines and functions +to tamper with what's in scope when. We'll see just a tiny example to fix +our particular problem. In the section "``Entry point routines``" of our +game -- after the ``Initialise`` routine, for instance -- include the +following lines: .. code-block:: inform @@ -436,20 +445,24 @@ instance -- include the following lines: :samp:`InScope({actor_obj_id})` is an entry point routine that can tamper with the scope rules for the given :samp:`{actor_obj_id}` (either the -player character or a NPC). We define it with one variable (which we name +player character or a NPC). We define it with one variable (which we name as we please; it's also a good idea to name variables in an intuitive way to remind us of what they represent), ``person`` , and then we make a complex test to see if the player is actually in the toilet and in the dark. -We have told you that the library variable :var:`location` holds the -current -room that the player is in. However, when there is no light, the -variable location gets assigned to the value of the special library -object thedark . It doesn't matter if we have ten dark rooms in our -game; location will be equal to thedark in all of them. There is yet -another variable, called :var:`real_location`, which holds the room the -player is in *even when there is no light to see by*. +.. Generated by autoindex +.. index:: + pair: location; library variable + pair: real_location; library variable + +We have told you that the library variable :var:`location` holds the +current room that the player is in. However, when there is no light, the +variable location gets assigned to the value of the special library object +thedark . It doesn't matter if we have ten dark rooms in our game; +location will be equal to thedark in all of them. There is yet another +variable, called :var:`real_location`, which holds the room the player is +in *even when there is no light to see by*. So the test: @@ -457,19 +470,22 @@ So the test: if (person == player && location == thedark && real_location == toilet) ... -is stating: if the specified actor is the :var:`player` character *and* he -finds himself in the dark *and* he actually happens to be in the -toilet... +is stating: if the specified actor is the :var:`player` character *and* he +finds himself in the dark *and* he actually happens to be in the toilet... + +.. Generated by autoindex +.. index:: + pair: true; library constant Then we make a call to one of the library routines, :samp:`PlaceInScope({obj_id})`, which has a very descriptive name: it -places in scope the given object. In our case, we want both the door and +places in scope the given object. In our case, we want both the door and the light switch to be within reach of the player, hence both additional -lines. Finally, we must ``return false``, because we want the normal scope +lines. Finally, we must ``return false``, because we want the normal scope rules for the defined actor -- the player -- to apply to the rest of the -objects of the game (if we returned :const:`true`, players would find that they -are able to interact with very little indeed). Now we get a friendlier and -more logical response: +objects of the game (if we returned :const:`true`, players would find that +they are able to interact with very little indeed). Now we get a +friendlier and more logical response: .. code-block:: transcript @@ -484,39 +500,44 @@ more logical response: little more than a lavatory and a light switch. The only exit is south, through the door and into the cafe. -And the same would happen with the door. Notice how the room description -gets displayed after we pass from dark to light; this is the normal -library behaviour. +And the same would happen with the door. Notice how the room description +gets displayed after we pass from dark to light; this is the normal library +behaviour. -There is still one final problem which, admittedly, might originate from -an improbable course of action; however, it could be a nuisance. Suppose -that the player enters the toilet, locks the door -- which is possible -in the dark now that the door is in scope -- and then drops the key. -There's no way to exit the toilet -- because the door is locked and the -key has disappeared, engulfed by the darkness -- unless the player -thinks to turn on the light switch, thereby placing the key in scope -once more. +There is still one final problem which, admittedly, might originate from an +improbable course of action; however, it could be a nuisance. Suppose that +the player enters the toilet, locks the door -- which is possible in the +dark now that the door is in scope -- and then drops the key. There's no +way to exit the toilet -- because the door is locked and the key has +disappeared, engulfed by the darkness -- unless the player thinks to turn +on the light switch, thereby placing the key in scope once more. Why don't we add a :samp:`PlaceInScope({toilet_key})` to the above routine? Well, for starters, the key can be moved around (as opposed to the door or -the light switch, which are fixed items in the toilet room). Suppose the +the light switch, which are fixed items in the toilet room). Suppose the player opens the door of the toilet, but drops the key in the café, then -enters the toilet and closes the door. The condition is met and the key is -placed in scope, when it's in another room. Second, this is a simple game +enters the toilet and closes the door. The condition is met and the key is +placed in scope, when it's in another room. Second, this is a simple game with just a few objects, so you can define a rule for each of them; but in any large game, you might like to be able to refer to objects in bunches, and make general rules that apply to all (or some) of them. -We need to add code to the ``InScope`` routine, telling the game to -place in scope all objects that we drop in the dark, so that we might -recover them (maybe going on all fours and groping a little, but it’s a -possible action). We don’t want the player to have other objects in -scope (like the coin, for instance), so it might be good to have a way -of testing if the objects have been touched and carried by the player. -The attribute :attr:`moved` is perfect for this. The library sets it for -every object that the player has picked up at one time in the game; -:attr:`scenery` and :attr:`static` objects, and those we have not yet seen don't -have :attr:`moved`. Here is the reworked ``InScope`` routine. There are a +.. Generated by autoindex +.. index:: + pair: moved; library attribute + pair: scenery; library attribute + pair: static; library attribute + +We need to add code to the ``InScope`` routine, telling the game to place +in scope all objects that we drop in the dark, so that we might recover +them (maybe going on all fours and groping a little, but it’s a possible +action). We don’t want the player to have other objects in scope (like the +coin, for instance), so it might be good to have a way of testing if the +objects have been touched and carried by the player. The attribute +:attr:`moved` is perfect for this. The library sets it for every object +that the player has picked up at one time in the game; :attr:`scenery` and +:attr:`static` objects, and those we have not yet seen don't have +:attr:`moved`. Here is the reworked ``InScope`` routine. There are a couple of new concepts to look at: .. code-block:: inform @@ -532,25 +553,25 @@ couple of new concepts to look at: return false; ]; -We have added one more local variable to the routine, ``item`` -- again, -this is a variable we have created and named on our own; it is not part -of the library. We make now a new test: if the actor is the player and -the location is any dark room, then perform a certain action. We don't -need to specify the toilet, because we want this rule to apply to all -dark rooms (well, the only dark room in the game *is* the toilet, but we -are trying to provide a general rule). +We have added one more local variable to the routine, ``item`` -- again, +this is a variable we have created and named on our own; it is not part of +the library. We make now a new test: if the actor is the player and the +location is any dark room, then perform a certain action. We don't need to +specify the toilet, because we want this rule to apply to all dark rooms +(well, the only dark room in the game *is* the toilet, but we are trying to +provide a general rule). :samp:`objectloop (variable) {statement};` -is a loop statement, one of the four defined in Inform. A loop statement is -a construct that allows you to run several times through a statement (or a -statement block). ``objectloop`` performs the :samp:`{statement}` once for -every object defined in the (:samp:`{variable}`) . If we were to code: +is a loop statement, one of the four defined in Inform. A loop statement +is a construct that allows you to run several times through a statement (or +a statement block). ``objectloop`` performs the :samp:`{statement}` once +for every object defined in the (:samp:`{variable}`) . If we were to code: :samp:`objectloop (item) {statement};` then the :samp:`{statement}` would be executed once for each object in the -game. However, we want to perform the statement only for those objects +game. However, we want to perform the statement only for those objects whose parent object is the same as the player's parent object: that is, for objects in the same room as the player, so we instead code: @@ -563,25 +584,24 @@ What is the actual :samp:`{statement}` that we'll repeatedly execute? if (item has moved) PlaceInScope(item); -The test: ``if (item has moved)`` ensures that ``PlaceInScope(item)`` -deals only with objects with the :attr:`moved` attribute set. So: if the -player is in the dark, let’s go through the objects which are in the -same room, one at a time. For each of them, check if it's an item that -the player has at some time carried, in which case, place it in scope. -All dropped objects within the room were carried at one time, so we let -players recollect them even if they can’t see them. - -As you see, darkness has its delicate side. If you plan to have dark -rooms galore in your games, bear in mind that you are in for some -elaborate code (unless you let the library carry on with default rules, -in which case there won't be much for your players to do). +The test: ``if (item has moved)`` ensures that ``PlaceInScope(item)`` deals +only with objects with the :attr:`moved` attribute set. So: if the player +is in the dark, let’s go through the objects which are in the same room, +one at a time. For each of them, check if it's an item that the player has +at some time carried, in which case, place it in scope. All dropped +objects within the room were carried at one time, so we let players +recollect them even if they can’t see them. +As you see, darkness has its delicate side. If you plan to have dark rooms +galore in your games, bear in mind that you are in for some elaborate code +(unless you let the library carry on with default rules, in which case +there won't be much for your players to do). Amazing techicolour dreamcoats ============================== -This leaves us the clothing items themselves, which will require a few -tailored actions. Let's see first the ordinary garments of John Covarth: +This leaves us the clothing items themselves, which will require a few +tailored actions. Let's see first the ordinary garments of John Covarth: .. code-block:: inform @@ -645,43 +665,47 @@ tailored actions. Let's see first the ordinary garments of John Covarth: ], clothing proper pluralname; -See how the object deals only with ``Wear``, ``Disrobe`` and ``Change``. -``Wear`` and ``Disrobe`` are standard library actions already defined in -Inform, but we'll have to make a new verb to allow for CHANGE CLOTHES. -In this game, ``Disrobe`` and ``Change`` are considered synonymous for -all purposes; we'll deal with them first. - -The goal of the game is for players to change their clothes, so we might -expect them to try this almost anywhere; but first of all we have to -check that the ``clothes`` object is actually being worn. If not, we -display a message reminding the player that this action has become -irrelevant. What we do with the ``switch`` statement is to offer a -variety of responses according to the :var:`location` variable. The street -(in or out of the booth) and the café all display refusals of some kind, -until the player character manages to enter the toilet, where we -additionally require that he locks the door before taking off his -clothes. If the door is closed but not locked, he is interrupted in his -naked state by a nervous woman who starts shouting, and the game is lost -(this is not as unfair as it seems, because the player may always revert -to the previous state with UNDO). If the door is locked, he succeeds in -his transformation (we take away the :attr:`worn` attribute from the -``clothes`` and give it to the ``costume`` instead). We add a special -refusal to change in the dark, forcing players to turn on the light and -then, we hope, to find the coin. And finally we code a ``default`` -entry; you'll remember that, in a ``switch`` statement, this is supposed -to cater for any value not explicitly listed for the expression under -control -- in this case, for the variable :var:`location`. Since we have -already gone through all the possible locations of the game, this entry -appears only as a defensive measure, just in case something unexpected -happens (for instance, we might extend the game with another room and -forget about this ``switch`` statement). In normal and controlled -conditions, it should never be reached, but it doesn't hurt one bit to -have it there. - -The ``Wear`` action just checks if these clothes are already being worn, -to offer two different rejection responses: the goal of the game is to -change into the hero's suit, after which we'll prevent a change back -into ordinary clothes. So now we are dealing with a Captain Fate in full +See how the object deals only with :act:`Wear`, :act:`Disrobe` and +:act:`Change`. :act:`Wear` and :act:`Disrobe` are standard library actions +already defined in Inform, but we'll have to make a new verb to allow for +CHANGE CLOTHES. In this game, :act:`Disrobe` and :act:`Change` are +considered synonymous for all purposes; we'll deal with them first. + +.. Generated by autoindex +.. index:: + pair: location; library variable + pair: worn; library attribute + +The goal of the game is for players to change their clothes, so we might +expect them to try this almost anywhere; but first of all we have to check +that the ``clothes`` object is actually being worn. If not, we display a +message reminding the player that this action has become irrelevant. What +we do with the ``switch`` statement is to offer a variety of responses +according to the :var:`location` variable. The street (in or out of the +booth) and the café all display refusals of some kind, until the player +character manages to enter the toilet, where we additionally require that +he locks the door before taking off his clothes. If the door is closed but +not locked, he is interrupted in his naked state by a nervous woman who +starts shouting, and the game is lost (this is not as unfair as it seems, +because the player may always revert to the previous state with UNDO). If +the door is locked, he succeeds in his transformation (we take away the +:attr:`worn` attribute from the ``clothes`` and give it to the ``costume`` +instead). We add a special refusal to change in the dark, forcing players +to turn on the light and then, we hope, to find the coin. And finally we +code a ``default`` entry; you'll remember that, in a ``switch`` statement, +this is supposed to cater for any value not explicitly listed for the +expression under control -- in this case, for the variable :var:`location`. +Since we have already gone through all the possible locations of the game, +this entry appears only as a defensive measure, just in case something +unexpected happens (for instance, we might extend the game with another +room and forget about this ``switch`` statement). In normal and controlled +conditions, it should never be reached, but it doesn't hurt one bit to have +it there. + +The :act:`Wear` action just checks if these clothes are already being worn, +to offer two different rejection responses: the goal of the game is to +change into the hero's suit, after which we'll prevent a change back into +ordinary clothes. So now we are dealing with a Captain Fate in full costume: .. code-block:: inform @@ -709,11 +733,10 @@ costume: ], has clothing proper; -Note that we intercept the action WEAR COSTUME and hint that players -should try TAKE OFF CLOTHES instead. We don't let them take off the -costume once it’s being worn, and we certainly don't let them misplace -it anywhere, by refusing to accept a ``Drop`` action. - +Note that we intercept the action WEAR COSTUME and hint that players should +try TAKE OFF CLOTHES instead. We don't let them take off the costume once +it’s being worn, and we certainly don't let them misplace it anywhere, by +refusing to accept a :act:`Drop` action. It's a wrap =========== @@ -722,9 +745,9 @@ Nearly there; just a few minor odds and ends to round things off. .. rubric:: Initialise routine -All the objects of our game are defined. Now we must add a couple of -lines to the ``Initialise`` routine to make sure that the player does -not start the game naked: +All the objects of our game are defined. Now we must add a couple of lines +to the ``Initialise`` routine to make sure that the player does not start +the game naked: .. code-block:: inform @@ -741,39 +764,39 @@ not start the game naked: Captain FATE costume fast...!^^"; ]; -Remember that we included a disambiguation package, ``pname.h``? There -were some additional comments in the accompanying text file that should -be taken in consideration: +Remember that we included a disambiguation package, ``pname.h``? There +were some additional comments in the accompanying text file that should be +taken in consideration: - pname.h provides a pname_verify routine. When DEBUG is defined, you - may call pname_verify() in your Initialise() routine to verify the pname + pname.h provides a pname_verify routine. When DEBUG is defined, you may + call pname_verify() in your Initialise() routine to verify the pname properties in your objects. -The designer of the package has made a debugging tool (a routine) to -check for errors when using his library, and he tells us how to use it. -So we include the suggested lines into our ``Initialise`` routine: +The designer of the package has made a debugging tool (a routine) to check +for errors when using his library, and he tells us how to use it. So we +include the suggested lines into our ``Initialise`` routine: .. code-block:: inform #Ifdef DEBUG; pname_verify(); #Endif; -As the text explains, what this does is: first check whether the game is -being compiled in Debug mode; if this is the case, run the -``pname_verify`` routine, so that it tests all ``pname`` properties to -see if they are written correctly. +As the text explains, what this does is: first check whether the game is +being compiled in Debug mode; if this is the case, run the ``pname_verify`` +routine, so that it tests all ``pname`` properties to see if they are +written correctly. .. rubric:: Demise of our hero We have made three possible endings: -#. The player attempts to change in the toilet with an unlocked door. +#. The player attempts to change in the toilet with an unlocked door. -#. The player tries to attack Benny while wearing the costume. +#. The player tries to attack Benny while wearing the costume. -#. The player manages to exit the café dressed as Captain Fate. +#. The player manages to exit the café dressed as Captain Fate. -(1) and (2) lose the game, (3) wins it. The library defaults for these -two states display, respectively, +(1) and (2) lose the game, (3) wins it. The library defaults for these two +states display, respectively, .. code-block:: transcript @@ -781,13 +804,13 @@ two states display, respectively, *** You have won *** -These states correspond to the values of the :var:`deadflag` variable: 1 -for losing, 2 for winning. However, we have made up different messages, -because our hero does not really die -- ours suffers a FATE worse than -death -- and because we want to give him a more descriptive winning -line. Therefore, we must define a ``DeathMessage`` routine as we did in -"William Tell", to write our customised messages and assign them to -:var:`deadflag` values greater than 2. +These states correspond to the values of the :var:`deadflag` variable: 1 +for losing, 2 for winning. However, we have made up different messages, +because our hero does not really die -- ours suffers a FATE worse than +death -- and because we want to give him a more descriptive winning line. +Therefore, we must define a ``DeathMessage`` routine as we did in "William +Tell", to write our customised messages and assign them to :var:`deadflag` +values greater than 2. .. code-block:: inform @@ -799,9 +822,9 @@ line. Therefore, we must define a ``DeathMessage`` routine as we did in .. rubric:: Grammar -Finally, we need to extend the existing grammar, to allow for a couple -of things. We have already seen that we need a verb CHANGE. We'll make -it really simple: +Finally, we need to extend the existing grammar, to allow for a couple of +things. We have already seen that we need a verb CHANGE. We'll make it +really simple: .. code-block:: inform @@ -814,19 +837,23 @@ it really simple: Verb 'change' 'exchange' 'swap' 'swop' * noun -> Change; -Just notice how the verb handler checks whether the noun given is plural -or singular, to display a suitable pronoun. +Just notice how the verb handler checks whether the noun given is plural or +singular, to display a suitable pronoun. + +A further detail: when players are in the café, they might ask Benny for +the coffee (as we intend and heavily hint), for a sandwich or a pastry +(both mentioned in the café description), for food or a snack (mentioned +here and there, and we have provided for those); but what if they try a +meat pie? Or scrambled eggs? There’s just so much decoration one can +reasonably insert in a game, and loading the dictionary with Benny’s full +menu would be overdoing it a bit. -A further detail: when players are in the café, they might ask Benny for -the coffee (as we intend and heavily hint), for a sandwich or a pastry -(both mentioned in the café description), for food or a snack (mentioned -here and there, and we have provided for those); but what if they try a -meat pie? Or scrambled eggs? There’s just so much decoration one can -reasonably insert in a game, and loading the dictionary with Benny’s -full menu would be overdoing it a bit. +.. Generated by autoindex +.. index:: + pair: Give; library action -One might reasonably imagine that the ``default`` line at the end of the -``Give`` action in the orders property handles every input not already +One might reasonably imagine that the ``default`` line at the end of the +:act:`Give` action in the orders property handles every input not already specified: .. code-block:: inform @@ -843,7 +870,7 @@ specified: } ], -Not so. The library grammar that deals with ASK BENNY FOR... is this +Not so. The library grammar that deals with ASK BENNY FOR... is this (specifically, the last line): .. code-block:: inform @@ -852,50 +879,45 @@ Not so. The library grammar that deals with ASK BENNY FOR... is this * creature 'about' topic -> Ask * creature 'for' noun -> AskFor -You'll see the :var:`noun` token, which means that whatever the player asks -him for must be a real game object, visible at that moment. Assuming -that the player mentions such an object, the interpreter finds it in the -dictionary and places its internal ID in the :var:`noun` variable, where -our ``switch`` statement can handle it. So, ASK BENNY FOR KEY assigns -the ``toilet_key`` object to the noun variable, and Benny responds. ASK -BENNY FOR CUSTOMERS also works; the ``default`` case picks that one up. -But, ASK BENNY FOR SPAGHETTI BOLOGNESE won't work: we have no object for -Spaghetti Bolognese (or any other delicacy from Benny's kitchen) -- the -words ``'spaghetti'`` and ``'bolognese'`` simply aren't in the -dictionary. This is perhaps not a major deficiency in our game, but it -takes very little to allow Benny to use his default line for *any* -undefined input from the player. We need to extend the existing ASK -grammar: +You'll see the :var:`noun` token, which means that whatever the player asks +him for must be a real game object, visible at that moment. Assuming that +the player mentions such an object, the interpreter finds it in the +dictionary and places its internal ID in the :var:`noun` variable, where +our ``switch`` statement can handle it. So, ASK BENNY FOR KEY assigns the +``toilet_key`` object to the noun variable, and Benny responds. ASK BENNY +FOR CUSTOMERS also works; the ``default`` case picks that one up. But, ASK +BENNY FOR SPAGHETTI BOLOGNESE won't work: we have no object for Spaghetti +Bolognese (or any other delicacy from Benny's kitchen) -- the words +``'spaghetti'`` and ``'bolognese'`` simply aren't in the dictionary. This +is perhaps not a major deficiency in our game, but it takes very little to +allow Benny to use his default line for *any* undefined input from the +player. We need to extend the existing ASK grammar: .. code-block:: inform Extend 'ask' * creature 'for' topic -> AskFor; -This line will be added to the end of the existing grammar for Ask, so -it doesn’t override the conventional noun-matching line. ``topic`` is a -token that roughly means “any input at all”; the value of noun isn't -important, because it'll be handled by the default case. Now players may -ask Benny for a tuna sandwich or a good time; they'll get: "I don’t -think that’s on the menu, sir", which makes Benny a barman with -attitude. - -And that's it; on the slightly surreal note of ASK BENNY FOR A GOOD TIME -we've taken "Captain Fate" as far as we intend to. The guide is nearly -done. All that's left is to recap some of the more important issues, -talk a little more about compilation and debugging, and send you off -into the big wide world of IF authorship. +This line will be added to the end of the existing grammar for Ask, so it +doesn’t override the conventional noun-matching line. ``topic`` is a token +that roughly means "any input at all"; the value of noun isn't important, +because it'll be handled by the default case. Now players may ask Benny +for a tuna sandwich or a good time; they'll get: "I don’t think that’s on +the menu, sir", which makes Benny a barman with attitude. +And that's it; on the slightly surreal note of ASK BENNY FOR A GOOD TIME +we've taken "Captain Fate" as far as we intend to. The guide is nearly +done. All that's left is to recap some of the more important issues, talk +a little more about compilation and debugging, and send you off into the +big wide world of IF authorship. .. rubric:: Footnotes .. [#dark] - - We're alluding here to the Classical concept of mimesis. In an - oft-quoted essay from 1996, Roger Giner-Sorolla wrote: "I see - successful fiction as an imitation or 'mimesis' of reality, be it - this world's or an alternate world's. Well-written fiction leads the - reader to temporarily enter and believe in the reality of that world. - A crime against mimesis is any aspect of an IF game that breaks the - coherence of its fictional world as a representation of reality." - + We're alluding here to the Classical concept of mimesis. In an + oft-quoted essay from 1996, Roger Giner-Sorolla wrote: "I see successful + fiction as an imitation or 'mimesis' of reality, be it this world's or + an alternate world's. Well-written fiction leads the reader to + temporarily enter and believe in the reality of that world. A crime + against mimesis is any aspect of an IF game that breaks the coherence of + its fictional world as a representation of reality." diff --git a/chapters/14.rst b/chapters/14.rst index 9a62a74..396261f 100644 --- a/chapters/14.rst +++ b/chapters/14.rst @@ -306,6 +306,11 @@ two-state variables are attributes. Properties ---------- +.. Generated by autoindex +.. index:: + pair: before; library property + pair: name; library property + The library defines around forty-eight standard property variables (such as :prop:`before` or :prop:`name`), but you can readily create further ones just by using them within an object definition. @@ -408,6 +413,10 @@ or implicitly when the routine runs out of statements. If none of these [ routine_id; statement; statement; ... statement; ]; +.. Generated by autoindex +.. index:: + pair: true; library constant + returns :const:`true` and :: property [; statement; statement; ... statement; ] @@ -663,6 +672,11 @@ you'll come across. "number" property and "general" attribute ----------------------------------------- +.. Generated by autoindex +.. index:: + pair: general; library attribute + pair: number; library property + The library defines a standard :prop:`number` property and a standard :attr:`general` attribute, whose roles are undefined: they are general-purpose variables available within every object to designers as @@ -705,6 +719,10 @@ is virtually unlimited. Setting up the object tree -------------------------- +.. Generated by autoindex +.. index:: + pair: found_in; library property + Throughout this guide, we've defined the initial position of each object within the overall object tree either by explicitly mentioning its parent's ``obj_id`` (if any) in the first line of the object definition diff --git a/chapters/15.rst b/chapters/15.rst index 491720a..c9dea8f 100644 --- a/chapters/15.rst +++ b/chapters/15.rst @@ -122,6 +122,10 @@ punctuation is missing:: > Object door Compiled with 1 error (no output) +.. Generated by autoindex +.. index:: + pair: door; library attribute + In fact, there's nothing wrong with the ordering or punctuation. The problem is actually that we've tried to define a new object with an internal ID of :attr:`door` -- reasonably enough, you might think, since the @@ -190,6 +194,10 @@ but some features should be in all environments. To obtain precise information about any particular version, run the compiler with the :option:`-h1` switch -- see :ref:`switches`. +.. Generated by autoindex +.. index:: + single: Strict mode + Often the compiler is run with the name of your source file as its only parameter. This tells the compiler to "read this file using Strict mode and from it generate a Version 5 story file of the same name". The source file @@ -319,6 +327,10 @@ and ``-X``. Some of the more useful switches are: .. option:: -v5 .. option:: -v8 + .. Generated by autoindex + .. index:: + single: Infocom + Compile to this version of story file. Versions 5 (on by default) and 8 are the only ones you should ever care about; they produce, respectively, story files with the extensions .z5 and .z8. Version 5 diff --git a/chapters/16.rst b/chapters/16.rst index 702a594..9b02717 100644 --- a/chapters/16.rst +++ b/chapters/16.rst @@ -268,6 +268,11 @@ GOTO *number* PURLOIN *object* + .. Generated by autoindex + .. index:: + pair: scenery; library attribute + pair: static; library attribute + PURLOIN works exactly as TAKE , with the nice addition that it doesn't matter where the object is: in another room, inside a locked container, in the claws of the bloodthirsty dragon. More dangerously, @@ -281,6 +286,12 @@ PURLOIN *object* ABSTRACT *object* TO *object* + .. Generated by autoindex + .. index:: + pair: animate; library attribute + pair: container; library attribute + pair: supporter; library attribute + This verb enables you to move the first *object* to the second *object*. As with PURLOIN , both objects can be anywhere in the game. Bear in mind that the second object should logically be a @@ -427,6 +438,10 @@ Of course, the code we've offered you in *this* edition takes care of those embarrassing issues, but it might very well happen that a few more undetected absurdities pop up from now on. +.. Generated by autoindex +.. index:: + single: RAIF + The final stage of debugging must happen elsewhere, at the hands of some wilful, headstrong and determined beta-testers; these are the people who, if you’re lucky, will methodically tear your game to shreds and diff --git a/tools/Makefile b/tools/Makefile new file mode 100644 index 0000000..bba017c --- /dev/null +++ b/tools/Makefile @@ -0,0 +1,24 @@ +# Makefile for IBG tools. + +PYTHON = python +AUTOINDEX = $(PYTHON) autoindex.py + +ROOT = .. +CHAP = $(ROOT)/chapters +APPX = $(ROOT)/appendices + +FILES = $(ROOT)/about.rst $(CHAP)/01.rst $(CHAP)/02.rst $(CHAP)/03.rst \ +$(CHAP)/04.rst $(CHAP)/05.rst $(CHAP)/06.rst $(CHAP)/07.rst $(CHAP)/08.rst \ +$(CHAP)/09.rst $(CHAP)/10.rst $(CHAP)/11.rst $(CHAP)/12.rst $(CHAP)/13.rst \ +$(CHAP)/14.rst $(CHAP)/15.rst $(CHAP)/16.rst $(APPX)/c.rst $(APPX)/e.rst + +all: index + +index: $(FILES) + @ for file in $(FILES); do \ + echo autoindexing $$file; \ + $(AUTOINDEX) $$file; \ + done + +test: autoindex-test.rst + $(AUTOINDEX) $< - diff --git a/tools/autoindex.cfg b/tools/autoindex.cfg new file mode 100644 index 0000000..3c020d0 --- /dev/null +++ b/tools/autoindex.cfg @@ -0,0 +1,29 @@ +# Autoindex config file, in -*- conf-unix -*- mode. + +[DEFAULT] +noindex = + list-table:: + code-block:: + :: *$ + +[keywords] +DOSI +IBM PC +IF Archive +IF Competition +Infocom +NotePad = text editor +NPC +RAIF +RGIF +Strict mode +TADS + +[rolemap] +obj = library object +const = library constant +var = library variable +prop = library property +attr = library attribute +act = library action +func = library routine diff --git a/tools/autoindex.py b/tools/autoindex.py new file mode 100644 index 0000000..b9d8982 --- /dev/null +++ b/tools/autoindex.py @@ -0,0 +1,216 @@ +""" +Add Sphinx index entries to RST source. + +TODO: scan directory tree, look for *.rst +TODO: add option to remove entries +""" + +import os +import re +import sys + +try: + from ConfigParser import ConfigParser +except ImportError: + from configparser import ConfigParser + + +# Configuration defaults. +defaults = {'comment': ".. Generated by autoindex", + 'mintext': '5000', + 'noindex': ''} + +# Read config settings. +config = ConfigParser(defaults, allow_no_value=True) +config.optionxform = str + +thisdir = os.path.dirname(__file__) +conffile = os.path.join(thisdir, "autoindex.cfg") +config.read(conffile) + +# Extract keywords and role mappings. +def getmap(section): + mapping = {} + + if config.has_section(section): + for name in config.options(section): + if name not in defaults: + mapping[name] = config.get(section, name) + + return mapping + +keywords = getmap('keywords') +rolemap = getmap('rolemap') + +# Autoindex comment. +comment = config.get('DEFAULT', 'comment') + +# Minimum amount of text twixt identical entries. +mintext = config.getint('DEFAULT', 'mintext') + +# Don't add index entries after paragraphs matching this. +noindex = config.get('DEFAULT', 'noindex').strip().split("\n") + +if noindex: + noindex_patterns = "(%s)" % "|".join(noindex) +else: + noindex_patterns = None + +# Paragraph separator. +separator = "\n\n" + + +def main(args): + # Parse command args. + if len(args) == 2: + infile = args[1] + outfile = None + elif len(args) == 3: + infile, outfile = args[1:] + else: + sys.exit("Usage: %s INFILE [OUTFILE]" % args[0]) + + ##dump_paragraphs(infile) + + # Do indexing. + autoindex_file(infile, outfile) + + +def autoindex_file(infile, outfile=None): + "Add index entries to a file." + + # Get original text. + with open(infile) as fp: + text = fp.read() + + # Index it. + itext = autoindex_text(text) + + # Write output (but don't modify original if nothing changed). + if outfile or itext != text: + if outfile == '-': + sys.stdout.write(itext) + else: + with open(outfile or infile, "wb") as fp: + fp.write(itext) + + +def autoindex_text(text): + "Add index entries to the given text." + return separator.join(indexed_paragraphs(text)) + + +def indexed_paragraphs(text): + "Yield indexed paragraphs from the specified text." + + # Current text position. + textpos = 0 + + # Text position of last entries for each index word (to avoid too many + # close together for the same entry). + lastpos = {} + + def addindex(index, name, desc=None): + if name not in lastpos or lastpos[name] + mintext < textpos: + index.append((name, desc, textpos)) + lastpos[name] = textpos + + # Whether to add index entries. + noindex = False + + for info in paragraph_info(text): + # Update text count. + para = info['text'] + textpos += len(para) + + # Initialise index (list of [name, desc, textpos]). + index = [] + + # Find index entries for roles. + for match in re.finditer(r':(.+?):`(.+?)`', para): + role, name = match.groups() + if role in rolemap: + addindex(index, name, rolemap[role]) + + # Find index entries for keywords. + paraline = para.replace("\n", " ") + for word, desc in keywords.items(): + if re.search(r'\b' + word + r'\b', paraline): + addindex(index, word, desc) + + # Yield index paragraph if required. + if index and not noindex: + indent = info['indent'] + lines = [indent + comment] + lines.append(indent + ".. index::") + + for name, desc, pos in sorted(index): + msg = "autoindex: " + name + + if desc: + text = " pair: %s; %s" % (name, desc) + msg += " (" + desc + ")" + else: + text = " single: %s" % name + + lines.append(indent + text) + sys.stderr.write("%s [%s]\n" % (msg, pos)) + + yield "\n".join(lines) + + noindex = info['noindex'] + + # Yield paragraph. + yield para + + +def unindexed_paragraphs(text): + "Yield paragraphs stripped of autoindex comments." + + for para in text.split(separator): + if comment not in para: + yield para + + +def paragraph_info(text): + "Yield paragraph information from text." + + noindex = False + noindex_level = None + + for para in unindexed_paragraphs(text): + indent = re.match(r' *', para).group() + level = len(indent) + + # Detect first entry in a list. Should be at same indent level as + # its text. + match = re.match(r'\* ', para.lstrip()) + if match: + level += len(match.group()) + + if noindex_patterns: + if not noindex and re.search(noindex_patterns, para, re.M): + noindex_level = level + noindex = True + elif noindex_level is not None and level <= noindex_level: + noindex_level = None + noindex = False + + yield {'text': para, + 'noindex': noindex, + 'indent': indent, + 'level': level} + + +def dump_paragraphs(infile): + print noindex_patterns + + with open(infile) as fp: + text = fp.read() + + for info in paragraph_info(text): + print info['level'], info['noindex'], info['text'].replace("\n", " ") + + +if __name__ == "__main__": + main(sys.argv) -- 2.31.1