Intercepting Actions (adv3Lite)

Dollars to doughnuts I’m going to have to email Eric Eve about this, but I don’t like to keep bugging him, so I’ll start here and see if anyone can offer any insight.

Here’s the problem, in plain words: The PC is in a room with an NPC, and there are quite a lot of things that the PC shouldn’t try doing. The NPC (a wizard) is sitting at his desk. The desk has a drawer, and there’s something important in the drawer. The wizard would quite obviously not want the young lady to be trying to do anything at all with the drawer, such as trying to unlock it or open it, or even touch it. If the player tries those actions or other similar things, the wizard should put up a roadblock.

But that doesn’t happen. I’ve given the NPC an actorBeforeAction, which is intended to snag any such actions. Here’s the relevant code:

actorBeforeAction() {
    "Test: gAction is <<gAction>>. ";
    if (((gDobj == drawer) || (gIobj == drawer)) && (gAction != Examine)) {
        "Porfiru lowers his eyebrows to gaze at you meaningfully, and you
        decide not to try that right now. ";
        exit;
    }
 }

This seems sensible enough to me (though of course I’m quite capable of writing bad code). But here’s the output:

>x drawer
Test: gAction is Examine ( 670). The drawer is tucked away beneath the desk. On the front of the drawer is, you guessed it, a keyhole. 

>open drawer
The drawer is locked. 

As you can see, the attempted action “open drawer” never reaches the wizard’s actorBeforeAction routine. Why doesn’t it? Because the parser has already decided the action ain’t gonna happen, so there’s no need to consult the actorBeforeAction.

Unfortunately, this is a total violation of the world model. The PC should not be allowed even to try that action.

Adv3Lite provides an option (in gameMain) called beforeRunsBeforeCheck. Worth a try, right? It does change the output a bit. Now I get this:

>open drawer
Test: gAction is Open (TAction). The drawer is locked. 

Now the actorBeforeAction is being reached – but its output message is still not being displayed. The parser evidently has to choose between two output strings, and chooses its own rather than mine.

Does anyone have any thoughts about how to make this work?

For your second attempt to work I think the if-condition needs to use ‘!gActionIs()’ or ‘!gActionIn()’ instead of ‘!=’

The reasoning as I understand it for having the orders of check and “before” routines in adv3Lite contra adv3 is that the “check” were supposed to qualify the objekt for even being possible to do action upon the object before running the different beforeAction-routines and then possibly stopping the action. In this case though it seems fairly obvious that you need your actorBeforeAction to run slightly before. I guess one alternative could be Doer here if you don’t want to change the ordering with beforeRunsBeforeCheck.

ahemmm… from your code,

if (((gDobj == drawer) || (gIobj == drawer)) && (gAction != Examine))

Seems to me that check only Examine, but not also Open…

the probably correct (I hate LISP for a reason…) if should be:

if (((gDobj == drawer) || (gIobj == drawer)) && ((gAction != Examine) || (gAction == Open)))

Dunno how many blockings you want to implement, but seems to me, pardon the horrible pun, a case for case :wink:

if the ugly pun isn’t clear, I recommend the usage of the case… select instruction.

Best regards from Italy,
dott. Piergiorgio.

ps. edited today, july 17, 2023, that is, three years and four months later, for correcting the very bad formatting whose render incomprehensible my post; how this mistake escapes me in these 3+ years escapes me…

Does Lite use the gActionIs macro? For instance, if(!gActionIs(Examine)) …
Are there any preConditions or verify routines that you need to clear before your wizard gets a chance to disallow the action?

In adv3, if you don’t use the macro, you should have to use this phrasing: if(!gAction.actionOfKind(ExamineAction)) …

Of course all that may have been changed for adv3Lite…

Yes. I have now tried it, and it does seem to help a bit. The problem, I think, is that beforeActions run after the verify() stage of an action. So if you put anything in the verify() other than { logical; }, it will be used, and the beforeAction will never be consulted. I’m going to experiment further with this.

The problem with using a Doer is that a Doer, at least as it’s described in the documentation, always responds to a single Action. If you want to trap ten actions (Unlock, UnlockWith, Push, Pull, Open, Close, Feel, etc.), you need to write ten Doers. But that may be the most effective method. This is going to take a few hours for me to explore.

Okay, Doers seem to run before anything else. I’ve now added a dozen of them to the room code – all the actions I could think of. I’m sure I missed a few, but they’re likely to be obscure. I now have a situation where you can’t touch the desk while the wizard is watching.

Are Doers an optional thing in Lite (I don’t really know what they are at this point)? Couldn’t situations like that just have a clause in the verify routine that clears them so that they can be failed later? Assuming that would be the verify for drawer.dobjFor(Open)…
Anyway, glad you seem to have it working…

They’re optional at the author level. I believe the library routes all actions through four behind-the-scenes Doers, but how they operate is a mystery to me.

Eric was inspired to create Doers by the Inform 7 “instead” rules. A Doer intervenes at an early stage, before any action processing has started. For instance, if I write:

Doer 'put redBall in Container'
execAction(c) { /* some code here*/ };

This will intercept the rather obvious set of user commands; note that the nouns/objects are defined using their code names or indeed class names, but the verb ‘put’ isn’t. Using a Doer, you can redirect any standard command input to do anything you like. I’m pretty sure Doers run before the verify() stage of action processing, which is what one normally wants if one is going to intercept an action and turn it into something else.

So basically like a streamlined interface for remap, interesting…

Does Lite have an efficient way of remapping all commands in certain situations? At a certain point in a game I’m developing, I’d like all commands to stop working normally - I’d like this to occur before anything else happens, like disambiguation, checking spelling etc. Doers seem to need specific commands to work on. At the moment, the only way I can find to do it seems to be by altering the parser itself - but maybe there’s a better way, as I’m more used to Adv3 than Lite. Maybe the better way is to use Adv3, but I like a lot of the features Lite adds like scenes, regions etc.

Yes, it does. I don’t know if this is documented, but if you insert an asterisk into the command string of a Doer, it will match anything at all. In addition, a Doer can have properties called where and when, to control the logical conditions in which it will or will not be consulted. I hadn’t used this before, but I just now tested it, and it works. Here’s my silly test routine:

Doer '* barredWindow'
    execAction(c) {
        "Ka-sproing!!! ";
        abort;
    }
;

There’s an object whose code name is barredWindow in my opening room. I didn’t add where or when properties to this, but they are documented. This Doer interferes when the player tries to do anything at all with the barredWindow. I could tinker with the code to make it more specific; that’s just for starters. For instance, adding a second Doer with the command string ‘x barredWindow’ grabs only the Examine action, while the one with the asterisk grabs everything else.

1 Like

That’s quite handy, and seems to pick up most things, although oddly, it doesn’t seem to catch reduced commands (e.g. of the form ‘x skeleton’) for some reason.