-
Notifications
You must be signed in to change notification settings - Fork 4
[OLD] General Mobs
Individual wiki pages have been created for each of the classes defined below, which go into more detail on how they should be used and the testing plan for each.
General mobs are a type of NPC entity which traverse across the grid, towards the towers and following set lanes. When they are in a certain range of the towers, they shoot projectiles to try and destroy the towers.
The purpose of the class is to be a base structure for all mobs. It functions as a container for relevant information to all mobs and provides a consistent base to build future specialised mobs from. It holds all enemy stats such as health, speed, base attack, and all abilities including long-range and close-range attacks. It lacks functionality aside from retrieving information of a specific enemy and is only to be used as a base structure, helping to streamline enemy construction.
The BaseEnemyConfig
holds all relevant information to initialise an advanced CombatStatsComponent
which is responsible for handling combat-related statistics including dealing and taking damage.
BaseEnemyConfig config = configs.specialtyEnemy;
…
….addComponent(new CombatStatsComponent(conifg.fullHealth, config.baseAttack, config.drops, config.getCloseRangeAbilities, config.getLongRangeAbilities)…
specialtyEnemy is a predefined enemy type. This has minimal functionality and should only be used to define a Speciality Enemy and not be used as an enemy itself.
The Melee
and Projectile
classes implement the Weapon
interface. The purpose of this interface is to promise the implementation of necessary functions and values to classes which can be used to attack. This interface is primarily featured in CombatStatsComponent
.
public Weapon getWeapon(Entity target) {
ArrayList<Melee> withinRange = withinRange(target);
if (withinRange.size() > 0) {
return withinRange.get(pickRandom(withinRange));
}
return longRangeAttack();
}
The type of weapon will depend on the distance from the target and therefore cannot be promised as a singular type. Using the weapon interface ensures the returning value has required functionality for future uses of the value.
The Melee
class is used to define an ability that will deal damage but requires a minimum distance from the target. It is implemented as a sibling to Projectiles where the CombatStatsComponent
will call for a Weapon
and return a Melee
for close range or Projectile
for long range. It is designed as a base to predefined Melee
abilities and is used in PredefinedWeapons
class. Its purpose is to store all relevant attributes required to attack a target.
Melee(int damage, int attackDamage, String element, int castTime, int cooldown)
Melee sword= new Melee(10, 4, “fire”, 1, 1);
This class is responsible for the storage of all weapons. All weapons should be defined in this class and accessed later. It is purely a container for weapon storage and does not have any other functionality, nor should it be used as anything else. When Specific mobs are built from the BaseEnemyConfig
they will only contain predefined weapons from this class.
public class PredefinedWeapons {
// Melee attacks
public static Melee sword = new Melee(10, 4, "fire", 1, 1);
public static Melee punch = new Melee(3, 1, "air", 1, 1);
…
CombatStatsComponent
was expanded upon to incorporate the abilities, close-range and long-range, that enemies have. There are two initialisers for this component including the original init and a new init which requires the drops, close range and long range of the entity. There were minimal changes, majority of alterations to this file were additions. The changes made include the addition of updating the state of the entity whenever health is updated.
public void changeState() {
if (this.health <= (this.fullHealth * 0.33)) {
…
}
}
The additional methods are:
-
getWeapon()
: return a Weapon -
withinRange()
: return a list of all Melee which can attack the target (if close enough to use) -
longRangeAttack()
: pick a random long range attack if one exists -
pickRandom()
: pick a random index of the provided list -
drop()
: pick a random currency to drop
pickRandom()
, withinRange()
and longRangeAttack()
work together to return the appropriate weapon when touchAttackComponent
requests a weapon. getWeapon()
receives a target Entity
which will be passed to withinRange()
. The withinRange()
function will measure the distance between this and the target Entity’s location. For each closeRangeAttack
, if the target is within range, it is added to a list of possible Melee
weapons to use and it returned to getWeapon()
. If none are returned then a Projectile
is provided to touchAttackComponent
, else a Melee
is selected at random.
if (combatStats.getWeapon(target) != null) {
targetStats.hit(combatStats.getWeapon(target).getDamage());
The Xeno Grunt is a basic enemy mobs which spawns in high volume down each lane, capable of shooting at the tower directly in front, or swinging at it once close enough.
This is the first basic mob to be implemented, and as such has a basic but diverse ability set, which serves as the basis for future general mob implementations.
The Xeno Grunt run animations are simple 8 frame loops, read from the xeno-grunt-run.png and xeno-grunt-run-damaged.png by xenoGruntRunning.atlas and xenoGruntRunningDamaged.atlas respectively. The current xeno-grunt-run-damaged.png sprite strip will likely be changed later to match the theme.
Attack animations are read from the xeno-grunt-melee-attack.png and xeno-grunt-range-attack.png by xenoGruntRangedAttack.atlas and xenoGruntMeleeAttack.atlas respectively. These animations operate on different loop lengths, with the first melee attack having a 9 frame loop, the second melee attack having a 6 frame loop, and the ranged attack having a 5 frame loop.
The death animation is read from xeno-grunt-death.png by xenoGruntDeath.atlas, and has a 5 frame loop. This sprite sheet has been made up from the separate PNGs xeno-grunt-death-falling.png and xeno-grunt-death-grounded.png.
The XenoAnimationController
class listens for the events relating to the Xeno Grunts ability set, and has been set up to play the relevant animation once detected.
Later in the development of the game, mobs will shoot projectiles and attack tower entities. To begin to implement this feature, a ShootTask
class was created, which will store all the relevant information for shooting projectiles. This task is assigned to all NPC entities in the NPCFactory
class, under the createBaseNPC()
function. The method detects when the player entity is within range, and is visible to the mob entity, and when it is, the shoot task is initiated. Each time the player moves into this range, the task is initiated. Currently, the implementation of the task can be seen by a print statement in the terminal. Upon integrating with the projectiles and towers, a projectile entity will be seen being shot from the mob towards a particular tower.
The ShootTask
class takes in four parameters, target, priority, viewDistance and maxChaseDistance. This is similar to the pre-existing ChaseTask
class, as the functionality to detect when the task should be actioned is the same here. A description of these parameters is given below:
-
target
: represents the Entity which the projectile will shoot at -
priority
: is an integer value, -1 if the target is out of range and the task should not be initiated (any positive value for if the target is in range) -
viewDistance
: is a float value representing the maximum distance from the target at which the shooting can start -
maxChaseDistance
: is a float value representing the maximum distance from the target while shooting before giving up
The methods defined in this class are outlined as follows:
-
start()
: defines what to do when the task is called (in this instance, print to the terminal) -
getPriority() -> int
: retrieve the current task priority based on whether the action is currently occurring or not -
getDistanceToTarget() -> float
: retrieve the current distance between the mob and the target -
getActivePriority() -> int
: retrieve the current active priority to determine whether the shoot task should continue or stop -
getInactivePriority() -> int
: retrieve the current inactive priority to determine whether to start the shoot task or to continue not performing the action -
isTargetVisible() -> bool
: returns True if the target entity is directly visible by the mob and False otherwise
As noted, the class is added as a task to the NPCFactory
class, in the createBaseNPC(Entity target)
method. This creates a new AIComponent()
and adds the task using .addTask(new ShootTask(target, 10, 3f, 4f))
. This sets the initial parameters to be the target player entity, the priority to be 10, the viewDistance to be 3 and the maxChaseDistance to be 4.
The createXenoGrunt()
function in the NPCFactory class is responsible for creating new Xeno Grunt mobs. Currently, the Xeno Grunt is created with the BaseEnemyConfig
configuration, which holds information about the Xeno Grunt's combat abilities and drops.
Mobs in the game spawn on the right side of the screen and move towards the player’s defences in order to attack them. These mobs are capable of spawning in any of the game’s “lanes”, and will only attack towers within its associated lane. The specific lane each individual mob spawns in is random between game sessions.
Mob movement was implemented by repurposing the given WanderTask
, rather than creating an entirely new class. A new private helper function getDirection()
was created which returned a GridPoint
at an equal y level to where the associated entity spawned, with the x coordinate being “0”, which is the leftmost column of the map. Thus, when the GridPoint
was given to a MovementTask
assigned to a mob, it would move directly to the left side of the map from the player’s perspective.
The spawning of the mobs was taken care of within the spawnXenoGrunts()
method in ForestGameArea
. Minimum and maximum bounds are assigned for mob spawning, which constrain the mob to only spawn in the rightmost map column, however at random y levels. The RandomUtils
class is utilised to accomplish this. Each mob is randomly assigned to a GridPoint
, where the entity spawns at the beginning of the game.