Tri-Part Inventory?

Okay, I knew that monkeying around with the inventory settings would be complicated, but I’d thought I’d had it mostly figured out. Basically, I wanted to tweak the inventory lister so that it shows three subdivisions, not just two, so that you’d get a read-out like:

However, upon testing, I find that my code seems to choke if check inventory while I’m holding the sword. Not even wielding, mind you, just holding it. And

it’s only the weapon; the gem gets a free pass, but holding a sword results in “Wrong Number of Arguments”. I’m stumped. What am I missing? :frowning:

Here’s my code:

#charset "us-ascii"
#include <adv3.h>
#include <en_us.h>

/*A hack to modify the Inventory Lister.*/

modify ThingState
    wieldedName(lst) { return listName(lst); }
;

modify Thing
showWieldedItem(options, pov, infoTab)
    {
        /* show the item, using the worn-listing state name */
        showListItemGen(options, pov, infoTab, &wieldedName);
    }
    showWieldedItemCounted(lst, options, pov, infoTab)
    {
        /* show the item, using the worn-listing state name */
        showListItemCountedGen(lst, options, pov, infoTab, &wieldedName);
    }
;

class WieldingLister: InventoryLister
    /* show the list item using the "worn listing" name */
    showListItem(obj, options, pov, infoTab)
        { obj.showWieldedItem(options, pov, infoTab); }
    showListItemCounted(lst, options, pov, infoTab)
        { lst[1].showWieldedItemCounted(lst, options, pov, infoTab); }
;

class WieldingSublister: WieldingLister
    /* don't show any prefix, suffix, or 'empty' messages */
    showListPrefixWide(itemCount, pov, parent) { }
    showListSuffixWide(itemCount, pov, parent) { }
    showListEmpty(pov, parent) { }

    /* don't show out-of-line contents */
    showSeparateContents(pov, lst, options, infoTab) { }
;

actorWieldingSublister: WieldingSublister;

