handleTopic (adv3Lite)

This is a question about what the library code is doing. Minor spoiler (for the game I may never finish) follows.

I have a group of NPCs (monkeys) that are implemented as a single Actor. Because the monkeys are not great conversationalists, I’ve written a DefaultAskTellTopic, which is defined on the Actor object itself, not within an ActorState:

[code]++ DefaultAskTellTopic, ShuffledEventList
[
'The monkeys give no sign that they have heard you. ',

    'The monkeys seem not to be great conversationalists. ',
    
    'Two of three of the monkeys look at you in vague puzzlement. One of them
    scratches its head and shrugs expressively. ',
    
    'It\'s no use --- the monkeys are not interested in verbal communication. '
]
shuffleFirst = nil

;[/code]
This works a treat – except when the player types ‘ask monkeys about themselves’. This produces the library’s default noResponseMsg, which is “The monkeys do not respond.” As nearly as I can tell, this output is coming from handleTopic on the Actor class.

What’s weird about this is that ‘ask monkeys about monkeys’ activates the DefaultAskTellTopic. But somehow, ‘themselves’ is being given special handling. This is not because ‘themselves’ is a not-understood word; if I ‘ask monkeys about aristotle’, Aristotle not being an object in the game, I get the stuff in my DefaultAskTellTopic.

If I’m understanding the library code in actor.t, it appears handleTopic is being invoked by handleCommand. But why that one command should end up there, when any other ask or tell command goes into my own Topic, is mysterious. I can write a method for noResponseMsg that will invoke my own Topic – that seems to solve the immediate problem – but this may be something that could be tinkered with in the library itself.

This happens because the library wasn’t testing for plural pronouns in this context. The fix is to add this test to the resolvePronouns() method of TopicTAction:

 resolvePronouns()
    {
        if(curIobj == nil)
            return;
        
        for(local cur in curIobj.topicList, local i = 1;; ++i)
        {
            if(cur == Him && curDobj.isHim)
                curIobj.topicList[i] = curDobj;
            
            if(cur == Her && curDobj.isHer)
                curIobj.topicList[i] = curDobj;
            
            if(cur == It && curDobj.isIt)
                curIobj.topicList[i] = curDobj;
            
            if(cur == Them && (curDobj.plural || curDobj.ambiguouslyPlural))  //ADD THIS
                curIobj.topicList[i] = curDobj;  // AND THIS
        }
    }

Got it … thanks!

I now have a new observation about the library:

Rather unexpectedly, travelVia(traveler) only works if the traveler is an Actor. I have a mechanical device in my game that can be moved around by remote control, but if I don’t make it an Actor, calling mechanicalDevice.travelVia(nextRoom) fails to move the device. It also doesn’t issue an error message, even though the travelVia method is not defined on the mechanical device object.

This probably isn’t a problem in my game. I removed Actor from the class list for fairly trivial reasons, so I’ll just put it back. But I’m wondering whether, in the next release of adv3Lite, it might be useful to move the travelVia code to Thing. Of course, that might have undesirable side effects…

I’ll need to look into that more carefully. There is one potentially undesirable side effect involving a possible collision of method names. As things stand the travelVia() method on Actor calls the travelVia() method on the relevant travel connector, and there’s no risk of name collision since a TravelConnector can never be an Actor. But a travel connector can be a Thing, so that if travelVia() is moved from Actor to Thing a travel connector that’s both a TravelConnector and a Thing will have two different classes to inherit different definitions of travelVia() from. Which it inherits will then depend on which superclass is listed first in the definition of the travel connector class or object in question. Perhaps this won’t matter too much in practice, provided the TravelConnector class is always listed first, but it looks just a little too much like an accident waiting to happen for me to be entirely comfortable with it.

One could rename one of the travelVia() methods to something else to avoid the clash, but this would probably break a lot of existing code, so I’m inclined to think it’s more trouble than it’s worth. It would also require defining sayDeparting() and sayArriving() methods on Thing.

I’ll take a look at it, however. One safeguard might be to right a check into the travelVia() method of Thing to check whether the object/class is also a subclass of TravelConnector (but that could turn out to be an unreliable kludge).

Probably my situation, with the remote-controlled traveling device, is an edge case. Making it an Actor is not too difficult, and may even be useful, as it’s a robot, so the cannotKissMsg and other Actor-oriented responses may be needed.

I thought it was an odd limitation, but yes, I can see that there would be problems with the clashing methods of the two classes.

It occurs to me that the way to deal with this, if it’s worth addressing, would be to add a new Traveler class, which would be a subclass of Thing and a superclass of Actor; travelVia() and its supporting methods would then be moved to this new Traveler class, which would allow them to be used on a non-Actor object without any fear of their clashing with the similarly named methods on TravelConnector. What I’m not so sure, however, is whether this is actually worth doing, not because it would be particularly difficult (it wouldn’t), but because it would add to the complexity of the library (one more class to explain), contrary to the design philosophy of adv3Lite, with little corresponding gain, since, as Jim says, this is an edge case. How often will there be games that need a class for something that can move around by itself but that can’t be represented by an Actor object? I suspect that this will be so rare that it can be left to the authors of any such games to roll their own Traveler class (it not being the role of a library to provide a ready-made solution for every conceivable situation game authors may wish to model).

Does anyone dissent from this view? Am I misunderestimating (!) the need for such a Traveler class?