Message when out of topics (adv3Lite)

Okay, here’s a weird one. Using the latest adv3Lite off GitHub.

While beta-testing my game, a couple of testers learned they could get a list of topics from an Actor they were conversing with by typing TALK TO ACTOR. This is all well and good, until you run out of suggested topics.

Like so:

>talk to bylo
>

The result is a blank turn. My beta tester has just flagged this as a bug in my game. I tried this on the previous adv3Lite (1.2) and the behavior is the same.

But wait, there’s more.

Poking around to fix the problem I decided to override the present implentation the Actor method showSuggestions. showSuggestions is called whenever we list a topic inventory to suggest, like when using <.topics>.

So I put it in my Actor modify code, like so:

modify Actor

    showSuggestions(explicit = true, tag = (pendingKeys == [] ? suggestionKey
                                            : pendingKeys))
    {
        inherited;
    }
;

And made no changes to the code, just calling it in and calling inherited to ensure it would compile. When I tried the above, it did this:

>talk to bylo

You have nothing in mind to discuss with Bylo just now.

While this indeed fixes the problem, I’m not completely sure why it does. I’ve checked this both ways: when we remove the overrided (unchanged) showSuggestions from the modify block it goes back to the old blank turn behavior again.

Any idea what’s going on in here?

I’ve seen the same thing. My solution was to do this on each actor (though with different text, appropriate to the individual actor)…

dobjFor(TalkTo) { action() { lucy.actorSay('<q>What is it you want to say, sweetie?</q> <.p>'); } }

Jerry

Your fix appears to fix the problem because it’s a programming error that will almost certainly cause problems elsewhere. You’ve forgotten to add the parameters to showSuggestions when you call inherited. In other words, this:

modify Actor
    showSuggestions(explicit = true, tag = (pendingKeys == [] ? suggestionKey
                                            : pendingKeys))
    {
        inherited;
    }
;

Should have been this:

modify Actor
    showSuggestions(explicit = true, tag = (pendingKeys == [] ? suggestionKey
                                            : pendingKeys))
    {
        inherited(explicit, tag); // DON'T FORGET TO PASS THE ARGUMENTS ON!
    }
;

Normally this error would have caused a run-time error. It doesn’t here because the showSuggestions() method defines default values for both its arguments. So what you’ve done is to override showSuggestions so that it always uses its default arguments whatever its caller wanted. It’s as if you had written:

modify Actor
    showSuggestions(explicit = true, tag = (pendingKeys == [] ? suggestionKey
                                            : pendingKeys))
    {
        inherited(true, pendingKeys == [] ? suggestionKey : pendingKeys));
    }
;

Thus, what you have done is to tell showSuggestions() to ignore the arguments that are passed to it. This is a bad idea; it will almost certainly cause problems elsewhere in your game (for example, you may not get the right list of suggested topics in a ConvNode).

The command TALK TO X is meant to be used to start a conversation with X. The command TOPICS is the command that explicitly asks for a list of topics. So when the player types TOPICS the showSuggestions() method is called with the explicit argument as true. If there are no topic suggestions to be displayed you then get the “You have nothing in mind…” message because the player explicitly asked for a list of topics. The command TALK TO X is not, however, an explicit request for a list of topics; it’s an explicit request to talk to x (i.e. to start a conversation with x). As a courtesy to the player it also displays a list of topic suggestions if there are any, enclosing them in parentheses. Since the player has not explicitly asked for a list of topics, he or she is not told that there aren’t any if indeed there are not. In this case showSuggestions() is called with the explicit argument set to nil.

This solution is safer than the ‘fix’ applied by the OP, but do be aware that it will prevent TALK TO from doing what it’s actually meant to do. In particular this not only prevents TALK TO X from ever displaying a list of suggested topics (which is what the OP wants), it also prevents TALK TO X from displaying any HelloTopic responses at the start of the conversation. If this is what you actually want in your game, then well and good, but it’s inadvisable as a general solution, since it breaks what TALK TO is meant to do. TALK TO X is meant to be equivalent to X, HELLO.

