HandleExistingFightDecision.cs 3.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. namespace MazeGen
  2. {
  3. /// <summary>
  4. /// Handles arrival at a room where a fight is already forming.
  5. /// Agent can join (raising odds) or try to sneak past, with failed sneak forcing entry.
  6. /// </summary>
  7. public class HandleExistingFightDecision : AgentDecision
  8. {
  9. private CombatDirector _director;
  10. private float _retreatThreshold;
  11. public override string DecisionName => "Handle Existing Fight";
  12. public HandleExistingFightDecision(CombatDirector director, float retreatThreshold)
  13. {
  14. _director = director;
  15. _retreatThreshold = retreatThreshold;
  16. }
  17. public override bool CanExecute(MazeAgent agent)
  18. {
  19. if (agent == null || _director == null) return false;
  20. if (agent.IsEngaged) return false; // Already committed
  21. Room room = agent.CurrentRoom;
  22. if (room == null) return false;
  23. // Is there a fight already mustering here that we're not in?
  24. PendingFight mustering = _director.FindMusteringFight(room);
  25. return mustering != null && !mustering.Contains(agent);
  26. }
  27. public override bool Execute(MazeAgent agent)
  28. {
  29. Room room = agent.CurrentRoom;
  30. PendingFight fight = _director.FindMusteringFight(room);
  31. if (fight == null) return false;
  32. float threat = fight.Encounter.GetPerceivedChallengeRating(agent.CharacterSheet);
  33. float effectiveThreshold = GetEffectiveReteatThreshold(agent);
  34. float sharedThreat = threat / (fight.Agents.Count + 1);
  35. bool wantsToSneak = sharedThreat > effectiveThreshold || agent.HasFledFrom(fight.Room.Id);
  36. if (!wantsToSneak)
  37. {
  38. // Join the fight
  39. var result = _director.ResolveArrival(agent, false);
  40. return result == CombatDirector.ArrivalDecision.Joined;
  41. }
  42. else
  43. {
  44. // Try to sneak, which might fail and force a join
  45. var result = _director.ResolveArrival(agent, true);
  46. return result == CombatDirector.ArrivalDecision.Joined ||
  47. result == CombatDirector.ArrivalDecision.SneakFailedAndJoined;
  48. }
  49. }
  50. public override void Think(MazeAgent agent)
  51. {
  52. Room room = agent.CurrentRoom;
  53. PendingFight fight = _director.FindMusteringFight(room);
  54. if (fight == null) return;
  55. float threat = fight.Encounter.GetPerceivedChallengeRating(agent.CharacterSheet);
  56. float effectiveThreshold = GetEffectiveReteatThreshold(agent);
  57. float sharedThreat = threat / (fight.Agents.Count + 1);
  58. bool wantsToSneak = sharedThreat > effectiveThreshold || agent.HasFledFrom(fight.Room.Id);
  59. if (!wantsToSneak)
  60. agent.Thoughts.Add(ThoughtKind.Fight,
  61. $"Walked into fight #{fight.Id} - {fight.Agents.Count} ally(s) already in, shared threat {sharedThreat:0.00}. Joining.");
  62. else
  63. agent.Thoughts.Add(ThoughtKind.Sneak,
  64. $"Walked into fight #{fight.Id} - shared threat {sharedThreat:0.00} still over my limit. Trying to sneak past it.");
  65. }
  66. private float GetEffectiveReteatThreshold(MazeAgent agent)
  67. {
  68. CharacterSheet sheet = agent.CharacterSheet;
  69. float maxHealth = sheet.Stats.MaxHealth;
  70. if (maxHealth <= 0) return _retreatThreshold;
  71. float healthPercent = sheet.Stats.CurrentHealth / maxHealth;
  72. if (healthPercent < 0.3f)
  73. return _retreatThreshold * 0.5f;
  74. else if (healthPercent < 0.5f)
  75. {
  76. float t = (healthPercent - 0.3f) / 0.2f;
  77. return UnityEngine.Mathf.Lerp(_retreatThreshold * 0.5f, _retreatThreshold, t);
  78. }
  79. return _retreatThreshold;
  80. }
  81. }
  82. }