From cbbe6d51edad853e3282d069131beab7773e3029 Mon Sep 17 00:00:00 2001 From: Brian Fertig Date: Sat, 30 Aug 2025 22:47:11 -0600 Subject: [PATCH] feat(towers): Implement tower system with gun towers, range visualization, and basic firing logic This commit introduces a complete tower management system including: - Added TowerManager class to handle tower creation, updates, and firing logic - Implemented gun tower configuration with damage, rate, duration, and range stats - Created visual range circles for towers - Integrated tower placement in level1 scene - Added basic enemy detection and firing mechanics - Updated wave manager to support level completion state The changes enable the core tower defense functionality where towers automatically detect enemies in range and fire at them based on configured rates. --- src/levels/level1.js | 11 ++++++ src/support/towerConfig.js | 25 ++++++++++++++ src/support/towerManager.js | 67 +++++++++++++++++++++++++++++++++++++ src/support/waveManager.js | 20 +++++------ 4 files changed, 112 insertions(+), 11 deletions(-) create mode 100644 src/support/towerConfig.js create mode 100644 src/support/towerManager.js diff --git a/src/levels/level1.js b/src/levels/level1.js index 5b80fe7..6c7a4ef 100644 --- a/src/levels/level1.js +++ b/src/levels/level1.js @@ -1,4 +1,5 @@ import { WaveManager } from '../support/waveManager.js'; +import { TowerManager } from '../support/towerManager.js'; export class Level1 extends Phaser.Scene { constructor() { @@ -12,6 +13,7 @@ export class Level1 extends Phaser.Scene { preload() { this.load.tilemapTiledJSON('level1', 'assets/level1.json'); this.load.image('terrain', 'assets/terrain.png'); + this.load.image('josh', 'assets/josh-life.png'); this.load.spritesheet('basic-enemies', 'assets/basic-enemies.png', { frameWidth: 50, frameHeight: 50 @@ -26,8 +28,12 @@ export class Level1 extends Phaser.Scene { this.platformsLayer = this.levelMap.createLayer('platforms', terrainTiles); this.waveManager = new WaveManager(this, 1, 1); + this.towerManager = new TowerManager(this); this.enemies = this.physics.add.group(); + this.towers = this.physics.add.group(); + + this.towerManager.createTower('gun', 4, 2); this.physics.add.collider(this.enemies, this.mainLayer); this.physics.add.collider(this.enemies, this.platformsLayer); @@ -54,7 +60,12 @@ export class Level1 extends Phaser.Scene { moveNextStep(); } + gridToLocation(num, offset = 0) { + return num * 200 + 100 + offset; + } + update(time, delta) { this.waveManager.update(time, delta); + this.towerManager.update(time, delta); } } \ No newline at end of file diff --git a/src/support/towerConfig.js b/src/support/towerConfig.js new file mode 100644 index 0000000..a6ce4c6 --- /dev/null +++ b/src/support/towerConfig.js @@ -0,0 +1,25 @@ +export const TOWERS_CONFIG = { + 'gun': { + 'level1': { + 'dmgLow': 10, + 'dmgHigh': 30, + 'rate': 2000, + 'duration': 500, + 'range': 275 + }, + 'level2': { + 'dmgLow': 12, + 'dmgHigh': 33, + 'rate': 1500, + 'duration': 750, + 'range': 300 + }, + 'level3': { + 'dmgLow': 15, + 'dmgHigh': 35, + 'rate': 1000, + 'duration': 1000, + 'range': 325 + } + } +} \ No newline at end of file diff --git a/src/support/towerManager.js b/src/support/towerManager.js new file mode 100644 index 0000000..f53ea6a --- /dev/null +++ b/src/support/towerManager.js @@ -0,0 +1,67 @@ +import { TOWERS_CONFIG } from './towerConfig.js'; + +export class TowerManager { + + constructor(scene) { + this.scene = scene; + this.lastFired = {}; // Track last fire time for each tower + } + + createTower(type, x, y) { + const posX = this.scene.gridToLocation(x); + const posY = this.scene.gridToLocation(y); + + const tower = this.scene.add.image(posX, posY, 'josh'); + this.scene.towers.add(tower); + + // Draw range circle + const config = TOWERS_CONFIG[type].level1; + if (config) { + const range = config.range; + const circle = this.scene.add.circle(posX, posY, range, 0x00ff00, 0.2); + } + } + + update(time, delta) { + // Iterate through all towers + this.scene.towers.children.iterate((tower) => { + const towerX = tower.x; + const towerY = tower.y; + + // Get tower config + const type = 'gun'; // Assuming gun towers for now + const config = TOWERS_CONFIG[type].level1; + + if (!config) return; + + const range = config.range; + const rate = config.rate; + + // Check if enough time has passed since last fire + if (this.lastFired[tower.id] === undefined || + time - this.lastFired[tower.id] >= rate) { + + // Check for enemies in range + let inRange = false; + this.scene.enemies.children.iterate((enemy) => { + const distance = Phaser.Math.Distance.Between( + towerX, towerY, + enemy.x, enemy.y + ); + + if (distance <= range) { + inRange = true; + return false; // Stop iterating once we find one enemy + } + }); + + // Fire if enemies are in range + if (inRange) { + console.log('fire'); + this.lastFired[tower.id] = time; + } + } + }); + } + +} \ No newline at end of file diff --git a/src/support/waveManager.js b/src/support/waveManager.js index 5fccff8..10a5050 100644 --- a/src/support/waveManager.js +++ b/src/support/waveManager.js @@ -14,6 +14,7 @@ export class WaveManager { this.endX = WAVE_CONFIG[this.level].endX; this.endY = WAVE_CONFIG[this.level].endY; this.path = null; + this.levelActive = true; this.waveTimer = 0; this.waveScheduleStartTime(); @@ -32,13 +33,7 @@ export class WaveManager { this.waveScheduleStartTime(); } - gridToLocation(num, offset = 0) { - return num * 200 + 100 + offset; - } - update(time, delta) { - this.waveTimer += delta; - // Handle Enemy Pathing this.scene.enemies.children.iterate((enemy) => { const path = enemy.props.path; @@ -58,8 +53,8 @@ export class WaveManager { if (currentPoint && nextPoint) { // Calculate direction vector from current to next point - const dx = this.gridToLocation(nextPoint.x, offsetX) - this.gridToLocation(currentPoint.x, offsetX); - const dy = this.gridToLocation(nextPoint.y, offsetY) - this.gridToLocation(currentPoint.y, offsetY); + const dx = this.scene.gridToLocation(nextPoint.x, offsetX) - this.scene.gridToLocation(currentPoint.x, offsetX); + const dy = this.scene.gridToLocation(nextPoint.y, offsetY) - this.scene.gridToLocation(currentPoint.y, offsetY); const distance = Math.sqrt(dx * dx + dy * dy); // Normalize the direction @@ -90,8 +85,8 @@ export class WaveManager { // Check if we've reached the next point const distToNextPoint = Math.sqrt( - Math.pow(enemy.x - this.gridToLocation(nextPoint.x, offsetX), 2) + - Math.pow(enemy.y - this.gridToLocation(nextPoint.y, offsetY), 2) + Math.pow(enemy.x - this.scene.gridToLocation(nextPoint.x, offsetX), 2) + + Math.pow(enemy.y - this.scene.gridToLocation(nextPoint.y, offsetY), 2) ); if (distToNextPoint < segmentSpeed * 0.5) { // Threshold for reaching point @@ -116,7 +111,9 @@ export class WaveManager { }); // Handle Waves and Schedules - if (this.waveTimer >= this.waveStart) { + this.waveTimer += delta; + + if (this.waveTimer >= this.waveStart && this.levelActive === true) { console.log('Wave',this.wave,'Schedule',this.schedule); // Make path synchronous this.makePath().then(() => { @@ -129,6 +126,7 @@ export class WaveManager { this.nextWave(); } else { console.log('LEVEL COMPLETE'); + this.levelActive = false; } }); }