Explorar o código

Hoveable effect on the spotted events

Axel Nordh hai 8 meses
pai
achega
f72d0fcede

+ 18 - 0
Assets/Scripts/Events/EventTriggerType.cs

@@ -0,0 +1,18 @@
+using UnityEngine;
+
+/// <summary>
+/// Defines how an event can be triggered during travel
+/// </summary>
+[System.Serializable]
+public enum EventTriggerType
+{
+    /// <summary>
+    /// Event triggers automatically when conditions are met (legacy behavior)
+    /// </summary>
+    Automatic,
+
+    /// <summary>
+    /// Event appears as a red marker within perception range that can be approached
+    /// </summary>
+    Spottable
+}

+ 33 - 33
Assets/Scripts/Events/PerceptionEventSystemSetup.cs

@@ -8,16 +8,16 @@ public class PerceptionEventSystemSetup : MonoBehaviour
 {
     [Header("Auto Setup")]
     [SerializeField] private bool autoSetupOnStart = true;
-    
+
     [Header("Event Marker Settings")]
     [SerializeField] private GameObject eventMarkerPrefab;
     [SerializeField] private Material eventMarkerMaterial;
     [SerializeField] private Color eventMarkerColor = Color.red;
     [SerializeField] private int maxActiveMarkers = 5;
-    
+
     [Header("Debug")]
     [SerializeField] private bool showSetupLogs = true;
-    
+
     void Start()
     {
         if (autoSetupOnStart)
@@ -25,7 +25,7 @@ public class PerceptionEventSystemSetup : MonoBehaviour
             SetupPerceptionEventSystem();
         }
     }
-    
+
     /// <summary>
     /// Sets up the perception and event system components
     /// </summary>
@@ -34,20 +34,20 @@ public class PerceptionEventSystemSetup : MonoBehaviour
     {
         if (showSetupLogs)
             Debug.Log("🔧 Setting up Perception Event System...");
-        
+
         // Ensure TeamPerceptionVisualizer exists
         SetupPerceptionVisualizer();
-        
+
         // Setup EventMarkerVisualizer
         SetupEventMarkerVisualizer();
-        
+
         // Verify TravelEventSystem integration
         VerifyTravelEventSystem();
-        
+
         if (showSetupLogs)
             Debug.Log("✅ Perception Event System setup complete!");
     }
-    
+
     private void SetupPerceptionVisualizer()
     {
         var perceptionVisualizer = FindFirstObjectByType<TeamPerceptionVisualizer>();
@@ -56,7 +56,7 @@ public class PerceptionEventSystemSetup : MonoBehaviour
             // Find a suitable GameObject to add it to (prefer the one with SimpleTeamPlacement)
             var teamPlacement = FindFirstObjectByType<SimpleTeamPlacement>();
             GameObject targetObject = teamPlacement?.gameObject ?? gameObject;
-            
+
             perceptionVisualizer = targetObject.AddComponent<TeamPerceptionVisualizer>();
             if (showSetupLogs)
                 Debug.Log($"✅ Added TeamPerceptionVisualizer to {targetObject.name}");
@@ -66,7 +66,7 @@ public class PerceptionEventSystemSetup : MonoBehaviour
             Debug.Log("✅ TeamPerceptionVisualizer already exists");
         }
     }
