TravelEventTypes.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  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. // Try to use combat event integration for popup choice
  62. Debug.Log("🔍 Looking for CombatEventIntegration component...");
  63. // First try to find it by component type
  64. var integrationComponents = FindObjectsByType<MonoBehaviour>(FindObjectsSortMode.None);
  65. var combatIntegration = System.Array.Find(integrationComponents,
  66. comp => comp.GetType().Name == "CombatEventIntegration");
  67. if (combatIntegration == null)
  68. {
  69. // Try the reflection approach as fallback
  70. var type = System.Type.GetType("CombatEventIntegration");
  71. if (type != null)
  72. {
  73. combatIntegration = FindFirstObjectByType(type) as MonoBehaviour;
  74. }
  75. }
  76. if (combatIntegration != null)
  77. {
  78. Debug.Log($"✅ Found CombatEventIntegration: {combatIntegration.name}");
  79. // Create battle data for the popup
  80. var battleData = new BattleEventData
  81. {
  82. enemyCount = enemyCount,
  83. enemyType = enemyType,
  84. enemyCharacterData = selectedEnemy
  85. };
  86. // Use reflection to call ShowCombatChoicePopup with the new signature
  87. var method = combatIntegration.GetType().GetMethod("ShowCombatChoicePopup");
  88. if (method != null)
  89. {
  90. Debug.Log($"🎭 Calling ShowCombatChoicePopup with {enemyCount} {enemyType}(s) and combat event reference");
  91. method.Invoke(combatIntegration, new object[] { battleData, context, description, this });
  92. // Return a special result that indicates popup is handling the choice
  93. return new EventResult("Combat encounter detected...")
  94. {
  95. shouldStopTravel = true, // Stop travel while popup is showing
  96. startBattle = false, // Don't start battle yet - popup will handle it
  97. eventOccurred = true
  98. };
  99. }
  100. else
  101. {
  102. Debug.LogError("❌ ShowCombatChoicePopup method not found on CombatEventIntegration");
  103. }
  104. }
  105. else
  106. {
  107. Debug.LogWarning("⚠️ No CombatEventIntegration component found in scene");
  108. }
  109. // Fallback to immediate battle if no integration found
  110. Debug.LogWarning("⚠️ No CombatEventIntegration found, proceeding with immediate battle");
  111. var result = EventResult.SimpleBattle(description, enemyCount, enemyType);
  112. result.battleData.enemyCharacterData = selectedEnemy;
  113. return result;
  114. }
  115. /// <summary>
  116. /// Handle escape attempt for this specific combat event
  117. /// </summary>
  118. public EventResult HandleEscapeAttempt(TravelEventContext context)
  119. {
  120. if (!allowRunningAway)
  121. {
  122. Debug.LogWarning($"🚫 Running away is not allowed for this combat event: {eventName}");
  123. return null; // Force battle
  124. }
  125. Debug.Log($"🏃 Processing escape attempt for {eventName} (success chance: {runAwaySuccessChance:P})");
  126. // Roll for escape success
  127. bool escapeSuccessful = Random.value <= runAwaySuccessChance;
  128. if (escapeSuccessful)
  129. {
  130. return CreateSuccessfulEscapeResult();
  131. }
  132. else
  133. {
  134. return CreateFailedEscapeResult(context);
  135. }
  136. }
  137. /// <summary>
  138. /// Create result for successful escape
  139. /// </summary>
  140. private EventResult CreateSuccessfulEscapeResult()
  141. {
  142. string message = successfulRunAwayMessages[Random.Range(0, successfulRunAwayMessages.Length)];
  143. Debug.Log($"✅ Escape successful: {message}");
  144. return new EventResult(message)
  145. {
  146. shouldStopTravel = false, // Don't stop travel for successful escape
  147. startBattle = false,
  148. eventOccurred = true
  149. };
  150. }
  151. /// <summary>
  152. /// Create result for failed escape
  153. /// </summary>
  154. private EventResult CreateFailedEscapeResult(TravelEventContext context)
  155. {
  156. string message = failedRunAwayMessages[Random.Range(0, failedRunAwayMessages.Length)];
  157. message = message.Replace("{damage}", runAwayHealthPenalty.ToString());
  158. Debug.Log($"❌ Escape failed: {message}");
  159. var result = new EventResult(message)
  160. {
  161. shouldStopTravel = true, // Stop travel for failed escape
  162. startBattle = false, // Don't start battle, just apply penalty
  163. eventOccurred = true,
  164. healthChange = -runAwayHealthPenalty // Apply health penalty through EventResult system
  165. };
  166. return result;
  167. }
  168. void OnEnable()
  169. {
  170. // Set default values for combat events
  171. eventType = EventType.Combat;
  172. rarity = EventRarity.Common;
  173. // Combat events are more likely in dangerous terrain
  174. forestChance = 0.8f;
  175. mountainChance = 0.9f;
  176. plainsChance = 0.4f;
  177. roadChance = 0.2f; // Much less likely on roads
  178. townChance = 0.1f; // Very unlikely in towns
  179. villageChance = 0.3f;
  180. }
  181. }
  182. /// <summary>
  183. /// Trading encounter events - merchants, traders, caravans
  184. /// More likely on roads and near settlements
  185. /// </summary>
  186. [CreateAssetMenu(fileName = "New Trading Event", menuName = "RPG/Travel Events/Trading Event")]
  187. public class TradingTravelEvent : TravelEvent
  188. {
  189. [Header("Trading Event Settings")]
  190. public string[] merchantTypes = { "Traveling Merchant", "Caravan Trader", "Wandering Peddler" };
  191. public bool canHaveRareItems = true;
  192. [Range(0f, 1f)]
  193. public float rareItemChance = 0.3f;
  194. [Range(0.5f, 1.5f)]
  195. public float priceModifier = 1f;
  196. [Header("Trading Event Descriptions")]
  197. [TextArea(2, 4)]
  198. public string[] encounterDescriptions = {
  199. "You encounter a {merchantType} on the road.",
  200. "A {merchantType} approaches your party with goods to sell.",
  201. "You come across a {merchantType} resting by the roadside."
  202. };
  203. public override EventResult ExecuteEvent(TravelEventContext context)
  204. {
  205. string merchantType = merchantTypes[Random.Range(0, merchantTypes.Length)];
  206. string description = encounterDescriptions[Random.Range(0, encounterDescriptions.Length)];
  207. description = description.Replace("{merchantType}", merchantType);
  208. var result = EventResult.SimpleTrading(description, merchantType);
  209. result.tradingData.hasRareItems = canHaveRareItems && Random.value < rareItemChance;
  210. result.tradingData.priceModifier = priceModifier;
  211. return result;
  212. }
  213. void OnEnable()
  214. {
  215. // Set default values for trading events
  216. eventType = EventType.Trading;
  217. rarity = EventRarity.Common;
  218. // Trading events are more likely on roads and near settlements
  219. roadChance = 0.9f;
  220. townChance = 0.7f;
  221. villageChance = 0.8f;
  222. bridgeChance = 0.6f; // Common crossing points
  223. ferryChance = 0.5f;
  224. // Less likely in wilderness
  225. forestChance = 0.3f;
  226. mountainChance = 0.2f;
  227. plainsChance = 0.5f;
  228. }
  229. }
  230. /// <summary>
  231. /// Discovery events - finding treasures, resources, or hidden locations
  232. /// </summary>
  233. [CreateAssetMenu(fileName = "New Discovery Event", menuName = "RPG/Travel Events/Discovery Event")]
  234. public class DiscoveryTravelEvent : TravelEvent
  235. {
  236. [Header("Discovery Settings")]
  237. public int minGoldFound = 10;
  238. public int maxGoldFound = 100;
  239. public bool canFindItems = true;
  240. [Header("Discovery Descriptions")]
  241. [TextArea(2, 4)]
  242. public string[] discoveryDescriptions = {
  243. "Your party discovers a hidden cache containing {amount} gold!",
  244. "While exploring, you find an abandoned stash with {amount} gold pieces.",
  245. "A keen-eyed party member spots something valuable - {amount} gold!"
  246. };
  247. public override EventResult ExecuteEvent(TravelEventContext context)
  248. {
  249. int goldFound = Random.Range(minGoldFound, maxGoldFound + 1);
  250. string description = discoveryDescriptions[Random.Range(0, discoveryDescriptions.Length)];
  251. description = description.Replace("{amount}", goldFound.ToString());
  252. return new EventResult(description)
  253. {
  254. goldChange = goldFound
  255. };
  256. }
  257. void OnEnable()
  258. {
  259. eventType = EventType.Discovery;
  260. rarity = EventRarity.Uncommon;
  261. // More likely in remote areas
  262. forestChance = 0.7f;
  263. mountainChance = 0.8f;
  264. plainsChance = 0.4f;
  265. roadChance = 0.3f;
  266. townChance = 0.1f;
  267. }
  268. }
  269. /// <summary>
  270. /// Rest events - safe camping spots, inns, healing springs
  271. /// </summary>
  272. [CreateAssetMenu(fileName = "New Rest Event", menuName = "RPG/Travel Events/Rest Event")]
  273. public class RestTravelEvent : TravelEvent
  274. {
  275. [Header("Rest Settings")]
  276. public int healthRestored = 25;
  277. public bool restoresAllHealth = false;
  278. [Header("Rest Descriptions")]
  279. [TextArea(2, 4)]
  280. public string[] restDescriptions = {
  281. "Your party finds a peaceful grove perfect for resting and recovers {health} health.",
  282. "A natural spring provides refreshing water, restoring {health} health to your party.",
  283. "Your party discovers a safe camping spot and takes time to tend wounds, recovering {health} health."
  284. };
  285. public override EventResult ExecuteEvent(TravelEventContext context)
  286. {
  287. int actualHealing = restoresAllHealth ? 100 : healthRestored;
  288. string description = restDescriptions[Random.Range(0, restDescriptions.Length)];
  289. description = description.Replace("{health}", actualHealing.ToString());
  290. return new EventResult(description)
  291. {
  292. healthChange = actualHealing
  293. };
  294. }
  295. void OnEnable()
  296. {
  297. eventType = EventType.Rest;
  298. rarity = EventRarity.Common;
  299. // More likely in natural areas
  300. forestChance = 0.8f;
  301. riverChance = 0.9f;
  302. lakeChance = 0.9f;
  303. plainsChance = 0.6f;
  304. mountainChance = 0.5f;
  305. roadChance = 0.4f;
  306. townChance = 0.2f;
  307. }
  308. }
  309. /// <summary>
  310. /// Hazard events - natural dangers, traps, environmental challenges
  311. /// </summary>
  312. [CreateAssetMenu(fileName = "New Hazard Event", menuName = "RPG/Travel Events/Hazard Event")]
  313. public class HazardTravelEvent : TravelEvent
  314. {
  315. [Header("Hazard Settings")]
  316. public int minHealthLoss = 5;
  317. public int maxHealthLoss = 20;
  318. public int goldCost = 0; // Cost to avoid the hazard
  319. [Header("Hazard Descriptions")]
  320. [TextArea(2, 4)]
  321. public string[] hazardDescriptions = {
  322. "Your party gets caught in a sudden rockslide, losing {damage} health.",
  323. "Treacherous terrain causes injuries to your party members, resulting in {damage} health loss.",
  324. "A hidden pit trap catches one of your party members, causing {damage} damage."
  325. };
  326. public override EventResult ExecuteEvent(TravelEventContext context)
  327. {
  328. int damage = Random.Range(minHealthLoss, maxHealthLoss + 1);
  329. string description = hazardDescriptions[Random.Range(0, hazardDescriptions.Length)];
  330. description = description.Replace("{damage}", damage.ToString());
  331. return new EventResult(description)
  332. {
  333. healthChange = -damage,
  334. goldChange = -goldCost
  335. };
  336. }
  337. void OnEnable()
  338. {
  339. eventType = EventType.Hazard;
  340. rarity = EventRarity.Common;
  341. // More likely in dangerous terrain
  342. mountainChance = 0.9f;
  343. forestChance = 0.6f;
  344. riverChance = 0.7f;
  345. oceanChance = 0.8f;
  346. // Less likely in safe areas
  347. roadChance = 0.2f;
  348. townChance = 0.1f;
  349. villageChance = 0.2f;
  350. plainsChance = 0.4f;
  351. }
  352. }