using System;
using Cielonos.MainGame.Map;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace Cielonos.MainGame.UI
{
///
/// 地图上的单个节点 UI 元素。
/// 由 MapUIPage 动态实例化,定位在 RunMapNode.position 对应的 UI 坐标上。
/// 通过 ApplyDisplayState 控制可见性、交互性和边框颜色。
///
public class MapNodeElement : MonoBehaviour
{
// ================================================================
// 边框颜色常量
// ================================================================
/// Outline 偏移量,控制边框粗细。
private static readonly Vector2 OUTLINE_DISTANCE = new Vector2(3f, 3f);
/// 已访问节点的边框颜色(白色)。
private static readonly Color OUTLINE_COLOR_VISITED = Color.white;
/// 可前往但未访问节点的边框颜色(绿色)。
private static readonly Color OUTLINE_COLOR_SELECTABLE = new Color(0.2f, 0.9f, 0.2f, 1f);
/// 不可前往节点的边框颜色(红色)。
private static readonly Color OUTLINE_COLOR_LOCKED = new Color(0.9f, 0.2f, 0.2f, 1f);
/// 当前所在节点的边框颜色(金黄色)。
private static readonly Color OUTLINE_COLOR_CURRENT = new Color(1f, 0.9f, 0.3f, 1f);
// ================================================================
// 序列化字段
// ================================================================
[Header("References")]
[SerializeField] private Image iconImage;
[SerializeField] private Image backgroundImage;
[SerializeField] private TMP_Text labelText;
[SerializeField] private Button button;
// ================================================================
// 公共属性
// ================================================================
/// 关联的网格坐标。
public Vector2Int GridPosition { get; private set; }
/// 关联的节点类型。
public MapNodeType NodeType { get; private set; }
/// 当前显示状态。
public NodeDisplayState DisplayState { get; private set; }
/// 节点被玩家点击时触发,参数为该节点的网格坐标。
public event Action OnClicked;
// ================================================================
// 运行时字段
// ================================================================
/// 运行时自动创建的 Outline 组件,用于显示交互状态边框。
private Outline _outline;
// ================================================================
// 初始化
// ================================================================
///
/// 初始化节点元素,设置网格坐标、类型、UI 位置,并绑定点击事件。
///
/// 对应的 RunMapNode 数据。
public void Setup(RunMapNode node)
{
GridPosition = node.gridPosition;
NodeType = node.nodeType;
// 将节点定位到 UI 坐标
RectTransform rt = GetComponent();
if (rt != null)
{
rt.anchoredPosition = node.position;
}
// 设置标签文本为节点类型简称
if (labelText != null)
{
labelText.text = GetNodeLabel(node.nodeType);
}
// 设置背景颜色
if (backgroundImage != null)
{
backgroundImage.color = GetNodeColor(node.nodeType);
}
// 确保 Outline 组件存在
EnsureOutline();
// 绑定按钮点击 → 触发 OnClicked 事件
if (button != null)
{
button.onClick.AddListener(HandleClick);
}
}
// ================================================================
// 显示状态
// ================================================================
///
/// 更新节点的完整显示状态(可见性、交互性、使用状态、边框颜色)。
/// 由 MapUIPage.RefreshDisplayStates 批量调用。
///
public void ApplyDisplayState(NodeDisplayState state)
{
DisplayState = state;
// --- 可见性 ---
// 所有节点始终可见:Hidden 不再出现,但保留防御性处理
switch (state.visibility)
{
case MapNodeVisibility.Hidden:
case MapNodeVisibility.Silhouette:
gameObject.SetActive(true);
if (iconImage != null) iconImage.enabled = false;
if (labelText != null) labelText.text = "?";
if (backgroundImage != null)
backgroundImage.color = new Color(0.4f, 0.4f, 0.4f, 0.6f);
break;
case MapNodeVisibility.Revealed:
gameObject.SetActive(true);
if (iconImage != null) iconImage.enabled = true;
if (labelText != null) labelText.text = GetNodeLabel(NodeType);
if (backgroundImage != null)
backgroundImage.color = GetNodeColor(NodeType);
break;
}
// --- 交互性(按钮是否可点击) ---
if (button != null)
{
button.interactable = state.interaction == MapNodeInteraction.Selectable;
}
// --- 使用状态的视觉反馈(已访问/已用完的节点背景变暗) ---
ApplyUsageVisual(state.usage, state.interaction);
// --- 边框颜色(白/绿/红/金) ---
ApplyOutlineVisual(state);
}
// ================================================================
// Outline 边框
// ================================================================
///
/// 确保 backgroundImage 上存在 Outline 组件。
/// 首次调用时自动添加。
///
private void EnsureOutline()
{
if (_outline != null) return;
if (backgroundImage == null) return;
_outline = backgroundImage.GetComponent();
if (_outline == null)
{
_outline = backgroundImage.gameObject.AddComponent();
}
_outline.effectDistance = OUTLINE_DISTANCE;
}
///
/// 根据节点的显示状态设置 Outline 边框颜色。
/// 优先级从高到低:
/// 1. 当前所在节点 → 金黄色
/// 2. 已访问/已用完 → 白色
/// 3. 可前往但未访问 → 绿色
/// 4. 不可前往 → 红色
/// Hidden 节点已被 SetActive(false),不在此处理。
///
private void ApplyOutlineVisual(NodeDisplayState state)
{
EnsureOutline();
if (_outline == null) return;
_outline.enabled = true;
// 优先级 1:当前所在节点 → 金黄色边框
if (state.interaction == MapNodeInteraction.Current)
{
_outline.effectColor = OUTLINE_COLOR_CURRENT;
return;
}
// 优先级 2:已访问或已用完 → 白色边框
if (state.usage == MapNodeUsage.Visited || state.usage == MapNodeUsage.Exhausted)
{
_outline.effectColor = OUTLINE_COLOR_VISITED;
return;
}
// 优先级 3:可前往但未曾去过 → 绿色边框
if (state.interaction == MapNodeInteraction.Selectable)
{
_outline.effectColor = OUTLINE_COLOR_SELECTABLE;
return;
}
// 优先级 4:不可前往(Locked)→ 红色边框
_outline.effectColor = OUTLINE_COLOR_LOCKED;
}
// ================================================================
// 使用状态视觉
// ================================================================
///
/// 根据使用状态调整视觉效果(已访问/已用完的节点背景变暗)。
///
private void ApplyUsageVisual(MapNodeUsage usage, MapNodeInteraction interaction)
{
if (backgroundImage == null) return;
Color baseColor = backgroundImage.color;
switch (usage)
{
case MapNodeUsage.Visited:
// 已访问节点稍微变暗
backgroundImage.color = new Color(baseColor.r * 0.7f, baseColor.g * 0.7f,
baseColor.b * 0.7f, baseColor.a);
break;
case MapNodeUsage.Exhausted:
// 已用完的节点明显变暗
backgroundImage.color = new Color(baseColor.r * 0.4f, baseColor.g * 0.4f,
baseColor.b * 0.4f, baseColor.a * 0.6f);
break;
}
// 当前所在节点高亮(覆盖变暗效果)
if (interaction == MapNodeInteraction.Current)
{
backgroundImage.color = Color.white;
}
}
// ================================================================
// 点击处理
// ================================================================
/// 按钮点击回调,转发为 OnClicked 事件。
private void HandleClick()
{
OnClicked?.Invoke(GridPosition);
}
// ================================================================
// 静态工具方法
// ================================================================
/// 获取节点类型的简称标签。
private static string GetNodeLabel(MapNodeType type)
{
return type switch
{
MapNodeType.Start => "S",
MapNodeType.NormalBattle => "B",
MapNodeType.EliteBattle => "E",
MapNodeType.BossBattle => "BOSS",
MapNodeType.MechanicalTable => "T",
MapNodeType.LogisticsCenter => "$",
MapNodeType.MedicalStation => "+",
_ => "?"
};
}
/// 获取节点类型对应的背景颜色。
private static Color GetNodeColor(MapNodeType type)
{
return type switch
{
MapNodeType.Start => new Color(0.2f, 0.8f, 0.2f, 1f), // 绿色
MapNodeType.NormalBattle => new Color(0.6f, 0.6f, 0.6f, 1f), // 灰色
MapNodeType.EliteBattle => new Color(0.9f, 0.6f, 0.1f, 1f), // 橙色
MapNodeType.BossBattle => new Color(0.9f, 0.1f, 0.1f, 1f), // 红色
MapNodeType.MechanicalTable => new Color(0.3f, 0.5f, 0.9f, 1f), // 蓝色
MapNodeType.LogisticsCenter => new Color(0.9f, 0.85f, 0.2f, 1f),// 金色
MapNodeType.MedicalStation => new Color(0.3f, 0.9f, 0.6f, 1f), // 青绿
_ => Color.white
};
}
}
}