-    
+
     private void SetupEventMarkerVisualizer()
     {
         var markerVisualizer = FindFirstObjectByType<EventMarkerVisualizer>();
@@ -75,33 +75,33 @@ public class PerceptionEventSystemSetup : MonoBehaviour
             // Find a suitable GameObject to add it to (prefer the one with TravelEventSystem)
             var eventSystem = FindFirstObjectByType<TravelEventSystem>();
             GameObject targetObject = eventSystem?.gameObject ?? gameObject;
-            
+
             markerVisualizer = targetObject.AddComponent<EventMarkerVisualizer>();
-            
+
             // Configure with our settings
             if (eventMarkerPrefab != null)
             {
                 // Use reflection to set private fields since they're SerializeField
-                var prefabField = markerVisualizer.GetType().GetField("eventMarkerPrefab", 
+                var prefabField = markerVisualizer.GetType().GetField("eventMarkerPrefab",
                     System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
                 prefabField?.SetValue(markerVisualizer, eventMarkerPrefab);
             }
-            
+
             if (eventMarkerMaterial != null)
             {
-                var materialField = markerVisualizer.GetType().GetField("eventMarkerMaterial", 
+                var materialField = markerVisualizer.GetType().GetField("eventMarkerMaterial",
                     System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
                 materialField?.SetValue(markerVisualizer, eventMarkerMaterial);
             }
-            
-            var colorField = markerVisualizer.GetType().GetField("eventMarkerColor", 
+
+            var colorField = markerVisualizer.GetType().GetField("eventMarkerColor",
                 System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
             colorField?.SetValue(markerVisualizer, eventMarkerColor);
-            
-            var maxMarkersField = markerVisualizer.GetType().GetField("maxActiveMarkers", 
+
+            var maxMarkersField = markerVisualizer.GetType().GetField("maxActiveMarkers",
                 System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
             maxMarkersField?.SetValue(markerVisualizer, maxActiveMarkers);
-            
+
             if (showSetupLogs)
                 Debug.Log($"✅ Added EventMarkerVisualizer to {targetObject.name}");
         }
@@ -110,7 +110,7 @@ public class PerceptionEventSystemSetup : MonoBehaviour
             Debug.Log("✅ EventMarkerVisualizer already exists");
         }
     }
-    
+
     private void VerifyTravelEventSystem()
     {
         var eventSystem = FindFirstObjectByType<TravelEventSystem>();
@@ -120,11 +120,11 @@ public class PerceptionEventSystemSetup : MonoBehaviour
                 Debug.LogWarning("⚠️ No TravelEventSystem found. Please add one to use the event system.");
             return;
         }
-        
+
         if (showSetupLogs)
             Debug.Log("✅ TravelEventSystem found and should integrate automatically");
     }
-    
+
     /// <summary>
     /// Creates example spottable events for testing
     /// </summary>
@@ -133,17 +133,17 @@ public class PerceptionEventSystemSetup : MonoBehaviour
     {
         if (showSetupLogs)
             Debug.Log("🎭 Creating example spottable events...");
-        
+
         // This would create ScriptableObject assets in the project
         // For now, just log what would be created
         Debug.Log("📋 Example events to create:");
         Debug.Log("- SpottableSkeletonPatrol: Skeleton warriors that can be spotted and avoided");
         Debug.Log("- SpottableBanditCamp: Bandit camps visible from a distance");
         Debug.Log("- SpottableTreasureCache: Hidden treasure that can be spotted with good perception");
-        
+
         Debug.Log("💡 Tip: Right-click in Project → Create → RPG → Travel Events → Spottable → [Event Type]");
     }
-    
+
     /// <summary>
     /// Shows current system status
     /// </summary>
@@ -151,19 +151,19 @@ public class PerceptionEventSystemSetup : MonoBehaviour
     public void ShowSystemStatus()
     {
         Debug.Log("=== Perception Event System Status ===");
-        
+
         var perceptionVisualizer = FindFirstObjectByType<TeamPerceptionVisualizer>();
         Debug.Log($"TeamPerceptionVisualizer: {(perceptionVisualizer ? "✅ Found" : "❌ Missing")}");
-        
+
         var markerVisualizer = FindFirstObjectByType<EventMarkerVisualizer>();
         Debug.Log($"EventMarkerVisualizer: {(markerVisualizer ? "✅ Found" : "❌ Missing")}");
-        
+
         var eventSystem = FindFirstObjectByType<TravelEventSystem>();
         Debug.Log($"TravelEventSystem: {(eventSystem ? "✅ Found" : "❌ Missing")}");
-        
+
         var teamPlacement = FindFirstObjectByType<SimpleTeamPlacement>();
         Debug.Log($"SimpleTeamPlacement: {(teamPlacement ? "✅ Found" : "❌ Missing")}");
-        
+
         if (markerVisualizer != null)
         {
             // Use reflection to get active marker count
@@ -174,7 +174,7 @@ public class PerceptionEventSystemSetup : MonoBehaviour
                 Debug.Log($"Active Event Markers: {activeMarkers}");
             }
         }
-        
+
         Debug.Log("========================================");
     }
 }

+ 11 - 11
Assets/Scripts/Events/SpottableSkeletonPatrol.cs

@@ -14,25 +14,25 @@ public class SpottableSkeletonPatrol : CombatTravelEvent
         "A patrol of undead warriors can be seen ahead...",
         "Your keen eyes detect a group of skeleton warriors patrolling the area..."
     };
-    
+
     void OnEnable()
     {
         // Configure as spottable event
         triggerType = EventTriggerType.Spottable;
-        
+
         // Set up the event details
         eventName = "Skeleton Patrol";
         eventDescription = "A patrol of skeleton warriors guards this area. They haven't noticed you yet.";
         eventType = EventType.Combat;
         rarity = EventRarity.Common;
-        
+
         // Configure combat settings
         minEnemies = 2;
         maxEnemies = 4;
         allowRunningAway = true;
         runAwaySuccessChance = 0.85f; // Higher chance since you can see them coming
         runAwayHealthPenalty = 2; // Lower penalty since you're prepared
-        
+
         // Set terrain preferences (skeletons prefer ruins, forests, mountains)
         plainsChance = 0.4f;
         forestChance = 0.8f;
@@ -40,7 +40,7 @@ public class SpottableSkeletonPatrol : CombatTravelEvent
         roadChance = 0.3f; // Less likely on roads
         townChance = 0.1f; // Very unlikely in settlements
         villageChance = 0.2f;
-        
+
         // Configure encounter descriptions
         encounterDescriptions = new string[]
         {
@@ -48,7 +48,7 @@ public class SpottableSkeletonPatrol : CombatTravelEvent
             "The undead warriors turn toward you with hollow, glowing eyes!",
             "The skeleton patrol forms a battle line as you draw near!"
         };
-        
+
         // Configure escape messages for spotted encounters
         successfulRunAwayMessages = new string[]
         {
@@ -56,7 +56,7 @@ public class SpottableSkeletonPatrol : CombatTravelEvent
             "Your party backs away quietly before the patrol spots you!",
             "You successfully avoid the skeleton patrol by taking a different route!"
         };
-        
+
         failedRunAwayMessages = new string[]
         {
             "The skeletons spot your retreat! You take {damage} damage as they give chase!",
@@ -64,20 +64,20 @@ public class SpottableSkeletonPatrol : CombatTravelEvent
             "The undead patrol catches up to your fleeing party for {damage} damage!"
         };
     }
-    
+
     public override EventResult ExecuteEvent(TravelEventContext context)
     {
         // Override the base execution to provide special messaging for spottable events
         Debug.Log($"🔍 Executing spottable skeleton patrol at {context.currentPosition}");
-        
+
         // Show spot description first
         string description = spotDescription[Random.Range(0, spotDescription.Length)];
         Debug.Log($"👁️ Spot description: {description}");
-        
+
         // Then execute as normal combat event
         return base.ExecuteEvent(context);
     }
-    
+
     /// <summary>
     /// Provides a description of what the team spotted
     /// </summary>

+ 1 - 18
Assets/Scripts/Events/TravelEvent.cs

@@ -1,23 +1,6 @@
 using UnityEngine;
 using System;
 
-/// <summary>
-/// Defines how an event can be triggered during travel
-/// </summary>
-[System.Serializable]
-public enum EventTriggerType
-{
-    /// <summary>
-    /// Event triggers automatically when conditions are met (legacy behavior)
-    /// </summary>
-    Automatic,
-    
-    /// <summary>
-    /// Event appears as a red marker within perception range that can be approached
-    /// </summary>
-    Spottable
-}
-
 /// <summary>
 /// Base class for all travel events that can occur during journeys
 /// </summary>
@@ -30,7 +13,7 @@ public abstract class TravelEvent : ScriptableObject
     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;

+ 2 - 2
Assets/Scripts/Events/TravelEventSystem.cs

@@ -45,12 +45,12 @@ public class TravelEventSystem : MonoBehaviour
         mapMaker = FindFirstObjectByType<MapMaker2>();
         teamPlacement = FindFirstObjectByType<SimpleTeamPlacement>();
         eventUI = FindFirstObjectByType<TravelEventUI>();
-        
+
         // Find EventMarkerVisualizer using reflection to avoid compilation order issues
         var markerVisualizerComponents = FindObjectsByType<MonoBehaviour>(FindObjectsSortMode.None);
         markerVisualizer = System.Array.Find(markerVisualizerComponents,
             comp => comp.GetType().Name == "EventMarkerVisualizer");
-        
+
         // Enhanced system will be found after compilation
         // enhancedEventSystem = FindFirstObjectByType<EnhancedTravelEventSystem>();
 

+ 93 - 76
Assets/Scripts/Map/EventMarkerVisualizer.cs

@@ -13,33 +13,33 @@ public class EventMarkerVisualizer : MonoBehaviour
     [SerializeField] private Color eventMarkerColor = Color.red;
     [SerializeField] private float markerSize = 0.8f;
     [SerializeField] private float markerHeight = 0.6f;
-    
+
     [Header("Spawn Settings")]
     [SerializeField] private int maxActiveMarkers = 5;
     [SerializeField] private float minDistanceFromTeam = 2f;
     [SerializeField] private float maxDistanceFromTeam = 8f;
     [SerializeField] private float markerLifetime = 30f; // Markers disappear after 30 seconds if not approached
-    
+
     [Header("Visual Effects")]
     [SerializeField] private bool enablePulsing = true;
     [SerializeField] private float pulseSpeed = 2f;
     [SerializeField] private float pulseIntensity = 0.3f;
-    
+
     [Header("Debug")]
     [SerializeField] private bool showDebugInfo = false;
-    
+
     // Component references
     private TeamPerceptionVisualizer perceptionVisualizer;
     private SimpleTeamPlacement teamPlacement;
     private TravelEventSystem eventSystem;
     private MapMaker2 mapMaker;
-    
+
     // Active markers tracking
     private List<EventMarker> activeMarkers = new List<EventMarker>();
     private List<Vector2Int> occupiedPositions = new List<Vector2Int>();
-    
+
     public static EventMarkerVisualizer Instance { get; private set; }
-    
+
     void Awake()
     {
         if (Instance == null)
@@ -53,7 +53,7 @@ public class EventMarkerVisualizer : MonoBehaviour
             return;
         }
     }
-    
+
     void Start()
     {
         // Find required components
@@ -61,30 +61,30 @@ public class EventMarkerVisualizer : MonoBehaviour
         teamPlacement = FindFirstObjectByType<SimpleTeamPlacement>();
         eventSystem = FindFirstObjectByType<TravelEventSystem>();
         mapMaker = FindFirstObjectByType<MapMaker2>();
-        
+
         if (perceptionVisualizer == null)
         {
             Debug.LogError("EventMarkerVisualizer: TeamPerceptionVisualizer not found!");
         }
-        
+
         if (teamPlacement == null)
         {
             Debug.LogError("EventMarkerVisualizer: SimpleTeamPlacement not found!");
         }
     }
-    
+
     void Update()
     {
         // Clean up expired markers
         CleanupExpiredMarkers();
-        
+
         // Update marker visuals (pulsing effect)
         if (enablePulsing)
         {
             UpdateMarkerPulsing();
         }
     }
-    
+
     /// <summary>
     /// Attempts to spawn a spottable event marker within perception range
     /// </summary>
@@ -96,18 +96,18 @@ public class EventMarkerVisualizer : MonoBehaviour
         {
             return false;
         }
-        
+
         // Get team position and perception range
         Vector2Int teamPosition = GetTeamPosition();
         float perceptionRange = GetTeamPerceptionRange();
-        
+
         if (perceptionRange <= 0)
         {
             if (showDebugInfo)
                 Debug.Log("EventMarkerVisualizer: No perception range, cannot spawn marker");
             return false;
         }
-        
+
         // Find a valid position within perception range
         Vector2Int markerPosition = FindValidMarkerPosition(teamPosition, perceptionRange);
         if (markerPosition == Vector2Int.zero)
@@ -116,14 +116,14 @@ public class EventMarkerVisualizer : MonoBehaviour
                 Debug.Log("EventMarkerVisualizer: Could not find valid position for event marker");
             return false;
         }
