TravelEvent.cs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. using UnityEngine;
  2. using System;
  3. /// <summary>
  4. /// Base class for all travel events that can occur during journeys
  5. /// </summary>
  6. [Serializable]
  7. public abstract class TravelEvent : ScriptableObject
  8. {
  9. [Header("Basic Event Info")]
  10. public string eventName;
  11. [TextArea(3, 6)]
  12. public string eventDescription;
  13. public EventType eventType;
  14. public EventRarity rarity = EventRarity.Common;
  15. [Header("Trigger Behavior")]
  16. [Tooltip("How this event is triggered - automatically or as a spottable marker")]
  17. public EventTriggerType triggerType = EventTriggerType.Automatic;
  18. [Header("Terrain Preferences")]
  19. [Range(0f, 1f)]
  20. public float plainsChance = 0.5f;
  21. [Range(0f, 1f)]
  22. public float forestChance = 0.5f;
  23. [Range(0f, 1f)]
  24. public float mountainChance = 0.5f;
  25. [Range(0f, 1f)]
  26. public float roadChance = 0.5f;
  27. [Range(0f, 1f)]
  28. public float riverChance = 0.5f;
  29. [Range(0f, 1f)]
  30. public float lakeChance = 0.5f;
  31. [Range(0f, 1f)]
  32. public float oceanChance = 0.5f;
  33. [Header("Feature Preferences")]
  34. [Range(0f, 1f)]
  35. public float townChance = 0.5f;
  36. [Range(0f, 1f)]
  37. public float villageChance = 0.5f;
  38. [Range(0f, 1f)]
  39. public float bridgeChance = 0.5f;
  40. [Range(0f, 1f)]
  41. public float tunnelChance = 0.5f;
  42. [Range(0f, 1f)]
  43. public float ferryChance = 0.5f;
  44. [Header("Timing")]
  45. public bool canOccurMultipleTimes = true;
  46. public float cooldownDays = 0f; // Days before this event can occur again
  47. /// <summary>
  48. /// Calculate the chance of this event occurring on a specific tile
  49. /// </summary>
  50. public virtual float GetEventChance(MapTile tile)
  51. {
  52. float baseChance = GetBaseChanceForRarity();
  53. float terrainModifier = GetTerrainModifier(tile.terrainType);
  54. float featureModifier = GetFeatureModifier(tile.featureType);
  55. return baseChance * terrainModifier * featureModifier;
  56. }
  57. /// <summary>
  58. /// Execute the event - must be implemented by derived classes
  59. /// </summary>
  60. public abstract EventResult ExecuteEvent(TravelEventContext context);
  61. /// <summary>
  62. /// Check if this event can occur given the current context
  63. /// </summary>
  64. public virtual bool CanOccur(TravelEventContext context)
  65. {
  66. // Check cooldown
  67. if (!canOccurMultipleTimes && context.eventHistory.HasOccurred(this))
  68. {
  69. return false;
  70. }
  71. if (cooldownDays > 0f && context.eventHistory.GetTimeSinceLastOccurrence(this) < cooldownDays)
  72. {
  73. return false;
  74. }
  75. return true;
  76. }
  77. private float GetBaseChanceForRarity()
  78. {
  79. return rarity switch
  80. {
  81. EventRarity.VeryCommon => 0.25f,
  82. EventRarity.Common => 0.15f,
  83. EventRarity.Uncommon => 0.08f,
  84. EventRarity.Rare => 0.04f,
  85. EventRarity.VeryRare => 0.02f,
  86. EventRarity.Legendary => 0.005f,
  87. _ => 0.1f
  88. };
  89. }
  90. private float GetTerrainModifier(TerrainType terrain)
  91. {
  92. return terrain switch
  93. {
  94. TerrainType.Plains => plainsChance,
  95. TerrainType.Forest => forestChance,
  96. TerrainType.Mountain => mountainChance,
  97. TerrainType.River => riverChance,
  98. TerrainType.Lake => lakeChance,
  99. TerrainType.Ocean => oceanChance,
  100. TerrainType.ForestRiver => forestChance * riverChance, // Hybrid terrain
  101. _ => 0.5f
  102. };
  103. }
  104. private float GetFeatureModifier(FeatureType feature)
  105. {
  106. return feature switch
  107. {
  108. FeatureType.Road => roadChance,
  109. FeatureType.Town => townChance,
  110. FeatureType.Village => villageChance,
  111. FeatureType.Bridge => bridgeChance,
  112. FeatureType.Tunnel => tunnelChance,
  113. FeatureType.Ferry => ferryChance,
  114. FeatureType.None => 1f, // No modifier for no feature
  115. _ => 0.5f
  116. };
  117. }
  118. }
  119. [Serializable]
  120. public enum EventType
  121. {
  122. Combat, // Battle encounters
  123. Trading, // Merchant encounters
  124. Discovery, // Finding items/resources
  125. Social, // Meeting NPCs, getting information
  126. Hazard, // Environmental dangers
  127. Rest, // Camping, healing opportunities
  128. Mystery // Strange occurrences, puzzles
  129. }
  130. [Serializable]
  131. public enum EventRarity
  132. {
  133. VeryCommon, // 25% base chance
  134. Common, // 15% base chance
  135. Uncommon, // 8% base chance
  136. Rare, // 4% base chance
  137. VeryRare, // 2% base chance
  138. Legendary // 0.5% base chance
  139. }
  140. /// <summary>
  141. /// Result of executing a travel event
  142. /// </summary>
  143. [Serializable]
  144. public class EventResult
  145. {
  146. public bool eventOccurred = true;
  147. public bool shouldStopTravel = false;
  148. public bool startBattle = false;
  149. public bool openTrading = false;
  150. public bool suppressMessage = false; // Skip showing event message (used when popup handles display)
  151. public string resultMessage = "";
  152. // Resource changes
  153. public int goldChange = 0;
  154. public int foodChange = 0;
  155. public int healthChange = 0;
  156. // Battle setup (if startBattle is true)
  157. public BattleEventData battleData;
  158. // Trading setup (if openTrading is true)
  159. public TradingEventData tradingData;
  160. public EventResult(string message)
  161. {
  162. resultMessage = message;
  163. }
  164. public static EventResult NoEvent()
  165. {
  166. return new EventResult("") { eventOccurred = false };
  167. }
  168. public static EventResult SimpleBattle(string message, int enemyCount, string enemyType = "Bandit")
  169. {
  170. return new EventResult(message)
  171. {
  172. shouldStopTravel = true,
  173. startBattle = true,
  174. battleData = new BattleEventData
  175. {
  176. enemyCount = enemyCount,
  177. enemyType = enemyType
  178. }
  179. };
  180. }
  181. public static EventResult SimpleTrading(string message, string merchantType = "Traveling Merchant")
  182. {
  183. return new EventResult(message)
  184. {
  185. shouldStopTravel = true,
  186. openTrading = true,
  187. tradingData = new TradingEventData
  188. {
  189. merchantType = merchantType,
  190. hasRareItems = UnityEngine.Random.value < 0.3f
  191. }
  192. };
  193. }
  194. }
  195. [Serializable]
  196. public class BattleEventData
  197. {
  198. public int enemyCount = 1;
  199. public string enemyType = "Bandit";
  200. public string battleDescription = "";
  201. [Tooltip("Reference to the actual enemy data asset (safer than string names)")]
  202. public EnemyCharacterData enemyCharacterData = null;
  203. }
  204. [Serializable]
  205. public class TradingEventData
  206. {
  207. public string merchantType = "Traveling Merchant";
  208. public bool hasRareItems = false;
  209. public float priceModifier = 1f; // 1f = normal prices, 0.8f = 20% discount, etc.
  210. }