can I make a Door destination dynamic in adv3Lite?

I have a moon lander docked to a mother ship orbiting Titan. There is a single entry/exit hatch into the lander.

The player character enters the lander by going up through the hatch. Once inside the lander, the down direction returns the PC to the launch dock.

The PC goes up, the lander is launched, lands on Titan, and the PC goes down. Now I want the destination of the hatch to be the surface of Titan.

+ hatchOutOfMoonLander: Door 'hatch'
    "Hatch out of moon lander. <.p>"
    
    otherSide = getDest
    travelDesc = getLanderExitText
    
    getDest()
    {
        local ret = hatchIntoMoonLander;
        if(saturnMoonLander.onTitan)
        {
            ret = hatchFromMoonIntoMoonLander;
        }
        return ret;
    }
    ...
;

This does not work. While I can manipulate the travelDesc text based on the state of the onTitan property of the saturnMoonLander

…the actual destination remains the launch dock inside the mother ship.

I’ve isolated the code from the larger game into a test bed environment and added a magic button that toggles the value of the onTitan property of the moon lander.

Here’s the transcript…

And here’s the 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 <a href="mailto:jerry.o.ford@gmail.com">
                  Jerry Ford</a>'
    version = '1'
    authorEmail = 'Jerry Ford <jerry.o.ford@gmail.com>'
    desc = 'Testing dynamic door destination.'
    htmlDesc = 'Testing dynamic door destination.'

;

gameMain: GameMainDef
    initialPlayerChar = me
    paraBrksBtwnSubcontents = nil
   
;

me: Actor 'me;him' @saturnMoonLander
    ""
    firstName = 'John'
    lastName = 'Doe'
    person = 2
;

saturnMoonLander: Room 'Moon Landing Module' 'landing module;Enceladus Titan
    Saturn moon;lander'
    "The moon lander is a small space with two recliner chairs in front of a
    control panel on which there is a big red button labelled <i>Magic
    Button</i>. \b
    The current location of the lander is <<if(!onTitan)>>docked to the mother
    ship<<else>>on Titan<<end>>. <.p>"
    
    onTitan = nil
    
    down = hatchOutOfMoonLander

;
+ magicButton: Button 'magic button'
    "The magic button toggles the lander's position between <i>on Titan</i> and
    <i>off Titan</i> <.p>"
    
    dobjFor(Push)
    {
        action()
        {
            saturnMoonLander.onTitan = saturnMoonLander.onTitan == nil ? true :
            nil;
            "At the touch of the magic button you are ";
            if(!saturnMoonLander.onTitan)
                "docked to the mother ship";
            else
                "on Titan";
            ". <.p>";
        }
    }
;
+ hatchOutOfMoonLander: Door 'hatch'
    "Hatch out of moon lander. <.p>"
    
    otherSide = getDest
    travelDesc = getLanderExitText
    
    getDest()
    {
        local ret = hatchIntoMoonLander;
        if(saturnMoonLander.onTitan)
        {
            ret = hatchFromMoonIntoMoonLander;
        }
        return ret;
    }
    
    getLanderExitText()
    {
        if(saturnMoonLander.onTitan)
        {
            "The hatch pops open. You drop down to the frozen
            surface of Titan. <.p>";
        }
        else
        {
            "You descend from the lander entry hatch into the launching dock.
            <.p>";
        }
    }
;
titanLandingSite: Room 'Titan Landing Site' 'landing site;Titan'
    "The lander sits on a low rise of icy ground. In the near distance to the
    south, the ice gives way to the dark liquid methane of Kraken Mare.
    <.p>"
    
    up = hatchFromMoonIntoMoonLander
;
+ hatchFromMoonIntoMoonLander: Door 'hatch into lander'
    "Hatch into lander. <.p>"
    
    otherSide = hatchOutOfMoonLander
    travelDesc = "After knocking your boots against the lander strut in an effort to
        dislodge whatever remains of Kraken Mare sludge clinging to them, you climb the 
        ladder and cycle through the air lock.<.p>"
;
landingModuleLaunchDock: Room 'Landing Module Launch Dock' 'landing module
    launch dock'
    "The landing-module launch dock is located at the forward tip of the
    Saturn mother ship core corridor---the spear that is the ship's axis. 
    <.p>"
    
    up: TravelConnector
    {
        destination = hatchIntoMoonLander
    }
;
+ hatchIntoMoonLander: Door 'hatch;moon lander'
    "Hatch into moon lander. <.p>"
    
    isOpen = nil
  
    otherSide = hatchOutOfMoonLander

;

Any suggestions on how I can make the otherSide property of the hatchOutOfMoonLander object (type Door) dynamic?

