There are times when infrastructure work can feel like a tax that you have to get through in order to get to the really fun coding; you do a ton of work but don’t necessarily get a “look at what I did!” moment at the end of it. But when the infrastructure is either (a) cleaning up and refactoring (aka removing code you now know isn’t needed any more; oh it feels good), or (b) enabling yourself to create new features with considerably lower effort, then it becomes more fun. And when it’s both, it’s definitely fun; that’s what last night and tonight were.
Here’s the net result of all that work:
Before explaining what’s cool about that picture, here’s what’s new in the code:
- Major cleanup and refactoring of the persistence code. I had been storing game state as a single monolithic file; since players can go up and down levels, I need to keep all of them around – that meant the gamestate file was eventually going to get unacceptably large. Also, I didn’t want to store static definitions in the save file since I want the ability to change them in updates. There are now three sets of files; “gameDefinitions.dat” which stores all ‘immutable definitions’ (mobDefn, lightDefn, cellDefn, and 15 other definition classes), “gameSlot#.dat” which stores data that spans characters (discovered recipes, total mobs killed count, settings, etc), and “level#.dat” which stores the instantiated models for a specific level (mobs, objects, etc). Much cleaner split of persisted data
- The problem with that is that while Protobuf-net serializes references without complaint, it can’t do it across files. So when a model (from level#.dat) references a Defn (from gameDefinitions.dat), what protobuf actually does is store a complete copy of the defn rather than a reference to it. This was fun to figure out since it’s practically impossible to tell that a copy was created and not a reference. This is not what I want because I want to be able to change definitions (e.g. tweak fields) in updates, and I can’t do that if the definitions are baked into the level data. So; Defns can safely reference Defns since both are in one file, and Models can safely reference Models since both are in one file; however, Models cannot reference Defns. So I went through and added load-time fixups for all instances of Defns and persist the Defn Id instead of the Defns themselves. On the plus side, in the future I can update the game and modify games that are already in progress.
- ParticleDefn and LightDefn now read from the Xlsx; itemdefns can specify up to three of each and the code handles creating them.
- I added the concept of ‘adornment items’; in the xlsx, CellDefns can specify inclusion of an item that will automatically be created in the cell – e.g. a Brazier or a poison pit.
Mix those together and now I can define what you see in the above picture just by adding to the xlsx:
* The green circles in the upper left are slightly glowing poison pits with particle “bubbles” and a celleffect that does 1 damage when you step in it
* the burning squares in the top middle are bright glowing firepits with a fire particle effect and a celleffect that adds a debuff that does 2 damage to the actor for 3 turns
* The smaller orange fire in the center left is a new “Brazier” adornment that includes a couple of lights and three particle effects (smoke, fire, and sparks) – all defined in the xls
* The smaller orange/green fire in the center right is a new “campfire” adornment that includes its own light and a different particle effect (cylcing through colors to green as a test). Again, all from the xls.
* As a test, I also added a dim green light and green particle effect to the skeleton mob (seen in the lower-middle), and again without touching code was able to create an entirely new and unique “poison zombie” mob. Give it a “poison touch” skill, and you’ve got a totally brand new mob with almost no effort.
And that’s why this infrastructure work was fun