319 lines
9.8 KiB
JavaScript
319 lines
9.8 KiB
JavaScript
import { catAnimation, imageAssets } from '../../sketch.js';
|
|
import { gameFrame } from '../constants/Prototype.js';
|
|
import { grid, cheeses, activeCats, activeMice, mouseGroup, throwableGroup, gameSprites } from '../scenes/GameScene.js';
|
|
import { calculateCell } from '../logic/Helper.js';
|
|
import { Yarn, Snowball } from './Throwable.js';
|
|
|
|
export const throwables = [];
|
|
const catAniDesc = {
|
|
chefCat: {
|
|
idle: { row: 0, frames: 4, frameSize: [200, 200], frameDelay: 10 },
|
|
action: { row: 0, frames: 4, frameSize: [200, 200], frameDelay: 10 }
|
|
},
|
|
singleYarnCat: {
|
|
idle: { row: 0, frameSize: [200, 200] },
|
|
action: {row: 1, frames: 8, frameSize: [200, 200], frameDelay: 22 }
|
|
},
|
|
doubleYarnCat: {
|
|
idle: { row: 0, frameSize: [200, 200] },
|
|
action: {row: 1, frames: 8, frameSize: [200, 200], frameDelay: 22 }
|
|
},
|
|
sleepyCat: {
|
|
idle: {row: 0, frames: 6, frameSize: [200, 200], frameDelay: 20 },
|
|
action: {row: 1, frames: 9, frameSize: [200, 200], frameDelay: 10 }
|
|
},
|
|
explosion: {
|
|
action: {row: 0, frames: 9, frameSize: [200, 200], frameDelay: 10 }
|
|
},
|
|
iceCat: {
|
|
idle: { row: 0, frameSize: [200, 200] },
|
|
action: {row: 1, frames: 8, frameSize: [200, 200], frameDelay: 22 }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Cat class representing a cat character in the game
|
|
*/
|
|
class Cat {
|
|
/**
|
|
* Creates an iinstance of a Cat
|
|
*
|
|
* @param {number} x - The x-coordinate of the cat's position
|
|
* @param {number} y - The y-coordinate of the cat's position
|
|
* @param {number} cost - The cost of placing the cat
|
|
* @param {p5.SpriteSheet} spriteSheet - The sprite sheet for the cat's animations
|
|
* @param {Object} ani - Animation details for the cat
|
|
*/
|
|
constructor(x, y, cost, spriteSheet, ani) {
|
|
// (x, y) is the center of the grid
|
|
this.width = 1.2 * gameFrame.tileWidth;
|
|
this.sprite = createSprite(x, y, this.width, this.width);
|
|
this.sprite.spriteSheet = spriteSheet;
|
|
this.sprite.scale = gameFrame.catRatio;
|
|
this.sprite.addAnis(ani);
|
|
this.sprite.collider = 'static';
|
|
this.sprite.overlaps(throwableGroup);
|
|
this.sprite.layer = 1;
|
|
this.sprite.changeAni('idle');
|
|
this.active = false;
|
|
this.explosion = undefined;
|
|
|
|
this.x = x;
|
|
this.y = y;
|
|
this.cost = cost;
|
|
this.ani = ani;
|
|
this.HP = 100;
|
|
|
|
const { row, col } = calculateCell(x, y);
|
|
this.row = row;
|
|
this.col = col;
|
|
}
|
|
|
|
/**
|
|
* Switches the cat's animation to 'idle' state
|
|
*/
|
|
switchToIdle() {
|
|
this.sprite.changeAni('idle');
|
|
this.active = false;
|
|
}
|
|
|
|
/**
|
|
* Switches the cat's animation to 'action' state
|
|
*/
|
|
switchToAction() {
|
|
this.sprite.changeAni('action');
|
|
this.active = true;
|
|
}
|
|
|
|
/**
|
|
* Called when the cat is attacked by a mouse
|
|
* @param {Object} mouse - The mouse attacking the cat
|
|
*/
|
|
attacked(mouse) {
|
|
this.addExplosion(imageAssets.grayExplosion);
|
|
this.explosion = undefined;
|
|
this.HP -= mouse.AP;
|
|
setTimeout(() => {
|
|
if (this.HP <= 0) {
|
|
this.remove();
|
|
mouse.targetCat = undefined;
|
|
}
|
|
else {
|
|
this.sprite.opacity = (this.HP / 100) * 0.7 + 0.3;
|
|
}
|
|
}, 1500);
|
|
}
|
|
|
|
/**
|
|
* Removes the cat from the game
|
|
*/
|
|
remove() {
|
|
this.sprite.remove();
|
|
grid[this.row][this.col] = null;
|
|
|
|
let index = activeCats.indexOf(this);
|
|
if (index !== -1) {
|
|
activeCats.splice(index, 1);
|
|
}
|
|
|
|
index = gameSprites.indexOf(this);
|
|
if (index !== -1) {
|
|
gameSprites.splice(index, 1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds an explosion animation to the cat
|
|
* SleepyCat - Red explosion when it overlaps with a mouse
|
|
* Other Cats - Gray explosion when it is attacked by a mouse
|
|
* @param {p5.SpriteSheet} spriteSheet - The sprite sheet for the explosion
|
|
*/
|
|
addExplosion(spriteSheet) {
|
|
this.explosion = createSprite(this.x, this.y, this.width, this.width);
|
|
gameSprites.push(this.explosion);
|
|
this.explosion.spriteSheet = spriteSheet;
|
|
this.explosion.scale = gameFrame.catRatio;
|
|
this.explosion.life = 90;
|
|
this.explosion.collider = 'none';
|
|
this.explosion.addAnis(catAniDesc.explosion);
|
|
this.explosion.changeAni('action');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Cat that generates cheese periodically for resources
|
|
*/
|
|
class ChefCat extends Cat {
|
|
constructor(x, y) {
|
|
super(x, y, 50, catAnimation.chefCat, catAniDesc.chefCat);
|
|
this.lastProduced = millis();
|
|
this.offset = 0;
|
|
}
|
|
|
|
action() {
|
|
// Produces 25 cheese every 10 seconds
|
|
if (millis() - this.lastProduced > 10000) {
|
|
const cheese = createSprite(this.x + this.width / 4 + this.offset * this.width / 20, this.y + this.width / 3 + this.offset * this.width / 20);
|
|
cheese.scale = this.width / 216;
|
|
cheese.image = imageAssets.cheese;
|
|
cheese.collider = 'static';
|
|
cheese.overlaps(mouseGroup);
|
|
|
|
cheeses.push(cheese);
|
|
gameSprites.push(cheese);
|
|
this.lastProduced = millis();
|
|
this.offset = (this.offset + 1) % 3;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Cat that throws a single yarn at mice every 3 seconds
|
|
*/
|
|
class SingleYarnCat extends Cat {
|
|
constructor(x, y) {
|
|
super(x, y, 100, catAnimation.singleYarnCat, catAniDesc.singleYarnCat);
|
|
this.lastShot = millis();
|
|
}
|
|
|
|
action() {
|
|
if (activeMice[this.row].length > 0) this.switchToAction();
|
|
else this.switchToIdle();
|
|
|
|
if (this.active && (millis() - this.lastShot > 3000)) {
|
|
let yarnX = this.x + gameFrame.tileWidth / 2;
|
|
let yarnY = this.y;
|
|
|
|
const yarn = new Yarn(yarnX, yarnY);
|
|
if (yarn) {
|
|
throwables.push(yarn);
|
|
throwableGroup.add(yarn.sprite);
|
|
}
|
|
|
|
this.lastShot = millis();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Cat that throws 2 yarns at mice every 3 seconds
|
|
*/
|
|
class DoubleYarnCat extends Cat {
|
|
constructor(x, y) {
|
|
super(x, y, 200, catAnimation.doubleYarnCat, catAniDesc.doubleYarnCat);
|
|
this.lastShot = millis();
|
|
}
|
|
|
|
action() {
|
|
if (activeMice[this.row].length > 0) this.switchToAction();
|
|
else this.switchToIdle();
|
|
|
|
if (this.active && (millis() - this.lastShot > 3000)) {
|
|
for (let offset of [0, 0.3 * gameFrame.tileWidth]) {
|
|
let yarnX = this.x + gameFrame.tileWidth / 2 + offset;
|
|
let yarnY = this.y;
|
|
|
|
const yarn = new Yarn(yarnX, yarnY);
|
|
if (yarn) {
|
|
throwables.push(yarn);
|
|
throwableGroup.add(yarn.sprite);
|
|
}
|
|
}
|
|
|
|
this.lastShot = millis();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Cat that activates when overlapping with a mouse and explodes, damaging the enemy by 150 points
|
|
*/
|
|
export class SleepyCat extends Cat {
|
|
constructor(x, y) {
|
|
super(x, y, 150, catAnimation.sleepyCat, catAniDesc.sleepyCat);
|
|
this.awake = false;
|
|
this.hasAttacked = false;
|
|
this.wakeStart = undefined;
|
|
this.targetMouse = undefined;
|
|
}
|
|
|
|
action(targetMouse) {
|
|
if (this.awake) {
|
|
this.switchToAction();
|
|
this.addExplosion(imageAssets.redExplosion);
|
|
this.wakeStart = millis();
|
|
this.targetMouse = targetMouse;
|
|
this.targetMouse.sprite.vel.x = 0;
|
|
this.targetMouse.sprite.changeAni('idle');
|
|
this.awake = false;
|
|
}
|
|
|
|
if (this.wakeStart != undefined) {
|
|
if (!this.hasAttacked && this.targetMouse && millis() - this.wakeStart > 900) {
|
|
const explode = { point: 150 };
|
|
this.targetMouse.attacked(explode);
|
|
if (this.targetMouse && this.targetMouse.HP > 0) this.targetMouse.sprite.changeAni('walk');
|
|
this.hasAttacked = true;
|
|
}
|
|
if (millis() - this.wakeStart > 1480) {
|
|
this.remove();
|
|
this.explosion.remove();
|
|
this.explosion = undefined;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Cat that throws snowballs at mice every 3 seconds
|
|
*/
|
|
class IceCat extends Cat {
|
|
constructor(x, y) {
|
|
super(x, y, 150, catAnimation.iceCat, catAniDesc.iceCat);
|
|
this.lastShot = millis();
|
|
}
|
|
|
|
action() {
|
|
if (activeMice[this.row].length > 0) this.switchToAction();
|
|
else this.switchToIdle();
|
|
|
|
if (this.active && (millis() - this.lastShot > 3000)) {
|
|
const snowballX = this.x + gameFrame.tileWidth / 2;
|
|
const snowballY = this.y;
|
|
|
|
const snowball = new Snowball(snowballX, snowballY)
|
|
if (snowball) {
|
|
throwables.push(snowball);
|
|
throwableGroup.add(snowball.sprite);
|
|
}
|
|
|
|
this.lastShot = millis();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Factory function to create different types of cats
|
|
* @param {string} type - The type of cat to create. One of 'chefCat', 'singleYarnCat', 'doubleYarnCat', 'sleepyCat', 'iceCat'
|
|
* @param {number} x - The x-coordinate
|
|
* @param {number} y - The y-coordinate
|
|
* @returns {Cat|undefined} The created cat instance or undefined if type is invalid
|
|
*/
|
|
export function createCat(type, x, y) {
|
|
switch (type) {
|
|
case 'chefCat':
|
|
const chefCat = new ChefCat(x, y);
|
|
chefCat.action();
|
|
return chefCat;
|
|
case 'singleYarnCat':
|
|
return new SingleYarnCat(x, y);
|
|
case 'doubleYarnCat':
|
|
return new DoubleYarnCat(x, y);
|
|
case 'sleepyCat':
|
|
return new SleepyCat(x, y);
|
|
case 'iceCat':
|
|
return new IceCat(x, y);
|
|
default:
|
|
return undefined;
|
|
}
|
|
} |