Add AOE tower support, implement flamethrower and cannon towers with animations, enhance tower targeting logic, and update tower configurations including damage types and ranges.

This commit is contained in:
Brian Fertig 2025-09-02 21:16:20 -06:00
parent 7180612aab
commit 37a292c022
8 changed files with 189 additions and 42 deletions

BIN
assets/ammo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
assets/ammo.psd Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 232 KiB

After

Width:  |  Height:  |  Size: 366 KiB

Binary file not shown.

View File

@ -13,6 +13,10 @@ export class Level extends Phaser.Scene {
preload() { preload() {
this.load.tilemapTiledJSON('level1', 'assets/level1.json'); this.load.tilemapTiledJSON('level1', 'assets/level1.json');
this.load.image('terrain', 'assets/terrain.png'); this.load.image('terrain', 'assets/terrain.png');
this.load.spritesheet('ammo', 'assets/ammo.png', {
frameWidth: 50,
frameHeight: 50
});
this.load.spritesheet('basic-enemies', 'assets/basic-enemies.png', { this.load.spritesheet('basic-enemies', 'assets/basic-enemies.png', {
frameWidth: 50, frameWidth: 50,
frameHeight: 50 frameHeight: 50
@ -57,23 +61,23 @@ export class Level extends Phaser.Scene {
} }
addControls() { addControls() {
this.input.on('wheel', (pointer, gameObjects, deltaX, deltaY) => { // this.input.on('wheel', (pointer, gameObjects, deltaX, deltaY) => {
const zoomSpeed = 0.1; // const zoomSpeed = 0.1;
if (deltaY < 0) { // if (deltaY < 0) {
// Zoom in // // Zoom in
this.cameras.main.zoom += zoomSpeed; // this.cameras.main.zoom += zoomSpeed;
} else if (deltaY > 0) { // } else if (deltaY > 0) {
// Zoom out // // Zoom out
this.cameras.main.zoom -= zoomSpeed; // this.cameras.main.zoom -= zoomSpeed;
} // }
// Limit zoom range to prevent extreme zoom levels // // Limit zoom range to prevent extreme zoom levels
this.cameras.main.zoom = Phaser.Math.Clamp(this.cameras.main.zoom, 0.5, 2); // this.cameras.main.zoom = Phaser.Math.Clamp(this.cameras.main.zoom, 0.5, 2);
// Zoom toward mouse position // // Zoom toward mouse position
const worldPoint = this.input.activePointer.positionToCamera(this.cameras.main); // const worldPoint = this.input.activePointer.positionToCamera(this.cameras.main);
this.cameras.main.centerOn(worldPoint.x, worldPoint.y); // this.cameras.main.centerOn(worldPoint.x, worldPoint.y);
}); // });
// Add camera panning functionality // Add camera panning functionality
this.input.on('pointerdown', (pointer) => { this.input.on('pointerdown', (pointer) => {

View File

@ -256,10 +256,6 @@ export class InterfaceManager {
x = 0; x = 0;
} }
} }
// this.gridAdd(0, 0, 'Gatlin Gun', 100, 'gun');
// this.gridAdd(0, 1, 'Flamethrower', 150, 'gun');
// this.gridAdd(1, 0, 'Laser', 200, 'gun');
// this.gridAdd(1, 1, 'Cannon', 200, 'cannon');
} }
gridAdd(x, y, text, cost, type) { gridAdd(x, y, text, cost, type) {
@ -367,6 +363,11 @@ export class InterfaceManager {
const towerInteractive = this.scene.add.rectangle(0, 0, 200, 200, 0x000000, 0); const towerInteractive = this.scene.add.rectangle(0, 0, 200, 200, 0x000000, 0);
this.selectedTower.add(towerInteractive); this.selectedTower.add(towerInteractive);
const rangeCircle = this.scene.add.circle(0, 0, TOWERS_CONFIG[type].level1.range, 0xc0b15c, 0.2)
.setOrigin(0.5)
.setScrollFactor(0);
this.selectedTower.add(rangeCircle);
const towerBase = this.scene.add.sprite(0, 0, 'towers', 7) const towerBase = this.scene.add.sprite(0, 0, 'towers', 7)
.setOrigin(0.5) .setOrigin(0.5)
.setScrollFactor(0) .setScrollFactor(0)
@ -442,14 +443,15 @@ export class InterfaceManager {
const tile = platformsLayer.getTileAt(tileX, tileY); const tile = platformsLayer.getTileAt(tileX, tileY);
if (tile) { if (tile) {
this.selectedTower.iterate((child) => { this.selectedTower.iterate((child) => {
if (child.type !== 'Rectangle') { if (child.type !== 'Rectangle' && child.type !== 'Arc') {
child.setTint(0x89ff5b); child.setTint(0x48ff00);
} }
}); });
this.selectedTower.safe = true; this.selectedTower.safe = true;
} else { } else {
this.selectedTower.iterate((child) => { this.selectedTower.iterate((child) => {
if (child.type !== 'Rectangle') { if (child.type !== 'Rectangle' && child.type !== 'Arc') {
console.log(child.type);
child.setTint(0xa32a00); child.setTint(0xa32a00);
} }
}); });

View File

@ -3,56 +3,87 @@ export const TOWERS_CONFIG = {
'name': 'Gatlin Gun', 'name': 'Gatlin Gun',
'cost': 100, 'cost': 100,
'spriteStart': 0, 'spriteStart': 0,
'type': 'direct', 'dmgType': 'direct',
'aoe': 0,
'level1': { 'level1': {
'dmgLow': 10, 'dmgLow': 10,
'dmgHigh': 30, 'dmgHigh': 30,
'rate': 2000, 'rate': 2000,
'duration': 500, 'duration': 500,
'range': 275 'range': 300
}, },
'level2': { 'level2': {
'dmgLow': 15, 'dmgLow': 15,
'dmgHigh': 35, 'dmgHigh': 35,
'rate': 1500, 'rate': 1500,
'duration': 750, 'duration': 750,
'range': 300 'range': 350
}, },
'level3': { 'level3': {
'dmgLow': 25, 'dmgLow': 25,
'dmgHigh': 50, 'dmgHigh': 50,
'rate': 1000, 'rate': 1000,
'duration': 1000, 'duration': 1000,
'range': 325 'range': 400
} }
}, },
'cannon': { 'cannon': {
'name': 'Cannon', 'name': 'Cannon',
'cost': 200, 'cost': 200,
'spriteStart': 10, 'spriteStart': 10,
'type': 'aoe', 'dmgType': 'aoe',
'aoe': 50,
'level1': { 'level1': {
'dmgLow': 15, 'dmgLow': 15,
'dmgHigh': 35, 'dmgHigh': 35,
'rate': 2000, 'rate': 2500,
'duration': 200, 'duration': 200,
'range': 400 'range': 400,
'aoe': 50,
}, },
'level2': { 'level2': {
'dmgLow': 25, 'dmgLow': 25,
'dmgHigh': 45, 'dmgHigh': 45,
'rate': 2000, 'rate': 2500,
'duration': 500, 'duration': 500,
'range': 450 'range': 450,
'aoe': 25,
}, },
'level3': { 'level3': {
'dmgLow': 35, 'dmgLow': 35,
'dmgHigh': 65, 'dmgHigh': 65,
'rate': 2000, 'rate': 2500,
'duration': 500, 'duration': 500,
'range': 500 'range': 500,
'aoe': 25,
}
},
'flame': {
'name': 'Flamethrower',
'cost': 150,
'spriteStart': 20,
'dmgType': 'aoe',
'level1': {
'dmgLow': 1,
'dmgHigh': 5,
'rate': 1000,
'duration': 1000,
'range': 300,
'aoe': 100,
},
'level2': {
'dmgLow': 3,
'dmgHigh': 8,
'rate': 1000,
'duration': 1000,
'range': 350,
'aoe': 100,
},
'level3': {
'dmgLow': 5,
'dmgHigh': 12,
'rate': 1000,
'duration': 1000,
'range': 400,
'aoe': 100,
} }
} }
} }

View File

@ -89,13 +89,19 @@ export class TowerManager {
}); });
this.following[tower.id] = furthestEnemy.props.id; this.following[tower.id] = furthestEnemy.props.id;
this.attackTarget(tower, furthestEnemy); const dmgType = TOWERS_CONFIG[tower.props.type].dmgType;
if (dmgType === 'aoe') {
this.aoeAttackTarget(tower, furthestEnemy);
} else {
this.attackTarget(tower, furthestEnemy);
}
this.lastFired[tower.id] = time; this.lastFired[tower.id] = time;
} }
} }
}); });
} }
attackTarget(tower, enemy) { attackTarget(tower, enemy) {
// Tower Properties // Tower Properties
const type = tower.props.type; const type = tower.props.type;
@ -121,11 +127,98 @@ export class TowerManager {
if (enemy.props.health > 0) { if (enemy.props.health > 0) {
this.createHealthBar(enemy); this.createHealthBar(enemy);
if (type === 'gun') {
const gunfire = this.scene.physics.add.sprite(enemy.x, enemy.y, 'ammo', 0);
gunfire.play('gunfire');
gunfire.setVelocity(enemy.body.velocity.x, enemy.body.velocity.y);
this.scene.time.delayedCall(config.duration, () => {
gunfire.destroy();
});
}
} else { } else {
this.destroyEnemy(enemy, tower); this.destroyEnemy(enemy, tower);
} }
} }
aoeAttackTarget(tower, enemy) {
// Tower Properties
const type = tower.props.type;
const level = 'level'+tower.props.level;
const config = TOWERS_CONFIG[type][level];
const duration = TOWERS_CONFIG[type][level].duration;
if (!config) return;
const dmgLow = config.dmgLow;
const dmgHigh = config.dmgHigh;
const angle = Phaser.Math.Angle.Between(tower.x, tower.y, enemy.x, enemy.y);
tower.rotation = angle;
if (type === 'cannon') {
const projDistance = 50;
const projX = tower.x + Math.cos(angle) * projDistance;
const projY = tower.y + Math.sin(angle) * projDistance;
const projectile = this.scene.add.sprite(projX, projY, 'ammo', 3);
this.scene.tweens.add({
targets: projectile,
x: enemy.x,
y: enemy.y,
duration: 100,
onComplete: () => {
projectile.setTexture('ammo', 4);
this.scene.time.delayedCall(200, () => {
projectile.destroy();
});
}
});
tower.play(`${type}-${level}-fire`, { 'duration': duration });
}
if (type === 'flame') {
const projDistance = 185;
const projDistance2 = 220;
const projX = tower.x + Math.cos(angle) * projDistance;
const projY = tower.y + Math.sin(angle) * projDistance;
const proj2X = tower.x + Math.cos(angle) * projDistance2;
const proj2Y = tower.y + Math.sin(angle) * projDistance2;
const projectile = this.scene.add.sprite(projX, projY, 'towers', 23).setScale(1.5).setAlpha(.5);
projectile.rotation = angle;
projectile.play(`flamethrower`);
this.scene.tweens.add({
targets: projectile,
x: proj2X,
y: proj2Y,
duration:1000,
onComplete: () => {
projectile.destroy();
}
});
}
// Calculate damage (random between low and high)
const damage = Phaser.Math.Between(dmgLow, dmgHigh);
// Get AOE distance
const aoeDistance = config.aoe;
// Apply damage to all enemies in range
const enemiesInRange = this.scene.enemies.getChildren().filter(e => {
return Phaser.Math.Distance.Between(e.x, e.y, enemy.x, enemy.y) <= aoeDistance;
});
enemiesInRange.forEach(targetEnemy => {
targetEnemy.props.health -= damage;
if (targetEnemy.props.health > 0) {
this.createHealthBar(targetEnemy);
} else {
this.destroyEnemy(targetEnemy, tower);
}
});
}
createHealthBar(enemy) { createHealthBar(enemy) {
const barWidth = 30; const barWidth = 30;
const barHeight = 5; const barHeight = 5;
@ -230,6 +323,15 @@ export class TowerManager {
} }
createAnims() { createAnims() {
this.scene.anims.create({
key: 'gunfire',
frames: this.scene.anims.generateFrameNumbers('ammo', {
start:0,
end:2
}),
framerate: 15,
repeat: -1
});
this.scene.anims.create({ this.scene.anims.create({
key: 'gun-level1-fire', key: 'gun-level1-fire',
frames: this.scene.anims.generateFrameNumbers('towers', { frames: this.scene.anims.generateFrameNumbers('towers', {
@ -237,7 +339,7 @@ export class TowerManager {
end: 1, end: 1,
}), }),
frameRate: 15, frameRate: 15,
repeat: 5, repeat: 6,
yoyo: true yoyo: true
}); });
this.scene.anims.create({ this.scene.anims.create({
@ -263,12 +365,20 @@ export class TowerManager {
this.scene.anims.create({ this.scene.anims.create({
key: 'cannon-level1-fire', key: 'cannon-level1-fire',
frames: this.scene.anims.generateFrameNumbers('towers', { frames: this.scene.anims.generateFrameNumbers('towers', {
start: 10, start: 11,
end: 11 end: 10
}), }),
frameRate:3, frameRate: 5,
repeat: 0, repeat: 0
yoyo: true });
this.scene.anims.create({
key: 'flamethrower',
frames: this.scene.anims.generateFrameNumbers('towers', {
start: 23,
end:25
}),
framerate: 4,
repeat: -1
}) })
} }