This update includes:


These are minor additions to cells that user can’t interact with; spiderwebs, torches, etc.  Used to add ‘color’ to the map. Also added persistence of cell adornments.


When you kill a mob there’s a chance it’ll leave a corpse.  Each monster specifies its corpse tile in its Monster.xml definition.

Monster Classes

I’ve added a new MonsterClass object which groups a collection of monstertypes.  LevelThemes include a set of one or more MonsterClasses and monstertypes are chosen from that list of classes to populate the Level.

When selecting a monster for inclusion in a level, a combination of the available MonsterClasses (defined by theme) and the current level depth is used to determine which monsters to include. So a MonsterClass can include *all* spiders, and if a level 1 theme uses that class then only the spiders whose range (defined for each monster in monsters.xml) includes 1 will be considered. Add on top of that that monsters can specify rarity (common, uncommon, rare, and named), and it requires a little bit of up-front work to ensure a monster can be quickly picked. That upfront work happens on level creation and is found in the LevelTheme.InitLevelMonsterTables function (below). LevelTheme.GetRandomMonsterTypeForLevel is then called for each monster that needs to be created.

        public void InitLevelMonsterTables(int level)
            for (MonsterRarity rarity = MonsterRarity.Common; rarity <= MonsterRarity.Named; rarity++)

            // If the Monster can be found on the specified level, then include entries for it.
            foreach (MonsterClass monsterClass in MonsterClasses)
                foreach (MonsterType MonsterType in monsterClass.MonsterTypes)
                    if (level >= MonsterType.MinLevel && level <= MonsterType.MaxLevel)

        // Pick a Monster that's appropriate for the specified level from the MonsterTypes in this MonsterClass
        public MonsterType GetRandomMonsterTypeForLevel(int level)
            MonsterRarity rarity;
            float rand = Rand.NextPercent();
            if (rand > .995f)
                rarity = MonsterRarity.Named;
            else if (rand > .95f)
                rarity = MonsterRarity.Rare;
            else if (rand > .8f)
                rarity = MonsterRarity.Uncommon;
                rarity = MonsterRarity.Common;

            while (rarity >= MonsterRarity.Common)
                if (LevelMonsterTypes[rarity].Count > 0)
                    return LevelMonsterTypes[rarity][Rand.Next(LevelMonsterTypes[rarity].Count)];


            return null;

And one last random thing: Xbox!

I got curious as to how much effort it would be to get this running on Xbox.  There’s no input yet and I’m not going to try to keep it up to date (I’ll add Xbox later), but it took about 5 minutes to get to this screenshot (from the Xbox):

Obviously not perfect, but impressive that it was so quick to get it up and running!  Interestingly, the one issue I ran into is my level validation code does a basic flood fill, and this was basically horking the box.  No idea why, this is *very* simple code; I’ll have to investigate and see if there’s something about recursion on Xbox.  Here’s the code (which I just skip on the Xbox for now):

        private void FloodFill(Cell cell)
            // Mark this cell as Visited
            cell.Test_Filled = true;

            // Visit all visitable neighboring cells
            foreach (Cell neighbor in cell.Neighbors)
                if (neighbor.CanWalkOn() || neighbor.CellBaseType is CellBaseType_Door)
                    if (!neighbor.Test_Filled)

Pretty straightforward, right? Ah well, it was just an experiment anyways; but cool to see! 🙂

Updated source:

Updated executable: