Yes yes, back I am for another evening, here once more to seal our Pact. Even though out of all of you who read the previous post, only a single one amongst you upheld our bargain! Disappointing. But not unexpected. Nor unacceptable. Beggars can't be choosers after all, and we are nothing if not cursed to be beggars, every last one of us. We shall take what souls we can.
Let me offer another glimpse then, into the festering morass of jam coding. Know you the riddle of Procedural Map Generation?

One person who played our game described procedural generation during a jam as a Decision. Ambitious. It pleases me that its presence pleases or impresses at least some of you. Others have said that the map geometry made ten second clears very difficult. This is fair and also true. Plus, we're hardly the only ones with procedural arenas and I'm sure there are entries out there with more sophisticated implementations. But hey, like I've said, one of the few things setting this project apart from the pack is scale. By the third day, Witch Contractor could well have been on the way to being a project of ten levels and a few dozen meaningful maps. Instead we managed forty encounters and something like 1.6 billion possible maps.
Quite early on, I unilaterally decided that the game would be lower resolution than all our previous ones. I wanted to try doing smaller pixel sprites than before. In the end, the art direction of the game never really came together. Much as I'm not really a programmer, I'm also not really a pixel artist. I just kind of mimic and bullshit my way through. Some of the flaws you can see (but maybe don't notice or quite put your finger on) is how we have black outlines for most things but no outline on the heroine. However, the point is, the 540 pixel tall screen stuck.
At some point, we noticed that my equally arbitrary 14 pixel margins left us with 512 pixels in one direction. 512 is, of course, a power of 2, an integer replete with numerological significance and magical qualities. If we used 32 pixel tiles, we'd comfortably have a play area composed of six blocks of 8 x 8 tiles. We decided to base any eventual procedural generation on this geometry. Now of course, I can already hear you lot whispering: "But what about noise generation? What about random rooms using physics boxes? Or waveform collapse? Or A-star that Godot has an object for? Look. I don't know what any of that stuff is! Heck, I didn't learn until a week after the jam how to make a camera follow a dude around. You can look back at all our previous games - all single screens! This 6-block format was immediately attractive to us because we strongly suspected that trying to have our main coder learn something more exotic would be project management suicide. The six-block format was closer to our actual skill level as devs, would let me contribute to it, and would still offer good variety.
Much as with mobs, the game spent most of development with a very limited number; just three 8x8 map blocks in addition to a fallback block, which was a block that has both mob and PC spawns and was composed entirely of empty tiles. Some hasty logic was put in place to put those in if map generation screwed up somehow. The composition was, at this point, totally random. We were now into the third day (the first part of it, just before I also fixed mobs) so there was no question of rewriting the placeholder entirely. The question was how I could improve it and expand it horizontally within my own abilities without taking up too much of the main coder's time. One part of this was that I spent a great deal of time playtesting my own game. The foundation of procedural generation, I believe, is doing the thing enough to come up with some rules and then taking those rules to generalise more and more. Letting RNG do all the work is bound to create insanity that needs to continually be patched by more and more rules. Gaze at the abyss and it will look back. One of the regrets we have is that we didn't put in the cheat early (because I, in fact, cannot clear my own game) so a lot of the bugs that crept in were related to later levels but that's an aside. Testing was also key to putting the player's speed, rate of fire and spread of fire values to where they were at launch. The player gained a great deal of speed from early development versions and then lost most of it again when we were sure we would have time to put in powerups. Rate of fire was greatly increased because shooting more bullets feels good and mobs were given escalating hp to compensate. Finally, accuracy was placed where it was and then tightened up a bit post launch in the balance patch.
While testing, one of the immediate concerns I had was that spawning next to mobs sucked because the timer is strict and most likely, every player hits every level with one direction key or another held down. So mob and player spawns had to be separated. The magic circle shares a spawn with players and I wanted it to be in a different place from where the player starts some but not all of the time. Finally, we needed to make sure every map was clearable. The simple implicit rule that all the existing blocks followed up to then was to keep blocks 4 and 5 clear at the edges of every rank and file, guaranteeing corridors between every block. That became very noticeable however, so I needed at least something to break that up some of the time. So what about blocks that break that rule? With two of them, it was possible to seal off a corner of the map and make it unclearable. Now obviously I could have done more sophisticated manipulations with the positions of the blocks and their orientation and so forth but with relatively limited time, I went with a simple rule of only having one block at most that would break this rule and also added an additional rule that every single block's spaces had to be contiguous - you can shoot every space in a given block that could spawn a mob no matter which side you enter from. In order to guarantee this composition, I split the blocks into three pools and then sat down and made about sixty of them. This is what it looked like in the end:

Is it elegant or sophisticated? No, but it works and it was something we knew that I could handle and expand upon when the time came. Every block is just a bunch of arrays describing the tiles that the game needs to set into each cell when the map is built. Then there's a list of spawn points for mobs, whether or not it can spawn a player or magic circle (requires a 4x4 cross in the middle). The blocks are split into three (or I guess four) pools:
Spawn Blocks contain no mobs and all have a clear area, clear exits in the middle of each side, and can spawn a player or a magic circle. There is one guaranteed per map and a 33% chance of a second.
Dead Blocks disobey the clear exit rule, even though they might be relatively clear overall. They may or may not spawn mobs or players/circles but never both. If you look at the screenshot above, the top right block is actually a dead block. It seems relatively open, but it is classed as Dead because it does not allow access from the middle of the top or bottom sides. There is a 50% chance of having one on a map, and a 50% of there being none.
Normal Blocks spawn mobs but not players or circles and have clear exits.
Fallback Block: In all of the testing we had, we had a single report of this actually triggering. It's extraordinarily rare and I think it requires the RNG to pick a certain set of blocks combined with a certain set of mob encounters that require large numbers of spawns. Hell if I know. That isn't fixed. If you're extremely lucky, you should technically still be able to cause a fallback map to spawn.
In this way, we went from "We don't have enough map!" to "We have enough blocks that I can comment out a few if they turn out to be problematic. One of the candidates was the infamous death corner:
That is, it is infamous among those who have played this game and tasted the fearsome Corner Orange.
During testing, I did notice that this formation of blocks was maybe the most difficult the game had to offer. The bottom left block isn't a Dead block because it allows entry from all sides (it only looks blocked at the bottom because of the final outer wall). However, it has less clearance than other blocks so you can only loop around the north if it happens to be end on against the wall of the arena. Put a high hp enemy in the corner, like an Orange, and it can easily end your run. In the end though, I didn't remove it and maybe that's why this game still hasn't been cleared yet by the community though several have made it into the mid thirties.
Now come! Cast your lot and fulfill our Accord. We pine for your presence! Throw yourself unto the Abyss of Bottomless Time and taste the bittersweet futility of the Corner Orange.
Witch Contractor: https://ldjam.com/events/ludum-dare/51/witch-contractor