modify actorInventoryLister
    /*
     *   Show the combined inventory listing, putting together the raw
     *   lists of the items being carried and the items being worn. 
     */
    showCombinedInventoryList(parent, carrying, wearing, wielding)
    {
        /* if one or the other sentence is empty, the format is simple */
        if (carrying == '' && wearing == '' && wielding == '')
        {
            /* the parent is completely empty-handed */
            showInventoryEmpty(parent);
        }
        else if (carrying == '' && wielding == '')
        {
            /* the whole list is being worn */
            showInventoryWearingOnly(parent, wearing);
        }
        else if (wearing == '' && wielding == '')
        {
            /* the whole list is being carried */
            showInventoryCarryingOnly(parent, carrying);
        }
        //HAX
        else if (carrying == '' && wearing == '')
        {
            /* the whole list is being worn */
            showInventoryWieldingOnly(parent, wielding);
        } 
        //Odd one out:
        else if (carrying == '' )
        {
            /* the whole list is being worn */
            showInventoryCarryNot(parent, wearing, wielding);
        }    
        else if (wearing == '' )
        {
            /* the whole list is being worn */
            showInventoryWearNot(parent, carrying, wielding);
        }           
        else if (wielding == '' )
        {
            /* the whole list is being worn */
            showInventoryWieldNot(parent, carrying, wearing);
        }           
        else
        {
            /*
             *   ALL listings are populated.  Count the number of
             *   comma-separated or semicolon-separated phrases in each
             *   list.  This will give us an estimate of the grammatical
             *   complexity of each list.  If we have very short lists, a
             *   single sentence will be easier to read; if the lists are
             *   long, we'll show the lists in separate sentences.  
             */
            if (countPhrases(carrying) + countPhrases(wearing) + countPhrases(wielding)
                <= singleSentenceMaxNouns)
            {
                /* short enough: use a single-sentence format */
                showInventoryShortLists(parent, carrying, wearing, wielding);
            }
            else
            {
                /* long: use a two-sentence format */
                showInventoryLongLists(parent, carrying, wearing, wielding);
            }
        }
    }

    /*
     *   Count the noun phrases in a string.  We'll count the number of
     *   elements in the list as indicated by commas and semicolons.  This
     *   might not be a perfect count of the actual number of noun phrases,
     *   since we could have commas setting off some other kind of clauses,
     *   but it nonetheless will give us a good estimate of the overall
     *   complexity of the text, which is what we're really after.  The
     *   point is that we want to break up the listings if they're long,
     *   but combine them into a single sentence if they're short.  
     */
    countPhrases(txt)
    {
        local cnt;
        
        /* if the string is empty, there are no phrases at all */
        if (txt == '')
            return 0;

        /* a non-empty string has at least one phrase */
        cnt = 1;

        /* scan for commas and semicolons */
        for (local startIdx = 1 ;;)
        {
            local idx;
            
            /* find the next phrase separator */
            idx = rexSearch(phraseSepPat, txt, startIdx);

            /* if we didn't find it, we're done */
            if (idx == nil)
                break;

            /* count it */
            ++cnt;

            /* continue scanning after the separator */
            startIdx = idx[1] + idx[2];
        }

        /* return the count */
        return cnt;
    }

    phraseSepPat = static new RexPattern(',(?! and )|;| and |<rparen>')

    /*
     *   Once we've made up our mind about the format, we'll call one of
     *   these methods to show the final sentence.  These are all separate
     *   methods so that the individual formats can be easily tweaked
     *   without overriding the whole combined-inventory-listing method. 
     */
    showInventoryEmpty(parent)
    {
        /* empty inventory */
        "<<buildSynthParam('The/he', parent)>> {is} empty-handed. ";
    }
    //HAX
    showInventoryWieldingOnly(parent, wielding)
    {
        /* we're carrying nothing but WIELDING some items */
        "<<buildSynthParam('The/he', parent)>> {is} wielding <<wielding>>, but {has} nothing else besides. ";        
    }
    showInventoryWearingOnly(parent, wearing)
    {
        /* we're carrying nothing but wearing some items */
        "<<buildSynthParam('The/he', parent)>> {is} carrying nothing,
        and {is} wearing <<wearing>>. ";
    }
    showInventoryCarryingOnly(parent, carrying)
    {
        /* we have only carried items to report */
        "<<buildSynthParam('The/he', parent)>> {is} carrying <<carrying>>. ";
    }
    showInventoryCarryNot(parent, wearing, wielding)
        {
          if (countPhrases(wearing) + countPhrases(wielding)
                <= singleSentenceMaxNouns)
            {
                /* short enough: use a single-sentence format */
                showInventoryShortLists1(parent, wearing, wielding);
            }
            else
            {
                /* long: use a two-sentence format */
                showInventoryLongLists1(parent, wearing, wielding);
            }
    }
    showInventoryWearNot(parent, carrying, wielding)
        {
         if (countPhrases(carrying) + countPhrases(wielding)
                <= singleSentenceMaxNouns)
            {
                /* short enough: use a single-sentence format */
                showInventoryShortLists2(parent, carrying, wielding);
            }
            else
            {
                /* long: use a two-sentence format */
                showInventoryLongLists2(parent, carrying, wielding);
            }        
    }    
    showInventoryWieldNot(parent, carrying, wearing)
        {
            if (countPhrases(carrying) + countPhrases(wearing)
                <= singleSentenceMaxNouns)
            {
                /* short enough: use a single-sentence format */
                showInventoryShortLists3(parent, carrying, wearing);
            }
            else
            {
                /* long: use a two-sentence format */
                showInventoryLongLists3(parent, carrying, wearing);
            }    
    }    
    showInventoryShortLists1(parent, wearing, wielding)
    {
        local nm = gSynthMessageParam(parent);
        
        /* short lists - combine carried and worn in a single sentence */
        "<<buildParam('The/he', nm)>> {is} wearing <<wearing>>,
        and <<buildParam('it\'s', nm)>>{subj} wielding <<wielding>>. ";
    }
    showInventoryLongLists1(parent, wearing, wielding)
    {
        local nm = gSynthMessageParam(parent);

        /* long lists - show carried and worn in separate sentences */
        "<<buildParam('The/he', nm)>> {is} wearing <<wearing>>.
        <<buildParam('It\'s', nm)>> wielding <<wielding>>. ";
    }    
    showInventoryShortLists2(parent, carrying, wielding)
    {
        local nm = gSynthMessageParam(parent);
        
        /* short lists - combine carried and worn in a single sentence */
        "<<buildParam('The/he', nm)>> {is} carrying <<carrying>>,
        and <<buildParam('it\'s', nm)>>{subj} wielding <<wielding>>. ";
    }
    showInventoryLongLists2(parent, carrying, wielding)
    {
        local nm = gSynthMessageParam(parent);

        /* long lists - show carried and worn in separate sentences */
        "<<buildParam('The/he', nm)>> {is} carrying <<carrying>>.
        <<buildParam('It\'s', nm)>> wielding <<wielding>>. ";
    }    
    showInventoryShortLists3(parent, carrying, wearing)
    {
        local nm = gSynthMessageParam(parent);
        
        /* short lists - combine carried and worn in a single sentence */
        "<<buildParam('The/he', nm)>> {is} carrying <<carrying>>,
        and <<buildParam('it\'s', nm)>>{subj} wearing <<wearing>>. ";
    }
    showInventoryLongLists3(parent, carrying, wearing)
    {
        local nm = gSynthMessageParam(parent);

        /* long lists - show carried and worn in separate sentences */
        "<<buildParam('The/he', nm)>> {is} carrying <<carrying>>.
        <<buildParam('It\'s', nm)>> wearing <<wearing>>. ";
    }    
    showInventoryShortLists(parent, carrying, wearing, wielding)
    {
        local nm = gSynthMessageParam(parent);
        
        /* short lists - combine carried and worn in a single sentence */
        "<<buildParam('The/he', nm)>> {is} carrying <<carrying>>, wearing <<wearing>>, and
        <<buildParam('it\'s', nm)>>{subj} wielding <<wielding>>. ";
    }
    showInventoryLongLists(parent, carrying, wearing, wielding)
    {
        local nm = gSynthMessageParam(parent);

        /* long lists - show carried and worn in separate sentences */
        "<<buildParam('The/he', nm)>> {is} carrying <<carrying>>.
        <<buildParam('It\'s', nm)>> wearing <<wearing>>.
        <<buildParam('It\'s', nm)>> wielding <<wielding>>.
        ";
    }

    /*
     *   For 'tall' listings, we'll use the standard listing style, so we
     *   need to provide the framing messages for the tall-mode listing.  
     */
    showListPrefixTall(itemCount, pov, parent)
        { "<<buildSynthParam('The/he', parent)>> {is} carrying:"; }
    showListContentsPrefixTall(itemCount, pov, parent)
        { "<<buildSynthParam('A/he', parent)>>, who {is} carrying:"; }
    showListEmpty(pov, parent)
        { "<<buildSynthParam('The/he', parent)>> {is} empty-handed. "; }
