TMI in contents listing with remapIn (adv3Lite)

I’m getting too much information in a contents listing of an object that uses remapIn/On.

I have a locker, with a main compartment, plus a shelf inside and a label identifying the owner on the outside.

In order to make both inside and outside functional, the locker uses remapIn and remapOn subcomponents.

The locker contains stuff, more stuff and still more stuff.

The shelf—the &remapIn subcomponent—contains stuff on the shelf, more stuff on the shelf, and still more stuff on the shelf.

The &remapOn subcomponent is the label on the outside of the locker.

When the player enters look in locker on the command line, there is no response. Nothing.

So, I have added a dobjFor(LookIn) to the remapIn code of the locker, in which I have custom code for itemizing contents.

Initially, I tried the following…

                    local list =  makeListStr(majorBerthingLocker);
                    "contains ";
                    say(list);
                    ". <.p>";

This produced the following…

Turns out, the library-defined makeListStr() code generates a list that itemizes every object in the source object’s contents list—including the SubComponent objects, which are both identified using the name of the locker, which is the the parent container of the remapped subcomponents. So, the first two entries in my list of contents of locker B are locker B and locker B.

As a workaround, I have defined my own list generator…

getReMapContainerContents(obj)
{
    local ret = [];
    foreach(local item in obj.contents)
    {
        if(!item.ofKind(SubComponent))
           ret = ret.append(item);
    }
        return makeListStr(ret);
}

…that suppresses items of kind SubCompenent from the generated list.

But shouldn’t the library be doing all of this for me?

I see two things wrong here: a) The library does not generate any output for the LookIn command, and b) the library’s makeListStr() generates a list that contains clearly unlistable objects.

Or, maybe I’m just doing it wrong.

Is there a more correct way to do what I’m trying to do (create a locker with both inside and outside elements)?

Thanks.

Jerry

BTW, here’s a working example from my test bed, saved for last because it’s kind of long…

#charset "us-ascii"

#include <tads.h>
#include "advlite.h"

versionInfo: GameID
    IFID = '445C38A3-AD1B-4729-957A-F584600DE5C1'
    name = 'test'
    byline = 'by Jerry Ford'
    htmlByline = 'by <a href="mailto:jerry.o.ford@gmail.com">
                  Jerry Ford</a>'
    version = '1'
    authorEmail = 'Jerry Ford <jerry.o.ford@gmail.com>'
    desc = 'Testing remapIn.'
    htmlDesc = 'Testing remapIn.'

;

gameMain: GameMainDef
    initialPlayerChar = me
    paraBrksBtwnSubcontents = nil
   
;

me: Actor 'me' @room
    "The main man.<.p>"
    isHim = true
    
    person = 2
;

room: Room 'room'
    "The room, with a locker. <.p>"
;
    
+ majorBerthingLocker: Thing, Fixture '() locker B;;locker'
    "A locker stretches from deck to overhead adjacent to one of the sleeping
    sacks."

    listOrder = 2
    
    contType = In
    
    remapIn: SubComponent 
    {
        isOpenable = true 
        dobjFor(LookIn)
        {
            action()
            {
                "The locker is divided by a shelf. The main compartment ";
                if(majorBerthingLocker.contents.length == 0)
                    reportAfter('is empty.<.p>');
                else
                {
                    
                    // using library's makeLstStr()
                    local list = makeListStr(majorBerthingLocker.contents);
                    
                    // using my custom getReMapContainerContents()
//                    local list = getReMapContainerContents(majorBerthingLocker);
                    "contains ";
                    say(list);
                    ". <.p>";
                }
                "The shelf ";
                if(majorBerthingLockerShelf.contents.length == 0)
                     reportAfter('is empty.<.p>');
                else
                {
                    local list = makeListStr(majorBerthingLockerShelf.contents);
                    "contains ";
                    say(list);
                    ". <.p>";
                }
                inherited;
            }
        }
    }
    remapOn: SubComponent { }
;
++ stuff: Thing '() stuff'
    "Stuff in the locker. <.p>"
    isListed = nil
;
++ moreStuff: Thing '() more stuff'
    "More stuff in the locker. <.p>"
    isListed = nil
;
++ stillMoreStuff: Thing '() still more stuff'
    "Still more stuff in the locker. <.p>"
    isListed = nil
;

++ majorBerthingLockerLabel: Thing, Fixture '() labelabel B;yellow;large red b'
    "On the yellow label in the center of the locker door, you read the name of
    the locker's owner."
    isListed = nil
    isReadable = true
    readDesc = "<center><font size=+2 color=red
        bgcolor=white>B</font><table><tr><td bgcolor=yellow align=center><font
        color=black>MAJOR
        otherSaturnExplorer.lastName\bUSAF</font></td></tr></table></center>"
    listOrder = 2
    subLocation = &remapOn
;
++ majorBerthingLockerShelf: Platform, Fixture 'shelf;;() locker B shelf'
    "A shelf in the locker contains otherSaturnExplorer.firstName's personal belongings. <.p>"
    
    disambigName = 'locker B shelf'
    
    subLocation = &remapIn
    dobjFor(Search)
    {
        action()
        {
            local list = makeListStr(majorBerthingLockerShelf.contents);
            "The shelf holds ";
            say(list);
            ". <.p>";
        }
    }
;
+++ stuffOnShelf: Thing '() stuff on shelf'
    "Stuff on the shelf. <.p>"
    isListed = nil
;
+++ moreStuffOnShelf: Thing '() more stuff on shelf'
    "More stuff on the shelf. <.p>"
    isListed = nil
