using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
using System;
using TMPro;
///
/// Radial menu (decision wheel) for selecting battle actions
///
public class BattleActionWheel : MonoBehaviour
{
[Header("UI References")]
public Canvas wheelCanvas;
public GameObject wheelBackground;
public GameObject actionButtonPrefab;
public Transform wheelCenter;
[Header("Wheel Settings")]
public float wheelRadius = 100f;
public float buttonSize = 60f;
public float animationDuration = 0.3f;
public AnimationCurve scaleCurve = AnimationCurve.EaseInOut(0f, 0f, 1f, 1f);
[Header("Action Icons")]
public Sprite moveIcon;
public Sprite attackIcon;
public Sprite itemIcon;
public Sprite spellIcon;
public Sprite runAwayIcon;
public Sprite defendIcon;
public Sprite waitIcon;
[Header("Colors")]
public Color defaultColor = Color.white;
public Color hoverColor = Color.yellow;
public Color disabledColor = Color.gray;
private List actionButtons = new List();
private Character currentCharacter;
private bool isVisible = false;
private Vector3 worldPosition;
private Camera mainCamera;
// Events
public event Action OnActionSelected;
public event Action OnWheelClosed;
[System.Serializable]
private class ActionButton
{
public GameObject buttonObject;
public Button button;
public Image icon;
public TextMeshProUGUI label;
public BattleActionType actionType;
public bool isEnabled = true;
public void SetEnabled(bool enabled)
{
isEnabled = enabled;
button.interactable = enabled;
icon.color = enabled ? Color.white : Color.gray;
}
public void SetHighlight(bool highlighted, Color normalColor, Color highlightColor)
{
icon.color = highlighted ? highlightColor : (isEnabled ? normalColor : Color.gray);
}
}
void Awake()
{
mainCamera = Camera.main;
if (wheelCanvas == null)
wheelCanvas = GetComponentInChildren();
if (wheelCanvas != null)
wheelCanvas.worldCamera = mainCamera;
SetVisible(false);
}
void Start()
{
InitializeWheel();
}
void Update()
{
if (isVisible)
{
// Update wheel position to follow character or world position
UpdateWheelPosition();
// Handle input for closing wheel
if (Input.GetKeyDown(KeyCode.Escape) || Input.GetMouseButtonDown(1))
{
HideWheel();
}
}
}
private void InitializeWheel()
{
if (actionButtonPrefab == null)
{
Debug.LogError("BattleActionWheel: Action button prefab not assigned!");
return;
}
CreateActionButtons();
}
private void CreateActionButtons()
{
// Define actions and their properties
var actionDefinitions = new[]
{
new { type = BattleActionType.Attack, icon = attackIcon, label = "Attack" },
new { type = BattleActionType.Move, icon = moveIcon, label = "Move" },
new { type = BattleActionType.UseItem, icon = itemIcon, label = "Use Item" },
new { type = BattleActionType.CastSpell, icon = spellIcon, label = "Cast Spell" },
new { type = BattleActionType.Defend, icon = defendIcon, label = "Defend" },
new { type = BattleActionType.RunAway, icon = runAwayIcon, label = "Run Away" }
};
int actionCount = actionDefinitions.Length;
float angleStep = 360f / actionCount;
for (int i = 0; i < actionCount; i++)
{
var actionDef = actionDefinitions[i];
float angle = i * angleStep - 90f; // Start from top
Vector3 position = GetPositionOnCircle(angle, wheelRadius);
CreateActionButton(actionDef.type, actionDef.icon, actionDef.label, position, i);
}
}
private void CreateActionButton(BattleActionType actionType, Sprite icon, string label, Vector3 position, int index)
{
GameObject buttonObj = Instantiate(actionButtonPrefab, wheelCenter);
buttonObj.name = $"ActionButton_{actionType}";
// Position the button
RectTransform rectTransform = buttonObj.GetComponent();
if (rectTransform != null)
{
rectTransform.anchoredPosition = position;
rectTransform.sizeDelta = Vector2.one * buttonSize;
}
// Get components
Button button = buttonObj.GetComponent();
Image iconImage = buttonObj.transform.Find("Icon")?.GetComponent();
TextMeshProUGUI labelText = buttonObj.transform.Find("Label")?.GetComponent();
// If components don't exist, create them
if (iconImage == null)
{
GameObject iconObj = new GameObject("Icon");
iconObj.transform.SetParent(buttonObj.transform, false);
iconImage = iconObj.AddComponent();
RectTransform iconRect = iconImage.GetComponent();
iconRect.anchorMin = Vector2.zero;
iconRect.anchorMax = Vector2.one;
iconRect.offsetMin = Vector2.zero;
iconRect.offsetMax = Vector2.zero;
}
if (labelText == null)
{
GameObject labelObj = new GameObject("Label");
labelObj.transform.SetParent(buttonObj.transform, false);
labelText = labelObj.AddComponent();
RectTransform labelRect = labelText.GetComponent();
labelRect.anchorMin = new Vector2(0f, -0.5f);
labelRect.anchorMax = new Vector2(1f, -0.1f);
labelRect.offsetMin = Vector2.zero;
labelRect.offsetMax = Vector2.zero;
labelText.text = label;
labelText.fontSize = 12;
labelText.alignment = TextAlignmentOptions.Center;
}
// Setup button
if (icon != null && iconImage != null)
iconImage.sprite = icon;
if (labelText != null)
labelText.text = label;
// Create action button data
ActionButton actionButton = new ActionButton
{
buttonObject = buttonObj,
button = button,
icon = iconImage,
label = labelText,
actionType = actionType
};
// Setup button events
if (button != null)
{
button.onClick.AddListener(() => OnActionButtonClicked(actionType));
// Add hover effects
var eventTrigger = buttonObj.GetComponent();
if (eventTrigger == null)
eventTrigger = buttonObj.AddComponent();
// Hover enter
var pointerEnter = new UnityEngine.EventSystems.EventTrigger.Entry();
pointerEnter.eventID = UnityEngine.EventSystems.EventTriggerType.PointerEnter;
pointerEnter.callback.AddListener((data) => { actionButton.SetHighlight(true, defaultColor, hoverColor); });
eventTrigger.triggers.Add(pointerEnter);
// Hover exit
var pointerExit = new UnityEngine.EventSystems.EventTrigger.Entry();
pointerExit.eventID = UnityEngine.EventSystems.EventTriggerType.PointerExit;
pointerExit.callback.AddListener((data) => { actionButton.SetHighlight(false, defaultColor, hoverColor); });
eventTrigger.triggers.Add(pointerExit);
}
actionButtons.Add(actionButton);
buttonObj.SetActive(false); // Start hidden
}
public void ShowWheel(Character character, Vector3 worldPos)
{
currentCharacter = character;
worldPosition = worldPos;
UpdateActionAvailability();
SetVisible(true);
AnimateWheelIn();
Debug.Log($"🎯 Action wheel shown for {character.CharacterName} at {worldPos}");
}
public void HideWheel()
{
AnimateWheelOut(() =>
{
SetVisible(false);
OnWheelClosed?.Invoke();
});
Debug.Log("🎯 Action wheel hidden");
}
private void UpdateActionAvailability()
{
if (currentCharacter == null) return;
foreach (var actionButton in actionButtons)
{
bool isAvailable = IsActionAvailable(actionButton.actionType);
actionButton.SetEnabled(isAvailable);
}
}
private bool IsActionAvailable(BattleActionType actionType)
{
if (currentCharacter == null) return false;
switch (actionType)
{
case BattleActionType.Attack:
return currentCharacter.Weapon != null;
case BattleActionType.Move:
return true; // Always available
case BattleActionType.UseItem:
// Check if character has any items (placeholder - implement item system)
return HasUsableItems();
case BattleActionType.CastSpell:
// Check if character can cast spells (placeholder - implement spell system)
return HasSpells();
case BattleActionType.Defend:
case BattleActionType.RunAway:
return true; // Usually always available
default:
return true;
}
}
private bool HasUsableItems()
{
// Placeholder implementation
// TODO: Check character's inventory for usable items
return true; // For now, assume characters always have items
}
private bool HasSpells()
{
// Placeholder implementation
// TODO: Check character's spell list or mana
return true; // For now, assume characters can cast spells
}
private void OnActionButtonClicked(BattleActionType actionType)
{
Debug.Log($"🎯 Action selected: {actionType}");
OnActionSelected?.Invoke(actionType);
HideWheel();
}
private Vector3 GetPositionOnCircle(float angleDegrees, float radius)
{
float angleRadians = angleDegrees * Mathf.Deg2Rad;
return new Vector3(
Mathf.Cos(angleRadians) * radius,
Mathf.Sin(angleRadians) * radius,
0f
);
}
private void UpdateWheelPosition()
{
if (wheelCanvas == null || mainCamera == null) return;
// Convert world position to screen position, then to canvas position
Vector3 screenPos = mainCamera.WorldToScreenPoint(worldPosition);
if (wheelCanvas.renderMode == RenderMode.ScreenSpaceOverlay)
{
wheelCenter.position = screenPos;
}
else if (wheelCanvas.renderMode == RenderMode.ScreenSpaceCamera)
{
Vector2 canvasPos;
RectTransformUtility.ScreenPointToLocalPointInRectangle(
wheelCanvas.transform as RectTransform,
screenPos,
wheelCanvas.worldCamera,
out canvasPos
);
wheelCenter.localPosition = canvasPos;
}
}
private void SetVisible(bool visible)
{
isVisible = visible;
if (wheelCanvas != null)
wheelCanvas.gameObject.SetActive(visible);
}
private void AnimateWheelIn()
{
// Simple scale animation using Unity's built-in animation
if (wheelCenter != null)
{
wheelCenter.localScale = Vector3.zero;
StartCoroutine(AnimateScale(wheelCenter, Vector3.zero, Vector3.one, animationDuration, () =>
{
// Show all buttons after animation
foreach (var button in actionButtons)
{
button.buttonObject.SetActive(true);
}
}));
}
}
private void AnimateWheelOut(Action onComplete = null)
{
// Hide buttons first
foreach (var button in actionButtons)
{
button.buttonObject.SetActive(false);
}
if (wheelCenter != null)
{
StartCoroutine(AnimateScale(wheelCenter, Vector3.one, Vector3.zero, animationDuration * 0.5f, onComplete));
}
else
{
onComplete?.Invoke();
}
}
private System.Collections.IEnumerator AnimateScale(Transform target, Vector3 from, Vector3 to, float duration, Action onComplete = null)
{
float elapsed = 0f;
while (elapsed < duration)
{
elapsed += Time.deltaTime;
float t = elapsed / duration;
// Apply easing curve if available
if (scaleCurve != null && scaleCurve.keys.Length > 0)
{
t = scaleCurve.Evaluate(t);
}
target.localScale = Vector3.Lerp(from, to, t);
yield return null;
}
target.localScale = to;
onComplete?.Invoke();
}
void OnDestroy()
{
// Clean up any running coroutines
StopAllCoroutines();
}
}