Spite: Curse of Archibald

Timeframe: 14 weeks (20h/week)

Team: 5 programmers, 3 designers, 5 artists

Engine: Our improved version of TGE, the school's in-house game engine made in C++ and DirectX11

Using an entity-component system library EnTT.

Inspiration: Diablo 3


Enemy AI

Consists of a finite-state machine with the following behaviors: Idle, Attack, Seek, and Separation. The movement solution is based on steering behaviors. Using the same pathfinding algorithm as the player, A*.


The enemies' "flocking behavior" was made by checking the amount of overlap each enemy had with one another and interpolating the result with the position of the player.


The combat portion of the enemies required a lot of tweaking and iteration regarding different ranges, angles of attack, smooth rotations in-between attacks, and general timing with the attack animation and damage output.

Rat AI

Blended behavior of Wander, Obstacle Avoidance, and Player Avoidance. The rat's were a stretch goal made during the last couple of weeks of the project. This was also my favorite part of the project for me. I learned a lot during the implementation.


They are in a static idle state until the player comes within their detection range. At this point, the rats scatter in randomized directions. Once they've traveled too far away from their starting position, they return.


For all of our pathfinding, we are using a navmesh. You can either be on the navmesh or not. However, because the object we want to avoid lacks geometry, (everything outside the navmesh) it is difficult to extract the necessary information for obstacle avoidance. To determine whether a path is valid or not, the rats must sample their next position. But what happens when the selected position is not valid? I provide a more detailed explanation of my solution at the bottom of this page. 

Player Combat Mechanics

Axe Hammer & Speedbuff

Throwing Axe & Whirlwind

Technical Details & Closing Thoughts

This project was my first time using EnTT, an entity-component system. I really enjoyed the workflow that came with it. Components made with structs, functions in namespaces, each function iterating over their respective component. The accessibility of everything was effortless, and the iteration of abilities went smoothly. Overall, I could have utilized components more.


Regarding the Rat AI, I developed a solution that involved using the normalized unit vector "awayFromPlayer". By extracting both normals, I was able to determine a "right" and "left" direction relative to this vector. However, I discovered that if the rat was in close proximity to the player, it would attempt to move away as intended, but this posed a problem when the rat became trapped against a wall with the player nearby. If the separation vector was too large, it could cause continuous out-of-bounds sampling and make it impossible for the rat to find a valid path. The answer I had to this was dynamic weighted blending. Change the magnitude of the separation vector dependent on the amount of overlap the rat has with the player. To avoid division with zero, I set a default value.

©2023 by Viktor Rissanen                                                 Resumé  LinkedIn    rissanenviktor@gmail.com