using UnityEngine; using System.Collections.Generic; /// /// Visualization component that displays a semi-transparent purple circle around the team marker /// to represent the team's collective perception range. The radius is based on the highest /// perception value among team members. /// public class TeamPerceptionVisualizer : MonoBehaviour { [Header("Perception Circle Settings")] [SerializeField] private Material perceptionCircleMaterial; [SerializeField] private Color perceptionColor = new Color(0.5f, 0f, 1f, 0.5f); // 50% transparent purple [SerializeField] private float perceptionMultiplier = 1.0f; // Converts perception value to world units [SerializeField] private int circleSegments = 64; // Circle resolution [SerializeField] private float heightOffset = 0.1f; // How high above ground to place the circle [Header("Dynamic Updates")] [SerializeField] private bool updatePerceptionInRealTime = true; [SerializeField] private float updateInterval = 1f; // How often to check for perception changes (in seconds) [Header("Debug")] [SerializeField] private bool showDebugInfo = true; // Private variables private GameObject perceptionCircleObject; private LineRenderer circleRenderer; private SimpleTeamPlacement teamPlacement; private GameStateManager gameStateManager; private MainTeamSelectScript teamSelectScript; private int lastKnownMaxPerception = -1; private float lastUpdateTime = 0f; public static TeamPerceptionVisualizer Instance { get; private set; } void Awake() { if (Instance == null) { Instance = this; } else { Debug.LogWarning("Multiple TeamPerceptionVisualizer instances found. Destroying this one."); Destroy(gameObject); return; } } void Start() { // Find required components teamPlacement = FindFirstObjectByType(); gameStateManager = FindFirstObjectByType(); teamSelectScript = FindFirstObjectByType(); if (teamPlacement == null) { Debug.LogError("TeamPerceptionVisualizer: SimpleTeamPlacement component not found!"); return; } // Create the perception circle CreatePerceptionCircle(); // Initial position update with slight delay to ensure team marker is loaded StartCoroutine(DelayedInitialUpdate()); } /// /// Performs initial updates with a small delay to ensure all components are loaded /// private System.Collections.IEnumerator DelayedInitialUpdate() { yield return new WaitForEndOfFrame(); yield return new WaitForSeconds(0.1f); // Small delay UpdateCirclePosition(); UpdatePerceptionVisualization(); } void Update() { // Always update position to follow team marker UpdateCirclePosition(); if (!updatePerceptionInRealTime || Time.time - lastUpdateTime < updateInterval) return; UpdatePerceptionVisualization(); lastUpdateTime = Time.time; } /// /// Creates the visual circle GameObject and LineRenderer component /// private void CreatePerceptionCircle() { // Create the circle GameObject perceptionCircleObject = new GameObject("PerceptionCircle"); perceptionCircleObject.transform.SetParent(this.transform); // Add and configure LineRenderer circleRenderer = perceptionCircleObject.AddComponent(); circleRenderer.useWorldSpace = false; circleRenderer.loop = true; circleRenderer.startWidth = 0.3f; circleRenderer.endWidth = 0.3f; circleRenderer.positionCount = circleSegments; circleRenderer.startColor = perceptionColor; circleRenderer.endColor = perceptionColor; // Set material if provided, otherwise create a simple one if (perceptionCircleMaterial != null) { circleRenderer.material = perceptionCircleMaterial; } else { // Create a simple material with transparency support Material simpleMaterial = new Material(Shader.Find("Sprites/Default")); simpleMaterial.color = perceptionColor; circleRenderer.material = simpleMaterial; } // Initially hide the circle until we have valid data perceptionCircleObject.SetActive(false); } /// /// Updates the circle position to follow the team marker /// private void UpdateCirclePosition() { if (perceptionCircleObject == null || teamPlacement == null) return; Vector3 teamPosition = GetTeamMarkerPosition(); if (teamPosition != Vector3.zero) { Vector3 newPosition = new Vector3( teamPosition.x, teamPosition.y + heightOffset, teamPosition.z ); // Only move if the position actually changed (to avoid spam) if (Vector3.Distance(perceptionCircleObject.transform.position, newPosition) > 0.1f) { perceptionCircleObject.transform.position = newPosition; if (showDebugInfo) { Debug.Log($"TeamPerceptionVisualizer: Updated circle position to {newPosition}"); } } } else if (showDebugInfo) { Debug.LogWarning("TeamPerceptionVisualizer: Could not find team marker position"); } } /// /// Updates the perception visualization based on current team data /// public void UpdatePerceptionVisualization() { if (circleRenderer == null) return; int maxPerception = GetHighestTeamPerception(); // If no valid perception data, hide the circle if (maxPerception <= 0) { if (perceptionCircleObject.activeSelf) { perceptionCircleObject.SetActive(false); if (showDebugInfo) Debug.Log("TeamPerceptionVisualizer: Hiding perception circle - no valid team data"); } return; } // If perception hasn't changed, no need to update if (maxPerception == lastKnownMaxPerception && perceptionCircleObject.activeSelf) return; lastKnownMaxPerception = maxPerception; // Calculate radius based on perception float radius = maxPerception * perceptionMultiplier; // Generate circle points Vector3[] points = new Vector3[circleSegments]; for (int i = 0; i < circleSegments; i++) { float angle = (float)i / circleSegments * 2f * Mathf.PI; points[i] = new Vector3( Mathf.Cos(angle) * radius, 0f, Mathf.Sin(angle) * radius ); } // Apply points to LineRenderer circleRenderer.positionCount = circleSegments; circleRenderer.SetPositions(points); // Show the circle if (!perceptionCircleObject.activeSelf) { perceptionCircleObject.SetActive(true); } if (showDebugInfo) { Debug.Log($"TeamPerceptionVisualizer: Updated perception circle - Max Perception: {maxPerception}, Radius: {radius:F1}"); } } /// /// Gets the highest perception value among all team members /// /// The highest perception value, or 0 if no team data available private int GetHighestTeamPerception() { int maxPerception = 0; List teamMembers = GetCurrentTeamMembers(); if (teamMembers == null || teamMembers.Count == 0) return 0; foreach (TeamCharacter character in teamMembers) { if (character != null) { int characterPerception = character.FinalPerception; // Use final perception (includes equipment bonuses) if (characterPerception > maxPerception) { maxPerception = characterPerception; } } } return maxPerception; } /// /// Gets the current team members from available sources /// /// List of team members or null if not available private List GetCurrentTeamMembers() { // Try to get team from MainTeamSelectScript first (if in current scene) if (teamSelectScript != null) { var configuredCharacters = teamSelectScript.GetConfiguredCharacters(); if (configuredCharacters != null && configuredCharacters.Count > 0) { if (showDebugInfo) Debug.Log($"TeamPerceptionVisualizer: Found {configuredCharacters.Count} team members from MainTeamSelectScript"); return configuredCharacters; } } // Fall back to GameStateManager saved data if (gameStateManager != null && gameStateManager.savedTeam != null) { var savedTeamList = new List(); foreach (var character in gameStateManager.savedTeam) { if (character != null) savedTeamList.Add(character); } if (savedTeamList.Count > 0) { if (showDebugInfo) Debug.Log($"TeamPerceptionVisualizer: Found {savedTeamList.Count} team members from GameStateManager"); return savedTeamList; } } // Final fallback: Load directly from PlayerPrefs (same as TeamOverviewController) var playerPrefTeam = new List(); for (int i = 0; i < 4; i++) { string prefix = $"Character{i}_"; if (PlayerPrefs.HasKey(prefix + "Exists") && PlayerPrefs.GetInt(prefix + "Exists") == 1) { var character = new TeamCharacter(); character.name = PlayerPrefs.GetString(prefix + "Name", ""); character.isMale = PlayerPrefs.GetInt(prefix + "IsMale", 1) == 1; character.strength = PlayerPrefs.GetInt(prefix + "Strength", 10); character.dexterity = PlayerPrefs.GetInt(prefix + "Dexterity", 10); character.constitution = PlayerPrefs.GetInt(prefix + "Constitution", 10); character.wisdom = PlayerPrefs.GetInt(prefix + "Wisdom", 10); character.perception = PlayerPrefs.GetInt(prefix + "Perception", 10); character.gold = PlayerPrefs.GetInt(prefix + "Gold", 25); playerPrefTeam.Add(character); } } if (playerPrefTeam.Count > 0) { if (showDebugInfo) Debug.Log($"TeamPerceptionVisualizer: Found {playerPrefTeam.Count} team members from PlayerPrefs"); return playerPrefTeam; } if (showDebugInfo) Debug.Log("TeamPerceptionVisualizer: No team members found in any source"); return null; } /// /// Gets the world position of the team marker /// /// World position of the team marker, or Vector3.zero if not found private Vector3 GetTeamMarkerPosition() { if (teamPlacement != null && teamPlacement.TeamMarkerInstance != null) { return teamPlacement.TeamMarkerWorldPosition; } // Fallback: try to find the marker by name or tag var teamMarker = GameObject.FindGameObjectWithTag("Player"); // The marker uses "Player" tag if (teamMarker != null && teamMarker.name == "TeamMarker") { return teamMarker.transform.position; } // Alternative: look for any GameObject with "TeamMarker" in the name var foundMarker = GameObject.Find("TeamMarker"); if (foundMarker != null) { return foundMarker.transform.position; } // If we can't find the marker, return zero return Vector3.zero; } /// /// Call this method when team composition or equipment changes /// public void ForceUpdatePerception() { lastKnownMaxPerception = -1; // Force update on next check UpdatePerceptionVisualization(); } /// /// Enables or disables the perception visualization /// /// Whether to show the perception circle public void SetPerceptionVisualizationEnabled(bool enabled) { if (perceptionCircleObject != null) { perceptionCircleObject.SetActive(enabled && GetHighestTeamPerception() > 0); } } /// /// Updates the circle color /// /// New color for the perception circle public void SetPerceptionColor(Color newColor) { perceptionColor = newColor; if (circleRenderer != null) { circleRenderer.startColor = newColor; circleRenderer.endColor = newColor; if (circleRenderer.material != null) { circleRenderer.material.color = newColor; } } } /// /// Updates the perception multiplier (how large the circle is relative to perception value) /// /// New multiplier value public void SetPerceptionMultiplier(float multiplier) { perceptionMultiplier = multiplier; ForceUpdatePerception(); // Update circle with new size } void OnDestroy() { if (Instance == this) { Instance = null; } } /// /// Debug method to show current perception info /// [ContextMenu("Show Perception Debug Info")] public void ShowPerceptionDebugInfo() { var teamMembers = GetCurrentTeamMembers(); if (teamMembers == null || teamMembers.Count == 0) { Debug.Log("No team members found"); return; } Debug.Log("=== Team Perception Debug Info ==="); Debug.Log($"Team Members Count: {teamMembers.Count}"); int maxPerception = 0; string bestCharacter = ""; foreach (var character in teamMembers) { if (character != null) { int perception = character.FinalPerception; Debug.Log($"- {character.name}: Perception {perception} (Base: {character.perception}, Modifier: {character.perceptionModifier})"); if (perception > maxPerception) { maxPerception = perception; bestCharacter = character.name; } } } Debug.Log($"Highest Perception: {maxPerception} ({bestCharacter})"); Debug.Log($"Circle Radius: {maxPerception * perceptionMultiplier:F1} units"); Debug.Log("=== End Debug Info ==="); } /// /// Test method to force immediate visualization update /// [ContextMenu("Force Update Visualization Now")] public void ForceUpdateVisualizationNow() { Debug.Log("=== Forcing Perception Visualization Update ==="); ForceUpdatePerception(); var teamMembers = GetCurrentTeamMembers(); if (teamMembers != null && teamMembers.Count > 0) { int maxPerception = GetHighestTeamPerception(); Debug.Log($"Found {teamMembers.Count} team members, highest perception: {maxPerception}"); if (perceptionCircleObject != null) { Debug.Log($"Circle active: {perceptionCircleObject.activeSelf}"); Debug.Log($"Circle position: {perceptionCircleObject.transform.position}"); } else { Debug.LogWarning("Perception circle object is null!"); } } else { Debug.LogWarning("No team members found for visualization!"); } Debug.Log("=== End Force Update ==="); } }