yes, no topics combined in single YesList (adv3Lite)

I want to be able to control the order in which suggested topics are listed and I want the order to be configurable for each topic. Since the order in which topics are listed is hardcoded in actor.t, I’ve replaced the suggestedTopicLister with a modified version that takes the order of output from a list that I maintain in my own dataStore.t file.

That’s all working. But I had to jump through an unexpected hoop to make it so.

The lister depends on individual lists of topics, by type. There is an askList that contains all AskTopic objects that are valid for the topic being processed, a tellList for TellTopic objects, etc., etc., etc., including a yesList for YesTopic objects and a noList for NoTopic objects.

But the yesList contains both YesTopic and NoTopic objects and the noList is empty even though there is a valid NoTopic (included in the yesList).

I’ve worked around this by parsing the yesList and sending YesTopics to a newYesList and NoTopics to a newNoList, but it seems like it might be a bug in the library, or else there’s some reason for combining the two that I’m not seeing.

But if so, why a separate yesList and noList?

Jerry

This is bug, albeit a relatively minor and normally harmless one, and I’ve now fixed it.

In the course of poking around, I then found a couple of other bugs with listing suggested topics (namely that ShowTopics and CommandTopics were not being listed). These have now been fixed as well.

At the same time I’ve added a listOrder property to ActorTopicEntry which gives some control over the order in which individual topic entries are listed (within each group of entries of the same type) in a list of suggested topics, and I’ve made suggestedTopicLister slightly less hard-coded in its approach to listing suggested topics by tidying up the code that does it so that it’s not so repetitive (though I’ve left the old code there for now, commented out, just in case), which would in principle allow a game author to tinker with the ordering of groups of suggestions by overriding the typeInfo property and changing the order of its elements.

All these changes have been incorporated into the version I’ve just uploaded to GitHub. Let me know when you find further bugs!

Great. Thanks.

I retrieved a new library master from GitHub and your changes work, at least as far as topic order goes (haven’t looked at the other changes).

However, there is one problem.

Admittedly, it may be a corner case with limited exposure beyond my particular situation but for me it’s a deal breaker, especially since I already have a working solution in place that does not rely on the changes you made.

The problem is a timing issue.

I have a conversation going between gPlayerChar and an NPC. The NPC takes on one of four personas depending on previous choices made by gPlayerChar. Three of these personas are male, one female, which necessitates a midstream setting of the isHim/isHer properties of the NPC Actor object.

The determination of which persona is to be activated is made in a SayTopic, for which there are two suggested topics to be displayed—Give or Ask about… This is where I wanted to control the order of topics, which was my motivation for replacing the default SuggestedTopicLister.

So, I have two things going on in this one SayTopic—there is call to a setGender() function that sets the NPC’s isHer/isHim properties appropriately, and there is a call to a setTopicOrder() function that lets me move the GiveTopic to the front of the line.

Using your new code, I can do this by redefining the typeInfo list.

That now works fine, and thank you for it.

But therein lies a glitch, as well.

When the game player invokes the SayTopic, here’s what goes to the game window…

The order of the suggested topics is correct, it’s the order in my new typeInfo list—GiveTopic first—but gender in the suggestion text is wrong—not her when it should be him, but a neutral it.

I know the setGender() function works because a breakpoint in the setTopicOrder() function shows the correctly updated gender before the typeInfo list is modified.

This does not happen in my replacement SuggestedTopicLister. I don’t alter the contents of the typeInfo list. Instead, I define a new topicOrder list containing text labels in my dataStore.t file, and I’ve added a switch() to the lister to output topics in my preferred order.

For some reason, this approach avoids the timing issue that seems to be setting the gender text in the output string before my setGender() function can do its work.

I don’t really need a library fix, I recognize that this may be a bit too much of a corner case to justify it. I’m just passing on a data point regarding how the library is working.

Jerry

That’s interesting to know, so out of curiosity I tried to reproduce it (without redefining the typeInfo list). I don’t know how you defined your SayTopic, but this is how I defined mine:

++ SayTopic 'Lucy sent me'
    topicResponse()
    {
        getActor.isHim = nil;
        getActor.replaceVocab('Freda;;woman;her');
        "<q>Lucy sent me,</q>\b
        <q>Well, call me a girl,</q> she says. <.topics>";       
        
    }    
;

The output I got was:

room
The room, with a locker. 

Fred is here. 

On the table you see a box and a clock. In the box you see a pin.

>talk to fred
Fred does not respond. 

(You could say Lucy sent me or say sorry; or ask him about himself or you; or tell him about yourself; or show him the pin; or say yes; or say no; or tell him to jump)

>lucy sent me
“Lucy sent me,”

“Well, call me a girl,” she says. 

(You could say sorry; or ask her about himself or you; or tell her about yourself; or show her the pin; or say yes; or say no; or tell her to jump)

>l
room
The room, with a locker. 

Freda is here. 

On the table you see a box and a clock. In the box you see a pin.

So I can’t immediately reproduce your problem; with my sample code the gender is changed correctly. As a matter of interest, how did you define your SayTopic?

Here’s the SayTopic

References to dataStore.mentor retrieve the name appropriate to the persona being activated.

My setTopicOrder(lst) function with code to handle either my lister or your lister, currently set for my lister…

[code]setTopicOrder(lst)
{
// set the order in which suggested topics are listed during conversations;
// see dataStore
// suggestedTopicLister.typeInfo = lst;
dataStore.topicOrder = lst;
}

[/code]

…and the setMentorGender() function, which is defined on the Actor object…

setMentorGender(gender) { switch(gender) { case 'him': isHim = true; isHer = nil; break; case 'her': isHim = nil; isHer = true; break; default: isHim = true; } }

