With my mid-game crisis more or less over (see last week!), it was time to get my hands dirty again and start digging into the core code to expand on the gameplay for Skein. All I had at that moment were a bunch of test rooms which ran the same scripts and generated pretty much the same gameplay experience, just with slightly different graphics, so my first real task would be to organise this into something a bit more progressive and distinct...
I knew right from the start that I didn't want my game to be a simple succession of rooms all based on the same maze algorithm, so I had to decide what kind of rooms I wanted and then modify my BSP code to create them. The structure I came up with was something like this:
So, we have the general levels (which are those created as normal by the BSP scripts), a treasure room, an arena, and a cave. What was going to make these levels distinct? I decided to start by working on the cave levels first, since they were going to be my way of connecting the over-arching sections of the game world. I wanted the levels to be different over time, and in most games of this type that simply means a change in graphics and monsters - look at Diablo, for example - so in a rather arbitrary way I decided that every ten levels would lead to a cave, which in turn would take you to a new section of the dungeon with new graphics and enemies.
After creating some appropriate cave/mine style graphics, I set the exit object to create a room that was longer than it was tall for my new level. All the levels in Skein are, by default, square, so that making these areas different would help communicate to the player that they are in a transition (I actually made the exit script create a room that would be either long and narrow, or tall and thin). I also made these room have a lot darker lighting to increase the feeling of a transition - I have corrected a little the screenshots below to compensate for this - but I still wasn't happy with the result... natural caves aren't square. My BSP scripts only create square and rectangular room areas!
However, I wasn't done yet with this and I remembered that there are some interesting functions for working with DS grids which are based on circular ("disk") samples of the grid. I looked them up in the manual to refresh my memory and that got me wondering whether they would be a viable way to generate cave spaces.
Going through my BSP scripts with this in mind, I saw that it would actually be quite easy to use the disk functions to generate circular cave spaces. My scripts create an array that stores the top left x/y position as well as the width and height of the room area, so all I had to do was get the minimal value for the width/height, then use that to get the radius that a disk region could be. Once I had the radius I could then choose a position along the x or y axis of the area (whichever was larger) and mark the area as "empty". if I also make the minimum size for the splits in the binary tree, then I end up with something a bit like this:
Okay, that is a bit more like what a cave should look like! There are still straight parts, but given that the graphic theme is more of a mine than a straight cave, and given that the room was for linking sections within the overall dungeon, I think a few straight corridors are to be expected and certainly work fine within the context of the game. I was really very please with this, and after some minor tweaking of sizes and positioning, I had a great looking cave, and all built on 90% of the same code I used for every other room in the game.
In the flow chart above you can see I have also marked a room as an "Arena". the idea here is that at certain times in the game you will be transported to the arena where you must battle a huge number of enemies and a "champion" enemy, but there would be no doors or walls or anything else to get in the way, meaning that you couldn't just run away and hide then pick the enemies off. Again, like the cave, I wanted to have something that breaks up the core gameplay and makes the player think and react in different ways.
My first attempts at creating an arena were, sadly, rather pathetic...
Yes, that was indeed how my first arena attempts looked and, to be honest, it was no fun at all to play. Run, stop shoot. Run, stop, shoot. Run, stop, GET BORED. I needed something a bit better here, and so I got to thinking again about using the disk functions for DS grids, only this time I thought, why not use it to make the whole area circular? Arenas should round right? Like bull rings, or like the Acropolis. And why can't they have some walls in them? The player really needs something so they can do a bit of "duck and cover" to use magic, or hide, or at least stem the flow of enemies a bit. But if I make the whole area circular, how will I add walls?
I realised at this point that I couldn't use my BSP scripts for this level and started writing a new set. First to simply clear the room with walls, then punch a hole in the middle of it to make the floor area. I also added in the player and the exit right in the middle of the room. I was going to make the exit "locked" and only open when the last enemy was slain, so there was no reason not to put them together right at the start, and having them in the centre seemed the logical choice. It would also help give the player a bit more of an "oh crap" feeling when they enter the room!
For the walls and generators, I decided to go with a less random approach, and sectioned the room up into quarters, then had a script populate the available space for one quarter with a few enemy generators and walls - making sure to set the amount of walls to a very low number so that only four or five would be created, and the number of generators would be spawned based on the current game level that the player had reached. I then took the newly generated quarter grid and mirrored it and flipped it so that the room was symmetrical along both axis. The final result was much more interesting I think, especially after adding in some lighting.
A good dungeon game shouldn't be without it's hidden rooms and secret vaults full of gold and other treasures, and Skein was going to be no different. I wasn't sure yet just how I was going to have the player find a treasure room, so I just made it spawn randomly, the same as the arena - I could work out the when and why in more detail later after I'd worked out the how.
I wanted the treasure rooms to feel more like bank vaults than like a dungeon or a cave, so I used the same metallic looking tiles I'd made for the arena, only this time I re-coloured them on the fly to make them more yellow/gold coloured rather than red (I should mention here that all my background tiles are actually grey-scale so that I can colour them if I choose - it's a simple trick but can make a great difference when used sparingly, as you can see from the screenshots here). I figured too that my BSP scripts should be fine to use as I wanted square edged rooms... but what would be the twist on the room structure then? I liked what I'd done before with the arena and wondered whether I could do the same flip/mirror trick with the treasure room, so I copied over the arena code into another new script and set about having it generate a full room, then take only the top left corner and copying it as required.
My first test of this script was a disaster. A complete and utter failure, mainly due to it creating rooms with no doors into or out of them, or for creating huge areas that were just walls with a tiny room areas to one side (again, with no connections to anything else). Joining the rooms in a coherent way seemed like it was a pretty rare occurence and there was also a pretty serious issue with the player and the exit spawning inside walls and doors. Not good. However I wasn't put off and actually expected this kind of thing, so I set about re-ordering and re-factoring the scripts in such a way that after the room spaces were calculated, they were then flipped, and then the doors, walls and player were added at the end.
A quirk of these changes was that doors were now "connected" over a room. So if you opened a door, it's mirror door would also open. I could fix this if I wanted, but after testing a few times I liked the effect, as if there were enemies behind that other door then they would get out and come at you from behind. So, the treasure room was pretty much done too, although I confess that I wasn't 100% happy with the results. Yes, they looked quite nice and were distinct enough from the regular levels, but the scripts failed to make a room as often as they managed it and I had to add in extra backup scripts to check that all areas of the room were accessible and that the player and exit had been correctly spawned. The room restart was seamless and the player would never know it was happening, but I knew that I'd have to come back to this later and sort it all out, especially as there was a very slim chance that the game could get stuck in a loop of constantly generating invalid rooms... unlikely, but still enough of a possibility to make me worry.
However I was a bit tired of working on this aspect of the game wanted to get on with other things... and since this (sort-of) worked for now, that was enough for me!
I'd added in about 8 or 9 enemies to the game at this point, but they currently didn't have much in the way of statistics or powers. The differences between them were in fact simply based on the sprite and nothing else, which meant it was time to start fleshing out this aspect of the game. I now had a more structured concept for levels and I needed to fit the enemies into this structure. However, I'd like to talk a bit more in depth about the decisions I took with the enemies, so I'll leave it for next week's tech blog I think...