(TADS 3) New Travel Action

I’m trying to define a new verb that results in travel; let’s call it “Crawl”. This way I can make a travel barrier to restrict passage in that direction if the action isn’t Crawl. The way I’ve defined it currently is as a Literal Action, where the direction is a literal string; this requires nested case-switch statements for every room and every possible direction. The good thing about this method is that I can control the output for each specific case. The bad thing is that’s a lot of cases to write. Honestly I’d like to avoid this brute-force method, since there must be a simpler way.

I’ve found this thread that describes something similar to what I’m trying to do. I’m just not sure how to adapt that code to my own purposes, as Crawl doesn’t require an item to enable travel.

Any thoughts or suggestions?

Since no one else has replied … it seems to me there are two ways to do this. (Knowing T3, there are probably more than two!) One would be to define a new action, Crawl, using the Travel actions in actions.t as a prototype. Your travelBarrier could then test gAction to find out if the player’s action was Crawl. The other method would be to add ‘crawl’ to the grammar for the Travel action in en_us.t, and then get the parser to check the first word in the player’s command. (Somehow – I’ve never asked the parser to look at the raw command line, but I’m sure there’s a way to do it.)

Travel is a bit of an odd duck, however, in that commands like ‘go back’, ‘enter the house’, and ‘climb the ladder’ are all going to result in a Travel action. You’ll need to think about how you want your game to respond to commands of this type.

You might be able to define Crawl as a new action and then use replaceAction in a dobjFor(Crawl), but you’d have to put that dobjFor on the direction objects. Not sure if that would work…

I think the problem I’m running into with this is that the Travel action gets replaced with a TravelVia action.
From actions.t:

[code]
DefineIAction(Travel)
execAction()
{
local conn;

    /* 
     *   Perform the travel via the connector, if we have one.  If
     *   there's no connector defined for this direction, show a
     *   default "you can't go that way" message. 
     */
    if ((conn = getConnector()) != nil)
    {
        /* 
         *   we have a connector - use the pseudo-action TravelVia with
         *   the connector to carry out the travel 
         */
        replaceAction(TravelVia, conn);
    }
    else
    {
        /* no connector - show a default "can't go that way" error */
        mainReport(&cannotGoThatWayMsg);
    }
}[/code]

And then it looks like each Connector class defines dobjFor(TravelVia) instead. So if I copy this implementation of Travel, I’m unable to use gAction because it returns the action as TravelVia, not Crawl. I suppose I could try to define something like CrawlVia…

Haven’t tried these methods yet (I, too, am unaware of how to look at the raw command line), but I will definitely take a look. Thanks for the advice!

I took a shot at it. Will this do?

#charset "us-ascii"

#include <tads.h>
#include "advlite.h"

versionInfo: GameID
    IFID = 'c11857ce-47dd-4ae3-b82f-896b58b616f3'
    name = 'test code for crawling movement'
    byline = 'by drayab'
    htmlByline = 'by <a href="mailto:your-email@host.com">Your Name</a>'
    version = '1'
    authorEmail = 'Your Name <your-email@host.com>'
    desc = 'Put a brief "blurb" about your game here'
    htmlDesc = 'Put a brief "blurb" about your game here'
;

gameMain: GameMainDef
    /* Define the initial player character; this is compulsory */
    initialPlayerChar = me
;

VerbRule(Crawl)
    'crawl'  singleDir
    : VerbProduction
    action = Crawl
    verbPhrase = 'crawl/crawling (where)'  
;

Crawl: TravelAction
    direction = (dirMatch.dir)
;

startroom: Room 'The Yard'
    "This is a grassy yard. Somewhat to your east is a small doghouse. "

    east: TravelConnector
    {
        destination = doghouse
        
        travelDesc = "After a mighty effort of squeezing and wiggling, you amazingly fit through the doggy door. "
        
        theName = 'the doggy door'
		
        travelBarriers = [doggyDoor]
    }
;


doghouse: Room 'The Doghouse'
    "It is extremely cramped and smelly in here. Your mother would be proud. "
    
    west: TravelConnector
    {
        destination = startroom
        
        travelDesc = "Nnnngh... you squeeze back out to the normalcy of the yard. "
        
        theName = 'the doggy door'
        
        travelBarriers = [doggyDoor]
    }
;

doggyDoor: TravelBarrier
        canTravelerPass(actor, connector)
        {
            return gActionIs(Crawl);
        }
        
        explainTravelBarrier(actor, connector)
        {
            "The doggy door is too small for normal movement. Looks like you'll have to <b>CRAWL</b>. ";
        }
;

+ me: Thing 'you'   
    isFixed = true
    location = startroom
    person = 2  // change to 1 for a first-person game
    contType = Carrier    
;

\o/ Wow, that certainly looks a hell of a lot simpler than what I’d have to do in T3. I guess I ought to take a look at adv3lite. Thanks!

(As a side note, I was messing around with modifying the execution of CrawlAction based on TravelVia, and somehow managed to make Crawl transport the player into THE VOID. As in, a nameless room with no exits. I just found it really hilarious.)

I like adv3Lite a lot. It simplifies certain tasks compared to adv3, and introduces (IIRC) a couple of features that are not found in adv3.

Because I am incredibly stubborn, I’ve still been trying to get this to work in T3 before I resort to adv3lite. And amazingly, I’ve had a partial success!
This method defines two actions, Crawl and CrawlVia. Crawl remaps travel through a connector to CrawlVia, and CrawlVia just copies the implementation of TravelVia. Then TravelConnector is modified to recognize CrawlVia. Thus it’s possible to set a TravelBarrier to only allow CrawlVia.

