Skip to content

Patrick Task

gregchan550 edited this page Oct 20, 2023 · 4 revisions

Overview

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.

Usage

To utilize PatrickTask, execute the following steps:

  1. Create an instance of PatrickTask.
  2. 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);

IMG_0076

State Management: update() and changeState(PatrickState state)

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.

State Descriptions

IDLE
  • Description: Default state.
  • Behavior: Calls the rangeAttack() method if animation is complete.
APPEAR
  • Description: Patrick Boss appearing.
  • Behavior: Checks the spawnFlag, meleeFlag and rangeFlag in respective order to determine what action to execute. If spawnFlag is true and the animation is complete, meleeAttack() is called. If meleeFlag is true, a sound event is triggered. If rangeFlag is true and 3 projectiles have been fired, calls meleeAttack(). (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 to ATTACK. If rangeFlag is true and shotsFired is less than 3, the state is set to IDLE.
ATTACK
  • 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 to initialPos.
WALK
  • Description: The walking state with animation.
  • Behavior: Triggers WALK.
HURT
  • Description: The hurt state with animation.
  • Behavior: Triggers HURT.
SPELL
  • Description: The spell state with animation.
  • Behavior: Triggers SPELL.
DEATH
  • Description: The death state.
  • Behavior: Triggers DEATH.
Constants and variables to be taken into consideration:
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.

Starting Range Attack: rangeAttack()

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.

Randomly Teleporting Patrick Boss: randomTeleport()

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));
}

Teleporting Patrick Boss: teleport(Vector2 pos)

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;
}

Spawning Random Projectile: spawnRandProjectile(Vector2 destination, boolean aoe)

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);
    }

Getting Random Effect: getEffect()

This method returns a random projectile effect.

Perform Melee Attack: meleeAttack()

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;
}

Half Health Attack: halfHealth()

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;
    }
}
Clone this wiki locally