Semi-Custom LiteralAction - HelloAction

Long-winded, inessential intro

With Comp nearly closed, I became charged with motivation to re-energize my TADS3 homeschooling. I’ve got a small training game I was using to get my hands around the syntax and development tools. I’m going to shed it in a few days (what remains is pushing the NPC dialogue into a better state) to start work on what I hope will be a 2023 Comp entry, best laid plans amirite? Sidebar, the documentation suite for TADS is just phenomenal. I have most of them open on my desktop and its been rare I can’t find an answer I need, or at least get me close enough that I can trial and error the last mile. I’m not a coding novice by a long shot, so I’ve actually been more impressed than daunted with the power and breadth of TADS.

My development environment is lightweight linux: GVim editor, t3Make and frobtads execution. I have the workbench on a windows machine but don’t use it so much. The debugger is the probably the piece I miss most, and will likely power it up if you guys leave me hanging! :]

My challenge: I was trying to implement an alternate “Hello” mechanism to mirror military radio protocol (what? you don’t know me!). The form is “[NPC], this is [player]” which was formally close to “[NPC], hello” that it seemed worth a try. I also wanted to set a variable based on the literal string the player used to identify themselves. The intent was something like this:

'>H43, this is U71
“This is H43, go ahead U71”

and also this

'>this is U71
“This is H43, go ahead U71”

Given how successful I’d been with mix-in classes until now, my first FAILED attempt was this:

class ThisIsAction : LiteralActionBase, HelloAction
	execAction() {
		inherited;
		local cs = getLiteral();
		// mainReport('...' + cs + '...');  // debug statement
		if (cs == 'U71') gReveal('caller'); // unlock dialogue if PC correctly id's themself
		me.makeCallSign(cs); // sets ID in actor

		gIssuingActor.sayHello(gActor);
	}
;
VerbRule(ThisIs)
	'this' 'is' singleLiteral : ThisIsAction
	verbPhrase = 'this is (what)'
;

This doesn’t work. Playing around with combinations of inherited or not, was never able to get the Literal value extracted correctly, it was always blank.

This, on the other hand is closer:

class ThisIsAction : LiteralAction
	doActionMain() {
		inherited;
		local cs = getLiteral();
		// mainReport('...' + cs + '...');  // debug statement
		if (cs == 'U71') gReveal('caller'); // unlock dialogue if PC correctly id's themself
		me.makeCallSign(cs); // sets ID in actor

		gIssuingActor.sayHello(gActor);
	}
;
VerbRule(ThisIs)
	'this' 'is' singleLiteral : ThisIsAction
	verbPhrase = 'this is (what)'
;

…but still is not quite there.

'>H43, this is U71
“H43, this is uh, a friend, over” you say. // This is the game saying “you don’t know who to claim to be”
// The literal is still not found.

and this is closer but also bad

'>this is U71
You can’t do that. “This is H43, go ahead U71” // works correctly, despite the error message! which I don’t know where it comes from or how to suppress.

Thanks in advance for any pointers that can forestall me having to power up my windows debugger.

You have no idea of the deluge of questions coming, now that I’ve found this board…

1 Like

Instead of defining ThisIs as a LiteralAction you probably want to define it as a SpecialTopic.

The problem you’re running into is that inputs of the form >ALICE, THIS IS BOB will be parsed as the speaking actor (probably the player) telling Alice to obey the command “This is Bob”. Which probably isn’t what you want.

You could fiddle around to make this work (e.g., by modifying Alice or maybe all of Actor or Person) to do something funny in obeyCommand(), but the cleanest way to handle it (especially if you’ve got a lot of inputs of this general format) is to handle them using TADS3/adv3’s Topic logic.

1 Like

Actually, I take it back. I think it turns out to be more complicated to try to handle this via Topic if you want to accept the syntax >ALICE, THIS IS BOB. The Topic approach is pretty straightforward if you just want to handle the syntax as >THIS IS BOB, but you end up having to re-solve the problem of the parser handling “this is bob” as a command to Alice for the former syntax.

Anyway, here’s a toy implementation that handles >ALICE, THIS IS BOB correctly within the bounds of my testing. Dunno if you’ve got any funny special cases.

#charset "us-ascii"
#include <adv3.h>
#include <en_us.h>

DefineLiteralAction(ThisIs)
        execAction() {
                local from;

                from = gIssuingActor;
                gMessageParams(from);
                mainReport('{You/he from} identif{ies from}
                        {yourself/herself from} as <q>'
                        + getLiteral() + '</q>. ');
        }
;
VerbRule(ThisIs)
        'this' 'is' singleLiteral
        : ThisIsAction
        verbPhrase = 'this is (who)'
;

modify Person
        obeyCommand(fromActor, action) {
                if(action.ofKind(ThisIsAction))
                        return(true);
                return(inherited(fromActor, action));
        }
;

startRoom:      Room 'Void'
        "This is a featureless void. "
;
+me:    Person;
+alice: Actor, Person 'Alice' 'Alice'
        "She looks like the first person you'd turn to in a problem. "
        isProperName = true
        isHer = true
;

versionInfo:    GameID
        name = 'sample'
        byline = 'nobody'
        authorEmail = 'nobody <foo@bar.com>'
        desc = '[This space intentionally left blank]'
        version = '1.0'
        IFID = '12345'
;
gameMain:       GameMainDef
        initialPlayerChar = me
;

Sample transcript:

Void
This is a featureless void.

Alice is standing here.

>alice, this is Bob
You identify yourself as "Bob".

>alice, this is foozle
You identify yourself as "foozle".

>alice, this is foo bar baz
You identify yourself as "foo bar baz".
2 Likes

So, not only did you answer lightning fast, not only did you correct a not-quite-right first cut (also fast), you also solved my issue in, like, barely an hour. You know what this means, right? This board is now my debugger :]

Thanks so much, solution worked like a champ. I did briefly go down the path of “looks like I need an obeyCommand here” when I dropped the HelloAction inheritance (which I had hoped would do the two form lifting for me) but lost the thread after a misguided attempt at the above.

FTR, here is the code that worked. Basically your toy code above.

DefineLiteralAction(ThisIs)
	execAction() {
		local xmitter = gIssuingActor;
		gMessageParams(xmitter);
		local cs = getLiteral();

		if (cs == 'U71') gReveal('caller');
		xmitter.makeCallSign(cs);
		xmitter.sayHello(gActor);
	}
;
VerbRule(ThisIs)
	'this' 'is' singleLiteral : ThisIsAction
	verbPhrase = 'this is (who)'
;
modify Actor
// cut a bunch of irrelevant additional mods
	obeyCommand(fromActor, action) {
		if(action.ofKind(ThisIsAction)) return true;
		return(inherited(fromActor, action));
	}
;

Don’t need to take more of your time, but the piece I’m still unclear on is what difference this made

verbPhrase = 'this is (who)'
vs
verbPhrase = 'this is (what)'

Lastly, only tangentially related, why are there no warnings about the seductive power of StringPreParser and regular expressions? I decided to try using that to solve a specific problem, then a few more, and if the ‘this is [player]’ form had worked at any point I probably would have jumped to that as well. There’s a real ‘first hit is free, you’ll be back kid’ vibe to that capability…

1 Like

Hey JJ! Welcome to the TADS crowd! I’m glad jbg got you fixed up. Yes, StringPreParsers can be great for special cases! I’m currently working in adv3 too, so I’ll help when I can but it sounds like you’re well past the beginning steps…

1 Like