The first inhabitant of every game world should be the player himself. Not because he might have something interesting to do yet, but to do Usability Testing. The most important thing to a game is how the users view it. Games receive little points for being technically sound but magnitudes more for how fun it is.
Know who the first player is? The developer(s) should be the first player. The most effective builders of a product are those who use it themselves.
Continue reading Morgemil Part 11 – Someone has to live in this world
Today I’m connecting dungeon rooms together.
My very first inclination was to make a Minimum Spanning Tree of some sort. I quickly discarded that idea as I thought about how I would construct a dungeon: with lots of passages. So I’m going to make up my own algorithm which is likely not original at all.
Continue reading Morgemil Part 10 – BSP Dungeon Generation Part 3
I’ve started a list of tiles to be found
let DungeonFloor = TileDefinition(1, "Floor", "Dungeon floors are often trapped", false, false, TileType.Land)
let DungeonWall = TileDefinition(2, "Wall", "Dungeon walls are built from the prisoner's bones", true, true, TileType.Land)
I’ve also changed the coloring slightly which is reflected in the header image.
let tileColor =
match tile with
| _ when TileDefinition.IsDefault tile -> Color.Black
| _ when Tiles.DungeonFloor.ID = tile.ID -> Color.White
| _ when Tiles.DungeonWall.ID = tile.ID -> Color.Red
| _ -> Color.Gray
Continue reading Morgemil Part 9 – BSP Dungeon Generation Part 2
Towards my goal of interesting dungeon generation. I started looking into various techniques and the first thing to catch my eye was a visual explanation of BSP Dungeon Generation.
This appeals to me so I wrote out some code to do just that:
Continue reading Morgemil Part 8 – BSP Dungeon Generation Part 1
My first order of business is to create a world. So before I go off typing up a code-storm, I want to give the one defining rule:
Interesting results is more important to Morgemil than consistent or realistic results. I will leave making an amazingly complex believable world up to the genius making Ultima Ratio Regum.
Now that the rule is out of the way, I’m going to start: The portion of the world I’m building today is a simple dungeon. The generation of this dungeon will be absurdly simple. The bigger concern is storage of this dungeon.
Continue reading Morgemil Part 7 – A whole new world
Morgemil can be loosely defined as a roguelike. Indeed, I’ve drawn much inspiration from some roguelikes of which the first I ever played was Angband eight years ago. The largest influence is ToME 2 “Troubles of Middle Earth” which is now mostly defunct and replaced by ToME 4 “Tales of Maj’Eyal”.
Now something most roguelikes have is procedural generation placing the player at the mercy of the Random Number Generator (RNG). This, simply put, determines whether the player lives or dies by creating unique dungeon levels, the level’s population of monsters, and the loot dropped by monsters.
My search’s first action was to check RogueBasin’s article on RNGs which quoted the Mersenne Twister as a good candidate “which produces fast and high-quality random numbers“. This is a sufficient recommendation for me so I then started searching for an implementation. I am fortunate in that there is an existing Mersenne Twister implementation in F# from a reputable source, the Math.NET Numerics library. So armed with the NuGet package I set to work.
/// Type alias for a System.Random implementation
type DefaultRNG = MersenneTwister
let SeedRNG(seed : int) = DefaultRNG seed
/// Given an RNG and likelihood, returns the success
/// <param name="chance">[0.0, 1.0]</param>
let Probability (rng : DefaultRNG) chance =
match chance with
| 0m -> false
| 1m -> true
| _ -> (rng.NextDecimal() >= chance)
This is not perfect, but it is at least a placeholder while I figure out what is needed. Testing this is a problem. All I know to test is the edge cases of 0.0 and 1.0.
let ``RNG Probability test``() =
let seed = 150
let rng = RNG.SeedRNG seed
Assert.IsFalse(RNG.Probability rng 0m)
Assert.True(RNG.Probability rng 1m)
With pseudo-random numbers I shall create a world generation equation that will dwarf any amount of creative content I could create myself.
My brother and I once took a ballroom dancing class where we were taught the basics of the waltz, salsa, tango, and foxtrot. I wasn’t much interested in the salsa and I no longer remember the foxtrot or the tango. What I do remember is a few steps of the waltz, especially the box where the movement of your feet make the corners of the box.
The dance instructor kept me making the box for a while and it was a little boring, we didn’t actually go anywhere. And that is what I feel like I’ve been doing on this project: taking a step backwards for every step I take forward. After I learned how to make the box and practiced it for a while, the instructor then showed me more steps that can be taken with the waltz to allow free-reign of the ballroom. In the same way, I’ve been brushing up on F# and functional paradigms so that I may have all the power they offer.
Continue reading Morgemil Part 5 – A step backwards and forwards