I had a burst of motivation this week fueled by small victories and growing awareness of how much work I need to do to complete history generation.
- Rework. The enums defining world state entities and entity attributes were replaced with classes. The dead giveaway that this was necessary was the presence of multiple switch statements that mapped data to the enums. In addition to the code changes, this necessitated reconfiguring the ~40 existing history event types. The rework was expensive, consuming 8 hours total (4.5 for coding, 1.5 for reconfiguring events, and 2 for testing/fixing). The benefits are 1) less code 2) a more data-driven architecture 3) history entities and attributes can be created with the Unity Inspector 4) some todo list items, such as filtering attribute dropdowns in the editor by entity type, were resolved.
- New history entity type: Object. It was incredibly easy to add the new Object entity type as a result of the above rework. In the actual game, Objects are implemented in the same manner as Actors (Objects are essentially stationary actors with no AI). In a history generation context, they are different enough to warrant a distinct entity type.
- Actor death handling. Some events kill off an actor, but actor death (in the world state context) wasn’t fully implemented. The issue this caused was that dead actors were able to act. I fixed this with a kludge, which was to automatically exclude dead actor entities from entity selection in events. The kludge caused a new issue: events involving dead actors, such as entombing an actor, no longer worked. I implemented another kludge to fix the new issue. I’m not sure what the appropriate solution is yet. One option is to require each event type involving a living actor to include a binding criterion to exclude dead actors, but this would be a hassle to maintain and I’m sure I’d occasionally forget to include the criterion when creating new event types.
- New binding criteria to check for presence/absence entity attributes. History events use the attribute presence criterion to select entities that have a particular attribute (the attribute value itself doesn’t matter). For example, an event in which an actor steals from another actor will look for existing entities who have an item. Events use the attribute absence criterion to select entities missing an attribute, which is useful for identifying items that aren’t possessed by an actor, for instance.
- Inclusive and exclusive binding criteria properly applied. The addition of exclusive binding criteria last week caused a new issue: when an entity attribute contained inclusive and exclusive binding criteria, the latter was disregarded if it was applied before the former. This was fixed by adding a binding criterion flag to indicate whether the criterion is inclusive or exclusive and using the flag to apply exclusive criteria after inclusive.
- More readable history event text. There are now two versions of the world history text dump, one for reading and another, more detailed format for troubleshooting. I created the readable format for myself to help assess how well a generated history flows. Players won’t see it, though I may add an option to export it to a file. It would be a list of spoilers to the player (the history is intended to be a mystery that is uncovered over the course of the run).
- History event text is now stored when the event is created. After history is generated, all history events are written to a file. The event text is generated dynamically, substituting placeholders with entities and entity attributes. The problem with this, I discovered when reviewing the history files, is that entity attributes can change over time. For example, a bandit that is bitten by a vampire turns into a vampire. Dynamically generated event text will use the final state of an entity, not the state that the entity was in during each event. The easy solution was to generate the event text when the event was
- More editor field validation and field setting automation. More logic was added to validate fields, set default values, show/hide fields, populate lists, and filter lists of values based on other field values. It makes history events easier to create and helps prevent missing and invalid data errors. This functionality was added using Odin Inspector.
- Arriving and settling entities linked. When a new faction or actor arrived in a section, a Settle trigger was fired but the responding event didn’t use the arriving entity as the settling entity. Bandits could arrive at the Catacombs and the following event would be Cultists settling at the Catacombs, for example.
- History event type testing and tuning. I’m finding that history event types require more explicit configuration than I expected. For example, in the event type where an actor steals from another actor, I need to configure the event to specify that the two actors aren’t the same, neither actor is dead (done implicitly, described above), the item is possessed by the victim actor, the item type can be stolen, etc.
Next week should be rework-free, allowing me to concentrate on testing and refining history generation. There is one open issue that may be challenging to fix: events with circular entity placeholder references. I’ve avoided entity reference issues thus far by sequencing the placeholders based on their dependencies. But, I’ve created a few events recently that require circular entity references. My plan is to have the entity binder construct a dependency map and bind entities in dependency order.