diff --git a/web/src/Game.ts b/web/src/Game.ts index 5a234f2..39ca611 100644 --- a/web/src/Game.ts +++ b/web/src/Game.ts @@ -8,7 +8,7 @@ import { Bat } from './Bat'; import { BeeSwarm } from './BeeSwarm'; import { Slime } from './Slime'; import { Brute } from './Brute'; -import { Boss } from './Boss'; +import { Golem } from './Golem'; import { Crab } from './Crab'; import { Turret } from './Turret'; import { EnemyProjectile } from './EnemyProjectile'; @@ -74,6 +74,10 @@ export class Game { spawns = 0; killed = 0; + // Boss-Wellen: Welle -> Boss-Factory (aktuell nur Welle 10; 20/30 folgen) + private bossWaves = new Map Enemy>([[10, () => new Golem()]]); + private bossFightActive = false; + // Debug hitzone visualization private showHitzones = false; private playerHitRing!: THREE.Mesh; @@ -638,6 +642,7 @@ export class Game { this.spawnTimer = 0; this.spawns = 0; this.killed = 0; + this.bossFightActive = false; this.pendingPicks = 0; this.hideLevelUp(); this.skillSystem = new SkillSystem(this.seed ?? 0); @@ -831,6 +836,19 @@ export class Game { return w; } + private isBossWave(wave: number): boolean { + return this.bossWaves.has(wave); + } + + private spawnBossForWave(wave: number) { + const factory = this.bossWaves.get(wave); + if (!factory) return; + const x = (Math.random() - 0.5) * 80; + const z = (Math.random() - 0.5) * 80; + this.spawnEnemyInstance(factory(), x, z, 0.15); + playSound('spawn', 0.8); + } + spawnEnemyInstance(enemy: Enemy, x: number, z: number, y: number) { enemy.body.position.set(x, y, z); this.scene.add(enemy.body); @@ -1054,9 +1072,17 @@ export class Game { const gasPct = Math.max(0, (gasTotal - this.trapper.cooldown2) / gasTotal); this.uiGasCD.style.width = `${gasPct * 100}%`; - const wavePct = this.spawnTimer / SPAWN_TIME; - this.uiWaveBarFill.style.width = `${wavePct * 100}%`; - this.uiWaveBarLabel.textContent = `Next Wave: ${Math.ceil(SPAWN_TIME - this.spawnTimer)}s`; + const wavePct = Math.min(1, this.spawnTimer / SPAWN_TIME); + if (this.bossFightActive) { + this.uiWaveBarFill.style.width = '100%'; + this.uiWaveBarLabel.textContent = 'BOSS!'; + } else if (this.spawnTimer >= SPAWN_TIME && this.isBossWave(this.spawns + 1)) { + this.uiWaveBarFill.style.width = '100%'; + this.uiWaveBarLabel.textContent = 'Gegner räumen!'; + } else { + this.uiWaveBarFill.style.width = `${wavePct * 100}%`; + this.uiWaveBarLabel.textContent = `Next Wave: ${Math.ceil(SPAWN_TIME - this.spawnTimer)}s`; + } if (this.player.health <= 0 && this.state === 'playing') { this.gameOver(); @@ -1185,20 +1211,32 @@ export class Game { // Spawn enemies in waves if (this.state === 'playing') { - this.spawnTimer += dt; - if (this.spawnTimer >= SPAWN_TIME) { - this.spawns++; - if (this.spawns % 5 === 0) { - const x = (Math.random() - 0.5) * 80; - const z = (Math.random() - 0.5) * 80; - this.spawnEnemyInstance(new Boss(), x, z, 0.15); - playSound('spawn', 0.8); + const arenaCleared = this.enemies.every(e => e.dead); + if (this.bossFightActive) { + // Boss besiegt -> naechste Welle startet + if (arenaCleared) { + this.bossFightActive = false; + this.spawns++; + this.spawnTimer = 0; + } + } else if (this.spawnTimer >= SPAWN_TIME) { + if (this.isBossWave(this.spawns + 1)) { + // Boss-Welle startet erst, wenn alle Gegner tot sind + if (arenaCleared) { + this.spawns++; + this.spawnBossForWave(this.spawns); + this.bossFightActive = true; + this.spawnTimer = 0; + } } else { + this.spawns++; for (let i = 0; i < this.spawns; i++) { this.spawnEnemy(); } + this.spawnTimer = 0; } - this.spawnTimer = 0; + } else { + this.spawnTimer += dt; } } diff --git a/web/src/Boss.ts b/web/src/Golem.ts similarity index 95% rename from web/src/Boss.ts rename to web/src/Golem.ts index 684b3c4..c36b1ec 100644 --- a/web/src/Boss.ts +++ b/web/src/Golem.ts @@ -2,7 +2,7 @@ import * as THREE from 'three'; import type { Game } from './Game'; import { Brute } from './Brute'; -export class Boss extends Brute { +export class Golem extends Brute { constructor() { super(); this.health = 800;