namespace MazeGen { /// /// Declares a fight with monsters in the current room if the threat level is acceptable. /// Uses a health-aware retreat threshold: lower health = more defensive. /// public class DeclareFightDecision : AgentDecision { private CombatDirector _director; private float _retreatThreshold; public override string DecisionName => "Declare Fight"; public DeclareFightDecision(CombatDirector director, float retreatThreshold) { _director = director; _retreatThreshold = retreatThreshold; } public override bool CanExecute(MazeAgent agent) { if (agent == null || _director == null) return false; if (agent.IsEngaged) return false; Room room = agent.CurrentRoom; if (room == null || room.Encounter == null || !room.Encounter.HasLivingMonsters) return false; // Already failed here before if (agent.HasFledFrom(room.Id)) return false; // Would we accept this threat level? float threat = room.Encounter.GetPerceivedChallengeRating(agent.CharacterSheet); float effectiveThreshold = GetEffectiveReteatThreshold(agent); return threat <= effectiveThreshold; } public override bool Execute(MazeAgent agent) { Room room = agent.CurrentRoom; float threat = room.Encounter.GetPerceivedChallengeRating(agent.CharacterSheet); int hostiles = room.Encounter.AliveMonsterCount; _director.DeclareFight(room, agent, $"sized up {hostiles} hostile(s) at threat {threat:0.00} and attacked"); return true; } public override void Think(MazeAgent agent) { Room room = agent.CurrentRoom; float threat = room.Encounter.GetPerceivedChallengeRating(agent.CharacterSheet); float effectiveThreshold = GetEffectiveReteatThreshold(agent); int hostiles = room.Encounter.AliveMonsterCount; agent.Thoughts.Add(ThoughtKind.Fight, $"Peeked: {hostiles} hostile(s), threat {threat:0.00} vs my limit {effectiveThreshold:0.00} - engaging. Fight starts in {_director.JoinWindowSeconds:0.0}s, hopefully someone joins."); } private float GetEffectiveReteatThreshold(MazeAgent agent) { CharacterSheet sheet = agent.CharacterSheet; float maxHealth = sheet.Stats.MaxHealth; if (maxHealth <= 0) return _retreatThreshold; float healthPercent = sheet.Stats.CurrentHealth / maxHealth; if (healthPercent < 0.3f) return _retreatThreshold * 0.5f; else if (healthPercent < 0.5f) { float t = (healthPercent - 0.3f) / 0.2f; return UnityEngine.Mathf.Lerp(_retreatThreshold * 0.5f, _retreatThreshold, t); } return _retreatThreshold; } } }