using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
///
/// The pose selection menu (the "Pose UI" canvas in Main): a modal pause overlay (same pattern as
/// ) opened via the overview menu's Pose button, closed by Esc through
/// (all Esc handling lives there). On every open it rebuilds a radial menu
/// of Icon Button prefab instances from the local player's
/// (icon + title come from each ); clicking an item applies that pose and
/// closes the menu. The active pose's button is disabled (its hover ring marks it, like the overview
/// menu's mode buttons). Only the visual is toggled - this binder stays alive
/// on its (always-active) canvas root.
///
[DisallowMultipleComponent]
public class PoseMenu : MonoBehaviour
{
[Tooltip("Mode manager supplying the local player (whose PlayerPoseController is driven) and the " +
"pause hook. Empty = found automatically.")]
[SerializeField] private GameModeManager gameMode;
[Header("Overlay")]
[Tooltip("Visual container toggled via Open/Close. Must be a CHILD of this binder's object so the " +
"binder stays alive while the panel is hidden.")]
[SerializeField] private GameObject panelRoot;
[Header("Radial menu")]
[Tooltip("Center the pose items are arranged around (typically a centered child of the panel).")]
[SerializeField] private RectTransform itemContainer;
[Tooltip("Icon Button prefab instantiated per pose (Icon child image + Label child TMP are bound).")]
[SerializeField] private GameObject itemPrefab;
[Tooltip("Distance of each item from the container center, in canvas units.")]
[Min(0f)]
[SerializeField] private float radius = 220f;
private readonly List _items = new();
private PlayerPoseController _poseController; // controller the open menu is bound to
private bool _open;
private void Awake()
{
if (!gameMode) gameMode = FindFirstObjectByType();
if (panelRoot) panelRoot.SetActive(false);
}
/// True while the modal overlay is open. The overview menu yields Esc to this overlay
/// (close-only) while it is open.
public bool IsOpen => _open;
/// True while the local player has selectable poses - the overview menu hides its Pose
/// button otherwise (no player bound, or an empty pose list).
public bool CanOpen => LocalPoseController() && LocalPoseController().Poses.Count > 0;
/// With 2 or fewer poses (the hunter's set) a radial menu is pointless - the pose button
/// and the C hotkey cycle directly via instead of opening the overlay.
public bool CyclesDirectly
{
get
{
PlayerPoseController pose = LocalPoseController();
return pose && pose.Poses.Count > 0 && pose.Poses.Count <= 2;
}
}
/// Advances to the next pose (wrapping) without opening the overlay.
public void CyclePose()
{
PlayerPoseController pose = LocalPoseController();
if (pose && pose.Poses.Count > 0) pose.SetPoseIndex(pose.CurrentIndex + 1);
}
/// Opens the modal overlay and (re)builds the radial items; false when there is nothing
/// to show, so the caller can drop the pause instead of stranding it.
public bool Open()
{
PlayerPoseController pose = LocalPoseController();
if (!pose || pose.Poses.Count == 0 || !itemContainer || !itemPrefab) return false;
_poseController = pose;
BuildItems(pose);
_open = true;
if (panelRoot) panelRoot.SetActive(true);
if (gameMode) gameMode.SetPaused(true);
return true;
}
/// Closes the modal overlay and resumes gameplay (Esc handling in ).
public void Close()
{
_open = false;
if (panelRoot) panelRoot.SetActive(false);
if (gameMode) gameMode.SetPaused(false);
}
private void Update()
{
// The bound player went away underneath the open menu (despawn/disconnect) -> don't linger
// as a stale pause overlay.
if (_open && !_poseController)
Close();
}
private PlayerPoseController LocalPoseController()
{
PlayerCharacterController player = gameMode ? gameMode.Player : null;
return player ? player.GetComponent() : null;
}
/// Instantiates one Icon Button per pose, evenly on a circle starting at 12 o'clock going
/// clockwise. Rebuilt on every open - pose count is tiny and the list can differ per bound player.
private void BuildItems(PlayerPoseController pose)
{
for (int i = 0; i < _items.Count; i++)
if (_items[i]) Destroy(_items[i]);
_items.Clear();
IReadOnlyList poses = pose.Poses;
for (int i = 0; i < poses.Count; i++)
{
GameObject item = Instantiate(itemPrefab, itemContainer);
float angle = (90f - i * 360f / poses.Count) * Mathf.Deg2Rad;
((RectTransform)item.transform).anchoredPosition =
new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)) * radius;
PlayerPose p = poses[i];
Transform iconChild = item.transform.Find("Icon");
Image icon = iconChild ? iconChild.GetComponent() : null;
if (icon)
{
icon.sprite = p ? p.Icon : null;
icon.preserveAspect = true;
icon.enabled = icon.sprite; // no icon authored -> no solid white square
icon.rectTransform.localScale = Vector3.one * (p ? p.IconScale : 1f);
}
TMP_Text label = item.GetComponentInChildren(true);
if (label) label.text = p && !string.IsNullOrEmpty(p.Title) ? p.Title : (p ? p.name : "");
Button button = item.GetComponent