Adv3Lite -- .travelVia() issue with doors

I don’t know if this is an issue with the door code, the .travelVia() method or my understanding of how this should work.

If an NPC moves between rooms (via doors) and the PC closes the door before the NPC travels through it, the NPC gets blocked. This is working code followed by the script it generated to show the issue:

[code]inside : Room ‘The Inside Room’
"The inside room. "
roomBeforeAction() { if (gActionIs(Yell)) { npcAgenda.isReady = true; } }
out = insideDoor
;

  • insideDoor : Door ‘inside door’
    "Inside door. "
    isOpen = true
    otherSide = outsideDoor
    ;

outside : Room ‘The Outside Room’
"The outside room. "
in = outsideDoor
;

  • outsideDoor : Door ‘outside door’
    "Outside door. "
    isOpen = true
    otherSide = insideDoor
    ;

npc : Actor ‘npc’ @inside
“An NPC.”
;

  • npcAgenda : AgendaItem
    isReady = nil
    initiallyActive = true
    count = 0
    invokeItem() {
    switch (count++) {
    case 0:
    break;
    case 1:
    npc.travelVia(insideDoor);
    break;
    case 2:
    break;
    case 3:
    npc.travelVia(outsideDoor);
    break;
    }
    }
    ;
    [/code]
    This is the resulting output file:

[code]The Inside Room
The inside room.

The npc was there.

out
The Outside Room
The outside room.

in
The Inside Room
The inside room.

The npc was there.

close door
Done.

out
(first opening the inside door)
The Outside Room
The outside room.

close door
Done.

in
(first opening the outside door)

The Inside Room
The inside room.

The npc was there.

yell
That was not really in my nature.

z
Time passed.

The npc left through the inside door.

z
Time passed.

z
Time passed.

The npc arrived in the area. *** Works ***

undo
One turn undone: z

undo
One turn undone: z

undo
One turn undone: z

undo
One turn undone: yell

close door
Done.

yell
That was not really in my nature.

z
Time passed.

The npc left through the inside door. (first opening the inside door)

z
Time passed.

z
Time passed.

The npc arrived in the area. *** Works ***

undo
One turn undone: z

undo
One turn undone: z

undo
One turn undone: z

undo
One turn undone: yell

yell
That was not really in my nature.

z
Time passed.

The npc left through the inside door. (first opening the inside door)

close door
Done.

z
Time passed.

The outside door was in the way. *** Does not work ***

z
Time passed. >[/code]
So if I manually close the inside door (which should close the outside door since they’re connected), the NPC gets blocked.

Am I missing something?

I get the same results you do. Actually, there are two problems here. Not only should the npc be able to implicitly open the door, but in the event that that doesn’t happen, no result should be printed, since the npc is not in scope.

Looking at the Door class, I don’t see any obvious reason why the outsideDoor should be preventing npc travel when it’s closed. The pc can implicitly open the outsideDoor, I checked that.

Dunno.

Hi Jim,

I tried to figure it out and I have a guess, but with limited experience in the library, it’s just a guess.

When checkTravelBarriers() is called to see if the NPC can travel through the outsideDoor, the method tryImplicitAction(open, outsideDoor) is called because the door is !isOpen.

Eventually, the action.resolvedOjectsInScope() method then determines that the outsideDoor is not in scope.

I believe the outsideDoor is checked to see if it’s in scope to the PC and not the traveler (NPC). I’m basing this on buildScopeList using gActor (me) instead of the NPC actor since scopeList only returns the items in the insideRoom.

Unfortunately, I’m not experienced enough to propose a solution, assuming of course, this is the issue.

I’ll shoot an email to Eric to let him know this thread exists. Hopefully if he has some time in the upcoming weeks, he can take a peek.

Thanks for helping verify it wasn’t something I was doing in my code, Jim.

– Mike

I would hazard a guess that you’re right. I’m not good at using the Debug commands in Workbench, but I traced it through as well as I could starting from my last saved run-through and putting a breakpoint at

scopeList = Q.scopeList(gActor).toList();

and the Watch Expressions window shows that gActor remains the me object throughout.

Here’s the weird thing, though. In looking through “Learning T3Lite,” it looks to me like your travelVia calls are backwards. It looks like the correct syntax would be

insideDoor.travelVia(npc);

However, changing that doesn’t help! The npc is still blocked by the closed door. As I said – weird.

Jim, you’re right in that the syntax for travelVia can be used that way, but it can also be used against Actor. It’s described in “Learning T3Lite” in Chapter 14.12 NPC Travel. The benefit (for what I’m trying to do) is that by using it against the Actor:

actor.travelVia(connector);

the SayDeparting() & SayArriving methods are automatically called vs. having to call those methods manually if they were needed.

But to your point, either way, the issue remains.

– Mike

Thanks for tracking down where the problem lies. Your analysis appears to be correct.

I’m not 100% certain of the best permanent solution, but as a first step try adding the following to your code:

