Small update today as work’s eaten into all of my spare time.  I’m trying to leave each feature/system in a code-complete state before I move on to the next one.  In that vein, I’ve made a few fixes:

  • When aggro’ing onto another Actor, face that Actor
  • When attacking another Actor, face that Actor
  • After generating a layout, ensure all Doors are “logical” – i.e. they’ve got walls on either both horz or both vert sides and they’ve got empty space on the other sides.

Load/Save progress

I did make some progress on Load/Save, but it’s not in a state where sharing the code makes sense.  The point of the big scenario list in the previous post was to make sure I understood each possible scenario before I started collapsing things.  Ultimately, many of the rows will collapse together; for example, here’s the new EnterLevel function which handles entering a dungeon for the first time, ascending/descending to a new level for the first time, ascending/descending to an existing level, as well as both resume and load scenarios:

        public Level EnterLevel(int levelNumber)
            Level newLevel = null;

            // If the player's currently in a Level, then persist it
            if (Player.CurrentLevel != null)
            using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForAssembly())
            using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
                string levelFile = Player.Current.PlayerId.ToString() + "/dungeon/level-" + levelNumber;
                if (store.FileExists(levelFile))
                    // re-entering existing level
                    FileReader reader = new FileReader(levelFile, false);
                    newLevel = new Level(reader, this);
                    // entering a new level; create it
                    newLevel = Level.Generate(this, levelNumber);

                    // Persist the newly generated level

            // Add the player to the level
            Player.CurrentLevel = newLevel;

            // Persist the current player's info (to store dungeon/level)

            return newLevel;

The biggest stumbling block I’ve hit is in persisting Player information. The Player object is just another Actor object, which means that all of the game logic deals with it generically; e.g. the Event system doesn’t need to special case Player, AggroTarget can be any Actor (Player or Monster), etc.

The problem is that unlike other Actors, Players aren’t localized to a specific Level.  For instance, in the MainMenu the user will be able to see what active Players they can continue, and should be able to see stats (Level, Class, etc).  I played for a while with the idea of a “Player” class and a “PlayerActor” class, but I didn’t like that because in order for Players to work as generic Actors in the game logic, I’d need to (a) I duplicate and sync information (e.g. ActorLevel) between the two classes, or (b) abstract stats like “Actor.Strength” into a virtual “Actor.GetStrength()” class and have PlayerActor.GetStrength() extract it from the Player class that it was representing.  That’s possibly the “right” way to do it, but I didn’t care for it. (side note to future self: ironic that Buffs will force me to eventually move to a “Get<stat>()” model.  Apologies for this earlier design decision).

Instead, Players are now persisted completely separately, but using the same Actor.Persist logic to avoid duplication.  This required a few special-casing spots in the persistence/restoration code, and requires a little bit of “fixup” when rehydrating a level. Not a huge fan, but I’m not seeing any alternatives that I like more.

At this point, Load/Save is mostly working, although I need to go through and ensure the fixups are working properly.  After that, I need to hook the tombstoning codepath into this (hopefully it’ll be able to leverage the EnterLevel function above).  So: Targeting Friday night for finally getting past Load/Save and back into the fun stuff! 🙂