-        
+
         // Create the marker
-        GameObject markerObject = CreateMarkerObject(markerPosition);
+        GameObject markerObject = CreateMarkerObject(markerPosition, travelEvent);
         if (markerObject == null)
         {
             return false;
         }
-        
+
         // Create event marker data
         EventMarker marker = new EventMarker
         {
@@ -133,18 +133,18 @@ public class EventMarkerVisualizer : MonoBehaviour
             spawnTime = Time.time,
             isActive = true
         };
-        
+
         activeMarkers.Add(marker);
         occupiedPositions.Add(markerPosition);
-        
+
         if (showDebugInfo)
         {
             Debug.Log($"EventMarkerVisualizer: Spawned marker for '{travelEvent.eventName}' at {markerPosition}");
         }
-        
+
         return true;
     }
-    
+
     /// <summary>
     /// Checks if the team is close enough to trigger an event marker
     /// </summary>
@@ -153,29 +153,29 @@ public class EventMarkerVisualizer : MonoBehaviour
     public TravelEvent CheckForMarkerTrigger(Vector2Int teamPosition)
     {
         float triggerDistance = 1.5f; // Distance to trigger event
-        
+
         foreach (var marker in activeMarkers.ToList())
         {
             if (!marker.isActive) continue;
-            
+
             float distance = Vector2Int.Distance(teamPosition, marker.position);
             if (distance <= triggerDistance)
             {
                 // Remove the marker and return the event
                 RemoveMarker(marker);
-                
+
                 if (showDebugInfo)
                 {
                     Debug.Log($"EventMarkerVisualizer: Team triggered event '{marker.travelEvent.eventName}' at {marker.position}");
                 }
-                
+
                 return marker.travelEvent;
             }
         }
-        
+
         return null;
     }
