Spaces:
Running
Running
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; | |
} | |
} | |