using System.Collections.Generic; using UnityEngine; namespace MazeGen { /// /// Represents a single monster encounter in a room: which monsters are present, /// their health state, and visibility to the agent (for the peek mechanic). /// public class MonsterEncounter { public class MonsterInstance { public Monster Template; public CharacterSheet CharacterSheet; public bool IsDefeated => !CharacterSheet.Stats.IsAlive; public MonsterInstance(Monster template) { Template = template; CharacterSheet = template.CreateCharacterSheet(); } } private List _monsters = new List(); private int _visibleMonsterCount; // Partial info: agent sees this many, not the full count public IReadOnlyList Monsters => _monsters.AsReadOnly(); public int AliveMonsterCount { get { int count = 0; foreach (var m in _monsters) if (!m.IsDefeated) count++; return count; } } /// /// How many monsters the agent can actually see in the room. /// This is less than or equal to AliveMonsterCount (incomplete information). /// public int VisibleMonsterCount => _visibleMonsterCount; public bool HasLivingMonsters => AliveMonsterCount > 0; /// Spawn monsters for this encounter based on a template. public void SpawnMonsters(Monster[] monsterTemplates, int minCount, int maxCount) { _monsters.Clear(); int count = Random.Range(minCount, maxCount + 1); for (int i = 0; i < count; i++) { Monster template = monsterTemplates[Random.Range(0, monsterTemplates.Length)]; _monsters.Add(new MonsterInstance(template)); } // Agent sees 60-100% of the actual monsters (incomplete info) _visibleMonsterCount = Mathf.RoundToInt(AliveMonsterCount * Random.Range(0.6f, 1.0f)); _visibleMonsterCount = Mathf.Max(1, _visibleMonsterCount); // At least see 1 if any exist } /// Mark a monster as defeated (called after combat resolution). public void DefeateMonster(int index) { if (index >= 0 && index < _monsters.Count) { _monsters[index].CharacterSheet.Stats.CurrentHealth = 0; } } /// /// Get the perceived challenge level from the agent's perspective. /// This is based on visible monsters only, not actual count. /// public float GetPerceivedChallengeRating(CharacterSheet agentStats) { if (_visibleMonsterCount == 0) return 0; // Simple calculation: sum the "threat" of visible monsters float totalThreat = 0; int counted = 0; foreach (var monster in _monsters) { if (monster.IsDefeated) continue; if (counted >= _visibleMonsterCount) break; float monsterThreat = monster.CharacterSheet.GetEffectiveAttack() + monster.CharacterSheet.Stats.CurrentHealth * 0.1f; totalThreat += monsterThreat; counted++; } float agentPower = agentStats.GetEffectiveAttack() + agentStats.GetEffectiveDefense(); return agentPower > 0 ? totalThreat / agentPower : totalThreat; } /// /// Get the actual challenge rating (true difficulty, not visible to agent). /// This is what combat system would use internally. /// public float GetActualChallengeRating(CharacterSheet agentStats) { if (_monsters.Count == 0) return 0; float totalThreat = 0; foreach (var monster in _monsters) { if (monster.IsDefeated) continue; float monsterThreat = monster.CharacterSheet.GetEffectiveAttack() + monster.CharacterSheet.Stats.CurrentHealth * 0.1f; totalThreat += monsterThreat; } float agentPower = agentStats.GetEffectiveAttack() + agentStats.GetEffectiveDefense(); return agentPower > 0 ? totalThreat / agentPower : totalThreat; } /// Reset all monsters' health for a retry. public void ResetEncounter() { foreach (var monster in _monsters) { monster.CharacterSheet.ResetForNewFight(); } } } }