Working with Action.verifyAction() results, a la illogical(), illogicalNow(), and so on

TADS3/adv3 provides a bunch of convenience methods and other semantic sugar for dealing with verify results from within action handlers on objects. But there doesn’t appear to be anything comparable for doing checks in the action declaration itself (as needs to be done for intransitive verbs).

Part of the problem is that all of the macros available for use in dobjFor() { verify() {} } (logical, illogical, illogicalNow, and so on) are doing something like a gVerifyResults.addResult() under the hood. gVerifyResults in turn is a macro that points to libGlobal.curVerifyResults, and libGlobal.curVerifyResults isn’t defined when Action.verifyAction() is called.

So Action.verifyAction() can’t use logical, illogical, and so on.

The cleanest thing that I could figure out to do is to add a method to Action like:

modify Action
        appendIllogicalResult(resultSoFar, msg) {
                if(resultSoFar == nil)
                        resultSoFar = new VerifyResultList();
                resultSoFar.addResult(new IllogicalVerifyResult(msg));

                return(resultSoFar);
        }
;

Then in an Action definition you can write verifyAction() using something like:

        verifyAction() {
                local result;

                result = inherited();
                if([SOME CONDITION YOU WANT TO CHECK])
                        result = appendIllogicalResult(result, '[YOUR FAILURE MESSAGE]');

                return(result);
        }

This works, but it’s still a bit of a mess compared to the way most action handlers on objects declare their verify() methods.

Basically just looking for a cleaner/neater way to do this.

Here’s a simple demo that illustrates the above. It defines a new action for twiddling one’s thumbs, >TWIDDLE, and a couple NPCs to try ordering to do it:

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

modify playerActionMessages
        wontTwiddle = '{You/he} refuse{s} to twiddle {its/her} thumbs. '
;

modify Action
        appendIllogicalResult(resultSoFar, msg) {
                if(resultSoFar == nil)
                        resultSoFar = new VerifyResultList();
                resultSoFar.addResult(new IllogicalVerifyResult(msg));

                return(resultSoFar);
        }
;

DefineIAction(Twiddle)
        verifyAction() {
                local result;

                result = inherited();
                if(!gActor.ofKind(TwiddleActor))
                        result = appendIllogicalResult(result, &wontTwiddle);

                return(result);
        }
        execAction() {
                defaultReport('{You/he} twiddle{s} {its/her} thumbs. ');
        }
;
VerbRule(Twiddle) 'twiddle' : TwiddleAction verbPhrase = 'twiddle/twiddling';

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

class TwiddleActor: Actor, Person;

startRoom:      Room 'Void'
        "This is a featureless void. "
;
+me: TwiddleActor;
+alice: TwiddleActor 'Alice' 'Alice'
        "She looks like the first person you'd turn to in a problem. "
        isProperName = true
        isHer = true
;
+bob: Person 'Bob' 'Bob'
        "He looks like a Robert, only shorter. "
        isProperName = true
        isHim = 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
;

That gets us:

Void
This is a featureless void.

Alice and Bob are standing here.

>twiddle
You twiddle your thumbs.

>alice, twiddle
Alice twiddles her thumbs.

>bob, twiddle
Bob refuses to twiddle his thumbs.

This is not a particularly good way to implement this particular test case (it would probably make more sense to change the behavior of obeyCommand()) but what I’m really concerned about here is the mechanics of Action.verifyAction() in general.

I’m thinking that the main purpose of verify() is to select a parser object. Since IActions have no simulation objects, you can simply use Thing.failCheck for any circumstances where you don’t want the action to succeed. At least that’s my take…

Yeah, that (or something equivalent—manually doing a reportFailure() and then exit) is how I have generally been handling this sort of thing.

But I have a bunch of cases where I want an intransitive verb to use most of the same sanity-checking logic used in the transitive form. And since the verify() model is to poll everything and allow anything polled to veto the action (and/or adjust its likelihood), it would be convenient (and involve less code duplication) to be able to use an equivalent mechanism in e.g. verifyAction(). If that all makes sense.

