using System; using System.Collections.Generic; using System.Numerics; using UnityEngine; namespace Skill_V2 { public class Skill : Entity { [SerializeField] CircleCollider2D castDistanceIndicator; StatData castDistance; StatData coolTime; int currentSequenceIndex = -1; float elapsedCoolTime; Bitset32.Mask targetTags; BIValue damageCalculator = new BIValue(); ContactFilter2D layerFilter; List castSkillDistOverlaped = new(); public Entity Caster { get; private set; } public Bitset32.Mask TargetTags => targetTags; public SkillData Data { get; private set; } public float CastDistance => castDistance != null ? castDistance.Value : 0f; public SkillSequence CurrentSequence => currentSequenceIndex >= 0 ? Data.Sequences[currentSequenceIndex] : null; public bool IsInCoolTime => coolTime != null ? elapsedCoolTime < (float)coolTime.Value : false; public float CoolTimeRemainTime => coolTime != null ? Mathf.Clamp((float)coolTime.Value - elapsedCoolTime, 0, (float)coolTime.Value) : 0f; public float ElapsedCoolTimeRate => coolTime != null ? Mathf.Clamp01(elapsedCoolTime / (float)coolTime.Value) : 1f; private void Awake() { AddTags(EntityTag.Skill); } private void Update() { var currentState = CurrentSequence; if (currentState != null) { if (currentState.IsDone) { currentSequenceIndex++; if (currentSequenceIndex < Data.Sequences.Count) { currentState.Exit(); CurrentSequence.Enter(); } else { currentSequenceIndex = -1; } } else { currentState.Excute(); } } } public void SetSkillData(SkillData skillData) { Debug.Assert(skillData != null, Logger.LogMessage(LogType.Assert, nameof(SetSkillData), "SkillData is null")); Data = skillData; for (int i = 0; i < skillData.Sequences.Count; i++) { skillData.Sequences[i].Initialize(this); } Data.TryGetStat(StatID.CastDistance, out castDistance); Data.TryGetStat(StatID.CoolTime, out coolTime); } public void UpdateSkillCoolTime() { if (!IsInCoolTime) return; elapsedCoolTime = Mathf.Min(elapsedCoolTime + Time.deltaTime, coolTime.Value); } public void SetTargetTags(LayerMask mask, params EntityTag[] tags) { layerFilter.SetLayerMask(mask); targetTags = Bitset32.GetMask(Array.ConvertAll(tags, (t) => (Enum)t)); } public void Cast(Entity caster, BigInteger atk) { Caster = caster; damageCalculator.ClearModifiers(); damageCalculator.DefaultValue = atk; if (Data.TryGetStat(StatID.SkillDamage, out var stat)) { damageCalculator.AddModifiers(new ValueModifier(ValueModifier.Type.Percent, stat.Value)); } Stop(); if(Data.Sequences.Count > 0) { currentSequenceIndex = 0; transform.position = caster.CenterPivot; ChangeLookDirection(caster.LookDirection); CurrentSequence.Enter(); } } public BigInteger GetSkillDamage() => damageCalculator.ModifiedValue; public void StartCoolTime() { elapsedCoolTime = 0f; } public void EndCoolTime() { elapsedCoolTime = coolTime.Value; } public void Stop() { CurrentSequence?.Stop(); currentSequenceIndex = -1; } public bool IsTargetExistInCastRange() { if (castDistanceIndicator is null) return false; castDistanceIndicator.radius = CastDistance; castSkillDistOverlaped.Clear(); castDistanceIndicator.OverlapCollider(layerFilter, castSkillDistOverlaped); for (int i = 0; i < castSkillDistOverlaped.Count; i++) { var overlapped = castSkillDistOverlaped[i]; if(overlapped.TryGetComponent(out IBattleEntity entity) && (entity as Entity).HasTagsAny(TargetTags) && entity.IsBattleAvail()) { return true; } } return false; } } }