Add medium enemies, enhance core damage system, update wave configurations, and improve tower upgrade UI

This commit introduces medium enemy types with updated sprite sheets and configurations, implements a core damage system that reduces health when enemies reach the end point, adds new wave configurations for levels 7 and 8 with mixed enemy types, enhances the tower upgrade menu UI by removing animations and improving layout, updates enemy spread values for better balance, and fixes various visual elements including camera scroll factors and text positioning.
This commit is contained in:
Brian Fertig 2025-09-06 12:19:37 -06:00
parent 7ee0ea2a47
commit 283a51044c
15 changed files with 249 additions and 82 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

View File

@ -3,19 +3,19 @@
"infinite":false,
"layers":[
{
"data":[2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 12, 0, 0,
2, 1, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 12, 0, 0,
2, 1, 1, 1, 2, 1, 1, 2, 2, 1, 1, 1, 2, 12, 0, 0,
2, 2, 2, 2, 2, 2, 1, 2, 2, 1, 2, 2, 2, 12, 0, 0,
2, 2, 2, 2, 2, 2, 1, 2, 2, 1, 2, 2, 2, 12, 0, 0,
2, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 2, 12, 0, 0,
2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 12, 0, 0,
2, 1, 2, 1, 1, 1, 1, 2, 2, 2, 2, 1, 2, 12, 0, 0,
2, 1, 2, 1, 2, 2, 1, 1, 2, 2, 2, 1, 2, 12, 0, 0,
2, 1, 2, 1, 2, 2, 2, 1, 1, 1, 1, 1, 2, 12, 0, 0,
2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 12, 0, 0,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 12, 0, 0,
2684354572, 2684354572, 2684354572, 2684354572, 2684354572, 2684354572, 2684354572, 2684354572, 2684354572, 2684354572, 2684354572, 2684354572, 2684354572, 2684354572, 0, 0],
"data":[2, 1, 2, 2, 2, 2, 2, 2, 12, 0, 0, 0, 0, 0, 0, 0,
2, 1, 2, 1, 1, 1, 2, 2, 12, 0, 0, 0, 0, 0, 0, 0,
2, 1, 1, 1, 2, 1, 1, 2, 12, 0, 0, 0, 0, 0, 0, 0,
2, 2, 2, 2, 2, 2, 1, 2, 12, 0, 0, 0, 0, 0, 0, 0,
2, 2, 2, 2, 2, 2, 1, 2, 12, 0, 0, 0, 0, 0, 0, 0,
2, 1, 1, 1, 1, 1, 1, 2, 12, 0, 0, 0, 0, 0, 0, 0,
2, 1, 2, 2, 2, 2, 2, 2, 12, 0, 0, 0, 0, 0, 0, 0,
2, 1, 2, 1, 1, 1, 1, 2, 12, 0, 0, 0, 0, 0, 0, 0,
2, 1, 2, 1, 2, 2, 1, 2, 12, 0, 0, 0, 0, 0, 0, 0,
2, 1, 2, 1, 2, 2, 2, 2, 12, 0, 0, 0, 0, 0, 0, 0,
2, 1, 1, 1, 2, 2, 2, 2, 12, 0, 0, 0, 0, 0, 0, 0,
2, 2, 2, 2, 2, 2, 2, 2, 12, 0, 0, 0, 0, 0, 0, 0,
2684354572, 2684354572, 2684354572, 2684354572, 2684354572, 2684354572, 2684354572, 2684354572, 2684354572, 0, 0, 0, 0, 0, 0, 0],
"height":13,
"id":1,
"name":"main",
@ -28,16 +28,16 @@
},
{
"data":[0, 0, 16, 0, 16, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 0, 0,
0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0,
16, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 16, 16, 0, 16, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 16, 16, 0, 0, 0, 0,
0, 16, 16, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 16, 0, 0, 16, 0, 0, 0, 0, 16, 0, 16, 0, 0, 0,
16, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0,
0, 0, 16, 0, 16, 16, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0,
0, 0, 16, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
16, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 16, 0, 16, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 16, 0, 0, 0, 0,
0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"height":13,

BIN
assets/medium-enemies.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 KiB

BIN
assets/medium-enemies.psd Normal file

Binary file not shown.

BIN
assets/music/main-bg.mp3 Normal file

Binary file not shown.

View File

@ -13,34 +13,34 @@
</tileset>
<layer id="1" name="main" width="16" height="13">
<data encoding="csv">
2,1,2,2,2,2,2,2,2,2,2,2,2,12,0,0,
2,1,2,1,1,1,2,2,2,2,2,2,2,12,0,0,
2,1,1,1,2,1,1,2,2,1,1,1,2,12,0,0,
2,2,2,2,2,2,1,2,2,1,2,2,2,12,0,0,
2,2,2,2,2,2,1,2,2,1,2,2,2,12,0,0,
2,1,1,1,1,1,1,2,2,1,1,1,2,12,0,0,
2,1,2,2,2,2,2,2,2,2,2,1,2,12,0,0,
2,1,2,1,1,1,1,2,2,2,2,1,2,12,0,0,
2,1,2,1,2,2,1,1,2,2,2,1,2,12,0,0,
2,1,2,1,2,2,2,1,1,1,1,1,2,12,0,0,
2,1,1,1,2,2,2,2,2,2,2,2,2,12,0,0,
2,2,2,2,2,2,2,2,2,2,2,2,2,12,0,0,
2684354572,2684354572,2684354572,2684354572,2684354572,2684354572,2684354572,2684354572,2684354572,2684354572,2684354572,2684354572,2684354572,2684354572,0,0
2,1,2,2,2,2,2,2,12,0,0,0,0,0,0,0,
2,1,2,1,1,1,2,2,12,0,0,0,0,0,0,0,
2,1,1,1,2,1,1,2,12,0,0,0,0,0,0,0,
2,2,2,2,2,2,1,2,12,0,0,0,0,0,0,0,
2,2,2,2,2,2,1,2,12,0,0,0,0,0,0,0,
2,1,1,1,1,1,1,2,12,0,0,0,0,0,0,0,
2,1,2,2,2,2,2,2,12,0,0,0,0,0,0,0,
2,1,2,1,1,1,1,2,12,0,0,0,0,0,0,0,
2,1,2,1,2,2,1,2,12,0,0,0,0,0,0,0,
2,1,2,1,2,2,2,2,12,0,0,0,0,0,0,0,
2,1,1,1,2,2,2,2,12,0,0,0,0,0,0,0,
2,2,2,2,2,2,2,2,12,0,0,0,0,0,0,0,
2684354572,2684354572,2684354572,2684354572,2684354572,2684354572,2684354572,2684354572,2684354572,0,0,0,0,0,0,0
</data>
</layer>
<layer id="2" name="platforms" width="16" height="13">
<data encoding="csv">
0,0,16,0,16,0,16,0,0,0,0,0,0,0,0,0,
0,0,16,0,0,0,16,0,0,0,16,0,0,0,0,0,
0,0,16,0,0,0,16,0,0,0,0,0,0,0,0,0,
16,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,
0,16,16,0,16,0,0,0,16,0,0,0,0,0,0,0,
0,0,0,16,0,0,0,0,0,0,16,16,0,0,0,0,
0,16,16,0,16,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,16,0,0,0,0,0,0,0,0,
0,0,16,0,0,16,0,0,0,0,16,0,16,0,0,0,
16,0,0,0,0,0,0,0,0,16,0,0,0,0,0,0,
0,0,16,0,16,16,0,0,0,0,16,0,0,0,0,0,
0,0,16,0,0,16,0,0,0,0,0,0,0,0,0,0,
16,0,0,0,0,0,0,16,0,0,0,0,0,0,0,0,
0,0,16,0,16,16,0,0,0,0,0,0,0,0,0,0,
0,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,16,0,0,16,0,0,0,0,
0,0,0,0,0,16,0,0,0,0,0,0,0,0,0,0,
0,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
</data>

View File

@ -6,6 +6,9 @@ export class Level extends Phaser.Scene {
super({ key: 'Level' });
this.allowPan = true;
this.level = 1;
this.wave = 1;
this.gold = 150;
}
init(data) {
@ -23,6 +26,10 @@ export class Level extends Phaser.Scene {
frameWidth: 50,
frameHeight: 50
});
this.load.spritesheet('medium-enemies', 'assets/medium-enemies.png', {
frameWidth: 100,
frameHeight: 100
});
this.load.spritesheet('advanced-enemies', 'assets/advanced-enemies.png', {
frameWidth: 150,
frameHeight: 150
@ -62,7 +69,7 @@ export class Level extends Phaser.Scene {
this.cameras.main.setBounds(0, 0, this.mainLayer.width, this.mainLayer.height);
this.addControls();
this.waveManager = new WaveManager(this, 1, 1);
this.waveManager = new WaveManager(this, this.level, this.wave);
this.towerManager = new TowerManager(this);
this.scene.launch('UIScene');
@ -77,6 +84,9 @@ export class Level extends Phaser.Scene {
this.physics.add.collider(this.enemies, this.mainLayer);
this.physics.add.collider(this.enemies, this.platformsLayer);
this.physics.add.collider(this.waveManager.core, this.enemies, (core, enemy) => {
this.UIScene.interfaceManager.damageCore(enemy);
});
}
gridToLocation(num, offset = 0) {
@ -88,6 +98,10 @@ export class Level extends Phaser.Scene {
this.towerManager.update(time, delta);
}
gameOver() {
console.log('GAME OVER');
}
addControls() {
// this.input.on('wheel', (pointer, gameObjects, deltaX, deltaY) => {
// const zoomSpeed = 0.1;

View File

@ -53,11 +53,11 @@ export class Enemies {
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) {
if (this.type.indexOf('advanced') === 0 || this.type.indexOf('medium') === 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+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);
this.createAnim('die', ENEMIES_CONFIG[this.type].spriteStart+9, ENEMIES_CONFIG[this.type].spriteStart+9, 0);
}
// Generate unique ID for enemy

View File

@ -1,6 +1,6 @@
export const ENEMIES_CONFIG = {
'basic1': {
'spread': 35,
'spread': 55,
'health': 25,
'fullHealth': 25,
'speedLow': 25,
@ -11,7 +11,7 @@ export const ENEMIES_CONFIG = {
'dropHigh': 2
},
'basic2': {
'spread': 25,
'spread': 55,
'health': 100,
'fullHealth': 100,
'speedLow': 45,
@ -22,7 +22,7 @@ export const ENEMIES_CONFIG = {
'dropHigh': 10
},
'basic3': {
'spread': 25,
'spread': 55,
'health': 300,
'fullHealth': 300,
'speedLow': 20,
@ -32,6 +32,39 @@ export const ENEMIES_CONFIG = {
'dropLow': 8,
'dropHigh': 16
},
'basic4': {
'spread': 55,
'health': 200,
'fullHealth': 200,
'speedLow': 80,
'speedHigh': 120,
'spriteStart': 30,
'spriteSheet': 'basic-enemies',
'dropLow': 7,
'dropHigh': 14
},
'medium1': {
'spread': 50,
'health': 350,
'fullHealth': 350,
'speedLow': 45,
'speedHigh': 65,
'spriteStart': 0,
'spriteSheet': 'medium-enemies',
'dropLow': 9,
'dropHigh': 18
},
'medium2': {
'spread': 50,
'health': 300,
'fullHealth': 300,
'speedLow': 70,
'speedHigh': 110,
'spriteStart': 10,
'spriteSheet': 'medium-enemies',
'dropLow': 9,
'dropHigh': 18
},
'advanced1': {
'spread': 0,
'health': 1200,

View File

@ -4,7 +4,7 @@ export class InterfaceManager {
constructor(scene) {
this.scene = scene;
this.gold = 150;
this.gold = scene.levelScene.gold;
this.cores = 20;
this.interfaceOpen = false;
this.selectedTower = false;
@ -385,6 +385,7 @@ export class InterfaceManager {
const centerToEdgeLine = this.scene.add.line(0, 0, 0, 0, TOWERS_CONFIG[type].level1.range, 0, 0x00ff00)
.setOrigin(0)
.setScrollFactor(0)
.setDepth(6);
this.selectedTower.add(centerToEdgeLine);
@ -456,6 +457,86 @@ export class InterfaceManager {
this.selectedTower = false;
}
damageCore(enemy) {
if (this.cores <= 0) return;
const type = enemy.props.type;
let dmg = 0;
enemy.destroy();
if (type.indexOf('basic') === 0) {
dmg = 1;
}
if (type.indexOf('medium') === 0) {
dmg = 4;
}
if (type.indexOf('advanced') === 0) {
dmg = 8;
}
this.cores -= dmg;
if (this.cores <= 0) {
this.cores = 0;
this.scene.levelScene.gameOver();
}
this.coreText.setText(this.cores);
const randX = Phaser.Math.Between(50,100);
const dmgText = this.scene.add.text(this.coreText.x + randX, this.coreText.y +25, `-${dmg}`, {
fontFamily: 'neuropol, arial',
fontSize: '36px',
fill: '#ff00ddff',
stroke: '#8a2a52ff',
strokeThickness: 2,
shadow: {
offsetX: 5,
offsetY: 5,
color: '#000000',
blur: 5,
stroke: false,
fill: true
}
}).setScrollFactor(0);
this.scene.tweens.add({
targets: dmgText,
y: this.coreText.y - 25,
alpha: .3,
duration: 2000,
onComplete: () => {
dmgText.destroy();
}
});
const randCoreX = Phaser.Math.Between(-50,50);
const camera = this.scene.levelScene.cameras.main;
const posX = (this.scene.levelScene.waveManager.endX * 200) + 100 - camera.scrollX;
const posY = (this.scene.levelScene.waveManager.endY * 200) + 100 - camera.scrollY;
const dmgCoreText = this.scene.add.text(posX + randCoreX, posY +25, `-${dmg}`, {
fontFamily: 'neuropol, arial',
fontSize: '48px',
fill: '#ff00ddff',
stroke: '#8a2a52ff',
strokeThickness: 2,
shadow: {
offsetX: 5,
offsetY: 5,
color: '#000000',
blur: 5,
stroke: false,
fill: true
}
}).setScrollFactor(0);
this.scene.tweens.add({
targets: dmgCoreText,
y: posY - 25,
alpha: .3,
duration: 2000,
onComplete: () => {
dmgCoreText.destroy();
}
});
}
update(time, delta) {
if (this.selectedTower) {
// Snap to current mouse position

View File

@ -12,19 +12,11 @@ export class TowerManager {
this.upgradeCost = null;
this.towerAnims = new TowerAnims(scene);
// Add global click handler to close upgrade menu
this.scene.input.on('pointerdown', (pointer) => {
if (this.selectedTower && !this.isPointerOverTower(pointer)) {
this.closeUpgradeMenu();
}
});
}
createTower(type, x, y) {
const posX = this.scene.gridToLocation(x);
const posY = this.scene.gridToLocation(y);
const towerContainer = this.scene.add.container();
let towerBase;
let tower;
@ -46,9 +38,6 @@ export class TowerManager {
tower.towerBase = towerBase;
towerContainer.add(towerBase);
towerContainer.add(tower);
// Generate unique ID for enemy
const uniqueId = Phaser.Math.Between(100000, 999999);
tower.id = uniqueId;
@ -70,6 +59,8 @@ export class TowerManager {
}
upgradeMenu(tower) {
if (this.upgradeDetails) this.upgradeDetails.destroy();
this.selectedTower = tower;
const currentLevel = `level${tower.props.level}`;
const nextLevel = currentLevel === 'level3' ? 'level3' : `level${tower.props.level + 1}`;
@ -96,9 +87,7 @@ export class TowerManager {
const intUpgrade = this.scene.add.image(tower.x + 100, tower.y, 'intUpgrade')
.setDepth(12)
.setTint(0xAAAAAA)
.setOrigin(0, 0.5)
.setScale(0)
.setAlpha(0);
.setOrigin(0, 0.5);
this.upgradeDetails.add(intUpgrade);
this.upgradeCost = TOWERS_CONFIG[tower.props.type][nextLevel].cost;
@ -117,12 +106,14 @@ export class TowerManager {
stroke: false,
fill: true
}
}).setDepth(13).setAlpha(0).setInteractive();
}).setDepth(13).setInteractive();
this.upgradeDetails.add(this.upgradeText);
if (currentLevel === 'level3') this.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`, {
this.upgradeCancelText = this.scene.add.text(tower.x + 175, tower.y + 25, `Cancel`, {
fontFamily: 'neuropol, arial',
fontSize: '32px',
fill: '#ff0000ff',
@ -136,20 +127,13 @@ export class TowerManager {
stroke: false,
fill: true
}
}).setDepth(13).setAlpha(0).setInteractive();
this.upgradeDetails.add(upgradeCancelText);
}).setDepth(14).setInteractive();
this.upgradeDetails.add(this.upgradeCancelText);
upgradeCancelText.on('pointerdown', () => {
this.upgradeCancelText.on('pointerdown', () => {
this.closeUpgradeMenu();
});
this.scene.tweens.add({
targets:[intUpgrade, this.upgradeText, upgradeCancelText],
scale: 1,
alpha: 1,
duration: 500
});
this.upgradeText.on('pointerdown', () => {
this.upgradeTower(tower);
});
@ -313,7 +297,7 @@ export class TowerManager {
laserBeam.lineTo(enemy.x, enemy.y);
laserBeam.strokePath();
this.scene.sound.play('laser');
this.scene.sound.play('laser', { volume: .3 });
// Fade out effect over duration
this.scene.tweens.add({

View File

@ -4,8 +4,8 @@ export const WAVE_CONFIG = {
//Spawn Point
spawnX: 1,
spawnY: 0,
endX: 11,
endY: 2,
endX: 6,
endY: 8,
// Wave
1: {
// Schedule
@ -126,6 +126,54 @@ export const WAVE_CONFIG = {
begin: 35,
advanced2: 1
}
},
//Wave
7: {
//Schedule
1: {
begin: 0,
medium1: 5,
basic3: 5
},
2: {
begin: 15,
basic2: 10,
basic4: 2,
advanced1: 1
},
3: {
begin: 25,
medium1: 6,
medium2: 2
},
4: {
begin: 35,
advanced2: 1
}
},
//Wave
8: {
//Schedule
1: {
begin: 0,
advanced2: 1,
basic4: 20
},
2: {
begin: 25,
basic2: 10,
medium2: 6,
},
3: {
begin: 45,
medium2: 5,
basic4: 15
},
4: {
begin: 35,
advanced1: 1,
basic3: 10
}
}
}
}

View File

@ -39,7 +39,8 @@ export class WaveManager {
}
placeCore() {
this.scene.add.sprite(this.scene.gridToLocation(this.endX), this.scene.gridToLocation(this.endY), 'towers', 8);
this.core = this.scene.physics.add.sprite(this.scene.gridToLocation(this.endX), this.scene.gridToLocation(this.endY), 'towers', 8);
this.core.setImmovable(true);
const rotatingCore = this.scene.add.sprite(this.scene.gridToLocation(this.endX), this.scene.gridToLocation(this.endY), 'towers', 9);
const glowingCore = this.scene.add.sprite(this.scene.gridToLocation(this.endX), this.scene.gridToLocation(this.endY), 'towers', 19).setAlpha(0);
this.scene.tweens.add({
@ -180,6 +181,12 @@ export class WaveManager {
const enemy = new Enemies(this.scene, type, this.spawnX, this.spawnY, this.path);
}
}
if (this.scheduleInfo.hasOwnProperty(`medium${i}`)) {
let type = `medium${i}`;
for (let e = 0; e < this.scheduleInfo[type]; e++) {
const enemy = new Enemies(this.scene, type, this.spawnX, this.spawnY, this.path);
}
}
if (this.scheduleInfo.hasOwnProperty(`advanced${i}`)) {
let type = `advanced${i}`;
for (let e = 0; e < this.scheduleInfo[type]; e++) {

View File

@ -24,8 +24,8 @@ export class UIScene extends Phaser.Scene {
}
create() {
this.interfaceManager = new InterfaceManager(this);
this.levelScene = this.scene.get('Level');
this.interfaceManager = new InterfaceManager(this);
this.cameras.main.setBounds(0, 0, 1600, 900);
}