using UnityEngine; /// /// Combat encounter events - ambushes, bandits, wild animals, etc. /// More likely in forests and mountains, less likely on roads /// [CreateAssetMenu(fileName = "New Combat Event", menuName = "RPG/Travel Events/Combat Event")] public class CombatTravelEvent : TravelEvent { [Header("Combat Event Settings")] public int minEnemies = 1; public int maxEnemies = 3; [Tooltip("Drag EnemyCharacterData assets here")] public EnemyCharacterData[] possibleEnemies = new EnemyCharacterData[0]; [Header("Escape Configuration")] public bool allowRunningAway = true; [Range(0f, 1f)] public float runAwaySuccessChance = 0.75f; public int runAwayHealthPenalty = 5; [Header("Run Away Messages")] [TextArea(2, 4)] public string[] successfulRunAwayMessages = { "Your party successfully escapes from the enemies!", "Quick thinking allows your party to avoid the confrontation!", "Your party slips away unnoticed by the enemies." }; [TextArea(2, 4)] public string[] failedRunAwayMessages = { "The enemies catch up to your party! You take {damage} damage while trying to escape.", "Your escape attempt fails! The enemies pursue and wound your party for {damage} damage.", "The enemies block your escape route! Your party suffers {damage} damage in the failed attempt." }; [Header("Combat Event Descriptions")] [TextArea(2, 4)] public string[] encounterDescriptions = { "Your party is ambushed by {enemyType}s!", "A group of {enemyType}s blocks your path!", "You stumble upon a {enemyType} camp!" }; public override EventResult ExecuteEvent(TravelEventContext context) { int enemyCount = Random.Range(minEnemies, maxEnemies + 1); // Ensure we have valid enemies configured if (possibleEnemies == null || possibleEnemies.Length == 0) { Debug.LogError($"No enemies configured for combat event: {eventName}"); return EventResult.NoEvent(); } // Filter out null entries var validEnemies = System.Array.FindAll(possibleEnemies, e => e != null); if (validEnemies.Length == 0) { Debug.LogError($"All enemy references are null for combat event: {eventName}"); return EventResult.NoEvent(); } // Select random enemy EnemyCharacterData selectedEnemy = validEnemies[Random.Range(0, validEnemies.Length)]; string enemyType = selectedEnemy.enemyName; // Generate description string description = encounterDescriptions[Random.Range(0, encounterDescriptions.Length)]; description = description.Replace("{enemyType}", enemyType); // Try to use combat event integration for popup choice Debug.Log("🔍 Looking for CombatEventIntegration component..."); // First try to find it by component type var integrationComponents = FindObjectsByType(FindObjectsSortMode.None); var combatIntegration = System.Array.Find(integrationComponents, comp => comp.GetType().Name == "CombatEventIntegration"); if (combatIntegration == null) { // Try the reflection approach as fallback var type = System.Type.GetType("CombatEventIntegration"); if (type != null) { combatIntegration = FindFirstObjectByType(type) as MonoBehaviour; } } if (combatIntegration != null) { Debug.Log($"✅ Found CombatEventIntegration: {combatIntegration.name}"); // Create battle data for the popup var battleData = new BattleEventData { enemyCount = enemyCount, enemyType = enemyType, enemyCharacterData = selectedEnemy }; // Use reflection to call ShowCombatChoicePopup with the new signature var method = combatIntegration.GetType().GetMethod("ShowCombatChoicePopup"); if (method != null) { Debug.Log($"🎭 Calling ShowCombatChoicePopup with {enemyCount} {enemyType}(s) and combat event reference"); method.Invoke(combatIntegration, new object[] { battleData, context, description, this }); // Return a special result that indicates popup is handling the choice return new EventResult("Combat encounter detected...") { shouldStopTravel = true, // Stop travel while popup is showing startBattle = false, // Don't start battle yet - popup will handle it eventOccurred = true }; } else { Debug.LogError("❌ ShowCombatChoicePopup method not found on CombatEventIntegration"); } } else { Debug.LogWarning("⚠️ No CombatEventIntegration component found in scene"); } // Fallback to immediate battle if no integration found Debug.LogWarning("⚠️ No CombatEventIntegration found, proceeding with immediate battle"); var result = EventResult.SimpleBattle(description, enemyCount, enemyType); result.battleData.enemyCharacterData = selectedEnemy; return result; } /// /// Handle escape attempt for this specific combat event /// public EventResult HandleEscapeAttempt(TravelEventContext context) { if (!allowRunningAway) { Debug.LogWarning($"🚫 Running away is not allowed for this combat event: {eventName}"); return null; // Force battle } Debug.Log($"🏃 Processing escape attempt for {eventName} (success chance: {runAwaySuccessChance:P})"); // Roll for escape success bool escapeSuccessful = Random.value <= runAwaySuccessChance; if (escapeSuccessful) { return CreateSuccessfulEscapeResult(); } else { return CreateFailedEscapeResult(context); } } /// /// Create result for successful escape /// private EventResult CreateSuccessfulEscapeResult() { string message = successfulRunAwayMessages[Random.Range(0, successfulRunAwayMessages.Length)]; Debug.Log($"✅ Escape successful: {message}"); return new EventResult(message) { shouldStopTravel = false, // Don't stop travel for successful escape startBattle = false, eventOccurred = true }; } /// /// Create result for failed escape /// private EventResult CreateFailedEscapeResult(TravelEventContext context) { string message = failedRunAwayMessages[Random.Range(0, failedRunAwayMessages.Length)]; message = message.Replace("{damage}", runAwayHealthPenalty.ToString()); Debug.Log($"❌ Escape failed: {message}"); var result = new EventResult(message) { shouldStopTravel = true, // Stop travel for failed escape startBattle = false, // Don't start battle, just apply penalty eventOccurred = true, healthChange = -runAwayHealthPenalty // Apply health penalty through EventResult system }; return result; } void OnEnable() { // Set default values for combat events eventType = EventType.Combat; rarity = EventRarity.Common; // Combat events are more likely in dangerous terrain forestChance = 0.8f; mountainChance = 0.9f; plainsChance = 0.4f; roadChance = 0.2f; // Much less likely on roads townChance = 0.1f; // Very unlikely in towns villageChance = 0.3f; } } /// /// Trading encounter events - merchants, traders, caravans /// More likely on roads and near settlements /// [CreateAssetMenu(fileName = "New Trading Event", menuName = "RPG/Travel Events/Trading Event")] public class TradingTravelEvent : TravelEvent { [Header("Trading Event Settings")] public string[] merchantTypes = { "Traveling Merchant", "Caravan Trader", "Wandering Peddler" }; public bool canHaveRareItems = true; [Range(0f, 1f)] public float rareItemChance = 0.3f; [Range(0.5f, 1.5f)] public float priceModifier = 1f; [Header("Trading Event Descriptions")] [TextArea(2, 4)] public string[] encounterDescriptions = { "You encounter a {merchantType} on the road.", "A {merchantType} approaches your party with goods to sell.", "You come across a {merchantType} resting by the roadside." }; public override EventResult ExecuteEvent(TravelEventContext context) { string merchantType = merchantTypes[Random.Range(0, merchantTypes.Length)]; string description = encounterDescriptions[Random.Range(0, encounterDescriptions.Length)]; description = description.Replace("{merchantType}", merchantType); var result = EventResult.SimpleTrading(description, merchantType); result.tradingData.hasRareItems = canHaveRareItems && Random.value < rareItemChance; result.tradingData.priceModifier = priceModifier; return result; } void OnEnable() { // Set default values for trading events eventType = EventType.Trading; rarity = EventRarity.Common; // Trading events are more likely on roads and near settlements roadChance = 0.9f; townChance = 0.7f; villageChance = 0.8f; bridgeChance = 0.6f; // Common crossing points ferryChance = 0.5f; // Less likely in wilderness forestChance = 0.3f; mountainChance = 0.2f; plainsChance = 0.5f; } } /// /// Discovery events - finding treasures, resources, or hidden locations /// [CreateAssetMenu(fileName = "New Discovery Event", menuName = "RPG/Travel Events/Discovery Event")] public class DiscoveryTravelEvent : TravelEvent { [Header("Discovery Settings")] public int minGoldFound = 10; public int maxGoldFound = 100; public bool canFindItems = true; [Header("Discovery Descriptions")] [TextArea(2, 4)] public string[] discoveryDescriptions = { "Your party discovers a hidden cache containing {amount} gold!", "While exploring, you find an abandoned stash with {amount} gold pieces.", "A keen-eyed party member spots something valuable - {amount} gold!" }; public override EventResult ExecuteEvent(TravelEventContext context) { int goldFound = Random.Range(minGoldFound, maxGoldFound + 1); string description = discoveryDescriptions[Random.Range(0, discoveryDescriptions.Length)]; description = description.Replace("{amount}", goldFound.ToString()); return new EventResult(description) { goldChange = goldFound }; } void OnEnable() { eventType = EventType.Discovery; rarity = EventRarity.Uncommon; // More likely in remote areas forestChance = 0.7f; mountainChance = 0.8f; plainsChance = 0.4f; roadChance = 0.3f; townChance = 0.1f; } } /// /// Rest events - safe camping spots, inns, healing springs /// [CreateAssetMenu(fileName = "New Rest Event", menuName = "RPG/Travel Events/Rest Event")] public class RestTravelEvent : TravelEvent { [Header("Rest Settings")] public int healthRestored = 25; public bool restoresAllHealth = false; [Header("Rest Descriptions")] [TextArea(2, 4)] public string[] restDescriptions = { "Your party finds a peaceful grove perfect for resting and recovers {health} health.", "A natural spring provides refreshing water, restoring {health} health to your party.", "Your party discovers a safe camping spot and takes time to tend wounds, recovering {health} health." }; public override EventResult ExecuteEvent(TravelEventContext context) { int actualHealing = restoresAllHealth ? 100 : healthRestored; string description = restDescriptions[Random.Range(0, restDescriptions.Length)]; description = description.Replace("{health}", actualHealing.ToString()); return new EventResult(description) { healthChange = actualHealing }; } void OnEnable() { eventType = EventType.Rest; rarity = EventRarity.Common; // More likely in natural areas forestChance = 0.8f; riverChance = 0.9f; lakeChance = 0.9f; plainsChance = 0.6f; mountainChance = 0.5f; roadChance = 0.4f; townChance = 0.2f; } } /// /// Hazard events - natural dangers, traps, environmental challenges /// [CreateAssetMenu(fileName = "New Hazard Event", menuName = "RPG/Travel Events/Hazard Event")] public class HazardTravelEvent : TravelEvent { [Header("Hazard Settings")] public int minHealthLoss = 5; public int maxHealthLoss = 20; public int goldCost = 0; // Cost to avoid the hazard [Header("Hazard Descriptions")] [TextArea(2, 4)] public string[] hazardDescriptions = { "Your party gets caught in a sudden rockslide, losing {damage} health.", "Treacherous terrain causes injuries to your party members, resulting in {damage} health loss.", "A hidden pit trap catches one of your party members, causing {damage} damage." }; public override EventResult ExecuteEvent(TravelEventContext context) { int damage = Random.Range(minHealthLoss, maxHealthLoss + 1); string description = hazardDescriptions[Random.Range(0, hazardDescriptions.Length)]; description = description.Replace("{damage}", damage.ToString()); return new EventResult(description) { healthChange = -damage, goldChange = -goldCost }; } void OnEnable() { eventType = EventType.Hazard; rarity = EventRarity.Common; // More likely in dangerous terrain mountainChance = 0.9f; forestChance = 0.6f; riverChance = 0.7f; oceanChance = 0.8f; // Less likely in safe areas roadChance = 0.2f; townChance = 0.1f; villageChance = 0.2f; plainsChance = 0.4f; } }