Part of the code upgrade from Soulcaster 2 to Soulcaster 3 is the way creatures attack one another. Since we’re going to have lots of complex equipment, magic, and creatures in the game, there needs to be a flexible system that can handle a wide variety of special abilities: Life steal, stuns, poison, regen, invulnerability, mind control… All of these things need special code, and need to be informed of the events of an attack so they can play their special role.
Exposing events for customization like this is called adding hooks.
There are two phases to any attack: targeting (finding a suitable foe) and collision (the actual hit where damage is done). Let’s look at both of these in depth to see how it handles everything behind the scenes.
In this case, our immortal archer, Shaedu, is wielding a bow with a poisoned arrow. A rat is nearby.
Last night, I had the opportunity to show the latest build to some friends, who are developers themselves. Now usually I take pity on any poor soul who has the misfortune of asking a developer to playtest his or her game–but I have to say how impressed I was with their restraint; all three simply offered feedback from a player’s perspective, and didn’t rework the design of the game from the ground up, like I have a tendency to do.
My friends shall remain anonymous here, since I didn’t ask permission to use their real names in this post, and these days, one can never be too cautious about this sort of thing.
The one who played the longest actually took the time to clear the entire floor of monsters (which took about 15 minutes). He said he was compelled to do this because
He likes to completely clear games out of principle
The rats were so repulsive he felt the need to exterminate them to extinction
It made me realize one of the things the prototype is sorely lacking: state changes on the in-game map, to show which rooms have been discovered and cleared. It also made me think a bit about rewards for clearing an entire region–maybe this is how you get the boss to appear, or maybe a special NPC (like a merchant) appears only once the realm is free of monsters. Or I could go full Captain Eo style, and have the boss turn into a friendly NPC after you break the corruption within him. (Bonk’s Adventure did this too, now that I think about it.)
I’m amped. I started writing a post on game production and teamwork versus working solo, but it’s just not something I can do tonight–draft saved. Right now I just have to celebrate the squashing of an ancient, pernicious demon bug from hell in the Escape Goat code. Could it even be called a bug? It never even revealed itself in that mortal form. It stayed for weeks, months even, as the Feature that Wouldn’t Be.
It was creature squish detection.
Here’s the situation in a nutshell: If a block falls on you, or you get mashed between two moving walls, you get squished and have to restart the room. Makes sense. And detecting this is easy enough: if after resolving all collisions (clipping creatures to the edges of walls) a creature is still intersecting with a wall, you’ve got a squish happening. That means one wall clipped the creature into another wall and it can’t be pushed away.
There was a problem with this.
Getting squished by a single pixel of falling block was annoying. So was getting crushed when you were only barely inside a moving wall. Come on, that should just nudge you out of the way… it’s barely grazing the goat’s tail! You can’t make me restart this whole puzzle for that.
But… I also can’t take out squishing. What if you get crushed by a block, do you just get to clip through the top of the block? Or through the floor below? That’s no good. Squishing machines are a great hazard device in this game. I’m not giving that up. There has to be a balance… you need to be able to tolerate a few pixels of squishing and “nudge” the player out of the way in those situations.
I tried about 5 different methods of handling this. And just a couple hours ago, after intense frustration and perseverance, I found the solution:
Do all clipping of creatures to walls and world obstacles
If any creatures are still intersecting with walls, start a crush timer on that creature, which lasts for 5 frames
If the crush timer reaches 0, kill the creature. (It took too many frames to resolve it to a safe position.)
If the creature is not intersecting, store its position as a “safe position”
If the creature is no longer intersecting, reset the crush timer, and compare its new position to the safe position that was stored before. If the distance is greater than the squish tolerance (I set it to 5 pixels in either axis), kill the creature.
Essentially, give the game 5 frames to resolve a safe position for the creature after being crushed. If this new position is too far from the start position, well, I can only be so forgiving.
When you face a feature from hell, stick with it, draw pictures, and try solving it from a variety of angles. You’ll get it eventually and it’ll feel great.