Jim Aikin wrote:
Why try to add the ActorState while the game is running? You can park it there in your code from the beginning of the game, and then activate it when it's needed using, if memory serves, someActor.setCurState(dealerState).
The problem was that there's more than one actor using the same state code, and I'm trying to avoid implementing that via cut-n-paste. I want to say "THIS actor has a blackjackPlaying actor state, and so does THAT one, and that other one" without repeating all the blackjack playing code three times.
Normally it's trivially easy to get more than one object that behaves similarly by just subclassing them from a common base class, but it's not that trivial when the object in question is an Actor and the behavior you're trying to inherit is driven by ActorStates and ConvNodes. One reason for this is that in TADS3, an inner class inside an outer class doesn't *really* re-instance itself with each instance of the outer class, as I found out the hard way. Instead each instance of the outer class references the SAME instance of the inner class, which makes it a bit harder to create a generic class for all blackjack-playing actors to inherit from when I need them to inherit more than just the actor subclass, but the entire linkage of ActorStates and ConvNodes that are inside the actor subclass. This is because a crucial aspect of their relationship is that ConvNodes must have their location properties pointing to parent ActorStates which must have their location properties pointing to a parent Actor, and you can't really get that type of relationship prior to runtime via just simply subclassing in TADS3. You need to run some code to make that linkage exist, and that code has to be run BEFORE the PreinitObject classes do their work because it is in the Preinit step that those location linkages are "walked" by the adv3 library. If they're not there, adv3 will crash before turn 1 start, and they *can't* be there yet prior to running the code in the engine because of the subclassing of inner classes problem mentioned above.
The only working solution seems to be to dynamically create instances of the ActorStates and ConvNodes underneath the Actor subclass each time a new instance of the Actor subclass is created. And that means finding a place to run code prior to Preinit.
I'm coming to a solution that involves using the construct() method, and the fact that all object instances are "run" once at compile time before the game image is saved. Thus I can get the compiler to run the construct() methods just before saving the initial game state in the t3 file and have it build up the location relationships I want then. This seems to be working but I want to test it out first before I post it as a solution. One feature of this is that each time you have code inside a property definition, like so:
Code:
myObj : Thing
someProp = 3 * x + 2
;
(So someProp is not a fixed value, but an expression that needs to be run) the compiler, which contains a lot of the TADS3 code execution engine in it too, actually executes that statement once before storing the compiled image in the t3 file.
So I can make the location connections like so:
Code:
class MyDealerActor : Actor
// other unrelated properties not shown.
innerState = new MyState(self)
;
class MyState : ActorState
// other unrelated properties not shown.
innerConvNode = new MyConvNode(self)
construct(containingObj) // I found out this is unnecessary as ActorState already has a constructor that does exactly this.
{ inherited(containingObj);
self.moveInto(containingObj);
}
;
class MyConvNode: ConvNode
// other unrelated properties not shown.
construct(containingObj) // But unlike ActorState, for some reason ConvNode does not have a constructor like this already, so this bit actually is neccessary.
{ inherited();
self.moveInto(containingObj);
}
;
// etc.
Thus the compiler (not the game runner) runs the initialization code for each instance of MyDealerActor (the 'new' operator in the property definition) and recurses through all these classes running all these new operators ONCE during compile time. This seems to be working as it causes the location relationships to get built at compile time despite them looking like dynamic code that runs at runtime.