You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1204 lines
33 KiB

using DG.Tweening;
using DG.Tweening.Core;
using DG.Tweening.Plugins.Options;
using IVDataFormat;
using Spine;
using Spine.Unity;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Numerics;
using UnityEngine;
using AnimationState = Spine.AnimationState;
using Quaternion = UnityEngine.Quaternion;
using Vector2 = UnityEngine.Vector2;
using Vector3 = UnityEngine.Vector3;
// 전투용 캐릭터/몬스터에서 상속받는 부모 클래스.
public class CreatureBase : MonoBehaviour
{
protected static readonly Vector3 V3_MonSizeNormal = new Vector3(0.75f, 0.75f, 0.75f);
protected static readonly Vector3 V3_MonSizeElite = new Vector3(0.8f, 0.8f, 0.8f);
protected static readonly Vector3 V3_MonSizeBoss = new Vector3(1f, 1f, 1f);
protected TweenerCore<Vector3, Vector3, VectorOptions> twcSummon = null;
protected TweenerCore<Vector3, Vector3, VectorOptions> twcUnsummon = null;
protected BattleMgr battleMgr;
protected int iIndex = -1;
protected bool bInitNeed = true;
protected bool bLoading = false;
protected bool bSummonNeed = false;
protected bool bFriendly = false;
protected bool bcoward = false;
protected int isDamaged = 0;
#region Battle State
public enum eState
{
idle,
move,
attack,
damage,
die,
skill,
ceremony,
warp_in,
warp_out,
attack2,
attack3,
rush,
}
public enum eCreatureClass
{
charEnemy = -2,
character = -1,
normal = 0,
elite = 1,
boss = 2,
bigboss=3,
summon = 10
}
/// <summary>
/// 몬스터 위치별 번호
/// </summary>
public enum eDgCreatureClass
{
DgN1,
DgN2,
DgN3,
DgN4,
DgN5,
DgE1,
DgBs,
DgAwBs
}
public bool IsInit { get; private set; }
protected ParticleSystem ptcHit;
[SerializeField] protected SkeletonAnimation skAnim;
Dictionary<EventData, Action> animEventHandlers = new Dictionary<EventData, Action>();
AnimationState _anState;
protected AnimationState anState
{
get => _anState;
set
{
if(_anState != null)
_anState.Event -= HandleAnimEvent;
KeyValuePair<string, Action>[] handlerPairs =
{
new KeyValuePair<string, Action>(nameof(AttackDamage), AttackDamage),
new KeyValuePair<string, Action>(nameof(CheckAttackState), CheckAttackState),
new KeyValuePair<string, Action>(nameof(CheckDamageState), CheckDamageState),
new KeyValuePair<string, Action>(nameof(CheckDieState), CheckDieState),
new KeyValuePair<string, Action>(nameof(SkillEffect), SkillEffect),
new KeyValuePair<string, Action>(nameof(CheckSkillState), CheckSkillState),
new KeyValuePair<string, Action>(nameof(CheckCeremonyState), CheckCeremonyState),
new KeyValuePair<string, Action>(nameof(CheckWarp_outState), CheckWarp_outState),
};
_anState = value;
animEventHandlers.Clear();
for(int i = 0; i < handlerPairs.Length; ++i)
{
var eventData = skAnim.Skeleton.Data.FindEvent(handlerPairs[i].Key);
if (eventData != null)
animEventHandlers.Add(eventData, handlerPairs[i].Value);
}
_anState.Event += HandleAnimEvent;
}
}
protected HpBar hpBar;
[SerializeField]
protected eState animState = eState.idle;
[SerializeField]
protected eCreatureClass creatureClass = eCreatureClass.normal;
protected int iTargetIndex = -1;
protected Vector3 v3TargetPos = Vector3.zero;
protected float fTargetDistance = 9999f;
protected float fUseSkillRange = 0f;
protected bool bCanMove = true;
protected bool bCanAttack = true;
protected bool bDead = false;
protected bool bSummon = false;
protected bool bAttackable = false;
public bool IsLookRight { get; protected set; }
[SerializeField]
protected float fAttackTick = 0f;
protected float fAttackTime = 1.2f;
protected float fSearchTick = 0f;
protected const float F_SearchTime = 0.1f;
protected BoxCollider areaSize;
protected Vector3 minArea;
protected Vector3 maxArea;
[Range(0.1f, 1.0f)] protected float atkSpeed = 1.0f;
[Range(1.0f, 4.0f)] protected float motionSpeed = 1.0f;
protected Stack<int> stackSkillAvail;
#endregion Battle State
#region Status
protected float fRange = 7f;
protected float frecogRange = 10f; // 플레이어 인식범위
protected float enemySlowSpeed = 0.2f; // 적군 농땡이 속도
protected eMonsterType eType;
protected bool alertPlayer = false;
protected BigInteger biAtk = 0L;
protected BigInteger biHp = 10;
protected float fCrtDam = dConst.RateMaxFloat;
protected int iCrtRate = 0;
protected float fMov = dConst.RateMaxFloat;
protected float fSpd = dConst.RateMaxFloat;
protected BigInteger biAtkCalc = 1;
protected BigInteger biHpCalc = 10;
protected BigInteger biHpCur = 10;
protected float fCrtDamCalc = dConst.RateMaxFloat;
protected int iCrtRateCalc = 0;
protected float fMovCalc = 3f;
protected float fSpdCalc = dConst.RateMaxFloat;
#endregion Status
#region Debuff
public class cDamage
{
public int sec;
public float time;
public BigInteger atk;
public float crtdam;
public int crtrate;
public int idnum;
}
// 둔화.
protected float fDecMov = 0f;
protected float fDecMovTime = 0f;
// 출혈.
protected int iDamageSecCnt = 0;
protected cDamage[] damageSecs = new cDamage[2] { new cDamage(), new cDamage() };
// 기절.
protected bool bStun = false;
protected float fStunTime = 0f;
// 기절/넉백 쿨타임.
protected float fStunPushCool = 0f;
#endregion Debuff
protected int backAllowed = 1;
[SerializeField]
float backtime = 0f;
protected bool isdash = false;
protected virtual void Start()
{
Init();
if (bInitNeed && !bLoading)
{
bInitNeed = false;
anState = skAnim.AnimationState;
}
}
protected void Update()
{
initAnimTimeScale();
if (BattleMgr.Instance.BattlePause || bSummon || bDead) return;
float ftimedec = Time.deltaTime;
#region Debuff
// 둔화.
if (fDecMovTime > 0f)
{
fDecMovTime -= ftimedec;
if (fDecMovTime <= 0f)
{
fDecMov = 0f;
fMovCalc = fMov * 12f / dConst.RateMaxFloat;
}
}
// 출혈.
if (iDamageSecCnt > 0)
{
for (int i = 0; i < damageSecs.Length; i++)
{
if (damageSecs[i].time <= 0f)
continue;
damageSecs[i].time -= ftimedec;
if (damageSecs[i].time <= 0f)
{
GetDamage(damageSecs[i].atk, damageSecs[i].crtdam, damageSecs[i].crtrate, damageSecs[i].idnum);
iDamageSecCnt--;
continue;
}
int isec = (int)damageSecs[i].time;
if (isec < damageSecs[i].sec)
{
damageSecs[i].sec = isec;
GetDamage(damageSecs[i].atk, damageSecs[i].crtdam, damageSecs[i].crtrate, damageSecs[i].idnum);
}
}
}
// 기절.
if (bStun)
{
fStunTime -= ftimedec;
if (fStunTime <= 0f)
{
bStun = false;
}
}
else if (fStunPushCool > 0f)
{
fStunPushCool -= ftimedec;
}
#endregion Debuff
if (bStun) return;
// 유저가 터치하면 이동
if (bFriendly && VirtualPad.isTouchScreen())
{
ChangeLookDirection(VirtualPad.GetPointDirection());
if (animState != eState.move)
{
ChangeStateForce(eState.move);
}
}
// 아니라면 AI로 동작
else
{
switch (animState)
{
case eState.idle:
// 공격 대기 시간 체크.
if (fAttackTick < fAttackTime)
{
fAttackTick += Time.deltaTime;
if (fAttackTick >= fAttackTime)
bAttackable = true;
}
// 목표 재설정 대기 시간 체크.
if (fSearchTick < F_SearchTime)
{
fSearchTick += Time.deltaTime;
if (fSearchTick >= F_SearchTime)
SearchTarget();
}
// 현재 상태가 idle이고 공격 범위 안에 있으며 공격력이 0이 아니면 공격 체크.
if (animState == eState.idle && fTargetDistance <= fRange && biAtk != 0)
CheckAttack();
break;
case eState.move:
// 공격 대기 시간 체크.
if (fAttackTick < fAttackTime)
{
fAttackTick += Time.deltaTime;
if (fAttackTick >= fAttackTime)
bAttackable = true;
}
// 목표 재설정 대기 시간 체크.
if (fSearchTick < F_SearchTime)
{
fSearchTick += Time.deltaTime;
if (fSearchTick >= F_SearchTime)
SearchTarget();
}
// 현재 상태가 move이고 이동 이후 공격 범위 안에 있으며 공격력이 0이 아니면 공격 체크.
if (animState == eState.move && MoveToTarget() <= fRange && biAtk != 0)
CheckAttack();
break;
// 스킬 중에 구르는 모션이 필요한게 있음. 아직 파악 불가.
case eState.rush:
backAllowed--;
// 공격 대기 시간 체크.
if (fAttackTick < fAttackTime)
{
fAttackTick += Time.deltaTime;
if (fAttackTick >= fAttackTime)
bAttackable = true;
}
//1f 가 시간
backtime += Time.deltaTime;
if (backtime > 0.4f)
{
CheckAttack();
backtime = 0;
isdash = false;
}
break;
}
}
}
#region Base & Init
// 컴포넌트 세팅.
public void SetComponent(BattleMgr mgr, HpBar hpbar)
{
battleMgr = mgr;
hpBar = hpbar;
}
// 피아 지정.
public void SetFriendly(bool bfriendly)
{
bFriendly = bfriendly;
}
// 크리쳐 종류, 인덱스 지정.
public void SetClassIndex(eCreatureClass cls, int index)
{
creatureClass = cls;
iIndex = index;
switch (creatureClass)
{
case eCreatureClass.normal:
skAnim.transform.localScale = V3_MonSizeNormal;
break;
case eCreatureClass.elite:
skAnim.transform.localScale = V3_MonSizeElite;
break;
case eCreatureClass.boss:
skAnim.transform.localScale = V3_MonSizeBoss;
break;
}
}
// 초기화.
public void Init()
{
if(IsInit) return;
IsInit = true;
ptcHit = transform.Find("hit").GetComponent<ParticleSystem>();
InitTween();
stackSkillAvail = new Stack<int>();
}
// 트윈 초기화.
protected virtual void InitTween()
{
twcSummon = transform.DOScale(Global.V3_1, 0.8f).ChangeStartValue(Global.V3_001).SetEase(Ease.OutQuad).OnComplete(SummonEnd).SetAutoKill(false).Pause();
twcUnsummon = transform.DOScale(Global.V3_1, 2.0f).SetEase(Ease.InQuad).OnComplete(UnsummonEnd).SetAutoKill(false).Pause();
}
#endregion Base & Init
#region Stat
// 스탯 세팅.
public void SetStatus(BigInteger fatk, BigInteger fhp, float fcrtdam, int icrtrate, float fmov, float fspd, float frange, bool bhpcurkeep = false, bool isrunaway = false, int igroup = 1)
{
biAtk = fatk;
biHp = fhp;
fCrtDam = fcrtdam;
iCrtRate = icrtrate;
fMov = fmov;
fSpd = fspd;
eType = (eMonsterType)igroup;
biAtkCalc = biAtk;
biHpCalc = biHp;
if (!bhpcurkeep)
biHpCur = biHpCalc;
fCrtDamCalc = fCrtDam;
iCrtRateCalc = iCrtRate;
fSpdCalc = fSpd;
fMovCalc = fMov * 12f / dConst.RateMaxFloat;
if (fDecMov > 0f)
fMovCalc *= (1f - fDecMov);
fRange = frange / dConst.RateMaxFloat;
float foffset = dConst.RateMaxFloat / fSpdCalc;
atkSpeed = Mathf.Lerp(0.1f, 0.75f, foffset); // 공속이 높을 수록 0.1로
motionSpeed = Mathf.Lerp(4.0f, 1.5f, foffset); // 공속이 높을 수록 4로
fAttackTime = 0.5f * atkSpeed;
bcoward = false;
if (isrunaway)
{
bcoward = true;
}
areaSize = BattleMgr.SGetBgSize();
minArea = areaSize.bounds.min;
maxArea = areaSize.bounds.max;
}
public eCreatureClass GetCreatureClass()
{
return creatureClass;
}
public BigInteger GetAtk()
{
return biAtkCalc;
}
public float GetCrtDam()
{
return fCrtDamCalc;
}
public int GetCrtRate()
{
return iCrtRateCalc;
}
public eMonsterType GetMonType()
{
return eType;
}
public bool GetDie()
{
return bDead;
}
#endregion Stat
#region Transform Control
/// <summary>
/// 타겟 위치 참조하여 바라보는 방향 변경.
/// </summary>
protected void ChangeFront()
{
ChangeLookDirection(v3TargetPos - transform.position);
}
protected void ChangeLookDirection(Vector2 lookDirection)
{
if (lookDirection.x >= 0)
{
IsLookRight = true;
transform.rotation = Quaternion.Euler(Vector3.zero);
}
else
{
IsLookRight = false;
transform.rotation = Quaternion.Euler(Global.V3_RotRev);
}
}
public virtual void MoveToDir(Vector2 moveVec)
{
switch (animState)
{
case eState.warp_in:
case eState.warp_out:
case eState.die:
case eState.rush:
return;
}
Vector3 v3framepos;
if(bFriendly)
v3framepos = Vector3.MoveTowards(transform.position, transform.position + (Vector3)moveVec, fMovCalc * Time.deltaTime);
else
v3framepos = Vector3.MoveTowards(transform.position, v3TargetPos, fMovCalc * Time.deltaTime);
v3framepos.z = v3framepos.y * 2f;
transform.position = v3framepos;
ChangeLookDirection(moveVec);
}
// 타겟쪽으로 이동(1프레임).
protected virtual float MoveToTarget()
{
if (!bcoward)
{
if (fMovCalc <= 0f)
return fTargetDistance;
}
ChangeFront();
Vector3 v3framepos = Vector3.MoveTowards(transform.position, v3TargetPos, fMovCalc * Time.deltaTime);
v3framepos.z = v3framepos.y * 0.1f;
transform.position = v3framepos;
fTargetDistance = Vector3.Distance(v3framepos, v3TargetPos);
return fTargetDistance;
}
// 타겟쪽에서 멀어짐 이동(1프레임).
protected float BackToTarget()
{
if (!bcoward)
{
if (fMovCalc <= 0f)
return fTargetDistance;
}
float speed = 0.02f;
ChangeFront();
Vector3 moveVec = -(v3TargetPos - transform.position).normalized;
transform.Translate(moveVec * fMovCalc * speed);
Vector3 v3framepos = transform.position;
if (bcoward)
{
if (v3framepos.x < -25)
{
v3framepos.x = -25;
}
if (v3framepos.x > 25)
{
v3framepos.x = 25;
}
if (v3framepos.y < -25)
{
v3framepos.y = -25;
}
if (v3framepos.y > 25)
{
v3framepos.y = 25;
}
}
v3framepos.z = v3framepos.y * 2f;
return fTargetDistance;
}
// 타겟쪽에게 대시 이동(1프레임).
public void FrontToTarget(float fdt)
{
isdash = true;
ChangeState(eState.rush);
ChangeFront();
Vector3 moveVec = (v3TargetPos - transform.position).normalized * fdt;//5 가 거리
transform.DOMove(transform.position + moveVec, 0.8f).SetEase(Ease.OutQuart).OnComplete(InitPos);//1f 가 시간
}
public void InitPos()
{
MoveToTarget();
}
// 타겟 반대편으로 이동
public void FrontToTargetR(float fdt)
{
isdash = true;
ChangeState(eState.rush);
Vector3 moveVec = -((v3TargetPos - transform.position).normalized * fdt);//5 가 거리
transform.DOMove(transform.position + moveVec, 0.8f).SetEase(Ease.OutQuart);//1f 가 시간
}
public virtual void MoveToRush(int dir, float fdt, bool bright)
{
Vector3 dirVec;
switch (dir)
{
case 0:
dirVec = Vector3.up;
break;
case 1:
dirVec = Vector3.Lerp(Vector3.up, Vector3.right, 0.33f).normalized;
break;
case 2:
dirVec = Vector3.Lerp(Vector3.up, Vector3.right, 0.66f).normalized;
break;
case 3:
dirVec = Vector3.right;
break;
case 4:
dirVec = Vector3.Lerp(Vector3.right, Vector3.down, 0.33f).normalized;
break;
case 5:
dirVec = Vector3.Lerp(Vector3.right, Vector3.down, 0.66f).normalized;
break;
case 6:
dirVec = Vector3.down;
break;
case 7:
dirVec = Vector3.Lerp(Vector3.down, Vector3.left, 0.33f).normalized;
break;
case 8:
dirVec = Vector3.Lerp(Vector3.down, Vector3.left, 0.66f).normalized;
break;
case 9:
dirVec = Vector3.left;
break;
case 10:
dirVec = Vector3.Lerp(Vector3.left, Vector3.up, 0.33f).normalized;
break;
case 11:
dirVec = Vector3.Lerp(Vector3.left, Vector3.up, 0.66f).normalized;
break;
default:
dirVec = (v3TargetPos - transform.position).normalized;
break;
}
dirVec = bright ? dirVec : new Vector3(dirVec.x * -1, dirVec.y, dirVec.z);
Vector3 moveVec = dirVec * fdt;//5 가 거리
transform.DOMove(transform.position + moveVec, 0.2f).SetEase(Ease.OutQuart);//1f 가 시간
}
#endregion Transform Control
#region Object Control
public bool IsBattleAvail()
{
return gameObject.activeSelf && !bDead && !bSummon;
}
public bool IsSkillAvail()
{
return fTargetDistance <= fRange && (animState == eState.idle || animState == eState.attack);
}
// 애니메이션 로드 대기중.
public void SetLoading()
{
bLoading = true;
}
public virtual void Summon(Vector3 v3pos, bool bfrontright)
{
transform.position = v3pos;
ChangeLookDirection(new Vector2(bfrontright ? 1f : -1f, 0f));
skAnim.skeleton.A = 1.0f;
Summon();
}
public virtual void Summon()
{
// 애니메이션 로드 대기중이면 소환 안함. 로드 완료되는 타이밍에 소환.
if (bLoading)
{
bSummonNeed = true;
return;
}
bDead = false;
bSummon = true;
bAttackable = false;
fAttackTick = 0f;
iTargetIndex = -1;
v3TargetPos = Vector3.zero;
fTargetDistance = 9999f;
gameObject.SetActive(true);
twcSummon.Restart();
// 애니메이션이 다른 애니메이션으로 변경되면 새로 로드해야 함.
if (bInitNeed)
{
bInitNeed = false;
anState = skAnim.AnimationState;
}
ChangeStateForce(eState.idle);
}
public void SummonEnd()
{
bSummon = false;
SearchTarget();
}
public virtual void Unsummon()
{
bSummon = true;
bAttackable = false;
twcUnsummon.Restart();
}
public void UnsummonEnd()
{
ChangeState(eState.idle);
gameObject.SetActive(false);
}
#endregion Object Control
#region Skill & Attack
// 사용 가능한 스킬 추가.
public void AddSkillAvail(int idx)
{
stackSkillAvail.Push(idx);
}
// 사용 가능한 스킬 모두 제거.
public void ClearSkillAvail()
{
stackSkillAvail.Clear();
}
public virtual void CheckSkill()
{
if (bStun)
return;
// 스킬이 사용 가능할 경우.
if (stackSkillAvail.Count > 0 && iTargetIndex >= 0)
{
//스킬 사용부분
int iskillid = stackSkillAvail.Peek();
int iskillused = battleMgr.UseSkill(bFriendly, iskillid, transform, iTargetIndex, battleMgr.GetTrfTarget(bFriendly, iTargetIndex), fCrtDamCalc, iCrtRateCalc, biAtkCalc);
// 스킬 사용함.
if (iskillused > 0)
{
stackSkillAvail.Pop();
//ChangeState(eState.skill);
return;
}
// 사용할 수 없는 스킬.
if (iskillused < 0)
{
stackSkillAvail.Pop();
}
}
}
public virtual void CheckAttack()
{
if (bStun)
return;
// 공격 가능(공격 속도 참조한 공격 시간 경과됨).
if (bAttackable)
{
ChangeFront();
ChangeState(eState.attack);
}
else
{
ChangeState(eState.idle);
}
}
#endregion Skill & Attack
#region Damage & Heal & Die
// 피격.
public virtual void GetDamage(BigInteger biatk, float fcrtdam, int icrtrate, int idnum = -1)
{
if (BattleMgr.Instance.BattlePause || bSummon || bDead) return;
bool bcrt = BattleMgr.SIsCritical(icrtrate);
biatk = BattleMgr.SCalcCrtDam(biatk, fcrtdam, icrtrate, bcrt);
if (biHpCur >= biHpCalc)
biHpCur = biHpCalc;
if (idnum != -1)
BattleMgr.SSetTotalDamage(biatk, idnum);
biHpCur -= biatk;
if (biHpCur <= 0)
{
hpBar.ShowHpBar(0f);
battleMgr.AddPvpDamage(bFriendly, biatk, 0f);
ChangeState(eState.die);
}
else
{
float fractionalPart = (float)(biHpCur * 10000 / biHpCalc) / 10000f;
hpBar.ShowHpBar(fractionalPart);
battleMgr.AddPvpDamage(bFriendly, biatk, fractionalPart);
if (eType == eMonsterType.SmallObject)
{
ChangeState(eState.damage);
}
else
{
StartCoroutine(DamagedRed());
}
}
battleMgr.ShowDamage(creatureClass, transform.position, biatk, bcrt);
if (!ptcHit.isPlaying)
{
ptcHit.Play();
}
}
IEnumerator DamagedRed()
{
Spine.Skeleton sk = skAnim.skeleton;
sk.SetColor(Color.red);
yield return new WaitForSeconds(0.2f);
sk.SetColor(Color.white);
}
// 회복.
public void GetHeal(float frate)
{
if (BattleMgr.Instance.BattlePause || bSummon || bDead) return;
BigInteger birate = new BigInteger(frate);
BigInteger biadd = biHpCalc * birate / dConst.RateMaxBi;
biHpCur += biadd;
if (biHpCur > biHpCalc)
biHpCur = biHpCalc;
//hpBar.ShowHpBar(fHpCur / fHpCalc);
}
// 사망시 처리.
protected virtual void OnDie()
{
bDead = true;
bAttackable = false;
SetIsDamaged(0);
twcUnsummon.Restart();
}
public int GetIsDamaged()
{
return isDamaged;
}
public void SetIsDamaged(int idamcnt)
{
isDamaged = idamcnt;
}
#endregion Damage & Heal & Die
#region Debuff
// 초기화.
public void ResetDebuff()
{
fDecMov = 0f;
fDecMovTime = 0f;
damageSecs[0].time = 0;
damageSecs[1].time = 0;
bStun = false;
fStunTime = 0f;
fStunPushCool = 0f;
}
// 둔화(이동속도 감소).
public void SetDecMove(float ftime, float fvalue)
{
if (BattleMgr.Instance.BattlePause || bSummon || bDead) return;
if (fvalue < fDecMov) return;
fDecMov = fvalue;
fDecMovTime = ftime;
fMovCalc = fMov * 12f / dConst.RateMaxFloat;
if (fDecMov > 0f)
fMovCalc *= (1f - fDecMov);
}
// 출혈(초당 데미지).
public virtual void SetDamageSec(float ftime, BigInteger fatk, float fcrtdam, int icrtrate, int idnum = -1)
{
if (BattleMgr.Instance.BattlePause || bSummon || bDead) return;
if (ftime < 1f)
{
return;
}
int index = -1;
for (int i = 0; i < damageSecs.Length; i++)
{
if (damageSecs[i].time <= 0f)
{
index = i;
break;
}
}
if (index < 0)
return;
damageSecs[index].time = ftime - 0.05f;
damageSecs[index].sec = (int)ftime;
damageSecs[index].atk = fatk;
damageSecs[index].crtdam = fcrtdam;
damageSecs[index].crtrate = icrtrate;
damageSecs[index].idnum = idnum;
iDamageSecCnt++;
}
// 기절.
public void SetStun(float ftime)
{
if (BattleMgr.Instance.BattlePause || bSummon || bDead) return;
if (fStunPushCool > 0f) return;
if (!bStun)
{
bStun = true;
ChangeStateForce(eState.idle);
if (creatureClass == eCreatureClass.boss)
fStunPushCool = 10f;
}
fStunTime = ftime;
}
// 넉백.
public void PushFrame(float fpushforce)
{
if (BattleMgr.Instance.BattlePause || bSummon || bDead || fStunPushCool > 0f) return;
if (BattleMgr.CurrentBattleType != BattleMgr.BattleType.GuardianDungeon)
PushFrameForce(fpushforce);
if (creatureClass == eCreatureClass.boss)
fStunPushCool = 10f;
}
// 넉백(강제).
protected void PushFrameForce(float fpushforce)
{
if (BattleMgr.CurrentBattleType == BattleMgr.BattleType.GuardianDungeon)
return;
Vector3 v3moveto = transform.position;
fpushforce *= 0.5f;
if (IsLookRight)
{
v3moveto.x -= 10;
}
else
{
v3moveto.x += 10;
}
Vector3 v3framepos = Vector3.MoveTowards(transform.position, v3moveto, fpushforce);
Vector3 v3movedir = v3framepos - transform.position;
v3movedir.y = 0f;
v3movedir.z = 0f;
if (gameObject.activeSelf)
{
transform.DOLocalMoveX(transform.position.x + v3movedir.x, 0.5f).SetEase(Ease.OutExpo);
}
}
// 끌어당기기
public void SetPull(BigInteger bivalue, Transform trf)
{
if (BattleMgr.CurrentBattleType != BattleMgr.BattleType.GuardianDungeon)
return;
float pullValue = (int)bivalue / dConst.RateMaxFloat;
Vector3 v3framepos = Vector3.Lerp(transform.position, trf.position, pullValue);
if (gameObject.activeSelf)
{
transform.DOLocalMove(v3framepos, 0.5f).SetEase(Ease.Linear);
}
}
#endregion Debuff
#region Target & Animation
// 타겟 위치 찾기.
protected virtual void SearchTarget() { }
// 애니메이션 이벤트. 스파인 애니메이션에서 호출됨.
void HandleAnimEvent(TrackEntry trackEntry, Spine.Event e)
{
if(animEventHandlers.TryGetValue(e.Data, out System.Action action))
action();
}
// 캐릭터 상태(애니메이션) 변경.
public void ChangeState(eState changestate)
{
if (changestate == animState && changestate != eState.attack && changestate != eState.skill)
return;
animState = changestate;
switch (animState)
{
case eState.idle:
anState.SetAnimation(0, "idle", true);
break;
case eState.move:
anState.SetAnimation(0, "move", true);
break;
case eState.attack:
case eState.attack2:
case eState.attack3:
bAttackable = false;
fAttackTick = 0f;
anState.SetAnimation(0, animState.ToString(), false);
break;
case eState.die:
anState.SetAnimation(0, "die", false);
OnDie();
break;
case eState.skill:
bAttackable = false;
fAttackTick = 0f;
anState.SetAnimation(0, "attack", false);
break;
case eState.ceremony:
anState.SetAnimation(0, "ceremony", false);
break;
case eState.warp_in:
anState.SetAnimation(0, "warp_in", false);
break;
case eState.warp_out:
anState.SetAnimation(0, "warp_out", false);
break;
case eState.rush:
anState.SetAnimation(0, "rush", false);
break;
case eState.damage:
anState.SetAnimation(0, "damage", false);
break;
}
}
// 캐릭터 상태(애니메이션) 변경.
public void ChangeStateForce(eState changestate)
{
if (anState == null)
return;
animState = changestate;
switch (animState)
{
case eState.idle:
anState.SetAnimation(0, "idle", true);
if (BattleMgr.CurrentBattleType == BattleMgr.BattleType.GuardianDungeon)
{
skAnim.gameObject.SetActive(true);
ChangeLookDirection(new Vector2(-1.0f, 0.0f));
}
else
{
skAnim.gameObject.SetActive(true);
ChangeLookDirection(new Vector2(1.0f, 0.0f));
}
break;
case eState.move:
anState.SetAnimation(0, "move", true);
break;
case eState.attack:
bAttackable = false;
fAttackTick = 0f;
anState.SetAnimation(0, animState.ToString(), false);
break;
case eState.die:
anState.SetAnimation(0, "die", false);
OnDie();
break;
case eState.skill:
bAttackable = false;
fAttackTick = 0f;
anState.SetAnimation(0, "attack", false);
break;
case eState.ceremony:
anState.SetAnimation(0, "ceremony", false);
break;
case eState.warp_in:
anState.SetAnimation(0, "warp_in", false);
break;
case eState.warp_out:
anState.SetAnimation(0, "warp_out", false);
break;
case eState.rush:
anState.SetAnimation(0, "rush", false);
break;
case eState.damage:
anState.SetAnimation(0, "damage", false);
break;
}
}
//애니메이션 속도 초가화
public void initAnimTimeScale()
{
if (animState == eState.attack || animState == eState.attack2)
skAnim.timeScale = 1.0f * motionSpeed;
else
skAnim.timeScale = 1.0f;
}
#endregion Target & Animation
#region Ani Event
// 공격 애니메이션 마지막 프레임.
public void CheckAttackState()
{
SearchTarget();
}
// 스킬 애니메이션 마지막 프레임.
public void CheckSkillState()
{
SearchTarget();
}
// 피해받음 애니메이션 마지막 프레임.
public void CheckDamageState()
{
ChangeState(eState.idle);
}
// 사망 애니메이션 마지막 프레임.
public void CheckDieState()
{
}
// 세레머니 애니메이션 마지막 프레임.
public void CheckCeremonyState()
{
ChangeState(eState.idle);
}
public void CheckWarp_outState()
{
ChangeState(eState.idle);
}
// 공격 애니메이션 중간 키(데미지 타이밍).
public virtual void AttackDamage() { }
// 스킬 애니메이션 중간 키.
public virtual void SkillEffect()
{
}
#endregion Ani Event
}