Indeed, what TALK TO X does in the library is simply to call X.sayHello(). The sayHello() method is defined as follows:

 sayHello()
    {
        /* 
         *   Only carry out the  full greeting if we're not already the player
         *   character's current interlocutor.
         */
        if(gPlayerChar.currentInterlocutor != self)
        {
            /* 
             *   Note that we are now the player character's current
             *   interlocutor
             */
            gPlayerChar.currentInterlocutor = self;
            
            /*  Look for an appropriate HelloTopic to handle the greeting. */
            handleTopic(&miscTopics, [helloTopicObj], &noResponseMsg);
        }
        
        /* Add a paragraph break */
        "<.p>";
        
        /* Display a list of not-explicitly-asked-for topic suggestions */
        showSuggestions(nil, suggestionKey);
    }

What this does is to show a greeting message (i.e. the response to any HelloTopic that’s been defined) if the player character isn’t already talking to the addressee, and then to go on and display an implicitly asked for list of topic suggestions. The bug is that it doesn’t do anything else when a TALK TO message is issued to an actor with whom the player character is already in conversation. The fix thus needs to be applied to the sayHello() method, something along the lines of:

 sayHello()
    {
        /* 
         *   Only carry out the  full greeting if we're not already the player
         *   character's current interlocutor.
         */
        if(gPlayerChar.currentInterlocutor != self)
        {
            /* 
             *   Note that we are now the player character's current
             *   interlocutor
             */
            gPlayerChar.currentInterlocutor = self;
            
            /*  Look for an appropriate HelloTopic to handle the greeting. */
            handleTopic(&miscTopics, [helloTopicObj], &noResponseMsg);
        }
        /* Otherwise say we're already talking to this actor */
        else
            say(alreadyTalkingMsg);    
   
        /* Add a paragraph break */
        "<.p>";
        
        /* Display a list of not-explicitly-asked-for topic suggestions */
        showSuggestions(nil, suggestionKey);
    }

    alreadyTalkingMsg = BMsg(already talking, '{I} {am} already talking to {1}. ', theName)

This response can readily be changed on a per-actor basis, e.g. to Jerry’s preferred:

lucy: Actor 'Lucy;;;her'
   alreadyTalkingMsg = '<q>What is it you want to say, sweetie?</q> '
;

I’ll take a look at making this change in the library (and checking that it works as expected) when I get a chance.

I’ve now implemented the fix and uploaded it to GitHub. It’s slightly more complex that the one I described in my previous post, since I took the opportunity to fix some related behaviour I didn’t much like.

Changes have been made to the messages displayed when greetings (including TALK TO X) are used but no suitable HelloTopics or GoodbyeTopics are available to respond to them. Previously this would result in the display of the noResponseMsg if a conversation was being started or ended, or nothing at all if HELLO or TALK TO X was used in the middle of a conversation (which was the bug reported by spaceflounder). In no case was this fully satisfactory. The library thus now defines three new message properties to cater for these situations. If HELLO or TALK TO is issued at the start of a conversation, then the noHelloResponseMsg will be used in default of any available HelpTopic. If HELLO or TALK TO is used while a conversation is already in progress, then the alreadyTalkingMsg will be used. Finally, if there’s no ByeTopic to deal with a BYE command then the actor’s noGoodbyeResponseMsg will be used.

Using the new default messages now defined on Actor this results in an exchange like:

You can, of course, override these defaults if you wish simply by overriding the relevant message properties, e.g.:

[code]
lucy: Actor ‘Lucy;;;her’

noHelloResponseMsg = ‘What is it you want to say, sweetie?’
alreadyTalkingMsg = noHelloResponseMsg

;[/code]

These two messages will then be routed through actorSay() because they contain a quotation mark, indicating that they represent a conversational response. A non-conversational response would be routed through say() so that the library won’t consider it to be conversational.

Aha! Thank you Eric!