;

modify DividedInventoryLister
    /*
     *   Show the list.  We completely override the main lister method so
     *   that we can show our two lists.  
     */
    showList(pov, parent, lst, options, indent, infoTab, parentGroup)
    {
        /* 
         *   If this is a 'tall' listing, use the normal listing style; for
         *   a 'wide' listing, use our special segregated style.  If we're
         *   being invoked recursively to show a contents listing, we
         *   similarly want to use the base handling. 
         */
        if ((options & (ListTall | ListContents)) != 0)
        {
            /* inherit the standard behavior */
            inherited(pov, parent, lst, options, indent, infoTab,
                      parentGroup);
        }
        else
        {
            local carryingLst, wearingLst, wieldingLst;
            local carryingStr, wearingStr, wieldingStr;

            /* divide the lists into 'carrying' and 'wearing' sublists */
            carryingLst = new Vector(32);
            wearingLst = new Vector(32);
            wieldingLst = new Vector(32);
            foreach (local cur in lst)
            {
                if(cur.isWornBy(parent)) 
                    wearingLst.append(cur);
                else if (cur.wieldedBy(parent)) 
                    wieldingLst.append(cur);
                else carryingLst.append(cur);
            }


            /* generate and capture the 'carried' listing */
            carryingStr = outputManager.curOutputStream.captureOutput({:
                carryingLister.showList(pov, parent, carryingLst, options,
                                        indent, infoTab, parentGroup)});

            /* generate and capture the 'worn' listing */
            wearingStr = outputManager.curOutputStream.captureOutput({:
                wearingLister.showList(pov, parent, wearingLst, options,
                                       indent, infoTab, parentGroup)});
            
            /*HAXX MUAHAHAHAHA*/
            wieldingStr = outputManager.curOutputStream.captureOutput({:
                wieldingLister.showList(pov, parent, wieldingLst, options,
                                       indent, infoTab, parentGroup)});

            /* generate the combined listing */
            showCombinedInventoryList(parent, carryingStr, wearingStr, wieldingStr);

            /* 
             *   Now show the out-of-line contents for the whole list, if
             *   appropriate.  We save this until after showing both parts
             *   of the list, to keep the direct inventory parts together
             *   at the beginning of the output.  
             */
            if ((options & ListRecurse) != 0
                && indent == 0
                && (options & ListContents) == 0)
            {
                /* show the contents of each object we didn't list */
                showSeparateContents(pov, lst, options | ListContents,
                                     infoTab);
            }
        }
    }

    /*
     *   Show the combined listing.  This must be provided by each
     *   language-specific subclass.  The inputs are the results (strings)
     *   of the captured output of the sublistings of the items being
     *   carried and the items being worn.  These will be "raw" listings,
     *   without any prefix or suffix text.  This routine's job is to
     *   display the final output, adding the framing text.  
     */
    showCombinedInventoryList(parent, carrying, wearing, wielding) { }

    /* 
     *   The recommended maximum number of number of noun phrases to show
     *   in the single-sentence format.  This should be used by the
     *   showCombinedInventoryList() method to decide whether to display
     *   the combined listing as a single sentence or as two separate
     *   sentences.  
     */
    singleSentenceMaxNouns = 7

    /*
     *   Our associated sub-listers for items begin carried and worn,
     *   respectively.  We'll use these to list our sublist of items being
     *   worn.  
     */
    carryingLister = actorCarryingSublister
    wearingLister = actorWearingSublister
    wieldingLister = actorWieldingSublister
