[adv3lite] Randomized quit message

Hi there. I’m learning TADS3 with adv3lite, jumped ship from adv3 since it seemed like adv3lite was the way of the world nowadays. I’m trying to create randomized quit messages a-la Doom. In adv3, I achieved such by doing this:

modify libMessages confirmQuit() { say(quitMessages.getNextValue() + ' (Press Y to quit.) '); } ;

But with the standard mechanism for custom messages in adv3lite, I always end up with the same message every time I try to quit. It gets randomized once, and then never again. Here’s my latest attempt:

CustomMessages messages = [ Msg(quit query, '<<one of>> Please don\'t leave, there\'s more bad guys to smack!<<or>> Let\'s beat it; this is getting really stupid!<<or>> I wouldn\'t leave if I were you. Real life is much worse.<<or>> You\'re trying to say you like real life better than me, right?<<or>> Don\'t leave yet; there\'s an eggbear around that corner!<<or>> Ya know, next time you come in here I\'m gonna toast ya.<<or>> Go ahead and leave. See if I care.<<or>> Are you sure you want to quit this great game?<<or>> You want to quit? Then, thou hast lost an eighth!<<or>> Don\'t go now, responsibilities are waiting outside!<<or>> Get outta here and go back to your boring programs.<<or>> If I were your boss, I\'d money match ya in a minute!<<or>> Look, bud. You leave now and you forfeit your hug count!<<or>> You\'re lucky I don\'t smack you for thinking about leaving. <<shuffled>> (y/n) ') ];

Why does this happen, and is there anything I can do to fix it?

Untested, but I found this on p. 159 of “Learning”:

“The randomize() function is used to re-seed the random number generator (to ensure that we get a different sequence of random choices each time the program is run).”

Try putting a call to randomize() at the top of your showIntro() in gameMain.

Right, I’ve already done that. What’s happening is this:

[code]>quit
Go ahead and leave. See if I care. (y/n) n

quit
Go ahead and leave. See if I care. (y/n) n
quit
Go ahead and leave. See if I care. (y/n) n[/code]
Each time I run it, the first message is random, but it just repeats that same one over and over.

I’m going to take a wild guess that the CustomMessages object is evaluated only once – at preinit. At that point, one of your messages is chosen at random, and that’s the end of the randomness, because the object is never evaluated again. If that’s the case, you need to (a) use an actual ShuffledEventList and (b) quite possibly call it from your own code. In other words, don’t use the library code that invokes the quit query message.

I haven’t set up a quit condition yet in the game I’m working on, so it would take me a few minutes to test this. I’ll investigate further, though.

Okay, thank you very much.

…and here’s how to do it:

modify Quit execAction(cmd) { local ans; quitMessages.doScript(); " (y/n)?\n>"; ans = inputManager.getInputLine(); if(ans.toLower.startsWith('y')) throw new QuittingException; } quitMessages: ShuffledEventList {[ 'Are you really, really sure you want to quit?', 'Okay, I get it. You want to quit. Right?', 'You\'re leaving already?' ]} ;
What I did here was repace the DMsg (which was just going to print out in any case) with a call to my own ShuffledEventList. Here’s my output:

Oh, sweet. Thank you very much for that. It’s too bad that it looks like the only way to create randomized messages is to modify parts of the library; I wanted to use random responses for even more things, especially for nonsense commands. I wonder what it would take to have the strings in a CustomMessages object evaluated every time they’re used.

Yeah, that would be a nice feature. Probably not a trivial thing to add to the library, but quite useful for making games more flowing. In many cases, of course, the library messages can be replaced in individual objects (or in classes of objects) using more or less the same technique I used here. Just attach your own notImportantMsg to a Decoration, for example. In such a case I’m pretty sure you CAN use <> constructions, so the usefulness of making the DMsg and BMsg macros more flexible might be limited.

What do you think, Eric? Severely impractical, or just a lot of work?

This isn’t to do with BMsg() as such, it’s to do with the way the TADS 3 language evaluates embedded expressions in single-quoted strings (see the chapter on String Literals in the TADS 3 System Manual). This means that the problem you’ve encountere effects CustomMessages objects but not BMsg in general. Basically since CustomMessages objects store their data in a LookUpTable, as soon as the LookUpTable is referenced in code, all the strings it contains are evaluated and the result stored as a constant string (with the results of evaluating the embedded expression the first time round).

The way round that is to wrap the string in an anonymous function, thus:

CustomMessages messages = [
    Msg(quit query, {:'<<one of>>
        Please don\'t leave, there\'s more bad guys to smack!<<or>>
        Let\'s beat it; this is getting really stupid!<<or>>
        I wouldn\'t leave if I were you. Real life is much worse.<<or>>
        You\'re trying to say you like real life better than me, right?<<or>>
        Don\'t leave yet; there\'s an eggbear around that corner!<<or>>
        Ya know, next time you come in here I\'m gonna toast ya.<<or>>
        Go ahead and leave. See if I care.<<or>>
        Are you sure you want to quit this great game?<<or>>
        You want to quit? Then, thou hast lost an eighth!<<or>>
        Don\'t go now, responsibilities are waiting outside!<<or>>
        Get outta here and go back to your boring programs.<<or>>
        If I were your boss, I\'d money match ya in a minute!<<or>>
        Look, bud. You leave now and you forfeit your hug count!<<or>>
        You\'re lucky I don\'t smack you for thinking about leaving.
        <<shuffled>> (y/n) ')}
];

But for this to work you need to make a tweak to the library code (which I’m also incorporating). Around line 153 in messages.t, in the definition of the buildMessage() function you’ll find:

/* if we found an override, use it instead of the provided text */
    if (cm != nil)
        txt = cm.msgTab[id];

Immediately after that you’ll need to add:

/* if txt is a function pointer, retrieve the value it returns */
    if(dataType(txt) == TypeFuncPtr)
        txt = txt();

This should come just before:

    /* set up a sentence context for the expansion */
    local ctx = new MessageCtx(args);

Note, however, that if you just keep running the game and quitting immediately, you’ll keep seeing the same message, since the pseudo-random number generator will be in the same state each time. If more of the game is played and more random stuff is generated you should then see different quit methods.

I’ll upload a version of adv3Lite with this change to GitHub later today.