using UnityEngine;
using System.Collections.Generic;
using System.Linq;
///
/// Main travel event system that manages when and where events occur during travel
/// Integrates with the existing TeamTravelSystem
///
public class TravelEventSystem : MonoBehaviour
{
[Header("Event System Settings")]
public bool enableEvents = true;
[Range(0f, 1f)]
public float baseEventChance = 0.1f; // 10% chance per tile check (reduced from 15%)
[Range(1, 10)]
public int tilesPerEventCheck = 4; // Check for events every 4 tiles (increased from 3)
[Header("Event Collections")]
public List availableEvents = new List();
[Header("Debug")]
public bool showDebugLogs = false;
public bool forceNextEvent = false; // For testing
// Event tracking
private TravelEventHistory globalEventHistory;
private int tilesTraveledSinceLastCheck = 0;
private TravelEventContext currentContext;
private Vector2Int lastTeamPosition = Vector2Int.zero;
private bool hasInitializedPosition = false;
// Component references
private TeamTravelSystem travelSystem;
private MapMaker2 mapMaker;
private SimpleTeamPlacement teamPlacement;
private TravelEventUI eventUI;
private MonoBehaviour markerVisualizer; // Use MonoBehaviour to avoid compilation issues
// Enhanced system will be linked after compilation
private object enhancedEventSystem;
void Start()
{
// Find required components
travelSystem = FindFirstObjectByType();
mapMaker = FindFirstObjectByType();
teamPlacement = FindFirstObjectByType();
eventUI = FindFirstObjectByType();
// Find EventMarkerVisualizer using reflection to avoid compilation order issues
var markerVisualizerComponents = FindObjectsByType(FindObjectsSortMode.None);
markerVisualizer = System.Array.Find(markerVisualizerComponents,
comp => comp.GetType().Name == "EventMarkerVisualizer");
// Enhanced system will be found after compilation
// enhancedEventSystem = FindFirstObjectByType();
globalEventHistory = new TravelEventHistory();
if (showDebugLogs)
{
if (eventUI == null)
Debug.LogWarning("⚠️ TravelEventUI not found. Event messages will only appear in console.");
LogEventDistribution();
}
// Load default events if none are assigned
if (availableEvents.Count == 0)
{
LoadDefaultEvents();
}
}
void Update()
{
if (!enableEvents || travelSystem == null) return;
// Track actual tile movement instead of frame-based checks
Vector2Int currentTeamPosition = GetCurrentTeamPosition();
if (!hasInitializedPosition)
{
lastTeamPosition = currentTeamPosition;
hasInitializedPosition = true;
return;
}
// Only check for events when the team actually moves to a new tile
if (currentTeamPosition != lastTeamPosition)
{
tilesTraveledSinceLastCheck++;
if (showDebugLogs)
{
Debug.Log($"🚶 Team moved from {lastTeamPosition} to {currentTeamPosition} (tiles since last check: {tilesTraveledSinceLastCheck})");
}
// Check for event marker triggers first
CheckForEventMarkerTriggers(currentTeamPosition);
lastTeamPosition = currentTeamPosition;
// Check for events every N tiles traveled
if (tilesTraveledSinceLastCheck >= tilesPerEventCheck)
{
if (showDebugLogs)
{
Debug.Log($"🎲 Checking for travel event after {tilesTraveledSinceLastCheck} tiles traveled");
}
CheckForTravelEvent();
tilesTraveledSinceLastCheck = 0;
}
}
}
///
/// Main method to check if a travel event should occur
///
public void CheckForTravelEvent()
{
if (!enableEvents) return;
// Get current travel context
currentContext = CreateCurrentContext();
if (currentContext == null) return;
// Calculate if an event should occur
float eventRoll = Random.value;
float eventThreshold = CalculateEventThreshold(currentContext);
// Force event for testing
if (forceNextEvent)
{
forceNextEvent = false;
eventRoll = 0f; // Guarantee event
eventThreshold = 1f;
}
if (eventRoll <= eventThreshold)
{
TriggerRandomEvent(currentContext);
}
}
///
/// Force a specific event to occur (for testing or story purposes)
///
public void TriggerSpecificEvent(TravelEvent eventToTrigger)
{
if (eventToTrigger == null) return;
currentContext = CreateCurrentContext();
if (currentContext == null) return;
ExecuteEvent(eventToTrigger, currentContext);
}
///
/// Check if team has triggered any event markers
///
private void CheckForEventMarkerTriggers(Vector2Int teamPosition)
{
if (markerVisualizer == null) return;
// Use reflection to call CheckForMarkerTrigger method
var method = markerVisualizer.GetType().GetMethod("CheckForMarkerTrigger");
if (method != null)
{
var triggeredEvent = method.Invoke(markerVisualizer, new object[] { teamPosition }) as TravelEvent;
if (triggeredEvent != null)
{
// Create context and execute the triggered event
currentContext = CreateCurrentContext();
if (currentContext != null)
{
ExecuteEvent(triggeredEvent, currentContext);
}
}
}
}
///
/// Trigger a random event based on current context
///
private void TriggerRandomEvent(TravelEventContext context)
{
var possibleEvents = GetPossibleEvents(context);
if (possibleEvents.Count == 0)
{
if (showDebugLogs)
Debug.Log("🎲 No valid events found for current context");
return;
}
// Separate events by trigger type
var automaticEvents = possibleEvents.Where(e => e.triggerType == EventTriggerType.Automatic).ToList();
var spottableEvents = possibleEvents.Where(e => e.triggerType == EventTriggerType.Spottable).ToList();
// Try to trigger automatic event first
if (automaticEvents.Count > 0)
{
TravelEvent selectedEvent = SelectWeightedEvent(automaticEvents, context);
if (selectedEvent != null)
{
ExecuteEvent(selectedEvent, context);
return;
}
}
// Try to spawn spottable event markers
if (spottableEvents.Count > 0 && markerVisualizer != null)
{
TravelEvent selectedSpottableEvent = SelectWeightedEvent(spottableEvents, context);
if (selectedSpottableEvent != null)
{
// Use reflection to call TrySpawnEventMarker method
var spawnMethod = markerVisualizer.GetType().GetMethod("TrySpawnEventMarker");
if (spawnMethod != null)
{
var markerSpawned = (bool)spawnMethod.Invoke(markerVisualizer, new object[] { selectedSpottableEvent });
if (markerSpawned && showDebugLogs)
{
Debug.Log($"🔴 Spawned spottable event marker: {selectedSpottableEvent.eventName}");
}
}
}
}
}
///
/// Execute a travel event and handle its results
///
private void ExecuteEvent(TravelEvent travelEvent, TravelEventContext context)
{
if (showDebugLogs)
{
Debug.Log($"🎭 Executing event: {travelEvent.eventName} at {context.currentPosition}");
}
// Execute the event
EventResult result = travelEvent.ExecuteEvent(context);
if (!result.eventOccurred)
{
if (showDebugLogs)
Debug.Log($"🎭 Event {travelEvent.eventName} chose not to occur");
return;
}
// Record the event
globalEventHistory.RecordEvent(travelEvent, context);
// Display event message (unless suppressed by popup system)
if (!result.suppressMessage)
{
if (eventUI != null)
{
eventUI.ShowEventResult(result);
}
else
{
ShowEventMessage(result.resultMessage);
}
}
// Handle the event result
HandleEventResult(result, context);
}
///
/// Handle the results of a travel event
///
private void HandleEventResult(EventResult result, TravelEventContext context)
{
// Apply resource changes
ApplyResourceChanges(result);
// Handle special event types
if (result.shouldStopTravel)
{
if (travelSystem != null)
{
// Stop current travel using the existing cancellation system
travelSystem.CancelJourney();
Debug.Log("🛑 Travel stopped due to event");
}
}
if (result.startBattle)
{
HandleBattleEvent(result.battleData, context);
}
if (result.openTrading)
{
HandleTradingEvent(result.tradingData, context);
}
}
///
/// Create travel event context from current game state
///
private TravelEventContext CreateCurrentContext()
{
if (teamPlacement == null || mapMaker == null || travelSystem == null)
return null;
Vector2Int currentPos = teamPlacement.GetCurrentTeamPosition();
var mapData = mapMaker.GetMapData();
var currentTile = mapData.GetTile(currentPos.x, currentPos.y);
var currentRoute = travelSystem.GetCurrentRoute();
var context = new TravelEventContext(currentPos, currentTile, currentRoute)
{
dayOfJourney = 1, // This could be tracked elsewhere
timeOfDay = 12f, // This could be from a day/night system
teamGold = 100, // This should come from actual team resources
teamFood = 50,
eventHistory = globalEventHistory
};
// Set travel context
if (currentRoute != null)
{
context.destination = travelSystem.GetPlannedDestination() ?? currentPos;
context.isOnMainRoad = currentTile.featureType == FeatureType.Road;
}
return context;
}
///
/// Calculate the threshold for events to occur based on context
///
private float CalculateEventThreshold(TravelEventContext context)
{
float threshold = baseEventChance;
// Modify based on terrain (some areas are more eventful)
switch (context.currentTile.terrainType)
{
case TerrainType.Forest:
threshold *= 1.3f; // More events in forests
break;
case TerrainType.Mountain:
threshold *= 1.4f; // Even more in mountains
break;
case TerrainType.Plains:
threshold *= 0.8f; // Fewer in plains
break;
}
// Modify based on features
switch (context.currentTile.featureType)
{
case FeatureType.Road:
threshold *= 0.7f; // Fewer events on roads (safer travel)
break;
case FeatureType.Town:
case FeatureType.Village:
threshold *= 0.5f; // Much fewer in settlements
break;
}
return Mathf.Clamp01(threshold);
}
///
/// Get all events that can occur in the current context
///
private List GetPossibleEvents(TravelEventContext context)
{
return availableEvents.Where(e => e != null && e.CanOccur(context)).ToList();
}
///
/// Select an event based on weighted probabilities
///
private TravelEvent SelectWeightedEvent(List events, TravelEventContext context)
{
if (events.Count == 0) return null;
if (events.Count == 1) return events[0];
// Calculate weights
var weights = events.Select(e => e.GetEventChance(context.currentTile)).ToList();
float totalWeight = weights.Sum();
if (totalWeight <= 0) return null;
// Select based on weight
float randomValue = Random.value * totalWeight;
float currentWeight = 0f;
for (int i = 0; i < events.Count; i++)
{
currentWeight += weights[i];
if (randomValue <= currentWeight)
{
return events[i];
}
}
return events.Last(); // Fallback
}
///
/// Apply resource changes from event results
///
private void ApplyResourceChanges(EventResult result)
{
if (result.goldChange != 0)
{
// Distribute gold to team members equally
DistributeGoldToTeam(result.goldChange);
}
if (result.healthChange != 0)
{
// Apply health change to team
// This would integrate with your character health system
Debug.Log($"❤️ Health changed by {result.healthChange}");
}
if (result.foodChange != 0)
{
// Apply food change
Debug.Log($"🍖 Food changed by {result.foodChange}");
}
}
///
/// Handle battle events by setting up battle data
///
private void HandleBattleEvent(BattleEventData battleData, TravelEventContext context)
{
if (battleData == null) return;
// Try to use enhanced event system for player choice popup
var enhancedSystem = FindFirstObjectByType(System.Type.GetType("EnhancedTravelEventSystem"));
if (enhancedSystem != null)
{
Debug.Log("🎭 Using Enhanced Travel Event System for combat popup");
// Use reflection to call the enhanced handler
var method = enhancedSystem.GetType().GetMethod("HandleEnhancedBattleEvent");
if (method != null)
{
string eventDescription = $"Your party encounters {battleData.enemyCount} {battleData.enemyType}!";
method.Invoke(enhancedSystem, new object[] { battleData, context, eventDescription });
return;
}
}
// Fallback to original battle handling
Debug.Log($"⚔️ Setting up battle: {battleData.enemyCount} {battleData.enemyType}(s)");
// Enhanced battle setup using EnemyCharacterData if available
if (battleData.enemyCharacterData != null)
{
Debug.Log($"🎯 Using enemy data: {battleData.enemyCharacterData.enemyName}");
Debug.Log($"📊 Enemy stats: HP={battleData.enemyCharacterData.maxHealth}, " +
$"AC={battleData.enemyCharacterData.armorClass}, " +
$"Threat={battleData.enemyCharacterData.threatLevel}");
// TODO: Enhanced battle setup with actual enemy data
/*
BattleSetupData.enemySelections.Clear();
for (int i = 0; i < battleData.enemyCount; i++)
{
var enemyData = battleData.enemyCharacterData;
BattleSetupData.enemySelections.Add(new CharacterSelection
{
characterName = $"{enemyData.enemyName}_{i+1}",
weaponType = enemyData.preferredWeapon != null ? enemyData.preferredWeapon.weaponType.ToString() : "Sword",
// Could also include:
// health = enemyData.maxHealth,
// armorClass = enemyData.armorClass,
// threatLevel = enemyData.threatLevel
});
}
*/
}
else
{
// Fallback to string-based system
Debug.Log("⚠️ No EnemyCharacterData found, using legacy string-based setup");
/*
BattleSetupData.enemySelections.Clear();
for (int i = 0; i < battleData.enemyCount; i++)
{
BattleSetupData.enemySelections.Add(new CharacterSelection
{
characterName = $"{battleData.enemyType}_{i+1}",
weaponType = "Sword" // Could be randomized
});
}
*/
}
}
///
/// Handle trading events by opening shop interface
///
private void HandleTradingEvent(TradingEventData tradingData, TravelEventContext context)
{
if (tradingData == null) return;
Debug.Log($"🛒 Opening trading with {tradingData.merchantType}");
// This would open your shop system
// You could modify shop inventory based on merchantType and hasRareItems
}
///
/// Display event message to player
///
private void ShowEventMessage(string message)
{
if (string.IsNullOrEmpty(message)) return;
Debug.Log($"📜 EVENT: {message}");
// Use UI system if available
if (eventUI != null)
{
eventUI.ShowEventMessage(message);
}
}
///
/// Load default events if none are assigned in inspector
///
private void LoadDefaultEvents()
{
// Load default events from Resources folder
TravelEvent[] defaultEvents = Resources.LoadAll("TravelEvents");
availableEvents.AddRange(defaultEvents);
if (showDebugLogs)
{
Debug.Log($"🔄 Loaded {defaultEvents.Length} default events from Resources");
}
}
///
/// Debug method to show event distribution
///
private void LogEventDistribution()
{
if (!showDebugLogs) return;
var eventsByType = availableEvents.GroupBy(e => e.eventType);
foreach (var group in eventsByType)
{
Debug.Log($"📊 {group.Key}: {group.Count()} events");
}
}
///
/// Public method to trigger event check manually (for testing)
///
[ContextMenu("Trigger Event Check")]
public void TriggerEventCheck()
{
forceNextEvent = true;
CheckForTravelEvent();
}
///
/// Reset event history (useful for testing)
///
[ContextMenu("Reset Event History")]
public void ResetEventHistory()
{
globalEventHistory = new TravelEventHistory();
tilesTraveledSinceLastCheck = 0;
Debug.Log("🔄 Event history reset");
}
///
/// Get the current team position in tile coordinates
///
private Vector2Int GetCurrentTeamPosition()
{
if (teamPlacement == null)
return Vector2Int.zero;
var teamMarker = teamPlacement.GetTeamMarker();
if (teamMarker == null)
return Vector2Int.zero;
// Convert world position to tile position
Vector3 worldPos = teamMarker.transform.position;
return new Vector2Int(
Mathf.RoundToInt(worldPos.x),
Mathf.RoundToInt(worldPos.z)
);
}
#region Gold Distribution System
///
/// Distribute gold equally among all team members
///
private void DistributeGoldToTeam(int totalGold)
{
if (totalGold <= 0)
{
Debug.LogWarning("Attempted to distribute non-positive gold amount");
return;
}
// Get team size from PlayerPrefs
int teamSize = PlayerPrefs.GetInt("TeamSize", 0);
if (teamSize <= 0)
{
Debug.LogWarning("No team members found to distribute gold to");
return;
}
// Calculate gold per member and remainder
int goldPerMember = totalGold / teamSize;
int remainderGold = totalGold % teamSize;
Debug.Log($"💰 Distributing {totalGold} gold among {teamSize} team members ({goldPerMember} each, {remainderGold} remainder)");
// Distribute gold to each team member
for (int i = 0; i < teamSize; i++)
{
var character = LoadCharacterFromPlayerPrefs(i);
if (character != null)
{
// Add base gold to each member
character.gold += goldPerMember;
// Give remainder gold to first few members
if (i < remainderGold)
{
character.gold += 1;
}
// Handle currency conversion (100 copper = 1 silver, 100 silver = 1 gold)
ConvertCurrency(character);
SaveCharacterToPlayerPrefs(character, i);
Debug.Log($"💰 {character.name}: +{goldPerMember + (i < remainderGold ? 1 : 0)} gold (Total: {character.gold}g {character.silver}s {character.copper}c)");
}
}
Debug.Log($"✅ Gold distribution complete: {totalGold} gold distributed to team");
}
///
/// Load character data from PlayerPrefs
///
private TeamCharacter LoadCharacterFromPlayerPrefs(int index)
{
string charName = PlayerPrefs.GetString($"Character{index}_Name", "");
if (string.IsNullOrEmpty(charName))
return null;
var character = new TeamCharacter();
character.name = charName;
character.gold = PlayerPrefs.GetInt($"Character{index}_Gold", 0);
character.silver = PlayerPrefs.GetInt($"Character{index}_Silver", 0);
character.copper = PlayerPrefs.GetInt($"Character{index}_Copper", 0);
return character;
}
///
/// Save character data to PlayerPrefs
///
private void SaveCharacterToPlayerPrefs(TeamCharacter character, int index)
{
PlayerPrefs.SetString($"Character{index}_Name", character.name);
PlayerPrefs.SetInt($"Character{index}_Gold", character.gold);
PlayerPrefs.SetInt($"Character{index}_Silver", character.silver);
PlayerPrefs.SetInt($"Character{index}_Copper", character.copper);
PlayerPrefs.Save();
}
///
/// Convert currency (100 copper = 1 silver, 100 silver = 1 gold)
///
private void ConvertCurrency(TeamCharacter character)
{
// Convert copper to silver
if (character.copper >= 100)
{
int newSilver = character.copper / 100;
character.silver += newSilver;
character.copper %= 100;
}
// Convert silver to gold
if (character.silver >= 100)
{
int newGold = character.silver / 100;
character.gold += newGold;
character.silver %= 100;
}
}
#endregion
}