Implement enemy movement tracking, health system, and tower attack mechanics

- Added distance traveled tracking to enemies for progression metrics
- Implemented enemy health system with damage calculation and health bars
- Created tower attack functionality with damage application and enemy destruction
- Enhanced wave manager to track enemy movement distances during path traversal
- Updated enemy configuration with full health values and drop ranges
- Modified tower manager to support dynamic tower properties and level-based configurations

This commit introduces core combat mechanics including enemy health tracking, visual health bars, tower damage system, and proper distance calculation for enemy progression through paths.
This commit is contained in:
Brian Fertig 2025-08-31 07:36:45 -06:00
parent cbbe6d51ed
commit dc1eef50dc
5 changed files with 109 additions and 28 deletions

View File

@ -39,27 +39,6 @@ export class Level1 extends Phaser.Scene {
this.physics.add.collider(this.enemies, this.platformsLayer); this.physics.add.collider(this.enemies, this.platformsLayer);
} }
moveJoshAlongPath(path) {
let currentIndex = 0;
const moveNextStep = () => {
if (currentIndex >= path.length - 1) return; // Reached target
const nextPoint = path[++currentIndex];
// Move josh to the next point
this.tweens.add({
targets: this.josh,
x: nextPoint.x * 200 + 100,
y: nextPoint.y * 200 + 100,
duration: 2000,
onComplete: moveNextStep
});
};
moveNextStep();
}
gridToLocation(num, offset = 0) { gridToLocation(num, offset = 0) {
return num * 200 + 100 + offset; return num * 200 + 100 + offset;
} }

View File

@ -39,7 +39,8 @@ export class Enemies {
'pathPhase': 0, 'pathPhase': 0,
'speed': randSpeed, 'speed': randSpeed,
'health': ENEMIES_CONFIG[this.type].health, 'health': ENEMIES_CONFIG[this.type].health,
'type': this.type 'type': this.type,
'distanceTraveled': 0
}; };
this.scene.enemies.add(enemy); this.scene.enemies.add(enemy);

View File

@ -2,17 +2,23 @@ export const ENEMIES_CONFIG = {
'basic1': { 'basic1': {
'spread': 25, 'spread': 25,
'health': 100, 'health': 100,
'fullHealth': 100,
'speedLow': 25, 'speedLow': 25,
'speedHigh': 35, 'speedHigh': 35,
'spriteStart': 0, 'spriteStart': 0,
'spriteSheet': 'basic-enemies' 'spriteSheet': 'basic-enemies',
'dropLow': 1,
'dropHigh': 2
}, },
'basic2': { 'basic2': {
'spread': 0, 'spread': 0,
'health': 300, 'health': 300,
'fullHealth': 100,
'speedLow': 45, 'speedLow': 45,
'speedHigh': 55, 'speedHigh': 55,
'spriteStart': 0, 'spriteStart': 0,
'spriteSheet': 'basic-enemies' 'spriteSheet': 'basic-enemies',
'dropLow': 1,
'dropHigh': 2
} }
} }

View File

