import * as THREE from 'three'; import { CFG } from './config.js'; import { G } from './globals.js'; import { getTerrainHeight } from './world.js'; import { showWaveBanner } from './hud.js'; import { spawnEnemy } from './enemies.js'; import { spawnAccelerators, spawnInfiniteAmmo } from './pickups.js'; export function startNextWave() { const waveCount = Math.min( CFG.waves.baseCount + CFG.waves.perWaveAdd * (G.waves.current - 1), CFG.waves.maxAlive ); // Add two wolves on top of base wave enemies G.waves.spawnQueue = waveCount + 2; G.waves.nextSpawnTimer = 0; G.waves.shamansToSpawn = 1; // exactly 1 shaman per wave G.waves.wolvesToSpawn = 2; // exactly 2 wolves per wave // Golem spawns on wave 3 and every 3 waves thereafter (3,6,9,...) const wave = G.waves.current; G.waves.golemsToSpawn = (wave >= 3 && ((wave - 3) % 3 === 0)) ? 1 : 0; // Choose a single spawn anchor for this wave (not near the center) const half = CFG.forestSize / 2; const margin = 24; const minR = Math.max(CFG.waves.annulusMin, 40); const maxR = Math.min(CFG.waves.annulusMax * 2.2, half - margin); const angle = G.random() * Math.PI * 2; const r = minR + G.random() * (maxR - minR); const ax = Math.cos(angle) * r; const az = Math.sin(angle) * r; G.waves.spawnAnchor = new THREE.Vector3(ax, getTerrainHeight(ax, az), az); showWaveBanner(`Wave ${G.waves.current}`); // Add 2 grenades each wave start (stacking) G.grenadeCount = (G.grenadeCount || 0) + 2; // Spawn 0..2 accelerator powerups at random locations const accelCount = Math.floor(G.random() * 3); // 0,1,2 if (accelCount > 0) spawnAccelerators(accelCount); // Spawn at least 1 infinite ammo on wave 1 for visibility, then occasionally const infCount = (G.waves.current === 1) ? 1 : Math.floor(G.random() * 2); if (infCount > 0) spawnInfiniteAmmo(infCount); } export function updateWaves(delta) { if (G.waves.inBreak) { G.waves.breakTimer -= delta; if (G.waves.breakTimer <= 0) { G.waves.inBreak = false; G.waves.current++; startNextWave(); } return; } // Spawn enemies if (G.waves.spawnQueue > 0 && G.waves.aliveCount < CFG.waves.maxAlive) { G.waves.nextSpawnTimer -= delta; if (G.waves.nextSpawnTimer <= 0) { if (G.waves.golemsToSpawn > 0) { spawnEnemy('golem'); G.waves.golemsToSpawn--; } else if (G.waves.shamansToSpawn > 0) { spawnEnemy('shaman'); G.waves.shamansToSpawn--; } else if (G.waves.wolvesToSpawn > 0) { spawnEnemy('wolf'); G.waves.wolvesToSpawn--; } else { spawnEnemy('orc'); } G.waves.spawnQueue--; const spawnRate = Math.max( CFG.waves.spawnMin, CFG.waves.spawnEvery - CFG.waves.spawnDecay * (G.waves.current - 1) ); G.waves.nextSpawnTimer = spawnRate; } } // Check wave complete if (G.waves.spawnQueue === 0 && G.waves.aliveCount === 0) { G.waves.inBreak = true; G.waves.breakTimer = CFG.waves.breakTime; } }