import type { DifficultyDef, DifficultyId, EnemyDef, EnemyKind, TowerDef, TowerKind, WaveGroup } from './types' export const TILE = 48 export const COLS = 20 export const ROWS = 11 export const W = COLS * TILE // 960 export const H = ROWS * TILE // 528 export const TOTAL_WAVES = 20 export const INTERMISSION = 20 // seconds between waves export const SELL_RATIO = 0.7 /** cost to remove blocking obstacles */ export const OBSTACLE_COST: Record<'tree' | 'rock', number> = { tree: 60, rock: 40, } /** ground path in tile coords; -1/20 are outside the grid (spawn/exit) */ export const MAP_WAYPOINTS: [number, number][] = [ [-1, 5], [2, 5], [2, 1], [6, 1], [6, 9], [10, 9], [10, 1], [14, 1], [14, 9], [17, 9], [17, 5], [20, 5], ] /** flying enemies fly straight across the middle */ export const FLY_WAYPOINTS: [number, number][] = [ [-1, 5], [20, 5], ] export const TOWER_ORDER: TowerKind[] = ['arrow', 'cannon', 'frost', 'tesla', 'laser'] export const TOWERS: Record = { arrow: { kind: 'arrow', name: 'Bogenturm', icon: '🏹', desc: 'Günstiger Standardturm mit schnellen Einzelschüssen.', cost: 50, upgradeCost: [60, 110], levels: [ { damage: 12, range: 110, rate: 1.7 }, { damage: 22, range: 120, rate: 2.0 }, { damage: 38, range: 132, rate: 2.4 }, ], hitsFlying: true, projectileSpeed: 460, color: '#9a6a33', accent: '#e8c27a', hotkey: '1', }, cannon: { kind: 'cannon', name: 'Kanone', icon: '🧨', desc: 'Flächenschaden durch Explosion – trifft aber keine Flieger.', cost: 110, upgradeCost: [100, 180], levels: [ { damage: 28, range: 118, rate: 0.55 }, { damage: 50, range: 128, rate: 0.62 }, { damage: 92, range: 140, rate: 0.7 }, ], hitsFlying: false, projectileSpeed: 300, splash: [42, 48, 56], color: '#57616e', accent: '#ff9c40', hotkey: '2', }, frost: { kind: 'frost', name: 'Eisturm', icon: '❄️', desc: 'Frostpuls verlangsamt ALLE Gegner in Reichweite (auch Flieger).', cost: 80, upgradeCost: [70, 130], levels: [ { damage: 5, range: 95, rate: 0.9 }, { damage: 9, range: 105, rate: 1.0 }, { damage: 15, range: 118, rate: 1.1 }, ], hitsFlying: true, slowFactor: [0.55, 0.45, 0.35], slowDuration: [1.5, 1.9, 2.3], color: '#4d8fb5', accent: '#aef0ff', hotkey: '3', }, tesla: { kind: 'tesla', name: 'Teslaturm', icon: '⚡', desc: 'Kettenblitz springt auf mehrere Gegner über.', cost: 130, upgradeCost: [120, 200], levels: [ { damage: 22, range: 105, rate: 1.0 }, { damage: 38, range: 115, rate: 1.1 }, { damage: 66, range: 128, rate: 1.2 }, ], hitsFlying: true, chain: [3, 4, 5], chainRange: 100, color: '#6a5fb0', accent: '#c8b4ff', hotkey: '4', }, laser: { kind: 'laser', name: 'Laserturm', icon: '💫', desc: 'Soforttreffer mit hoher Reichweite und hohem Schaden.', cost: 160, upgradeCost: [150, 260], levels: [ { damage: 60, range: 170, rate: 0.5 }, { damage: 105, range: 185, rate: 0.55 }, { damage: 185, range: 205, rate: 0.6 }, ], hitsFlying: true, color: '#b0483f', accent: '#ff8a7a', hotkey: '5', }, } export const ENEMIES: Record = { normal: { kind: 'normal', name: 'Kriecher', hp: 55, speed: 55, reward: 8, radius: 11, dmg: 1, flying: false, color: '#58c14b', }, fast: { kind: 'fast', name: 'Läufer', hp: 34, speed: 105, reward: 9, radius: 9, dmg: 1, flying: false, color: '#ffd23e', }, tank: { kind: 'tank', name: 'Panzer', hp: 210, speed: 34, reward: 20, radius: 14, dmg: 2, flying: false, color: '#7d8894', }, flyer: { kind: 'flyer', name: 'Flieger', hp: 48, speed: 72, reward: 10, radius: 10, dmg: 1, flying: true, color: '#b06ee8', }, boss: { kind: 'boss', name: 'Boss', hp: 1500, speed: 42, reward: 130, radius: 20, dmg: 5, flying: false, color: '#d84b3f', }, } /** hp multiplier for wave n */ export function waveHpScale(n: number): number { const m = n - 1 return 1 + 0.18 * m + 0.02 * m * m } /** speed multiplier for wave n */ export function waveSpeedScale(n: number): number { return Math.min(1.32, 1 + 0.008 * (n - 1)) } /** reward multiplier for wave n */ export function waveRewardScale(n: number): number { return 1 + 0.04 * (n - 1) } export function waveClearBonus(n: number): number { return 35 + 8 * n } export function earlyCallBonus(remainingSeconds: number): number { return Math.round(remainingSeconds * 2.5) } const G = (kind: EnemyKind, count: number, interval: number): WaveGroup => ({ kind, count, interval }) /** hand-tuned waves 1..20 */ export const WAVES: WaveGroup[][] = [ /* 1 */ [G('normal', 8, 1.1)], /* 2 */ [G('normal', 12, 0.9)], /* 3 */ [G('normal', 8, 0.9), G('fast', 5, 0.7)], /* 4 */ [G('fast', 10, 0.55), G('normal', 8, 0.8)], /* 5 */ [G('normal', 10, 0.75), G('tank', 3, 2.2)], /* 6 */ [G('fast', 12, 0.5), G('tank', 4, 2.0)], /* 7 */ [G('flyer', 8, 0.9), G('normal', 10, 0.7)], /* 8 */ [G('fast', 10, 0.5), G('flyer', 10, 0.7)], /* 9 */ [G('tank', 6, 1.6), G('fast', 12, 0.45), G('normal', 10, 0.6)], /* 10 */ [G('boss', 1, 1), G('normal', 12, 0.8)], /* 11 */ [G('flyer', 14, 0.55), G('fast', 12, 0.45)], /* 12 */ [G('tank', 8, 1.3), G('normal', 16, 0.5)], /* 13 */ [G('fast', 20, 0.35), G('flyer', 12, 0.5)], /* 14 */ [G('tank', 10, 1.1), G('fast', 16, 0.4)], /* 15 */ [G('boss', 2, 8), G('flyer', 10, 0.5)], /* 16 */ [G('normal', 24, 0.35), G('tank', 8, 1.0)], /* 17 */ [G('fast', 24, 0.3), G('flyer', 16, 0.45)], /* 18 */ [G('tank', 12, 0.9), G('normal', 20, 0.4)], /* 19 */ [G('fast', 20, 0.3), G('flyer', 20, 0.4), G('tank', 8, 0.9)], /* 20 */ [G('tank', 8, 1.0), G('boss', 2, 8), G('fast', 16, 0.35)], ] /** endless mode waves after 20 */ export function endlessWave(n: number): WaveGroup[] { const m = n - 20 const groups: WaveGroup[] = [] if (n % 5 === 0) groups.push(G('boss', 1 + Math.floor(m / 5), 8)) groups.push(G('tank', 8 + m * 2, Math.max(0.5, 1.1 - m * 0.02))) groups.push(G('fast', 18 + m * 3, Math.max(0.15, 0.32 - m * 0.01))) groups.push(G('flyer', 14 + m * 2, Math.max(0.25, 0.45 - m * 0.01))) groups.push(G('normal', 20 + m * 4, Math.max(0.18, 0.35 - m * 0.01))) return groups } export function getWave(n: number): WaveGroup[] { if (n <= WAVES.length) return WAVES[n - 1] return endlessWave(n) } export const DIFFICULTIES: Record = { easy: { id: 'easy', name: 'Leicht', desc: '30 Leben, mehr Startgold, schwächere Gegner', lives: 30, money: 320, hpMul: 0.85, }, normal: { id: 'normal', name: 'Normal', desc: '20 Leben, ausgewogenes Balancing', lives: 20, money: 260, hpMul: 1, }, hard: { id: 'hard', name: 'Schwer', desc: '12 Leben, weniger Gold, zähere Gegner', lives: 12, money: 220, hpMul: 1.15, }, } export const ENEMY_COLORS: Record = { normal: '#58c14b', fast: '#ffd23e', tank: '#7d8894', flyer: '#b06ee8', boss: '#d84b3f', } export const ENEMY_ICONS: Record = { normal: '🟢', fast: '🟡', tank: '⬜', flyer: '🟣', boss: '🔴', }