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.
- The archer’s WeaponHand (has all details on the equipped weapon and timing) is instructed to find an immediate foe.
- The WeaponHand creates a simple data object called a FoeFinder which represents an individual search for a target. It is tagged with the attacker (archer) and the weapon used (bow).
- The FoeFinder object is passed to the Arena (which has a list of everything currently in the room), with the request of finding a suitable foe.
- The Arena checks the FoeFinder’s weapon tag, and calls on the weapon’s Targeter reference. The Targeter is a small piece of code that checks for immediately hittable foes, given the weapon’s range and capabilities (such as clipping through walls).
- Aha! The Targeter finds a rat within the bow’s range, and there are no walls obstructing the shot. The rat is tagged in the FoeFinder and control is passed back to the WeaponHand.
- The WeaponHand checks the FoeFinder object to see if a target was found. Yup, the rat reference is there, so let’s shoot that sucker.
- The Attack Warmup Timer is loaded with the number of warmup frames in the weapon’s stats. The archer is notified that an attack warmup started, so she aims the bow towards the rat. (The delay gives a slight dramatic effect, and prevents dead-on aiming of fast-moving foes.)
- When the Warmup Timer ticks down to zero, a Projectile Attack is created (the arrow). It spawns at the archer’s current position, with velocity vectors set to hit the rat’s position.
- The Attack Cooldown Timer starts, which controls the interval between shots. The FoeFinder data is cleared and the process can repeat again.
This all happens within one frame.
Once an Attack is in the scene, it is continually checked against each Creature in the scene to see if there’s a hit. Here’s what this looks like:
- On an intersection between Creature and Attack, an Assault object is created (a small data class very similar to FoeFinder). This is tagged with the Attack (arrow) and the Victim (rat).
- The Attack is notified that it has a hit, and handles any special cases needed. In this case, the hit applies poison to the target, so a Poison Affliction is created and applied to the rat. This adds the Poison Affliction to the scene, where it is updated and will damage the rat once per second until it disappears (after four seconds).
- The victim (rat) is notified that he has been damaged, and is given the Assault object for reference. There is no special case here, but if this were some sort of Acid Rat which spawns corrosive acid on injury, that would be handled right here.
- The Assault evaluates any resistances or weaknesses the creature has to the damage type (for example, undead would be weak against Holy attacks, but resistant to poison). The rat has no such resistances! Final damage is calculated, and the rat’s health is reduced by this amount.
- A check happens here to see if the attack was fatal, and handle all the Kill events if so. In this case, no, just a maiming.
- The attacker (archer) is notified that she damaged a creature. If there were any special event for this, such as life steal, it would be handled here.
- The victim (rat) is notified that he took damage, which checks for AI events. In this case, the behavior says to run away in fear after being harmed. The AI state is updated to Frightened.
With this system in place, it’s been pretty straightforward to create new types of items, monsters, and status afflictions. They just listen for whichever events they care about in the above sequence (plus a few others) and execute the relevant code at those points.
Maybe next time, the rat will get the upper hand…
Wow, I totally missed the announcement of a new SoulCaster game. Awesome! Also, thanks for this behind-the-scenes look. I love reading about these details. Good show!
I am quite excited to be working on it! So many new things this time around, but I will do what I can to keep the posts coming in.