…your typeInfo list reordered to move Give, yes and no to the front of the line (this list was created by cut and paste from your original)…

[code] giveFirst = [
[&giveList, &giveTopics, GiveTopic, BMsg(give, 'give {him interlocutor} ')],
[&yesList, &miscTopics, YesTopic, nil],
[&noList, &miscTopics, NoTopic, nil],
[&sayList, &sayTopics, SayTopic, nil],
[&queryList, &queryTopics, QueryTopic, BMsg(ask query, 'ask {him interlocutor} ')],
[&askList, &askTopics, AskTopic, BMsg(ask about, 'ask {him interlocutor} about ')],
[&tellList, &tellTopics, TellTopic, BMsg(tell about, 'tell {him interlocutor} about ')],
[&talkList, &talkTopics, TalkTopic, BMsg(talk about, 'talk about ')],
[&showToList, &showTopics, ShowTopic, BMsg(show, 'show {him interlocutor} ')],
[&askForList, &askForTopics, AskForTopic, BMsg(ask for, 'ask {him interlocutor} for ')],
[&commandList, &commandTopics, CommandTopic, BMsg(tell to, 'tell {him interlocutor} to ')]

]

[/code]

…and the topicOrder list used by my lister…

giveFirstTopicOrder = [ 'give', 'say', 'ask', 'query', 'ask for', 'tell', 'yes', 'no', 'show to', 'talk', 'command' ]

My revision of the suggestedTopicLister code is kind of long for posting, so I’ve attached it instead (but had to change the .t file extension to .txt to pass muster with the Forum’s file attachment filter). Other than an #ifdef __DEBUG warning at line 16, the interesting code starts at line 166.

Jerry
topic_order.txt (17.7 KB)

Thanks for the further code samples. I can’t see anything immediately obvious that would be causing the effect you describe, so I shall have to do some deeper poking around. I just thought I’d warn you that might take a while, since it doesn’t seem to be particularly urgent and there’s some other adv3Lite work I’m in the middle of right now.

Thanks.

When you do, you might want to focus on the timing of what happens when during the construction of the suggestion list.

The isHim/isHer properties of the Actor object are being handled correctly. A breakpoint in setTopicOrder() shows that the gender issue has been correctly resolved before the new typeInfo list is declared.

And while the displayed suggested topic says Give it the letter, the command Give him the letter works.

And finally, subsequent topic suggestions use the correct pronoun (him not it) without any further action on my part.

My conclusion from this chain of events is that changing the typeInfo list seems to somehow upset TADS’ timing in the construction of the suggested topics list text.

No, not urgent at all. My solution works fine.

Jerry

I’ve worked out what the problem is, and it’s a good thing you caught it. It turns out that it’s not just peculiar to what you’re trying to do – it would have afflicted any game in which the player character address an NPC of one gender and subsequently addressed an NPC of another gender. The ‘him’ or ‘her’ of in suggested topics would be stuck at the gender of the first NPC addressed for the duration of the game. This is a side-effect of the way TADS 3 evaluates lists; the consequence is that all those BMsg() macros with their {him interlocutor} parameter substitutions get replaced with constant values (‘him’, ‘her’ or ‘it’) that are then used throughout the game.

I think the way to fix it is to change the definition of suggestedTopicLister.typeInfo thus:

typeInfo = [
        [&sayList, &sayTopics, SayTopic, nil],
        [&queryList, &queryTopics, QueryTopic, &queryPrefix],        
        [&askList, &askTopics, AskTopic, &askPrefix],
        [&tellList, &tellTopics, TellTopic, &tellPrefix],
        [&talkList, &talkTopics, TalkTopic, &talkPrefix], 
        [&giveList, &giveTopics, GiveTopic, &givePrefix],
        [&showToList, &showTopics, ShowTopic, &showPrefix],
        [&askForList, &askForTopics, AskForTopic, &askForPrefix],
        [&yesList, &miscTopics, YesTopic, nil],
        [&noList, &miscTopics, NoTopic, nil],
        [&commandList, &commandTopics, CommandTopic, &tellToPrefix]
        
    ]

 sayPrefix = BMsg(say prefix, 'say ')
    queryPrefix = BMsg(ask query, 'ask {him interlocutor} ')
    askPrefix = BMsg(ask about, 'ask {him interlocutor} about ')
    tellPrefix = BMsg(tell about, 'tell {him interlocutor} about ')
    talkPrefix = BMsg(talk about, 'talk about ')    
    givePrefix = BMsg(give, 'give {him interlocutor} ')
    showPrefix = BMsg(show, 'show {him interlocutor} ')
    askForPrefix = BMsg(ask for, 'ask {him interlocutor} for ')
    tellToPrefix = BMsg(tell to, 'tell {him interlocutor} to ')

This requires a corresponding tweak in the showSection() method of the same lister, which now needs to read:

showSection(prop, listStarted, sectionIntro)
    {
        local lst = self.(prop);
        
        /* 
         *   If the list is empty return nil to tell our caller we haven't
         *   displayed anything.
         */
        if(lst.length == 0)
            return nil;
        
        /* If the list has already begun, show our list separator */
        if(listStarted)
            say(orListSep);
        
        /* Show the appropriate intro for this section. */
        if(sectionIntro)
            say(self.(sectionIntro)); // THIS IS THE LINE THAT CHANGES
        
        /* Show the list */
        showList(lst);
        
        /* Tell our caller we've displayed something. */
        return true;       
    }

A quick test suggest this prevents the premature evaluation, since the parameter substitutions now stay outside the list (we just use pointers to them instead).

If you wanted to use this, you’d obviously need to make the corresponding change to your giveFirst list.