Easy Action Responses (adv3Lite)

Hello again, TADS friends!

I am posting a module that provides an extremely quick and easy way for gamemakers to add custom action responses to game world objects. I have posted about this before, but 1. there weren’t as many active TADS users on the forum then, and 2. that version was for adv3, and this version is for adv3Lite!

Note: this system can be incorporated into an existing game in progress! None of your existing code will be invalidated by using it. You can freely mix the adv3Lite way of doing things with the “n/k” system’s way of doing things in the same object.

What’s it about? The point of the system is to encourage authors to add more verb responses by making them much easier to type. Simply put, you can put one of three prefixes onto any verb name (including your own custom verbs), and use this term as a property name followed by a message. Based on the prefix you use, the message you write will be printed in either verify(), check(), or action(). The prefix n- corresponds to “no/not/can't”, and will fail the action in verify. The prefix ch- will automatically clear verify, and fail an action in check (which means preconditions will have to be satisfied first). And k- stands for “okay/proceeding” which will automatically clear verify and check (for just this verb on this object), and print the message in action if preconditions are first met.
Instead of:

gameThing: Thing 'thing;;'
    isDiggable = true
    dobjFor(Dig) {
        action() {
            "You paw around in the heap of fropwizzle for awhile without finding anything. ";
        }
    }
    dobjFor(Take) {
        action() {
            "You pick up an armload of fropwizzle but it mysteriously disintegrates. "; 
        }
    }
    dobjFor(SmellSomething) {
        check() {
            "You don't actually want to bury your nose in that stuff: it's potent. ";
        }
    }
    isBreakable = nil
    cannotBreakMsg = 'Fropwizzle isn\'t breakable, properly speaking. '
;

we can have:

gameThing: Thing 'thing;;'
    kDig = 'You paw around in the heap of fropwizzle for awhile without finding anything. '
    kTake = 'You pick up an armload of fropwizzle but it mysteriously disintegrates. '
    chSmell = 'You don't actually want to bury your nose in that stuff: it's potent. '
    nBreak = 'Fropwizzle isn\'t breakable, properly speaking. '
;

Or more functionally:

+ unimportantWoodpile: Fixture ‘vocab;;’ “Desc”
   // other props and methods
   nFeel = ‘You don\’t want to get a splinter. ‘
   nMove = ‘There\’s too much wood and too little reason. ‘
   kClimb = ‘You climb up the woodpile, but since nothing comes of it, you get back down. ‘
   kLookBehind = ‘There\’s just a bunch of loose bark and such. ‘
   //etc. etc.
;

And to be clear, you can freely mix dobjFor, isVerbable/cannotVerbMsg, and nVerb/chVerb/kVerb for different verbs on the same object. Note, though, that if for any reason you end up with isFeelable = nil and kFeel on the same object, the n/k system is checked first, and the kFeel message will indeed be printed. But if you have an object that defines isFeelable = nil/cannotFeelMsg, defining kAnyOtherVerb on that object isn't going to interfere with cannotFeel or any other code that’s already written.

What the system is not: it’s not a replacement for dobjFor. It isn’t intended to handle complicated processing: it’s meant to make it easy to add supplementary content. Anything at all that fails verify under certain conditions but passes it in others is not a candidate for the system. Probably all of the verb responses for actions that are critical to your puzzle chain will still be handled by dobjFor. Nevertheless a parser game can be greatly enriched by rewarding the inquisitive and experimenting player with custom responses to non-essential actions, and this makes that easy to do.

The system can also be included by your custom verbs with the use of four or five easy-to-learn macros. The pattern looks like this:

DefineTAction(JumpOn) ;    //VerbRules etc...
modify Thing
   dobjFor(JumpOn)  nkTouch(JumpOn)
   nJumpOn = 'You decide you\'ve got better ideas to entertain. '
;

Because all Things will initially have nJumpOn defined (and no kJumpOn defined), ‘jump on thing’ will be dismissed in the verify phase, without consideration to preconditions. However, if you define

puddle: Thing
   kJumpOn = 'You jump in the puddle and make a big splash. ' 
;
statue: Thing
   nJumpOn = 'The statue is way too high to jump on. ' 
;

the statue will simply use your new message, still disallowing the action in verify, while the library will make sure you can touch the puddle before printing your message, because you used the nkTouch macro. If you had used the nkNoPrec macro in the verb definition, there would be no preconds and your message would be printed if the object is in scope at all.
The other macros for use with custom verbs are nkHeld and nkVisible; the usage is more fully explained in nkLite.t.

It would be great if some TADS-loving soul could at least give this a try just to confirm that it works properly! I am not a Lite user myself, and have only tested it minimally on a skeletal Lite game.
nkLite.t (13.0 KB)
nkLite.h.txt (4.6 KB)
(remove the .txt extension from the above file, or else paste its contents into an existing header file)

3 Likes

Okay, so basically if you want to have a single message to flat-out reject an action for an object, then you set this message property for the object, which makes the action fail, and that message then appears?

1 Like

There is quite a bit more explanation in the usage guide at the top of nkLite.t…
Writing

+pond: Thing
   nDrink = 'You\'d probably get giardia from that water. '
;

is functionally equivalent to writing

+pond: Thing
   dobjFor(Drink) {
      verify {
          illogical('You\'d probably get giardia from that water. ');
      }
   }
;

Whereas if you wrote

+pond: Thing
   kDrink = 'You take a slurp from the pond. '
;

the library would first check to make sure preconditions are met (able to touch pond), and then print your message in response to DRINK POND. You would not need to set isDrinkable = true for this message to appear, or manually clear verify and check anywhere else… the macro system does it for you. Defining a k[Verb] message means that (for that verb on that particular object) verify and check will be brute-force cleared, and your message will be printed after preconds…

chDrink is a form you can use if you specifically want the failure to occur in check() phase (the object will still be a more logical choice for the parser, and preconditions will still run)…

1 Like

Ohhhhh okay! Interesting!

1 Like