;

Also, some copy-pasta and stuff to illustrate how it fails to work:


DefineTAction(Wield);

   VerbRule(Wield)
     ('wield') singleDobj
     : WieldAction
     verbPhrase = 'wield/wielding (what)'
   ;

   modify Thing
     dobjFor(Wield)
     {
       verify() 
       {
         illogical('{You/he} cannot wield that. ');
       }
     }
   ;


  DefineTAction(Unwield);

   VerbRule(Unwield)
     ('unwield') singleDobj
     : UnwieldAction
     verbPhrase = 'unwield/unwielding (what)'
   ;

   modify Thing
     dobjFor(Unwield)
     {
       verify() 
       {
         illogical('{You/he} {are} not wielding that. ');
       }
     }
   ;

class Weapon : Thing 'weapon*weapons' 'weapon'
atk = 2
isWielded = nil 
wieldedBy = nil
dobjFor(Drop) maybeRemapTo(isWielded, Unwield, self) 
iobjFor(AttackWith)
{
verify(){}
check(){}
action()
  {
   } 
}
dobjFor(Wield)
{
verify(){}
check() 
  {
   if(gActor.wielding)
    failCheck('{You/he} already have a weapon. ');
   }
action()
  {
   moveInto(gActor);
   gActor.atk += gDobj.atk;
   gActor.wielding = true;
   gActor.weapon = gDobj;
   gDobj.isWielded = true;
   gDobj.wieldedBy = gActor;
 "{You/he} wield {the dobj/him} as {your/his} weapon. ";
   }
}
dobjFor(Unwield)
{
verify(){}
check() 
  {
   if(!gDobj.isWielded)
     failCheck('{You/he} are not wielding {the dobj/him} ') ;
   }
action()
  {
   gActor.atk -= gDobj.atk;
   gDobj.isWielded = nil;
   gActor.wielding=nil;
   gActor.weapon = nil;
   gDobj.wieldedBy = nil;
 "You stop wielding {the dobj/him} as your weapon. ";
   }
}
;

