| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155 |
- using System.Collections.Generic;
- using UnityEngine;
- namespace MazeGen
- {
- /// <summary>
- /// Represents a single monster encounter in a room: which monsters are present,
- /// their health state, and visibility to the agent (for the peek mechanic).
- /// </summary>
- public class MonsterEncounter
- {
- public class MonsterInstance
- {
- public Monster Template;
- public CharacterSheet CharacterSheet;
- public bool IsDefeated => !CharacterSheet.Stats.IsAlive;
- private List<Item> _remainingLoot; // Track which loot items haven't been claimed yet
- public MonsterInstance(Monster template)
- {
- Template = template;
- CharacterSheet = template.CreateCharacterSheet();
- // Generate the loot pool based on the template's loot table and min/max counts
- _remainingLoot = template.GenerateLootPool();
- }
- /// <summary>
- /// Get a random loot item from this monster instance's remaining loot pool.
- /// The selected item is removed so it won't be looted again.
- /// Returns null if no loot remains.
- /// </summary>
- public Item GetRandomLoot()
- {
- if (_remainingLoot == null || _remainingLoot.Count == 0)
- return null;
- int lootIndex = Random.Range(0, _remainingLoot.Count);
- Item loot = _remainingLoot[lootIndex];
- _remainingLoot.RemoveAt(lootIndex); // Remove so it can't be looted again
- return loot;
- }
- }
- private List<MonsterInstance> _monsters = new List<MonsterInstance>();
- private int _visibleMonsterCount; // Partial info: agent sees this many, not the full count
- public IReadOnlyList<MonsterInstance> Monsters => _monsters.AsReadOnly();
- public int AliveMonsterCount
- {
- get
- {
- int count = 0;
- foreach (var m in _monsters)
- if (!m.IsDefeated) count++;
- return count;
- }
- }
- /// <summary>
- /// How many monsters the agent can actually see in the room.
- /// This is less than or equal to AliveMonsterCount (incomplete information).
- /// </summary>
- public int VisibleMonsterCount => _visibleMonsterCount;
- public bool HasLivingMonsters => AliveMonsterCount > 0;
- /// <summary>Spawn monsters for this encounter based on a template.</summary>
- 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
- }
- /// <summary>Mark a monster as defeated (called after combat resolution).</summary>
- public void DefeateMonster(int index)
- {
- if (index >= 0 && index < _monsters.Count)
- {
- _monsters[index].CharacterSheet.Stats.CurrentHealth = 0;
- }
- }
- /// <summary>
- /// Get the perceived challenge level from the agent's perspective.
- /// This is based on visible monsters only, not actual count.
- /// </summary>
- 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;
- }
- /// <summary>
- /// Get the actual challenge rating (true difficulty, not visible to agent).
- /// This is what combat system would use internally.
- /// </summary>
- 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;
- }
- /// <summary>Reset all monsters' health for a retry.</summary>
- public void ResetEncounter()
- {
- foreach (var monster in _monsters)
- {
- monster.CharacterSheet.ResetForNewFight();
- }
- }
- }
- }
|