823 lines
27 KiB
C#
823 lines
27 KiB
C#
using System;
|
|
using Unity.Netcode;
|
|
using UnityEngine;
|
|
using Northbound.Data;
|
|
|
|
namespace Northbound
|
|
{
|
|
public class Building : NetworkBehaviour, IDamageable, IVisionProvider, ITeamMember, IHealthProvider, IInteractable
|
|
{
|
|
[Header("References")]
|
|
public TowerData buildingData;
|
|
|
|
[Header("Runtime Info")]
|
|
public Vector3Int gridPosition;
|
|
public int rotation; // 0-3 (0=0°, 1=90°, 2=180°, 3=270°)
|
|
|
|
[Header("Team")]
|
|
[Tooltip("건물의 팀 (플레이어/적대세력/몬스터/중립)")]
|
|
public TeamType initialTeam = TeamType.Player;
|
|
|
|
[Header("Ownership (for pre-placed buildings)")]
|
|
[Tooltip("씬에 미리 배치된 건물의 경우 여기서 소유자 설정 (0 = 중립, 1+ = 플레이어 ID)")]
|
|
public ulong initialOwnerId = 0;
|
|
[Tooltip("사전 배치 건물인가요? 체크하면 initialOwnerId를 사용합니다")]
|
|
public bool useInitialOwner = false;
|
|
|
|
[Header("Health")]
|
|
public bool showHealthBar = true;
|
|
public GameObject healthBarPrefab;
|
|
|
|
[Header("Visual Effects")]
|
|
public GameObject destroyEffectPrefab;
|
|
public GameObject damageEffectPrefab;
|
|
public Transform effectSpawnPoint;
|
|
|
|
[Header("Debug")]
|
|
public bool showGridBounds = true;
|
|
public Color gridBoundsColor = Color.cyan;
|
|
|
|
// 현재 체력
|
|
private NetworkVariable<int> _currentHealth = new NetworkVariable<int>(
|
|
0,
|
|
NetworkVariableReadPermission.Everyone,
|
|
NetworkVariableWritePermission.Server
|
|
);
|
|
|
|
// 건물 소유자 (사전 배치 건물 또는 동적 건물 모두 지원)
|
|
private NetworkVariable<ulong> _ownerId = new NetworkVariable<ulong>(
|
|
0,
|
|
NetworkVariableReadPermission.Everyone,
|
|
NetworkVariableWritePermission.Server
|
|
);
|
|
|
|
// 건물 팀
|
|
private NetworkVariable<TeamType> _team = new NetworkVariable<TeamType>(
|
|
TeamType.Neutral,
|
|
NetworkVariableReadPermission.Everyone,
|
|
NetworkVariableWritePermission.Server
|
|
);
|
|
|
|
// 이벤트
|
|
public event Action<int, int> OnHealthChanged; // (current, max)
|
|
public event Action OnDestroyed;
|
|
public event Action<TeamType> OnTeamChanged;
|
|
|
|
private UnitHealthBar _healthBar;
|
|
private float _lastRegenTime;
|
|
|
|
// 업그레이드 관련
|
|
private NetworkVariable<bool> _isUpgrading = new NetworkVariable<bool>(
|
|
false,
|
|
NetworkVariableReadPermission.Everyone,
|
|
NetworkVariableWritePermission.Server
|
|
);
|
|
private NetworkVariable<float> _upgradeProgress = new NetworkVariable<float>(
|
|
0f,
|
|
NetworkVariableReadPermission.Everyone,
|
|
NetworkVariableWritePermission.Server
|
|
);
|
|
private NetworkVariable<int> _upgradeTargetId = new NetworkVariable<int>(
|
|
0,
|
|
NetworkVariableReadPermission.Everyone,
|
|
NetworkVariableWritePermission.Server
|
|
);
|
|
|
|
private float _lastInteractionTime;
|
|
private const float INTERACTION_COOLDOWN = 0.5f;
|
|
|
|
public override void OnNetworkSpawn()
|
|
{
|
|
base.OnNetworkSpawn();
|
|
|
|
if (IsServer)
|
|
{
|
|
// 체력 초기화
|
|
if (_currentHealth.Value == 0)
|
|
{
|
|
_currentHealth.Value = buildingData != null ? buildingData.maxHealth : 100;
|
|
}
|
|
|
|
// 팀 초기화
|
|
if (_team.Value == TeamType.Neutral)
|
|
{
|
|
_team.Value = initialTeam;
|
|
}
|
|
|
|
// 소유자 초기화 (사전 배치 건물 체크)
|
|
if (useInitialOwner && _ownerId.Value == 0)
|
|
{
|
|
_ownerId.Value = initialOwnerId;
|
|
// Debug.Log($"<color=cyan>[Building] 사전 배치 건물 '{buildingData?.buildingName ?? gameObject.name}' 소유자: {initialOwnerId}, 팀: {_team.Value}</color>");
|
|
}
|
|
else if (!useInitialOwner && _ownerId.Value == 0)
|
|
{
|
|
// 동적 생성 건물은 NetworkObject의 Owner 사용
|
|
_ownerId.Value = OwnerClientId;
|
|
}
|
|
|
|
_lastRegenTime = Time.time;
|
|
|
|
// FogOfWar 시스템에 시야 제공자로 등록
|
|
if (buildingData != null && buildingData.providesVision)
|
|
{
|
|
FogOfWarSystem.Instance?.RegisterVisionProvider(this);
|
|
}
|
|
}
|
|
|
|
// 이벤트 구독
|
|
_currentHealth.OnValueChanged += OnHealthValueChanged;
|
|
_team.OnValueChanged += OnTeamValueChanged;
|
|
_upgradeProgress.OnValueChanged += OnUpgradeProgressChanged;
|
|
_isUpgrading.OnValueChanged += OnUpgradingChanged;
|
|
|
|
// 체력바 생성
|
|
if (showHealthBar && healthBarPrefab != null)
|
|
{
|
|
CreateHealthBar();
|
|
}
|
|
|
|
// 초기 체력 UI 업데이트
|
|
UpdateHealthUI();
|
|
UpdateTeamVisuals();
|
|
}
|
|
|
|
public override void OnNetworkDespawn()
|
|
{
|
|
_currentHealth.OnValueChanged -= OnHealthValueChanged;
|
|
_team.OnValueChanged -= OnTeamValueChanged;
|
|
_upgradeProgress.OnValueChanged -= OnUpgradeProgressChanged;
|
|
_isUpgrading.OnValueChanged -= OnUpgradingChanged;
|
|
|
|
// FogOfWar 시스템에서 제거
|
|
if (IsServer && buildingData != null && buildingData.providesVision)
|
|
{
|
|
FogOfWarSystem.Instance?.UnregisterVisionProvider(this);
|
|
}
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (!IsServer || buildingData == null)
|
|
return;
|
|
|
|
// 자동 체력 회복
|
|
if (buildingData.autoRegenerate && _currentHealth.Value < buildingData.maxHealth)
|
|
{
|
|
if (Time.time - _lastRegenTime >= 1f)
|
|
{
|
|
int regenAmount = Mathf.Min(buildingData.regenPerSecond, buildingData.maxHealth - _currentHealth.Value);
|
|
_currentHealth.Value += regenAmount;
|
|
_lastRegenTime = Time.time;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 건물 초기화 (BuildingManager가 동적 생성 시 호출)
|
|
/// </summary>
|
|
public void Initialize(TowerData data, Vector3Int gridPos, int rot, ulong ownerId, TeamType team = TeamType.Player)
|
|
{
|
|
buildingData = data;
|
|
gridPosition = gridPos;
|
|
rotation = rot;
|
|
|
|
// 이미 스폰된 경우
|
|
if (IsServer && IsSpawned)
|
|
{
|
|
_currentHealth.Value = data.maxHealth;
|
|
_ownerId.Value = ownerId;
|
|
_team.Value = team;
|
|
|
|
// 시야 제공자 등록
|
|
if (data.providesVision)
|
|
{
|
|
FogOfWarSystem.Instance?.RegisterVisionProvider(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 건물 소유권 변경 (점령 등)
|
|
/// </summary>
|
|
public void SetOwner(ulong newOwnerId, TeamType newTeam)
|
|
{
|
|
if (!IsServer) return;
|
|
|
|
_ownerId.Value = newOwnerId;
|
|
_team.Value = newTeam;
|
|
|
|
// 시야 제공자 재등록 (소유자가 바뀌었으므로)
|
|
if (buildingData != null && buildingData.providesVision)
|
|
{
|
|
FogOfWarSystem.Instance?.UnregisterVisionProvider(this);
|
|
FogOfWarSystem.Instance?.RegisterVisionProvider(this);
|
|
}
|
|
}
|
|
|
|
#region ITeamMember Implementation
|
|
|
|
public TeamType GetTeam() => _team.Value;
|
|
|
|
public bool IsDead() => _currentHealth.Value <= 0;
|
|
|
|
public void SetTeam(TeamType team)
|
|
{
|
|
if (!IsServer) return;
|
|
_team.Value = team;
|
|
}
|
|
|
|
private void OnTeamValueChanged(TeamType previousValue, TeamType newValue)
|
|
{
|
|
OnTeamChanged?.Invoke(newValue);
|
|
UpdateTeamVisuals();
|
|
}
|
|
|
|
private void UpdateTeamVisuals()
|
|
{
|
|
// 팀 색상으로 건물 외곽선이나 이펙트 변경 가능
|
|
// 예: Renderer의 emission 색상 변경
|
|
Color teamColor = TeamManager.GetTeamColor(_team.Value);
|
|
|
|
// 여기에 실제 비주얼 업데이트 로직 추가
|
|
// 예: outline shader, emission, particle system 색상 등
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IVisionProvider Implementation
|
|
|
|
public ulong GetOwnerId() => _ownerId.Value;
|
|
|
|
public float GetVisionRange()
|
|
{
|
|
return buildingData != null ? buildingData.visionRange : 0f;
|
|
}
|
|
|
|
public Transform GetTransform() => transform;
|
|
|
|
public bool IsActive()
|
|
{
|
|
// 건물이 스폰되어 있고, 파괴되지 않았으며, 시야 제공 설정이 켜져있어야 함
|
|
return IsSpawned && !IsDestroyed() && buildingData != null && buildingData.providesVision;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IDamageable Implementation
|
|
|
|
public void TakeDamage(int damage, ulong attackerId)
|
|
{
|
|
if (!IsServer)
|
|
{
|
|
// 클라이언트는 서버에 요청
|
|
TakeDamageServerRpc(damage, attackerId);
|
|
return;
|
|
}
|
|
|
|
// 무적 건물
|
|
if (buildingData != null && buildingData.isIndestructible)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// 이미 파괴됨
|
|
if (_currentHealth.Value <= 0)
|
|
return;
|
|
|
|
// 공격자의 팀 확인 (팀 공격 방지)
|
|
var attackerObj = NetworkManager.Singleton.SpawnManager.SpawnedObjects[attackerId];
|
|
var attackerTeamMember = attackerObj?.GetComponent<ITeamMember>();
|
|
|
|
if (attackerTeamMember != null)
|
|
{
|
|
if (!TeamManager.CanAttack(attackerTeamMember, this))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
// 데미지 적용
|
|
int actualDamage = Mathf.Min(damage, _currentHealth.Value);
|
|
_currentHealth.Value -= actualDamage;
|
|
|
|
// 데미지 이펙트
|
|
ShowDamageEffectClientRpc();
|
|
|
|
// 체력이 0이 되면 파괴
|
|
if (_currentHealth.Value <= 0)
|
|
{
|
|
DestroyBuilding(attackerId);
|
|
}
|
|
}
|
|
|
|
[Rpc(SendTo.Server)]
|
|
private void TakeDamageServerRpc(int damage, ulong attackerId)
|
|
{
|
|
TakeDamage(damage, attackerId);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Health Management
|
|
|
|
/// <summary>
|
|
/// 건물 파괴
|
|
/// </summary>
|
|
private void DestroyBuilding(ulong attackerId)
|
|
{
|
|
if (!IsServer)
|
|
return;
|
|
|
|
// 파괴 이벤트 발생
|
|
OnDestroyed?.Invoke();
|
|
NotifyDestroyedClientRpc();
|
|
|
|
// 파괴 이펙트
|
|
ShowDestroyEffectClientRpc();
|
|
|
|
// FogOfWar 시스템에서 제거
|
|
if (buildingData != null && buildingData.providesVision)
|
|
{
|
|
FogOfWarSystem.Instance?.UnregisterVisionProvider(this);
|
|
}
|
|
|
|
// BuildingManager에서 제거
|
|
if (BuildingManager.Instance != null)
|
|
{
|
|
BuildingManager.Instance.RemoveBuilding(this);
|
|
}
|
|
|
|
// 네트워크 오브젝트 파괴 (약간의 딜레이)
|
|
Invoke(nameof(DespawnBuilding), 0.5f);
|
|
}
|
|
|
|
private void DespawnBuilding()
|
|
{
|
|
if (IsServer && NetworkObject != null)
|
|
{
|
|
NetworkObject.Despawn(true);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 체력 회복
|
|
/// </summary>
|
|
public void Heal(int amount)
|
|
{
|
|
if (!IsServer)
|
|
return;
|
|
|
|
if (buildingData == null)
|
|
return;
|
|
|
|
int healAmount = Mathf.Min(amount, buildingData.maxHealth - _currentHealth.Value);
|
|
_currentHealth.Value += healAmount;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 현재 체력
|
|
/// </summary>
|
|
public int GetCurrentHealth() => _currentHealth.Value;
|
|
|
|
/// <summary>
|
|
/// 최대 체력
|
|
/// </summary>
|
|
public int GetMaxHealth() => buildingData != null ? buildingData.maxHealth : 100;
|
|
|
|
/// <summary>
|
|
/// 체력 비율 (0.0 ~ 1.0)
|
|
/// </summary>
|
|
public float GetHealthPercentage()
|
|
{
|
|
int maxHealth = GetMaxHealth();
|
|
return maxHealth > 0 ? (float)_currentHealth.Value / maxHealth : 0f;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 파괴되었는지 여부
|
|
/// </summary>
|
|
public bool IsDestroyed() => _currentHealth.Value <= 0;
|
|
|
|
#endregion
|
|
|
|
#region Health UI
|
|
|
|
private void CreateHealthBar()
|
|
{
|
|
if (_healthBar != null)
|
|
return;
|
|
|
|
if (healthBarPrefab == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
GameObject healthBarObj = Instantiate(healthBarPrefab, transform);
|
|
_healthBar = healthBarObj.GetComponent<UnitHealthBar>();
|
|
|
|
if (_healthBar != null)
|
|
{
|
|
_healthBar.Initialize(this);
|
|
}
|
|
}
|
|
|
|
private void UpdateHealthUI()
|
|
{
|
|
if (_healthBar != null)
|
|
{
|
|
int current = _currentHealth.Value;
|
|
int max = GetMaxHealth();
|
|
_healthBar.UpdateHealth(current, max);
|
|
}
|
|
|
|
OnHealthChanged?.Invoke(_currentHealth.Value, GetMaxHealth());
|
|
}
|
|
|
|
private void OnHealthValueChanged(int previousValue, int newValue)
|
|
{
|
|
UpdateHealthUI();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Visual Effects
|
|
|
|
[ClientRpc]
|
|
private void ShowDamageEffectClientRpc()
|
|
{
|
|
if (damageEffectPrefab != null)
|
|
{
|
|
Transform spawnPoint = effectSpawnPoint != null ? effectSpawnPoint : transform;
|
|
GameObject effect = Instantiate(damageEffectPrefab, spawnPoint.position, spawnPoint.rotation);
|
|
Destroy(effect, 2f);
|
|
}
|
|
}
|
|
|
|
[ClientRpc]
|
|
private void ShowDestroyEffectClientRpc()
|
|
{
|
|
if (destroyEffectPrefab != null)
|
|
{
|
|
Transform spawnPoint = effectSpawnPoint != null ? effectSpawnPoint : transform;
|
|
GameObject effect = Instantiate(destroyEffectPrefab, spawnPoint.position, spawnPoint.rotation);
|
|
Destroy(effect, 3f);
|
|
}
|
|
}
|
|
|
|
[ClientRpc]
|
|
private void NotifyDestroyedClientRpc()
|
|
{
|
|
if (!IsServer)
|
|
{
|
|
OnDestroyed?.Invoke();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Grid Bounds
|
|
|
|
/// <summary>
|
|
/// Gets the grid-based bounds (from TowerData width/length/height)
|
|
/// This is used for placement validation, NOT the actual collider bounds
|
|
/// Bounds are slightly shrunk to allow adjacent buildings to touch
|
|
/// </summary>
|
|
public Bounds GetGridBounds()
|
|
{
|
|
if (buildingData == null) return new Bounds(transform.position, Vector3.one);
|
|
|
|
Vector3 gridSize = buildingData.GetSize(rotation);
|
|
|
|
// Shrink slightly to allow buildings to be adjacent without Intersects() returning true
|
|
Vector3 shrunkSize = gridSize - Vector3.one * 0.01f;
|
|
return new Bounds(transform.position + Vector3.up * gridSize.y * 0.5f, shrunkSize);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Legacy method, use GetGridBounds() instead
|
|
/// </summary>
|
|
public Bounds GetBounds()
|
|
{
|
|
return GetGridBounds();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IInteractable Implementation (Upgrade)
|
|
|
|
/// <summary>
|
|
/// 상호작용 가능 여부
|
|
/// </summary>
|
|
public bool CanInteract(ulong playerId)
|
|
{
|
|
// 쿨다운 확인
|
|
if (Time.time - _lastInteractionTime < INTERACTION_COOLDOWN)
|
|
return false;
|
|
|
|
// 같은 팀만 업그레이드 가능
|
|
TeamType team = GetPlayerTeam(playerId);
|
|
if (team != _team.Value)
|
|
return false;
|
|
|
|
// 업그레이드 중이면 진행 가능
|
|
if (_isUpgrading.Value)
|
|
return true;
|
|
|
|
// 업그레이드 시작 가능 여부 확인
|
|
if (buildingData == null || buildingData.upgradeTo == 0)
|
|
return false;
|
|
|
|
TowerData upgradeData = GetUpgradeData();
|
|
if (upgradeData == null)
|
|
return false;
|
|
|
|
// 자원이 충분한지 확인
|
|
var coreResourceManager = CoreResourceManager.Instance;
|
|
if (coreResourceManager == null || !coreResourceManager.CanAfford(upgradeData.mana))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 상호작용 실행 (업그레이드)
|
|
/// </summary>
|
|
public void Interact(ulong playerId)
|
|
{
|
|
if (!CanInteract(playerId))
|
|
return;
|
|
|
|
// 서버에 업그레이드 요청
|
|
RequestUpgradeServerRpc(playerId);
|
|
}
|
|
|
|
[Rpc(SendTo.Server)]
|
|
private void RequestUpgradeServerRpc(ulong playerId)
|
|
{
|
|
if (!CanInteract(playerId))
|
|
return;
|
|
|
|
_lastInteractionTime = Time.time;
|
|
|
|
// 업그레이드가 시작되지 않았으면 시작만 하고 종료
|
|
if (!_isUpgrading.Value)
|
|
{
|
|
TowerData upgradeData = GetUpgradeData();
|
|
if (upgradeData == null)
|
|
return;
|
|
|
|
// 자원 확인 및 소비
|
|
var coreResourceManager = CoreResourceManager.Instance;
|
|
if (coreResourceManager == null || !coreResourceManager.CanAfford(upgradeData.mana))
|
|
return;
|
|
|
|
coreResourceManager.SpendResources(upgradeData.mana);
|
|
|
|
// 업그레이드 시작 - 토대로 교체
|
|
StartUpgradeToFoundation(upgradeData);
|
|
return;
|
|
}
|
|
|
|
// 이미 업그레이드 중이면 플레이어의 manpower만큼 진행
|
|
float playerManpower = GetPlayerManpower(playerId);
|
|
_upgradeProgress.Value += playerManpower;
|
|
|
|
// 완료 체크
|
|
TowerData targetData = BuildingManager.Instance?.GetTowerDataById(_upgradeTargetId.Value);
|
|
if (targetData != null && _upgradeProgress.Value >= targetData.manpower)
|
|
{
|
|
CompleteUpgrade(targetData);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 업그레이드 시작 - 건물을 토대로 교체
|
|
/// </summary>
|
|
private void StartUpgradeToFoundation(TowerData upgradeData)
|
|
{
|
|
if (!IsServer) return;
|
|
if (BuildingManager.Instance == null) return;
|
|
if (BuildingManager.Instance.foundationPrefab == null) return;
|
|
|
|
// 현재 건물 정보 저장
|
|
Vector3Int gridPos = gridPosition;
|
|
int rot = rotation;
|
|
ulong owner = _ownerId.Value;
|
|
TeamType team = _team.Value;
|
|
|
|
// BuildingManager에서 건물 제거
|
|
BuildingManager.Instance.RemoveBuilding(this);
|
|
|
|
// FogOfWar 시스템에서 제거
|
|
if (buildingData != null && buildingData.providesVision)
|
|
{
|
|
FogOfWarSystem.Instance?.UnregisterVisionProvider(this);
|
|
}
|
|
|
|
// 현재 건물 제거
|
|
NetworkObject.Despawn(true);
|
|
|
|
// 토대 생성
|
|
Vector3 worldPosition = BuildingManager.Instance.GridToWorld(gridPos);
|
|
GameObject foundationObj = Instantiate(BuildingManager.Instance.foundationPrefab, worldPosition + upgradeData.placementOffset, Quaternion.Euler(0, rot * 90f, 0));
|
|
NetworkObject foundationNetObj = foundationObj.GetComponent<NetworkObject>();
|
|
|
|
if (foundationNetObj != null)
|
|
{
|
|
foundationNetObj.Spawn();
|
|
|
|
BuildingFoundation foundation = foundationObj.GetComponent<BuildingFoundation>();
|
|
if (foundation != null)
|
|
{
|
|
foundation.Initialize(upgradeData, gridPos, rot, owner, team);
|
|
BuildingManager.Instance.RegisterFoundation(foundation);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Destroy(foundationObj);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 업그레이드 완료
|
|
/// </summary>
|
|
private void CompleteUpgrade(TowerData upgradeData)
|
|
{
|
|
if (!IsServer) return;
|
|
|
|
BuildingManager.Instance?.UpgradeBuilding(this, upgradeData);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 상호작용 프롬프트 텍스트
|
|
/// </summary>
|
|
public string GetInteractionPrompt()
|
|
{
|
|
// 업그레이드 중이면 진행도 표시
|
|
if (_isUpgrading.Value)
|
|
{
|
|
TowerData targetData = BuildingManager.Instance?.GetTowerDataById(_upgradeTargetId.Value);
|
|
if (targetData != null)
|
|
{
|
|
float percentage = (_upgradeProgress.Value / targetData.manpower) * 100f;
|
|
return $"[E] Upgrading to {targetData.buildingName} ({percentage:F0}%)";
|
|
}
|
|
return "[E] Upgrading...";
|
|
}
|
|
|
|
TowerData upgradeData = GetUpgradeData();
|
|
if (upgradeData == null)
|
|
return "";
|
|
|
|
var coreResourceManager = CoreResourceManager.Instance;
|
|
bool canAfford = coreResourceManager != null && coreResourceManager.CanAfford(upgradeData.mana);
|
|
|
|
string costText = canAfford ? $"{upgradeData.mana}" : $"<color=red>{upgradeData.mana}</color>";
|
|
return $"[E] Upgrade to {upgradeData.buildingName} ({costText})";
|
|
}
|
|
|
|
public string GetInteractionAnimation()
|
|
{
|
|
return "Build"; // 건설 애니메이션 재사용
|
|
}
|
|
|
|
public EquipmentData GetEquipmentData()
|
|
{
|
|
return buildingData?.constructionEquipment;
|
|
}
|
|
|
|
Transform IInteractable.GetTransform()
|
|
{
|
|
return transform;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 업그레이드 대상 TowerData 반환
|
|
/// </summary>
|
|
public TowerData GetUpgradeData()
|
|
{
|
|
if (buildingData == null || buildingData.upgradeTo == 0)
|
|
return null;
|
|
|
|
if (BuildingManager.Instance == null)
|
|
return null;
|
|
|
|
return BuildingManager.Instance.GetTowerDataById(buildingData.upgradeTo);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 플레이어의 팀 가져오기
|
|
/// </summary>
|
|
private TeamType GetPlayerTeam(ulong playerId)
|
|
{
|
|
if (NetworkManager.Singleton != null && NetworkManager.Singleton.ConnectedClients != null)
|
|
{
|
|
if (NetworkManager.Singleton.ConnectedClients.TryGetValue(playerId, out var client))
|
|
{
|
|
if (client.PlayerObject != null)
|
|
{
|
|
var teamMember = client.PlayerObject.GetComponent<ITeamMember>();
|
|
if (teamMember != null)
|
|
{
|
|
return teamMember.GetTeam();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return TeamType.Player;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 플레이어의 manpower 가져오기
|
|
/// </summary>
|
|
private float GetPlayerManpower(ulong playerId)
|
|
{
|
|
if (NetworkManager.Singleton != null && NetworkManager.Singleton.ConnectedClients != null)
|
|
{
|
|
if (NetworkManager.Singleton.ConnectedClients.TryGetValue(playerId, out var client))
|
|
{
|
|
if (client.PlayerObject != null)
|
|
{
|
|
var playerInteraction = client.PlayerObject.GetComponent<PlayerInteraction>();
|
|
if (playerInteraction != null)
|
|
{
|
|
return playerInteraction.WorkPower;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 10f; // 기본값
|
|
}
|
|
|
|
private void OnUpgradeProgressChanged(float oldValue, float newValue)
|
|
{
|
|
// 업그레이드 진행 UI 업데이트 (필요시 구현)
|
|
}
|
|
|
|
private void OnUpgradingChanged(bool oldValue, bool newValue)
|
|
{
|
|
// 업그레이드 상태 변경 시 처리 (필요시 구현)
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Gizmos
|
|
|
|
private void OnDrawGizmos()
|
|
{
|
|
if (!showGridBounds || buildingData == null) return;
|
|
|
|
Bounds bounds = GetGridBounds();
|
|
|
|
// 팀 색상으로 표시
|
|
Color teamColor = Application.isPlaying ? TeamManager.GetTeamColor(_team.Value) : TeamManager.GetTeamColor(initialTeam);
|
|
Gizmos.color = new Color(teamColor.r, teamColor.g, teamColor.b, 0.3f);
|
|
Gizmos.DrawWireCube(bounds.center, bounds.size);
|
|
}
|
|
|
|
private void OnDrawGizmosSelected()
|
|
{
|
|
if (buildingData == null) return;
|
|
|
|
Bounds bounds = GetGridBounds();
|
|
Gizmos.color = Color.yellow;
|
|
Gizmos.DrawWireCube(bounds.center, bounds.size);
|
|
|
|
// Draw grid position
|
|
if (BuildingManager.Instance != null)
|
|
{
|
|
Vector3 worldPos = BuildingManager.Instance.GridToWorld(gridPosition);
|
|
Gizmos.color = Color.magenta;
|
|
Gizmos.DrawSphere(worldPos, 0.2f);
|
|
}
|
|
|
|
// Draw vision range (if provides vision)
|
|
if (buildingData.providesVision)
|
|
{
|
|
Gizmos.color = Color.cyan;
|
|
Gizmos.DrawWireSphere(transform.position, buildingData.visionRange);
|
|
}
|
|
|
|
// Draw team info label
|
|
#if UNITY_EDITOR
|
|
if (Application.isPlaying)
|
|
{
|
|
string teamName = TeamManager.GetTeamName(_team.Value);
|
|
UnityEditor.Handles.Label(transform.position + Vector3.up * 3f,
|
|
$"Owner: {_ownerId.Value}\nTeam: {teamName}");
|
|
}
|
|
else if (useInitialOwner)
|
|
{
|
|
string teamName = TeamManager.GetTeamName(initialTeam);
|
|
UnityEditor.Handles.Label(transform.position + Vector3.up * 3f,
|
|
$"Initial Owner: {initialOwnerId}\nTeam: {teamName}");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|