diff --git a/public/assets/json/Towers.json b/public/assets/json/Towers.json index 9b34a7c..6b43daa 100644 --- a/public/assets/json/Towers.json +++ b/public/assets/json/Towers.json @@ -91,7 +91,7 @@ "cost": 125, "range": 2.5, "timeToLive": 12, - "pierce": 30 + "pierce": 1 } }, { diff --git a/src/classes/Events.ts b/src/classes/Events.ts index 67be16b..0416b23 100644 --- a/src/classes/Events.ts +++ b/src/classes/Events.ts @@ -7,6 +7,7 @@ export enum WaveManagerEvents { export enum CreepEvents { Died = 'died', TakenDamage = 'takenDamage', + GiveEffect = 'giveEffect', Escaped = 'escaped', Moved = 'moved', } diff --git a/src/classes/game/Creep.ts b/src/classes/game/Creep.ts index 4558aba..6483cc3 100644 --- a/src/classes/game/Creep.ts +++ b/src/classes/game/Creep.ts @@ -1,4 +1,3 @@ -import GameAssets from '../Assets'; import Assets from '../Assets'; import { Engine } from '../Bastion'; import { CreepResistancesDefinition, CreepStatsDefinition, CreepType, PathDefinition } from '../Definitions'; @@ -6,6 +5,21 @@ import GameObject from '../GameObject'; import * as PIXI from 'pixi.js'; import { CreepEvents } from '../Events'; +export enum CreepEffects { + MovingBackwards = 'MovingBackwards', + DebuffTowerDebuff = 'DebuffTowerDebuff', +} + +class Effect { + public effectEnum: CreepEffects; + public durationInMS: number; + public ticks: number = 0; + constructor(effectEnum: CreepEffects, durationInMS: number) { + this.effectEnum = effectEnum; + this.durationInMS = durationInMS; + } +} + export default class Creep extends GameObject { public id: number; public creepType: CreepType; @@ -17,6 +31,7 @@ export default class Creep extends GameObject { private direction: number = 1; private healthBarGraphics: PIXI.Graphics = new PIXI.Graphics(); private healthBarWidth = 50; + private effects: Effect[] = []; public health: number; public maxHealth: number; public escaped: boolean = false; @@ -56,7 +71,10 @@ export default class Creep extends GameObject { CreepEvents.TakenDamage, (creepID, damage, gemResistanceModifications: CreepResistancesDefinition) => { if (creepID != this.id) return; - + if (this.effects.find((e) => e.effectEnum == CreepEffects.DebuffTowerDebuff)) { + damage = damage * 1.5; + console.log('multiplying damage, ' + damage); + } // Apply resistances. this.health -= damage + damage * (gemResistanceModifications.physical - this.stats.resistance.physical); if (gemResistanceModifications.fire != 0) @@ -76,23 +94,49 @@ export default class Creep extends GameObject { this.UpdateHealthbar(); } ); + Engine.GameScene.events.on( + CreepEvents.GiveEffect, + (creepID: number, effect: CreepEffects, durationInMS: number) => { + if (creepID != this.id) return; + console.log(' I CAUGHT THE EVENT!'); + if (this.effects.find((e) => e.effectEnum == effect) == undefined) + this.effects.push(new Effect(effect, durationInMS)); + } + ); Engine.Grid.container.addChild(this.container); this.container.addChild(this.healthBarGraphics); this.container.addChild(this.sprite); this.UpdateHealthbar(); } + // Used ChatGPT to make this easier to understand. private UpdateHealthbar() { this.healthBarGraphics.clear(); + const hp = this.health; const maxHp = this.maxHealth; - const percent = hp / maxHp; - const width = this.healthBarWidth * percent; - // ! TODO: MAKE THIS BETTER! It works like this now, but I don't like how its implemented. - this.healthBarGraphics.rect(-this.healthBarWidth / 2 + 3, -32, this.healthBarWidth + 4, 14); + const percent = Math.max(0, hp / maxHp); + + const barWidth = this.healthBarWidth; + const barHeight = 10; // Height of the health bar + const borderPadding = 2; // Border thickness around the health bar + const offsetX = -barWidth / 2; // Centering the bar + const offsetY = -32; // Position above the entity + + // Border + this.healthBarGraphics.rect( + offsetX - borderPadding, + offsetY - borderPadding, + barWidth + borderPadding * 2, + barHeight + borderPadding * 2 + ); this.healthBarGraphics.fill({ color: 0x000000 }); - this.healthBarGraphics.rect(-this.healthBarWidth / 2 + 5, -30, width, 10); + + // Health + const healthWidth = barWidth * percent; + this.healthBarGraphics.rect(offsetX, offsetY, healthWidth, barHeight); this.healthBarGraphics.fill({ color: 0xff0000 }); } + public update(elapsedMS: number) { if (this.dead) return; if (this.health <= 0) { @@ -110,14 +154,30 @@ export default class Creep extends GameObject { this.escaped = true; return; } + const currentCell = this.path[this.pathIndex]; const targetCell = this.path[this.pathIndex + 1]; + const previousCell = this.pathIndex - 1 != 0 ? this.path[this.pathIndex - 1] : this.path[0]; + let isMovingBackwards = false; + for (let i = this.effects.length - 1; i >= 0; i--) { + let effect = this.effects[i]; + effect.ticks += elapsedMS * Engine.GameScene.gameSpeedMultiplier; + if (effect.ticks >= effect.durationInMS) this.effects.splice(i, 1); + else if (effect.effectEnum == CreepEffects.MovingBackwards) return (isMovingBackwards = true); + } - // Added + 32 for centering. - const targetX = targetCell[0] * Engine.GridCellSize + Engine.GridCellSize / 2; - const targetY = targetCell[1] * Engine.GridCellSize + Engine.GridCellSize / 2; - const directionX = targetCell[0] - currentCell[0]; - const directionY = targetCell[1] - currentCell[1]; + let targetX, targetY, directionX, directionY; + if (!isMovingBackwards) { + targetX = targetCell[0] * Engine.GridCellSize + Engine.GridCellSize / 2; + targetY = targetCell[1] * Engine.GridCellSize + Engine.GridCellSize / 2; + directionX = targetCell[0] - currentCell[0]; + directionY = targetCell[1] - currentCell[1]; + } else { + targetX = previousCell[0] * Engine.GridCellSize + Engine.GridCellSize / 2; + targetY = previousCell[1] * Engine.GridCellSize + Engine.GridCellSize / 2; + directionX = currentCell[0] - previousCell[0]; + directionY = previousCell[1] - currentCell[1]; + } if (directionX > 0) { // Going right if (this.direction != 1) { @@ -157,7 +217,9 @@ export default class Creep extends GameObject { } this.x += deltaX; this.y += deltaY; - if (increaseIndex) this.pathIndex++; + if (increaseIndex) { + if (!isMovingBackwards) this.pathIndex++; + } this.container.x = this.x; this.container.y = this.y; } diff --git a/src/classes/game/Projectile.ts b/src/classes/game/Projectile.ts index 5d880e4..d59e5d1 100644 --- a/src/classes/game/Projectile.ts +++ b/src/classes/game/Projectile.ts @@ -143,6 +143,7 @@ export class RailProjectile extends Projectile { newVisual.anchor.set(0.5, 0.5); this.visuals.push(newVisual); this.visuals.forEach((visual) => { + if (visual.scale == null) return visual.destroy(); if (visual.width && visual.height && visual.alpha) { visual.width -= 4; visual.height -= 4; diff --git a/src/classes/game/TowerBehaviours.ts b/src/classes/game/TowerBehaviours.ts index 8ca005f..b1b9ce5 100644 --- a/src/classes/game/TowerBehaviours.ts +++ b/src/classes/game/TowerBehaviours.ts @@ -2,7 +2,7 @@ import GameAssets from '../Assets'; import { Engine } from '../Bastion'; import { TowerType } from '../Definitions'; import { CreepEvents } from '../Events'; -import Creep from './Creep'; +import Creep, { CreepEffects } from './Creep'; import Projectile, { calculateAngleToPoint, VisualLightning } from './Projectile'; import { distance, Tower } from './Tower'; import * as PIXI from 'pixi.js'; @@ -16,17 +16,21 @@ import * as PIXI from 'pixi.js'; * @param elapsedMS - The elapsed time in milliseconds since the last update. */ function projectileCheck(tower: Tower, elapsedMS: number) { - tower.projectiles.forEach((proj) => { + for (let i = tower.projectiles.length - 1; i >= 0; i--) { + let proj = tower.projectiles[i]; + if (proj.deleteMe || tower.sold) { proj.collidedCreepIDs.forEach(() => { tower.damageDealt += tower.computedDamageToDeal; }); proj.collidedCreepIDs = []; - tower.projectiles.splice(tower.projectiles.indexOf(proj), 1); + proj.destroy(); - proj = null; - } else proj.update(elapsedMS); - }); + tower.projectiles.splice(i, 1); + } else { + proj.update(elapsedMS); + } + } } /** @@ -215,7 +219,10 @@ export function StrongTowerBehaviour(tower: Tower, elapsedMS: number) { let x = tower.column * Engine.GridCellSize + Engine.GridCellSize / 2; let y = tower.row * Engine.GridCellSize + Engine.GridCellSize / 2; tower.millisecondsUntilNextShot = tower.computedCooldown; - tower.Shoot(calculateAngleToPoint(x, y, focus.x, focus.y)); + let proj = tower.Shoot(calculateAngleToPoint(x, y, focus.x, focus.y)); + proj.onCollide = (creep: Creep, proj: Projectile) => { + Engine.GameScene.events.emit(CreepEvents.GiveEffect, creep.id, CreepEffects.MovingBackwards, 500); + }; } } } @@ -233,6 +240,7 @@ export function RailTowerBehaviour(tower: Tower, elapsedMS: number) { let x = tower.column * Engine.GridCellSize + Engine.GridCellSize / 2; let y = tower.row * Engine.GridCellSize + Engine.GridCellSize / 2; tower.millisecondsUntilNextShot = tower.computedCooldown; + // Custom logic handled via tower.Shoot tower.Shoot(calculateAngleToPoint(x, y, focus.x, focus.y)); } } @@ -259,7 +267,10 @@ export function DebuffTowerBehaviour(tower: Tower, elapsedMS: number) { let x = tower.column * Engine.GridCellSize + Engine.GridCellSize / 2; let y = tower.row * Engine.GridCellSize + Engine.GridCellSize / 2; tower.millisecondsUntilNextShot = tower.computedCooldown; - tower.Shoot(calculateAngleToPoint(x, y, focus.x, focus.y)); + let proj = tower.Shoot(calculateAngleToPoint(x, y, focus.x, focus.y)); + proj.onCollide = (creep: Creep, proj: Projectile) => { + Engine.GameScene.events.emit(CreepEvents.GiveEffect, creep.id, CreepEffects.DebuffTowerDebuff, 5000); + }; } } } diff --git a/src/scenes/Game.ts b/src/scenes/Game.ts index ff00725..2b2f7dc 100644 --- a/src/scenes/Game.ts +++ b/src/scenes/Game.ts @@ -167,7 +167,6 @@ export class GameScene extends Scene { this.ticker.add(() => { if (this.update) this.update(this.ticker.elapsedMS); - // if (this.isFastForwarded) this.update(this.ticker.elapsedMS); }); this.ticker.start(); }