Lister Problem (adv3Lite)

This is rather mysterious, and stepping through the code in the debugger – an activity at which, admittedly, I am not skilled – has failed to reveal an explanation.

I’ve created a slightly modified Lister for a container (the PC’s purse object) in order to display, when the purse is opened, a mirror mounted beneath the flap of the purse. Code for the modified Lister is below. Here’s the output:

What’s happening is that showListEmpty() is being called, even though the list of contents was NOT empty. Here’s the customized Lister:

purseOpeningContentsLister: openingContentsLister showListPrefix(lst, pl, parent) { gMessageParams(parent); "In addition to the mirror mounted beneath the flap, your purse contain{s/ed} "; } showListEmpty(parent) { "Your purse is empty, except for the mirror mounted beneath the flap. "; } ;
This is used both as the OpeningContentsLister and the LookInContentsLister for the purse, but I don’t see how that could be causing the problem, because the list would not be empty in either case. Naturally, the Thing class has been modified so that myOpeningContentsLister is used in place of the default, but by default, myOpeningContentsLister = openingContentsLister. Only in the purse object have I swapped in my own Lister.

That all seems to work as desired. And when I step through the code, I can see in the Watch Expressions pane in Workbench that the lst parameter has a length of 3, so the else{} block that calls showListEmpty() should not be used. And yet it’s being used.

What am I doing wrong?

But wait … it’s worse than that! Consider this transcript. I’ve purloined a couple of random items from the game, and I’m going to put them in the purse:

If I take the phone from the purse, leaving only the wallet and car key (which start the game in that location), the extra message doesn’t print. But it’s not just any two items, because if I manipulate the contents so that the purse contains only the feathered dart and the Easter egg, I still see the showListEmpty message. And if there are excess items in the purse, I’ll see it TWICE.

It’s very hard for me to see how anything in my code could be causing this. The one tricky bit is that the mirror mounted beneath the flap is ALWAYS in the purse – it’s a Component – so the contents list is never empty. It’s possible that at some point I wrote some code to reduce the length of the contents list by 1 (that would have made sense, I think), and I might have introduced a bug at that point, but I’ll be darned if I can find the code where I did that.

The solution to the problem appears to be trivial:

showListEmpty(parent) { if (purse.contents.length() == 1) "Your purse is empty, except for the mirror mounted beneath the flap. "; }
But that’s a kludge. I shouldn’t have to do that. That test should already have been carried out by the Lister.

Finding where you altered the contents of purse should be easy…

grep purse.contents *.t

…assuming you have the open source grep utility installed.

Jerry

I don’t need grep. Workbench cheerfully searches through the author’s project source files. I’m definitely not using that list or adjusting its calculated length at any point. There is no way showListEmpty should be firing at all, as far as I can see, let alone firing twice.

If it works for you, great, but I don’t fully trust the Workbench search dialog. I’ve had issues with it when using replace. Doesn’t always replace what I expect it to replace.

I wasn’t able to reproduce this behaviour with your definition of purseOpeningContentsLister. Perhaps there’s something else going on in your code? Ho did you define the purse object, for example?

I’ve figured it out … sort of. The difficulty, which may indeed be a library bug, has something to do with what happens when an object to be listed has a component. To see the difference, run the code below before and then after commenting out the photoListObject, which is a component of the cell phone object (which is in the purse). When photoListObject is present, the lister thinks the list is empty, even though it isn’t. At first I thought it might be because the makeupMirror is a component of the purse object itself, but commenting that out doesn’t prevent the problem – it’s when there’s an object in the container that has a component.

[code]Test ‘me’ [‘open purse’];

modify Thing
dobjFor(Open) {
action() {
makeOpen(true);
if(!gAction.isImplicit) {
unmention(contents);
listSubcontentsOf(self, myOpeningContentsLister);
}
}
}
myOpeningContentsLister = openingContentsLister

dobjFor(LookIn) {
    action() {     
        if(contType == In) {            
            if(hiddenIn.length > 0)                
                moveHidden(&hiddenIn, self);                    
            if(contents.length == 0)
                display(&lookInMsg);                    
            else
            {
                unmention(contents);
                if(gOutStream.watchForOutput(
                    {: listSubcontentsOf(self, myLookInLister) }) == nil)
                  display(&lookInMsg);       
            }
        }
        
        else if(hiddenIn.length > 0)            
            findHidden(&hiddenIn, In);                               

        else
            display(&lookInMsg);
    }
}
myLookInLister = lookInLister

;

purseOpeningContentsLister: openingContentsLister
showListPrefix(lst, pl, parent) {
gMessageParams(parent);
"In addition to the mirror mounted
beneath the flap, your purse contain{s/ed} ";
}
showListEmpty(parent) {
// if (purse.contents.length() == 1)
"Your purse is empty, except for the mirror mounted beneath the flap. ";
}
;

purse: OpenableContainer ‘purse; leather scuffed my; pocketbook handbag bag flap’ @me
"It’s not your favorite purse — the leather is a bit scuffed. You grabbed it
in a hurry on your way out the door, without loading it with the assortment of
things you usually carry — only the bare necessities. << if isOpen>>Attached
to the underside of the flap is a makeup mirror. "
theName = ‘your purse’
aName = ‘your purse’
bulkCapacity = 10
bulk = 15
openStatusReportable = nil
myOpeningContentsLister = purseOpeningContentsLister
myLookInLister = purseOpeningContentsLister
;

  • makeupMirror: Component ‘makeup mirror’
    "The makeup mirror is mounted beneath the flap of the purse. It’s rectangular,
    and large enough that you can see a good portion of your face in it, or check your hair. "
    hideFromAll(action) {
    return true;
    }
    lookInMsg = 'By holding the mirror at just the right angle, you can see
    yourself. ’
    cannotTakeMsg = 'You can’t remove the mirror; it’s part of the purse. ’
    ;

  • cellPhone: Thing ‘cell phone; smart’
    "Your cell phone is a couple of years old, and its link to a carrier has become almost completely
    unreliable, to the point where you don’t even think about using it as a phone anymore. It’s still
    handy as a camera, however. You can take pictures with it, and then look at the
    pictures. "
    photoList = []
    showPhotoList() { }
    addToPhotos(x) { }
    ;

// Comment out the photoListObject to make the lister problem go away:

++ photoListObject: Component
‘photo display ;video graphic LCD; picture pictures photos photograph photographs snapshot snapshots’
desc {
local len = cellPhone.photoList.length();

    if (len == 0) "You haven't taken any photos yet. ";
    else {
        "In your cell phone's video display, you can
        see <<if (len == 1)>>a photo<<else>>photos<<end>> of ";
        cellPhone.showPhotoList();
    }
}

;[/code]

Your analysis is essentially correct, and there is a library bug here which I can now reproduce. Basically it’s occurring because the library is attempting to recursively list contents of contents and then giving the showListEmpty() response when there’s nothing to list for the contents of the camera. The solution is reasonably simple but a bit fiddly to describe since it involves tweaks in several places. I’ve just uploaded the fixed version to GitHub.