And, you know, adv3 actually does use the same basic logic—verifyAction() is expected to either return nil or a VerifyResultList instance. It just doesn’t supply all the semantic sugar that’s available (out of box) for verify() methods to add the result.

1 Like

Slightly cleaner version of my above code, using macros similar to the stock adv3 ones:

#define illogicalIAction(msg, params...) \
        { local _r; _r = inherited(); if(!_r) _r = new VerifyResultList(); \
        _r.addResult(new IllogicalVerifyResult(msg, ##params)); return(_r); }

This allows the verifyAction() in the demo code to be rewritten as:

        verifyAction() {
                if(!gActor.ofKind(TwiddleActor))
                        illogicalIAction(&wontTwiddle);
                return(nil);
        }

This still doesn’t quite get me what I want (you can’t reduce this to a single method that could be called from either a verifyAction() or verify() method) but it’s stylistically closer to what the illogical macros do for verify().

Here’s an updated demo that includes some of the other illogical macros. Not all of them need to be implemented, because adjusting logical likelihood doesn’t have any effect in verifyAction():

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

modify playerActionMessages
        wontTwiddle = '{You/he} refuse{s} to twiddle {its/her} thumbs. '
        wontTwiddleAlready = '{You/he} already twiddled {its/her} thumbs. '
        okayTwiddle = '{You/he} twiddle{s} {its/her} thumbs. '
;

#define illogicalIAction(msg, params...) \
        { local _r; _r = inherited(); if(!_r) _r = new VerifyResultList(); \
        _r.addResult(new IllogicalVerifyResult(msg, ##params)); return(_r); }
#define illogicalNowIAction(msg, params...) \
        { local _r; _r = inherited(); if(!_r) _r = new VerifyResultList(); \
        _r.addResult(new IllogicalNowVerifyResult(msg, ##params)); return(_r); }
#define illogicalAlreadyIAction(msg, params...) \
        { local _r; _r = inherited(); if(!_r) _r = new VerifyResultList(); \
        _r.addResult(new IllogicalAlreadyVerifyResult(msg, ##params)); \
        return(_r); }
#define inaccessibleIAction(msg, params...) \
        { local _r; _r = inherited(); if(!_r) _r = new VerifyResultList(); \
        _r.addResult(new InaccessibleVerifyResult(msg, ##params)); return(_r); }

DefineIAction(Twiddle)
        verifyAction() {
                if(!gActor.ofKind(TwiddleActor))
                        illogicalIAction(&wontTwiddle);
                if(gActor.twiddleFlag == true)
                        illogicalAlreadyIAction(&wontTwiddleAlready);
                return(nil);
        }
        execAction() {
                gActor.twiddleFlag = true;
                defaultReport(&okayTwiddle);
        }
;
VerbRule(Twiddle) 'twiddle' : TwiddleAction verbPhrase = 'twiddle/twiddling';

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

class TwiddleActor: Actor, Person
        twiddleFlag = nil
;

startRoom:      Room 'Void'
        "This is a featureless void. "
;
+me: TwiddleActor;
+alice: TwiddleActor 'Alice' 'Alice'
        "She looks like the first person you'd turn to in a problem. "
        isProperName = true
        isHer = true
;
+bob: Person 'Bob' 'Bob'
        "He looks like a Robert, only shorter. "
        isProperName = true
        isHim = 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
;

This also implements a twiddleFlag on actors who can twiddle, to give a condition to test for for the illogicalAlready equivalent. A thrilling transcript:

Void
This is a featureless void.

Alice and Bob are standing here.

>twiddle
You twiddle your thumbs.

>twiddle
You already twiddled your thumbs.

>alice, twiddle
Alice twiddles her thumbs.

>alice, twiddle
Alice already twiddled her thumbs.

>bob, twiddle
Bob refuses to twiddle his thumbs.

1 Like