@ -12,6 +12,10 @@ export class TowerManager {
const posY = this.scene.gridToLocation(y); const posY = this.scene.gridToLocation(y);
const tower = this.scene.add.image(posX, posY, 'josh'); const tower = this.scene.add.image(posX, posY, 'josh');
tower.props = {
'type': type,
'level': 1
}
this.scene.towers.add(tower); this.scene.towers.add(tower);
// Draw range circle // Draw range circle
@ -29,8 +33,9 @@ export class TowerManager {
const towerY = tower.y; const towerY = tower.y;
// Get tower config // Get tower config
const type = 'gun'; // Assuming gun towers for now const type = tower.props.type;
const config = TOWERS_CONFIG[type].level1; const level = 'level'+tower.props.level;
const config = TOWERS_CONFIG[type][level];
if (!config) return; if (!config) return;
@ -44,6 +49,7 @@ export class TowerManager {
// Check for enemies in range // Check for enemies in range
let inRange = false; let inRange = false;
this.scene.enemies.children.iterate((enemy) => { this.scene.enemies.children.iterate((enemy) => {
const distanceTraveled = enemy.props.distanceTraveled;
const distance = Phaser.Math.Distance.Between( const distance = Phaser.Math.Distance.Between(
towerX, towerY, towerX, towerY,
enemy.x, enemy.y enemy.x, enemy.y
@ -51,6 +57,7 @@ export class TowerManager {
if (distance <= range) { if (distance <= range) {
inRange = true; inRange = true;
this.attackTarget(tower, enemy);
return false; // Stop iterating once we find one enemy return false; // Stop iterating once we find one enemy
} }
}); });
@ -64,4 +71,84 @@ export class TowerManager {
}); });
} }
attackTarget(tower, enemy) {
// Tower Properties
const type = tower.props.type;
const level = 'level'+tower.props.level;
const config = TOWERS_CONFIG[type][level];
if (!config) return;
const dmgLow = config.dmgLow;
const dmgHigh = config.dmgHigh;
// Enemy Information
const fullHealth = enemy.props.fullHealth;
const currentHealth = enemy.props.health;
// Calculate damage (random between low and high)
const damage = Phaser.Math.Between(dmgLow, dmgHigh);
// Apply damage to enemy
enemy.props.health -= damage;
// Create or update health bar
if (!enemy.healthBar) {
this.createHealthBar(enemy);
}
// Update health bar display
this.updateHealthBar(enemy);
// Check if enemy should be destroyed
if (enemy.props.health <= 0) {
this.destroyEnemy(enemy);
}
}
createHealthBar(enemy) {
const barWidth = 30;
const barHeight = 5;
const barX = enemy.x - barWidth/2;
const barY = enemy.y - enemy.displayHeight/2 - 10;
// Create health bar container
enemy.healthBar = this.scene.add.container(enemy.x, enemy.y - enemy.displayHeight/2 - 10);
// Background bar (gray)
const background = this.scene.add.rectangle(0, 0, barWidth, barHeight, 0x808080);
// Health fill (green)
const healthFill = this.scene.add.rectangle(0, 0, barWidth, barHeight, 0x00ff00);
// Position the fill relative to background
healthFill.x = -barWidth/2 + (barWidth * (enemy.props.health / enemy.props.fullHealth)) / 2;
enemy.healthBar.add([background, healthFill]);
}
updateHealthBar(enemy) {
if (!enemy.healthBar) return;
const barWidth = 30;
const healthPercentage = enemy.props.health / enemy.props.fullHealth;
// Update the fill width based on current health
enemy.healthBar.list[1].width = barWidth * healthPercentage;
// Position the container correctly
enemy.healthBar.x = enemy.x;
enemy.healthBar.y = enemy.y - enemy.displayHeight/2 - 10;
}
destroyEnemy(enemy) {
// Remove health bar if exists
if (enemy.healthBar) {
enemy.healthBar.destroy();
}
// Destroy the enemy sprite
enemy.destroy();
}
} }

View File

@ -42,6 +42,11 @@ export class WaveManager {
const offsetX = enemy.props.offsetX; const offsetX = enemy.props.offsetX;
const offsetY = enemy.props.offsetY; const offsetY = enemy.props.offsetY;
// Initialize distance tracking if not exists
if (enemy.props.distanceTraveled === undefined) {
enemy.props.distanceTraveled = 0;
}
// Only move if we have a valid path and pathPhase // Only move if we have a valid path and pathPhase
if (path && pathPhase !== undefined) { if (path && pathPhase !== undefined) {
// Calculate movement for this frame // Calculate movement for this frame
@ -64,6 +69,11 @@ export class WaveManager {
const velocX = normalizedDx * segmentSpeed; const velocX = normalizedDx * segmentSpeed;
const velocY = normalizedDy * segmentSpeed; const velocY = normalizedDy * segmentSpeed;
// Calculate actual movement this frame
const movementDistance = Math.sqrt(velocX * velocX + velocY * velocY) * (delta / 1000);
enemy.props.distanceTraveled += movementDistance;
// Move towards next point // Move towards next point
enemy.body.setVelocity(velocX, velocY); enemy.body.setVelocity(velocX, velocY);
@ -130,8 +140,6 @@ export class WaveManager {
} }
}); });
} }
} }
spawnSchedule() { spawnSchedule() {