using UnityEngine; using System; /// /// Base class for all travel events that can occur during journeys /// [Serializable] public abstract class TravelEvent : ScriptableObject { [Header("Basic Event Info")] public string eventName; [TextArea(3, 6)] public string eventDescription; public EventType eventType; public EventRarity rarity = EventRarity.Common; [Header("Trigger Behavior")] [Tooltip("How this event is triggered - automatically or as a spottable marker")] public EventTriggerType triggerType = EventTriggerType.Automatic; [Header("Terrain Preferences")] [Range(0f, 1f)] public float plainsChance = 0.5f; [Range(0f, 1f)] public float forestChance = 0.5f; [Range(0f, 1f)] public float mountainChance = 0.5f; [Range(0f, 1f)] public float roadChance = 0.5f; [Range(0f, 1f)] public float riverChance = 0.5f; [Range(0f, 1f)] public float lakeChance = 0.5f; [Range(0f, 1f)] public float oceanChance = 0.5f; [Header("Feature Preferences")] [Range(0f, 1f)] public float townChance = 0.5f; [Range(0f, 1f)] public float villageChance = 0.5f; [Range(0f, 1f)] public float bridgeChance = 0.5f; [Range(0f, 1f)] public float tunnelChance = 0.5f; [Range(0f, 1f)] public float ferryChance = 0.5f; [Header("Timing")] public bool canOccurMultipleTimes = true; public float cooldownDays = 0f; // Days before this event can occur again /// /// Calculate the chance of this event occurring on a specific tile /// public virtual float GetEventChance(MapTile tile) { float baseChance = GetBaseChanceForRarity(); float terrainModifier = GetTerrainModifier(tile.terrainType); float featureModifier = GetFeatureModifier(tile.featureType); return baseChance * terrainModifier * featureModifier; } /// /// Execute the event - must be implemented by derived classes /// public abstract EventResult ExecuteEvent(TravelEventContext context); /// /// Check if this event can occur given the current context /// public virtual bool CanOccur(TravelEventContext context) { // Check cooldown if (!canOccurMultipleTimes && context.eventHistory.HasOccurred(this)) { return false; } if (cooldownDays > 0f && context.eventHistory.GetTimeSinceLastOccurrence(this) < cooldownDays) { return false; } return true; } private float GetBaseChanceForRarity() { return rarity switch { EventRarity.VeryCommon => 0.25f, EventRarity.Common => 0.15f, EventRarity.Uncommon => 0.08f, EventRarity.Rare => 0.04f, EventRarity.VeryRare => 0.02f, EventRarity.Legendary => 0.005f, _ => 0.1f }; } private float GetTerrainModifier(TerrainType terrain) { return terrain switch { TerrainType.Plains => plainsChance, TerrainType.Forest => forestChance, TerrainType.Mountain => mountainChance, TerrainType.River => riverChance, TerrainType.Lake => lakeChance, TerrainType.Ocean => oceanChance, TerrainType.ForestRiver => forestChance * riverChance, // Hybrid terrain _ => 0.5f }; } private float GetFeatureModifier(FeatureType feature) { return feature switch { FeatureType.Road => roadChance, FeatureType.Town => townChance, FeatureType.Village => villageChance, FeatureType.Bridge => bridgeChance, FeatureType.Tunnel => tunnelChance, FeatureType.Ferry => ferryChance, FeatureType.None => 1f, // No modifier for no feature _ => 0.5f }; } } [Serializable] public enum EventType { Combat, // Battle encounters Trading, // Merchant encounters Discovery, // Finding items/resources Social, // Meeting NPCs, getting information Hazard, // Environmental dangers Rest, // Camping, healing opportunities Mystery // Strange occurrences, puzzles } [Serializable] public enum EventRarity { VeryCommon, // 25% base chance Common, // 15% base chance Uncommon, // 8% base chance Rare, // 4% base chance VeryRare, // 2% base chance Legendary // 0.5% base chance } /// /// Result of executing a travel event /// [Serializable] public class EventResult { public bool eventOccurred = true; public bool shouldStopTravel = false; public bool startBattle = false; public bool openTrading = false; public string resultMessage = ""; // Resource changes public int goldChange = 0; public int foodChange = 0; public int healthChange = 0; // Battle setup (if startBattle is true) public BattleEventData battleData; // Trading setup (if openTrading is true) public TradingEventData tradingData; public EventResult(string message) { resultMessage = message; } public static EventResult NoEvent() { return new EventResult("") { eventOccurred = false }; } public static EventResult SimpleBattle(string message, int enemyCount, string enemyType = "Bandit") { return new EventResult(message) { shouldStopTravel = true, startBattle = true, battleData = new BattleEventData { enemyCount = enemyCount, enemyType = enemyType } }; } public static EventResult SimpleTrading(string message, string merchantType = "Traveling Merchant") { return new EventResult(message) { shouldStopTravel = true, openTrading = true, tradingData = new TradingEventData { merchantType = merchantType, hasRareItems = UnityEngine.Random.value < 0.3f } }; } } [Serializable] public class BattleEventData { public int enemyCount = 1; public string enemyType = "Bandit"; public string battleDescription = ""; [Tooltip("Reference to the actual enemy data asset (safer than string names)")] public EnemyCharacterData enemyCharacterData = null; } [Serializable] public class TradingEventData { public string merchantType = "Traveling Merchant"; public bool hasRareItems = false; public float priceModifier = 1f; // 1f = normal prices, 0.8f = 20% discount, etc. }