-    
+
     /// <summary>
     /// Removes all active event markers
     /// </summary>
@@ -186,7 +186,7 @@ public class EventMarkerVisualizer : MonoBehaviour
             RemoveMarker(marker);
         }
     }
-    
+
     private Vector2Int GetTeamPosition()
     {
         if (teamPlacement != null)
@@ -195,7 +195,7 @@ public class EventMarkerVisualizer : MonoBehaviour
         }
         return Vector2Int.zero;
     }
-    
+
     private float GetTeamPerceptionRange()
     {
         if (perceptionVisualizer != null)
@@ -206,16 +206,16 @@ public class EventMarkerVisualizer : MonoBehaviour
         }
         return 0f;
     }
-    
+
     private int GetHighestTeamPerception()
     {
         // This mirrors the logic from TeamPerceptionVisualizer
         int maxPerception = 0;
         List<TeamCharacter> teamMembers = GetCurrentTeamMembers();
-        
+
         if (teamMembers == null || teamMembers.Count == 0)
             return 0;
-        
+
         foreach (TeamCharacter character in teamMembers)
         {
             if (character != null)
@@ -227,16 +227,16 @@ public class EventMarkerVisualizer : MonoBehaviour
                 }
             }
         }
-        
+
         return maxPerception;
     }
-    
+
     private List<TeamCharacter> GetCurrentTeamMembers()
     {
         // Use the same logic as TeamPerceptionVisualizer
         var teamSelectScript = FindFirstObjectByType<MainTeamSelectScript>();
         var gameStateManager = FindFirstObjectByType<GameStateManager>();
-        
+
         // Try to get team from MainTeamSelectScript first
         if (teamSelectScript != null)
         {
@@ -246,7 +246,7 @@ public class EventMarkerVisualizer : MonoBehaviour
                 return configuredCharacters;
             }
         }
-        
+
         // Fall back to GameStateManager
         if (gameStateManager != null && gameStateManager.savedTeam != null)
         {
@@ -256,13 +256,13 @@ public class EventMarkerVisualizer : MonoBehaviour
                 if (character != null)
                     savedTeamList.Add(character);
             }
-            
+
             if (savedTeamList.Count > 0)
             {
                 return savedTeamList;
             }
         }
-        
+
         // Final fallback: Load from PlayerPrefs
         var playerPrefTeam = new List<TeamCharacter>();
         for (int i = 0; i < 4; i++)
@@ -279,39 +279,39 @@ public class EventMarkerVisualizer : MonoBehaviour
                 character.wisdom = PlayerPrefs.GetInt(prefix + "Wisdom", 10);
                 character.perception = PlayerPrefs.GetInt(prefix + "Perception", 10);
                 character.gold = PlayerPrefs.GetInt(prefix + "Gold", 25);
-                
+
                 playerPrefTeam.Add(character);
             }
         }
-        
+
         return playerPrefTeam.Count > 0 ? playerPrefTeam : null;
     }
-    
+
     private Vector2Int FindValidMarkerPosition(Vector2Int teamPosition, float perceptionRange)
     {
         int maxAttempts = 20;
-        
+
         for (int i = 0; i < maxAttempts; i++)
         {
             // Generate random position within perception range
             float angle = Random.Range(0f, 360f) * Mathf.Deg2Rad;
             float distance = Random.Range(minDistanceFromTeam, Mathf.Min(maxDistanceFromTeam, perceptionRange));
-            
+
             Vector2Int candidatePosition = new Vector2Int(
                 teamPosition.x + Mathf.RoundToInt(Mathf.Cos(angle) * distance),
                 teamPosition.y + Mathf.RoundToInt(Mathf.Sin(angle) * distance)
             );
-            
+
             // Check if position is valid
             if (IsValidMarkerPosition(candidatePosition))
             {
                 return candidatePosition;
             }
         }
-        
+
         return Vector2Int.zero; // Failed to find valid position
     }
