feat: Add new sound effects, music, and enhance tower mechanics

- Added new audio assets including sound effects for towers (cannon, flamethrower, gatling gun, laser) and interface sounds (portal, coin, upgrade)
- Integrated background music tracks for levels
- Enhanced tower animations with new fire animations for cannon and laser towers
- Improved tower behavior with visual effects like laser beams and particle effects
- Updated tower configurations including AOE ranges and costs for different levels
- Modified wave progression to include more challenging enemy combinations
- Added sound feedback for key game events (tower placement, upgrades, waves)
This commit is contained in:
Brian Fertig 2025-09-05 21:10:28 -06:00
parent b4c806cb83
commit 7ee0ea2a47
29 changed files with 194 additions and 19 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 450 KiB

After

Width:  |  Height:  |  Size: 494 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

BIN
assets/music/level-bg1.mp3 Normal file

Binary file not shown.

BIN
assets/music/level-bg2.mp3 Normal file

Binary file not shown.

BIN
assets/sounds/cannon.mp3 Normal file

Binary file not shown.

BIN
assets/sounds/coin.mp3 Normal file

Binary file not shown.

BIN
assets/sounds/coinWoosh.mp3 Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
assets/sounds/laser.mp3 Normal file

Binary file not shown.

BIN
assets/sounds/nextWave.mp3 Normal file

Binary file not shown.

BIN
assets/sounds/portal.mp3 Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
assets/sounds/upgrade.mp3 Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 596 KiB

After

Width:  |  Height:  |  Size: 634 KiB

Binary file not shown.

View File

