diff --git a/docs/linecount.md b/docs/linecount.md index bf1b6a8..6c13e68 100644 --- a/docs/linecount.md +++ b/docs/linecount.md @@ -2,7 +2,7 @@ Generated by: `find ./src -name '*.ts' | xargs wc -l` 92 ./src/main.ts 1 ./src/vite-env.d.ts 17 ./src/classes/GameUIConstants.ts -145 ./src/classes/gui/TowerTab.ts +217 ./src/classes/gui/TowerTab.ts 375 ./src/classes/gui/TowerPanel.ts 52 ./src/classes/gui/TextInput.ts 211 ./src/classes/gui/ModalDialog.ts @@ -15,29 +15,30 @@ Generated by: `find ./src -name '*.ts' | xargs wc -l` 59 ./src/classes/gui/GamePausedDialog.ts 36 ./src/classes/gui/MessageBox.ts 205 ./src/classes/gui/Tooltip.ts -126 ./src/classes/Definitions.ts +133 ./src/classes/Definitions.ts 77 ./src/classes/game/WaveManager.ts -260 ./src/classes/game/Grid.ts +264 ./src/classes/game/Grid.ts 51 ./src/classes/game/Gem.ts -114 ./src/classes/game/TowerBehaviours.ts -154 ./src/classes/game/Tower.ts +324 ./src/classes/game/TowerBehaviours.ts +192 ./src/classes/game/Tower.ts 180 ./src/classes/game/MissionStats.ts 100 ./src/classes/game/AnimationManager.ts -177 ./src/classes/game/Creep.ts +239 ./src/classes/game/Creep.ts 47 ./src/classes/game/KeyboardManager.ts -104 ./src/classes/game/Projectile.ts +251 ./src/classes/game/Projectile.ts 86 ./src/classes/game/NotificationManager.ts -130 ./src/classes/game/TowerManager.ts +40 ./src/classes/game/DebrisManager.ts +135 ./src/classes/game/TowerManager.ts 71 ./src/classes/game/HighScoreManager.ts 76 ./src/classes/GuiObject.ts -203 ./src/classes/Assets.ts +209 ./src/classes/Assets.ts 52 ./src/classes/GameObject.ts -68 ./src/classes/Bastion.ts -30 ./src/classes/Events.ts +71 ./src/classes/Bastion.ts +31 ./src/classes/Events.ts 37 ./src/scenes/Scene.ts 17 ./src/scenes/Settings.ts 67 ./src/scenes/HowToPlay.ts 59 ./src/scenes/Main.ts -353 ./src/scenes/Game.ts +352 ./src/scenes/Game.ts 27 ./src/scenes/MissionPicker.ts -`4536 total` +`5130 total` diff --git a/public/assets/json/Towers.json b/public/assets/json/Towers.json index 9b34a7c..e69c100 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 } }, { @@ -120,7 +120,7 @@ "texture": null, "projectile": "stone", "projectileTextures": [], - "projectileTexturesArrayLength": 2, + "projectileTexturesArrayLength": 1, "description": "If you feel a little circular.", "stats": { "damage": 2, @@ -128,8 +128,8 @@ "gemSlotsAmount": 3, "cost": 125, "range": 2.5, - "timeToLive": 12, - "pierce": 30 + "timeToLive": 400, + "pierce": 2 } }, { diff --git a/public/assets/projectiles/stone/0.png b/public/assets/projectiles/stone/0.png index 774f930..923d968 100644 Binary files a/public/assets/projectiles/stone/0.png and b/public/assets/projectiles/stone/0.png differ diff --git a/public/assets/projectiles/stone/1.png b/public/assets/projectiles/stone/1.png deleted file mode 100644 index 553f463..0000000 Binary files a/public/assets/projectiles/stone/1.png and /dev/null differ 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/DebrisManager.ts b/src/classes/game/DebrisManager.ts index b226ca5..1d2f8a4 100644 --- a/src/classes/game/DebrisManager.ts +++ b/src/classes/game/DebrisManager.ts @@ -12,7 +12,6 @@ class Debris { this.debris.destroy(); } public update(elapsedMS) { - console.log(this.debris instanceof GameObject); if (this.debris instanceof GameObject) { this.debris.update(elapsedMS); } diff --git a/src/classes/game/Grid.ts b/src/classes/game/Grid.ts index 0ce25ef..b8ee63d 100644 --- a/src/classes/game/Grid.ts +++ b/src/classes/game/Grid.ts @@ -204,6 +204,17 @@ export class Grid extends GameObject { if (d < range + Engine.GridCellSize / 2) return true; else return false; } + public GetPathCellsInRange(row, col, range) { + let result = []; + this.cells.forEach((cell) => { + if (cell.isPath) { + if (this.IsCellInRangeOfOtherCell(row, col, range, cell)) { + result.push(cell); + } + } + }); + return result; + } public toggleGrid(force?: 'hide' | 'show') { this.cells.forEach((cell) => { if (force) { diff --git a/src/classes/game/Projectile.ts b/src/classes/game/Projectile.ts index 6ebf97d..52ee524 100644 --- a/src/classes/game/Projectile.ts +++ b/src/classes/game/Projectile.ts @@ -6,6 +6,7 @@ import { CreepEvents } from '../Events'; import { distance, Tower } from './Tower'; import { CreepResistancesDefinition } from '../Definitions'; import GameAssets from '../Assets'; +import { RoundMode } from '../../scenes/Game'; export function calculateAngleToPoint(x, y, targetX, targetY) { const dx = targetX - x; @@ -64,7 +65,7 @@ export default class Projectile extends GameObject { public update(elapsedMS) { if (this.deleteMe) return; - if (this.x > 2000 || this.x < 0 || this.y > 2000 || this.y < 0 || this.pierce <= 0 || this.timeToLive <= 0) + if (this.x > 1720 || this.x < 0 || this.y > 2000 || this.y < 0 || this.pierce <= 0 || this.timeToLive <= 0) return this.destroy(); this.timeToLive -= Engine.GameScene.gameSpeedMultiplier; Engine.Grid.creeps.forEach((creep) => { @@ -105,6 +106,111 @@ export default class Projectile extends GameObject { } } +export class RailProjectile extends Projectile { + public visuals: PIXI.Sprite[] = []; + public counter: number = 0; + constructor( + x, + y, + textures, + angle, + damage, + tint, + timeToLive, + pierce, + gemResistanceModifications: CreepResistancesDefinition + ) { + super(x, y, textures, angle, damage, tint, timeToLive, pierce, gemResistanceModifications); + this.pierce = 1000; + this.timeToLive = 2000; + } + + public destroy(): void { + super.destroy(); + this.visuals.forEach((visual) => visual.destroy()); + this.visuals = []; + } + public update(elapsedMS) { + super.update(elapsedMS); + if (this.counter == 2) { + this.counter = 0; + let newVisual = new PIXI.Sprite({ + x: this.x, + y: this.y, + rotation: this.angle, + texture: this.sprite.texture, + scale: 0.25, + }); + 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; + visual.alpha -= 0.1; + if (visual.width <= 0 || visual.height <= 0 || visual.alpha <= 0) visual.destroy(); + } + }); + Engine.GameScene.stage.addChild(newVisual); + } else this.counter++; + } +} + +export class TrapProjectile extends Projectile { + public visuals: PIXI.Sprite[] = []; + public counter: number = 0; + private goalX: number; + private goalY: number; + constructor( + x, + y, + textures, + angle, + goalX, + goalY, + damage, + tint, + timeToLive, + pierce, + gemResistanceModifications: CreepResistancesDefinition + ) { + super(x, y, textures, angle, damage, tint, timeToLive, pierce, gemResistanceModifications); + this.sprite.scale = 0.5; + this.goalX = goalX; + this.goalY = goalY; + } + + public destroy(): void { + super.destroy(); + this.visuals.forEach((visual) => visual.destroy()); + this.visuals = []; + } + public update(elapsedMS) { + if (this.deleteMe) return; + if (this.x > 1720 || this.x < 0 || this.y > 2000 || this.y < 0 || this.pierce <= 0 || this.timeToLive <= 0) + return this.destroy(); + if (distance(this.x, this.y, this.goalX, this.goalY) < 25) { + this.timeToLive -= Engine.GameScene.gameSpeedMultiplier; + Engine.Grid.creeps.forEach((creep) => { + if (this.pierce <= 0) return; + if (creep && creep.container && this.checkCollision(creep)) { + this.collidedCreepIDs.push(creep); + this.pierce--; + this.onCollide(creep, this); + return; + } + }); + } else { + this.x += Math.cos(this.angle) * this.speed * elapsedMS * Engine.GameScene.gameSpeedMultiplier; + this.y += Math.sin(this.angle) * this.speed * elapsedMS * Engine.GameScene.gameSpeedMultiplier; + + this.container.x = this.x; + this.container.y = this.y; + } + } +} + export class VisualLightning extends GameObject { public deleteMe: boolean = false; private c: Creep; diff --git a/src/classes/game/Tower.ts b/src/classes/game/Tower.ts index 3443383..154545f 100644 --- a/src/classes/game/Tower.ts +++ b/src/classes/game/Tower.ts @@ -4,7 +4,7 @@ import GameObject from '../GameObject'; import { CreepResistancesDefinition, TowerDefinition } from '../Definitions'; import { Cell } from './Grid'; import { TowerBehaviours } from './TowerManager'; -import Projectile from './Projectile'; +import Projectile, { RailProjectile } from './Projectile'; import Gem from './Gem'; import { DebuffTowerBehaviour, @@ -132,18 +132,34 @@ export class Tower extends GameObject { } combinedTint = color; } - let proj = new Projectile( - x, - y, - this.definition.projectileTextures, - angle, - this.computedDamageToDeal, - combinedTint, - this.computedTimeToLive, - this.computedPierce, - this.totalGemResistanceModifications - ); - const time = new Date().toISOString(); + + let proj; + if (this.behaviour == TowerBehaviours.RailTowerBehaviour) { + proj = new RailProjectile( + x, + y, + this.definition.projectileTextures, + angle, + this.computedDamageToDeal, + combinedTint, + this.computedTimeToLive, + this.computedPierce, + this.totalGemResistanceModifications + ); + } else { + proj = new Projectile( + x, + y, + this.definition.projectileTextures, + angle, + this.computedDamageToDeal, + combinedTint, + this.computedTimeToLive, + this.computedPierce, + this.totalGemResistanceModifications + ); + } + // const time = new Date().toISOString(); // console.log(`${time} ${this.definition.name} shot at ${angle} degrees`); this.projectiles.push(proj); return proj; diff --git a/src/classes/game/TowerBehaviours.ts b/src/classes/game/TowerBehaviours.ts index 396d299..16bf6b4 100644 --- a/src/classes/game/TowerBehaviours.ts +++ b/src/classes/game/TowerBehaviours.ts @@ -1,9 +1,10 @@ +import { RoundMode } from '../../scenes/Game'; import GameAssets from '../Assets'; import { Engine } from '../Bastion'; import { TowerType } from '../Definitions'; import { CreepEvents } from '../Events'; -import Creep from './Creep'; -import Projectile, { calculateAngleToPoint, VisualLightning } from './Projectile'; +import Creep, { CreepEffects } from './Creep'; +import Projectile, { calculateAngleToPoint, TrapProjectile, VisualLightning } from './Projectile'; import { distance, Tower } from './Tower'; import * as PIXI from 'pixi.js'; @@ -16,17 +17,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); + } + } } /** @@ -74,10 +79,10 @@ export function computeGemImprovements(tower: Tower) { if (tower.parent.isBuffedBy.length > 0 && tower.definition.name != GameAssets.Towers[TowerType.Buff].name) { let buffedBy = tower.parent.isBuffedBy[0]; tower.computedDamageToDeal += Number((buffedBy.computedDamageToDeal / 2).toFixed(1)); - tower.computedCooldown -= (buffedBy.computedCooldown * 100) / 5 / 100; + tower.computedCooldown -= Number((buffedBy.computedCooldown / 5).toFixed(1)); tower.computedRange += Number((buffedBy.computedRange / 10).toFixed(1)); - tower.computedTimeToLive += (buffedBy.computedTimeToLive * 100) / 5 / 100; - tower.computedPierce += (buffedBy.computedPierce * 100) / 4 / 100; + tower.computedTimeToLive += Number((buffedBy.computedTimeToLive / 5).toFixed(1)); + tower.computedPierce += Number((buffedBy.computedPierce / 4).toFixed(1)); tower.totalGemResistanceModifications.physical += (buffedBy.totalGemResistanceModifications.physical * 100) / 2 / 100; @@ -95,10 +100,10 @@ export function BasicTowerBehaviour(tower: Tower, elapsedMS: number) { projectileCheck(tower, elapsedMS); if (tower.millisecondsUntilNextShot > 0) tower.millisecondsUntilNextShot -= elapsedMS * Engine.GameScene.gameSpeedMultiplier; - let creepsInRange = tower.GetCreepsInRange(); - if (creepsInRange.length > 0) { - let focus = creepsInRange[0]; - if (tower.millisecondsUntilNextShot <= 0) { + else { + let creepsInRange = tower.GetCreepsInRange(); + if (creepsInRange.length > 0) { + let focus = creepsInRange[0]; let x = tower.column * Engine.GridCellSize + Engine.GridCellSize / 2; let y = tower.row * Engine.GridCellSize + Engine.GridCellSize / 2; tower.millisecondsUntilNextShot = tower.computedCooldown; @@ -112,9 +117,9 @@ export function CircleTowerBehaviour(tower: Tower, elapsedMS: number) { projectileCheck(tower, elapsedMS); if (tower.millisecondsUntilNextShot > 0) tower.millisecondsUntilNextShot -= elapsedMS * Engine.GameScene.gameSpeedMultiplier; - let creepsInRange = tower.GetCreepsInRange(); - if (creepsInRange.length > 0) { - if (tower.millisecondsUntilNextShot <= 0) { + else { + let creepsInRange = tower.GetCreepsInRange(); + if (creepsInRange.length > 0) { tower.millisecondsUntilNextShot = tower.computedCooldown; let x = tower.column * Engine.GridCellSize + Engine.GridCellSize / 2; let y = tower.row * Engine.GridCellSize + Engine.GridCellSize / 2; @@ -136,12 +141,11 @@ export function ElectricTowerBehaviour(tower: Tower, elapsedMS: number) { if (tower.millisecondsUntilNextShot > 0) tower.millisecondsUntilNextShot -= elapsedMS * Engine.GameScene.gameSpeedMultiplier; + else { + let creepsInRange = tower.GetCreepsInRange(); - let creepsInRange = tower.GetCreepsInRange(); - - if (creepsInRange.length > 0) { - let focus = creepsInRange[0]; - if (tower.millisecondsUntilNextShot <= 0) { + if (creepsInRange.length > 0) { + let focus = creepsInRange[0]; let x = tower.column * Engine.GridCellSize + Engine.GridCellSize / 2; let y = tower.row * Engine.GridCellSize + Engine.GridCellSize / 2; tower.millisecondsUntilNextShot = tower.computedCooldown; @@ -149,36 +153,47 @@ export function ElectricTowerBehaviour(tower: Tower, elapsedMS: number) { proj.onCollide = (creep: Creep, proj: Projectile) => { let chainedCreepIds = []; proj.pierce = 0; - function checkNearBy(c) { - let nearByCreeps = Engine.Grid.creeps.filter((nCreep) => { + let recursionCount = 0; + function checkNearBy(c: Creep) { + if (recursionCount >= 50) { + Engine.GameScene.PauseGame(); + Engine.NotificationManager.Notify( + 'Electric Tower max recursion exceeded! Please tell koneko! (game paused)', + 'danger' + ); + return; + } + recursionCount++; + + Engine.Grid.creeps.forEach((nCreep) => { if (nCreep.id != creep.id) { const x = nCreep.x; const y = nCreep.y; const radius = 1.5 * Engine.GridCellSize; const d = distance(c.x, c.y, x, y); - return d < radius; + if (d < radius) { + if (chainedCreepIds.find((crID) => crID == nCreep.id) != undefined) return; + chainedCreepIds.push(nCreep.id); + new VisualLightning(c, nCreep); + Engine.GameScene.events.emit( + CreepEvents.TakenDamage, + nCreep.id, + Math.round(proj.damage / 2), + proj.gemResistanceModifications + ); + checkNearBy(nCreep); + } } }); - nearByCreeps.forEach((nearCreep) => { - if (!chainedCreepIds.find((crID) => crID == nearCreep.id)) chainedCreepIds.push(nearCreep.id); - else return; - checkNearBy(nearCreep); - new VisualLightning(c, nearCreep); - Engine.GameScene.events.emit( - CreepEvents.TakenDamage, - nearCreep.id, - Math.round(proj.damage / 2), - proj.gemResistanceModifications - ); - }); } - checkNearBy(creep); Engine.GameScene.events.emit( CreepEvents.TakenDamage, creep.id, proj.damage, proj.gemResistanceModifications ); + chainedCreepIds.push(creep.id); + checkNearBy(creep); }; } } @@ -197,14 +212,23 @@ export function StrongTowerBehaviour(tower: Tower, elapsedMS: number) { if (tower.millisecondsUntilNextShot > 0) tower.millisecondsUntilNextShot -= elapsedMS * Engine.GameScene.gameSpeedMultiplier; - let creepsInRange = tower.GetCreepsInRange(); - if (creepsInRange.length > 0) { - let focus = creepsInRange[0]; - if (tower.millisecondsUntilNextShot <= 0) { + else { + let creepsInRange = tower.GetCreepsInRange(); + if (creepsInRange.length > 0) { + let focus = creepsInRange[0]; 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, 750); + Engine.GameScene.events.emit( + CreepEvents.TakenDamage, + creep.id, + proj.damage / 2, + proj.gemResistanceModifications + ); + }; } } } @@ -215,13 +239,14 @@ export function RailTowerBehaviour(tower: Tower, elapsedMS: number) { if (tower.millisecondsUntilNextShot > 0) tower.millisecondsUntilNextShot -= elapsedMS * Engine.GameScene.gameSpeedMultiplier; - let creepsInRange = tower.GetCreepsInRange(); - if (creepsInRange.length > 0) { - let focus = creepsInRange[0]; - if (tower.millisecondsUntilNextShot <= 0) { + else { + let creepsInRange = tower.GetCreepsInRange(); + if (creepsInRange.length > 0) { + let focus = creepsInRange[0]; 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)); } } @@ -233,6 +258,42 @@ export function TrapperTowerBehaviour(tower: Tower, elapsedMS: number) { if (tower.millisecondsUntilNextShot > 0) tower.millisecondsUntilNextShot -= elapsedMS * Engine.GameScene.gameSpeedMultiplier; + else { + if (Engine.GameScene.roundMode != RoundMode.Combat) return; + let x = tower.column * Engine.GridCellSize + Engine.GridCellSize / 2; + let y = tower.row * Engine.GridCellSize + Engine.GridCellSize / 2; + let cells = Engine.Grid.GetPathCellsInRange(tower.row, tower.column, tower.computedRange); + if (cells.length > 0) { + let idx = Math.floor(Math.random() * cells.length); + let cell = cells[idx]; + let cellx = cell.column * Engine.GridCellSize + Engine.GridCellSize / 2; + let celly = cell.row * Engine.GridCellSize + Engine.GridCellSize / 2; + let combinedTint = new PIXI.Color('white'); + if (tower.slottedGems.length > 0) { + let color = new PIXI.Color(tower.slottedGems[0].definition.color); + for (let i = 1; i < tower.slottedGems.length; i++) { + const element = tower.slottedGems[i]; + color.multiply(element.definition.color); + } + combinedTint = color; + } + let proj = new TrapProjectile( + x, + y, + tower.definition.projectileTextures, + calculateAngleToPoint(x, y, cellx, celly), + cellx, + celly, + tower.computedDamageToDeal, + combinedTint, + tower.computedTimeToLive, + tower.computedPierce, + tower.totalGemResistanceModifications + ); + tower.projectiles.push(proj); + tower.millisecondsUntilNextShot = tower.computedCooldown; + } + } } export function DebuffTowerBehaviour(tower: Tower, elapsedMS: number) { @@ -248,7 +309,16 @@ 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); + Engine.GameScene.events.emit( + CreepEvents.TakenDamage, + creep.id, + proj.damage / 1.5, + proj.gemResistanceModifications + ); + }; } } } diff --git a/src/scenes/Game.ts b/src/scenes/Game.ts index ff00725..eba63bb 100644 --- a/src/scenes/Game.ts +++ b/src/scenes/Game.ts @@ -20,7 +20,7 @@ import EndGameDialog from '../classes/gui/EndGameDialog'; import HighScoreDialog, { HighScoreDialogButtons } from '../classes/gui/HighScoreDialog'; import GamePausedDialog from '../classes/gui/GamePausedDialog'; -enum RoundMode { +export enum RoundMode { Purchase = 0, Combat = 1, Misc = 2, @@ -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(); }