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
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
|
|
|
|
}
|