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();
}
}
}
}