initializing dynamic objects

Is there a better way of dynamically instantiating an Actor (or a subclass of)? I tried the following:

npc = new Actor();

And received a nil object reference error immediately in Actor.readyForTurn()

I couldn’t find an explicit construct() method for Actor. So, I was also confused on how to define the typical things required by the Actor template. The best “working” solution I found was:

npc.initializeActor(); npc.vocabWords = 'npc'; npc.name = 'test name'; npc.npcDesc = 'test desc'; npc.moveInto(hallway);

In my game, I’m envisioning a scenario where I want the number and type of NPCs at a location to be randomized. For example, a group of travelers appearing on a road. Maybe there are 2-4, one is a merchant, one is a visiting monk. Or, an ambush with a variable number of brigands. Or, a subset of NPCs used in a random lineup (to solve a crime).

I thought to do this with dynamic instantiations of classes that are derived from Actor. I guess an alternative would be to classically define objects (‘e.g. npc1: Actor template;’) and then refer to the pool?

Thanks,
-Brad

My experience doesn’t exactly fit your scenario, but it might be adaptable.

I recently needed to implement a crowd of people at a bus stop, with randomly selected characters mouthing off at intervals.

The crowd also needs to be made up of different folks for different occasions (different bus stops, at the stop or on the bus in transit, etc.).

Part of the solution was to program their comments into a ShuffledEventList and fire the list’s doScript() method at predetermined intervals in the bus stop’s (or bus’s) roomAfterAction() method. But I also wanted the crowd to be present during a Look response, and I wanted the option of some interactive dialog with crowd members.

So I created one Actor object for the crowd, then created ActorStates within the object for a) different crowd scenes (on bus, at each stop) and b) different individuals (punk, elderly woman, middle aged guy, gangsta girl) with individual conversation topics as needed.

Then I just change state whenever I want a particular crowd or person in the crowd. I also have an updateVocab(obj) function with a list of vocab strings. I can call the function with an Actor as obj and use a switch to call replaceVocab(listItem) on the actor object.

As stated, this may not be an exact match to your needs, but with a little tinkering it might work. (You could, for example, create containers located in nil for each crowd member’s inventory if you wanted individual ActorStates to have a persistent stash, though you’d have to manage the contents of these individual inventories yourself rather than rely on TADS; if these pseudo characters become too elaborate, at some point it may make sense to just create persistent actor objects for them.)

Jerry

Ah, okay, so…a quick dip into the Tads3 System Manual’s discussion of Dynamic Object Creation (under Part III: Language) tells how it’s done, and a quick test in a test-bed game shows that the following works…

            local x = new Actor();
            x.replaceVocab('new kid on the block');
            x.desc = 'The new kid on the block stands dumbfounded.<.p>';
            x.isHim = true;
            x.moveInto(otherRoom);

Here’s the game output, where the dynamic creation code is in the dobjFor(Push) macro of the magic button object. When the button is pushed, the actor is created and moved, along with the PC, into the Other Room…

But is the OP using adv3Lite (which Jerry’s code presupposes) or adv3?

So far, I’ve been using adv3. From my cursory reading, it seems that adv3lite would also work (I’m not far along yet to have a strong opinion either way).

OK- my original code didn’t fully work either. The dynamic creation of the Actor as I have it doesn’t execute the necessary bootstrapping found in the adv3LibPreinit. So, I started going down the path of this:

class DynActor : Actor {
    construct(vW, n, nD, loc) {
        inherited Actor.construct(); // not sure if this is an empty constructor or the default constructor that would call the other multiclass constructors
        self.vocabWords = vw;
        self.name = n;
        self.npcDesc = nD;
        self.location = loc;
        self.initializeVocab(); // this still doesn't work- I'm getting a nil reference in initializeVocab()
        self.initializeThing();
        self.initializeActor();
    }
}

npc = new DynActor('test npc', 'test', 'A very testy NPC.', hallway);

It seems that this is going to get me in the woods real quick. The other option, which is more or less what Jerry seems to be recommending, is relying on the “getter/setter”-type methods such as moveTo() to compensate for the missing bootstrapping at run-time. Does anyone know if the adv3 or adv3lite objects were designed with this in mind? As in, you can safely instantiate the objects with all the default values (e.g. empty constructor) and then set them accordingly at run-time? It also seems as though I could fairly easily replace/move the adv3LibPreinit stuff to something run at run-time (and see what the corresponding performance hit is).

I really don’t want to fight the design goals of the original library too much. Regardless, this has been a pretty fun system to learn. Really well-documented!
-Brad

Something along the following lines seems to work:

       
class DynamicPerson: Person
    construct(voc_, name_)
    {
        vocabWords = voc_;
        name = name_;
        inherited(); 
        initializeActor();        
    }
;

Note that the call to the inherited() constructor (note the syntax used to call it) calls the Thing constructor which in turn calls the VocabObject constructor, which between them call several of the methods you were trying to call manually, which may have been part of the problem. Note also that you need to set the vocabWords_ property on your dynamic Person object (I suggest using the Person class rather than the Actor class if these dynamic actors are meant to represent human beings) before calling the inherited constructor that will then use the vocabWords property to populate the dictionary.

With this definition you can then create a dynamic Person object, set some of its other properties, and then move it into play like this:

        local fred = new DynamicPerson('big burly fred/man/chap', 'Fred');
        fred.isProperName = true;
        fred.moveIntoForTravel(startRoom);
        fred.setMethod(&desc, 'He\'s a big burly chap. ');

Note the use here of setMethod to set the desc property of fred. You can’t write:

   fred.desc = "He\'s a big burly chap. ";

Since you can’t assign double-quoted strings to properties as if they were values. Using setMethod() works round this restriction.

Thanks! This all seems to work!