using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using TheIsland.Models;
using TheIsland.Network;
namespace TheIsland.Agents
{
///
/// Controller for individual Agent prefabs.
/// Handles UI updates, animations, and speech bubbles.
///
public class AgentController : MonoBehaviour
{
#region UI References
[Header("UI Elements")]
[SerializeField] private TextMeshProUGUI nameLabel;
[SerializeField] private TextMeshProUGUI personalityLabel;
[SerializeField] private Slider hpBar;
[SerializeField] private Slider energyBar;
[SerializeField] private Image hpFill;
[SerializeField] private Image energyFill;
[Header("Speech Bubble")]
[SerializeField] private GameObject speechBubble;
[SerializeField] private TextMeshProUGUI speechText;
[SerializeField] private float speechDuration = 5f;
[Header("Visual Feedback")]
[SerializeField] private Renderer characterRenderer;
[SerializeField] private Color aliveColor = Color.white;
[SerializeField] private Color deadColor = new Color(0.3f, 0.3f, 0.3f, 1f);
[SerializeField] private GameObject deathOverlay;
[Header("Animation")]
[SerializeField] private Animator animator;
#endregion
#region Private Fields
private int _agentId;
private AgentData _currentData;
private Coroutine _speechCoroutine;
private Material _characterMaterial;
#endregion
#region Properties
public int AgentId => _agentId;
public AgentData CurrentData => _currentData;
public bool IsAlive => _currentData?.IsAlive ?? false;
#endregion
#region Initialization
private void Awake()
{
// Cache material for color changes
if (characterRenderer != null)
{
_characterMaterial = characterRenderer.material;
}
// Hide speech bubble initially
if (speechBubble != null)
{
speechBubble.SetActive(false);
}
// Hide death overlay initially
if (deathOverlay != null)
{
deathOverlay.SetActive(false);
}
}
///
/// Initialize the agent with server data.
/// Called once when the agent is first spawned.
///
public void Initialize(AgentData data)
{
_agentId = data.id;
_currentData = data;
// Set static labels
if (nameLabel != null)
{
nameLabel.text = data.name;
}
if (personalityLabel != null)
{
personalityLabel.text = data.personality;
}
// Set game object name for debugging
gameObject.name = $"Agent_{data.id}_{data.name}";
// Apply initial stats
UpdateStats(data);
Debug.Log($"[AgentController] Initialized {data.name} (ID: {data.id})");
}
#endregion
#region Stats Update
///
/// Update the agent's visual state based on server data.
///
public void UpdateStats(AgentData data)
{
_currentData = data;
// Update HP bar
if (hpBar != null)
{
hpBar.value = data.hp / 100f;
UpdateBarColor(hpFill, data.hp);
}
// Update Energy bar
if (energyBar != null)
{
energyBar.value = data.energy / 100f;
UpdateBarColor(energyFill, data.energy, isEnergy: true);
}
// Handle death state
if (!data.IsAlive)
{
OnDeath();
}
else
{
OnAlive();
}
// Trigger animation based on energy level
UpdateAnimation();
}
private void UpdateBarColor(Image fillImage, int value, bool isEnergy = false)
{
if (fillImage == null) return;
if (isEnergy)
{
// Energy: Yellow to Orange gradient
fillImage.color = Color.Lerp(
new Color(1f, 0.5f, 0f), // Orange (low)
new Color(1f, 0.8f, 0f), // Yellow (high)
value / 100f
);
}
else
{
// HP: Red to Green gradient
fillImage.color = Color.Lerp(
new Color(1f, 0.2f, 0.2f), // Red (low)
new Color(0.2f, 1f, 0.2f), // Green (high)
value / 100f
);
}
}
private void UpdateAnimation()
{
if (animator == null) return;
if (_currentData == null) return;
// Set animator parameters based on state
animator.SetBool("IsAlive", _currentData.IsAlive);
animator.SetFloat("Energy", _currentData.energy / 100f);
animator.SetFloat("HP", _currentData.hp / 100f);
// Trigger low energy animation if starving
if (_currentData.energy <= 20 && _currentData.IsAlive)
{
animator.SetBool("IsStarving", true);
}
else
{
animator.SetBool("IsStarving", false);
}
}
#endregion
#region Death Handling
private void OnDeath()
{
Debug.Log($"[AgentController] {_currentData.name} has died!");
// Change character color to gray
if (_characterMaterial != null)
{
_characterMaterial.color = deadColor;
}
// Show death overlay
if (deathOverlay != null)
{
deathOverlay.SetActive(true);
}
// Trigger death animation
if (animator != null)
{
animator.SetTrigger("Die");
}
// Hide speech bubble
HideSpeech();
}
private void OnAlive()
{
// Restore character color
if (_characterMaterial != null)
{
_characterMaterial.color = aliveColor;
}
// Hide death overlay
if (deathOverlay != null)
{
deathOverlay.SetActive(false);
}
}
#endregion
#region Speech Bubble
///
/// Show speech bubble with text from LLM.
/// Auto-hides after speechDuration seconds.
///
public void ShowSpeech(string text)
{
if (speechBubble == null || speechText == null)
{
Debug.LogWarning($"[AgentController] Speech bubble not configured for {_currentData?.name}");
return;
}
// Don't show speech for dead agents
if (!IsAlive)
{
return;
}
// Stop existing speech coroutine if any
if (_speechCoroutine != null)
{
StopCoroutine(_speechCoroutine);
}
// Set text and show bubble
speechText.text = text;
speechBubble.SetActive(true);
// Start auto-hide coroutine
_speechCoroutine = StartCoroutine(HideSpeechAfterDelay());
Debug.Log($"[AgentController] {_currentData?.name} says: \"{text}\"");
}
private IEnumerator HideSpeechAfterDelay()
{
yield return new WaitForSeconds(speechDuration);
HideSpeech();
}
public void HideSpeech()
{
if (speechBubble != null)
{
speechBubble.SetActive(false);
}
if (_speechCoroutine != null)
{
StopCoroutine(_speechCoroutine);
_speechCoroutine = null;
}
}
#endregion
#region Interaction
///
/// Called when player clicks/taps on this agent.
///
public void OnClick()
{
if (!IsAlive)
{
Debug.Log($"[AgentController] Cannot interact with dead agent: {_currentData?.name}");
return;
}
// Feed the agent
NetworkManager.Instance.FeedAgent(_currentData.name);
}
private void OnMouseDown()
{
OnClick();
}
#endregion
#region Cleanup
private void OnDestroy()
{
// Clean up material instance
if (_characterMaterial != null)
{
Destroy(_characterMaterial);
}
}
#endregion
}
}