When onTitan is true, I want the other side to be hatchFromMoonIntoMoonLander on the titanLandingSite.

When onTitan is nil, I want the hatch’s other side to be hatchIntoMoonLander on the landingModuleLaunchDock.

Doable? (A break point in the getDest() method of hatchOutOfMoonLander object stops exactly once when I run the program—early on, I believe during pre-init, then never again no matter how often the PC goes through the hatch.)

If not doable, suggestions for alternatives?

Thanks.

Jerry

I think your diagnosis that Door object’s otherSide property is only relevant at preinit time is correct.

I haven’t tried, but I think making the hatch a TravelConnector object and change its destination property is the way to go.

Instead of trying to mess around with the otherSide property, it’s probably much easier just to move the outside of the hatch between Titan and the landing bay; something like this:

#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 <a href="mailto:jerry.o.ford@gmail.com">
                  Jerry Ford</a>'
    version = '1'
    authorEmail = 'Jerry Ford <jerry.o.ford@gmail.com>'
    desc = 'Testing dynamic door destination.'
    htmlDesc = 'Testing dynamic door destination.'

;

gameMain: GameMainDef
    initialPlayerChar = me
    paraBrksBtwnSubcontents = nil
   
;

me: Actor 'me;him' @saturnMoonLander
    ""
    firstName = 'John'
    lastName = 'Doe'
    person = 2
;

saturnMoonLander: Room 'Moon Landing Module' 'landing module;Enceladus Titan
    Saturn moon;lander'
    "The moon lander is a small space with two recliner chairs in front of a
    control panel on which there is a big red button labelled <i>Magic
    Button</i>. \b
    The current location of the lander is <<if(!onTitan)>>docked to the mother
    ship<<else>>on Titan<<end>>. <.p>"
    
    onTitan = nil
    
    down = hatchOutOfMoonLander

;
+ magicButton: Button 'magic button'
    "The magic button toggles the lander's position between <i>on Titan</i> and
    <i>off Titan</i> <.p>"
    
    dobjFor(Push)
    {
        action()
        {
            saturnMoonLander.onTitan = saturnMoonLander.onTitan == nil ? true :
            nil;
            
            /* HERE'S THE STATEMENT THAT DOES THE WORK */
            hatchIntoMoonLander.moveInto(saturnMoonLander.onTitan ?
                                         titanLandingSite :
                                         landingModuleLaunchDock);
            
            "At the touch of the magic button you are ";
            if(!saturnMoonLander.onTitan)
                "docked to the mother ship";
            else
                "on Titan";
            ". <.p>";
        }
    }
;
+ hatchOutOfMoonLander: Door 'hatch'
    "Hatch out of moon lander. <.p>"
    
    otherSide = hatchIntoMoonLander
    travelDesc
    {
        if(saturnMoonLander.onTitan)
        {
            "The hatch pops open. You drop down to the frozen
            surface of Titan. <.p>";
        }
        else
        {
            "You descend from the lander entry hatch into the launching dock.
            <.p>";
        }
    }
;
titanLandingSite: Room 'Titan Landing Site' 'landing site;Titan'
    "The lander sits on a low rise of icy ground. In the near distance to the
    south, the ice gives way to the dark liquid methane of Kraken Mare.
    <.p>"
    
    up = hatchIntoMoonLander
;

landingModuleLaunchDock: Room 'Landing Module Launch Dock' 'landing module
    launch dock'
    "The landing-module launch dock is located at the forward tip of the
    Saturn mother ship core corridor---the spear that is the ship's axis. 
    <.p>"
    
    up = hatchIntoMoonLander
;
+ hatchIntoMoonLander: Door 'hatch;moon lander'
    "Hatch into moon lander. <.p>"
    
    isOpen = nil
  
    otherSide = hatchOutOfMoonLander
    
    travelDesc
    {
        if(saturnMoonLander.onTitan)            
            "After knocking your boots against the lander strut in an
            effort to dislodge whatever remains of Kraken Mare sludge clinging to
            them, you climb the ladder and cycle through the air lock.<.p>";
        
        else
            
            "You climb up the ladder into the lander.<.p>";
        
    }
    
;

Actually that isn’t the case. The otherSide property is also used by makeOpen() and makeLocked() to keep the two sides of a door in sync, and in the destination method that’s called in several places, not least by the door’s execTravel() method that carries out travel via the door. This means you could change the otherSide property if you wanted, say, to implement a lift (elevator). E.g., to expand the previous example:

