using System.Collections.Generic; using UnityEngine; using Spine.Unity; using Skill_V2; using System.Numerics; using System; public struct Damage { public BigInteger value; public bool isCritical; } public class Character : Entity, IBattleEntity { [SerializeField] SkeletonAnimation skeletonAnimation; [SerializeField] StateHandler stateHandler; StatData hpStat; LayerMask layerMask; Skill[] skills = new Skill[0]; public CharacterData Data { get; private set; } public SkeletonAnimation SkeletonAnimation => skeletonAnimation; public StateHandler StateHandler => stateHandler; public Bitset32.Mask TargetTags { get; private set; } public IReadOnlyList Skills => skills; public BigInteger CurrentHp { get; private set; } public bool IsDead => CurrentHp <= 0; public bool IsBattleReady { get; set; } public event Action OnSpawned; public event Action OnDisposed; public event Action OnDamaged; private void Awake() { if (skeletonAnimation == null) skeletonAnimation = GetComponent(); if (stateHandler == null) stateHandler = GetComponent>(); AddTags(EntityTag.Character); } private void Update() { for(int i = 0; i < skills.Length; i++) skills[i].UpdateSkillCoolTime(); } private void OnDestroy() { for (int i = 0; i < skills.Length; i++) { var skill = skills[i]; if (skill != null) continue; skill.Stop(); if(skill.gameObject != null) Destroy(skill.gameObject); } } public void SetData(CharacterData charData) { Debug.Assert(charData != null, Logger.LogMessage(LogType.Assert, nameof(SetData), " data is null")); Debug.Assert(Data == null, Logger.LogMessage(LogType.Assert, nameof(SetData), " data is already set")); Data = charData; if (Data.Skills != null) { skills = new Skill[Data.Skills.Count]; int i = 0; foreach (var skillData in Data.Skills) { skills[i] = Instantiate(skillData.Prefab); skills[i].SetSkillData(skillData); skills[i].Stop(); i++; } } stateHandler.Initialize(this); foreach (var component in GetComponents>()) { component.Initialize(this); } } public void SetTargetTags(LayerMask mask, params EntityTag[] tags) { layerMask = mask; TargetTags = Bitset32.GetMask(Array.ConvertAll(tags, (t) => (Enum)t)); foreach (var skill in skills) { skill.SetTargetTags(layerMask, tags); } } public void Spawn(UnityEngine.Vector2 spawnPos) { Debug.Assert(stateHandler != null, "StateHandler is null"); Debug.Assert(skeletonAnimation != null, "SkeletonAnimation is null"); transform.position = spawnPos; if (Data.TryGetStat(StatID.Hp, out hpStat)) { CurrentHp = hpStat.Value; hpStat.OnValueChanged += OnHpChanged; } OnSpawned?.Invoke(this); } public void Dispose() { if (hpStat != null) hpStat.OnValueChanged -= OnHpChanged; for (int i = 0; i < skills.Length; i++) { skills[i].Stop(); } OnDisposed?.Invoke(this); } private void OnHpChanged() { if (CurrentHp >= (BigInteger)hpStat.Value) CurrentHp = hpStat.Value; } public bool IsBattleAvail() => IsBattleReady; public void GetDamage(BigInteger atk, float critDmgRate, int critRate, int dnum = -1) { if(CurrentHp <= 0) return; bool isCritical = CreatureBase.CheckCritical(critRate); if(isCritical) atk = CreatureBase.GetCriticalDamage(atk, critDmgRate, critRate); Damage dmg = new Damage { value = atk, isCritical = isCritical }; CurrentHp = BigInteger.Max(0, CurrentHp - dmg.value); OnDamaged?.Invoke(this, dmg); } private void OnValidate() { if (skeletonAnimation == null) skeletonAnimation = GetComponent(); if (stateHandler == null) stateHandler = GetComponent>(); } }