X-Git-Url: https://jxself.org/git/?p=ibg.git;a=blobdiff_plain;f=chapters%2F09.rst;fp=chapters%2F09.rst;h=83d9f8dcafe0c7be367d17b96be62de9c5bc562b;hp=24b04dc7e5d6ccfccc5e698d88731f3eae33034a;hb=fb8b7c14f10733e913e2b87f9a82e5b44c0dc7be;hpb=54830106a3ef48c411e0346f54bfb56f3072b8a2 diff --git a/chapters/09.rst b/chapters/09.rst index 24b04dc..83d9f8d 100644 --- a/chapters/09.rst +++ b/chapters/09.rst @@ -53,24 +53,27 @@ has only one feature of interest:: ], has scenery; -The tree's ``before`` property is intercepting a ``FireAt`` action, which +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 -``second`` object is a feasible weapon. To deal with silly commands like -SHOOT AT TREE WITH HELGA, we must test that ``second`` is the bow, one of +: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. +.. index:: + single: arguments (of a routine) + .. _working-with-routines: A diversion: working with routines ================================== A standalone routine, like the familiar routines embedded as the value of a -property such as ``before`` or ``each_turn``, is simply a set of statements +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 the default return value: @@ -86,8 +89,8 @@ the default return value: * If an embedded routine executes all of its statements and reaches the final ``];`` without encountering some form of ``return`` statement, it - returns the value ``false``. In the same circumstances, a standalone - routine returns the value ``true``. There's a good reason for this + returns the value :const:`false`. In the same circumstances, a standalone + routine returns the value :const:`true`. There's a good reason for this difference -- it usually turns out to be the natural default behaviour -- but it can sometimes baffle newcomers. To avoid confusion, we've always included explicit ``return`` statements in our routines. @@ -110,7 +113,7 @@ look at some simple examples; first consider these unexciting foodstuffs:: description "It doesn't look at all appetising.", ... -The ``description``\s are identical: perhaps we could display them using a +The :prop:`description`\s are identical: perhaps we could display them using a routine? :: [ Inedible; print_ret "It doesn't look at all appetising."; ]; @@ -133,7 +136,7 @@ wherever we need to. Here's another simple example showing how, by returning a value, a routine can report back to the piece of code which called it. We've once or twice used the test ``if (self has visited) ...``; we could create a routine -which performs that same check and then returns ``true`` or ``false`` to +which performs that same check and then returns :const:`true` or :const:`false` to indicate what it discovered:: [ BeenHereBefore; @@ -153,7 +156,7 @@ we *could* write another routine to perform that check:: ]; However, the two routines are very similar; the only difference is the name -of the variable -- ``self`` or ``location`` -- which is being checked. A +of the variable -- :var:`self` or :var:`location` -- which is being checked. A better approach might be to rework our ``BeenHereBefore`` routine so that it does both jobs, but we somehow need to tell it which variable's value is to be checked. That's easy: we design the routine so that it expects an @@ -167,17 +170,17 @@ to be checked. That's easy: we design the routine so that it expects an Notice that the argument's name is one that we've invented to be descriptive of its content; it doesn't matter if we define it as "``x``", "``this_room``" or "``hubba_hubba``". Whatever its name, the argument acts -as a placeholder for a value (here, one of the variables ``self`` or -``location``) which we must supply when calling the routine:: +as a placeholder for a value (here, one of the variables :var:`self` or +:var:`location`) which we must supply when calling the routine:: if (BeenToBefore(self) == true) ... if (BeenToBefore(location) == true) ... -In the first line, we supply ``self`` as the routine's argument. The +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 -``visited`` attribute. On the second line we supply ``location`` as the +:attr:`visited` attribute. On the second line we supply :var:`location` as the argument, but the routine just sees another value in its ``this_room`` variable. ``this_room`` is called a :term:`local variable` of the ``BeenToBefore`` routine, one that must be set to a suitable value each @@ -192,7 +195,7 @@ Remember that: write a ``return``, ``rtrue`` or ``rfalse`` statement, or because execution reaches the ``]`` marking the routine's end. -#. All routines return a value, which can be ``true``, or ``false``, or any +#. All routines return a value, which can be :const:`true`, or :const:`false`, or any other number. This value is determined by the ``return``, ``rtrue`` or ``rfalse`` statement, or by the the ``]`` marking the routine's end (in which case the default STEF rule applies: Standalone routines return @@ -211,7 +214,7 @@ Remember that: 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 chapter contains a ``print_ret`` statement and so always returns - ``true``, but we didn't take any notice; the sole purpose of the routine + :const:`true`, but we didn't take any notice; the sole purpose of the routine was to display some text. Compare this with the ``BeenToBefore`` routine, which does nothing *except* return a value; if we'd ignored that, then calling the routine would have been a waste of time. @@ -273,12 +276,12 @@ The result is that we ask three questions: Is ``o`` the ``bow`` object? which is a member of the ``Arrow`` class? What this means is that the value returned by the call ``BowOrArrow(bow)`` -is ``true``, while the value returned by the call ``BowOrArrow(tree)`` is -``false``. Or, more generally, the value returned by the call -``BowOrArrow(second)`` will be either ``true`` or ``false``, depending on +is :const:`true`, while the value returned by the call ``BowOrArrow(tree)`` is +:const:`false`. Or, more generally, the value returned by the call +``BowOrArrow(second)`` will be either :const:`true` or :const:`false`, depending on the characteristics of the object defined by the value of the variable -``second``. So, we can write this set of statements in an object's -``before`` property:: +:var:`second`. So, we can write this set of statements in an object's +:prop:`before` property:: if (BowOrArrow(second) == true) { This object deals with having an arrow fired at it @@ -287,13 +290,13 @@ the characteristics of the object defined by the value of the variable and the effect is either -* ``second`` is a weapon: ``BowOrArrow`` displays nothing and returns a - value of ``true``, the ``if`` statement reacts to that value and executes +* :var:`second` is a weapon: ``BowOrArrow`` displays nothing and returns a + value of :const:`true`, the ``if`` statement reacts to that value and executes the following statements to produce an appropriate response to the fast-approaching arrow; or -* ``second`` isn't a weapon: ``BowOrArrow`` displays a standard "don't be - silly" message and returns a value of ``false``, the ``if`` statement +* :var:`second` isn't a weapon: ``BowOrArrow`` displays a standard "don't be + silly" message and returns a value of :const:`false`, the ``if`` statement reacts to that value and ignores the following statements. Then * in both cases, the ``return true`` statement terminates the object's @@ -301,7 +304,7 @@ and the effect is either 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 ``deadflag`` to 3 +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 @@ -346,7 +349,7 @@ There's nothing in Gessler's definition that we haven't already encountered:: ], has male; -Like most NPCs, Gessler has a ``life`` property which deals with actions +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). @@ -400,17 +403,17 @@ Walter:: found_in [; return true; ], has male proper scenery transparent; -His attributes are ``male`` (he's your son, after all), ``proper`` (so the -interpreter doesn't mention "the your son"), ``scenery`` (so he's not -listed in every room description), and ``transparent`` (because you see -right through him). No, that's wrong: a ``transparent`` object isn't made +His attributes are :attr:`male` (he's your son, after all), :attr:`proper` (so the +interpreter doesn't mention "the your son"), :attr:`scenery` (so he's not +listed in every room description), and :attr:`transparent` (because you see +right through him). No, that's wrong: a :attr:`transparent` object isn't made of glass; it's one whose possessions are visible to you. We've done that because we'd still like to be able to EXAMINE APPLE even when Walter is -carrying it. Without the ``transparent`` attribute, it would be as though +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". -Walter has a ``found_in`` property which automatically moves him to the +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 realistic model world, NPCs often move around independently, but we don't @@ -421,14 +424,14 @@ 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 ``life`` property responds to ``Give`` (as in GIVE APPLE TO +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 ``score``, thus rewarding the player's generous good -nature. His ``before`` property is perhaps a little confusing. It's +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: #. The ``Examine``, ``Listen``, ``Salute`` and ``Talk`` actions are always - available (a ``Talk`` action then gets passed to Walter's ``life`` + available (a ``Talk`` action then gets passed to Walter's :prop:`life` property). #. The ``FireAt`` action is permitted in the ``marketplace``, albeit with @@ -438,8 +441,8 @@ saying: #. 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 ``before`` property responds -to the ``FireAt`` action by setting ``deadflag`` to 2. When that happens, +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. :: Object apple "apple" @@ -510,7 +513,7 @@ prepared to accept:: * noun -> Untie; and we need a routine to handle the action in the default situation (where -the action isn't intercepted by an object's ``before`` property). :: +the action isn't intercepted by an object's :prop:`before` property). :: [ UntieSub; print_ret "You really shouldn't try that."; ]; @@ -522,7 +525,7 @@ The grammar is less complex than it perhaps at first appears: #. The asterisk ``*`` indicates the start of a pattern defining what word(s) might follow the verb. -#. In this example, there's only one pattern: the "``noun``" token +#. In this example, there's only one pattern: the ":var:`noun`" token represents an object which is currently in scope -- in the same room as the player. @@ -566,12 +569,12 @@ 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 ``noun`` in the grammar which implies that the action needs a direct +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 -``before`` property intercepts the ``Untie`` action (and indeed, since this +: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 @@ -582,7 +585,7 @@ The principles presented here are those that you should generally employ: write a generic action handler which either refuses to do anything (see, for example SQUASH or HIT), or performs the action without affecting the state of the model world (see, for example, JUMP or WAVE); then, intercept -that non-action (generally using a ``before`` property) for those objects +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. @@ -601,7 +604,7 @@ the hat on the pole. Here's the default action handler:: You'll notice that this is slightly more intelligent than our ``Untie`` handler, since it produces different responses depending on whether the -object being saluted -- stored in the ``noun`` variable -- is ``animate`` +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' @@ -734,27 +737,27 @@ semicolon in all of the grammar, right at the very end. 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 ``noun`` and ``second`` will contain ``nothing``, so we reject the +both :var:`noun` and :var:`second` will contain ``nothing``, so we reject the attempt with the "at random?" message. Otherwise, we've got at least a -``noun`` value, and possibly a ``second`` value also, so we make our -standard check that ``second`` is something that can be fired, and then +:var:`noun` value, and possibly a :var:`second` value also, so we make our +standard check that :var:`second` is something that can be fired, and then reject the attempt with the "Unthinkable!" message. There are a couple of reasons why you might find this grammar a bit tricky. -The first is that on some lines the word ``noun`` appears twice: you need -to remember that in this context ``noun`` is a parsing token which matches +The first is that on some lines the word :var:`noun` appears twice: you need +to remember that in this context :var:`noun` is a parsing token which matches any single object visible to the player. Thus, the line:: * 'at' noun 'with' noun -> FireAt is matching FIRE AT :samp:`{some_visible_target}` WITH :samp:`{some_visible_weapon}`; perhaps confusingly, the value of the target -object is then stored in variable ``noun``, and the value of the weapon -object in variable ``second``. +object is then stored in variable :var:`noun`, and the value of the weapon +object in variable :var:`second`. The second difficulty may be the final grammar line. Whereas on the -preceding lines, the first ``noun`` matches a target object and the second -``noun``, if present, matches a weapon object, that final line matches FIRE +preceding lines, the first :var:`noun` matches a target object and the second +:var:`noun`, if present, matches a weapon object, that final line matches FIRE :samp:`{some_visible_weapon}` AT :samp:`{some_visible_target}` -- the two objects are mentioned in the wrong sequence. If we did nothing, our ``FireAtSub`` would get pretty confused at this point, but we can swap the @@ -785,9 +788,9 @@ curious), and does three things: #. Deals with TALK TO ME or TALK TO MYSELF. -#. Checks (a) whether the creature being talked to has a ``life`` +#. 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 ``true``. If all + 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... @@ -804,10 +807,10 @@ 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 - ``life`` property. We've decided to use it in exactly the same way + :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 ``true`` - value if the ``life`` property has intercepted the action, ``false`` + 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:: @@ -855,12 +858,12 @@ And Helga's is the most sophisticated (though that isn't saying much):: } ], -This handler uses Helga's ``times_spoken_to`` property -- not a library -property, it's one that we invented, like the ``mid_square.warnings_count`` -and ``pole.has_been_saluted`` properties -- to keep track of what's been -said, permitting two snatches of conversation (and awarding a point) before -falling back on the embarrassing silences implied by "You can't think of -anything to say". +This handler uses Helga's :prop:`times_spoken_to` property -- not a library +property, it's one that we invented, like the +:prop:`mid_square.warnings_count` and :prop:`pole.has_been_saluted` +properties -- to keep track of what's been said, permitting two snatches of +conversation (and awarding a point) before falling back on the embarrassing +silences implied by "You can't think of anything to say". That's the end of our little fable; you'll find a transcript and the full source in :doc:`/appendices/c`. And now, it's time to meet -- Captain