|
|
@@ -0,0 +1,474 @@
|
|
|
+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)
|
|
|
+
|
|
|
+ [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();
|
|
|
+
|
|
|
+ // 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()
|
|
|
+ {
|
|
|
+ // 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)
|
|
|
+ {
|
|
|
+ if (perceptionCircleObject != null)
|
|
|
+ {
|
|
|
+ perceptionCircleObject.SetActive(enabled && GetHighestTeamPerception() > 0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <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 ===");
|
|
|
+ }
|
|
|
+}
|