From 8ebb75fd1a0f8cefed6447cb3e4f03635f105a8b Mon Sep 17 00:00:00 2001 From: koneko <67551503+koneko@users.noreply.github.com> Date: Wed, 8 Jan 2025 00:51:10 +0100 Subject: [PATCH] enemies can be killed and they give money upon death. towers shoot properly --- public/assets/Towers.json | 2 +- src/classes/GameObject.ts | 8 ++++++ src/classes/game/Creep.ts | 17 +++++++++-- src/classes/game/Grid.ts | 5 +++- src/classes/game/Projectile.ts | 50 ++++++++++++++++++++++++--------- src/classes/game/Tower.ts | 17 +++++++++-- src/classes/game/WaveManager.ts | 4 ++- src/scenes/Game.ts | 3 ++ 8 files changed, 84 insertions(+), 22 deletions(-) diff --git a/public/assets/Towers.json b/public/assets/Towers.json index 44b34c0..ba6f616 100644 --- a/public/assets/Towers.json +++ b/public/assets/Towers.json @@ -6,7 +6,7 @@ "description": "The building block of society, nothing more basic exists.", "stats": { "damage": 2, - "cooldown": 120, + "cooldown": 60, "gemSlotsAmount": 2, "cost": 100, "range": 3 diff --git a/src/classes/GameObject.ts b/src/classes/GameObject.ts index 6e822e6..e90b0bc 100644 --- a/src/classes/GameObject.ts +++ b/src/classes/GameObject.ts @@ -23,5 +23,13 @@ export default abstract class GameObject { return this._events; } + public copyContainerToBB() { + this.bb.x = this.container.x; + this.bb.y = this.container.y; + this.bb.width = this.container.width; + this.bb.height = this.container.height; + return this.bb; + } + public abstract update(elapsedMS): void; } diff --git a/src/classes/game/Creep.ts b/src/classes/game/Creep.ts index bbf4269..6d319c5 100644 --- a/src/classes/game/Creep.ts +++ b/src/classes/game/Creep.ts @@ -13,6 +13,7 @@ export enum CreepEvents { } export default class Creep extends GameObject { + public id: number; public creepType: CreepType; private sprite: PIXI.Sprite; private path: PathDefinition; @@ -25,14 +26,15 @@ export default class Creep extends GameObject { public died: boolean = false; public x: number; public y: number; - constructor(creepType: CreepType, path: PathDefinition) { + public dead: boolean = false; + constructor(creepType: CreepType, path: PathDefinition, id) { super(); this.creepType = creepType; this.stats = structuredClone(Assets.CreepStats[this.creepType]); this.sprite = new PIXI.Sprite({ texture: GameAssets.BasicCreepTexture, }); - this.container.label = 'creep-' + creepType.toString(); + this.id = id; // because wave manager spawns all instantly and i dont want // it to look like a shit game (they all spawn in top left corner) // i want to hide minion - mario @@ -46,10 +48,21 @@ export default class Creep extends GameObject { this.path = path; this.x = path[0][1] * 64 + 32; // centered this.y = path[0][0] * 64 + 32; + Globals.GameScene.events.on(CreepEvents.TakenDamage, (creepID, damage) => { + if (creepID != this.id) return; + this.health -= damage; + }); Globals.Grid.container.addChild(this.container); this.container.addChild(this.sprite); } public update(elapsedMS: number) { + if (this.dead) return; + if (this.health <= 0) { + Globals.GameScene.events.emit(CreepEvents.Died, this.maxHealth, this); + this.destroy(); + this.dead = true; + return; + } if (this.pathIndex + 1 == this.path.length) { if (this.escaped) return; this.events.emit(CreepEvents.Escaped, this); diff --git a/src/classes/game/Grid.ts b/src/classes/game/Grid.ts index 0f80765..a02ad52 100644 --- a/src/classes/game/Grid.ts +++ b/src/classes/game/Grid.ts @@ -141,7 +141,10 @@ export class Grid extends GameObject { } public update(elapsedMS) { this.creeps.forEach((creep) => { - creep.update(elapsedMS); + if (creep.dead) { + this.creeps.splice(this.creeps.indexOf(creep), 1); + creep = null; + } else creep.update(elapsedMS); }); } public getCellByRowAndCol(row, column) { diff --git a/src/classes/game/Projectile.ts b/src/classes/game/Projectile.ts index a0186eb..e30ca52 100644 --- a/src/classes/game/Projectile.ts +++ b/src/classes/game/Projectile.ts @@ -1,6 +1,7 @@ import * as PIXI from 'pixi.js'; import GameObject from '../GameObject'; import { Globals } from '../Bastion'; +import Creep, { CreepEvents } from './Creep'; export function calculateAngleToPoint(x, y, targetX, targetY) { const dx = targetX - x; @@ -9,15 +10,20 @@ export function calculateAngleToPoint(x, y, targetX, targetY) { } export default class Projectile extends GameObject { + public deleteMe: boolean = false; public sprite: PIXI.Sprite; public x: number; public y: number; public angle: number; public speed: number; - constructor(x, y, spriteTexture, angle) { + public damage: number; + public timeToLive: number = 1; + constructor(x, y, spriteTexture, angle, damage) { super(); + console.log('I SHOOTTED!'); this.x = x; this.y = y; + this.damage = damage; this.sprite = new PIXI.Sprite({ texture: spriteTexture, scale: 0.5, rotation: angle }); this.sprite.anchor.set(0.5); @@ -27,12 +33,24 @@ export default class Projectile extends GameObject { Globals.app.stage.addChild(this.container); this.angle = angle; - this.speed = 0.9; } + public destroy(): void { + super.destroy(); + this.deleteMe = true; + } public update(elapsedMS) { - if (this.x > 2000 || this.x < 0 || this.y > 2000 || this.y < 0) return this.destroy(); + if (this.deleteMe) return; + if (this.x > 2000 || this.x < 0 || this.y > 2000 || this.y < 0 || this.timeToLive <= 0) return this.destroy(); + Globals.Grid.creeps.forEach((creep) => { + if (this.timeToLive <= 0) return; + if (creep.container && this.checkCollision(creep)) { + this.timeToLive--; + this.onCollide(creep); + return; + } + }); this.x += Math.cos(this.angle) * this.speed * elapsedMS; this.y += Math.sin(this.angle) * this.speed * elapsedMS; @@ -40,18 +58,22 @@ export default class Projectile extends GameObject { this.container.y = this.y; } - public onCollide(otherSprite) { - console.log(`Collision detected with`, otherSprite); + public onCollide(creep) { + console.log('COLLIDED WITH' + creep); + Globals.GameScene.events.emit(CreepEvents.TakenDamage, creep.id, this.damage); } - public checkCollision(otherSprite) { - const boundsA = this.sprite.getBounds(); - const boundsB = otherSprite.getBounds(); - return ( - boundsA.x < boundsB.x + boundsB.width && - boundsA.x + boundsA.width > boundsB.x && - boundsA.y < boundsB.y + boundsB.height && - boundsA.y + boundsA.height > boundsB.y - ); + public checkCollision(creep: Creep) { + if (creep == null) return; + let mybb = this.copyContainerToBB(); + let otherbb = creep.copyContainerToBB(); + return mybb.getBounds().intersects(otherbb.getBounds()); + // console.log(boundsA, boundsB); + // return ( + // boundsA.x < boundsB.x + boundsB.width && + // boundsA.x + boundsA.width > boundsB.x && + // boundsA.y < boundsB.y + boundsB.height && + // boundsA.y + boundsA.height > boundsB.y + // ); } } diff --git a/src/classes/game/Tower.ts b/src/classes/game/Tower.ts index de989bd..c4ee7ea 100644 --- a/src/classes/game/Tower.ts +++ b/src/classes/game/Tower.ts @@ -38,6 +38,7 @@ export class Tower extends GameObject { private behaviour: string; private definition: TowerDefinition; private sprite: PIXI.Sprite; + private ticksUntilNextShot: number; private graphics: PIXI.Graphics = new PIXI.Graphics(); constructor(row, column, texture, definition, behaviour) { super(); @@ -45,6 +46,7 @@ export class Tower extends GameObject { this.column = column; this.behaviour = behaviour; this.definition = definition; + this.ticksUntilNextShot = 0; let parent: Cell = Globals.Grid.getCellByRowAndCol(row, column); this.sprite = new PIXI.Sprite({ texture: texture, @@ -79,17 +81,26 @@ export class Tower extends GameObject { let x = this.column * 64 + 32; let y = this.row * 64 + 32; let angle = calculateAngleToPoint(x, y, creep.x, creep.y); - this.projectiles.push(new Projectile(x, y, GameAssets.BasicProjectileTexture, angle)); + this.projectiles.push( + new Projectile(x, y, GameAssets.BasicProjectileTexture, angle, this.definition.stats.damage) + ); } public update(elapsedMS: any): void { this.projectiles.forEach((proj) => { - proj.update(elapsedMS); + if (proj.deleteMe) { + this.projectiles.splice(this.projectiles.indexOf(proj), 1); + proj = null; + } else proj.update(elapsedMS); }); if (this.behaviour == TowerBehaviours.BasicTowerBehaviour) { + if (this.ticksUntilNextShot > 0) this.ticksUntilNextShot--; let creepsInRange = this.GetCreepsInRange(); if (creepsInRange.length > 0) { let focus = creepsInRange[0]; - this.Shoot(focus); + if (this.ticksUntilNextShot == 0) { + this.ticksUntilNextShot = this.definition.stats.cooldown; + this.Shoot(focus); + } } } } diff --git a/src/classes/game/WaveManager.ts b/src/classes/game/WaveManager.ts index 9076b43..76ddfcc 100644 --- a/src/classes/game/WaveManager.ts +++ b/src/classes/game/WaveManager.ts @@ -23,6 +23,7 @@ export default class WaveManager { private started: boolean = false; public finished: boolean = false; public events = new PIXI.EventEmitter(); + private internalCreepId: number = 0; constructor(rounds: MissionRoundDefinition[], paths: PathDefinition[]) { Globals.WaveManager = this; this.rounds = rounds; @@ -37,7 +38,8 @@ export default class WaveManager { this.rounds[roundIndex].waves.forEach((wave) => { tickToSpawnAt += wave.firstCreepSpawnTick; wave.creeps.forEach((creep) => { - const creepObj = new Creep(creep, this.paths[0]); + const creepObj = new Creep(creep, this.paths[0], this.internalCreepId); + this.internalCreepId++; const creepInstance = { creep: creepObj, tickToSpawnAt, diff --git a/src/scenes/Game.ts b/src/scenes/Game.ts index 0a58e40..13890a9 100644 --- a/src/scenes/Game.ts +++ b/src/scenes/Game.ts @@ -58,6 +58,9 @@ export class GameScene extends Scene { this.onCreepEscaped(creep); }); }); + this.events.on(CreepEvents.Died, (playerAward, creepThatDied) => { + this.MissionStats.earnGold(playerAward); + }); this.sidebar = new Sidebar(SidebarRect); this.changeRoundButton = new Button(changeRoundButtonRect, 'Start', ButtonTexture.Button01, true); this.changeRoundButton.container.removeFromParent();