ImpByeTopic not working in adv3lite conversation

I’m having multiple problems with a conversation between characters.

I want the player character, Harry, to get intermittent calls on his cell phone from NPCs who then give him information he needs to know in order to go on to subsequent activities.

So I have a playerCallsHarry scene in which a daemon is defined to call harry every x turns until Harry answers (at which point the daemon is destroyed, so that it doesn’t keep calling.) Harry then has a conversation with the caller. Once he’s told what he needs to know, a <.reveal info> flag gets set.

So far, so good. It works as planned. But there are glitches. I don’t want Harry to be able to stop the conversation before he gets the information, so I’ve been experimenting with ImpByeTopic, LeaveByeTopic, and BoredByeTopic, all unsuccessfully. The only thing that works is a plain vanilla ByeTopic, which doesn’t give me the control I need.

I only want to stop breaks in the conversation that occur before the .reveal.

I set the attentionSpan variable on the NPC, I run the game and use the debugger to inspect boredomCount periodically, and it is in fact getting bumped up on every move that harry makes without talking to the NPC, but the ImpByeTopic never gets used.

At this point, attentionSpan is 3 (which is what I set it to) and boredomCount is 0.

boredomCount is now 3, attentionSpan is 3…

boredomCount is 5, attentionSpan is 3, but still no ImpByeTopic.

Here’s the pertinent code…

[code]harrysSonMax: Actor ‘Max;teenager teen age ager;;him’ @maxsBedroom
“”

globalParamName = 'max'
person = 3   
bulkCapacity = 5000

attentionSpan = 3

;

  • ImpByeTopic
    "“My patience has worn thin. I’m outta here,” Max grumbled. "
    ;
  • HelloTopic
    ““Max, dude, 'Sup?” Harry said with forced cheerfulness. \b
    “'Sup?” Max grunted back dully. “Don’t say dude. It’s not in
    your age bracket.””
    ;
  • DefaultAskTellTopic
    "“Idunno,” Max slurred the words together. “Whyou al’ays askin’me
    stuff like’at?” "
    ;
  • ByeTopic
    ““Whatever,” Max replied.”
    ;
    [/code]

Full code is a bit long for quoting in the thread, but I’ve attached it.

I also have a new problem, unveiled by the release of the new 09 lite library (for which many thanks) :slight_smile:

In order to establish a conversation between Harry and an NPC, the two actors need to be in the same room. So, when the daemon rings the phone, I silently move max into harry.location, then when the conversation is over I return max to his original location.

With the 08 library, I could get away with this. Even with a look of the room, max was never listed, as evidenced by the transcript posted above, which is from an 08 library run.

Now he is shown as being in the room. Here’s a transcript from an 09 library run…

Is there a way I can suppress this?
test_bed.zip (2.58 KB)

Whose law is it that states that a new bug will always come to light just after a release?

This is a bug in the library. Since the next release may not be for a while, you may want to patch your copy of the library to fix it. The change that needs making is in actor.t, in the noteConversed() method of the Actor class (which starts at line 514 in my copy). This needs to be changed to read:

noteConversed()
    {
        /* Note that we're the player character's current interlocutor */
        gPlayerChar.currentInterlocutor = self;
        
        /* Note that we last conversed on this turn */
        lastConvTime = libGlobal.totalTurns;
        
        /* Note that this actor is a possible antecedent for a pronoun */
        notePronounAntecedent(self);
        
        /* Add our boredomAgendaItem to our agenda if it isn't already there */
        if(valToList(agendaList).indexOf(boredomAgendaItem) == nil)
            addToAgenda(boredomAgendaItem);
    }

The part that needs to be added here is at the end: adding the boredomAgendaItem if it isn’t already there.

That said, if what you want to do is to prevent a conversation from ending until certain information has been imparted, then it may be the canEndConversation() method you need to look at using.

This was a deliberate change to the library, as explained in the change log, since it’s surely right than an actor should be listed if present. You can change that by setting actorSpecialDesc to nil on the max object, but that doesn’t really solve your problem, because players can still examine Max, or touch him, or hit him, or do any number of things they shouldn’t be able to do if he isn’t actually there (and believe me, some players will attempt such actions).

What’s needed is a general purpose solution to allow communication between the player character and actors who aren’t in the same location. Since this is likely to be a not uncommon requirement, I wanted to come up with something that was reasonably general and reasonably easy to use to include in the next release. This is what I’ve come up with:

/*------------------------------------------------------------------------- */
/*
 *   A commLink is a Special that establishes a communications link between the
 *   player character and one or more actors in remote locations.
 *
 *   To activate the commLink with another actor, call
 *   commLink.connectTo(other). To make it a video link as well as an audio
 *   link, call commLink.connectTo(other, true).
 *
 *   To disconnect the call with a specific actor,  call
 *   commLink.disconnectFrom(other); to terminate the commLink with all actors,
 *   call commLink.disconnect()
 *
 */