-    
+
     private bool IsValidMarkerPosition(Vector2Int position)
     {
         // Check if position is already occupied
@@ -319,20 +319,20 @@ public class EventMarkerVisualizer : MonoBehaviour
         {
             return false;
         }
-        
+
         // Check if position is valid on the map
         if (mapMaker != null && mapMaker.GetMapData() != null)
         {
             return mapMaker.GetMapData().IsValidPosition(position.x, position.y);
         }
-        
+
         return true; // Default to valid if no map check available
     }
-    
-    private GameObject CreateMarkerObject(Vector2Int position)
+
+    private GameObject CreateMarkerObject(Vector2Int position, TravelEvent travelEvent)
     {
         GameObject markerObject;
-        
+
         if (eventMarkerPrefab != null)
         {
             markerObject = Instantiate(eventMarkerPrefab);
@@ -342,7 +342,7 @@ public class EventMarkerVisualizer : MonoBehaviour
             // Create simple sphere marker
             markerObject = GameObject.CreatePrimitive(PrimitiveType.Sphere);
             markerObject.transform.localScale = Vector3.one * markerSize;
-            
+
             // Remove collider to avoid interference
             var collider = markerObject.GetComponent<Collider>();
             if (collider != null)
@@ -350,22 +350,39 @@ public class EventMarkerVisualizer : MonoBehaviour
                 DestroyImmediate(collider);
             }
         }
-        
+
         // Position the marker
         Vector3 worldPosition = GetWorldPosition(position);
         markerObject.transform.position = worldPosition;
-        
+
         // Apply material and color
         ApplyMarkerMaterial(markerObject);
-        
+
+        // Add tooltip component for hover information using reflection (compilation-safe)
+        var tooltipType = System.Type.GetType("EventMarkerTooltip");
+        if (tooltipType != null)
+        {
+            var tooltip = markerObject.AddComponent(tooltipType) as MonoBehaviour;
+            if (tooltip != null)
+            {
+                // Use reflection to call Initialize method
+                var initMethod = tooltipType.GetMethod("Initialize");
+                initMethod?.Invoke(tooltip, new object[] { travelEvent });
+            }
+        }
+        else if (showDebugInfo)
+        {
+            Debug.Log("EventMarkerTooltip component not found - tooltips will not be available");
+        }
+
         // Set name and tag
         markerObject.name = "EventMarker";
         // Use default "Untagged" tag to avoid tag definition errors
         markerObject.tag = "Untagged";
-        
+
         return markerObject;
     }
-    
+
     private Vector3 GetWorldPosition(Vector2Int mapPosition)
     {
         // Convert map coordinates to world position
@@ -374,19 +391,19 @@ public class EventMarkerVisualizer : MonoBehaviour
         {
             tileSize = mapMaker.mapVisualizer.tileSize;
         }
-        
+
         return new Vector3(
             mapPosition.x * tileSize,
             markerHeight,
             mapPosition.y * tileSize
         );
     }
-    
+
     private void ApplyMarkerMaterial(GameObject markerObject)
     {
         Renderer renderer = markerObject.GetComponent<Renderer>();
         if (renderer == null) return;
-        
+
         if (eventMarkerMaterial != null)
         {
             renderer.material = eventMarkerMaterial;
@@ -399,39 +416,39 @@ public class EventMarkerVisualizer : MonoBehaviour
             {
                 material = new Material(Shader.Find("Standard"));
             }
-            
+
             material.color = eventMarkerColor;
-            
+
             // Add emission for visibility
             if (material.HasProperty("_EmissionColor"))
             {
                 material.EnableKeyword("_EMISSION");
                 material.SetColor("_EmissionColor", eventMarkerColor * 0.5f);
             }
-            
+
             renderer.material = material;
         }
     }
-    
+
     private void CleanupExpiredMarkers()
     {
-        var markersToRemove = activeMarkers.Where(m => 
-            !m.isActive || 
-            m.gameObject == null || 
+        var markersToRemove = activeMarkers.Where(m =>
+            !m.isActive ||
+            m.gameObject == null ||
             (Time.time - m.spawnTime) > markerLifetime
         ).ToList();
-        
+
         foreach (var marker in markersToRemove)
         {
             RemoveMarker(marker);
         }
     }
-    
+
     private void UpdateMarkerPulsing()
     {
         float pulseValue = (Mathf.Sin(Time.time * pulseSpeed) + 1f) * 0.5f; // 0 to 1
         float scale = 1f + (pulseValue * pulseIntensity);
-        
+
         foreach (var marker in activeMarkers)
         {
             if (marker.isActive && marker.gameObject != null)
@@ -440,19 +457,19 @@ public class EventMarkerVisualizer : MonoBehaviour
             }
         }
     }
-    
+
     private void RemoveMarker(EventMarker marker)
     {
         if (marker.gameObject != null)
         {
             DestroyImmediate(marker.gameObject);
         }
-        
+
         activeMarkers.Remove(marker);
         occupiedPositions.Remove(marker.position);
         marker.isActive = false;
     }
-    
+
     /// <summary>
     /// Gets the number of currently active event markers
     /// </summary>
@@ -460,7 +477,7 @@ public class EventMarkerVisualizer : MonoBehaviour
     {
         return activeMarkers.Count(m => m.isActive);
     }
-    
+
     /// <summary>
     /// Debug method to show all active markers
     /// </summary>

+ 331 - 0
Assets/Scripts/UI/EventMarkerTooltip.cs

