feat: Add rabbit entity with dynamic movement and sprite rendering
This commit is contained in:
141
js/entities/rabbit.js
Normal file
141
js/entities/rabbit.js
Normal file
@@ -0,0 +1,141 @@
|
||||
// Rabbit entity
|
||||
class Rabbit extends Entity {
|
||||
constructor(x, y, options = {}) {
|
||||
super(ENTITY_TYPES.RABBIT, x, y, {
|
||||
width: 6, // Smaller size for rabbit
|
||||
height: 6,
|
||||
...options
|
||||
});
|
||||
|
||||
// Load rabbit sprite
|
||||
this.sprite = new Image();
|
||||
this.sprite.src = 'sprites/rabbit.png';
|
||||
|
||||
// Rabbit specific properties
|
||||
this.jumpCooldown = 0;
|
||||
this.jumpForce = -3.5;
|
||||
this.moveSpeed = 0.8;
|
||||
this.direction = Math.random() > 0.5 ? 1 : -1; // 1 for right, -1 for left
|
||||
this.isJumping = false;
|
||||
this.thinkTimer = 0;
|
||||
this.actionDuration = 0;
|
||||
this.currentAction = 'idle';
|
||||
|
||||
// Apply gravity
|
||||
this.gravity = 0.2;
|
||||
}
|
||||
|
||||
update() {
|
||||
const now = performance.now();
|
||||
const deltaTime = Math.min(50, now - this.lastUpdate);
|
||||
this.lastUpdate = now;
|
||||
|
||||
// Apply gravity
|
||||
this.vy += this.gravity;
|
||||
|
||||
// Calculate new position
|
||||
let newX = this.x + this.vx;
|
||||
let newY = this.y + this.vy;
|
||||
|
||||
// Check for collisions
|
||||
const collisionResult = this.checkCollisions(newX, newY);
|
||||
|
||||
if (collisionResult.collision) {
|
||||
if (collisionResult.horizontal) {
|
||||
// Hit a wall, reverse direction
|
||||
this.direction *= -1;
|
||||
this.vx = this.moveSpeed * this.direction;
|
||||
newX = this.x; // Don't move horizontally this frame
|
||||
}
|
||||
|
||||
if (collisionResult.vertical) {
|
||||
if (collisionResult.ground) {
|
||||
// Landed on ground
|
||||
this.vy = 0;
|
||||
this.isJumping = false;
|
||||
|
||||
// Find exact ground position
|
||||
while (this.isPixelSolid(this.x, newY)) {
|
||||
newY--;
|
||||
}
|
||||
newY = Math.floor(newY) + 0.99; // Position just above ground
|
||||
} else {
|
||||
// Hit ceiling
|
||||
this.vy = 0;
|
||||
newY = this.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update position
|
||||
this.x = newX;
|
||||
this.y = newY;
|
||||
|
||||
// Update jump cooldown
|
||||
if (this.jumpCooldown > 0) {
|
||||
this.jumpCooldown--;
|
||||
}
|
||||
|
||||
// AI behavior
|
||||
this.thinkTimer++;
|
||||
if (this.thinkTimer >= 30) { // Think every 30 frames
|
||||
this.thinkTimer = 0;
|
||||
this.think();
|
||||
}
|
||||
|
||||
// Update action duration
|
||||
if (this.actionDuration > 0) {
|
||||
this.actionDuration--;
|
||||
} else if (this.currentAction !== 'idle') {
|
||||
this.currentAction = 'idle';
|
||||
this.vx = 0;
|
||||
}
|
||||
|
||||
// Update sprite direction
|
||||
this.flipped = this.direction < 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
think() {
|
||||
// Only make decisions when on the ground and not already in an action
|
||||
if (!this.isJumping && this.actionDuration <= 0) {
|
||||
const decision = Math.random();
|
||||
|
||||
if (decision < 0.2) {
|
||||
// Jump
|
||||
this.jump();
|
||||
} else if (decision < 0.6) {
|
||||
// Move
|
||||
this.move();
|
||||
} else {
|
||||
// Idle
|
||||
this.idle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jump() {
|
||||
if (!this.isJumping && this.jumpCooldown <= 0) {
|
||||
this.vy = this.jumpForce;
|
||||
this.vx = this.moveSpeed * this.direction;
|
||||
this.isJumping = true;
|
||||
this.jumpCooldown = 20;
|
||||
this.currentAction = 'jump';
|
||||
this.actionDuration = 30;
|
||||
}
|
||||
}
|
||||
|
||||
move() {
|
||||
this.direction = Math.random() > 0.5 ? 1 : -1;
|
||||
this.vx = this.moveSpeed * this.direction;
|
||||
this.currentAction = 'move';
|
||||
this.actionDuration = 60 + Math.floor(Math.random() * 60);
|
||||
}
|
||||
|
||||
idle() {
|
||||
this.vx = 0;
|
||||
this.currentAction = 'idle';
|
||||
this.actionDuration = 30 + Math.floor(Math.random() * 30);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user