The Horroring is a darkly comedic, third person adventure game about a lonely ghost who must kill off her friends so they may join her for a splendid dinner party in the after life! Play the game here!
As technical lead my role involved designing and programming a variety of systems and mechanics for The Horroring. Some of the more complex systems I worked on were:
Dynamic Loop System:
Created a dynamic looping system that takes any closed shape made by the player and calculates whether an object is inside or outside the loop.
Four AI Subsets:
Designed and built a number of different AIs: villagers that reacted to being scared by the player, exorcists that blessed areas that had been scared by the player while sapping their power, dogs that seek out the player and kill them off.
Ghost Power System:
Created a system in which the player spends ghost power to scare villagers, gaining more ghost power raises the player's level, enabling them to create even more dramatic scares in the village.
Dynamic Loop System
Arguably the most important mechanic in the game was the looping system. The idea is that the ghost loops around a group of villagers and gets ghost points for the number of villagers caught in the loop. This translate into ghost power which in turn can be used to create longer loops. Of course, looping costs ghost power in the first place, so if you fail to successfully loop around a group of villagers you will lose power.
The mechanic had two main requirements:
The player can make any shape, so long as it makes an enclosed shape.
All villagers can be targeted.
The development of this mechanic took a couple of iterations to get right. Here are the two algorithms used, their flaws and benefits.
Dividing the Shape into Triangles:
The first attempt for calculating whether an object is inside the closed shape exploits the fact that the cross product between two edges of the triangle and a point inside that triangle will produce a perpendicular vector heading in the same direction. The direction of these perpendicular vectors can be checked getting the dot product, which should be the same if both perpediculars vectors are normalized.
That being the case, it now becomes a question of dividing a list of vectors representing an enclosed shape into a set of triangles...
This was achieved by using a rather complex algorithm that went as follows:
Take the list of vectors and create two new lists, list A and list B. These lists are used to divide a shape that is created in a clockwise and anti-clockwise fashion.
Set one loop direction modifier for each list: -1 for a loop created in a clockwise motion and 1 for a loop created in an anti-clockwise motion.
Take the first point in the list of vectors A. And the next two points, B and C.
For both list A and list B...
If Cross(AB, AC), normalized, is equal to the loop direction modifier...
Add the points A, B and C to a list of triangles.
Remove point B from the list of points.
Set point C to point B and get the next available point in the list, starting from the beginning if all points used.
Set point B to point A, point C to point B and get the next available point in the list, starting from the beginning if all points have been used.
Repeat until one of the lists has only three remaining points.
Add these points to the list of triangles.
Return this list of triangles.
As one can imagine this was a rather complex algorithm to program and an expensive one to use. Further, while it worked well for most shapes, we discovered that hour glass shapes, as in the picture above-left, would not successfully return a complete list of triangles. The areas of these missing triangles were substantial enough for players to notice that the villagers were failing to be 'caught'. A rethink was needed.
An alternative, much simpler algorithm was found, which exploited existing code for calculating whether two lines intersected. Additionally this algorithm was much, much faster and worked every time!
It works on a very simple principle: If you draw a line from outside the shape to the point inside the shape and calculate how many times the line intersects an edge of the shape you can tell whether the point is inside the shape of not.
Cross (AB, AC) and Cross (AB, AD) will both produce a perpendicular vector heading in the same direction.
Normalizing both perpendicular vectors and calculating their dot product should return a value of 1 if they are going in the same direction.
This can be repeated for all sides of the triangle.
If the line from outside the shape to the point intersects the shape an odd number of times, the point is inside the shape. Otherwise it must be outside the shape.
In order to check whether a point was inside or outside a shape, we created a line from somewhere outside the bounds of the world to the point we were testing. We then counted how many times that line intersected the shape by testing the line against each side of the shape. If it was an odd number of times we knew the point was inside the shape.
The point was usually a villager. If caught they either ran off terrified or fainted with fright!
There were four small AIs that moved around the village: villagers, dogs, children and exorcists. All were built with state machines that changed the behaviour of the AI depending various sets of circumstances. Here is a summary of each AI, its various states and their behaviour.
Villagers are a superstitious bunch of folk whose fear can be exploited to empower the player's ghost. The villager's states were as follows:
IDLE: When idle the villager will happily wander around the village.
SCARED: When scared the villager will flee to the nearest safe place in the villlage. The villager becomes scared when the player's ghost successfully loops around the villager or dashes around them.
FAINT: If you catch a scared villager, either by looping them or dashing into them, they'll collapse in abject fear.
Children are much more sensitive to the supernatural and will run away as soon as they feel your presence. Just floating by them will give you a speed boost and send them running off in fear! Their states:
IDLE: Children roam around the village, idly playing.
SCARED: Scared children will flee to the nearest safe place until they regain their courage to go out and play again.
Exorcists use their knowledge of the world beyond to seek out the restless dead and send them back. Exorcists cannot be frightened by the player's ghost but nor can the seek them out. They must wait for the ghost to strike and then quickly cleanse the area of evil. If they catch a ghost in their blessed area, it will be sent back! Furthermore, the very aura of an exorcist will drain a ghost's power. Their states:
IDLE: They roam around a given territory waiting for evidence of the presence of spirits.
CLEANSE: If the player scares someone within range of an exorcist they will go to the last point of the scare and cleanse the area of all ghosts.
As an animal with exceptional senses, the dog is the only AI that can 'see' the player's ghost and move to attack. If only the villagers had left them off their leash! Their states:
IDLE: The dog roams around its kennel area, as far as its leash will permit.
ATTACK: As soon as the dog can pick up the presence of the ghost it will try to attack them. Only the dog's leash will limit its ability to hunt down the ghost.