@@ -0,0 +1,331 @@
+using UnityEngine;
+using UnityEngine.UI;
+using TMPro;
+
+/// <summary>
+/// Handles mouse hover tooltips for event markers
+/// Shows event information when hovering over red event markers
+/// </summary>
+public class EventMarkerTooltip : MonoBehaviour
+{
+    [Header("Tooltip Settings")]
+    [SerializeField] private GameObject tooltipPrefab;
+    [SerializeField] private Canvas tooltipCanvas;
+    [SerializeField] private float showDelay = 0.5f;
+    [SerializeField] private Vector2 tooltipOffset = new Vector2(10f, 10f);
+
+    // References
+    private TravelEvent associatedEvent;
+    private GameObject tooltipInstance;
+    private Camera mainCamera;
+    private bool isHovering = false;
+    private float hoverStartTime;
+
+    // Static tooltip management (only one tooltip at a time)
+    private static EventMarkerTooltip currentTooltipShowing;
+
+    void Start()
+    {
+        mainCamera = Camera.main;
+        if (mainCamera == null)
+            mainCamera = FindFirstObjectByType<Camera>();
+
+        // Find or create tooltip canvas
+        SetupTooltipCanvas();
+    }
+
+    void Update()
+    {
+        // Handle mouse hover detection
+        if (isHovering && tooltipInstance == null && Time.time - hoverStartTime >= showDelay)
+        {
+            ShowTooltip();
+        }
+
+        // Update tooltip position if showing
+        if (tooltipInstance != null && isHovering)
+        {
+            UpdateTooltipPosition();
+        }
+    }
+
+    /// <summary>
+    /// Initialize this tooltip with event data
+    /// </summary>
+    /// <param name="travelEvent">The event this marker represents</param>
+    public void Initialize(TravelEvent travelEvent)
+    {
+        associatedEvent = travelEvent;
+
+        // Add collider for mouse detection if not present
+        if (GetComponent<Collider>() == null)
+        {
+            var collider = gameObject.AddComponent<SphereCollider>();
+            collider.radius = 1.5f; // Larger area for easier hovering when zoomed out
+            collider.isTrigger = true;
+        }
+    }
+
+    void OnMouseEnter()
+    {
+        if (associatedEvent == null) return;
+
+        isHovering = true;
+        hoverStartTime = Time.time;
+
+        // Hide any other tooltips
+        if (currentTooltipShowing != null && currentTooltipShowing != this)
+        {
+            currentTooltipShowing.HideTooltip();
+        }
+    }
+
+    void OnMouseExit()
+    {
+        isHovering = false;
+        HideTooltip();
+    }
+
+    void OnDestroy()
+    {
+        HideTooltip();
+    }
+
+    private void SetupTooltipCanvas()
+    {
+        if (tooltipCanvas == null)
+        {
+            // Find existing UI canvas or create one
+            tooltipCanvas = FindFirstObjectByType<Canvas>();
+
+            if (tooltipCanvas == null)
+            {
+                // Create a new canvas for tooltips
+                GameObject canvasObject = new GameObject("EventTooltipCanvas");
+                tooltipCanvas = canvasObject.AddComponent<Canvas>();
+                tooltipCanvas.renderMode = RenderMode.ScreenSpaceOverlay;
+                tooltipCanvas.sortingOrder = 1000; // High sorting order to appear on top
+
+                // Add Canvas Scaler for responsive UI
+                var scaler = canvasObject.AddComponent<CanvasScaler>();
+                scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
+                scaler.referenceResolution = new Vector2(1920, 1080);
+
+                // Add GraphicRaycaster for UI interaction
+                canvasObject.AddComponent<GraphicRaycaster>();
+            }
+        }
+    }
+
+    private void ShowTooltip()
+    {
+        if (tooltipInstance != null || associatedEvent == null) return;
+
+        // Create tooltip from prefab or default
+        if (tooltipPrefab != null)
+        {
+            tooltipInstance = Instantiate(tooltipPrefab, tooltipCanvas.transform);
+        }
+        else
+        {
+            tooltipInstance = CreateDefaultTooltip();
+        }
+
+        // Set tooltip content
+        SetTooltipContent();
+
+        // Position tooltip
+        UpdateTooltipPosition();
+
+        currentTooltipShowing = this;
+    }
+
+    private GameObject CreateDefaultTooltip()
+    {
+        // Create default tooltip UI
+        GameObject tooltip = new GameObject("EventTooltip");
+        tooltip.transform.SetParent(tooltipCanvas.transform, false);
+
+        // Add background image
+        var bgImage = tooltip.AddComponent<Image>();
+        bgImage.color = new Color(0.05f, 0.05f, 0.05f, 0.95f); // Darker, more opaque background for better readability
+
+        // Add subtle border/outline effect
+        var outline = tooltip.AddComponent<Outline>();
+        outline.effectColor = new Color(0.3f, 0.3f, 0.3f, 1f);
+        outline.effectDistance = new Vector2(1f, 1f);
+
+        // Add content layout
+        var layoutGroup = tooltip.AddComponent<VerticalLayoutGroup>();
+        layoutGroup.padding = new RectOffset(15, 15, 12, 12); // More padding for better spacing
+        layoutGroup.spacing = 8f; // More spacing between elements
+        layoutGroup.childAlignment = TextAnchor.MiddleLeft;
+        layoutGroup.childControlWidth = true;
+        layoutGroup.childControlHeight = true;
+        layoutGroup.childForceExpandWidth = false;
+        layoutGroup.childForceExpandHeight = false;
+
+        // Add content size fitter
+        var sizeFitter = tooltip.AddComponent<ContentSizeFitter>();
+        sizeFitter.horizontalFit = ContentSizeFitter.FitMode.PreferredSize;
+        sizeFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
+
+        // Create title text
+        GameObject titleObject = new GameObject("Title");
+        titleObject.transform.SetParent(tooltip.transform, false);
+        var titleText = titleObject.AddComponent<TextMeshProUGUI>();
+        titleText.text = "Event Title";
+        titleText.fontSize = 18f; // Larger font for better readability
+        titleText.fontWeight = FontWeight.Bold;
+        titleText.color = Color.white;
+
+        // Create description text
+        GameObject descObject = new GameObject("Description");
+        descObject.transform.SetParent(tooltip.transform, false);
+        var descText = descObject.AddComponent<TextMeshProUGUI>();
+        descText.text = "Event description...";
+        descText.fontSize = 14f; // Larger font for better readability
+        descText.color = new Color(0.9f, 0.9f, 0.9f, 1f); // Lighter gray for better contrast
+        descText.textWrappingMode = TMPro.TextWrappingModes.Normal; // Updated property name
+
+        // Set max width for text wrapping
+        var titleRect = titleText.GetComponent<RectTransform>();
+        titleRect.sizeDelta = new Vector2(350f, 0f); // Wider tooltip for better text layout
+
+        var descRect = descText.GetComponent<RectTransform>();
+        descRect.sizeDelta = new Vector2(350f, 0f);
+
+        return tooltip;
+    }
+
+    private void SetTooltipContent()
+    {
+        if (tooltipInstance == null || associatedEvent == null) return;
+
+        // Find text components
+        var textComponents = tooltipInstance.GetComponentsInChildren<TextMeshProUGUI>();
+        if (textComponents.Length >= 2)
+        {
+            // Set title
+            textComponents[0].text = GetEventTitle();
+
+            // Set description
+            textComponents[1].text = GetEventDescription();
+        }
+        else
+        {
+            // Fallback for different tooltip structures
+            var singleText = tooltipInstance.GetComponentInChildren<TextMeshProUGUI>();
+            if (singleText != null)
+            {
+                singleText.text = $"{GetEventTitle()}\n{GetEventDescription()}";
+            }
+        }
+    }
+
+    private string GetEventTitle()
+    {
+        if (associatedEvent == null) return "Unknown Event";
+
+        // Get event type icon
+        string icon = associatedEvent.eventType switch
+        {
+            EventType.Combat => "⚔️",
+            EventType.Trading => "💰",
+            EventType.Discovery => "🔍",
+            EventType.Social => "💬",
+            EventType.Hazard => "⚠️",
+            EventType.Rest => "🏕️",
+            EventType.Mystery => "❓",
+            _ => "📍"
+        };
+
+        return $"{icon} {associatedEvent.eventName}";
+    }
+
+    private string GetEventDescription()
+    {
+        if (associatedEvent == null) return "No information available.";
+
+        string description = associatedEvent.eventDescription;
+
+        // Add event type information
+        string typeInfo = associatedEvent.eventType switch
+        {
+            EventType.Combat => "Combat encounter - approach to engage",
+            EventType.Trading => "Trading opportunity - approach to trade",
+            EventType.Discovery => "Something to discover - approach to investigate",
+            EventType.Social => "Social encounter - approach to interact",
+            EventType.Hazard => "Environmental hazard - approach with caution",
+            EventType.Rest => "Rest opportunity - approach to recover",
+            EventType.Mystery => "Mysterious event - approach to uncover",
+            _ => "Unknown event type"
+        };
+
+        // Add rarity information
+        string rarityInfo = associatedEvent.rarity switch
+        {
+            EventRarity.VeryCommon => "Very Common",
+            EventRarity.Common => "Common",
+            EventRarity.Uncommon => "Uncommon",
+            EventRarity.Rare => "Rare",
+            EventRarity.VeryRare => "Very Rare",
+            EventRarity.Legendary => "Legendary",
+            _ => "Unknown"
+        };
+
+        return $"{description}\n\nType: {typeInfo}\nRarity: {rarityInfo}\n\n<i>Move close to trigger this event</i>";
+    }
+
+    private void UpdateTooltipPosition()
+    {
+        if (tooltipInstance == null || mainCamera == null) return;
+
+        // Get mouse position
+        Vector3 mousePos = Input.mousePosition;
+
+        // Add offset
+        mousePos.x += tooltipOffset.x;
+        mousePos.y += tooltipOffset.y;
+
+        // Ensure tooltip stays on screen
+        RectTransform tooltipRect = tooltipInstance.GetComponent<RectTransform>();
+        if (tooltipRect != null)
+        {
+            // Get screen bounds
+            float screenWidth = Screen.width;
+            float screenHeight = Screen.height;
+
+            // Get tooltip size
+            Vector2 tooltipSize = tooltipRect.sizeDelta;
+
+            // Adjust position to keep tooltip on screen
+            if (mousePos.x + tooltipSize.x > screenWidth)
+            {
+                mousePos.x = screenWidth - tooltipSize.x - 10f;
+            }
+
+            if (mousePos.y + tooltipSize.y > screenHeight)
+            {
+                mousePos.y = mousePos.y - tooltipSize.y - tooltipOffset.y * 2;
+            }
+
+            // Set position
+            tooltipRect.position = mousePos;
+        }
+    }
+
+    private void HideTooltip()
+    {
+        if (tooltipInstance != null)
+        {
+            Destroy(tooltipInstance);
+            tooltipInstance = null;
+        }
+
+        if (currentTooltipShowing == this)
+        {
+            currentTooltipShowing = null;
+        }
+    }
+}

