ThreeKingdomsSlingshot/Assets/Scripts/Runtime/Unit.cs

137 lines
5.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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>궁병·책사 = 사거리 = 슬링샷 최대 거리(적 가장자리까지의 간격 기준 · 제자리 사격) · 창병 = 대기(견제) 반경.</summary>
public float AttackRange;
/// <summary>궁병·책사가 사거리 밖(~ 사거리 × (1 + archerRangeRatio))의 적에게 다가갈 수 있는 최대 거리. 다른 병종 = 0.</summary>
public float ArcherMoveAllow;
/// <summary>Resources/Units/&lt;ArtId&gt;/ 아트 폴더 · 비면 도형.</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 #1080: 궁병 사거리 = 슬링샷 범위(제자리 사격) · 그 밖 +비율 안의 적은 들어올 만큼만 다가가 사격 · 창병은 자산의 대기 반경
// 책사(PD 2026-11-05 #1087 ⑤) = 궁병과 같은 사거리 규칙
bool ranged = UnitTypeInfo.IsRanged(type);
AttackRange = ranged ? s.slingMaxDistance : s.attackRange;
ArcherMoveAllow = ranged ? s.slingMaxDistance * cfg.archerRangeRatio : 0f;
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();
}
}
}