[code]DefineAction(Crawl, TravelAction)
execAction()
{
local conn;

    if ((conn = getConnector()) != nil)
    {
        replaceAction(CrawlVia, conn);
    }
    else
    {
        mainReport(&cannotGoThatWayMsg);
    }
}

;

VerbRule(Crawl)
‘crawl’ singleDir
: CrawlAction
verbPhrase = ‘crawl/crawling’ + dirMatch.dir.name
;

DefineTAction(CrawlVia)
getCurrentObjects = []
;

modify TravelConnector
{
dobjFor(CrawlVia)
{
preCond()
{
return gActor.getTraveler(self).travelerPreCond(self)
+ gActor.location.roomTravelPreCond()
+ connectorTravelPreCond();
}
verify()
{
}
check()
{
local t = gActor.getTraveler(self);
local dest;

        dest = getDestination(t.location, t);

        gActor.checkDarkTravel(dest, self);

        checkTravelBarriers(dest);
    }
    
    action()
    {
        local t = gActor.getTraveler(self);
        local dest;

        dest = getDestination(t.location, t);

        gActor.travelTo(dest, self, connectorBack(t, dest));
    }
}

}[/code]

Now, I say “partial” success because this only works if there is actually a connector in the direction you’re crawling. If there’s nothing there, instead of saying “You can’t go that way.” it once again transports the player into THE VOID. This is not nearly as amusing now, because I thought I finally had it.
Well, I just thought I’d post this bit of progress anyway, and see if anyone had any ideas. :slight_smile:

EDIT: Okay, I spoke too soon. I was able to fix it by adding this line:

if(dest==nil) failCheck(&cannotGoThatWayMsg); to the check() method in dobjFor(CrawlVia) in TravelConnector. It now correctly displays “You can’t go that way.” Not sure why it didn’t do that on its own, but hey. Now if only I could get it to show the ExitLister in addition to the message, it’d be perfect.

Hey! So this has been very enlightening, and am glad this is a Topic one can pull up for reference as I too wish to create some new travel actions in TADS while sticking with TADS rather than LITE. I think Dreyab’s LITE example mentioned earlier does a great job at how one would accomplish this on that platform.
I try to replicate working code in order to ensure I know how it works. I also think that a basic template for creating a new Travel Action would be greatly beneficial I just cant seem to get the mechanics down. Now I’ve run into a problem, I’ve used this code to try and make a LEAP command, instead of CRAWL. But every time I try to (Leap) it quits the game and points me at this

/* create an instance of the desired action class */ action = actionClass.createActionInstance();

So i think both TadsLite and Tads code on this page has somewhat confuzzled me, even though its good to have both in the same topics for easier reference. I was hoping to get it fleshed out so it can be of best benefit to the readers(and meself of course)
This is the code I’m using, Basically trying to leap off of a ledge in sOutp and land in the location ssOutp.

[code]sOutp: OutdoorRoom ‘south outpost’ ‘South Outpost Location’
'The location you find yourself in is ’
north = cPost
east = seOutp
west = swOutp
south = endLedge
;
endLedge: TravelBarrier
canTravelerPass(actor, connector)
{
return gActionIs(Leap);
}

    explainTravelBarrier(actor, connector)
    {
        "The ledge drops off suddenly, you'll need to <b>LEAP</b> in order to make it down. ";
    }      

;[/code]

Then I did a copy/alteration of the Crawl-Code to basically incorporate Leap. which ought to work, I think.

[code]DefineAction(Leap, TravelAction)
execAction()
{
local conn;

    if ((conn = getConnector()) != nil)
    {
        replaceAction(LeapVia, conn);
    }
    else
    {
        mainReport(&cannotGoThatWayMsg);
    }
}

;

VerbRule(Leap)
‘leap’ singleDir
: LeapAction
verbPhrase = ‘leap/leaping’ + dirMatch.dir.name
;

DefineTAction(leapVia)
getCurrentObjects = []
;

modify TravelConnector
{
dobjFor(leapVia)
{
preCond()
{
return gActor.getTraveler(self).travelerPreCond(self)
+ gActor.location.roomTravelPreCond()
+ connectorTravelPreCond();
}
verify()
{
}
check()
{
local t = gActor.getTraveler(self);
local dest;

        dest = getDestination(t.location, t);

        gActor.checkDarkTravel(dest, self);

        checkTravelBarriers(dest);
    }
    
    action()
    {
        local t = gActor.getTraveler(self);
        local dest;

        dest = getDestination(t.location, t);

        gActor.travelTo(dest, self, connectorBack(t, dest));
    }
}

}

;[/code]

I think I’m pretty close, but could use help to figure out how to spark this to life. Currently, South says I can’t go that way(Instead of the ledge scroll) LEAP says it doesn’t understand the command(Maybe we should have it say, “Leap which direction?”) and LEAP S keeps crashing the game and telling me I need to make a new instance of the ActionClass.

Any assistance would be of great benefit, and I’ll try and make sure there’s a finished template at the end of this which has the basics down, and pointing to where the (Crawl/Leap) code is, which can be changed to (Phase-through/roll/squeeze for maneuvering, or DART through for example if a winged character was flying and was trying to expertly fly through a tightly enclosed space for example.