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 6b43daa..e69c100 100644 --- a/public/assets/json/Towers.json +++ b/public/assets/json/Towers.json @@ -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/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 d59e5d1..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; @@ -156,6 +157,60 @@ export class RailProjectile extends Projectile { } } +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/TowerBehaviours.ts b/src/classes/game/TowerBehaviours.ts index b1b9ce5..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, { CreepEffects } from './Creep'; -import Projectile, { calculateAngleToPoint, VisualLightning } from './Projectile'; +import Projectile, { calculateAngleToPoint, TrapProjectile, VisualLightning } from './Projectile'; import { distance, Tower } from './Tower'; import * as PIXI from 'pixi.js'; @@ -78,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; @@ -99,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; @@ -116,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; @@ -140,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; @@ -212,16 +212,22 @@ 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; 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); + Engine.GameScene.events.emit(CreepEvents.GiveEffect, creep.id, CreepEffects.MovingBackwards, 750); + Engine.GameScene.events.emit( + CreepEvents.TakenDamage, + creep.id, + proj.damage / 2, + proj.gemResistanceModifications + ); }; } } @@ -233,10 +239,10 @@ 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; @@ -252,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) { @@ -270,6 +312,12 @@ export function DebuffTowerBehaviour(tower: Tower, elapsedMS: number) { 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 2b2f7dc..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,