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)
[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();
gameStateManager = FindFirstObjectByType();
teamSelectScript = FindFirstObjectByType();
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());
}
///
/// 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()
{
// 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;
}
///
/// 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)
{
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);
}
}
}
///
/// 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 ===");
}
}