namespace MazeGen { /// /// When the threat is too high to fight, tries to sneak out of the room. /// If the sneak fails, declares a forced fight instead. /// public class SneakAwayDecision : AgentDecision { private CombatDirector _director; private float _retreatThreshold; public override string DecisionName => "Sneak Away"; public SneakAwayDecision(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 reject this threat level? float threat = room.Encounter.GetPerceivedChallengeRating(agent.CharacterSheet); float effectiveThreshold = GetEffectiveReteatThreshold(agent); return threat > effectiveThreshold; } public override bool Execute(MazeAgent agent) { // Try to sneak if (_director.TrySneakOut(agent, out float odds)) { // Successfully sneaked away return false; // Return false so MoveOn can proceed this same tick } else { // Sneak failed - now forced to fight Room room = agent.CurrentRoom; int hostiles = room.Encounter.AliveMonsterCount; _director.DeclareFight(room, agent, $"was caught trying to sneak out past {hostiles} hostile(s)"); return true; // Consumed the turn } } 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.Avoid, $"Peeked: {hostiles} hostile(s) at threat {threat:0.00}, over my limit {effectiveThreshold:0.00} - trying to slip out instead of fighting."); if (_director.TrySneakOut(agent, out float odds)) agent.Thoughts.Add(ThoughtKind.Sneak, $"Slipped away unnoticed ({odds:P0} odds)."); else agent.Thoughts.Add(ThoughtKind.Sneak, $"Botched the sneak ({odds:P0} odds) - they've seen me, no choice but to fight."); } 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; } } }