using DG.Tweening; using Spine; using Spine.Unity.AttachmentTools; using UnityEngine; public class IVCharacter : CreatureBase { private static readonly float F_DamNormal = 24f; private static readonly Color CLR_CharDam = new Color(0.8f, 0.8f, 0.8f); [SerializeField] protected Skeleton skeleton; [SerializeField] private AudioSource asNormalAttack; [SerializeField] private Transform colObj; [SerializeField] ParticleSystem[] attackEffects; [SerializeField] Transform damageIndicatorPos; BoxCollider col; Material mainMaterial; int attackState = 0; int currentCostumeID = -1; int currentWeaponID = -1; protected override void Start() { base.Start(); skeleton = skAnim.Skeleton; var renderer = skAnim.GetComponent(); mainMaterial = renderer.sharedMaterial; col = colObj.GetChild(0).GetComponent(); bFriendly = true; } protected override void InitTween() { twcSummon = transform.DOScale(Global.V3_1, 1.1f).SetEase(Ease.OutQuad).OnComplete(SummonEnd).SetAutoKill(false).Pause(); twcUnsummon = transform.DOScale(Global.V3_1, 1.1f).SetEase(Ease.InQuad).OnComplete(UnsummonEnd).SetAutoKill(false).Pause(); } protected override void OnDie() { base.OnDie(); if (bFriendly) battleMgr.DieFriendly(); else battleMgr.DieEnemy(eCreatureClass.charEnemy, 0); } protected override void SearchTarget() { if (!IsBattleAvail()) { if (battleMgr.BattlePause) ChangeState(eState.idle); return; } fSearchTick = 0f; if (!TargetFinder.TryFindNearest(transform.position, TargetFilter, out target)) { ChangeState(eState.idle); } else { if (stackSkillAvail.Count > 0 && stackSkillAvail.Peek() == -1) { ChangeState(eState.move); stackSkillAvail.Pop(); } else { useSkillRange = stackSkillAvail.Count > 0 ? battleMgr.GetSkillRange(stackSkillAvail.Peek()) : 0; } if (sqrtDistToTarget <= useSkillRange * useSkillRange) { ChangeState(eState.skill); CheckSkill(); } if(animState == eState.rush) { if (!isdash) CheckAttack(); return; } if (sqrtDistToTarget <= attackRange * attackRange) { CheckAttack(); } else { ChangeState(eState.move); } } } public override void Summon(Vector3 v3pos, bool bfrontright) { base.Summon(v3pos, bfrontright); BattleMgr.SSetPetFront(bFriendly, IsLookRight); BattleMgr.SSetGuardianFront(bFriendly, IsLookRight); } public override void Summon() { if (bLoading) { bSummonNeed = true; return; } bDead = false; bSummon = true; bAttackable = false; fAttackTick = 0f; target = null; gameObject.SetActive(true); ChangeStateForce(eState.warp_in); twcSummon.Restart(); if (bInitNeed) { bInitNeed = false; anState = skAnim.AnimationState; } BattleMgr.SSetPetFront(bFriendly, IsLookRight); BattleMgr.SSetGuardianFront(bFriendly, IsLookRight); } public override void Unsummon() { bSummon = true; bAttackable = false; ChangeStateForce(eState.warp_out); } public void MoveToDir(Vector2 moveVec) { switch (animState) { case eState.warp_in: case eState.warp_out: case eState.die: case eState.rush: return; } var newPos = transform.position + (Vector3)moveVec * moveSpeed * Time.deltaTime; newPos.x = Mathf.Clamp(newPos.x, minArea.x, maxArea.x); newPos.y = Mathf.Clamp(newPos.y, minArea.y, maxArea.y); newPos.z = newPos.y * 2f; transform.position = newPos; ChangeLookDirection(moveVec); } protected override void MoveToTarget() { if (!bCoward && moveSpeed <= 0f || target is null) return; Vector2 to = (targetPos - (Vector2)transform.position).normalized; var newPos = transform.position + (Vector3)to * moveSpeed * Time.deltaTime; newPos.x = Mathf.Clamp(newPos.x, minArea.x, maxArea.x); newPos.y = Mathf.Clamp(newPos.y, minArea.y, maxArea.y); newPos.z = newPos.y * 0.1f; transform.position = newPos; ChangeLookDirection(targetTransform); } protected override void OnDamaged(Damage damage) { var indicator = battleMgr.GetDamageIndicatorText(true); if(indicator != null) { indicator.SetText(FormatString.BigIntString1(damage.value), F_DamNormal, CLR_CharDam); indicator.Show(damageIndicatorPos.position); } if (!ptcHit.isPlaying) ptcHit.Play(); } public override void CheckAttack() { if (bStun) return; if (bAttackable && target != null) { ChangeLookDirection(targetTransform); if (SoundMgr.Instance.EfcOn) asNormalAttack.Play(); if(attackEffects.Length > attackState && attackEffects[attackState] != null) attackEffects[attackState].Play(); switch(attackState) { case 0: ChangeState(eState.attack); attackState = 1; break; case 1: ChangeState(eState.attack2); attackState = 2; break; case 2: ChangeState(eState.attack3); attackState = 0; break; } skAnim.timeScale = 1.0f * motionSpeed; backAllowed = 1; } else { ChangeState(eState.idle); } } public override void CheckSkill() { if (bStun) return; if (stackSkillAvail.Count > 0 && target != null) { int skillid = stackSkillAvail.Peek(); if (!battleMgr.IsOnAutoSkill && skillid != battleMgr.GetUseSkillid()) { stackSkillAvail.Pop(); } else { int skillused = battleMgr.UseSkill(bFriendly, skillid, this, target, fCrtDamCalc, iCrtRateCalc, currentAtk); if (skillused != 0) // 스킬 사용함 or 사용할 수 없는 스킬. stackSkillAvail.Pop(); } } } public void RefreshStat() { maxAtk = BuffMgr.Instance.GetCharAtk(); currentAtk = maxAtk; maxHp = BuffMgr.Instance.GetCharHp(); if(currentHp > maxHp) currentHp = maxHp; } #region Ani Event public override void AttackDamage() { if (battleMgr.BattlePause || bSummon || bDead) return; colObj.position = targetPos; const int maxAttackCount = 3; var targets = TargetFinder.GetNears(Position, TargetFilter); int attackCount = 0; for(int i = 0; i < targets.Count; i++) { var target = targets[i]; if(col.bounds.Contains((target as Entity).Position)) { target.GetDamage(currentAtk, fCrtDamCalc, iCrtRateCalc); if(++attackCount >= maxAttackCount) break; } } if (bFriendly && attackCount > 0) IVCameraController.SShakeCamera(0.2f, 0.3f, 0); } #endregion Ani Event #region Costume & Color public void SetCostume(int costumeID) => SetSkin(costumeID, currentWeaponID); public void SetWeapon(int weaponID) => SetSkin(currentCostumeID, weaponID); public void SetSkin(int costumeID, int weaponID) { if (costumeID == currentCostumeID && weaponID == currentWeaponID) return; currentCostumeID = costumeID; currentWeaponID = weaponID; var newSkin = new Skin("Custom"); newSkin.AddSkin(skeleton.Data.DefaultSkin); if (skeleton.Data.TryGetSkin($"Costume/{currentCostumeID}", out Skin costumeSkin)) newSkin.AddSkin(costumeSkin); if (skeleton.Data.TryGetSkin($"Weapon/{currentWeaponID}", out Skin weaponSkin)) newSkin.AddSkin(weaponSkin); var repackedSkin = newSkin.GetRepackedSkin("Custom", mainMaterial, out var newMat, out var newTex); skeleton.SetSkin(repackedSkin); skeleton.SetSlotsToSetupPose(); skAnim.Update(0); AtlasUtilities.ClearCache(); } #endregion Costume & color }