CombatTravelEvent.cs 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. using UnityEngine;
  2. /// <summary>
  3. /// Combat encounter events - ambushes, bandits, wild animals, etc.
  4. /// More likely in forests and mountains, less likely on roads
  5. /// </summary>
  6. [CreateAssetMenu(fileName = "New Combat Event", menuName = "RPG/Travel Events/Combat Event")]
  7. public class CombatTravelEvent : TravelEvent
  8. {
  9. [Header("Combat Event Settings")]
  10. public int minEnemies = 1;
  11. public int maxEnemies = 3;
  12. [Tooltip("Drag EnemyCharacterData assets here")]
  13. public EnemyCharacterData[] possibleEnemies = new EnemyCharacterData[0];
  14. [Header("Escape Configuration")]
  15. public bool allowRunningAway = true;
  16. [Range(0f, 1f)]
  17. public float runAwaySuccessChance = 0.75f;
  18. public int runAwayHealthPenalty = 5;
  19. [Header("Run Away Messages")]
  20. [TextArea(2, 4)]
  21. public string[] successfulRunAwayMessages = {
  22. "Your party successfully escapes from the enemies!",
  23. "Quick thinking allows your party to avoid the confrontation!",
  24. "Your party slips away unnoticed by the enemies."
  25. };
  26. [TextArea(2, 4)]
  27. public string[] failedRunAwayMessages = {
  28. "The enemies catch up to your party! You take {damage} damage while trying to escape.",
  29. "Your escape attempt fails! The enemies pursue and wound your party for {damage} damage.",
  30. "The enemies block your escape route! Your party suffers {damage} damage in the failed attempt."
  31. };
  32. [Header("Combat Event Descriptions")]
  33. [TextArea(2, 4)]
  34. public string[] encounterDescriptions = {
  35. "Your party is ambushed by {enemyType}s!",
  36. "A group of {enemyType}s blocks your path!",
  37. "You stumble upon a {enemyType} camp!"
  38. };
  39. public override EventResult ExecuteEvent(TravelEventContext context)
  40. {
  41. int enemyCount = Random.Range(minEnemies, maxEnemies + 1);
  42. // Ensure we have valid enemies configured
  43. if (possibleEnemies == null || possibleEnemies.Length == 0)
  44. {
  45. Debug.LogError($"No enemies configured for combat event: {eventName}");
  46. return EventResult.NoEvent();
  47. }
  48. // Filter out null entries
  49. var validEnemies = System.Array.FindAll(possibleEnemies, e => e != null);
  50. if (validEnemies.Length == 0)
  51. {
  52. Debug.LogError($"All enemy references are null for combat event: {eventName}");
  53. return EventResult.NoEvent();
  54. }
  55. // Select random enemy
  56. EnemyCharacterData selectedEnemy = validEnemies[Random.Range(0, validEnemies.Length)];
  57. string enemyType = selectedEnemy.enemyName;
  58. // Generate description
  59. string description = encounterDescriptions[Random.Range(0, encounterDescriptions.Length)];
  60. description = description.Replace("{enemyType}", enemyType);
  61. // First try to find it by component type
  62. var integrationComponents = FindObjectsByType<MonoBehaviour>(FindObjectsSortMode.None);
  63. var combatIntegration = System.Array.Find(integrationComponents,
  64. comp => comp.GetType().Name == "CombatEventIntegration");
  65. if (combatIntegration == null)
  66. {
  67. // Try the reflection approach as fallback
  68. var type = System.Type.GetType("CombatEventIntegration");
  69. if (type != null)
  70. {
  71. combatIntegration = FindFirstObjectByType(type) as MonoBehaviour;
  72. }
  73. }
  74. if (combatIntegration != null)
  75. {
  76. // Create battle data for the popup
  77. var battleData = new BattleEventData
  78. {
  79. enemyCount = enemyCount,
  80. enemyType = enemyType,
  81. enemyCharacterData = selectedEnemy
  82. };
  83. // Use reflection to call ShowCombatChoicePopup with the new signature
  84. var method = combatIntegration.GetType().GetMethod("ShowCombatChoicePopup");
  85. if (method != null)
  86. {
  87. method.Invoke(combatIntegration, new object[] { battleData, context, description, this });
  88. // Return a special result that indicates popup is handling the choice
  89. return new EventResult("Combat encounter detected...")
  90. {
  91. shouldStopTravel = true, // Stop travel while popup is showing
  92. startBattle = false, // Don't start battle yet - popup will handle it
  93. suppressMessage = true, // Don't show default message - popup handles display
  94. eventOccurred = true
  95. };
  96. }
  97. else
  98. {
  99. Debug.LogError("❌ ShowCombatChoicePopup method not found on CombatEventIntegration");
  100. }
  101. }
  102. else
  103. {
  104. Debug.LogWarning("⚠️ No CombatEventIntegration component found in scene");
  105. }
  106. // Fallback to immediate battle if no integration found
  107. Debug.LogWarning("⚠️ No CombatEventIntegration found, proceeding with immediate battle");
  108. var result = EventResult.SimpleBattle(description, enemyCount, enemyType);
  109. result.battleData.enemyCharacterData = selectedEnemy;
  110. return result;
  111. }
  112. /// <summary>
  113. /// Handle escape attempt for this specific combat event
  114. /// </summary>
  115. public EventResult HandleEscapeAttempt(TravelEventContext context)
  116. {
  117. if (!allowRunningAway)
  118. {
  119. Debug.LogWarning($"🚫 Running away is not allowed for this combat event: {eventName}");
  120. return null; // Force battle
  121. }
  122. // Roll for escape success
  123. bool escapeSuccessful = Random.value <= runAwaySuccessChance;
  124. if (escapeSuccessful)
  125. {
  126. return CreateSuccessfulEscapeResult();
  127. }
  128. else
  129. {
  130. return CreateFailedEscapeResult(context);
  131. }
  132. }
  133. /// <summary>
  134. /// Create result for successful escape
  135. /// </summary>
  136. private EventResult CreateSuccessfulEscapeResult()
  137. {
  138. string message = successfulRunAwayMessages[Random.Range(0, successfulRunAwayMessages.Length)];
  139. return new EventResult(message)
  140. {
  141. shouldStopTravel = false, // Don't stop travel for successful escape
  142. startBattle = false,
  143. eventOccurred = true
  144. };
  145. }
  146. /// <summary>
  147. /// Create result for failed escape
  148. /// </summary>
  149. private EventResult CreateFailedEscapeResult(TravelEventContext context)
  150. {
  151. string message = failedRunAwayMessages[Random.Range(0, failedRunAwayMessages.Length)];
  152. message = message.Replace("{damage}", runAwayHealthPenalty.ToString());
  153. var result = new EventResult(message)
  154. {
  155. shouldStopTravel = true, // Stop travel for failed escape
  156. startBattle = false, // Don't start battle, just apply penalty
  157. eventOccurred = true,
  158. healthChange = -runAwayHealthPenalty // Apply health penalty through EventResult system
  159. };
  160. return result;
  161. }
  162. void OnEnable()
  163. {
  164. // Set default values for combat events
  165. eventType = EventType.Combat;
  166. rarity = EventRarity.Common;
  167. // Combat events are more likely in dangerous terrain
  168. forestChance = 0.8f;
  169. mountainChance = 0.9f;
  170. plainsChance = 0.4f;
  171. roadChance = 0.2f; // Much less likely on roads
  172. townChance = 0.1f; // Very unlikely in towns
  173. villageChance = 0.3f;
  174. }
  175. }