+ 143 - 0
EVENT_MARKER_TOOLTIPS_GUIDE.md

@@ -0,0 +1,143 @@
+# Event Marker Hover Tooltips - Implementation Guide
+
+## 🎯 New Feature Added
+
+**Mouse hover tooltips** now appear when you hover over red event markers! Players can now see detailed information about events before deciding whether to approach them.
+
+## 📋 What the Tooltips Show
+
+### **Event Information**
+- **📍 Event Name** with type icon (⚔️ Combat, 💰 Trading, etc.)
+- **📖 Event Description** - what the event is about
+- **🎭 Event Type** - Combat, Trading, Discovery, Social, Hazard, Rest, Mystery
+- **⭐ Rarity Level** - Common, Uncommon, Rare, etc.
+- **💡 Interaction Hint** - "Move close to trigger this event"
+
+### **Event Type Icons**
+- ⚔️ **Combat** - Battle encounters
+- 💰 **Trading** - Merchant opportunities  
+- 🔍 **Discovery** - Hidden treasures/resources
+- 💬 **Social** - NPC interactions
+- ⚠️ **Hazard** - Environmental dangers
+- 🏕️ **Rest** - Recovery opportunities
+- ❓ **Mystery** - Strange occurrences
+
+## 🎮 How It Works
+
+### **Player Experience**
+1. **Red markers** appear within perception range as before
+2. **Hover mouse** over any red marker
+3. **Tooltip appears** after 0.5 second delay
+4. **Move mouse away** to hide tooltip
+5. **Make informed decision** whether to approach or avoid
+
+### **Smart Tooltip Behavior**
+- **Delay**: 0.5 seconds to avoid accidental popups
+- **Single tooltip**: Only one tooltip shows at a time
+- **Screen bounds**: Tooltips stay within screen boundaries
+- **Auto-positioning**: Tooltips reposition to avoid screen edges
+
+## 🔧 Technical Implementation
+
+### **New Components**
+- **`EventMarkerTooltip.cs`** - Handles mouse detection and tooltip display
+- **Auto-added** to every event marker automatically
+- **Canvas creation** - Creates UI canvas if none exists
+
+### **Integration**
+- **`EventMarkerVisualizer`** - Enhanced to add tooltip components
+- **Reflection-based** - Safe component addition for compilation
+- **No dependencies** - Works with existing UI systems
+
+## ⚙️ Customization Options
+
+### **Tooltip Timing**
+```csharp
+[SerializeField] private float showDelay = 0.5f; // Hover delay before showing
+```
+
+### **Tooltip Positioning**
+```csharp
+[SerializeField] private Vector2 tooltipOffset = new Vector2(10f, 10f); // Offset from mouse
+```
+
+### **Visual Styling**
+- **Background**: Dark semi-transparent (0.1, 0.1, 0.1, 0.9)
+- **Title**: White, bold, 16pt font
+- **Description**: Gray, regular, 12pt font
+- **Max Width**: 300px with word wrapping
+
+## 🎯 Strategic Benefits
+
+### **Informed Decision Making**
+- **Know before you go** - See what you're getting into
+- **Risk assessment** - Evaluate danger vs reward
+- **Resource planning** - Prepare for specific event types
+
+### **Enhanced Immersion**
+- **Realistic scouting** - "Spotting" events feels more authentic
+- **Environmental storytelling** - Event descriptions add lore
+- **Visual feedback** - Icons quickly communicate event types
+
+### **Improved UX**
+- **No guesswork** - Clear information about opportunities
+- **Better strategy** - Plan routes around preferred/avoided events
+- **Accessibility** - Visual information supports decision-making
+
+## 🧪 Testing the Feature
+
+### **Basic Testing**
+1. Start travel scene with perception system active
+2. Move around until red markers appear
+3. Hover mouse over red markers
+4. Verify tooltips appear with correct information
+5. Test tooltip positioning near screen edges
+
+### **Event Type Testing**
+Create different event types to test tooltip content:
+- Combat events show ⚔️ and battle information
+- Trading events show 💰 and merchant details
+- Discovery events show 🔍 and treasure hints
+
+### **Edge Case Testing**
+- Hover near screen edges (tooltips should reposition)
+- Hover on multiple markers quickly (only one tooltip shows)
+- Move mouse rapidly (tooltips should hide properly)
+
+## 🐛 Troubleshooting
+
+### **Tooltips Not Appearing**
+- Check that markers have colliders (auto-added by tooltip component)
+- Verify EventMarkerTooltip component is on marker objects
+- Check console for "EventMarkerTooltip component not found" message
+
+### **Tooltips Appear Off-Screen**
+- Check tooltip positioning logic in UpdateTooltipPosition
+- Verify screen resolution detection
+- Adjust tooltipOffset values if needed
+
+### **Multiple Tooltips Showing**
+- Check currentTooltipShowing static management
+- Verify OnMouseExit calls are working properly
+- Look for tooltip cleanup in OnDestroy
+
+## 🚀 Future Enhancements
+
+### **Visual Improvements**
+- **Custom tooltip prefabs** for different event types
+- **Animated tooltips** with fade in/out effects
+- **Rich text formatting** with colors and styles
+
+### **Content Enhancements**
+- **Reward previews** - Show potential loot/XP
+- **Risk indicators** - Difficulty ratings
+- **Contextual information** - Team status relevance
+
+### **Interaction Improvements**
+- **Click to approach** - Direct interaction from tooltip
+- **Keyboard shortcuts** - Quick navigation to events
+- **Bookmark events** - Mark interesting events for later
+
+## ✅ Implementation Complete
+
+The hover tooltip system is now fully integrated with your spottable event system! Players can hover over red event markers to see detailed information, making informed decisions about which encounters to pursue. This enhances the strategic depth of the perception-based travel system while maintaining smooth gameplay flow.