commLink: Special
   
    /* 
     *   Our scope list must include all the actors we're currently connected
     *   to.
     */
    scopeList(actor)
    {
        local s = next();
        
        s.vec_ += connectionList.mapAll({x: x[1]});
        
        return s;
    }
    
    /* We can hear an actor if s/he's in our connection list */
    canHear(a, b)
    {
        /* 
         *   We assume that if a can hear b, b can hear a, but the link is only
         *   between the player character an another actor. If b is the player
         *   character swap a and b so that the tests that follow will still
         *   apply.
         */
        if(b == gPlayerChar)
        {
            b = a;
            a = gPlayerChar;
        }
        
        /* 
         *   If one of the actors is the player character and the other is in
         *   our connection list, then they can hear each other.
         */
        if(a == gPlayerChar && connectionList.indexWhich({x: x[1] == b}))
            return true;
        
        /* Otherwise use the next special. */
        return next();
    }
    
    canSee(a, b)
    {
        /* 
         *   We assume that if a can see b, b can see a, but the link is only
         *   between the player character an another actor. If b is the player
         *   character swap a and b so that the tests that follow will still
         *   apply.
         */
        if(b == gPlayerChar)
        {
            b = a;
            a = gPlayerChar;
        }
        
        /* 
         *   If one of the actors is the player character and the other is in
         *   our connection list with a video value of true, then they can see
         *   each other.
         */
        if(a == gPlayerChar && connectionList.indexOf([b, true]))
            return true;
        
        /* Otherwise use the next special. */
        return next();
    }
    
    canTalkTo(a, b)   
    {
        /* 
         *   We assume that if a can talk to b, b can talk to a, but the link is
         *   only between the player character an another actor. If b is the
         *   player character swap a and b so that the tests that follow will
         *   still apply.
         */
        if(b == gPlayerChar)
        {
            b = a;
            a = gPlayerChar;
        }
        
        /* 
         *   If one of the actors is the player character and the other is in
         *   our connection list, then they can talk to each other.
         */
        if(a == gPlayerChar && connectionList.indexWhich({x: x[1] == b}))
            return true;
        
        /* Otherwise use the next special. */
        return next();
    }
    
    /* 
     *   The list of actors we're currently connected to. This is a list of two
     *   element lists in the form [actor, video], where actor is the actor
     *   we're connected to and video is true or nil according to whether the
     *   link to that actor is a video link as well as an audio link.
     */
    connectionList = []
    
    /* This Special is active is there's anything in its connectionList. */
    active = connectionList.length > 0
    
    /* 
     *   Connect this comms link to other; if video is specified and is true,
     *   the comms links is also a video link.
     */
    connectTo(other, video = nil)
    {
        /* Add other to our connection list. */
        connectionList = connectionList.append([other, video]);
        
        /* Force the Special class to rebuild its list of active Specials. */
        Special.allActive_ = nil;
    }
    
    /* Disconnect this commLink from everyone */
    disconnect()
    {
        /* Empty our out connectionList */
        connectionList = [];
        
        /* Force the Special class to rebuild its list of active Specials. */
        Special.allActive_ = nil;
    }
    
    /* 
     *   Disconnect this commLink from lst, where lst may be a single actor or a
     *   list of actors.
     */
    disconnectFrom(lst)
    {
        /* Convert the lst parameter to a list if it isn't one already */
        lst = valToList(lst);
        
        /* 
         *   Reduce our connectionList to a subset of members that aren't in
         *   lst.
         */
        connectionList = connectionList.subset({x: lst.indexOf(x[1]) == nil});
        
        /* Force the Special class to rebuild its list of active Specials. */
        Special.allActive_ = nil;
    }
 
    
    /* 
     *   Give this Special a higher priority that the QSenseRegion Special so
     *   that it takes precedence when its active.
     */
    priority = 5
;

I’ve added this to the query.t file, so if you want to try it that’s where you may want to copy it to (then there won’t be any problems in upgrading to the next release when it eventually appears).

It’s possible that there are hidden snags, but that’s all the more reason for you to try it out to see if you can find them, since so far you’re proving to be my star beta-tester!

The comments should make it reasonably clear how to use it, but in essence when Harry answers the phone to Max, you need to call commLink.connectTo(max). If it’s also a video link and they can see each other, call commLink.connectTo(max, true). At the end of the call, use commLink.disconnect() or commLink.disconnectFrom(max).

One other point: if you want to establish a video link, the default response to X MAX will be that he’s too far away to see any detail. To can change this either by setting sightSize = large on the max object (which is fine if this is the only situation under which he’s viewed remotely) or by overriding remoteDesc(pov) on max (or his current ActorState) to give a description of what he looks like over the video connection.

– Eric

Excellent. Thanks, and I’m impressed by the turn around time on the remote connection problem. Careful, you’ll spoil us—we’ll come to expect code on demand. :slight_smile:

The fix for ImpByeTopic works great, but the canEndConversation() method works even better. Again, thanks, both for the fix and for the suggested implementation.

As for commLink, that appears to be working as well. I’ll give it a more rigorous workout as Harry and I continue to explore the library but for now it seems to be just what I needed.

Jerry