Files
Colosseum/Assets/_Game/Scripts/UI/SkillSlotUI.cs
dal4segno 3663692b9d feat: 무기 특성(WeaponTrait) 시스템 및 보조손 장착 구현
- WeaponTrait enum 추가 (Melee/TwoHanded/Defense/Magic/Ranged)

- SkillData에 allowedWeaponTraits 필드 및 MatchesWeaponTrait() 검증 메서드 추가

- WeaponEquipment에 보조손(오프핸드) 슬롯 지원 (장착/해제/스탯 보너스/네트워크 동기화)

- EquippedWeaponTraits 프로퍼티로 메인+보조 무기 특성 합산 제공

- PlayerSkillInput 4곳(OnSkillInput/RPC/CanUseSkill/DebugExecute)에 무기 trait 제약 검증 추가

- SkillSlotUI에 무기 불호환 시 회색 표시(weaponIncompatibleColor) 지원

- 기존 검 에셋에 weaponTrait=Melee 설정
2026-04-01 21:13:05 +09:00

176 lines
6.1 KiB
C#

using UnityEngine;
using UnityEngine.UI;
using TMPro;
using Colosseum.Skills;
namespace Colosseum.UI
{
/// <summary>
/// 개별 스킬 슬롯 UI
/// </summary>
public class SkillSlotUI : MonoBehaviour
{
[Header("References")]
[SerializeField] private Image iconImage;
[SerializeField] private Image cooldownOverlay;
[SerializeField] private TMP_Text cooldownText;
[SerializeField] private TMP_Text keybindText;
[Header("Settings")]
[SerializeField] private Color availableColor = Color.white;
[SerializeField] private Color cooldownColor = new Color(0.2f, 0.2f, 0.2f, 0.9f);
[SerializeField] private Color noManaColor = new Color(0.5f, 0.2f, 0.2f, 0.8f);
[SerializeField] private Color weaponIncompatibleColor = new Color(0.3f, 0.3f, 0.3f, 0.8f);
private SkillData skill;
private int slotIndex;
private bool useIconForCooldown = false;
private Animator cooldownAnimator; // 쿨다운 오버레이를 제어하는 Animator
private GameObject cooldownContainer; // 쿨다운 오버레이의 부모 GameObject (Cooldown)
public int SlotIndex => slotIndex;
private void Awake()
{
if (cooldownOverlay == null)
{
useIconForCooldown = true;
}
else if (cooldownOverlay.type != Image.Type.Filled)
{
useIconForCooldown = true;
}
else
{
// 쿨다운 오버레이의 상위 GameObject에 있는 Animator 찾기
// 구조: CooldownItem (Animator) -> Cooldown -> SPR_Cooldown (Image)
cooldownAnimator = cooldownOverlay.GetComponentInParent<Animator>();
// Animator가 Cooldown GameObject를 제어하므로
// 쿨다운 표시를 위해 Animator를 비활성화하고 수동으로 제어
if (cooldownAnimator != null)
{
cooldownAnimator.enabled = false;
// Animator가 제어하던 Cooldown GameObject 찾기
// SPR_Cooldown의 부모가 Cooldown
cooldownContainer = cooldownOverlay.transform.parent?.gameObject;
}
// 쿨다운 오버레이 초기화
cooldownOverlay.fillAmount = 0f;
cooldownOverlay.enabled = false;
// Cooldown 컨테이너 비활성화
if (cooldownContainer != null)
{
cooldownContainer.SetActive(false);
}
}
}
public void Initialize(int index, SkillData skillData, string keyLabel)
{
slotIndex = index;
skill = skillData;
if (keybindText != null)
keybindText.text = keyLabel;
if (skill != null && iconImage != null)
{
iconImage.sprite = skill.Icon;
iconImage.enabled = true;
iconImage.color = availableColor;
}
else if (iconImage != null)
{
iconImage.enabled = false;
}
}
public void UpdateState(float cooldownRemaining, float cooldownTotal, bool hasEnoughMana, bool isWeaponIncompatible = false)
{
if (skill == null)
{
if (cooldownContainer != null) cooldownContainer.SetActive(false);
if (cooldownOverlay != null) cooldownOverlay.enabled = false;
if (cooldownText != null) cooldownText.text = "";
return;
}
if (cooldownRemaining > 0f)
{
float ratio = cooldownRemaining / cooldownTotal;
if (useIconForCooldown && iconImage != null)
{
// 아이콘 색상으로 쿨다운 표시
float brightness = Mathf.Lerp(0.3f, 1f, 1f - ratio);
iconImage.color = new Color(brightness, brightness, brightness, 1f);
}
else if (cooldownOverlay != null)
{
// Cooldown 컨테이너 활성화
if (cooldownContainer != null && !cooldownContainer.activeSelf)
{
cooldownContainer.SetActive(true);
}
// Image 컴포넌트 활성화
cooldownOverlay.enabled = true;
cooldownOverlay.fillAmount = ratio;
}
if (cooldownText != null)
{
cooldownText.text = cooldownRemaining < 1f
? $"{cooldownRemaining:F1}"
: $"{Mathf.CeilToInt(cooldownRemaining)}";
}
}
else
{
// 쿨다운 완료
if (useIconForCooldown && iconImage != null)
{
iconImage.color = isWeaponIncompatible ? weaponIncompatibleColor
: hasEnoughMana ? availableColor : noManaColor;
}
else if (cooldownOverlay != null)
{
// Image 컴포넌트 비활성화
cooldownOverlay.enabled = false;
// Cooldown 컨테이너 비활성화
if (cooldownContainer != null)
{
cooldownContainer.SetActive(false);
}
}
if (cooldownText != null)
{
cooldownText.text = "";
}
}
}
public void SetSkill(SkillData skillData)
{
skill = skillData;
if (skill != null && iconImage != null)
{
iconImage.sprite = skill.Icon;
iconImage.enabled = true;
iconImage.color = availableColor;
}
else if (iconImage != null)
{
iconImage.enabled = false;
}
}
}
}