-
Notifications
You must be signed in to change notification settings - Fork 4
Patrick Task
The PatrickTask
class is responsible for executing the AI logic for the Patrick Boss entity within the game. This class handles the actions that the Patrick Boss executes based on a predetermined sequence and the boss's current hp. The class is designed to be added to an AiTaskComponent, which should be attached to a Demon Boss entity.
To utilize PatrickTask
, execute the following steps:
- Create an instance of
PatrickTask
. - Add it to an
AiTaskComponent
instance that is connected to a Patrick Boss entity.
// Create a Patrick Boss Entity
patrick = MobBossFactory.createPatrickBoss();
// Create an AiTaskComponent instance
AITaskComponent aiTaskComponent = new AITaskComponent();
// Add the PatrickTaskto the AiTaskComponent
aiTaskComponent.addTask(new PatrickTask());
// Add AiTaskComponent to Droid entity
patrick.addComponent(aiTaskComponent);
The update()
and changeState(PatrickState state)
methods manage the Patrick Boss's states and transitions. These methods toggle between multiple states described by the PatrickState
enum: IDLE
, APPEAR
, ATTACK
, WALK
, HURT
, SPELL
, and DEATH
.
- Description: Default state.
-
Behavior: Calls the
rangeAttack()
method if animation is complete.
- Description: Patrick Boss appearing.
-
Behavior: Checks the
spawnFlag
,meleeFlag
andrangeFlag
in respective order to determine what action to execute. IfspawnFlag
is true and the animation is complete,meleeAttack()
is called. IfmeleeFlag
is true, a sound event is triggered. IfrangeFlag
is true and 3 projectiles have been fired, callsmeleeAttack()
. (Note: State check each of the variable one by one and only executes the first one that's true.) -
Transition: If
meleeFlag
is true, the state is set toATTACK
. IfrangeFlag
is true andshotsFired
is less than 3, the state is set toIDLE
.
- Description: Patrick Boss attacking the nearest human enitity.
-
Behavior: Checks if the animation is complete. If true, deals
ATTACK_DAMAGE
to the target and teleports Patrick Boss back toinitialPos
.
- Description: The walking state with animation.
-
Behavior: Triggers
WALK
.
- Description: The hurt state with animation.
-
Behavior: Triggers
HURT
.
- Description: The spell state with animation.
-
Behavior: Triggers
SPELL
.
- Description: The death state.
-
Behavior: Triggers
DEATH
.
private static final float RANGE_MIN_X = 10f;
private static final float RANGE_MAX_X = 18f;
private static final float RANGE_MIN_Y = 1f;
private static final float RANGE_MAX_Y = 6f;
private static final int ATTACK_DAMAGE = 100;
private static final int HALF_HEALTH_ATTACKS = 5;
private boolean spawnFlag = false;
private boolean meleeFlag = false;
private boolean rangeFlag = false;
private int shotsFired;
private Vector2 initialPos;
Method that calls the event that corresponds with the state:
private void animate() {
// Check if same animation is being called
if (prevState.equals(state)) {
return; // skip rest of function
}
switch (state) {
case IDLE -> owner.getEntity().getEvents().trigger("patrick_idle");
case WALK -> owner.getEntity().getEvents().trigger("patrick_walk");
case HURT -> owner.getEntity().getEvents().trigger("patrick_hurt");
case SPELL -> owner.getEntity().getEvents().trigger("patrick_spell");
case APPEAR -> owner.getEntity().getEvents().trigger("patrick_death");
case ATTACK -> owner.getEntity().getEvents().trigger("patrick_attack");
default -> logger.debug("Patrick animation {state} not found");
}
prevState = state;
}
NOTE: All the events triggered in this class are captured by event listeners in PatrickAnimationController.
This method calls randomTeleport()
and spawnRandProjectile(Vector2 destination, boolean aoe)
methods to perform a teleportation and a range attack. shotsFired
is also incremented in this function.
This method teleports Patrick Boss to a random location within the RANGE_MIN_X
, RANGE_MAX_X
and RANGE_MIN_Y
, RANGE_MAX_Y
and by calling teleport(Vector2 pos)
.
private void randomTeleport() {
// teleport to random position
float randomX = MathUtils.random(RANGE_MIN_X, RANGE_MAX_X);
float randomY = MathUtils.random(RANGE_MIN_Y, RANGE_MAX_Y);
teleport(new Vector2(randomX, randomY));
}
This method teleports Patrick boss using a new PatrickTeleportTask
and passes Patrick Boss and pos
as parameters.
private void teleport(Vector2 pos) {
teleportTask = new PatrickTeleportTask(patrick, pos);
teleportTask.create(owner);
teleportTask.start();
teleportFlag = true;
}
This method spawns a projectile with a random effect using getEffect()
method.
private void spawnRandProjectile(Vector2 destination, boolean aoe) {
// spawn random projectile
Entity projectile = ProjectileFactory.createEffectProjectile(PhysicsLayer.HUMANS, destination, new Vector2(2, 2), getEffect(), aoe);
projectile.setPosition(patrick.getPosition().x, patrick.getPosition().y);
projectile.setScale(-1f, 1f);
ServiceLocator.getEntityService().register(projectile);
}
This method returns a random projectile effect.
This method teleports Patrick Boss to the closest human entity and attacks it. Uses teleport(Vector2 pos)
to teleport and the existing code ServiceLocator.getEntityService().getClosestEntityOfLayer(patrick, PhysicsLayer.HUMANS)
to implement the desired outcome.
private void meleeAttack() {
initialPos = patrick.getPosition();
meleeTarget = ServiceLocator.getEntityService().getClosestEntityOfLayer(patrick, PhysicsLayer.HUMANS);
teleport(meleeTarget.getPosition());
meleeFlag = true;
}
This method fires HALF_HEALTH_ATTACKS
random projectiles towards the human entities once the Patrick Boss's health drops to half or below.
private void halfHealth() {
float startAngle = (float) Math.toRadians(135);
float endAngle = (float) Math.toRadians(225);
float angleIncrement = (endAngle - startAngle) / (HALF_HEALTH_ATTACKS - 1);
for (int i = 0; i < HALF_HEALTH_ATTACKS; i++) {
// calculate unit vectors for projectiles
float currentAngle = startAngle + i * angleIncrement;
float x = MathUtils.cos(currentAngle) * 20;
float y = MathUtils.sin(currentAngle) * 20;
Vector2 destination = new Vector2(x, y);
spawnRandProjectile(destination, true);
}
if (shotsFired == HALF_HEALTH_ATTACKS) {
meleeFlag = true;
}
}