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.
}