I reread a GDC paper from 2006 on procedural NPC AI, and that gave me the urge to mess around with that general idea. I decided, based on gut feeling mostly, that the first thing I wanted to do for this was to port out and update the ECS engine from Dennis.
Getting the code to a state where I think it should work took most of the day. I'm not exactly sure how I want to progress once I have it in a working state (with tests, typechecking, and published on PyPI). I can either try to put together a rudimentary world model, or port other bits of Dennis piecemeal until I've got enough to put it all together into "a game", then hack on top of that.
I don't have anywhere to go from that, so I might as well mention the changes I made. Looking through the Ink codebase has me kind of soured on the concept of "zero-argument constructors for objects that get all of their state injected in deserialization", so I redid the constructor to handle much of the logic that is done in the deserialization routines in Dennis, and also added a helper to do most of the work needed for serialization. One of the weird janky bits of Dennis's implementation is that the component fields were wrapped in a descriptor that contained a tiny bit a metadata, and all access to the components was done by dynamically generating attribute names to use with getattr. Essentially, it was a layer of syntactic sugar that wasn't actually really visible outside the module where it was defined. This doesn't really make sense. Since I kept the idea of a "Component" base marker class, I just had it detect when class attributes were subclasses of the marker class, and assemble a dictionary at instantiation time. This approach might end up being harder to typecheck, but I'm pretty sure that the version in Dennis now only typechecks because all of the getattr stuff is implicitly tossing out Any values that then get coerced to the expected type implicitly.
Okay, it's getting late. I have to call this now.