@ -31,6 +31,24 @@ export class Level extends Phaser.Scene {
frameHeight: 150, frameHeight: 150,
frameWidth: 150 frameWidth: 150
}); });
// Sound FX
this.load.audio('rocket-launch', 'assets/sounds/rocket-launch.mp3');
this.load.audio('rocket-explosion', 'assets/sounds/rocket-explosion.mp3');
this.load.audio('cannon', 'assets/sounds/cannon.mp3');
this.load.audio('flamethrower', 'assets/sounds/flamethrower.mp3');
this.load.audio('gatling-gun', 'assets/sounds/gatling-gun.mp3');
this.load.audio('interfaceOpen', 'assets/sounds/interfaceOpen.mp3');
this.load.audio('nextWave', 'assets/sounds/nextWave.mp3');
this.load.audio('towerPlace', 'assets/sounds/towerPlace.mp3');
this.load.audio('coin', 'assets/sounds/coin.mp3');
this.load.audio('coinWoosh', 'assets/sounds/coinWoosh.mp3');
this.load.audio('portal', 'assets/sounds/portal.mp3');
this.load.audio('laser', 'assets/sounds/laser.mp3');
this.load.audio('upgrade', 'assets/sounds/upgrade.mp3');
// Music
this.load.audio('level-bg1', 'assets/music/level-bg1.mp3');
} }
create() { create() {
@ -53,6 +71,10 @@ export class Level extends Phaser.Scene {
this.enemies = this.physics.add.group(); this.enemies = this.physics.add.group();
this.towers = this.physics.add.group(); this.towers = this.physics.add.group();
this.bgMusic = this.sound.add('level-bg1', { volume: 1 });
this.bgMusic.loop = true;
this.bgMusic.play();
this.physics.add.collider(this.enemies, this.mainLayer); this.physics.add.collider(this.enemies, this.mainLayer);
this.physics.add.collider(this.enemies, this.platformsLayer); this.physics.add.collider(this.enemies, this.platformsLayer);
} }

View File

@ -29,8 +29,22 @@ export class Enemies {
// Randomize Spawn Time a bit // Randomize Spawn Time a bit
this.scene.time.delayedCall(Phaser.Math.Between(this.spawnRange.low, this.spawnRange.high), () => { this.scene.time.delayedCall(Phaser.Math.Between(this.spawnRange.low, this.spawnRange.high), () => {
// Spawn Portal
this.scene.sound.play('portal');
const portal = this.scene.add.sprite(spawnX, spawnY, ENEMIES_CONFIG[this.type].spriteSheet, 90).setAlpha(1).setScale(2).setDepth(8);
this.scene.tweens.add({
targets: portal,
scale: .5,
alpha: .4,
angle: 270,
duration: 1000,
onComplete: () => {
portal.destroy();
}
});
// Create enemy and store reference // Create enemy and store reference
const enemy = this.scene.add.sprite(spawnX, spawnY, ENEMIES_CONFIG[this.type].spriteSheet, ENEMIES_CONFIG[this.type].spriteStart); const enemy = this.scene.add.sprite(spawnX, spawnY, ENEMIES_CONFIG[this.type].spriteSheet, ENEMIES_CONFIG[this.type].spriteStart).setDepth(10);
// Create Animations // Create Animations
if (this.type.indexOf('basic') === 0) { if (this.type.indexOf('basic') === 0) {

View File

@ -4,7 +4,7 @@ export class InterfaceManager {
constructor(scene) { constructor(scene) {
this.scene = scene; this.scene = scene;
this.gold = 1150; this.gold = 150;
this.cores = 20; this.cores = 20;
this.interfaceOpen = false; this.interfaceOpen = false;
this.selectedTower = false; this.selectedTower = false;
@ -204,6 +204,7 @@ export class InterfaceManager {
} }
openInterface() { openInterface() {
this.scene.sound.play('interfaceOpen');
this.interfaceOpen = true; this.interfaceOpen = true;
this.scene.levelScene.allowPan = false; this.scene.levelScene.allowPan = false;
this.upLeft.setAlpha(0).setRotation(Math.PI); this.upLeft.setAlpha(0).setRotation(Math.PI);
@ -315,7 +316,7 @@ export class InterfaceManager {
} }
}).setOrigin(1, 0).setScrollFactor(0); }).setOrigin(1, 0).setScrollFactor(0);
slot.add(slotCost); slot.add(slotCost);
console.log(type);
if (type === 'icbm') { if (type === 'icbm') {
const towerBase = this.scene.add.sprite(startX, startY, 'towers', TOWERS_CONFIG[type].spriteStart) const towerBase = this.scene.add.sprite(startX, startY, 'towers', TOWERS_CONFIG[type].spriteStart)
.setOrigin(0.5) .setOrigin(0.5)
@ -430,6 +431,7 @@ export class InterfaceManager {
placeTower(type) { placeTower(type) {
if (this.selectedTower.safe === true) { if (this.selectedTower.safe === true) {
this.scene.sound.play('towerPlace');
//Bring up Next Wave on first tower: //Bring up Next Wave on first tower:
if (this.scene.levelScene.towers.countActive() === 0) { if (this.scene.levelScene.towers.countActive() === 0) {
this.showNextWave(); this.showNextWave();

View File

@ -53,6 +53,24 @@ export class TowerAnims {
frameRate: 5, frameRate: 5,
repeat: 0 repeat: 0
}); });
this.scene.anims.create({
key: 'cannon-level2-fire',
frames: this.scene.anims.generateFrameNumbers('towers', {
start: 13,
end: 12
}),
frameRate: 5,
repeat: 0
});
this.scene.anims.create({
key: 'cannon-level3-fire',
frames: this.scene.anims.generateFrameNumbers('towers', {
start: 15,
end: 14
}),
frameRate: 5,
repeat: 0
});
this.scene.anims.create({ this.scene.anims.create({
key: 'flamethrower', key: 'flamethrower',
frames: this.scene.anims.generateFrameNumbers('towers', { frames: this.scene.anims.generateFrameNumbers('towers', {
@ -71,6 +89,6 @@ export class TowerAnims {
}), }),
framerate: 4, framerate: 4,
repeat: -1 repeat: -1
}) });
} }
} }

View File

@ -44,7 +44,7 @@ export const TOWERS_CONFIG = {
'rate': 2500, 'rate': 2500,
'duration': 200, 'duration': 200,
'range': 400, 'range': 400,
'aoe': 50, 'aoe': 55,
'anim': 'cannon-level1-fire' 'anim': 'cannon-level1-fire'
}, },
'level2': { 'level2': {
@ -55,7 +55,7 @@ export const TOWERS_CONFIG = {
'rate': 2500, 'rate': 2500,
'duration': 500, 'duration': 500,
'range': 450, 'range': 450,
'aoe': 25, 'aoe': 60,
'anim': 'cannon-level1-fire' 'anim': 'cannon-level1-fire'
}, },
'level3': { 'level3': {
@ -66,7 +66,7 @@ export const TOWERS_CONFIG = {
'rate': 2500, 'rate': 2500,
'duration': 500, 'duration': 500,
'range': 500, 'range': 500,
'aoe': 25, 'aoe': 65,
'anim': 'cannon-level1-fire' 'anim': 'cannon-level1-fire'
} }
}, },
@ -118,7 +118,7 @@ export const TOWERS_CONFIG = {
'rate': 10000, 'rate': 10000,
'duration': 2000, 'duration': 2000,
'range': 800, 'range': 800,
'aoe': 250, 'aoe': 200,
'anim': 'none' 'anim': 'none'
}, },
'level2': { 'level2': {
@ -128,7 +128,7 @@ export const TOWERS_CONFIG = {
'rate': 8000, 'rate': 8000,
'duration': 2000, 'duration': 2000,
'range': 800, 'range': 800,
'aoe': 250, 'aoe': 210,
'anim': 'none' 'anim': 'none'
}, },
'level3': { 'level3': {
@ -138,8 +138,42 @@ export const TOWERS_CONFIG = {
'rate': 5000, 'rate': 5000,
'duration': 2000, 'duration': 2000,
'range': 800, 'range': 800,
'aoe': 250, 'aoe': 225,
'anim': 'none' 'anim': 'none'
} }
},
'laser': {
'name': 'Laser Cannon',
'cost': 200,
'spriteStart': 40,
'dmgType': 'direct',
'level1': {
'dmgLow': 5,
'dmgHigh': 10,
'rate': 500,
'duration': 500,
'range': 350,
'anim': 'laser-level1-fire'
},
'level2': {
'sprite': 42,
'cost': 400,
'dmgLow': 8,
'dmgHigh': 16,
'rate': 500,
'duration': 500,
'range': 375,
'anim': 'laser-level2-fire'
},
'level3': {
'sprite': 44,
'cost': 800,
'dmgLow': 10,
'dmgHigh': 20,
'rate': 500,
'duration': 500,
'range': 400,
'anim': 'laser-level3-fire'
}
} }
} }

View File

@ -24,6 +24,7 @@ export class TowerManager {
createTower(type, x, y) { createTower(type, x, y) {
const posX = this.scene.gridToLocation(x); const posX = this.scene.gridToLocation(x);
const posY = this.scene.gridToLocation(y); const posY = this.scene.gridToLocation(y);
const towerContainer = this.scene.add.container();
let towerBase; let towerBase;
let tower; let tower;
@ -43,6 +44,11 @@ export class TowerManager {
} }
} }
tower.towerBase = towerBase;
towerContainer.add(towerBase);
towerContainer.add(tower);
// Generate unique ID for enemy // Generate unique ID for enemy
const uniqueId = Phaser.Math.Between(100000, 999999); const uniqueId = Phaser.Math.Between(100000, 999999);
tower.id = uniqueId; tower.id = uniqueId;
@ -68,7 +74,7 @@ export class TowerManager {
const currentLevel = `level${tower.props.level}`; const currentLevel = `level${tower.props.level}`;
const nextLevel = currentLevel === 'level3' ? 'level3' : `level${tower.props.level + 1}`; const nextLevel = currentLevel === 'level3' ? 'level3' : `level${tower.props.level + 1}`;
this.upgradeDetails = this.scene.add.container(); this.upgradeDetails = this.scene.add.container().setDepth(18);
const rangeCircle = this.scene.add.circle(tower.x, tower.y, TOWERS_CONFIG[tower.props.type][currentLevel].range, 0xc009900, 0.2) const rangeCircle = this.scene.add.circle(tower.x, tower.y, TOWERS_CONFIG[tower.props.type][currentLevel].range, 0xc009900, 0.2)
.setOrigin(0.5) .setOrigin(0.5)
@ -114,7 +120,7 @@ export class TowerManager {
}).setDepth(13).setAlpha(0).setInteractive(); }).setDepth(13).setAlpha(0).setInteractive();
this.upgradeDetails.add(this.upgradeText); this.upgradeDetails.add(this.upgradeText);
if (currentLevel === 'level3') upgradeText.setText('Max Upgrade').disableInteractive(); if (currentLevel === 'level3') this.upgradeText.setText('Max Upgrade').disableInteractive();
const upgradeCancelText = this.scene.add.text(tower.x +175, tower.y + 25, `Cancel`, { const upgradeCancelText = this.scene.add.text(tower.x +175, tower.y + 25, `Cancel`, {
fontFamily: 'neuropol, arial', fontFamily: 'neuropol, arial',
@ -182,7 +188,12 @@ export class TowerManager {
if (tower.props.level === 3) return; if (tower.props.level === 3) return;
tower.props.level++; tower.props.level++;
const newLevel = `level${tower.props.level}` const newLevel = `level${tower.props.level}`
tower.setTexture('towers', TOWERS_CONFIG[tower.props.type][newLevel].sprite); this.scene.sound.play('upgrade');
if (tower.props.type === 'icbm') {
tower.towerBase.setTexture('towers', 29+tower.props.level);
} else {
tower.setTexture('towers', TOWERS_CONFIG[tower.props.type][newLevel].sprite);
}
this.scene.UIScene.removeGold(TOWERS_CONFIG[tower.props.type][newLevel].cost); this.scene.UIScene.removeGold(TOWERS_CONFIG[tower.props.type][newLevel].cost);
} }
@ -277,10 +288,53 @@ export class TowerManager {
const angle = Phaser.Math.Angle.Between(tower.x, tower.y, enemy.x, enemy.y); const angle = Phaser.Math.Angle.Between(tower.x, tower.y, enemy.x, enemy.y);
tower.rotation = angle; tower.rotation = angle;
const vid = tower.play(config.anim); if (type === 'gun') {
this.scene.time.delayedCall(config.duration, () => { const gunSound = this.scene.sound.add('gatling-gun');
vid.stop(); gunSound.play();
});
const vid = tower.play(config.anim);
this.scene.time.delayedCall(config.duration, () => {
vid.stop();
gunSound.stop();
});
}
if (type === 'laser') {
// Create laser beam effect
const laserBeam = this.scene.add.graphics();
laserBeam.lineStyle(3, 0x00ff00, 1); // 3px wide, bright green, full opacity
// Draw line from tower to enemy
const projDistance = 60;
const projX = tower.x + Math.cos(angle) * projDistance;
const projY = tower.y + Math.sin(angle) * projDistance;
laserBeam.beginPath();
laserBeam.moveTo(projX, projY);
laserBeam.lineTo(enemy.x, enemy.y);
laserBeam.strokePath();
this.scene.sound.play('laser');
// Fade out effect over duration
this.scene.tweens.add({
targets: laserBeam,
alpha: 0,
duration: config.duration,
ease: 'Linear',
onComplete: () => {
laserBeam.destroy();
}
});
// Reset tower texture after duration
let currentFrame = 40;
if (tower.props.level === 2) currentFrame = 42;
if (tower.props.level === 3) currentFrame = 44;
tower.setTexture('towers', currentFrame+1);
this.scene.time.delayedCall(config.duration, () => {
tower.setTexture('towers', currentFrame);
});
}
// Calculate damage (random between low and high) // Calculate damage (random between low and high)
const damage = Phaser.Math.Between(dmgLow, dmgHigh); const damage = Phaser.Math.Between(dmgLow, dmgHigh);
@ -319,6 +373,7 @@ export class TowerManager {
tower.rotation = angle; tower.rotation = angle;
if (type === 'cannon') { if (type === 'cannon') {
this.scene.sound.play('cannon');
const projDistance = 50; const projDistance = 50;
const projX = tower.x + Math.cos(angle) * projDistance; const projX = tower.x + Math.cos(angle) * projDistance;
const projY = tower.y + Math.sin(angle) * projDistance; const projY = tower.y + Math.sin(angle) * projDistance;
@ -348,6 +403,8 @@ export class TowerManager {
const projectile = this.scene.add.sprite(projX, projY, 'towers', 23).setScale(1.5).setAlpha(.5); const projectile = this.scene.add.sprite(projX, projY, 'towers', 23).setScale(1.5).setAlpha(.5);
projectile.rotation = angle; projectile.rotation = angle;
projectile.play(`flamethrower`); projectile.play(`flamethrower`);
const flameSound = this.scene.sound.add('flamethrower');
flameSound.play();
this.scene.tweens.add({ this.scene.tweens.add({
targets: projectile, targets: projectile,
x: proj2X, x: proj2X,
@ -355,6 +412,7 @@ export class TowerManager {
duration:1000, duration:1000,
onComplete: () => { onComplete: () => {
projectile.destroy(); projectile.destroy();
flameSound.stop();
} }
}); });
@ -397,6 +455,7 @@ export class TowerManager {
const fireMissle = this.scene.add.sprite(tower.x, tower.y, 'towers', 33).setDepth(12); const fireMissle = this.scene.add.sprite(tower.x, tower.y, 'towers', 33).setDepth(12);
tower.setVisible(false); tower.setVisible(false);
this.scene.sound.play('rocket-launch');
this.scene.tweens.add({ this.scene.tweens.add({
targets: fireMissle, targets: fireMissle,
x: fireMissle.x, x: fireMissle.x,
@ -415,6 +474,7 @@ export class TowerManager {
onComplete: () => { onComplete: () => {
fireMissle.destroy(); fireMissle.destroy();
tower.setVisible(true); tower.setVisible(true);
this.scene.sound.play('rocket-explosion');
const damage = Phaser.Math.Between(dmgLow, dmgHigh); const damage = Phaser.Math.Between(dmgLow, dmgHigh);
const aoeDistance = config.aoe; const aoeDistance = config.aoe;
const enemiesInRange = this.scene.enemies.getChildren().filter(e => { const enemiesInRange = this.scene.enemies.getChildren().filter(e => {
@ -508,6 +568,7 @@ export class TowerManager {
const cam = this.scene.cameras.main; const cam = this.scene.cameras.main;
// Animate the cash drop moving to the new position // Animate the cash drop moving to the new position
this.scene.sound.play('coinWoosh', { volume: 2 });
this.scene.tweens.add({ this.scene.tweens.add({
targets: cashDrop, targets: cashDrop,
x: endX, x: endX,
@ -523,6 +584,7 @@ export class TowerManager {
alpha: 0, alpha: 0,
duration: 500, duration: 500,
onComplete: () => { onComplete: () => {
this.scene.sound.play('coin');
cashDrop.destroy(); cashDrop.destroy();
this.scene.UIScene.addGold(drop); this.scene.UIScene.addGold(drop);
this.scene.UIScene.interfaceManager.updateTowerAvail(); this.scene.UIScene.interfaceManager.updateTowerAvail();

View File

@ -98,12 +98,34 @@ export const WAVE_CONFIG = {
}, },
4: { 4: {
begin: 35, begin: 35,
basic1: 15 basic1: 25
}, },
5: { 5: {
begin: 40, begin: 40,
advanced1: 1 advanced1: 1
} }
},
//Wave
6: {
//Schedule
1: {
begin: 0,
basic2: 10,
advanced2: 1
},
2: {
begin: 15,
basic1: 20,
},
3: {
begin: 25,
basic3: 6,
advanced1: 1
},
4: {
begin: 35,
advanced2: 1
}
} }
} }
} }

View File

@ -29,6 +29,7 @@ export class WaveManager {
nextWave() { nextWave() {
// Next Wave will be started in the interfaceManager // Next Wave will be started in the interfaceManager
this.scene.sound.play('nextWave', { volume: 2 });
if (this.waveActive === true) { if (this.waveActive === true) {
this.wave++; this.wave++;
this.waveTimer = 0; this.waveTimer = 0;