Problem with multi-component Containers

Hey, so I tried to create a Safe in adv3lite, with an external keypad and an internal container, as the manual discusses in section 5.5. Problem is, whenever I actually try to open the safe, I get a too far away message. Is there any way to work around this problem?

[code]SafePainting: Decor ‘painting’
"It’s a pretty painting. "
triedToTake = nil

constructOnce()
{
    inherited;
    local safe = new Safe;
   hiddenBehind = [Safe]
}

dobjFor(Take)
{
    check()
    {
        if(triedToTake)
            "<<theName>> is too heavy to carry. ";
    }
    action()
    {
        inherited;
        "You take <<theName>> off the wall only to realize it's too heavy to carry, and drop it on the floor. ";
        moveInto(location.location);
        triedToTake = true;
    }
}

;
Safe: Fixture ‘safe; black wall locked unlocked’
"The black safe requires a four digit key code to open. "
remapIn: SubComponent, LockableContainer { }

constructOnce()
{
    inherited;
    new SafeKeypad.moveInto(self);
}

;
SafeKeypad: Component, NumberedDial ‘keypad; key safe number code; pad numbers buttons’
minSetting = 0
maxSetting = 9999

makeSetting(val)
{
    inherited(val);
    if(val == 1212)
        location.remapIn.makeLocked(nil); //No idea if this will work.
}

;[/code]

Even without the keypad component, it doesn’t seem to work.

EDIT: I was able to find the problem. It seems it doesn’t like the safe being generated and placed dynamically behind the painting. Is there a way I can hide something behind an object besides having it start there? I’m trying to keep most of my objects linked together so if I want do random generation later it’s much easier.

I’m speculating, as I’m not that familiar with the library, but it’s possible that hiddenBehind is set up during preinit, which would mean you couldn’t use it to hide objects behind (or under, or in) dynamically created objects. I think you could set up your painting with a remapBehind property (a subcomponent) but not put anything in that subLocation. When you want to move the safe into scope, you could use moveInto(safePainting.remapBehind) or something of that sort. But of course you probably don’t want the safe to be anywhere until the painting is moved, so moving the safe into safePainting.remapBehind would be rather pointless – it would make more sense just to move it into the room when it’s needed.

It’s possible that the problem is that you wrote:

 constructOnce()
    {
        inherited;
        local safe = new Safe;
       hiddenBehind = [Safe]
    }

Where you meant:

 constructOnce()
    {
        inherited;
        local safe = new Safe;
       hiddenBehind = [safe] // Hide the object, not the class!
    }

You need to put the object (safe) in the hiddenIn list, not the class (Safe).

No, though thank you for pointing that out!

I’ve actually had a somewhat similar problem with dynamicRegion, where if I set the regions = [aRegion] for a room when it was constructed, it wouldn’t count as part of that region, though it wasn’t enough of an issue to worry about.

If it makes a difference, here’s the code for constructOnce:

modify Thing constructed = 0 //Since construction happens twice, and building has to be done on second call, this is an int. construct() { inherited; constructed++; if ((!self.ofKind(Room) && constructed == 2) || (self.ofKind(Room) && constructed == 1)) constructOnce(); } constructOnce() { } ;

I suspect there may be another potential problem with this code:

Safe: Fixture 'safe; black wall locked unlocked'
    "The black safe requires a four digit key code to open. "
    remapIn: SubComponent, LockableContainer { }
    
    constructOnce()
    {
        inherited;
        new SafeKeypad.moveInto(self);
    }
;

I’m fairly sure that a nested object (like the SubComponent on remapIn above) can only be defined this way on an object (i.e. an instance of a class), and not on the class itself. I think what you’d need to do to make this work dynamically (on constructing a new Safe) is something like:

class SafeInterior: SubComponent, LockableContainer ;

Safe: Fixture 'safe; black wall locked unlocked'
    "The black safe requires a four digit key code to open. "
    remapIn = perInstance(new SafeInterior)
    
    constructOnce()
    {
        inherited;
        local sk = SafeKeypad;
        sk.moveInto(self);
    }
;

Alternatively:

class SafeInterior: SubComponent, LockableContainer ;

Safe: Fixture 'safe; black wall locked unlocked'
    "The black safe requires a four digit key code to open. "
   
    
    constructOnce()
    {
        inherited;
        local sk = SafeKeypad;
        sk.moveInto(self);
        remapIn = new SafeInterior;        
    }
;

Hm, I still get the same problem I had before with the first example of the safe being too far away (the second example doesn’t seem to register the interior at all).

I was never entirely sure if the Safe was truly a class, or just a “faux class” where’s it an instance that gets copied. I never explicitly defined it as such (for reasons that escape me at the moment), though treated it as a class so the player will only ever interact with a copy of it.

The fundamental problem is that the SubComponent class is designed to be attached to the remapXXX property of an object, not a class, so it may be there simply isn’t a way to create your safe dynamically. That said, I still don’t really see what you’d want to, since you appear simply to be created extra problems for yourself.

You could try something like this (I tested it and it does seem to work):

class SafeInterior: SubComponent, OpenableContainer 
    construct(loc)
    {
        inherited();  
        moveInto(loc);
        initializeSubComponent(loc);
    }
;

class Safe: Fixture 'safe; black wall locked unlocked'
    "The black safe requires a four digit key code to open. "
     construct()
    {
        inherited();
        local si = new SafeInterior(self);
        remapIn = si;
    }        
;

You’ll then need to adapt it to your needs (note that I’ve made Safe a proper class here, not an object; if it’s not a class you’ll get a run-time error on start-up). But you’re really going against the grain of the library (and in a way, the language), by trying to create everything dynamically instead of just defining objects.

The main reason I’m dynamically creating everything is because I want the game itself to be randomly generated. Dynamically creation just meant that I don’t have to figure out how to “reset” every object when the world is regenerated, I can just recreate everything from scratch. Now, I don’t know how realistic that idea is (I’ve put the world generation on hold for right now simply from a design perspective), but it’s easier to go down rather than up, so to speak. If I get it working, for example, and it’s not much fun, I can just have it create everything once.

Thanks for all the help though, you’ve been amazing for helping me figure everything out.

Okay, but in the case of the safe (or anything with nested objects), it’s enough of a pain to create dynamically that unless you actually need more than one safe, it’d probably be easier to define it statically, and then move it to where you want it and maybe randomize some of its properties. There’s not much advantage in trying to create a complex item like that dynamically unless you need multiple copies of it.