sword : Weapon 'sword' 'sword of beta testing'
;

modify Actor
atk = 0
wielding = nil
weapon = nil
;

tunic : Wearable 'tunic' 'tunic'
@me
"A midieval tunic, of the sort often worn by DnD characters. "
wornBy = me
;

gem : Thing 'sparkly gem/jewel' 'sparkly gem'
"It's so sparkly! "
;

Sorry to say this question is completely beyond me. I do have a comment, though: I think there would be other situations than wielding where a three-part list would be useful. For instance:

This may seem unlikely, in that juggling normally requires both hands. But dare we assume that the PC will always be a two-handed life form? I’m not actually kidding. I think wielding, juggling, fondling, munching on, and perhaps other forms of containment should also be able to be handled by a multi-part lister. And not only for inventory.

The article on listers has a good example of grouping writing implements on a table, but I just plain don’t know enough to see how to adapt that example to an InventoryLister.

The issue is the use of the wieldedBy in showList().

...
if(cur.isWornBy(parent)) 
    wearingLst.append(cur);
else if (cur.wieldedBy(parent)) 
    wieldingLst.append(cur);
...

wieldedBy is not a function and takes no arguments. You need to define an isWieldedBy equivalent to isWornBy.

class Weapon : Thing 'weapon*weapons' 'weapon'
    atk = 2
    dobjFor(Drop) maybeRemapTo(isWielded, Unwield, self) 
    iobjFor(AttackWith)
    {
        verify(){}
        check(){}
        action()
        {
        } 
    }
    
    dobjFor(Wield)
    {
        verify(){}
        check() 
        {
            if(isWieldedBy(gActor))
                failCheck('{You/he} are already wielding {the dobj/him}. ');
            
            if(gActor.isWielding())
                failCheck('{You/he} are already wielding a weapon. ');
        }
        action()
        {
            moveInto(gActor);
            gActor.wield(self);
            "{You/he} wield {the dobj/him} as {your/his} weapon. ";
        }
    }
    
    dobjFor(Unwield)
    {
        verify(){}
        check() 
        {
            if(!isWieldedBy(gActor))
                failCheck('{You/he} are not wielding {the dobj/him} ') ;
        }
        action()
        {
            gActor.unwield(self);
            "You stop wielding {the dobj/him} as your weapon. ";
        }
    }
    
    wieldedBy = nil
    isWielded() { return wieldedBy != nil; }
    isWieldedBy(actor) { return wieldedBy == actor; }
    makeWieldedBy(actor) { wieldedBy = actor; }
    getAtk() { return atk; }
;

modify Actor
    atk = 0
    weapon = nil
    wield(obj) { weapon = obj; obj.makeWieldedBy(self); atk += obj.getAtk(); }
    unwield(obj) { weapon = nil; obj.makeWieldedBy(nil); atk -= obj.getAtk(); }
    isWielding { return weapon != nil; }
;

It works!!! (^o^)/

Thanks, Bcressy! :slight_smile: