feat(towers): Implement ICBM tower with unique animation and explosion effects
This commit adds a new ICBM tower type with distinct visual effects and attack behavior. Key changes include: - Added ICBM tower configuration with 3 levels, damage, range, and AOE properties - Implemented custom animation system for tower firing sequences - Created specialized icbmAttackTarget method with missile launch and explosion effects - Updated tower rendering logic to handle ICBM's unique sprite setup (base + top) - Modified enemy targeting logic to exclude ICBM from normal rotation behavior - Added new animation definitions for ICBM explosion effects - Adjusted interface manager to display correct sprites for ICBM tower in menus The ICBM tower features a missile launch animation, AOE damage on impact, and explosion visual effects that distinguish it from other tower types.
This commit is contained in:
parent
b6bdaf8645
commit
b4c806cb83
Binary file not shown.
|
Before Width: | Height: | Size: 366 KiB After Width: | Height: | Size: 596 KiB |
Binary file not shown.
|
|
@ -35,13 +35,13 @@ export class Enemies {
|
|||
// Create Animations
|
||||
if (this.type.indexOf('basic') === 0) {
|
||||
this.createAnim('side', ENEMIES_CONFIG[this.type].spriteStart, ENEMIES_CONFIG[this.type].spriteStart+2);
|
||||
this.createAnim('up', ENEMIES_CONFIG[this.type].spriteStart+6, ENEMIES_CONFIG[this.type].spriteStart+8);
|
||||
this.createAnim('up', ENEMIES_CONFIG[this.type].spriteStart+6, ENEMIES_CONFIG[this.type].spriteStart+7);
|
||||
this.createAnim('down', ENEMIES_CONFIG[this.type].spriteStart+3, ENEMIES_CONFIG[this.type].spriteStart+5);
|
||||
this.createAnim('die', ENEMIES_CONFIG[this.type].spriteStart+8, ENEMIES_CONFIG[this.type].spriteStart+9, 0);
|
||||
}
|
||||
if (this.type.indexOf('advanced') === 0) {
|
||||
this.createAnim('side', ENEMIES_CONFIG[this.type].spriteStart+3, ENEMIES_CONFIG[this.type].spriteStart+5);
|
||||
this.createAnim('up', ENEMIES_CONFIG[this.type].spriteStart+6, ENEMIES_CONFIG[this.type].spriteStart+7);
|
||||
this.createAnim('up', ENEMIES_CONFIG[this.type].spriteStart+6, ENEMIES_CONFIG[this.type].spriteStart+8);
|
||||
this.createAnim('down', ENEMIES_CONFIG[this.type].spriteStart, ENEMIES_CONFIG[this.type].spriteStart+2);
|
||||
this.createAnim('die', ENEMIES_CONFIG[this.type].spriteStart+8, ENEMIES_CONFIG[this.type].spriteStart+9, 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ export class InterfaceManager {
|
|||
|
||||
constructor(scene) {
|
||||
this.scene = scene;
|
||||
this.gold = 150;
|
||||
this.gold = 1150;
|
||||
this.cores = 20;
|
||||
this.interfaceOpen = false;
|
||||
this.selectedTower = false;
|
||||
|
|
@ -315,7 +315,18 @@ export class InterfaceManager {
|
|||
}
|
||||
}).setOrigin(1, 0).setScrollFactor(0);
|
||||
slot.add(slotCost);
|
||||
console.log(type);
|
||||
if (type === 'icbm') {
|
||||
const towerBase = this.scene.add.sprite(startX, startY, 'towers', TOWERS_CONFIG[type].spriteStart)
|
||||
.setOrigin(0.5)
|
||||
.setScrollFactor(0);
|
||||
slot.add(towerBase);
|
||||
|
||||
const towerTop = this.scene.add.sprite(startX, startY, 'towers', TOWERS_CONFIG[type].spriteStart+3)
|
||||
.setOrigin(0.5)
|
||||
.setScrollFactor(0);
|
||||
slot.add(towerTop);
|
||||
} else {
|
||||
const towerBase = this.scene.add.sprite(startX, startY, 'towers', 7)
|
||||
.setOrigin(0.5)
|
||||
.setScrollFactor(0);
|
||||
|
|
@ -325,6 +336,7 @@ export class InterfaceManager {
|
|||
.setOrigin(0.5)
|
||||
.setScrollFactor(0);
|
||||
slot.add(towerTop);
|
||||
}
|
||||
|
||||
this.towerDisplay.add(slot);
|
||||
this.updateTowerAvail();
|
||||
|
|
@ -382,6 +394,19 @@ export class InterfaceManager {
|
|||
repeat: -1
|
||||
});
|
||||
|
||||
if (type === 'icbm') {
|
||||
const towerBase = this.scene.add.sprite(0, 0, 'towers', TOWERS_CONFIG[type].spriteStart)
|
||||
.setOrigin(0.5)
|
||||
.setScrollFactor(0)
|
||||
.setTint(0xa32a00);
|
||||
this.selectedTower.add(towerBase);
|
||||
|
||||
const towerTop = this.scene.add.sprite(0, 0, 'towers', TOWERS_CONFIG[type].spriteStart+3)
|
||||
.setOrigin(0.5)
|
||||
.setScrollFactor(0)
|
||||
.setTint(0xa32a00);
|
||||
this.selectedTower.add(towerTop);
|
||||
} else {
|
||||
const towerBase = this.scene.add.sprite(0, 0, 'towers', 7)
|
||||
.setOrigin(0.5)
|
||||
.setScrollFactor(0)
|
||||
|
|
@ -393,6 +418,7 @@ export class InterfaceManager {
|
|||
.setScrollFactor(0)
|
||||
.setTint(0xa32a00);
|
||||
this.selectedTower.add(towerTop);
|
||||
}
|
||||
|
||||
towerInteractive.on('pointerdown', () => {
|
||||
this.placeTower(type);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
export class TowerAnims {
|
||||
|
||||
constructor(scene) {
|
||||
this.scene = scene;
|
||||
|
||||
this.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({
|
||||
key: 'gun-level1-fire',
|
||||
frames: this.scene.anims.generateFrameNumbers('towers', {
|
||||
start: 1,
|
||||
end: 0,
|
||||
}),
|
||||
frameRate: 15,
|
||||
repeat: -1,
|
||||
});
|
||||
this.scene.anims.create({
|
||||
key: 'gun-level2-fire',
|
||||
frames: this.scene.anims.generateFrameNumbers('towers', {
|
||||
start: 3,
|
||||
end: 2,
|
||||
}),
|
||||
frameRate: 15,
|
||||
repeat: -1,
|
||||
});
|
||||
this.scene.anims.create({
|
||||
key: 'gun-level3-fire',
|
||||
frames: this.scene.anims.generateFrameNumbers('towers', {
|
||||
start: 5,
|
||||
end: 4,
|
||||
}),
|
||||
frameRate: 15,
|
||||
repeat: -1,
|
||||
yoyo: true
|
||||
});
|
||||
this.scene.anims.create({
|
||||
key: 'cannon-level1-fire',
|
||||
frames: this.scene.anims.generateFrameNumbers('towers', {
|
||||
start: 11,
|
||||
end: 10
|
||||
}),
|
||||
frameRate: 5,
|
||||
repeat: 0
|
||||
});
|
||||
this.scene.anims.create({
|
||||
key: 'flamethrower',
|
||||
frames: this.scene.anims.generateFrameNumbers('towers', {
|
||||
start: 23,
|
||||
end:25
|
||||
}),
|
||||
framerate: 4,
|
||||
repeat: -1
|
||||
});
|
||||
this.scene.anims.create({
|
||||
key: 'icbmExplosion',
|
||||
frames: this.scene.anims.generateFrameNumbers('towers', {
|
||||
start: 34,
|
||||
end: 37,
|
||||
framerate: 4,
|
||||
}),
|
||||
framerate: 4,
|
||||
repeat: -1
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,8 @@ export const TOWERS_CONFIG = {
|
|||
'dmgHigh': 30,
|
||||
'rate': 2000,
|
||||
'duration': 500,
|
||||
'range': 300
|
||||
'range': 300,
|
||||
'anim': 'gun-level1-fire'
|
||||
},
|
||||
'level2': {
|
||||
'sprite': 2,
|
||||
|
|
@ -18,7 +19,8 @@ export const TOWERS_CONFIG = {
|
|||
'dmgHigh': 35,
|
||||
'rate': 1500,
|
||||
'duration': 750,
|
||||
'range': 350
|
||||
'range': 350,
|
||||
'anim': 'gun-level2-fire'
|
||||
},
|
||||
'level3': {
|
||||
'sprite': 4,
|
||||
|
|
@ -27,7 +29,8 @@ export const TOWERS_CONFIG = {
|
|||
'dmgHigh': 50,
|
||||
'rate': 1000,
|
||||
'duration': 1000,
|
||||
'range': 400
|
||||
'range': 400,
|
||||
'anim': 'gun-level3-fire'
|
||||
}
|
||||
},
|
||||
'cannon': {
|
||||
|
|
@ -42,6 +45,7 @@ export const TOWERS_CONFIG = {
|
|||
'duration': 200,
|
||||
'range': 400,
|
||||
'aoe': 50,
|
||||
'anim': 'cannon-level1-fire'
|
||||
},
|
||||
'level2': {
|
||||
'sprite': 12,
|
||||
|
|
@ -52,6 +56,7 @@ export const TOWERS_CONFIG = {
|
|||
'duration': 500,
|
||||
'range': 450,
|
||||
'aoe': 25,
|
||||
'anim': 'cannon-level1-fire'
|
||||
},
|
||||
'level3': {
|
||||
'sprite': 14,
|
||||
|
|
@ -62,6 +67,7 @@ export const TOWERS_CONFIG = {
|
|||
'duration': 500,
|
||||
'range': 500,
|
||||
'aoe': 25,
|
||||
'anim': 'cannon-level1-fire'
|
||||
}
|
||||
},
|
||||
'flame': {
|
||||
|
|
@ -76,6 +82,7 @@ export const TOWERS_CONFIG = {
|
|||
'duration': 1000,
|
||||
'range': 300,
|
||||
'aoe': 100,
|
||||
'anim': 'flamethrower'
|
||||
},
|
||||
'level2': {
|
||||
'sprite': 21,
|
||||
|
|
@ -86,6 +93,7 @@ export const TOWERS_CONFIG = {
|
|||
'duration': 1000,
|
||||
'range': 350,
|
||||
'aoe': 100,
|
||||
'anim': 'flamethrower'
|
||||
},
|
||||
'level3': {
|
||||
'sprite': 22,
|
||||
|
|
@ -96,6 +104,42 @@ export const TOWERS_CONFIG = {
|
|||
'duration': 1000,
|
||||
'range': 400,
|
||||
'aoe': 100,
|
||||
'anim': 'flamethrower'
|
||||
}
|
||||
},
|
||||
'icbm': {
|
||||
'name': 'ICBM Launcher',
|
||||
'cost': 500,
|
||||
'spriteStart': 30,
|
||||
'dmgType': 'aoe',
|
||||
'level1': {
|
||||
'dmgLow': 40,
|
||||
'dmgHigh': 50,
|
||||
'rate': 10000,
|
||||
'duration': 2000,
|
||||
'range': 800,
|
||||
'aoe': 250,
|
||||
'anim': 'none'
|
||||
},
|
||||
'level2': {
|
||||
'cost': 1000,
|
||||
'dmgLow': 40,
|
||||
'dmgHigh': 50,
|
||||
'rate': 8000,
|
||||
'duration': 2000,
|
||||
'range': 800,
|
||||
'aoe': 250,
|
||||
'anim': 'none'
|
||||
},
|
||||
'level3': {
|
||||
'cost': 2000,
|
||||
'dmgLow': 50,
|
||||
'dmgHigh': 60,
|
||||
'rate': 5000,
|
||||
'duration': 2000,
|
||||
'range': 800,
|
||||
'aoe': 250,
|
||||
'anim': 'none'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import { ENEMIES_CONFIG } from './enemiesConfig.js';
|
||||
import { TOWERS_CONFIG } from './towerConfig.js';
|
||||
import { TowerAnims } from './towerAnims.js';
|
||||
|
||||
export class TowerManager {
|
||||
|
||||
|
|
@ -10,7 +11,7 @@ export class TowerManager {
|
|||
this.selectedTower = null;
|
||||
this.upgradeCost = null;
|
||||
|
||||
this.createAnims();
|
||||
this.towerAnims = new TowerAnims(scene);
|
||||
|
||||
// Add global click handler to close upgrade menu
|
||||
this.scene.input.on('pointerdown', (pointer) => {
|
||||
|
|
@ -23,13 +24,24 @@ export class TowerManager {
|
|||
createTower(type, x, y) {
|
||||
const posX = this.scene.gridToLocation(x);
|
||||
const posY = this.scene.gridToLocation(y);
|
||||
let towerBase;
|
||||
let tower;
|
||||
|
||||
const towerBase = this.scene.add.sprite(posX, posY, 'towers', 7).setDepth(10);
|
||||
const tower = this.scene.add.sprite(posX, posY, 'towers', TOWERS_CONFIG[type].spriteStart).setDepth(11);
|
||||
if (type === 'icbm') {
|
||||
towerBase = this.scene.add.sprite(posX, posY, 'towers', TOWERS_CONFIG[type].spriteStart).setDepth(10);
|
||||
tower = this.scene.add.sprite(posX, posY, 'towers', TOWERS_CONFIG[type].spriteStart+3).setDepth(11);
|
||||
tower.props = {
|
||||
'type': type,
|
||||
'level': 1
|
||||
}
|
||||
} else {
|
||||
towerBase = this.scene.add.sprite(posX, posY, 'towers', 7).setDepth(10);
|
||||
tower = this.scene.add.sprite(posX, posY, 'towers', TOWERS_CONFIG[type].spriteStart).setDepth(11);
|
||||
tower.props = {
|
||||
'type': type,
|
||||
'level': 1
|
||||
}
|
||||
}
|
||||
|
||||
// Generate unique ID for enemy
|
||||
const uniqueId = Phaser.Math.Between(100000, 999999);
|
||||
|
|
@ -39,7 +51,6 @@ export class TowerManager {
|
|||
tower.setInteractive();
|
||||
tower.on('pointerdown', () => {
|
||||
if (!this.selectedTower) {
|
||||
console.log('CLICK');
|
||||
this.upgradeMenu(tower);
|
||||
}
|
||||
});
|
||||
|
|
@ -188,7 +199,9 @@ export class TowerManager {
|
|||
|
||||
if (this.following.hasOwnProperty(tower.id)) {
|
||||
this.scene.enemies.children.iterate((enemy) => {
|
||||
if (this.following[tower.id] === enemy.props.id) {
|
||||
if (this.following[tower.id] === enemy.props.id
|
||||
&& type !== 'icbm')
|
||||
{
|
||||
// Rotate tower to face the enemy
|
||||
const angle = Phaser.Math.Angle.Between(towerX, towerY, enemy.x, enemy.y);
|
||||
tower.rotation = angle;
|
||||
|
|
@ -229,11 +242,15 @@ export class TowerManager {
|
|||
|
||||
this.following[tower.id] = furthestEnemy.props.id;
|
||||
const dmgType = TOWERS_CONFIG[tower.props.type].dmgType;
|
||||
if (tower.props.type === 'icbm') {
|
||||
this.icbmAttackTarget(tower, furthestEnemy);
|
||||
} else {
|
||||
if (dmgType === 'aoe') {
|
||||
this.aoeAttackTarget(tower, furthestEnemy);
|
||||
} else {
|
||||
this.attackTarget(tower, furthestEnemy);
|
||||
}
|
||||
}
|
||||
|
||||
this.lastFired[tower.id] = time;
|
||||
}
|
||||
|
|
@ -260,7 +277,10 @@ export class TowerManager {
|
|||
const angle = Phaser.Math.Angle.Between(tower.x, tower.y, enemy.x, enemy.y);
|
||||
tower.rotation = angle;
|
||||
|
||||
tower.play(`${type}-${level}-fire`, { 'duration': duration });
|
||||
const vid = tower.play(config.anim);
|
||||
this.scene.time.delayedCall(config.duration, () => {
|
||||
vid.stop();
|
||||
});
|
||||
|
||||
// Calculate damage (random between low and high)
|
||||
const damage = Phaser.Math.Between(dmgLow, dmgHigh);
|
||||
|
|
@ -362,6 +382,70 @@ export class TowerManager {
|
|||
});
|
||||
}
|
||||
|
||||
icbmAttackTarget(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 fireMissle = this.scene.add.sprite(tower.x, tower.y, 'towers', 33).setDepth(12);
|
||||
tower.setVisible(false);
|
||||
|
||||
this.scene.tweens.add({
|
||||
targets: fireMissle,
|
||||
x: fireMissle.x,
|
||||
y: fireMissle.y - 1200,
|
||||
duration: 2000,
|
||||
ease: 'Cubic.easeIn',
|
||||
onComplete: () => {
|
||||
fireMissle.setFlipY(true);
|
||||
fireMissle.x = enemy.x;
|
||||
this.scene.tweens.add({
|
||||
targets: fireMissle,
|
||||
x: enemy.x,
|
||||
y: enemy.y,
|
||||
duration: 500,
|
||||
ease: 'Cubic.easeIn',
|
||||
onComplete: () => {
|
||||
fireMissle.destroy();
|
||||
tower.setVisible(true);
|
||||
const damage = Phaser.Math.Between(dmgLow, dmgHigh);
|
||||
const aoeDistance = config.aoe;
|
||||
const enemiesInRange = this.scene.enemies.getChildren().filter(e => {
|
||||
return Phaser.Math.Distance.Between(e.x, e.y, enemy.x, enemy.y) <= aoeDistance;
|
||||
});
|
||||
const explosion = this.scene.add.sprite(enemy.x, enemy.y, 'towers', 34).setDepth(12);
|
||||
explosion.play('icbmExplosion');
|
||||
this.scene.tweens.add({
|
||||
targets:explosion,
|
||||
duration: 1000,
|
||||
scale: 3,
|
||||
alpha: .2,
|
||||
onComplete: () => {
|
||||
explosion.destroy();
|
||||
}
|
||||
});
|
||||
enemiesInRange.forEach(targetEnemy => {
|
||||
targetEnemy.props.health -= damage;
|
||||
|
||||
if (targetEnemy.props.health > 0) {
|
||||
this.createHealthBar(targetEnemy);
|
||||
} else {
|
||||
this.destroyEnemy(targetEnemy, tower);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
createHealthBar(enemy) {
|
||||
const barWidth = 30;
|
||||
const barHeight = 5;
|
||||
|
|
@ -465,64 +549,4 @@ export class TowerManager {
|
|||
this.following[tower.id] = null;
|
||||
}
|
||||
|
||||
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({
|
||||
key: 'gun-level1-fire',
|
||||
frames: this.scene.anims.generateFrameNumbers('towers', {
|
||||
start: 0,
|
||||
end: 1,
|
||||
}),
|
||||
frameRate: 15,
|
||||
repeat: 6,
|
||||
yoyo: true
|
||||
});
|
||||
this.scene.anims.create({
|
||||
key: 'gun-level2-fire',
|
||||
frames: this.scene.anims.generateFrameNumbers('towers', {
|
||||
start: 2,
|
||||
end: 3,
|
||||
}),
|
||||
frameRate: 15,
|
||||
repeat: 10,
|
||||
yoyo: true
|
||||
});
|
||||
this.scene.anims.create({
|
||||
key: 'gun-level3-fire',
|
||||
frames: this.scene.anims.generateFrameNumbers('towers', {
|
||||
start: 4,
|
||||
end: 5,
|
||||
}),
|
||||
frameRate: 15,
|
||||
repeat: 15,
|
||||
yoyo: true
|
||||
});
|
||||
this.scene.anims.create({
|
||||
key: 'cannon-level1-fire',
|
||||
frames: this.scene.anims.generateFrameNumbers('towers', {
|
||||
start: 11,
|
||||
end: 10
|
||||
}),
|
||||
frameRate: 5,
|
||||
repeat: 0
|
||||
});
|
||||
this.scene.anims.create({
|
||||
key: 'flamethrower',
|
||||
frames: this.scene.anims.generateFrameNumbers('towers', {
|
||||
start: 23,
|
||||
end:25
|
||||
}),
|
||||
framerate: 4,
|
||||
repeat: -1
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue