What a great idea! I was wondering how do to something like this.
A couple of techier (for Pulp) bits I thought might be interesting to share:
Implementing simple stealth
I wanted to make some simple line-of-sight stealth mechanics - think being spotted by Pokemon trainers. I thought through a few different ways I might achieve it.
The naive approach would be to loop over each guard (or whoever is trying to spot the player) every frame and then look out along their line-of-sight until you find the player, hit a solid wall or reach the edge of the screen. With a bit of code this could work but it'd mean adding code to (or called from) the game's loop event and it could be quite impactful if I have several guards on screen and am needing to check each guard's line of sight every frame.
My first "trick" was to flip around the way of thinking about line-of-sight and instead start from the player. That means only worrying about four lines-of-sight in the cardinal directions from the player.
My second trick was to decide to use chained events to propagate along and check the lines-of-sight rather than having some code do it from the loop event. Every frame I already call an event on the tile at the player's location called "playerPresent" (I use it for hiding the player and taking damage on spike tiles). I realised I could add a "playerPresent" event handler to my floor tiles that would trigger four events, one for each cardinal direction, that would call the same directional event on the next tile in that direction. Provided the next tile in that direction similarly implements that directional event the event will propagate outwards along that line of sight. If it reaches a guard, the event will be called on the guard, and I can then check if the guard is facing in the right direction (from their current frame) and have them react accordingly. Even better, when laying out rooms I only need to use floor tiles implementing these events if they will potentially be in a guard's line of sight (so while I could have the events on every floor tile, it's cheaper not to). All the code ends up contained in the relevant tiles and there is zero overhead otherwise as I already had the playerPresent event.
That implementation looks a little like this (the "v" tiles are placeholder floor tiles implementing the directional events):
One neat advantage of this approach is that doing something like pushing a block into the guard's line-of-sight will block their sight without me coding for that specifically!
Randomising on which tile something happens from a set of tiles
Or in other words - whack-a-mole!
This mole enemy will popup from a random mole hole and throw a stone in the cardinal direction closest to the player.
I wanted to randomise which hole the mole would pop up from but without hardcoding the locations of all of the mole holes on the screen, and also without using loads of variables to store the number of holes and their locations.
After a bit of thinking, the solution suddenly occurred to me using only one variable. When I want to spawn a mole I first set this variable to be 0. I then emit a "spawnable" event. The mole hole tile implements this event to simply increment the variable, so when the emit completes I have a count of the spawnable tiles. I then reassign the variable using the
random function to get a number between 1 and the variable itself i.e. the count of spawnable tiles. This is effectively the index of the list of spawnable tiles where I want to spawn the mole. I emit a second event, "spawn", again implemented by the mole holes. This event checks the variable - if it is greater than 1, it decrements it and finishes. If it is equal to 1, it spawns the mole and then decrements. If it is equal to 0 it does nothing. You can think of emit as looping through a list, and my randomised variable decides at which index in the list something should happen.
Now I can add, remove and move mole holes around a room and know that the spawning will always be randomised correctly, no further effort required!
Here's how that line-of-sight stealth is shaping up in-game:
The screen wipe transitions are implemented using
crop. Unlike other approaches,
crop doesn't have to be called from the player's
draw event, so I could keep the code for the screen transitions all self-contained.