Answers to the questions follow; don't read on if you're interested in
solving them for yourself...
Spoiler warnings
> 1. You have an object that appears in several places (using the
> `found_in' property), but which is `absent' to start with. Your
> code executes `give object ~absent', but the object fails to
> appear. Why not?
Objects that appear in multiple places get moved around by the
`MoveFloatingObjects' routine in VerbLib.h, which in the normal course
of events only gets called when the player enters a new room. So you
should call `MoveFloatingObjects' right away.
Note: it isn't always safe to use `move object to location', because (i)
`location' might be `thedark'; (ii) `location' might not be one of the
object's permissible locations.
> 2. Can you write a room so that going north wins the game, without
> producing a spurious room description before the `you have won'
> message? (Remember that going north might occur because of a
> command like `push car north').
Write a `n_to' routine that sets `deadflag = 2' and then returns 1.
Note: if you tried to implement this using `GamePreRoutine' or `before',
did you remember to trap all three of <Go n_obj>, <Enter n_obj> and
<PushDir X n_obj>? What if there was a door to the north and going
through the door was the winning move?
> 3. Can you write a vehicle in which movement produces a failure
> message of your own choosing, with no spurious text after it (for
> example, a one-way car in which `go north' produces `The car will
> only go south.').
This one is tricky, because the vehicle's `before' routine can only
return `0' (in which case the library prints the `you can't go that way'
message) or `1' (in which case movement takes place). My best guess is
to use the `LibraryMessages' system to trap the `you can't go that way'
message and not to print it if a particular flag is set. Then have the
vehicle print its own failure message and set the flag.
> 4. Can you redefine the `ask ... about ...' verb so that it parses the
> question you are asking as an object in a particular scope? Make
> sure that the player can't discover which words refer to subjects
> in the game (that is, `ask person about green aardvark' should
> produce `No reply' or something similar, regardless of whether or
> not `green aardvark' is a genuine subject of conversation).
I wrote a long article about this a while ago; look for the message-id
<GDR11.95Apr26121803@stint.cl.cam.ac.uk>, posted on 26 April 1995 (it'll
be in the archive at <ftp://ftp.gmd.de/if-archive/rec.arts.int-fiction/>.
> 5. How can you arrange that the command `person, yes' has the same
> effect as `say yes to person' (recall that `yes' is a verb)?
Because `yes' is a verb, the command `person, yes' gets turned into an
`Order' action (which doesn't go through the usual `before' routines, so
you can't modify its behaviour there). I think something like this:
Property doesnt_obey 0;
Class TalkableClass
has animate
with life [ b;
Order:
if (action == ##Yes) b = 'yes';
if (action == ##No) b = 'no';
if (b ~= 0) {
special_word = b; <<Ask self b>>;
}
if (self.doesnt_obey ~= 0) {
PrintOrRun(self,doesnt_obey); rtrue;
}
];
The section of a creature's `Life' routine that processes the `Order'
action should drop off the end if it fails to understand it; then the
routine above takes over. The `doesnt_obey' property allows creatures
to continue to have customised default responses to being ordered
around.
> 6. What pitfalls are there in having an object called `Door to the
> kitchen'? How would you get round them (apart from renaming it
> `Kitchen door')?
Assuming you allow the words `door', `to' and `kicthen' to refer to the
object. The player will get a strange response from the parser when
s/he types the (admittedly nonsensical command) `show door to kitchen to
person'. The parser thinks `door to kitchen to' is one object.
The best solution is to rename; failing that, write a `parse_name'
routine that fails to parse a string of words ending in `to'.
> 7. What bugs are there in the 5/10 beta version of `AllowPushDir' (in
> VerbLib.h)? How would you fix them?
Here it is:
[ AllowPushDir i;
if (parent(second)~=compass) return L__M(##PushDir,2);
if (second==u_obj or d_obj) return L__M(##PushDir,3);
AfterRoutines();
i=noun; <Go second>; move i to location;
];
It's that `move i to location' at the end. What if the player pushes
something into darkness? To make it work, replace `location' by
`real_location'.
> 8. Two rooms have a glass window separating them. How would you
> implement this?
(1) make the window `found_in' both locations.
(2) give the window an `add_to_scope' property that puts all the objects
from the other location into scope.
(3) give both rooms a `before' routine that traps any action other than
`Examine' when applied to an object that's behind the window.
> 9. An NPC in the game is `transparent' because he has possessions that
> the player needs to refer to, but when you give him the gun, he
> hides it in an inside pocket. Any ideas how to solve this one?
> Does your solution produce the correct response when the player
> types `objects'?
The obvious thing to do is simply to `remove gun' and then restore it
when it appears again. However, if you do this, the gun will be
described as `(lost)' instead of `(given away)' in the output from the
`objects' command. So instead, create a dummy limbo location with the
`animate' attribute, and move the gun there.
> 10. A thief steals everything the player is carrying, but not anything
> that player is wearing. How would you code this up? What if one
> of the objects is a toffee apple that sticks to the player's hands?
This seems obvious; after all, there's a `worn' attribute, isn't there?
But recall that you can't move objects around while looping over them
without risking crashing the z-machine or generally making a mess of the
object tree. Here's one solution:
[ DumpCarries dest i j;
do {
j = 0;
objectloop(i in player)
if (i hasnt worn)
j = i;
if (j ~= 0) move j to dest;
} until (j == 0);
];
If you do have sticky objects in your game and you don't want the thief
to be able to steal them, then maybe some trick like this would work:
if (j ~= 0) {
keep_silent = 1; <Drop j>; keep_silent = 0;
if (j in parent(player))
move j to dest;
}
(But you'd have to add code to the sticky objects so that they wouldn't
print their failure message when the thief tried to steal them, so maybe
it would be best to code up the stickiness directly by replacing `if (i
hasnt worn)' with `if (i hasnt worn && i ~= ToffeeApple or PotOGlue)'.)
-- Gareth Rees