landingModuleLaunchDock: Room 'Landing Module Launch Dock' 'landing module
    launch dock'
    "The landing-module launch dock is located at the forward tip of the
    Saturn mother ship core corridor---the spear that is the ship's axis. There
    is a lift immediately to the north. <.p>"
    
    up = hatchIntoMoonLander
    north = dockDoor
;

+ dockDoor: Door 'lift door'
    otherSide = (lift.floorNum == 1 ? liftDoor : self)
;

+ hatchIntoMoonLander: Door 'hatch;moon lander'
    "Hatch into moon lander. <.p>"
    
    isOpen = nil
  
    otherSide = hatchOutOfMoonLander
    
    travelDesc
    {
        if(saturnMoonLander.onTitan)            
            "After knocking your boots against the lander strut in an
            effort to dislodge whatever remains of Kraken Mare sludge clinging to
            them, you climb the ladder and cycle through the air lock.<.p>";
        
        else
            
            "You climb up the ladder into the lander.<.p>";
        
    }
    
;

lift: Room 'Lift'
    "A plain cubicle with an exit to the south. A red button on the wall makes
    the lift move between floors. "
    south = liftDoor
    out asExit(south)
    
    floorNum = 1
;

+ liftDoor: Door 'door'
    otherSide = [dockDoor, loungeDoor][lift.floorNum]
;

+ redButton: Button 'red button'
    makePushed()
    {
        liftDoor.makeOpen(nil);
        "The lift door slides shut. ";
        lift.floorNum = lift.floorNum == 1 ? 2 : 1;
        "The lift moves a short way and comes to a halt; then the door opens
        again. ";
        liftDoor.makeOpen(true);
    }
;

lounge: Room 'Lounge'
    "This is the recreation area. A lift is just to the north. "
    north = loungeDoor
;

+ loungeDoor: Door 'lift door'
    otherSide = (lift.floorNum == 2 ? liftDoor : self)
;

Note that here you have to take care of opening and closing doors at the appropriate moment. In the lander example it’s easier just to move the outer door to the new location since keeping the open/closed and locked/unlocked status in sync on both sides of the door is then taken care of for you; it also better models what’s actually happening (i.e. the outer hatch door would in fact move with the lander).

Eirc:

Your solution (move the hatch according to the expected destination) appears to work. Thank you.

Re: your statement that …

…okay, I accept your greater knowledge of how TADS and the library work, but I haven’t been able to make it happen. Maybe it’s the way I attempted it or the place in the code I tried to make it happen (otherSide = getDest in the Door object), but the breakpoint I set in the getDest() method only stopped the build during preinit.

When I tried stepping through the door code at the place where it is used during game play, I could not get the breakpoint to work.

At this point, the question is moot—your solution works, I like it, I’m good.

But I think I need to do some more experimenting with the otherSide property just to understand it. I do in fact have a lift under the moon lander, it just doesn’t do very much (lifts a rover into a storage bay). Maybe with the ability to change the otherSide destination, I can do some other things with it.

Jerry

When I tested it, the code I gave in my second post (for the lift) worked just fine.

It may be (I haven’t tested this) that part of the problem with your original code was the indirection of setting otherSide to getDest and then computing the result in getDest. It could be that this makes the compiler/VM calculate the value of otherSide only once (at preinit) and then store the constant result. That’s something you might be able to experiment with. It’s probably better to make otherSide a function or expression that directly returns the desired result.

Another problem I can see with your sample code is this:

landingModuleLaunchDock: Room 'Landing Module Launch Dock' 'landing module
    launch dock'
    "The landing-module launch dock is located at the forward tip of the
    Saturn mother ship core corridor---the spear that is the ship's axis. 
    <.p>"
    
    up: TravelConnector
    {
        destination = hatchIntoMoonLander
    }
;

I can’t see why you employ the indirection of defining a TravelConnector on the up property that then has hatchIntoMoonLander as its destination property instead of defining the door directly on the direction property:

   up = hatchIntoMoonLander

The latter is how it’s meant to be done, and the former may very well not work properly.

I plead ignorance :slight_smile:

I must have momentarily forgot that a door is a travel connector when I created that bit of code. So, I made a travel connector to go to the travel connector. Still climbing the learning curve.

I have not yet tinkered with the elevator example you gave, my comments about not being able to get it to work were intended to reference my original code, trying to get the break point to work.

You may be right about indirection through a local method causing the compiler to store values calculated during preinit.

During my trial and error phase, I thought about assigning the otherSide directly using something like otherSide = on Titan ? go to titan surface : go to launch dock but abandoned the thought before doing any serious testing because I will eventually need a third alternative (the explorers are eventually going to go to Enceladus, and the lander needs an exit there as well).

Jerry