import { EncounterResult, MissionDifficulty, MissionState, ResolveEncounterInput, SurvivorStats } from '@fog-explorer/api-interfaces'; import { applyPerkModifiers, clamp } from './perk-math'; const DIFFICULTY_BASE: Record = { [MissionDifficulty.Easy]: 0.72, [MissionDifficulty.Normal]: 0.6, [MissionDifficulty.Hard]: 0.48, [MissionDifficulty.Nightmare]: 0.38 }; export function resolveEncounter(input: ResolveEncounterInput): EncounterResult { const baseChance = DIFFICULTY_BASE[input.difficulty]; const perkBoost = input.perkIds.length * 0.015; const statBoost = input.stats.stealth * 0.002 + input.stats.teamwork * 0.002 + input.stats.luck * 0.002; const successChance = clamp( applyPerkModifiers(baseChance + perkBoost + statBoost, []), 0.05, 0.95 ); const roll = seededRoll(input.seed ?? input.tickIndex); if (roll <= successChance) { return { outcome: 'success', text: 'The survivor outplayed the fog and advanced.', successChance, roll, nextState: MissionState.InProgress, nextStats: { ...input.stats, health: Math.min(100, input.stats.health + 1) } }; } const injuryThreshold = successChance + 0.25; if (roll <= injuryThreshold) { const injured = applyDamage(input.stats, 14); return { outcome: 'injury', text: 'A close call leaves the survivor injured.', successChance, roll, nextState: injured.health <= 0 ? MissionState.Failed : MissionState.InProgress, nextStats: injured }; } return { outcome: 'sacrifice', text: 'The fog claims another soul.', successChance, roll, nextState: MissionState.Failed, nextStats: { ...input.stats, health: 0 } }; } function applyDamage(stats: SurvivorStats, amount: number): SurvivorStats { return { ...stats, health: Math.max(0, stats.health - amount) }; } function seededRoll(seed: number): number { const normalized = Math.abs(Math.sin(seed * 99991)) * 10000; return normalized - Math.floor(normalized); }