// chicken.class.js
/**
* @class Chicken
* @extends MovableObject
*
* Represents a walking enemy (chicken) in the game world.
* Chickens can move and be defeated by the player.
*/
class Chicken extends MovableObject {
/**
* Array of image paths for the chicken's walking animation.
* @type {string[]}
*/
IMAGES_WALKING = [
"assets/img/3_enemies_chicken/chicken_normal/1_walk/1_w.png",
"assets/img/3_enemies_chicken/chicken_normal/1_walk/2_w.png",
"assets/img/3_enemies_chicken/chicken_normal/1_walk/3_w.png",
];
/**
* Image path for the dead chicken sprite.
* @type {string[]}
*/
IMAGES_DEAD = ["assets/img/3_enemies_chicken/chicken_normal/2_dead/dead.png"];
/**
* Defines pixel offsets for more accurate collision detection.
* These values shrink the hitbox compared to the visual image.
* @type {{ top: number, right: number, bottom: number, left: number }}
*/
offset = {
top: 0,
right: 2,
bottom: 12,
left: 0,
};
/**
* Width of the chicken in pixels.
* Affects rendering and collision boundaries.
* @type {number}
*/
width = 50;
/**
* Height of the chicken in pixels.
* Affects rendering and collision boundaries.
* @type {number}
*/
height = 50;
/**
* Initial vertical velocity for jumps.
* Negative values move upward.
* @type {number}
*/
speedY = -5;
/**
* Rate at which vertical speed changes.
* Used for gravity calculations.
* @type {number}
*/
acceleration = 1;
/**
* Delay in frames between changes in the walking animation.
* Lower values result in faster animation.
* @type {number}
*/
frameDelayWalking = 5;
/**
* The amount of health the chicken has.
* Once it reaches 0, the chicken is considered dead.
* @type {number}
*/
health = 25;
/**
* Indicates whether the chicken should be removed from the game world.
* Used for despawn logic after death or leaving the screen.
* @type {boolean}
*/
isMarkedForDespawn = false;
/**
* Tracks the last objects this chicken had contact with.
* Used to prevent multiple collision responses.
* @type {Object.<string, Object>}
*/
lastContactWith = {};
/**
* Creates a new chicken enemy at the specified x-position.
* Loads all images and starts the animation loop.
*
* @param {number} x - Horizontal starting position of the chicken
* @param {number} [y=375] - Vertical starting position of the chicken
* @param {boolean} [isMinion=false] - Whether this chicken is spawned by the endboss
* @param {number} [speed=0.4] - Movement speed of the chicken
*/
constructor(x, y = 375, isMinion = false, speed = 0.4) {
super().loadImage("assets/img/3_enemies_chicken/chicken_normal/1_walk/1_w.png");
this.loadImages(this.IMAGES_WALKING);
this.loadImages(this.IMAGES_DEAD);
/**
* Horizontal position of the chicken on the canvas.
* Can be randomized to space out enemies.
* @type {number}
*/
this.x = x;
/**
* Vertical position of the chicken on the canvas.
* Default is 375 (ground level).
* @type {number}
*/
this.y = y;
/**
* Movement speed of the chicken.
* Affects how fast it walks.
* @type {number}
*/
this.speed = speed;
/**
* Indicates if this chicken is a minion spawned by the endboss.
* Affects behavior and despawn logic.
* @type {boolean}
*/
this.isMinion = isMinion;
this.initChickenLoops();
}
/**
* Initializes the chicken's animation and physics loops.
* Waits for world initialization before starting animations.
* Uses recursive setTimeout for initialization retry.
*/
initChickenLoops() {
if (!worldIsReady) {
setTimeout(() => {
this.initChickenLoops();
}, 200);
} else {
this.applyGravity();
this.animate();
}
}
/**
* Starts the chicken's animation loop.
* Updates the character state and triggers the corresponding behavior.
* Synchronized with display refresh rate via requestAnimationFrame.
*/
animate() {
this.updateState();
switch (this.currentState) {
case "dead":
this.handleDeadChicken();
break;
case "walking":
this.handleWalkingChicken();
break;
}
setStoppableRAF(() => this.animate());
}
/**
* Handles the behavior of the chicken in the "dead" state.
* Plays the dead animation and disables the hitbox for collision detection.
*/
handleDeadChicken() {
this.playAnimation(this.IMAGES_DEAD);
this.disableHitbox();
}
/**
* Handles the behavior of the chicken in the "walking" state.
* Moves the chicken to the left and plays walking animation.
* Animation updates are synchronized with world timing for consistent speed.
*/
handleWalkingChicken() {
if (!this.isAboveGround()) {
this.moveLeft();
}
if (world.isInSync()) {
if (this.skipFrame % this.frameDelayWalking === 0) {
this.playAnimation(this.IMAGES_WALKING);
}
this.skipFrame += 1;
}
}
/**
* Updates the current state of the chicken based on its internal status.
* Sets the state to "dead" if the chicken is dead, otherwise to "walking".
*/
updateState() {
let newState;
if (this.isDead()) {
newState = "dead";
} else {
newState = "walking";
}
this.currentState = newState;
}
}