132 lines
4.5 KiB
C#
132 lines
4.5 KiB
C#
using UnityEngine;
|
||
|
||
namespace TKS
|
||
{
|
||
/// <summary>전장의 유닛 1기. 능력치·상태·콜라이더를 가진다. 시각은 UnitView 가 담당.</summary>
|
||
[RequireComponent(typeof(CircleCollider2D))]
|
||
public class Unit : MonoBehaviour
|
||
{
|
||
public string DisplayName;
|
||
public UnitType Type;
|
||
public Faction Faction;
|
||
public bool IsBoss;
|
||
|
||
public int MaxHp;
|
||
public int Hp;
|
||
public int Atk;
|
||
public int Def;
|
||
public float Radius;
|
||
public float SlingMaxDistance;
|
||
/// <summary>궁병 = 슬링샷 최대 거리 × archerRangeRatio (적 가장자리까지의 간격 기준) · 창병 = 대기(견제) 반경.</summary>
|
||
public float AttackRange;
|
||
/// <summary>Resources/Units/<ArtId>/ 아트 폴더 · 비면 도형.</summary>
|
||
public string ArtId = "";
|
||
/// <summary>아트 세로 크기(월드) = unitArtHeight × 반지름 배율.</summary>
|
||
public float ArtHeight;
|
||
|
||
/// <summary>이번 아군 턴에 행동을 마쳤는가.</summary>
|
||
public bool ActedThisTurn;
|
||
/// <summary>보병이 최종 목표점 명중으로 얻은 추가 이동(이동만 가능).</summary>
|
||
public bool BonusMoveAvailable;
|
||
/// <summary>창병 대기(견제) 상태 — 적군 턴에 반경 안에서 움직이는 적에게 피해.</summary>
|
||
public bool Overwatch;
|
||
|
||
public CircleCollider2D Collider { get; private set; }
|
||
public UnitView View { get; private set; }
|
||
|
||
public bool IsAlive
|
||
{
|
||
get { return Hp > 0; }
|
||
}
|
||
|
||
public Vector2 Position
|
||
{
|
||
get { return transform.position; }
|
||
set { transform.position = new Vector3(value.x, value.y, 0f); }
|
||
}
|
||
|
||
public void Init(BalanceConfig cfg, UnitType type, Faction faction, string displayName, float hpMult, float atkMult, bool boss, string artId = null)
|
||
{
|
||
var s = cfg.GetStats(type);
|
||
Type = type;
|
||
Faction = faction;
|
||
DisplayName = displayName;
|
||
IsBoss = boss;
|
||
ArtId = artId ?? "";
|
||
MaxHp = Mathf.Max(1, Mathf.RoundToInt(s.maxHp * hpMult * (boss ? cfg.bossHpMult : 1f)));
|
||
Hp = MaxHp;
|
||
Atk = Mathf.Max(1, Mathf.RoundToInt(s.atk * atkMult));
|
||
Def = s.def;
|
||
float radiusMult = boss ? cfg.bossRadiusMult : 1f;
|
||
Radius = s.radius * radiusMult;
|
||
ArtHeight = cfg.unitArtHeight * radiusMult;
|
||
SlingMaxDistance = s.slingMaxDistance;
|
||
// PD 2026-11-04: 궁병 사거리 = 슬링샷 범위의 비율(+20 %) · 창병은 자산의 대기 반경
|
||
AttackRange = type == UnitType.Archer ? s.slingMaxDistance * cfg.archerRangeRatio : s.attackRange;
|
||
gameObject.name = (faction == Faction.Player ? "P_" : "E_") + displayName;
|
||
|
||
Collider = GetComponent<CircleCollider2D>();
|
||
Collider.radius = Radius;
|
||
Collider.isTrigger = false;
|
||
Collider.enabled = true;
|
||
|
||
View = GetComponentInChildren<UnitView>();
|
||
if (View == null)
|
||
{
|
||
var vgo = new GameObject("View");
|
||
vgo.transform.SetParent(transform, false);
|
||
View = vgo.AddComponent<UnitView>();
|
||
}
|
||
View.Build(this);
|
||
}
|
||
|
||
public void TakeDamage(int dmg)
|
||
{
|
||
if (!IsAlive) return;
|
||
Hp = Mathf.Max(0, Hp - dmg);
|
||
if (View != null)
|
||
{
|
||
View.OnHit();
|
||
View.Refresh();
|
||
}
|
||
if (!IsAlive) Die();
|
||
}
|
||
|
||
void Die()
|
||
{
|
||
Overwatch = false;
|
||
BonusMoveAvailable = false;
|
||
Collider.enabled = false;
|
||
if (View != null) View.PlayDeath();
|
||
}
|
||
|
||
public void SetOverwatch(bool on)
|
||
{
|
||
Overwatch = on && IsAlive;
|
||
if (View != null) View.Refresh();
|
||
}
|
||
|
||
public void ResetForNewTurn()
|
||
{
|
||
ActedThisTurn = false;
|
||
BonusMoveAvailable = false;
|
||
Overwatch = false;
|
||
if (View != null) View.Refresh();
|
||
}
|
||
|
||
public void MarkActed()
|
||
{
|
||
ActedThisTurn = true;
|
||
BonusMoveAvailable = false;
|
||
if (View != null) View.Refresh();
|
||
}
|
||
|
||
public void GrantBonusMove()
|
||
{
|
||
ActedThisTurn = false;
|
||
BonusMoveAvailable = true;
|
||
if (View != null) View.Refresh();
|
||
}
|
||
}
|
||
}
|