gCommand global seems unpredictable (adv3Lite)

I need to detect when a player is trying to go south in a specific situation where I want to redirect the action. (South from the current position is a container, not a room, so I want to change the Travel command to Enter.)

I have implemented a Doer for this purpose…

Doer 'go dir' execAction(c) { if (gCommand.action.direction == southDir) { if(moonRover.position >= 4) doInstead(Enter, tigerStripeRubbleField); else "You're not close enough to the rubble field to enter it. <.p>"; abort; } } where = [enceladusLandingSite] ;

Except when I enter s (or south) in the game window with a breakpoint in the if statement, I see in the debugger that gCommand.action.direction is downDir, not southDir as expected.

Since this code is in a somewhat large and complex game that is nearly finished, I tried to isolate it in a test bed environment with just the Doer and a couple of rooms.

And to my surprise, gCommand.action.direction generates a nil object reference error. When I look in the debugger, sure enough, there is no direction reference in gCommand.action. The value of gCommand.action is Travel in both cases, but in one (the game implementation) there is a direction reference and not in the other (the test bed).

Under what circumstances does the direction object get added to the gCommand object? I checked the entire gCommand nest in the debugger and can find no reference to any direction.

And, of more immediate concern (since redirection of the command does work otherwise in my game, just not in the correct direction), why is south being changed to down in the game implementation? I can find nothing in my code that would do this (grep for direction through the entire code base produces no smoking gun).

Here’s the test bed code, where gCommand.action has no direction reference…

[code]#charset “us-ascii”

#include <tads.h>
#include “advlite.h”

versionInfo: GameID
IFID = ‘445C38A3-AD1B-4729-957A-F584600DE5C1’
name = ‘test’
byline = ‘by Jerry Ford’
htmlByline = ‘by
Jerry Ford

version = ‘1’
authorEmail = ‘Jerry Ford jerry.o.ford@gmail.com
desc = ‘Testing go.’
htmlDesc = ‘Testing go.’

;

gameMain: GameMainDef
initialPlayerChar = me
paraBrksBtwnSubcontents = nil

;

me: Actor ‘me’ @room
“The main man.<.p>”
isHim = true
person = 2
;

room: Room ‘room’
“In the room. <.p>”

up = roof

// south = otherRoom
south: TravelConnector
{
destination = otherRoom
canTravelerPass(traveler)
{
local ret = nil;
return ret;
}
explainTravelBarrier(traveler)
{
“The door is sealed shut. <.p>”;
}
}
;
otherRoom: Room ‘other room’
“The other room. <.p>”

north = room

;
roof: Room ‘roof’
“The roof.<.p>”
down = room
;
Doer ‘go Dir’
execAction©
{
if(gCommand.action.direction == southDir)
{
“Heading south. <.p>”;
}
else
inherited;
}
;
[/code]

Without actually testing it, my immediate reaction is that you’re trying to test the value of gCommand.action.direction too soon in the action processing cycle. A Doer is executed before the normal action handling that would set the value of action.direction, so what you’re seeing at that point is either nil (since Travel.direction has never been set) or the old value of Travel.direction from a previous command (which is probably where downDir is coming from).

There is another property of gCommand you can test to get the direction entered by the player: gCommand.verbProd.dirMatch.dir. But before you test this you need to check that gCommand.verbProd.dirMatch is not nil, or else you could risk causing a nil object reference run-time error. In the execAction© method of a Doer the c parameter is in any case the current command, so to get at the current direction you could use something like:

Doer 'go dir'
    execAction(c)
    {
        local dirn;

       /* Possibly over cautious if we're matching a Travel action, but it's best to be safe. */
        if(c.verbProd.dirMatch != nil)
           dirn = c.verbProd.dirMatch.dir;

        if (dirn == southDir)
        {
            if(moonRover.position >= 4)
                doInstead(Enter, tigerStripeRubbleField);
            else
                "You're not close enough to the rubble field to enter it. <.p>";

            /* Using abort here would prevent this command from being treated as a turn
                which probably isn't what you want. */
            // abort;
        }
    }
    where = [enceladusLandingSite]
;

Since this does look like a potential trap for the unwary I may look at tweaking it in the next update so that gCommand.action.dir comes closer to doing what you expect.

That said, you are once again making things more complicated than they need be, since you could simply define your Doer here as:

Doer 'go south'
    execAction(c)
    {            
            if(moonRover.position >= 4)
                doInstead(Enter, tigerStripeRubbleField);
            else
                "You're not close enough to the rubble field to enter it. <.p>";           
        
    }
    where = [enceladusLandingSite]
;

See https://dl.dropboxusercontent.com/u/58348218/adv3Lite/docs/manual/doer.htm#doerdir