TravelEvent.cs 7.0 KB

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