| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500 |
- using UnityEngine;
- using System.Collections.Generic;
- /// <summary>
- /// 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.
- /// </summary>
- 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)
- [SerializeField] private bool isVisualizationEnabled = true; // Can be controlled by UI
- [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<SimpleTeamPlacement>();
- gameStateManager = FindFirstObjectByType<GameStateManager>();
- teamSelectScript = FindFirstObjectByType<MainTeamSelectScript>();
- if (teamPlacement == null)
- {
- Debug.LogError("TeamPerceptionVisualizer: SimpleTeamPlacement component not found!");
- return;
- }
- // Create the perception circle
- CreatePerceptionCircle();
- // Load visualization state from PlayerPrefs
- isVisualizationEnabled = PlayerPrefs.GetInt("ShowPerceptionVisualization", 1) == 1;
- // Initial position update with slight delay to ensure team marker is loaded
- StartCoroutine(DelayedInitialUpdate());
- }
- /// <summary>
- /// Performs initial updates with a small delay to ensure all components are loaded
- /// </summary>
- private System.Collections.IEnumerator DelayedInitialUpdate()
- {
- yield return new WaitForEndOfFrame();
- yield return new WaitForSeconds(0.1f); // Small delay
- UpdateCirclePosition();
- UpdatePerceptionVisualization();
- }
- void Update()
- {
- // Early exit if visualization is disabled
- if (!isVisualizationEnabled)
- {
- // Hide the circle if it's currently visible
- if (perceptionCircleObject != null && perceptionCircleObject.activeSelf)
- {
- perceptionCircleObject.SetActive(false);
- }
- return;
- }
- // Always update position to follow team marker
- UpdateCirclePosition();
- if (!updatePerceptionInRealTime || Time.time - lastUpdateTime < updateInterval)
- return;
- UpdatePerceptionVisualization();
- lastUpdateTime = Time.time;
- }
- /// <summary>
- /// Creates the visual circle GameObject and LineRenderer component
- /// </summary>
- private void CreatePerceptionCircle()
- {
- // Create the circle GameObject
- perceptionCircleObject = new GameObject("PerceptionCircle");
- perceptionCircleObject.transform.SetParent(this.transform);
- // Add and configure LineRenderer
- circleRenderer = perceptionCircleObject.AddComponent<LineRenderer>();
- 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);
- }
- /// <summary>
- /// Updates the circle position to follow the team marker
- /// </summary>
- 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");
- }
- }
- /// <summary>
- /// Updates the perception visualization based on current team data
- /// </summary>
- 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}");
- }
- }
- /// <summary>
- /// Gets the highest perception value among all team members
- /// </summary>
- /// <returns>The highest perception value, or 0 if no team data available</returns>
- private int GetHighestTeamPerception()
- {
- int maxPerception = 0;
- List<TeamCharacter> 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;
- }
- /// <summary>
- /// Gets the current team members from available sources
- /// </summary>
- /// <returns>List of team members or null if not available</returns>
- private List<TeamCharacter> 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<TeamCharacter>();
- 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<TeamCharacter>();
- 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;
- }
- /// <summary>
- /// Gets the world position of the team marker
- /// </summary>
- /// <returns>World position of the team marker, or Vector3.zero if not found</returns>
- 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;
- }
- /// <summary>
- /// Call this method when team composition or equipment changes
- /// </summary>
- public void ForceUpdatePerception()
- {
- lastKnownMaxPerception = -1; // Force update on next check
- UpdatePerceptionVisualization();
- }
- /// <summary>
- /// Enables or disables the perception visualization
- /// </summary>
- /// <param name="enabled">Whether to show the perception circle</param>
- public void SetPerceptionVisualizationEnabled(bool enabled)
- {
- isVisualizationEnabled = enabled;
- if (perceptionCircleObject != null)
- {
- if (enabled)
- {
- // If enabling, trigger an update to show the circle if appropriate
- UpdatePerceptionVisualization();
- }
- else
- {
- // If disabling, hide the circle immediately
- perceptionCircleObject.SetActive(false);
- }
- }
- }
- /// <summary>
- /// Updates the circle color
- /// </summary>
- /// <param name="newColor">New color for the perception circle</param>
- public void SetPerceptionColor(Color newColor)
- {
- perceptionColor = newColor;
- if (circleRenderer != null)
- {
- circleRenderer.startColor = newColor;
- circleRenderer.endColor = newColor;
- if (circleRenderer.material != null)
- {
- circleRenderer.material.color = newColor;
- }
- }
- }
- /// <summary>
- /// Updates the perception multiplier (how large the circle is relative to perception value)
- /// </summary>
- /// <param name="multiplier">New multiplier value</param>
- public void SetPerceptionMultiplier(float multiplier)
- {
- perceptionMultiplier = multiplier;
- ForceUpdatePerception(); // Update circle with new size
- }
- void OnDestroy()
- {
- if (Instance == this)
- {
- Instance = null;
- }
- }
- /// <summary>
- /// Debug method to show current perception info
- /// </summary>
- [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 ===");
- }
- /// <summary>
- /// Test method to force immediate visualization update
- /// </summary>
- [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 ===");
- }
- }
|