Just writing this down in order to think my way through it.
So I'm building levels for the game and I've gotten to this point where I want to have the player climb up a spiraling tower. As the player ascends, the areas at a higher elevation, i.e. beyond the cliff face, will remain hidden but the areas on lower elevations can be seen (within the players current light). Also if the player walks off the cliff edge then she will fall over the cliff (without player control) and land on the flat lower floor.
Now the first part is already handled by my existing shadow casting system. The shadow (and collision) vectors are baked into the tile-set sprite-sheet and only vectors whose right-hand normal is pointing towards the player is considered for casting a shadow per frame. Therefore, when the player is on the 'higher' side of the cliff on a tile (as I have my tiles drawn) she can look 'down' into the lower section with no shadowing.
This is all fine for just the shadow system, but a problem arises for my second feature wish as there actually is no 'higher' or 'lower' part of the tile, other than the visual representation. While the collision vectors as I have them currently set up will prevent the player from moving from the 'lower' to the 'higher' areas of a tile (which is perfectly fine for all the levels I've built to date; same elevation, penned in by walls), movement in the opposite direction will go unhindered. This allows the player to move on and
along a cliff face, thereby allowing her to reach areas intended to be inaccessible such as the summit of the spiral.
So I need to designate areas that if walked upon, or walked off, will move that object to a lower elevation.
My initial thought was to use the rooms map data. The layout of rooms are saved as a png image, where each pixel of the png represents a tile in the room (the influence of Metaplace again). So for example, this room is 17x17 tiles in size, so its map png is 17x17 pixels. Each colour value of a pixel represents a certain piece of data, with values from 0 to 255; red stores the tileId (which tile to pull from the tile-set), the green I'd set aside from the start for elevation (defaulting to 128) and blue I had originally used for blocking state in my earlier simple collision system, but is now unused (I think Metaplace were going to use it for water).
So my thinking went; what if tiles with an even elevation are flat, and odd are sloped. Then if an object moves from an even origin tile to an odd destination tile, whose value is lower than the origin, then the object would be pushed past the odd tile. The image to the left is from the map editor showing elevation data overlaid on the room, with red tinted being even and blue tinted being odd (there are different shades there within each red and blue, indicating increasing elevations as you climb the spiral, but it doesn't show up well). There are a number of problems with this approach, but not the least of which in my mind was with - again - how the tiles are drawn. Some tiles show a lot of high ground with almost no low ground (the player would visually appear to be on flat ground, but is actually on a 'slope' and so falls), while others go low-high-low. Examples can be seen in the tile editor above.
Now one answer could be
"Why on earth do you draw your tiles that way? You're just making things harder for yourself. Don't do that!"
The conventional wisdom for top-down tiles, I imagine, is that a wall tile is just a wall, you can't walk on it. While this almost certainly makes things a lot simpler in terms of collisions and the like, I feel it also makes level design that bit less interesting.
So although my multi-elevation tile set up is undoubtedly complicating things for myself, it provides more freedom to make not only the spiral but such situations as shown to the right, where I can butt two tiles up against each other to create narrow walkways for the player to navigate, with pits on either side.
So sticking with my madness, I think I'll have to embed either elevation or slope data into the tile-set, in a similar way to the shadow and collision vectors. To implement those vectors I had added an extra row and column of pixels to each tile in the tile-set sprite-sheet (as seen above). Each pixel in the top row represents a shadow vector and in the left-hand column are collision vectors, with the top left pixel being a control (detailing how many of each type). So each pixel has to store the four values of the x & y coordinates of the start and end points of the vector. Initially my thinking was that since I have four values to save, and there are four discrete values for each pixel - red, green, blue and alpha - it should be a pretty straight-forward translation. But because I create the png image files via PHP, and PHPs GD2 image library handles alpha values differently from the colours (differently enough that it was a bit too annoying to work with), I had to limit myself to using just the three colour values.
So I had four values and three variable spaces to store them in. Or to look at it another way, one 24 bit space to store them in (each colour value being a byte, 8 bits). That leaves me with 6 bits per coordinate value, which is a maximum value range of 0 to 63. So I am limiting myself to a maximum tile size of 64x64 pixels.
The four values that define a vector can just a easily define a rectangle, which is what I'll need to mark out either areas of elevation or areas of slope. But I'll also need additional information to then define that elevation or slope. If I were to reduce the coordinate values to 5 bits, leaving me with maximum tiles sizes of 32x32, then I would be left with 4 bits for that extra data. Now it turns out that 32x32 tiles is what I intended to used in the finished (ha!) game, what I'm using now are just placeholders (and are only 20x20 at that, which will cause me headaches down the road) until I find someone with actual talent to create art assets for the game. So what can I do with those 4 bits?
I think the easier option is to define slopes, then those 4 bits can be
+x, -x, +y and
-y. This only gives me eight directions of travel, but I think it should be sufficient.
So I think that's what I'll push ahead with and see how it works out!
--- brain emptied, cue tumbleweeds...