[code]

tryImplicitActorAction(actor, action, [objs])
{
local res = nil;
local oldActor = gActor;

try
{
    gActor = actor;
    res = tryImplicitAction(action, objs...);
}

finally
{
    gActor = oldActor;
}

return res;

}

modify Door
/*
* The most likely barrier to travel through a door is that the door is
* closed and locked, so we check for than after the other kinds of travel
* barrier.
/
checkTravelBarriers(traveler)
{
/

* Carry out the inherited checking of travel barriers and return nil
* if they fail to indicate that travel through the door is not
* possible.
*/
if(inherited TravelConnector(traveler) == nil)
return nil;

    /*  If the Door isn't open, try to open it via an implicit action. */
    if(!isOpen)
    {
        gMessageParams(traveler);
        local obj = self;
        gMessageParams(obj);
            
        
        if(tryImplicitActorAction(traveler, Open, self))
        {
            if(gPlayerChar.isOrIsIn(traveler))
           "<<gAction.buildImplicitActionAnnouncement(true)>>";
            else if(gPlayerChar.canSee(traveler))
                DMsg(npc opens door, '{The subj traveler} open{s/ed} {the
                    obj}. ');
            else if(otherSide && gPlayerChar.canSee(otherSide))
            {
                obj = otherSide;
                gMessageParams(obj);
                DMsg(door opens, '{The subj obj} open{s/ed}. ');
            }
                
        
        }    
        /* 
         *   If we're not allowed to open this door via an implicit action
         *   (because opening it is marked as dangerous or nonObvious at the
         *   verify stage) display a message explaining why the travel can't
         *   be carried out, provided the player char can see the traveler.
         */
        
        else if(gPlayerChar.canSee(traveler))            
        {
            local obj = self;
            gMessageParams(obj);
            
            
            say(cannotGoThroughClosedDoorMsg);
        }
    }
    
    /* 
     *   We pass the travel barrier test if and only if the door ends up
     *   open.
     */
    return isOpen;
}

;[/code]

If that works as you’d expect without causing any unwanted side-effects then I can modify the library accordingly.

Thanks for the quick turnaround, Eric!

The code you supplied above does work, but there is a side-effect that results in odd phrasing.

The prior version would display “(first opening the inside door)” but the new code doesn’t make it an implied action.

Consider the following two attempts – both behave the same way but I wanted to test both arriving & departing.

Closing the door, but staying in the arriving (inside) room.[code]The Inside Room
The inside room.

The npc is here.

yell
That is not really in my nature.

z
Time passes.

The npc leaves through the inside door. The npc opens the inside door. *** Odd phrasing

close door
Done.

z
Time passes.

The inside door opens. The npc arrives in the area.[/code]

And closing the door from the departing room:[code]The Inside Room
The inside room.

The npc is here.

yell
That is not really in my nature.

out
(first opening the inside door)

The Outside Room
The outside room.

The npc arrives in the area.

close door
Done.

z
Time passes.

The npc leaves through the outside door. The npc opens the outside door. *** Odd phrasing[/code]

I can probably address this through the SayArriving() & SayDeparting methods, but perhaps the default messaging could be tweaked to be a bit smoother.

Thanks again for your help!

– Mike

That’s because I thought it would look odd for the npc’s action to be reported as an implicit action.

The main problem can perhaps be addressed by slightly altering the order of events thus:

modify Actor
    travelVia(conn, announceArrival = true)
    {
        local wasSeenLeaving = nil;
        local oldLoc = location;
        
        if(Q.canSee(gPlayerChar, self))
        {           
            /* Note that we were seen leaving. */
            wasSeenLeaving = true;
        }
         /* Move this actor via conn. */
        conn.travelVia(self);
        
        /* 
         *   If the player character can see this actor, display a message
         *   indicating this player's departure.
         */
        
         if(wasSeenLeaving)
            sayDeparting(conn);
        
        if(announceArrival && !wasSeenLeaving && Q.canSee(gPlayerChar, self))
            sayArriving(oldLoc);
    }
;

Is that better?

I think this works great now!

(result are:)

[code]The Inside Room
The inside room.

The npc is here.

yell
That is not really in my nature.

z
Time passes.

The npc opens the inside door. The npc leaves through the inside door.

close door
Done.

z
Time passes.

The inside door opens. The npc arrives in the area.

yell
That is not really in my nature.

out
The Outside Room
The outside room.

The npc arrives in the area.

close door
Done.

z
Time passes.

The npc opens the outside door. The npc leaves through the outside door.[/code]
Thanks for the revision, Eric.

– Mike

There is also the Tads 3 pathfind.t include file that could handle pathfinding from point A to point B. It’s been awhile since I used it though but last time I did it found all the doors easily enough and the NPC traversed them just fine.

Whether an adv3 file would work with adv3Lite … you’d need to test it, but I wouldn’t expect it to work.

In any case adv3Lite has its own pathfinder.