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.
This commit is contained in:
Brian Fertig 2025-08-30 22:47:11 -06:00
parent 9bf0b55f33
commit cbbe6d51ed
4 changed files with 112 additions and 11 deletions

View File

@ -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);
}
}

View File

@ -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
}
}
}

View File

@ -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;
}
}
});
}
}

View File

@ -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;
}
});
}