using System;
using UnityEngine;
using Unity.Netcode;
using Colosseum.Stats;
namespace Colosseum.Weapons
{
///
/// 무기 장착을 관리하는 컴포넌트.
/// 무기 장착 시 스탯 보너스를 적용하고 배율을 제공하며, 무기 외형을 표시합니다.
/// 메시 이름으로 소켓을 자동 검색합니다.
///
public class WeaponEquipment : NetworkBehaviour
{
[Header("References")]
[Tooltip("CharacterStats 컴포넌트 (없으면 자동 검색)")]
[SerializeField] private CharacterStats characterStats;
[Header("Socket Names (메시 이름)")]
[Tooltip("오른손 메시 이름")]
[SerializeField] private string rightHandName = "Hand_R";
[Tooltip("왼손 메시 이름")]
[SerializeField] private string leftHandName = "Hand_L";
[Tooltip("등 메시 이름")]
[SerializeField] private string backName = "Spine";
[Tooltip("허리 메시 이름")]
[SerializeField] private string hipName = "Hip";
[Tooltip("양손 메시 이름 (기본값: 오른손 사용)")]
[SerializeField] private string twoHandedName = "";
[Header("Starting Weapon")]
[Tooltip("시작 무기 (선택)")]
[SerializeField] private WeaponData startingWeapon;
[Header("네트워크 동기화")]
[Tooltip("이 장착 시스템이 사용하는 모든 WeaponData 목록. 서버→클라이언트 무기 동기화에 사용됩니다.")]
[SerializeField] private System.Collections.Generic.List registeredWeapons = new();
// 캐싱된 소켓 Transform들
private Transform rightHandSocket;
private Transform leftHandSocket;
private Transform backSocket;
private Transform hipSocket;
private Transform twoHandedSocket;
// 현재 장착 중인 무기
private WeaponData currentWeapon;
// 현재 생성된 무기 인스턴스
private GameObject currentWeaponInstance;
// 현재 적용된 스탯 수정자들 (해제 시 제거용)
private readonly System.Collections.Generic.Dictionary activeModifiers
= new System.Collections.Generic.Dictionary();
// 무기 장착 상태 동기화 (registeredWeapons 인덱스, -1 = 없음)
private NetworkVariable equippedWeaponId = new NetworkVariable(-1);
public WeaponData CurrentWeapon => currentWeapon;
public bool HasWeaponEquipped => currentWeapon != null;
public GameObject CurrentWeaponInstance => currentWeaponInstance;
// 배율 프로퍼티 (무기 없으면 기본값 1.0)
public float DamageMultiplier => currentWeapon != null ? currentWeapon.DamageMultiplier : 1f;
public float RangeMultiplier => currentWeapon != null ? currentWeapon.RangeMultiplier : 1f;
public float ManaCostMultiplier => currentWeapon != null ? currentWeapon.ManaCostMultiplier : 1f;
// 이벤트
public event Action OnWeaponEquipped;
public event Action OnWeaponUnequipped;
private void Awake()
{
// CharacterStats 참조 확인
if (characterStats == null)
{
characterStats = GetComponent();
}
// 소켓 자동 검색
CacheSockets();
}
public override void OnNetworkSpawn()
{
equippedWeaponId.OnValueChanged += HandleEquippedWeaponChanged;
if (IsServer && startingWeapon != null)
{
EquipWeapon(startingWeapon);
}
else if (!IsServer && equippedWeaponId.Value >= 0)
{
// 늦게 접속한 클라이언트: 현재 장착된 무기 시각화
SpawnWeaponVisualsLocal(equippedWeaponId.Value);
}
}
public override void OnNetworkDespawn()
{
equippedWeaponId.OnValueChanged -= HandleEquippedWeaponChanged;
}
///
/// 메시 이름으로 소켓 Transform 캐싱
///
private void CacheSockets()
{
rightHandSocket = FindDeepChild(rightHandName);
leftHandSocket = FindDeepChild(leftHandName);
backSocket = FindDeepChild(backName);
hipSocket = FindDeepChild(hipName);
// 양손은 별도 이름 없으면 오른손 사용
if (!string.IsNullOrEmpty(twoHandedName))
{
twoHandedSocket = FindDeepChild(twoHandedName);
}
else
{
twoHandedSocket = rightHandSocket;
}
Debug.Log($"[WeaponEquipment] Sockets cached - R:{rightHandSocket != null}, L:{leftHandSocket != null}, Back:{backSocket != null}, Hip:{hipSocket != null}");
}
///
/// 이름으로 자식 Transform 재귀 검색
///
private Transform FindDeepChild(string name)
{
if (string.IsNullOrEmpty(name)) return null;
// BFS로 검색
var queue = new System.Collections.Generic.Queue();
queue.Enqueue(transform);
while (queue.Count > 0)
{
Transform current = queue.Dequeue();
if (current.name == name)
{
return current;
}
foreach (Transform child in current)
{
queue.Enqueue(child);
}
}
return null;
}
private void HandleEquippedWeaponChanged(int oldValue, int newValue)
{
if (IsServer) return; // 서버는 EquipWeapon/UnequipWeapon에서 직접 처리
if (newValue == -1)
UnequipWeaponInternal();
else
SpawnWeaponVisualsLocal(newValue);
}
///
/// 무기 장착 (서버에서만 호출)
///
public void EquipWeapon(WeaponData weapon)
{
if (weapon == null)
{
Debug.LogWarning("[WeaponEquipment] EquipWeapon called with null weapon");
return;
}
if (!IsServer)
{
Debug.LogWarning("[WeaponEquipment] EquipWeapon can only be called on server");
return;
}
// 기존 무기 해제
if (currentWeapon != null)
{
UnequipWeapon();
}
currentWeapon = weapon;
// 스탯 보너스 적용
ApplyStatBonuses(weapon);
// 무기 외형 생성 및 부착
SpawnWeaponVisuals(weapon);
// registeredWeapons 인덱스로 동기화
equippedWeaponId.Value = registeredWeapons.IndexOf(weapon);
if (equippedWeaponId.Value < 0)
Debug.LogWarning($"[WeaponEquipment] '{weapon.WeaponName}' is not in registeredWeapons. Add it to sync to clients.");
// 이벤트 발생
OnWeaponEquipped?.Invoke(weapon);
Debug.Log($"[WeaponEquipment] Equipped: {weapon.WeaponName} at {weapon.WeaponSlot}");
}
///
/// 무기 해제 (서버에서만 호출)
///
public void UnequipWeapon()
{
if (currentWeapon == null) return;
if (!IsServer)
{
Debug.LogWarning("[WeaponEquipment] UnequipWeapon can only be called on server");
return;
}
WeaponData previousWeapon = currentWeapon;
// 스탯 보너스 제거
RemoveStatBonuses();
// 무기 외형 제거
DespawnWeaponVisuals();
currentWeapon = null;
equippedWeaponId.Value = -1;
// 이벤트 발생
OnWeaponUnequipped?.Invoke(previousWeapon);
Debug.Log($"[WeaponEquipment] Unequipped: {previousWeapon.WeaponName}");
}
///
/// 내부 해제 로직 (클라이언트 동기화용)
///
private void UnequipWeaponInternal()
{
if (currentWeaponInstance == null && currentWeapon == null) return;
WeaponData previousWeapon = currentWeapon;
DespawnWeaponVisualsLocal();
OnWeaponUnequipped?.Invoke(previousWeapon);
}
///
/// 무기의 스탯 보너스 적용
///
private void ApplyStatBonuses(WeaponData weapon)
{
if (characterStats == null) return;
// 모든 스탯 타입에 대해 보너스 적용
foreach (StatType statType in System.Enum.GetValues(typeof(StatType)))
{
int bonus = weapon.GetStatBonus(statType);
if (bonus != 0)
{
var stat = characterStats.GetStat(statType);
if (stat != null)
{
var modifier = new StatModifier(bonus, StatModType.Flat, weapon);
stat.AddModifier(modifier);
activeModifiers[statType] = modifier;
Debug.Log($"[WeaponEquipment] Applied {statType} +{bonus}");
}
}
}
}
///
/// 무기의 스탯 보너스 제거
///
private void RemoveStatBonuses()
{
if (characterStats == null) return;
// 각 스탯에서 무기로부터 추가된 수정자 제거
foreach (StatType statType in System.Enum.GetValues(typeof(StatType)))
{
var stat = characterStats.GetStat(statType);
if (stat != null && activeModifiers.TryGetValue(statType, out StatModifier modifier))
{
stat.RemoveModifier(modifier);
Debug.Log($"[WeaponEquipment] Removed {statType} modifier");
}
}
activeModifiers.Clear();
}
///
/// 무기 외형 생성 및 부착 (서버)
///
private void SpawnWeaponVisuals(WeaponData weapon)
{
if (weapon == null || weapon.WeaponPrefab == null) return;
Transform socket = GetSocketForSlot(weapon.WeaponSlot);
if (socket == null)
{
Debug.LogWarning($"[WeaponEquipment] No socket found for slot: {weapon.WeaponSlot}");
return;
}
currentWeaponInstance = Instantiate(weapon.WeaponPrefab, socket);
currentWeaponInstance.transform.localPosition = weapon.PositionOffset;
currentWeaponInstance.transform.localRotation = Quaternion.Euler(weapon.RotationOffset);
currentWeaponInstance.transform.localScale = weapon.Scale;
Debug.Log($"[WeaponEquipment] Spawned weapon visual: {weapon.WeaponName}");
}
///
/// 클라이언트: registeredWeapons 인덱스로 무기 외형 생성
///
private void SpawnWeaponVisualsLocal(int weaponIndex)
{
if (weaponIndex < 0 || weaponIndex >= registeredWeapons.Count || registeredWeapons[weaponIndex] == null)
{
Debug.LogWarning($"[WeaponEquipment] Weapon index {weaponIndex} not found in registeredWeapons.");
return;
}
var weapon = registeredWeapons[weaponIndex];
if (weapon.WeaponPrefab == null) return;
DespawnWeaponVisualsLocal();
Transform socket = GetSocketForSlot(weapon.WeaponSlot);
if (socket == null)
{
Debug.LogWarning($"[WeaponEquipment] No socket found for slot: {weapon.WeaponSlot}");
return;
}
currentWeaponInstance = Instantiate(weapon.WeaponPrefab, socket);
currentWeaponInstance.transform.localPosition = weapon.PositionOffset;
currentWeaponInstance.transform.localRotation = Quaternion.Euler(weapon.RotationOffset);
currentWeaponInstance.transform.localScale = weapon.Scale;
currentWeapon = weapon;
}
///
/// 무기 외형 제거 (서버)
///
private void DespawnWeaponVisuals()
{
if (currentWeaponInstance == null) return;
Destroy(currentWeaponInstance);
currentWeaponInstance = null;
}
///
/// 무기 외형 제거 (클라이언트)
///
private void DespawnWeaponVisualsLocal()
{
if (currentWeaponInstance == null) return;
Destroy(currentWeaponInstance);
currentWeaponInstance = null;
currentWeapon = null;
}
///
/// 슬롯 타입에 맞는 소켓 Transform 반환
///
private Transform GetSocketForSlot(WeaponSlot slot)
{
return slot switch
{
WeaponSlot.RightHand => rightHandSocket,
WeaponSlot.LeftHand => leftHandSocket,
WeaponSlot.Back => backSocket,
WeaponSlot.Hip => hipSocket,
WeaponSlot.TwoHanded => twoHandedSocket != null ? twoHandedSocket : rightHandSocket,
_ => rightHandSocket,
};
}
///
/// 서버에 무기 장착 요청
///
[Rpc(SendTo.Server)]
public void RequestEquipWeaponRpc(int weaponInstanceId)
{
// TODO: WeaponDatabase에서 ID로 WeaponData 조회 후 EquipWeapon 호출
Debug.Log($"[WeaponEquipment] Client requested weapon equip: {weaponInstanceId}");
}
///
/// 서버에 무기 해제 요청
///
[Rpc(SendTo.Server)]
public void RequestUnequipWeaponRpc()
{
UnequipWeapon();
}
}
}