Weirdness with TAKE ALL FROM a ComplexContainer

The problem: TAKE ALL FROM an instance of ComplexContainer doesn’t appear to work in the expected way.

Sample code: defines a Furniture class, which here is just an alias for Heavy. Also defines a Dresser class, which is Furniture which is also a ComplexContainer. Each Dresser comes with: a DresserDrawer which is an OpenableContainer and a ComplexComponent; and a DresserTop which is a Surface and a ComplexComponent.

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

startRoom:      Room 'Void'
        "This is a featureless void with a wooden dresser in one corner. "
;

class Furniture: Heavy;

class DresserDrawer: ComplexComponent, OpenableContainer
        '(dresser) drawer' 'drawer';
class DresserTop: ComplexComponent, Surface
        'on (dresser) top' 'top';

class Dresser: ComplexContainer, Furniture 'dresser' 'dresser'
        subContainer = perInstance(new DresserDrawer())
        subSurface = perInstance(new DresserTop())
        contentsListed = nil
        contentsListedInExamine = nil

        drawer = subContainer
        top = subSurface

        initializeThing() {
                subSurface.moveInto(self);
                subSurface.targetObj = self;
                subContainer.moveInto(self);
                subContainer.targetObj = self;
                inherited();
        }
;

dresser: Dresser 'plain wooden dresser' 'dresser'
        "It's a plain wooden dresser with one drawer. "
        location = startRoom
;
+ pebble: Thing 'small round pebble' 'pebble'
        "A small, round pebble. "
        subLocation = &drawer
;
+ widget: Thing 'nondescript widget' 'widget'
        "It's a nondescript widget. "
        subLocation = &top
;

me:     Person
        location = startRoom
;

versionInfo:    GameID
        name = 'sample'
        byline = 'nobody'
        authorEmail = 'nobody <foo@bar.com>'
        desc = '[This space intentionally left blank]'
        version = '1.0'
        IFID = '12345'
;
gameMain:       GameMainDef
        initialPlayerChar = me
;

This mostly works: the drawer can be opened and closed, OPEN DRESSER automagically opens the drawer, and so on. But trying TAKE ALL FROM DRESSER confuses the parser:

Void
This is a featureless void with a wooden dresser in one corner.

On the dresser, you see a widget.

>open dresser
Opening the dresser reveals a pebble.

>take all from dresser
widget: The widget isn't in that.

pebble: The pebble isn't in that.

Weirdly it figures out, correctly, which objects are in scope. Just not what to do with them.

Am I missing something obvious here, or is this a weirdness in ComplexContainer that needs to be architected around?

Replying to myself with a kludge/workaround.

It looks like the problem is in ComplexComponent.initializeLocation(). It is:

    initializeLocation()
    {
        /* set our location to our lexical parent */
        location = lexicalParent;

        /* inherit default so we initialize our container's 'contents' list */
        inherited();
    }

…which forces the component’s location to be lexicalParent. This means any attempt to declare the components as their own classes/instances is going to have unexpected consequences.

The examples in the comments assume that you’ll declare things something like (this is from extras.t:

*.  + washingMachine: ComplexContainer 'washing machine' 'washing machine'
 *.    subContainer: ComplexComponent, Container { etc }
 *.    subSurface: ComplexComponent, Surface { etc }

Unfortunately, this default expectation only works if each ComplexContainer is a unique in-game object. Because declared this way, each component is per class, not instance. So in my example (of a dresser) if the game contains multiple dressers (which is in fact what I’m working on) then each dresser would share a single common drawer…meaning you could put something in one dresser and it would be present in all of them.

Anyway, here’s a kinda/sorta workaround:

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

class Furniture: Heavy;

class MyComplexComponent: ComplexComponent
        initializeLocation() {
                local oldLoc;

                oldLoc = location;
                inherited();

                if(!lexicalParent && oldLoc)
                        self.baseMoveInto(oldLoc);
        }
;

class DresserDrawer: MyComplexComponent, OpenableContainer
        '(dresser) drawer' 'drawer'
;
class DresserTop: MyComplexComponent, Surface
        'on (dresser) top' 'top'
;
class Dresser: ComplexContainer, Furniture 'dresser' 'dresser'
        subContainer = perInstance(new DresserDrawer())
        subSurface = perInstance(new DresserTop())

        drawer = subContainer
        top = subSurface

        initializeThing() {
                subSurface.baseMoveInto(self);
                subSurface.targetObj = self;
                subContainer.baseMoveInto(self);
                subContainer.targetObj = self;
                inherited();
        }
;

startRoom:      Room 'Void'
        "This is a featureless void with a wooden dresser in one corner. "
;
+ dresser: Dresser 'plain wooden dresser' 'dresser'
        "It's a plain wooden dresser with one drawer. "
;
++ pebble: Thing 'small round pebble' 'pebble'
        "A small, round pebble. "
        subLocation = &drawer
;
++ widget: Thing 'nondescript widget' 'widget'
        "It's a nondescript widget. "
        subLocation = &top
;

me:     Person
        location = startRoom
;

versionInfo:    GameID
        name = 'sample'
        byline = 'nobody'
        authorEmail = 'nobody <foo@bar.com>'
        desc = '[This space intentionally left blank]'
        version = '1.0'
        IFID = '12345'
;
gameMain:       GameMainDef
        initialPlayerChar = me
;

This implements MyComplexComponent, which overwrites initializeLocation() to check lexicalParent and restores the component’s defined location if lexicalParent is nil.

This seems to work at least as far as my test cases go:

Void
This is a featureless void with a wooden dresser in one corner.

On the dresser is a widget.

>open dresser
Opening the dresser reveals a pebble.

>take all from dresser
widget: Taken.
pebble: Taken.

>undo
Taking back one turn: "take all from dresser".

Void
This is a featureless void with a wooden dresser in one corner.

On the dresser is a widget.  The dresser contains a pebble.

>take all from drawer
pebble: Taken.

I don’t know if this interferes with some other expected behavior(s) of ComplexComponent/ComplexContainer…because they’re not really documented.

I’ll also mention that this affects the solution proposed at the end of this thread, which is one of the references I turned up when I was trying to implement this.

2 Likes

I hope I can be notified when your game is released… it sounds really interesting…

1 Like

Thanks, but in the interest of full disclosure I should admit that there will be somewhat fewer pebbles in the finished game than would be suggested by the example code.

1 Like

Anything with involved mechanics intrigues me, as that’s what I’ve had fun tinkering with in my own game…