Another feature of the region code I’m developing is that it will allow me to implement moving platforms in Unangband. A platform overwrites the existing terrain with a new terrain type (say ‘wooden deck’) and records the old terrain type within the region code (a single scalar value can be stored for each grid in a region – this will record the terrain type originally underneath).
Then, when the platform moves, we rewrite the old terrain, translate the platform region one grid, and then overwrite the terrain in the destination location with the platform terrain type; recording the original terrain in the region data structure. It is important for this discussion to realise that we can’t stack multiple terrain types: each grid can have only one type of terrain.
Impassable terrain, monsters and objects complicate the algorithm. Impassable terrain is defined as any terrain through which the platform cannot pass. I’m still deciding how to define this flexibly: I suspect I’ll have a number of different platform types, capable of moving over floors, chasms, water, lava and so on – each of which is currently represented by a flag in the terrain data type.
To determine if the platform is going to move into impassable terrain, we’ll simply make a copy of it, translate it, and then fail to move the platform if the translated copy intersects any terrain it is not permitted to exist on.
For monsters (and the player), if they are standing on the platform, they’ll be translated along with the platform. If they are in the path of the platform, and capable of standing on the platform terrain, they’ll stay in their existing location (effectively stepping onto the platform as it moves over them); otherwise they’ll be pushed into an adjacent grid which is neither in the current platform location, or any grid of the translated copy.
Objects on the platform always move with it, if they are in the path of the platform they are always pushed: if no grid exists which the object can be pushed to, it is destroyed.
How do we get platforms to move in the first place? The fallback for many games is to have the platforms moving independently on a fixed path, and allow the player to jump on and off the platform at will. But this will massively complicate the level design – it may still be useful, but I have a smarter idea.
A key component of roguelike design is the ‘bump’ – that is allowing the player to interact with the game world by the process of attempting to move into it.
I’ll extend the concept of the bump to that of pushing. An terrain feature is push can be pushed by the player by having the player attempt to walk into a normally impassable piece of terrain, and the terrain feature is moved to an adjacent grid, allowing the player to pass. This process of moving requires that we make certain assumptions about what the underlying terrain is: for normally pushable terrain, we assume that the underlying terrain is an empty floor grid. In this instance, we can push terrain around as follows:
- Choose an empty floor grid adjacent to the pushed terrain – first in the same direction as the player attempted to move, and then check another adjacent grid, alternating clockwise and counter-clockwise from the first grid considered.
- If an empty floor grid is found, overwrite this grid with the terrain type.
- Overwrite the pushed grid with empty floor terrain.
- Move monsters, objects as described for moving platforms.
- Allow the player to move into the now empty grid.
By combining platforms, and this concept of pushing, I can allow the player to push platforms around by defining a second region which contains ‘pushable hotspots’ (shown on the irregularly shaped platform to the left as '*' symbols) that, instead of just pushing a single grid, moves the entire platform in the direction the player specifies.
In this instance, I have to specifically link multiple regions together. But I have to do that anyway, to handle the instance where I might have multiple intersecting platforms. Consider the case where a platform of ice is created over a wooden platform. The wooden platform stores the underlying terrain types in it’s data structure; the ice platform stores only those regions which don’t intersect the wooden platform, for the rest it stores the fact that there is a wooden platform terrain type under it.
If the wooden platform moves, I’ll suddenly overwrite parts of the ice platform. So I have to come up with an algorithm to allow both platforms to correctly move over each other. If I have to worry about this special case anyway, I can readily enough include the code to move both platforms at the same time if they are linked together.
I can even get trickier, and demand that my pushable hotspot(s) have more strict requirements for what terrain they sit on than the rest of the platform. Consider a wooden platform that is held up by a chain over a chasm. I want to allow the platform part to exist anywhere in the chasm, but the part that is connected to a chain to only move on terrain that has the chain in it.
So I make this part the pushable hotspot, and apply the more stringent requirement to the pushable region.
This is where things start to get interesting: up until now I’ve assumed that a platform is built of a single terrain type – plus a second for the pushable regions. But I could equally build a platform from an ASCII template and have a mix of terrain types, pushable regions and grids which expose the underlying terrain. And this lets me implement vehicles: like sailing ships, but drawing the ASCII template onto the map at the vessel location. Bear with me as we look at some ASCII art:
This sturdy rowboat has two pushable regions: the oars. I mark these regions ‘reversed’ so that by pushing at the oars (‘*’), you go in the opposite direction to that you pushed. By sitting between the oars you can steer the boat left and right, sitting in the prow you can propel the boat forwards.
This large galley is filled with the undead spirits of sailors. By beating on the drums (‘*’) you cause the undead on that side of the galley to row – again in the reverse direction to your beat. The large deck will lead to much combat and looting of treasure.
This sailboat has a single sail (‘<’) that you can push to slowly propel the boat along - ideally you need a gust of wind, perhaps from a spell, to move quicker. By pushing at the boom (‘'’) you can reflect the way the template is laid out, causing the sail to swing to the other side of the boat.
The complication with the templates, is that I need to be able to rotate the templates as well as translate them. I need a robust design to handle this – but using the Angband project_path function should allow me to rotate the vertical strips of the template to any angle of a fixed length. Although a flight of fancy at this stage, there is the real possibility that in a future version of Unangband, you’ll be able to take to the sea and avast the land lubbers in other roguelikes.