;
+++ stillMoreStuffOnShelf: Thing '() still more stuff on shelf'
    "Still more stuff on the shelf. <.p>"
    isListed = nil
;

getReMapContainerContents(obj)
{
    local ret = [];
    foreach(local item in obj.contents)
    {
        if(!item.ofKind(SubComponent))
           ret = ret.append(item);
    }
        return makeListStr(ret);
}

No; makeListStr(lst) simply formats a list of whatever it’s passed in the lst parameter. That’s it’s job. If you call it as makeListStr(obj.contents) then you will, of course, get a list itemizing every object in the source object’s contents list, since that’s what you asked for. That’s just makeListStr() doing its job. If you want something different you need to pass a different argument.

Well, the library defines a listableContents property you could use for this purpose (instead of contents), or you could work even more with the flow of the library by using the various Listers that are there to handle this kind of stuff.

Well, I’d certainly go about it a bit differently. As a first shot, which you’ll probably have to tweak a bit further to make it work just how you want, I’d do something like this:

+ majorBerthingLocker: Fixture '() locker B;'
    "A locker stretches from deck to overhead adjacent to one of the sleeping
    sacks."

    listOrder = 2      
    
    remapIn: SubComponent 
    {
        isOpenable = true 
        
        myLookInLister: lookInLister
        {
            showListPrefix(lst, pl, parent)            
            {                
                "<<commonDesc>> contains ";        
            }          
            
            
            showListEmpty(lst, pl, parent)
            {
                "<<commonDesc>> is empty. ";
            }
            
             commonDesc = "The locker is divided by a shelf. The main
                 compartment "
        }       
 
    }

;
++ stuff: Thing '() stuff'
    "Stuff in the locker. <.p>"
//    isListed = nil
    subLocation = &remapIn
;
++ moreStuff: Thing '() more stuff'
    "More stuff in the locker. <.p>"
//    isListed = nil
    subLocation = &remapIn
;
++ stillMoreStuff: Thing '() still more stuff'
    "Still more stuff in the locker. <.p>"
//    isListed = nil
    subLocation = &remapIn
;

++ majorBerthingLockerLabel: Component '() label B;yellow;large red b'
    "On the yellow label in the center of the locker door, you read the name of
    the locker's owner."
    isListed = nil
    isReadable = true
    readDesc = "<center><font size=+2 color=red
        bgcolor=white>B</font><table><tr><td bgcolor=yellow align=center><font
        color=black>MAJOR
        otherSaturnExplorer.lastName\bUSAF</font></td></tr></table></center>"
    listOrder = 2    
;

++ majorBerthingLockerShelf: Surface, Fixture 'shelf; (locker) (B)'
    "A shelf in the locker contains otherSaturnExplorer.firstName's personal
    belongings. <.p>"
   
   
    disambigName = 'locker B shelf'
    
    subLocation = &remapIn
    
    myLookInLister: lookInLister
    {
        showListPrefix(lst, pl, parent)            
        {                
            "The shelf holds ";        
        }  
    }
    
    /* 
     *   If I refer to SHELF I'm more likely to mean this object, the shelf,
     *   that any of the STUFF ON SHELF objects.
     */
    vocabLikelihood = 10

    
    
;
+++ stuffOnShelf: Thing '() stuff on shelf'
    "Stuff on the shelf. <.p>"
//    isListed = nil
;
+++ moreStuffOnShelf: Thing '() more stuff on shelf'
    "More stuff on the shelf. <.p>"
//    isListed = nil
;
+++ stillMoreStuffOnShelf: Thing '() still more stuff on shelf'
    "Still more stuff on the shelf. <.p>"
//    isListed = nil
;

The label isn’t on top of the Locker, so you don’t need to put it in a remapOn object (that would represent the top surface of the locker if it was possible to put things on top of it). The label is simply an external component. But you then mustn’t make the locker a container (with contType = In); that’s the job of its remapIn SubComponent, and your code has a bit of a tendency to problematic duplication. You also need to be more careful with your vocab properties (although I appreciate this is only testbed code). For example there’s hardly ever a good reason to include the same word more than once, as you often seem to do. Also, be careful that you don’t (as your code does) make the shelf and the locker both respond to ‘Locker B’ as nouns, since then LOCKER by itself will refer to the shelf as well as the Locker, with odd results.

You’ll see how I’ve dealt with these and other points in the code above, which also goes more with the grain of the library by making use of (customized) listers.

That said, in order to make it work precisely as I wanted, I had to make a few library tweaks, which are now uploaded to GitHub. These are mainly to make the listing of contents of contents use the Lister appropriate to the immediate container, rather than the one at the top of the chain.

Eric:

Thank you. Your changes to my code appear to be just what I needed. It works much better now. There is one small problem, however. Your addition of a vocabLikelihood value does not seem to do anything…

With that code in place, I get…

I’ve tried adding lower vocabLikelihood (5, -5) values to the individual objects on the shelf, but the results are always the same.

This is not likely to be a problem in my game, since the items that are on the shelf will have unique names that won’t conflict with the shelf vocab property (as opposed to this test bed code), but still, it seems like the vocabLikelihood property would be a neat tool to have in reserve if it works.

Jerry

Actually, vocabLikelihood is working fine, as you’ll see if you try X SHELF or SEARCH SHELF. The problem is that adv3Lite doesn’t define a LOOK ON command, so LOOK ON SHELF is interpreted as EXAMINE ON SHELF, which then has to disambiguate between all the objects with ‘on shelf’ in their vocab, hence “Which do you mean, still more stuff on shelf, more stuff on shelf, or stuff on shelf?”