namespace MazeGen { /// /// Handles arrival at a room where a fight is already forming. /// Agent can join (raising odds) or try to sneak past, with failed sneak forcing entry. /// public class HandleExistingFightDecision : AgentDecision { private CombatDirector _director; private float _retreatThreshold; public override string DecisionName => "Handle Existing Fight"; public HandleExistingFightDecision(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; // Already committed Room room = agent.CurrentRoom; if (room == null) return false; // Is there a fight already mustering here that we're not in? PendingFight mustering = _director.FindMusteringFight(room); return mustering != null && !mustering.Contains(agent); } public override bool Execute(MazeAgent agent) { Room room = agent.CurrentRoom; PendingFight fight = _director.FindMusteringFight(room); if (fight == null) return false; float threat = fight.Encounter.GetPerceivedChallengeRating(agent.CharacterSheet); float effectiveThreshold = GetEffectiveReteatThreshold(agent); float sharedThreat = threat / (fight.Agents.Count + 1); bool wantsToSneak = sharedThreat > effectiveThreshold || agent.HasFledFrom(fight.Room.Id); if (!wantsToSneak) { // Join the fight var result = _director.ResolveArrival(agent, false); return result == CombatDirector.ArrivalDecision.Joined; } else { // Try to sneak, which might fail and force a join var result = _director.ResolveArrival(agent, true); return result == CombatDirector.ArrivalDecision.Joined || result == CombatDirector.ArrivalDecision.SneakFailedAndJoined; } } public override void Think(MazeAgent agent) { Room room = agent.CurrentRoom; PendingFight fight = _director.FindMusteringFight(room); if (fight == null) return; float threat = fight.Encounter.GetPerceivedChallengeRating(agent.CharacterSheet); float effectiveThreshold = GetEffectiveReteatThreshold(agent); float sharedThreat = threat / (fight.Agents.Count + 1); bool wantsToSneak = sharedThreat > effectiveThreshold || agent.HasFledFrom(fight.Room.Id); if (!wantsToSneak) agent.Thoughts.Add(ThoughtKind.Fight, $"Walked into fight #{fight.Id} - {fight.Agents.Count} ally(s) already in, shared threat {sharedThreat:0.00}. Joining."); else agent.Thoughts.Add(ThoughtKind.Sneak, $"Walked into fight #{fight.Id} - shared threat {sharedThreat:0.00} still over my limit. Trying to sneak past it."); } 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; } } }