1837 lines
72 KiB
C#
1837 lines
72 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEngine;
|
|
using UnityEngine.AI;
|
|
|
|
public class Actor : MyCoroutine
|
|
{
|
|
public GameObject[] gos_Effect;
|
|
public eRole m_Role; // 프리팹 설정
|
|
public eSubRol m_SubRole; // 프리팹 설정
|
|
public eAttackType m_AttackType; // 프리팹 설정
|
|
|
|
protected NavMeshAgent m_NavMeshAgent;
|
|
protected BoxCollider m_BoxCollider;
|
|
eAnim _curanim;
|
|
protected eAnim CurAnim
|
|
{
|
|
get { return _curanim; }
|
|
set
|
|
{
|
|
_curanim = value;
|
|
//if (IsRole(eRole.PC)) Debug.Log("애님 변경 " + value);
|
|
}
|
|
}
|
|
public Animator m_animation;
|
|
protected ActorStatInfo m_Stat;
|
|
protected double receiveaccumulateDmg, m_Damage, m_attackCoolTime; // 내가 받은 피해, 내가 준 피해, 공격 쿨타임
|
|
protected Transform tf_HUD_HP, tf_HUD_Dmg;
|
|
protected Vector3 ObjSize, Original_Scale;
|
|
[SerializeField]
|
|
protected Actor m_Target, m_MyPet;
|
|
protected Dictionary<int, UseMagicInfo> dic_MagicData = new Dictionary<int, UseMagicInfo>();
|
|
protected UseMagicInfo m_Magic;
|
|
protected float m_NavSpeed, m_NavStopDistance, m_HitTime, FindOtherTargetTime = 7.5f, GoToTargetTime = 10f, OriginalAnimSpeed, m_fAttackMoveTime, m_DamageRate = 1f;
|
|
protected bool m_Enemy, IsPlaySkill, IsChargeAttack, m_Auto, DeadStatus, hasAppliedDamage;
|
|
protected bool _isstop;
|
|
protected bool IsStop
|
|
{
|
|
get { return _isstop; }
|
|
set
|
|
{
|
|
_isstop = value;
|
|
//Debug.Log("IsStop 변경 " + value);
|
|
}
|
|
}
|
|
public bool m_IsLockTarget { get; set; }
|
|
protected bool m_bCanDodge, m_bImmortal;
|
|
protected int m_ID;
|
|
protected ServerData m_ServerData;
|
|
protected Renderer[] arr_Renderer;
|
|
protected TurnOff_GO[] arr_Effects;
|
|
protected Dictionary<eSkillExtraType, bool> dic_skillextra = new Dictionary<eSkillExtraType, bool>();
|
|
protected Dictionary<eCC, CCData> dic_ccData = new Dictionary<eCC, CCData>();
|
|
protected Dictionary<eCC, Dictionary<Actor, double>> dic_ccAddRate = new Dictionary<eCC, Dictionary<Actor, double>>();
|
|
protected void Init_CC()
|
|
{
|
|
if (dic_ccData.Count == 0)
|
|
for (eCC i = 0; i < eCC.Max; i++)
|
|
dic_ccData.Add(i, new CCData());
|
|
foreach (var item in dic_ccData)
|
|
item.Value.Init();
|
|
dic_ccAddRate.Clear();
|
|
foreach (var key in dic_skillextra.Keys.ToList())
|
|
dic_skillextra[key] = false;
|
|
}
|
|
protected void Check_CCAddRate(eCC cc, Actor actor, double v = 0)
|
|
{
|
|
if (!dic_ccAddRate.ContainsKey(cc)) dic_ccAddRate.Add(cc, new Dictionary<Actor, double>());
|
|
if (!dic_ccAddRate[cc].ContainsKey(actor)) dic_ccAddRate[cc].Add(actor, v);
|
|
}
|
|
protected double Get_CCAddRate(eCC cc, Actor actor)
|
|
{
|
|
return dic_ccAddRate.ContainsKey(cc) && dic_ccAddRate[cc].ContainsKey(actor) ? dic_ccAddRate[cc][actor] : 0;
|
|
}
|
|
protected int m_AttackIndex = 0, m_NextAttackIndex = 0;
|
|
protected Action<bool> act_MoveEnd;
|
|
protected List<ProjectileData> list_SkillData = new List<ProjectileData>();
|
|
ProjectileData m_SkillData;
|
|
|
|
public HUDUI m_HUDUI { get; set; }
|
|
|
|
protected virtual void Awake()
|
|
{
|
|
m_NavMeshAgent = GetComponent<NavMeshAgent>();
|
|
m_BoxCollider = GetComponent<BoxCollider>();
|
|
m_animation = GetComponent<Animator>();
|
|
arr_Renderer = GetComponentsInChildren<Renderer>();
|
|
arr_Effects = GetComponentsInChildren<TurnOff_GO>(true);
|
|
|
|
for (int i = 0; i < 6; i++)
|
|
list_SkillData.Add(new ProjectileData { shooter = this });
|
|
|
|
// HUD 만들기
|
|
Make_HUDobj();
|
|
|
|
m_NavSpeed = m_NavMeshAgent.speed;
|
|
m_NavStopDistance = m_NavMeshAgent.stoppingDistance;
|
|
}
|
|
void Make_HUDobj()
|
|
{
|
|
float maxY = float.MinValue; // 가장 큰 y값을 저장할 변수
|
|
|
|
// 캐릭터 하위의 모든 Renderer를 찾음
|
|
Renderer[] renderers = GetComponentsInChildren<Renderer>();
|
|
|
|
// 각 Renderer의 Bounds.max.y를 비교
|
|
foreach (Renderer renderer in renderers)
|
|
{
|
|
float rendererMaxY = renderer.bounds.max.y;
|
|
if (rendererMaxY > maxY)
|
|
{
|
|
maxY = rendererMaxY;
|
|
}
|
|
}
|
|
|
|
var hud_hp = new GameObject("HUD_HP");
|
|
tf_HUD_HP = hud_hp.transform;
|
|
tf_HUD_HP.parent = transform;
|
|
tf_HUD_HP.position = Get_position() + Vector3.down * 0.1f;
|
|
|
|
// maxY를 기준으로 HUD 위치 조정
|
|
Vector3 headPosition = new Vector3(transform.position.x, maxY, transform.position.z);
|
|
var hud_dmg = new GameObject("HUD_Dmg");
|
|
tf_HUD_Dmg = hud_dmg.transform;
|
|
tf_HUD_Dmg.parent = transform;
|
|
tf_HUD_Dmg.position = headPosition;
|
|
}
|
|
|
|
protected virtual void Start()
|
|
{
|
|
}
|
|
|
|
protected virtual void OnEnable()
|
|
{
|
|
if (gos_Effect != null) DSUtil.InActivateGameObjects(gos_Effect);
|
|
}
|
|
|
|
protected virtual void OnDestroy()
|
|
{
|
|
if (m_HUDUI) Destroy(m_HUDUI.gameObject);
|
|
}
|
|
|
|
protected override void Update()
|
|
{
|
|
if (IsChargeAttack)
|
|
{
|
|
if (IsStop || IsDead() || IsCC()) return;
|
|
|
|
if (DSUtil.CheckNull(m_Target) || m_Target.IsDead())
|
|
Set_Target();
|
|
if (DSUtil.CheckNull(m_Target))
|
|
{
|
|
Play_Idle();
|
|
return;
|
|
}
|
|
|
|
if (Vector3.Distance(Get_position(), m_Target.Get_position()) > m_NavMeshAgent.stoppingDistance)
|
|
Play_Run();
|
|
else if (CurAnim != eAnim.Attack)
|
|
Play_Attack(0, (float)m_Stat.Get_Stat(eStat.FinalAttackSpeed));
|
|
}
|
|
|
|
Update_ETC();
|
|
}
|
|
|
|
protected virtual void FixedUpdate() { }
|
|
protected void Update_ETC()
|
|
{
|
|
foreach (var item in dic_ccData)
|
|
{
|
|
if (item.Value.CCReduceTime > 0f)
|
|
item.Value.CCReduceTime -= 0.0001f;
|
|
if (item.Value.CCReduceTime < 0f)
|
|
item.Value.CCReduceTime = 0f;
|
|
}
|
|
|
|
foreach (var item in dic_ccData)
|
|
{
|
|
if (item.Value.TimeUpdate && item.Value.CCTime > 0)
|
|
{
|
|
item.Value.CCTime -= Time.deltaTime;
|
|
if (item.Value.CCTime <= 0)
|
|
{
|
|
switch (item.Key)
|
|
{
|
|
case eCC.STR_Mul_Up: Off_StatUpBuff(eStat.STR); break;
|
|
case eCC.DEX_Mul_Up: Off_StatUpBuff(eStat.DEX); break;
|
|
case eCC.INT_Mul_Up: Off_StatUpBuff(eStat.INT); break;
|
|
case eCC.LUK_Mul_Up: Off_StatUpBuff(eStat.LUK); break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected bool isPlaying()
|
|
{
|
|
float normalizedTime = m_animation.GetCurrentAnimatorStateInfo(0).normalizedTime;
|
|
|
|
// normalizedTime을 0.0에서 1.0 사이로 맞춤
|
|
normalizedTime = Mathf.Repeat(normalizedTime, 1.0f);
|
|
|
|
// 애니메이션이 95% 이하로 진행 중인지 확인
|
|
return normalizedTime < 0.95f;
|
|
}
|
|
protected bool isPlaying(params eAnim[] anims)
|
|
{
|
|
var curState = m_animation.GetCurrentAnimatorStateInfo(0);
|
|
for (int i = 0; i < anims.Length; i++)
|
|
{
|
|
if (curState.IsName(MyValue.Get_AnimName(anims[i])))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
protected virtual void Set_Target(bool _usemagic = false) { }
|
|
public virtual void Set(bool isEnemy, Actor owner, ServerData sdata, int _id, bool _stop, bool _ui = false)
|
|
{
|
|
if (IsRole(eRole.PC)) ObjSize = GetComponent<CapsuleCollider>().bounds.size;
|
|
else ObjSize = GetComponentInChildren<SkinnedMeshRenderer>().bounds.size;
|
|
|
|
gameObject.SetActive(true);
|
|
|
|
Init_CC();
|
|
DeadStatus = false;
|
|
m_Enemy = isEnemy;
|
|
Make_HUDUI();
|
|
m_ServerData = sdata;
|
|
CurAnim = eAnim.Idle;
|
|
Play_Idle();
|
|
m_ID = _id;
|
|
IsStop = _stop;
|
|
//m_NavMeshAgent.speed = m_NavSpeed;
|
|
receiveaccumulateDmg = m_Damage = 0;
|
|
}
|
|
public virtual void Off()
|
|
{
|
|
if (!DSUtil.CheckNull(m_HUDUI)) m_HUDUI.Off();
|
|
gameObject.SetActive(false);
|
|
}
|
|
public virtual void Set_Card(int index, int chapter) { }
|
|
public virtual void ReSet() { }
|
|
public virtual void Set_Stat(bool init)
|
|
{
|
|
if (DSUtil.CheckNull(m_Stat))
|
|
m_Stat = MyValue.MakeorGet_ActorStatInfo(m_Role, Get_ServerData(), null);
|
|
else
|
|
m_Stat.Reset();
|
|
|
|
m_NavMeshAgent.speed = (float)m_Stat.Get_Stat(eStat.FinalMoveSpeed);
|
|
}
|
|
protected void Add_MoveSpeed(float _v) { m_NavMeshAgent.speed = Mathf.Min(m_NavSpeed + _v, 8.5f); }
|
|
public virtual void Set_MaxHP_Minus_HP(double _hp) { m_Stat.Set_Stat(false, eStat.HP, _hp); }
|
|
public virtual void Set_Stat_SelectSkill(SelectSkillTableData _sstd) { }
|
|
|
|
public virtual void Stop(bool _stop)
|
|
{
|
|
IsStop = _stop;
|
|
if (IsStop)
|
|
{
|
|
if (m_NavMeshAgent && m_NavMeshAgent.isOnNavMesh) m_NavMeshAgent.ResetPath();
|
|
Play_Idle();
|
|
}
|
|
}
|
|
|
|
public void Set_Warp(Vector3 _pos)
|
|
{
|
|
if (m_BoxCollider) m_BoxCollider.enabled = true;
|
|
m_NavMeshAgent.enabled = true; // 사망 시 off 됨. 다시 세팅될 때 켜줌
|
|
m_NavMeshAgent.Warp(_pos);
|
|
}
|
|
public virtual void Run_byJoystick(Vector3 _v) { }
|
|
|
|
protected void StopMove_Imm()
|
|
{
|
|
CurAnim = eAnim.Idle;
|
|
if (m_NavMeshAgent.isOnNavMesh)
|
|
{
|
|
m_NavMeshAgent.isStopped = true;
|
|
m_NavMeshAgent.ResetPath(); // 경로를 비워서 멈춤
|
|
m_NavMeshAgent.velocity = Vector3.zero; // 이 경우도 velocity를 0으로 설정
|
|
}
|
|
}
|
|
int FindCount = 0;
|
|
protected bool Set_Path(Vector3 _pos)
|
|
{
|
|
if (m_NavMeshAgent.enabled && m_NavMeshAgent.isOnNavMesh)
|
|
{
|
|
NavMeshPath path = new NavMeshPath();
|
|
m_NavMeshAgent.CalculatePath(_pos, path);
|
|
|
|
switch (path.status)
|
|
{
|
|
case NavMeshPathStatus.PathComplete:
|
|
m_NavMeshAgent.speed = Get_MoveSpeed();
|
|
m_NavMeshAgent.SetPath(path);
|
|
FindCount = 0; // 성공 시 초기화
|
|
return true;
|
|
case NavMeshPathStatus.PathPartial:
|
|
++FindCount;
|
|
Debug.Log($"Path Partial. Attempt: {FindCount}");
|
|
if (FindCount > 5)
|
|
{
|
|
FindCount = 0;
|
|
return false;
|
|
}
|
|
var newpos = DSUtil.Get_RandomPos_onNavMesh(_pos, 0f, 5f);
|
|
return Set_Path(newpos);
|
|
case NavMeshPathStatus.PathInvalid:
|
|
Debug.LogWarning("Path Invalid.");
|
|
// 적 근처로 워프넣어볼까?
|
|
//Set_Warp(DSUtil.Get_RandomPos_onNavMesh(_pos, 3f));
|
|
break;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
protected void Set_Move(Vector3 _pos)
|
|
{
|
|
if (m_NavMeshAgent.enabled && m_NavMeshAgent.isOnNavMesh)
|
|
{
|
|
m_fAttackMoveTime = CurAnim != eAnim.Dodge ? 1f : 0f;
|
|
#if UNITY_EDITOR
|
|
if (UnityEngine.SceneManagement.SceneManager.GetActiveScene().name == "StatTest")
|
|
m_fAttackMoveTime = 0f;
|
|
#endif
|
|
m_NavMeshAgent.stoppingDistance = 0; // 툴에서 사용 용도
|
|
m_NavMeshAgent.isStopped = false;
|
|
m_NavMeshAgent.angularSpeed = 0f;
|
|
NavMeshPath path = new NavMeshPath();
|
|
m_NavMeshAgent.CalculatePath(_pos, path);
|
|
|
|
switch (path.status)
|
|
{
|
|
case NavMeshPathStatus.PathComplete:
|
|
m_NavMeshAgent.SetPath(path);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
protected void Set_Destination(Vector3 _pos)
|
|
{
|
|
if (m_NavMeshAgent.enabled && m_NavMeshAgent.isOnNavMesh)
|
|
{
|
|
m_NavMeshAgent.ResetPath();
|
|
m_NavMeshAgent.SetDestination(_pos);
|
|
}
|
|
}
|
|
protected bool Complete_Destination()
|
|
{
|
|
if (m_NavMeshAgent.isOnNavMesh && !m_NavMeshAgent.pathPending)
|
|
if (m_NavMeshAgent.remainingDistance <= m_NavMeshAgent.stoppingDistance)
|
|
if (!m_NavMeshAgent.hasPath || m_NavMeshAgent.velocity.sqrMagnitude == 0f)
|
|
return true;
|
|
return false;
|
|
}
|
|
public void ReCal_NaveMesh()
|
|
{
|
|
//return; // TODO 정인호 : 내비메시의 이상한 위치에 있으면 주변을 검색해서 내비메시 위로 올리는데 이상한 위치로 갈 수 있어서 일단 다시 뺌
|
|
if (m_ID != 61 && m_NavMeshAgent && m_NavMeshAgent.enabled)
|
|
{
|
|
var newpos = DSUtil.Get_RandomPos_onNavMesh(Get_position(), 0f, 1f);
|
|
if (!Vector3.Equals(newpos, Get_position()))
|
|
Set_Warp(newpos);
|
|
}
|
|
}
|
|
|
|
public void Set_AttackCoolTime(float _rate)
|
|
{
|
|
var cooltime = m_Stat.Get_Stat(eStat.AttackCoolTime) * _rate;
|
|
if (m_attackCoolTime > cooltime)
|
|
m_attackCoolTime = cooltime;
|
|
}
|
|
|
|
protected void Make_HUDUI(Action act = null)
|
|
{
|
|
#if UNITY_EDITOR
|
|
if (UnityEngine.SceneManagement.SceneManager.GetActiveScene().name == "StatTest" ||
|
|
UnityEngine.SceneManagement.SceneManager.GetActiveScene().name == "Settings") return;
|
|
#endif
|
|
InGameInfo.Ins.Get_HUDUI(hudui =>
|
|
{
|
|
m_HUDUI = hudui;
|
|
m_HUDUI.name = name;
|
|
m_HUDUI.Init(this, tf_HUD_HP);
|
|
act?.Invoke();
|
|
});
|
|
}
|
|
public virtual DamageInfo Get_DamageInfo(ProjectileData _pd, double _attackrate = 1d, bool forceCri = false)
|
|
{
|
|
var atkData = Get_CCData(eCC.ATK_Up);
|
|
var addAtk = atkData.CCTime > 0 ? atkData.CCDmg : 0f;
|
|
|
|
var dinfo = new DamageInfo();
|
|
dinfo.Beater = this;
|
|
dinfo.Beater_pd = _pd;
|
|
dinfo.BaseDmg = _pd.BaseDmg > 0 ? _pd.BaseDmg : m_Stat.Get_Stat(eStat.BaseDmg);
|
|
dinfo.Damage = (addAtk + dinfo.BaseDmg) * _attackrate;
|
|
dinfo.PiercingDmg = dinfo.BaseDmg * (m_Stat.Get_Stat(eStat.PIERCING_Multiplier) + dinfo.Get_AddValue(eAddExtraType.Add_Piercing_Multiplier_bySpiriBless));
|
|
dinfo.TextColor = HUDBase.Get_TextColor(m_AttackType);
|
|
|
|
if (dic_ccData[eCC.PiercingBuff].CCTime > 0)
|
|
dinfo.PiercingDmg += dinfo.BaseDmg * m_Stat.Get_Stat(eStat.PiercingBuff_byAttack);
|
|
|
|
dinfo.Cal_Cri(m_AttackType, m_Stat, forceCri); // 크리티컬 판정
|
|
|
|
return dinfo;
|
|
}
|
|
public virtual void Get_Damage(DamageInfo _dinfo)
|
|
{
|
|
if (IsDead() || m_bImmortal) return;
|
|
|
|
if (_dinfo.Damage < 1) _dinfo.Damage = 1d;
|
|
|
|
if (Get_HP() >= Get_MaxHP() && Get_HP() <= _dinfo.Damage) // 한 방에 사망
|
|
Kill_OnePunch(_dinfo);
|
|
VeryStrongDamage(_dinfo.Damage);
|
|
|
|
//if (!IsRole(eRole.PC)) _dinfo.Damage = 2500; // 테스트 : 데미지 고정
|
|
if (!DSUtil.CheckNull(_dinfo.Beater))
|
|
{
|
|
var beaterStat = _dinfo.Beater.Get_StatInfo();
|
|
|
|
double reduceDef = 0d, atk_fixed = 0d;
|
|
|
|
// 방어력 계산
|
|
double def = 0d;
|
|
switch (_dinfo.Beater.m_AttackType)
|
|
{
|
|
case eAttackType.Physics:
|
|
def = m_Stat.Get_Stat(eStat.FinalDef);
|
|
def -= beaterStat.Get_Stat(eStat.DEF_Ignore);
|
|
def -= def * (beaterStat.Get_Stat(eStat.DEF_Ignore_Rate) * beaterStat.Get_MainStatValue());
|
|
var hpper = (float)(m_Stat.Get_Stat(eStat.HP) / m_Stat.Get_Stat(eStat.MaxHP));
|
|
if (hpper <= m_Stat.Get_Stat(eStat.HeroBuffHP))
|
|
def += m_Stat.Get_Stat(eStat.DEF) * m_Stat.Get_Stat(eStat.HeroBuff_DEF);
|
|
|
|
Check_CCAddRate(eCC.DEF_Down, _dinfo.Beater);
|
|
++dic_ccAddRate[eCC.DEF_Down][_dinfo.Beater];
|
|
if (dic_ccAddRate[eCC.DEF_Down][_dinfo.Beater] > beaterStat.Get_Stat(eStat.DEF_Down_Limit))
|
|
dic_ccAddRate[eCC.DEF_Down][_dinfo.Beater] = beaterStat.Get_Stat(eStat.DEF_Down_Limit);
|
|
reduceDef = beaterStat.Get_Stat(eStat.DEF_Down) * dic_ccAddRate[eCC.DEF_Down][_dinfo.Beater]; // 방어력 감소 체크
|
|
def -= reduceDef;
|
|
if (beaterStat.Get_Stat(eStat.DEF_Down_Step3) >= 1)
|
|
atk_fixed = reduceDef;
|
|
if (dic_ccData[eCC.DEF_Down].CCTime > 0f)
|
|
def -= dic_ccData[eCC.DEF_Down].CCDmg;
|
|
break;
|
|
case eAttackType.Magic:
|
|
def = m_Stat.Get_Stat(eStat.FinalMDef);
|
|
Check_CCAddRate(eCC.M_DEF_Down, _dinfo.Beater);
|
|
++dic_ccAddRate[eCC.M_DEF_Down][_dinfo.Beater];
|
|
if (dic_ccAddRate[eCC.M_DEF_Down][_dinfo.Beater] > beaterStat.Get_Stat(eStat.M_DEF_Down_Limit))
|
|
dic_ccAddRate[eCC.M_DEF_Down][_dinfo.Beater] = beaterStat.Get_Stat(eStat.M_DEF_Down_Limit);
|
|
reduceDef = beaterStat.Get_Stat(eStat.M_DEF_Down) * dic_ccAddRate[eCC.M_DEF_Down][_dinfo.Beater]; // 마법저항력 감소 체크
|
|
def -= reduceDef;
|
|
if (beaterStat.Get_Stat(eStat.M_DEF_Down_Step3) >= 1)
|
|
atk_fixed = reduceDef;
|
|
if (dic_ccData[eCC.M_DEF_Down].CCTime > 0f)
|
|
def -= dic_ccData[eCC.M_DEF_Down].CCDmg;
|
|
break;
|
|
}
|
|
|
|
if (def > 0f)
|
|
{
|
|
double defmul = table_GlobalValue.Ins.Get_Int("Defense_Coefficient");
|
|
// 기존 def_cal 계산식에서 / 를 *로 변경
|
|
var def_cal = def * defmul * (0.0001 + def * defmul * 0.0001) * 1000000;
|
|
var dmg = Math.Round(def_cal * _dinfo.Damage * 0.000001); // /1000000을 *0.000001로 변경
|
|
_dinfo.Damage -= dmg;
|
|
}
|
|
else
|
|
{
|
|
var cal1 = 2d - Math.Pow(0.94d, -def * 0.01);
|
|
var cal2 = cal1 * 1000000;
|
|
var cal3 = cal2 * _dinfo.Damage;
|
|
_dinfo.Damage = Math.Round(cal3 * 0.000001); // /1000000을 *0.000001로 변경
|
|
}
|
|
|
|
// 몬스터 데미지 추가
|
|
if (IsRole(eRole.Mob))
|
|
{
|
|
Check_CCAddRate(eCC.MobDamage, _dinfo.Beater);
|
|
++dic_ccAddRate[eCC.MobDamage][_dinfo.Beater];
|
|
if (dic_ccAddRate[eCC.MobDamage][_dinfo.Beater] > beaterStat.Get_Stat(eStat.MobDamage_Limit))
|
|
dic_ccAddRate[eCC.MobDamage][_dinfo.Beater] = beaterStat.Get_Stat(eStat.MobDamage_Limit);
|
|
|
|
_dinfo.Damage += _dinfo.Damage * beaterStat.Get_Stat(eStat.Monster_DMG_Multiplier) * dic_ccAddRate[eCC.MobDamage][_dinfo.Beater];
|
|
|
|
_dinfo.PiercingDmg += beaterStat.Get_Stat(eStat.BaseDmg) * beaterStat.Get_Stat(eStat.MOB_Piercing_Multiplier);
|
|
}
|
|
else if (IsRole(eRole.Boss))
|
|
{
|
|
Check_CCAddRate(eCC.BossDamage, _dinfo.Beater);
|
|
++dic_ccAddRate[eCC.BossDamage][_dinfo.Beater];
|
|
if (dic_ccAddRate[eCC.BossDamage][_dinfo.Beater] > beaterStat.Get_Stat(eStat.BossDamage_Limit))
|
|
dic_ccAddRate[eCC.BossDamage][_dinfo.Beater] = beaterStat.Get_Stat(eStat.BossDamage_Limit);
|
|
|
|
_dinfo.Damage += _dinfo.Damage * beaterStat.Get_Stat(eStat.BOSS_DMG_Multiplier) * dic_ccAddRate[eCC.BossDamage][_dinfo.Beater];
|
|
|
|
_dinfo.PiercingDmg += beaterStat.Get_Stat(eStat.BaseDmg) * beaterStat.Get_Stat(eStat.BOSS_Piercing_Multiplier);
|
|
}
|
|
|
|
// 고정 피해 계산
|
|
_dinfo.Damage += (int)_dinfo.Beater.m_Stat.Get_Stat(eStat.ATK_FIXED) + (int)atk_fixed - (int)m_Stat.Get_Stat(eStat.ATK_FIXED_DECREASE);
|
|
if (_dinfo.Damage < 1d) _dinfo.Damage = 1d;
|
|
}
|
|
|
|
_dinfo.PiercingDmg -= _dinfo.PiercingDmg * m_Stat.Get_Stat(eStat.PIERCING_Block);
|
|
_dinfo.Damage += _dinfo.PiercingDmg; // 관통 피해 추가
|
|
|
|
// 데미지 감소 적용
|
|
_dinfo.Damage -= _dinfo.Damage * m_Stat.Get_Stat(eStat.DMG_DECREASE);
|
|
|
|
if (dic_ccData[eCC.ManaShield].CCTime > 0d)
|
|
{ // 마나 실드
|
|
var reduceDmg = Mathf.Max(1, (int)(_dinfo.Damage * dic_ccData[eCC.ManaShield].CCDmg));
|
|
_dinfo.Damage -= reduceDmg;
|
|
var useMP = Mathf.Max(1, (int)(reduceDmg * dic_ccData[eCC.ManaShield].CCReduceTime));
|
|
Heal_MP(-useMP, true);
|
|
if (m_Stat.Get_Stat(eStat.MP) <= 0)
|
|
{
|
|
dic_ccData[eCC.ManaShield].CCTime = 0d;
|
|
dic_ccData[eCC.ManaShield].Off_Effect();
|
|
}
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
if (UnityEngine.SceneManagement.SceneManager.GetActiveScene().name == "StatTest")
|
|
{
|
|
MainToStatTest.Ins.Set_Dmglabel(HUDBase.Get_TextColor(_dinfo.Beater.m_AttackType) + (_dinfo.Critical == eStat.CRI || _dinfo.Critical == eStat.M_CRI ? "" + _dinfo.Critical + " " : " ") + _dinfo.Damage);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
Cal_Damage(_dinfo);
|
|
Check_CC_byAttack(_dinfo);
|
|
}
|
|
public virtual void Cal_Damage(DamageInfo _dinfo)
|
|
{
|
|
if (IsDead()) return;
|
|
|
|
m_Stat.Set_Stat(false, eStat.HP, _dinfo.Damage);
|
|
_dinfo.act_returnLastDmg?.Invoke(_dinfo.Damage);
|
|
_dinfo.act_CriAfterHit?.Invoke(_dinfo.Critical);
|
|
Add_ReceiveDmg(_dinfo.Damage);
|
|
_dinfo.Beater?.Add_MyRealDmg(_dinfo.Damage);
|
|
BossRaidCard_HP();
|
|
|
|
var pd = _dinfo.Beater_pd;
|
|
if (m_Stat.Get_Stat(eStat.HP) <= 0d)
|
|
{
|
|
Check_Drain_byKill(_dinfo);
|
|
DeadStatus = _dinfo.IsDeadByThisDamage = true;
|
|
Set_Die();
|
|
if (_dinfo.Beater.IsRole(eRole.BattleNPC))
|
|
ServerInfo.Ins.m_ServerData.Add_TitleCondition(72, 73); // 칭호 : 승차감 좋은 버스, 택시
|
|
|
|
if (!DSUtil.CheckNull(pd) && pd.IsSkill(eEffect.Skill_Ignited) && pd.CC_AddVal2 > 0)
|
|
{ // 버닝효과 체크, 버닝 효과 발동
|
|
var ccData = Get_CCData(eCC.Burn);
|
|
if (ccData.CCTime > 0f)
|
|
{
|
|
var other = ActorInfo.Ins.Get_NearstAllyActor(IsEnemy(), Get_position(), 5f);
|
|
if (!DSUtil.CheckNull(other))
|
|
{
|
|
other.DotDamage(eCC.Burn, pd.shooter.Get_DamageInfo(pd), ccData.CCTime,
|
|
pd.m_SkillListTableData.f_ExtraValue2 + pd.CC_AddVal1, pd.m_SkillListTableData.f_ExtraValue3);
|
|
pd.m_ProjectileBase.Show_Effect(other, (float)ccData.CCTime, ccData);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (DSUtil.CheckNull(pd) ||
|
|
!DSUtil.CheckNull(pd) && !pd.IsSkill(eEffect.Skill_Ignited) && !pd.IsSkill(eEffect.Select_BodyonFire))
|
|
Play_Hit();
|
|
}
|
|
|
|
// UI 설정
|
|
m_HUDUI.Set_HPSlider(m_Stat.Get_Stat(eStat.HP), m_Stat.Get_Stat(eStat.MaxHP));
|
|
InGameInfo.Ins.Get_HUDDMGUI().Set(tf_HUD_Dmg, _dinfo.TextColor, _dinfo.Damage, _dinfo.Critical);
|
|
//if (IsRole(eRole.Boss)) { GameUI.Ins.IngameUI.m_BossInfoUI.Set_HP(m_Stat.Get_Stat(eStat.HP), m_Stat.Get_Stat(eStat.MaxHP)); } // TODO 정인호 : 보스 hp ui
|
|
}
|
|
public void Add_MyRealDmg(double _dmg) { m_Damage += _dmg; }
|
|
public double Get_RealDamage() { return m_Damage; }
|
|
public virtual void Add_ReceiveDmg(double _dmg) { receiveaccumulateDmg += _dmg; }
|
|
public double Get_ReceiveDmg() { return receiveaccumulateDmg; }
|
|
protected virtual void Kill_OnePunch(DamageInfo dinfo) { }
|
|
protected virtual void VeryStrongDamage(double dmg) { }
|
|
public virtual void OnTrigger_Damage(Actor _actor) { }
|
|
protected virtual void Set_Die()
|
|
{
|
|
Cancel_Freeze(0f);
|
|
foreach (var key in dic_ccCoroutine.Keys.ToList()) dic_ccCoroutine[key] = null;
|
|
Play_Die();
|
|
Set_Coroutine(After_Die, 3f); // TODO 정인호 : 애니메이터에서 애니메이션 길이값 가져오는 법이 있긴 한데 일관성이 없음
|
|
if (m_BoxCollider) m_BoxCollider.enabled = false;
|
|
m_NavMeshAgent.enabled = false;
|
|
m_IsLockTarget = false;
|
|
|
|
Set_Outline(false);
|
|
}
|
|
protected virtual void After_Die()
|
|
{
|
|
#if UNITY_EDITOR
|
|
if (UnityEngine.SceneManagement.SceneManager.GetActiveScene().name == "StatTest")
|
|
{
|
|
On_Regen();
|
|
transform.localPosition = Vector3.zero;
|
|
CurAnim = eAnim.Die;
|
|
Play_Idle();
|
|
MyValue.MyPC.Change_Target(false, this);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
m_animation.speed = 0f;
|
|
switch (m_Role)
|
|
{
|
|
case eRole.Mob:
|
|
case eRole.Boss:
|
|
case eRole.OBJ:
|
|
MobInfo.Ins.Add_Dead();
|
|
Off();
|
|
break;
|
|
case eRole.PC:
|
|
//InGameInfo.Ins.Defeat_byMonster();
|
|
// 소환수 이동
|
|
var summ = ActorInfo.Ins.Get_Actor(eRole.Summon);
|
|
if (!DSUtil.CheckNull(summ)) summ.On_Regen();
|
|
// 펫 부활
|
|
var pet = ActorInfo.Ins.Get_Actor(eRole.Pet);
|
|
if (!DSUtil.CheckNull(pet)) pet.On_Regen();
|
|
// PC 부활
|
|
On_Regen();
|
|
break;
|
|
}
|
|
}
|
|
protected void Reset_AttackCoolTime() { m_attackCoolTime = m_Stat.Get_Stat(eStat.AttackCoolTime); }
|
|
|
|
protected virtual void Projectile_To_Target(string _proj)
|
|
{
|
|
Reset_AttackCoolTime();
|
|
ProjectileInfo.Ins.Shoot_Projectile(_proj, this, m_Target, 1f, Get_ProjectileStartPos() );
|
|
}
|
|
protected virtual void Projectile_To_Target2(string _proj)
|
|
{
|
|
Reset_AttackCoolTime();
|
|
ProjectileInfo.Ins.Shoot_Projectile(_proj, this, m_Target, 1f, Get_ProjectileStartPos());
|
|
}
|
|
protected virtual void Projectile_To_Target3(string _proj)
|
|
{
|
|
Reset_AttackCoolTime();
|
|
ProjectileInfo.Ins.Shoot_Projectile(_proj, this, m_Target, 1f, Get_ProjectileStartPos());
|
|
}
|
|
protected virtual void Heal_To_Target() { }
|
|
protected virtual void Set_Script() { }
|
|
protected virtual void Create_LastEffect() { }
|
|
public virtual void BossRaidCard_AddDmg(double _dmg) { }
|
|
public virtual void BossRaidCard_HP() { }
|
|
public virtual bool Use_Skill(SkillListTableData skilldata, bool autoskill, int index)
|
|
{
|
|
if (skilldata.e_SkillType == eSkillType.Passive) return false;
|
|
|
|
if (CurAnim >= eAnim.Skill)
|
|
{
|
|
if (!autoskill) MessageInfo.Ins.Add_Log(109);
|
|
return false;
|
|
}
|
|
|
|
// 내 직업이 사용할 수 있는 스킬인가?
|
|
if (skilldata.n_SkillClassID > 0 && skilldata.n_SkillClassID != m_ID)
|
|
return false;
|
|
|
|
m_Magic = dic_MagicData[index];
|
|
|
|
if (IsDead() || IsCC() || CurAnim >= eAnim.Skill)
|
|
{
|
|
Del_Magic();
|
|
return false;
|
|
}
|
|
|
|
if (skilldata.e_SkillTarget == eSkillTarget.Enemy)
|
|
{
|
|
if (DSUtil.CheckNull(m_Target)) Set_Target(true);
|
|
if (DSUtil.CheckNull(m_Target))
|
|
{
|
|
if (IsRole(eRole.PC)) ToastUI.Ins.Set(105);
|
|
Del_Magic();
|
|
return false;
|
|
}
|
|
else if (Vector3.Distance(m_Target.Get_position(), Get_position()) > skilldata.f_SkillRange)
|
|
{ // 마법 사용 사정거리보다 멀리 있다. 마법 쏘러 가자.
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (IsRole(eRole.PC))
|
|
{
|
|
var needMP = skilldata.n_MP - (skilldata.n_MP * m_Stat.Get_Stat(eStat.SKILL_COST));
|
|
if (needMP > m_Stat.Get_Stat(eStat.MP))
|
|
{
|
|
if (!autoskill)
|
|
{
|
|
ToastUI.Ins.Set(106);
|
|
MessageInfo.Ins.Add_Log(106);
|
|
}
|
|
Del_Magic();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!Check_MagicCoolTime(index)) return false;
|
|
|
|
list_SkillData[index].m_SkillListTableData = m_Magic.m_SkillTableData;
|
|
list_SkillData[index].target = m_Target;
|
|
m_SkillData = list_SkillData[index];
|
|
m_Stat.Set_Stat(false, eStat.MP, skilldata.n_MP);
|
|
Set_MagicCoolTime(skilldata.Get_CoolTime(m_Stat.Get_Stat(eStat.COOLTIME_Multiplier)), index);
|
|
return Play_Skill(skilldata.s_Animation);
|
|
}
|
|
public virtual void Shoot_Skill()
|
|
{
|
|
if (IsDead() || DSUtil.CheckNull(m_Magic)) return;
|
|
|
|
InGameInfo.Ins.Shoot_Effect(m_SkillData);
|
|
SoundInfo.Ins.Play_OneShot(eSound.s009_CastSpell);
|
|
if (IsRole(eRole.PC))
|
|
NewGameUI.Ins.m_GuideQuestUI.Add_GuideQuest(ePurpose.ShootMagic);
|
|
Del_Magic();
|
|
}
|
|
public void Del_Magic()
|
|
{
|
|
if (!DSUtil.CheckNull(co_casting))
|
|
{
|
|
StopCoroutine(co_casting);
|
|
co_casting = null;
|
|
}
|
|
m_Magic = null;
|
|
}
|
|
public virtual void Set_MagicCoolTime(float _time, int _index) { }
|
|
public virtual void Set_SkillCoolTime(bool add, int skillid, float rate) { }
|
|
protected virtual bool Check_MagicCoolTime(int _index) { return false; }
|
|
|
|
public virtual void Heal(float _rate, bool showhud = true)
|
|
{
|
|
if (IsDead()) return;
|
|
|
|
var heal_hp = m_Stat.Get_Stat(eStat.MaxHP) * _rate;
|
|
m_Stat.Set_Stat(true, eStat.HP, heal_hp);
|
|
if (showhud)
|
|
{
|
|
InGameInfo.Ins.Get_HUDDMGUI().Set(tf_HUD_Dmg, "<color=#00ff00>", heal_hp, eStat.None);
|
|
if (m_HUDUI != null)
|
|
m_HUDUI.Set_HPSlider(m_Stat.Get_Stat(eStat.HP), m_Stat.Get_Stat(eStat.MaxHP));
|
|
}
|
|
}
|
|
public virtual void Heal(double _hp, bool showhud = true)
|
|
{
|
|
if (IsDead()) return;
|
|
|
|
m_Stat.Set_Stat(true, eStat.HP, _hp);
|
|
if (m_HUDUI != null) m_HUDUI.Set_HPSlider(m_Stat.Get_Stat(eStat.HP), m_Stat.Get_Stat(eStat.MaxHP));
|
|
if (showhud)
|
|
InGameInfo.Ins.Get_HUDDMGUI().Set(tf_HUD_Dmg, "<color=#00ff00>", _hp, eStat.None);
|
|
}
|
|
public virtual void Heal_MP(double _mp, bool showhud = true)
|
|
{
|
|
if (IsDead()) return;
|
|
|
|
m_Stat.Set_Stat(true, eStat.MP, _mp);
|
|
if (showhud)
|
|
InGameInfo.Ins.Get_HUDDMGUI().Set(tf_HUD_Dmg, "<color=#0000ff>", _mp, eStat.None);
|
|
}
|
|
|
|
#region 스킬 동작
|
|
IEnumerator Co_JumpAttack()
|
|
{
|
|
var startPos = Get_position(); // 현재 위치
|
|
Vector3 direction = (m_Target.Get_position() - Get_position()).normalized; // 목표와 현재 위치의 방향 벡터를 구함
|
|
var targetPos = m_Target.Get_position() - direction * 1f; // 목표 위치에서 -1f 앞 지점 계산
|
|
|
|
var heightPos_y = 1f;
|
|
var duration = 0.8f; // 전체 점프 시간
|
|
var elapsed = 0f;
|
|
|
|
while (elapsed < duration)
|
|
{
|
|
elapsed += Time.deltaTime;
|
|
float t = elapsed / duration;
|
|
|
|
// 포물선의 중간 높이를 만들기 위해 선형 보간 사용
|
|
Vector3 currentPos = Vector3.Lerp(startPos, targetPos, t);
|
|
|
|
// 높이를 포물선 형태로 변경
|
|
currentPos.y += Mathf.Sin(t * Mathf.PI) * (heightPos_y - startPos.y);
|
|
|
|
transform.position = currentPos; // 캐릭터 위치 업데이트
|
|
yield return null;
|
|
}
|
|
|
|
transform.position = targetPos; // 최종 위치 설정
|
|
}
|
|
#endregion
|
|
|
|
public int Get_ID() { return m_ID; }
|
|
public virtual int Get_Group() { return 0; }
|
|
public Vector3 Get_position() { return transform.position; }
|
|
public Vector3 Get_Center_position()
|
|
{
|
|
return new Vector3(transform.position.x, transform.position.y + ObjSize.y * 0.5f, transform.position.z);
|
|
}
|
|
public Vector3 Get_Top_postion() { return tf_HUD_Dmg.position; }
|
|
public Vector3 Get_ProjectileStartPos() { return Get_Center_position() + transform.forward; }
|
|
public Vector3 Get_PositionFoward() { return Get_position() + transform.forward; }
|
|
public float Get_ScaleSizeDist() { return transform.localScale.x > 2f ? ObjSize.z * 0.5f : 0f; }
|
|
public ActorStatInfo Get_StatInfo() { return m_Stat; }
|
|
public double Get_HP() { return m_Stat.Get_Stat(eStat.HP); }
|
|
public double Get_MaxHP() { return m_Stat.Get_Stat(eStat.MaxHP); }
|
|
public double Get_MaxMP() { return m_Stat.Get_Stat(eStat.MaxMP); }
|
|
public Actor Get_Target() { return m_Target; }
|
|
public virtual void Del_Target(bool forcedel = false)
|
|
{
|
|
if (m_Target)
|
|
{
|
|
if (!forcedel && m_Target.m_IsLockTarget && !m_Target.IsDead()) return;
|
|
if (IsRole(eRole.PC) && !IsEnemy()) m_Target.Set_Outline(false);
|
|
}
|
|
m_Target = null;
|
|
}
|
|
public bool IsDead() { return DeadStatus || !gameObject.activeInHierarchy; }
|
|
public bool IsOnlyDead() { return DeadStatus; }
|
|
public bool IsRole(eRole _role) { return m_Role == _role; }
|
|
public bool IsEnemy() { return m_Enemy; }
|
|
public virtual bool IsBerserkPet() { return false; }
|
|
public virtual bool Get_IsMainPC() { return false; }
|
|
public bool Get_SelectSkill(eEffect _effect)
|
|
{
|
|
if (IsRole(eRole.PC) && !IsEnemy())
|
|
return InGameInfo.Ins.Get_SelectSkillData(_effect);
|
|
return false;
|
|
}
|
|
public ServerData Get_ServerData() { return m_ServerData; }
|
|
public virtual void Change_Target(bool _ispc, Actor target, bool _ChangeTargetByHit = false)
|
|
{
|
|
if (!DSUtil.CheckNull(target) && !target.IsDead())
|
|
{
|
|
if (_ispc)
|
|
{
|
|
if (!DSUtil.CheckNull(MyValue.MyPC.m_Target))
|
|
MyValue.MyPC.m_Target.Set_Outline(false);
|
|
}
|
|
|
|
if (_ChangeTargetByHit)
|
|
{
|
|
if (DSUtil.CheckNull(m_Target) || m_Target.IsDead())
|
|
{
|
|
m_Target = target;
|
|
FindOtherTargetTime = 7.5f;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_Target = target;
|
|
FindOtherTargetTime = 7.5f;
|
|
}
|
|
|
|
if (_ispc)
|
|
{
|
|
m_Target.Set_Outline(true);
|
|
// 퀘스트 몹은 선택 사항이므로 마녀가 터치로만 타겟을 잡을 수가 있다. 퀘스트몹을 선택했다는 것은 퀘스트를 하겠다는 뜻이므로 모든 펫이 같이 때린다.
|
|
// 강화석도 같이 캐자
|
|
if (m_Target.IsRole(eRole.QuestMob) || (m_Target.IsRole(eRole.OBJ) && m_Target.m_SubRole == eSubRol.Ore))
|
|
ActorInfo.Ins.Get_Actors(eRole.Pet, 0).ForEach(f => f.Change_Target(false, m_Target));
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Resurrection(float _hprate)
|
|
{
|
|
Heal(_hprate);
|
|
Play_Idle();
|
|
if (m_BoxCollider) m_BoxCollider.enabled = true;
|
|
m_NavMeshAgent.enabled = true;
|
|
}
|
|
protected virtual void On_NavMeshAgent()
|
|
{
|
|
if (!IsCC())
|
|
{ // 모든 상태이상이 해제 되었다.
|
|
m_NavMeshAgent.updatePosition = true;
|
|
if (m_NavMeshAgent.isOnNavMesh) m_NavMeshAgent.isStopped = false;
|
|
m_animation.speed = OriginalAnimSpeed;
|
|
}
|
|
}
|
|
#region 적 처치 시 회복
|
|
void Check_Drain_byKill(DamageInfo _dinfo)
|
|
{
|
|
if (!DSUtil.CheckNull(_dinfo) && !DSUtil.CheckNull(_dinfo.Beater) && _dinfo.Beater.IsEnemy() != IsEnemy())
|
|
{
|
|
var statinfo = _dinfo.Beater.Get_StatInfo();
|
|
if (!DSUtil.CheckNull(statinfo))
|
|
{
|
|
if (DSUtil.RandomTrue(statinfo.Get_Stat(eStat.KillToHP_Rate)))
|
|
{ // 회복
|
|
_dinfo.Beater.Heal(statinfo.Get_Stat(eStat.KillToHP));
|
|
_dinfo.Beater.Heal_MP(statinfo.Get_Stat(eStat.KillToMP));
|
|
}
|
|
if (DSUtil.RandomTrue(statinfo.Get_Stat(eStat.KillToMP_Rate)))
|
|
{ // MP 회복
|
|
_dinfo.Beater.Heal(statinfo.Get_Stat(eStat.KillToHP));
|
|
_dinfo.Beater.Heal_MP(statinfo.Get_Stat(eStat.KillToMP));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
public bool IsCC()
|
|
{
|
|
foreach (var item in dic_ccData)
|
|
{
|
|
if (item.Key >= eCC.Freeze && item.Value.CCTime > 0) return true;
|
|
}
|
|
return false;
|
|
}
|
|
protected virtual void Check_CC_byAttack(DamageInfo _dinfo)
|
|
{
|
|
if (dic_ccData[eCC.Freeze].CCTime > 0f)
|
|
Off_Freeze();
|
|
|
|
float knockbackTime = 0f;
|
|
if ((IsRole(eRole.Mob) || IsRole(eRole.Boss)) && !IsCC())
|
|
knockbackTime = table_GlobalValue.Ins.Get_Float("f_KnockbackTime");
|
|
|
|
var beaterActor = _dinfo.Beater;
|
|
|
|
if (!DSUtil.CheckNull(_dinfo) && !DSUtil.CheckNull(beaterActor))
|
|
{
|
|
if (!DSUtil.CheckNull(_dinfo.Beater_pd))
|
|
{ // 서리 상태인 적을 냉기 폭격으로 때렸을 때 얼림
|
|
var pd = _dinfo.Beater_pd;
|
|
if (!DSUtil.CheckNull(pd.m_SkillListTableData) && pd.m_SkillListTableData.e_Skill == eEffect.Skill_IceArrows &&
|
|
pd.CC_AddVal2 > 0f && IsSkillExtraStatus(eSkillExtraType.Frost))
|
|
Freeze(pd.CC_AddVal2, _dinfo.act_AfterFreezing);
|
|
}
|
|
|
|
var statinfo = beaterActor.Get_StatInfo();
|
|
if (!DSUtil.CheckNull(statinfo))
|
|
{
|
|
var cc = eCC.Blooding;
|
|
if (DSUtil.RandomTrue(statinfo.Get_Stat(eStat.Blooding)))
|
|
{ // 출혈 체크
|
|
DotDamage(eCC.Blooding, _dinfo.Copy(), statinfo.Get_Stat(eStat.BloodingTime),
|
|
statinfo.Get_Stat(eStat.BloodingDelay), statinfo.Get_Stat(eStat.BloodingDmg));
|
|
}
|
|
|
|
cc = eCC.Stun;
|
|
if (dic_ccData[cc].CCTime <= 0f)
|
|
{ // 무한 스턴 방지
|
|
var addrate = Get_CCAddRate(cc, beaterActor);
|
|
Check_CCAddRate(cc, beaterActor);
|
|
dic_ccAddRate[cc][beaterActor] += statinfo.Get_Stat(eStat.StunAdd);
|
|
|
|
if (DSUtil.RandomTrue(statinfo.Get_Stat(eStat.Stun) + addrate))
|
|
{ // 스턴 체크
|
|
dic_ccAddRate[cc][beaterActor] = 0;
|
|
dic_ccData[cc].CCTime = statinfo.Get_Stat(eStat.StunTime);
|
|
_dinfo.Damage = dic_ccData[cc].CCDmg = _dinfo.Damage * statinfo.Get_Stat(eStat.StunDmg);
|
|
Cal_Damage(_dinfo);
|
|
Stun(dic_ccData[cc].CCTime);
|
|
}
|
|
}
|
|
|
|
knockbackTime += (float)statinfo.Get_Stat(eStat.AttackKnockBack);
|
|
|
|
if (DSUtil.RandomTrue(statinfo.Get_Stat(eStat.AttackSpeedRate_byAttack)))
|
|
beaterActor.Buff_or_Debuff(eCC.ATKSPD_Up,
|
|
beaterActor.Get_StatInfo().Get_Stat(eStat.AttackSpeedTime_byAttack),
|
|
beaterActor.Get_StatInfo().Get_Stat(eStat.AttackSpeedBuff_byAttack));
|
|
if (DSUtil.RandomTrue(statinfo.Get_Stat(eStat.MoveSpeedRate_byAttack)))
|
|
beaterActor.Buff_or_Debuff(eCC.MOVSPD_Up,
|
|
beaterActor.Get_StatInfo().Get_Stat(eStat.MoveSpeedTime_byAttack),
|
|
beaterActor.Get_StatInfo().Get_Stat(eStat.MoveSpeedBuff_byAttack));
|
|
if (DSUtil.RandomTrue(statinfo.Get_Stat(eStat.PiercingRate_byAttack)))
|
|
beaterActor.Buff_or_Debuff(eCC.PiercingBuff,
|
|
beaterActor.Get_StatInfo().Get_Stat(eStat.PiercingTime_byAttack),
|
|
beaterActor.Get_StatInfo().Get_Stat(eStat.PiercingRate_byAttack));
|
|
|
|
var STCdata = _dinfo.Beater_pd.m_skillTypeConfigTable;
|
|
if (!DSUtil.CheckNull(STCdata) && STCdata.e_SkillExtraTiming == eSkillExtraTiming.Hit)
|
|
Check_Extra(_dinfo);
|
|
|
|
Check_EnchantOption(_dinfo);
|
|
|
|
if (knockbackTime > 0f) KnockBack(knockbackTime);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Check_Extra(DamageInfo _dinfo)
|
|
{
|
|
var pd = _dinfo.Beater_pd;
|
|
if (pd.e_SkillExtraType == eSkillExtraType.None) return;
|
|
|
|
float val1 = 0f, val2 = 0f, val3 = 0f, val4 = 0f;
|
|
bool bSKill, bOption;
|
|
|
|
SkillListTableData skillData = _dinfo.Beater_pd.m_SkillListTableData;
|
|
bSKill = !DSUtil.CheckNull(skillData);
|
|
if (bSKill)
|
|
{
|
|
val1 = skillData.f_ExtraValue1;
|
|
val2 = skillData.f_ExtraValue2;
|
|
val3 = skillData.f_ExtraValue3;
|
|
val4 = skillData.f_ExtraValue4;
|
|
}
|
|
else
|
|
{
|
|
OptionConfigTableData optionData = _dinfo.Beater_pd.m_OptionConfigTableData;
|
|
bOption = !DSUtil.CheckNull(optionData);
|
|
if (bOption)
|
|
{
|
|
val1 = optionData.f_ExtraValue1;
|
|
val2 = optionData.f_ExtraValue2;
|
|
val3 = optionData.f_ExtraValue3;
|
|
val4 = optionData.f_ExtraValue4;
|
|
}
|
|
}
|
|
if (!DSUtil.RandomTrue(val4)) return; // 확률 체크
|
|
|
|
SkillTypeConfigTableData skillExtraData = _dinfo.Beater_pd.m_skillTypeConfigTable;
|
|
ActorStatInfo statinfo = _dinfo.Beater.Get_StatInfo();
|
|
|
|
CCData ccdata = null;
|
|
float LifeTime = val1 + pd.CC_AddTime;
|
|
switch (skillExtraData.e_SkillExtraType)
|
|
{
|
|
// 적에게
|
|
case eSkillExtraType.Frost:
|
|
case eSkillExtraType.Slow:
|
|
Buff_or_Debuff(eCC.ATKSPD_Down, LifeTime, val2 + pd.CC_AddVal1);
|
|
Buff_or_Debuff(eCC.MOVSPD_Down, LifeTime, val3 + pd.CC_AddVal2);
|
|
if (_dinfo.bExtraDmg) Cal_Damage(_dinfo); // 추가 데미지
|
|
break;
|
|
case eSkillExtraType.Freezing:
|
|
Freeze(LifeTime, _dinfo.act_AfterFreezing);
|
|
if (_dinfo.bExtraDmg) Cal_Damage(_dinfo); // 추가 데미지
|
|
break;
|
|
case eSkillExtraType.Burn:
|
|
DotDamage(eCC.Burn, _dinfo.Copy(), LifeTime, val2 + pd.CC_AddVal1, val3);
|
|
break;
|
|
case eSkillExtraType.Blood:
|
|
DotDamage(eCC.Blooding, _dinfo.Copy(), LifeTime, val2, val3);
|
|
break;
|
|
case eSkillExtraType.Poison:
|
|
DotDamage(eCC.Poison, _dinfo.Copy(), LifeTime, val2, val3);
|
|
break;
|
|
case eSkillExtraType.Stun:
|
|
Stun(LifeTime);
|
|
if (_dinfo.bExtraDmg)
|
|
{
|
|
_dinfo.Damage *= val2 + pd.CC_AddDmg;
|
|
Cal_Damage(_dinfo); // 스턴 추가 데미지
|
|
}
|
|
break;
|
|
case eSkillExtraType.DefenceDecrease:
|
|
Buff_or_Debuff(eCC.DEF_Down, LifeTime, val2);
|
|
Buff_or_Debuff(eCC.M_DEF_Down, LifeTime, val3);
|
|
if (_dinfo.bExtraDmg) Cal_Damage(_dinfo); // 추가 데미지
|
|
break;
|
|
|
|
// 시전자에게
|
|
case eSkillExtraType.StatUp:
|
|
{
|
|
var rate = val3;
|
|
var type = (int)val2;
|
|
var lst = MyValue.dic_StatUp[type];
|
|
for (int i = 0; i < lst.Count; i++)
|
|
Stat_BufforDebuff(statinfo, lst[i], LifeTime, rate, type, i == lst.Count - 1);
|
|
}
|
|
break;
|
|
case eSkillExtraType.ManaShield:
|
|
LifeTime += LifeTime * pd.Get_SkillDamagePercent();
|
|
Buff_or_Debuff(eCC.ManaShield, LifeTime, val3, 0, val2);
|
|
ccdata = dic_ccData[eCC.ManaShield];
|
|
break;
|
|
case eSkillExtraType.SwordShield:
|
|
break;
|
|
case eSkillExtraType.SpeedUp:
|
|
Buff_or_Debuff(eCC.ATKSPD_Up, LifeTime, val2);
|
|
Buff_or_Debuff(eCC.MOVSPD_Up, LifeTime, val3);
|
|
break;
|
|
case eSkillExtraType.Aggro:
|
|
break;
|
|
case eSkillExtraType.VampirHeal:
|
|
_dinfo.Beater.Heal(val2);
|
|
_dinfo.Beater.Heal_MP(val3);
|
|
break;
|
|
case eSkillExtraType.Cure:
|
|
_dinfo.Beater.DotHeal(LifeTime, val2, _dinfo.Beater.Get_MaxHP() * val3, _dinfo.Beater.Get_MaxMP() * val3);
|
|
break;
|
|
case eSkillExtraType.Knockback:
|
|
KnockBack(val1);
|
|
break;
|
|
case eSkillExtraType.Airborne:
|
|
KnockedAirborne(val1);
|
|
break;
|
|
case eSkillExtraType.Curse: // 단순 상태 확인 및 이펙트 표시 용도
|
|
break;
|
|
}
|
|
|
|
if (!dic_skillextra.ContainsKey(skillExtraData.e_SkillExtraType))
|
|
dic_skillextra.Add(skillExtraData.e_SkillExtraType, true);
|
|
dic_skillextra[skillExtraData.e_SkillExtraType] = true;
|
|
Set_Coroutine(() =>
|
|
{
|
|
if (dic_skillextra.ContainsKey(skillExtraData.e_SkillExtraType))
|
|
dic_skillextra[skillExtraData.e_SkillExtraType] = false;
|
|
}, LifeTime);
|
|
|
|
pd.m_ProjectileBase.Show_Effect(this, LifeTime, ccdata);
|
|
}
|
|
public void Check_EnchantOption(DamageInfo _dinfo, bool whenhit = true)
|
|
{
|
|
if (!DSUtil.CheckNull(_dinfo.Beater))
|
|
{
|
|
var sdata = _dinfo.Beater.Get_ServerData();
|
|
if (!DSUtil.CheckNull(sdata))
|
|
for (int i = 0; i < sdata.Slot.Count; i++)
|
|
{
|
|
for (int j = 0; j < sdata.Slot[i].Enchant.Count; j++)
|
|
{
|
|
var optionid = sdata.Slot[i].Enchant[j].ID;
|
|
if (optionid > 0)
|
|
{
|
|
var statinfo = _dinfo.Beater.Get_StatInfo();
|
|
var Tdata = table_optionconfig.Ins.Get_Data(optionid);
|
|
var dcopy = _dinfo.Copy();
|
|
dcopy.Beater_pd.m_OptionConfigTableData = Tdata;
|
|
if (DSUtil.CheckNull(dcopy.Beater_pd))
|
|
dcopy.Beater_pd = new ProjectileData();
|
|
var val = Tdata.Get_Value(sdata.Slot[i].Enchant[j].Lv);
|
|
|
|
switch (Tdata.e_OptionType)
|
|
{
|
|
case eStat.STR_EXTRA_STUN:
|
|
if (whenhit)
|
|
{
|
|
dcopy.Set_Extra(eSkillExtraType.Stun);
|
|
dcopy.bExtraDmg = true;
|
|
dcopy.Damage = statinfo.Get_Stat(eStat.STR) * val;
|
|
Check_Extra(dcopy);
|
|
}
|
|
break;
|
|
case eStat.DEX_EXTRA_BLEED:
|
|
if (whenhit)
|
|
{
|
|
dcopy.Set_Extra(eSkillExtraType.Blood);
|
|
dcopy.Beater_pd.BaseDmg = statinfo.Get_Stat(eStat.DEX) * val;
|
|
Check_Extra(dcopy);
|
|
}
|
|
break;
|
|
case eStat.INT_EXTRA_SLOW:
|
|
if (whenhit)
|
|
{
|
|
dcopy.Set_Extra(eSkillExtraType.Slow);
|
|
dcopy.bExtraDmg = true;
|
|
dcopy.Damage = statinfo.Get_Stat(eStat.INT) * val;
|
|
Check_Extra(dcopy);
|
|
}
|
|
break;
|
|
case eStat.LUK_EXTRA_CURSE:
|
|
if (whenhit && DSUtil.RandomTrue(Tdata.f_ExtraValue4))
|
|
{
|
|
dcopy.Damage = statinfo.Get_Stat(eStat.LUK) * val;
|
|
Cal_Damage(dcopy);
|
|
}
|
|
break;
|
|
case eStat.STR_EXTRA_SPLASH:
|
|
if (whenhit && DSUtil.RandomTrue(Tdata.f_ExtraValue4))
|
|
{
|
|
dcopy.Damage = statinfo.Get_Stat(eStat.STR) * val;
|
|
var allies = ActorInfo.Ins.Get_AlliesActors(IsEnemy(), Get_position(), Tdata.f_ExtraValue1);
|
|
for (int k = 0; k < allies.Count; k++)
|
|
{
|
|
allies[k].Cal_Damage(dcopy);
|
|
InGameInfo.Ins.Show_Effect("Effect_STR_EXTRA_SPLASH", allies[k]);
|
|
}
|
|
}
|
|
break;
|
|
case eStat.DEX_EXTRA_DART:
|
|
if (!whenhit && DSUtil.RandomTrue(Tdata.f_ExtraValue4))
|
|
{
|
|
ProjectileData pd = new ProjectileData();
|
|
pd.CheckOptionWhenStart = false;
|
|
pd.shooter = _dinfo.Beater;
|
|
pd.eStartPos = eProjectileStartPos.Custom;
|
|
pd.v3_StartPos = _dinfo.Beater.Get_Center_position();
|
|
pd.m_OptionConfigTableData = Tdata;
|
|
pd.BaseDmg = statinfo.Get_Stat(eStat.DEX) * val;
|
|
ProjectileInfo.Ins.Shoot_Projectile("Projectile_Suriken", pd);
|
|
}
|
|
break;
|
|
case eStat.INT_EXTRA_FREEZING:
|
|
if (!whenhit && DSUtil.RandomTrue(Tdata.f_ExtraValue4))
|
|
{
|
|
ProjectileData pd = new ProjectileData();
|
|
pd.CheckOptionWhenStart = false;
|
|
pd.shooter = _dinfo.Beater;
|
|
pd.eStartPos = eProjectileStartPos.Custom;
|
|
pd.v3_StartPos = _dinfo.Beater.Get_Center_position();
|
|
pd.e_SkillExtraType = eSkillExtraType.Freezing;
|
|
pd.m_OptionConfigTableData = Tdata;
|
|
pd.BaseDmg = statinfo.Get_Stat(eStat.INT) * val;
|
|
ProjectileInfo.Ins.Shoot_Projectile("Projectile_INT_EXTRA_FREEZING", pd);
|
|
}
|
|
break;
|
|
case eStat.LUK_EXTRA_CRITICAL:
|
|
if (!whenhit)
|
|
{
|
|
_dinfo.act_CriAfterHit = cri =>
|
|
{
|
|
if (cri == eStat.CRI || cri == eStat.M_CRI)
|
|
{
|
|
dcopy.Damage = statinfo.Get_Stat(eStat.LUK) * val;
|
|
dcopy.Critical = eStat.None;
|
|
Cal_Damage(dcopy);
|
|
}
|
|
};
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Set_DicCCData(eCC cc, double time, double dmg, bool isUpdate = true)
|
|
{
|
|
dic_ccData[cc].TimeUpdate = isUpdate;
|
|
dic_ccData[cc].CCTime = time;
|
|
dic_ccData[cc].CCDmg = dmg;
|
|
}
|
|
|
|
#region 버프 or 디버프 : 공속, 이속, 관통력, 방마저, 스탯
|
|
/// <summary>
|
|
/// stat = 힘, 민, 지, 행
|
|
/// </summary>
|
|
public void Stat_BufforDebuff(ActorStatInfo statinfo, eStat stat, float LifeTime, float rate, int type = 0, bool resetStat = false)
|
|
{
|
|
if (stat == eStat.STR || stat == eStat.DEX || stat == eStat.INT || stat == eStat.LUK)
|
|
{
|
|
statinfo.Set_Buff(MyValue.dic_StatUp_from_Stat[stat], rate);
|
|
Buff_or_Debuff(MyValue.dic_StatUpCC_from_Stat[stat], LifeTime, rate, type);
|
|
if (resetStat) statinfo.Reset(true);
|
|
}
|
|
}
|
|
public void Buff_or_Debuff(eCC cc, double lifeTime, double val, int ival = 0, float fval = 0f)
|
|
{
|
|
dic_ccData[cc].TimeUpdate = true;
|
|
dic_ccData[cc].CCTime = lifeTime;
|
|
dic_ccData[cc].CCDmg = val;
|
|
dic_ccData[cc].CCVal = ival;
|
|
dic_ccData[cc].CCReduceTime = fval;
|
|
}
|
|
public CCData Get_CCData(eCC cc) { return dic_ccData[cc]; }
|
|
public bool IsSkillExtraStatus(eSkillExtraType skillExtraType)
|
|
{ return dic_skillextra.ContainsKey(skillExtraType) && dic_skillextra[skillExtraType]; }
|
|
protected void Off_StatUpBuff(eStat stat)
|
|
{
|
|
Get_StatInfo().Set_Buff(MyValue.dic_StatUp_from_Stat[stat], 0d);
|
|
Get_StatInfo().Reset(true);
|
|
}
|
|
#endregion
|
|
#region 상태이상 : 도트 데미지 (출혈, 독)
|
|
Dictionary<eCC, Coroutine> dic_ccCoroutine = new Dictionary<eCC, Coroutine>();
|
|
public void DotDamage(eCC cc, DamageInfo dinfocopy, double lifetime, double gaptime, double dmg)
|
|
{
|
|
if (!dic_ccCoroutine.ContainsKey(cc)) dic_ccCoroutine.Add(cc, null);
|
|
|
|
dic_ccData[cc].CCTime = lifetime;
|
|
dic_ccData[cc].CCDelay = gaptime;
|
|
dic_ccData[cc].CCDmg = dmg;
|
|
|
|
if (!DSUtil.CheckNull(dic_ccCoroutine[cc])) StopCoroutine(dic_ccCoroutine[cc]);
|
|
dic_ccCoroutine[cc] = StartCoroutine(Co_DotDamage(cc, dinfocopy));
|
|
}
|
|
IEnumerator Co_DotDamage(eCC cc, DamageInfo dinfocopy)
|
|
{
|
|
var ccData = dic_ccData[cc];
|
|
var gapTime = (float)ccData.CCDelay;
|
|
while (ccData.CCTime > 0)
|
|
{
|
|
var dinfo = dinfocopy.Beater.Get_DamageInfo(dinfocopy.Beater_pd, ccData.CCDmg);
|
|
dinfo.TextColor = $"<color=red>{cc} ";
|
|
Cal_Damage(dinfo);
|
|
yield return new WaitForSeconds(gapTime);
|
|
ccData.CCTime -= gapTime;
|
|
}
|
|
dic_ccCoroutine[cc] = null;
|
|
}
|
|
#endregion
|
|
#region 상태이상 : 도트 힐
|
|
Coroutine co_dotheal;
|
|
public void DotHeal(double lifetime, float gaptime, double healhp, double healmp)
|
|
{
|
|
if (healhp < 1) healhp = 1;
|
|
if (healmp < 1) healmp = 1;
|
|
|
|
if (!DSUtil.CheckNull(co_dotheal)) StopCoroutine(co_dotheal);
|
|
co_dotheal = StartCoroutine(Co_DotHeal(lifetime, gaptime, healhp, healmp));
|
|
}
|
|
IEnumerator Co_DotHeal(double lifetime, float gaptime, double healhp, double healmp)
|
|
{
|
|
while (lifetime > 0)
|
|
{
|
|
yield return new WaitForSeconds(gaptime);
|
|
|
|
Heal(healhp);
|
|
Heal_MP(healmp);
|
|
|
|
lifetime -= gaptime;
|
|
}
|
|
co_dotheal = null;
|
|
}
|
|
#endregion
|
|
#region 상태이상 : 프리즈
|
|
Action<Actor> act_AfterFreezing;
|
|
public virtual void Freeze(float _freezeTime, Action<Actor> act = null)
|
|
{
|
|
if (DSUtil.CheckNull(m_NavMeshAgent) || DSUtil.CheckNull(m_animation)) return;
|
|
if (DSUtil.CheckNull(act_AfterFreezing)) act_AfterFreezing = act;
|
|
|
|
dic_ccData[eCC.Freeze].CCTime = 10f;
|
|
if (!IsDead())
|
|
{
|
|
OriginalAnimSpeed = m_animation.speed;
|
|
m_animation.speed = 0f;
|
|
}
|
|
|
|
StopMove_Imm();
|
|
|
|
//Remove_Freeze();
|
|
//for (int j = 0; j < arr_Renderer.Length; j++)
|
|
//{
|
|
// var materials = arr_Renderer[j].sharedMaterials.ToList();
|
|
// materials.Add(ProjectileInfo.Ins.arr_materials[0]);
|
|
// arr_Renderer[j].materials = materials.ToArray();
|
|
//}
|
|
|
|
_freezeTime -= (float)dic_ccData[eCC.Freeze].CCReduceTime;
|
|
if (_freezeTime < 0.1f) _freezeTime = 0.1f;
|
|
dic_ccData[eCC.Freeze].CCReduceTime += 0.05f;
|
|
|
|
if (IsRole(eRole.Boss) || transform.localScale.x > 2f) Cancel_Freeze(_freezeTime * 0.4f);
|
|
else if (IsRole(eRole.PC)) Cancel_Freeze(_freezeTime * 0.5f);
|
|
else Cancel_Freeze(_freezeTime);
|
|
}
|
|
void Remove_Freeze()
|
|
{
|
|
for (int j = 0; j < arr_Renderer.Length; j++)
|
|
{
|
|
var materials = arr_Renderer[j].sharedMaterials.ToList();
|
|
materials.Remove(ProjectileInfo.Ins.arr_materials[0]);
|
|
arr_Renderer[j].materials = materials.ToArray();
|
|
}
|
|
}
|
|
void Off_Freeze()
|
|
{
|
|
Remove_Freeze();
|
|
dic_ccData[eCC.Freeze].CCTime = 0f;
|
|
InGameInfo.Ins.Show_Effect("Effect_Hit_IceBolt", transform.position);
|
|
act_AfterFreezing?.Invoke(this);
|
|
}
|
|
void Cancel_Freeze(float _offTime)
|
|
{
|
|
if (dic_ccData[eCC.Freeze].CCTime > 0f)
|
|
{
|
|
if (IsInvoking(nameof(Off_Freeze))) CancelInvoke(nameof(Off_Freeze));
|
|
Invoke(nameof(Off_Freeze), _offTime);
|
|
}
|
|
dic_ccData[eCC.Freeze].CCReduceTime = 0;
|
|
On_NavMeshAgent();
|
|
}
|
|
#endregion
|
|
#region 상태이상 : 넉백
|
|
float StunAfterKnockBackTime = 0f;
|
|
public virtual void KnockBack(double _time, float speed = 4f)
|
|
{
|
|
if (IsRole(eRole.OBJ) || IsRole(eRole.Boss) || DSUtil.CheckNull(m_NavMeshAgent) || IsCC()) return;
|
|
if (dic_ccData[eCC.KnockBack].CCTime < _time)
|
|
StartCoroutine(Co_KnockBack(_time, speed));
|
|
}
|
|
IEnumerator Co_KnockBack(double _time, float speed)
|
|
{
|
|
dic_ccData[eCC.KnockBack].CCTime = _time;
|
|
|
|
//if (IsRole(eRole.Boss)) _time *= 0.7f;
|
|
dic_ccData[eCC.KnockBack].CCTime -= dic_ccData[eCC.KnockBack].CCReduceTime;
|
|
if (dic_ccData[eCC.KnockBack].CCTime < 0.1f) dic_ccData[eCC.KnockBack].CCTime = 0.1f;
|
|
dic_ccData[eCC.KnockBack].CCReduceTime += 0.05f;
|
|
|
|
while (dic_ccData[eCC.KnockBack].CCTime > 0f)
|
|
{
|
|
dic_ccData[eCC.KnockBack].CCTime -= Time.deltaTime;
|
|
transform.Translate(speed * Time.deltaTime * Vector3.back);
|
|
Set_Warp(transform.position);
|
|
yield return null;
|
|
}
|
|
|
|
dic_ccData[eCC.KnockBack].CCTime = 0f;
|
|
OriginalAnimSpeed = 1f;
|
|
On_NavMeshAgent();
|
|
|
|
if (StunAfterKnockBackTime > 0)
|
|
{
|
|
Stun(StunAfterKnockBackTime, true);
|
|
StunAfterKnockBackTime = 0f;
|
|
}
|
|
}
|
|
public void Stun_AfterKnockBack(float time) { StunAfterKnockBackTime = time; }
|
|
#endregion
|
|
#region 상태이상 : 특정 위치로 끌어당기기
|
|
public virtual void Attract(Vector3 _pos)
|
|
{
|
|
if (DSUtil.CheckNull(m_NavMeshAgent)) return;
|
|
if (gameObject.activeInHierarchy && dic_ccData[eCC.Attract].CCTime <= 0f)
|
|
StartCoroutine(Co_Attract(_pos));
|
|
}
|
|
IEnumerator Co_Attract(Vector3 _pos)
|
|
{
|
|
dic_ccData[eCC.Attract].CCTime = 10f;
|
|
|
|
float t = 0.3f;
|
|
while (t > 0f)
|
|
{
|
|
m_NavMeshAgent.Warp(Vector3.MoveTowards(transform.position, _pos, Time.deltaTime * 2f));
|
|
yield return null;
|
|
t -= Time.deltaTime;
|
|
}
|
|
|
|
dic_ccData[eCC.Attract].CCTime = 0f;
|
|
On_NavMeshAgent();
|
|
}
|
|
#endregion
|
|
#region 상태이상 : 공중으로 에어본
|
|
public virtual void KnockedAirborne(double _airborneTime = 1d)
|
|
{
|
|
if (dic_ccData[eCC.KnockedAirborne].CCTime > 0) return;
|
|
StartCoroutine(Co_KnockedAirborne((float)_airborneTime));
|
|
}
|
|
IEnumerator Co_KnockedAirborne(float _airborneTime)
|
|
{
|
|
dic_ccData[eCC.KnockedAirborne].CCTime = _airborneTime;
|
|
|
|
StopMove_Imm();
|
|
m_NavMeshAgent.enabled = false;
|
|
|
|
Vector3 v3_ori = new Vector3(transform.position.x, transform.position.y, transform.position.z);
|
|
Vector3 v3_target = new Vector3(transform.position.x, transform.position.y + 1f, transform.position.z);
|
|
float t = 0f;
|
|
while (t < 1f)
|
|
{
|
|
yield return null;
|
|
t += Time.deltaTime * 2f;
|
|
transform.position = Vector3.Lerp(v3_ori, v3_target, t);
|
|
}
|
|
|
|
yield return new WaitForSeconds(_airborneTime * 0.75f);
|
|
|
|
t = 0f;
|
|
while (t < 1f)
|
|
{
|
|
yield return null;
|
|
t += Time.deltaTime * 4f;
|
|
transform.position = Vector3.Lerp(v3_target, v3_ori, t);
|
|
}
|
|
|
|
dic_ccData[eCC.KnockedAirborne].CCTime = 0f;
|
|
m_NavMeshAgent.enabled = true;
|
|
On_NavMeshAgent();
|
|
}
|
|
#endregion
|
|
#region 상태이상 : 스턴(기절)
|
|
public void Stun(double time, bool showEffect = false) { StartCoroutine(Co_Stun(time, showEffect)); }
|
|
IEnumerator Co_Stun(double _time, bool showEffect = false)
|
|
{
|
|
m_animation.speed = 1f;
|
|
//AnimationPlay("stun");
|
|
var cc = eCC.Stun;
|
|
dic_ccData[cc].CCTime = _time;
|
|
StopMove_Imm();
|
|
yield return new WaitForSeconds((float)_time);
|
|
dic_ccData[cc].CCTime = 0f;
|
|
On_NavMeshAgent();
|
|
|
|
if (showEffect) InGameInfo.Ins.Show_Effect("Effect_Stun", this, eEffectLocation.Top, (float)_time);
|
|
}
|
|
#endregion
|
|
#region 이동속도
|
|
public void Set_MoveSpeed(bool _add, double _v)
|
|
{
|
|
if (DSUtil.CheckNull(m_NavMeshAgent)) return;
|
|
if (_add) m_NavMeshAgent.speed += (float)_v;
|
|
else m_NavMeshAgent.speed -= (float)_v;
|
|
}
|
|
public float Get_MoveSpeed()
|
|
{
|
|
var speed = m_NavSpeed;
|
|
if (dic_ccData[eCC.MOVSPD_Up].CCTime > 0)
|
|
speed += speed * (float)dic_ccData[eCC.MOVSPD_Up].CCDmg;
|
|
if (dic_ccData[eCC.MOVSPD_Down].CCTime > 0)
|
|
speed -= speed * (float)dic_ccData[eCC.MOVSPD_Down].CCDmg;
|
|
return speed;
|
|
}
|
|
|
|
#endregion
|
|
#region 애니메이션 실행
|
|
public virtual void Play_Idle()
|
|
{
|
|
if (IsDead() || isPlaying(eAnim.Idle)) return;
|
|
if (CurAnim != eAnim.Idle || (CurAnim == eAnim.Idle && !isPlaying()))
|
|
{
|
|
CurAnim = eAnim.Idle;
|
|
AnimationPlay(MyValue.Get_AnimName(eAnim.Idle));
|
|
}
|
|
}
|
|
public virtual void Play_Run()
|
|
{
|
|
Play_RunAnim();
|
|
}
|
|
protected void Play_RunAnim()
|
|
{
|
|
if (!IsDead() && CurAnim != eAnim.Run)
|
|
{
|
|
if (m_NavMeshAgent.isOnNavMesh)
|
|
m_NavMeshAgent.isStopped = false;
|
|
CurAnim = eAnim.Run;
|
|
m_animation.speed = 1f;
|
|
AnimationPlay(MyValue.Get_AnimName(CurAnim));
|
|
m_NextAttackIndex = m_AttackIndex;
|
|
}
|
|
if (CurAnim == eAnim.Run && !Get_AnimStatus(eAnim.Run))
|
|
AnimationPlay(MyValue.Get_AnimName(CurAnim));
|
|
}
|
|
public virtual void Play_Hit()
|
|
{
|
|
HitEffect_Color();
|
|
if (IsDead() || IsCC() || CurAnim >= eAnim.Attack) return; // 공격, 스킬 쓸 때는 피격 애님 무시
|
|
if (CurAnim == eAnim.Run) return; // 달릴 때도 무시
|
|
|
|
if (IsRole(eRole.Mob)) m_HitTime = 0.5f;
|
|
var aniname = MyValue.Get_AnimName(eAnim.Hit);
|
|
m_animation.speed = 1f;
|
|
AnimationPlay(aniname);
|
|
}
|
|
public virtual void Play_Die()
|
|
{
|
|
var aniname = MyValue.Get_AnimName(eAnim.Die);
|
|
m_animation.speed = 1f;
|
|
AnimationPlay(aniname);
|
|
|
|
if (IsRole(eRole.PC) && !DSUtil.CheckNull(ActorInfo.Ins)) ActorInfo.Ins.All_DelTarget();
|
|
}
|
|
public virtual void Play_Attack(int _attack, float _as)
|
|
{
|
|
if (IsRole(eRole.Pet) && CurAnim == eAnim.Attack) return;
|
|
|
|
Set_RunSmoke(false);
|
|
//if (IsDead() || CurAnim == eAnim.Attack + _attack || CurAnim >= eAnim.Skill1) return;
|
|
if (IsDead() || CurAnim >= eAnim.Skill) return;
|
|
|
|
//if (m_NavMeshAgent.enabled && m_NavMeshAgent.isOnNavMesh)
|
|
// m_NavMeshAgent.isStopped = true;
|
|
StopMove_Imm();
|
|
CurAnim = eAnim.Attack + _attack;
|
|
|
|
var aniname = MyValue.Get_AnimName(eAnim.Attack + _attack);
|
|
m_animation.speed = _as <= 0f ? 1f : _as;
|
|
if (dic_ccData.ContainsKey(eCC.ATKSPD_Up) && dic_ccData[eCC.ATKSPD_Up].CCTime > 0)
|
|
m_animation.speed += m_animation.speed * (float)dic_ccData[eCC.ATKSPD_Up].CCDmg;
|
|
if (dic_ccData.ContainsKey(eCC.ATKSPD_Down) && dic_ccData[eCC.ATKSPD_Down].CCTime > 0f)
|
|
m_animation.speed -= m_animation.speed * (float)dic_ccData[eCC.ATKSPD_Down].CCDmg;
|
|
AnimationPlay(aniname);
|
|
|
|
if (m_Target != null) transform.LookAt(m_Target.transform);
|
|
if (gos_Effect != null && gos_Effect.Length > _attack)
|
|
{
|
|
gos_Effect[_attack].SetActive(false);
|
|
gos_Effect[_attack].SetActive(true);
|
|
}
|
|
}
|
|
Coroutine co_casting;
|
|
public virtual bool Play_Skill(string motion)
|
|
{
|
|
m_animation.speed = 1f;
|
|
CurAnim = eAnim.Skill;
|
|
AnimationPlay(motion);
|
|
|
|
if (!DSUtil.CheckNull(m_Target)) transform.LookAt(m_Target.transform);
|
|
|
|
if (m_NavMeshAgent.isOnNavMesh)
|
|
m_NavMeshAgent.isStopped = true;
|
|
Set_RunSmoke(false);
|
|
|
|
if (motion.Equals("casting"))
|
|
{
|
|
co_casting = StartCoroutine(Co_Casting(m_Magic.m_SkillTableData.f_CastingTime));
|
|
m_NavMeshAgent.ResetPath();
|
|
}
|
|
if (!DSUtil.CheckNull(m_Magic))
|
|
{
|
|
if (m_Magic.m_SkillTableData.e_Skill == eEffect.Skill_JumpAttack)
|
|
StartCoroutine(Co_JumpAttack());
|
|
if (!string.IsNullOrEmpty(m_Magic.m_SkillTableData.s_CastingEffect))
|
|
InGameInfo.Ins.Show_Effect(m_Magic.m_SkillTableData.s_CastingEffect, Get_position());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
IEnumerator Co_Casting(float castTime)
|
|
{
|
|
yield return new WaitForSeconds(castTime);
|
|
if (!string.IsNullOrEmpty(m_Magic.m_SkillTableData.s_NextAnimation))
|
|
AnimationPlay(m_Magic.m_SkillTableData.s_NextAnimation);
|
|
else
|
|
AnimationPlay("casting_end");
|
|
}
|
|
public virtual void Play_Dodge()
|
|
{
|
|
if (IsAvoidCoolTime() || CurAnim == eAnim.Dodge || IsDead()) return;
|
|
|
|
Set_AvoidCoolTime();
|
|
Set_AutoBraking(false);
|
|
act_MoveEnd = Set_AutoBraking;
|
|
CurAnim = eAnim.Dodge;
|
|
m_animation.speed = 2f;
|
|
m_NavMeshAgent.speed = 6f;
|
|
m_NavMeshAgent.isStopped = false;
|
|
Del_Magic();
|
|
AnimationPlay(MyValue.Get_AnimName(CurAnim));
|
|
|
|
//Set_Path(transform.position + transform.forward * 3f);
|
|
Set_Move(transform.position + transform.forward * 3f);
|
|
//Set_Warp(transform.position + transform.forward * 2f);
|
|
}
|
|
public void AnimationPlay(string aniname)
|
|
{
|
|
m_animation.Play(aniname);
|
|
//if (IsRole(eRole.Boss)) Debug.Log(aniname);
|
|
}
|
|
protected bool Get_AnimStatus(eAnim anim) { return m_animation.GetCurrentAnimatorStateInfo(0).IsName(MyValue.Get_AnimName(anim)); }
|
|
#endregion
|
|
|
|
#region 아웃라인
|
|
Outline m_Outline;
|
|
public void Set_Outline(bool active)
|
|
{
|
|
if (active)
|
|
{
|
|
if (DSUtil.CheckNull(m_Outline)) m_Outline = gameObject.AddComponent<Outline>();
|
|
m_Outline.enabled = true;
|
|
m_Outline.OutlineColor = new Color32(168, 0, 0, 102);
|
|
m_Outline.OutlineWidth = 5f;
|
|
}
|
|
else if (m_Outline != null)
|
|
m_Outline.enabled = false;
|
|
|
|
Set_MobMark(active);
|
|
}
|
|
protected virtual void Set_MobMark(bool active) { }
|
|
#endregion
|
|
#region 히트 이펙트
|
|
public virtual void HitEffect_Color()
|
|
{
|
|
if (!ProjectileInfo.Ins || IsInvoking(nameof(Off_HitEffect_Color))) return;
|
|
|
|
for (int j = 0; j < arr_Renderer.Length; j++)
|
|
{
|
|
var materials = arr_Renderer[j].sharedMaterials.ToList();
|
|
materials.Add(ProjectileInfo.Ins.arr_materials[1]);
|
|
arr_Renderer[j].materials = materials.ToArray();
|
|
}
|
|
|
|
Invoke(nameof(Off_HitEffect_Color), 0.1f);
|
|
}
|
|
void Off_HitEffect_Color()
|
|
{
|
|
for (int j = 0; j < arr_Renderer.Length; j++)
|
|
{
|
|
var materials = arr_Renderer[j].sharedMaterials.ToList();
|
|
materials.Remove(ProjectileInfo.Ins.arr_materials[1]);
|
|
arr_Renderer[j].materials = materials.ToArray();
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
public bool CanMove() { return !IsCC() && m_NavMeshAgent.isOnNavMesh && !m_NavMeshAgent.isStopped; }
|
|
protected virtual bool IsAvoidCoolTime() { return false; }
|
|
protected virtual void Set_AvoidCoolTime() { }
|
|
public virtual void On_Is10_4() { }
|
|
public virtual void Check_10_4() { }
|
|
public virtual void Set_RunSmoke(bool _active) { }
|
|
public virtual MonsterAppearData Get_MonsterStatTableData() { return null; }
|
|
public virtual void On_Regen()
|
|
{
|
|
gameObject.SetActive(true);
|
|
|
|
DeadStatus = false;
|
|
m_Stat.Clear_BuffDebuff();
|
|
m_Stat.Set_Stat(eStat.HP, 0);
|
|
Heal(1f, false);
|
|
Init_CC();
|
|
}
|
|
public virtual void Set_AutoBraking(bool _active) { }
|
|
public void Set_Auto()
|
|
{
|
|
m_Auto = !m_Auto;
|
|
ServerInfo.Ins.m_ServerData.Set_Common(eCommon.Auto, m_Auto ? 1 : 0);
|
|
if (m_Auto)
|
|
{
|
|
IsStop = false;
|
|
NewGameUI.Ins.m_GuideQuestUI.Add_GuideQuest(ePurpose.AutoMagic);
|
|
}
|
|
else
|
|
{
|
|
Del_Magic();
|
|
if (m_Target != null)
|
|
{
|
|
if (IsRole(eRole.PC))
|
|
{
|
|
m_Target.Set_Outline(false);
|
|
m_NavMeshAgent.ResetPath();
|
|
Play_Idle();
|
|
}
|
|
}
|
|
Del_Target();
|
|
}
|
|
}
|
|
public Actor Get_MyPet() { return m_MyPet; }
|
|
public void Set_MyPet(Actor actor) { m_MyPet = actor; }
|
|
|
|
#region 애니메이션 이벤트
|
|
public virtual void Event_Idle(int a)
|
|
{
|
|
CurAnim = eAnim.Idle;
|
|
#if UNITY_EDITOR
|
|
if (SettingsMain.Ins) SettingsMain.Ins.Add_Event(System.Reflection.MethodBase.GetCurrentMethod().Name, 1, a);
|
|
#endif
|
|
}
|
|
public virtual void Move(float b)
|
|
{
|
|
#if UNITY_EDITOR
|
|
if (SettingsMain.Ins) SettingsMain.Ins.Add_Event(System.Reflection.MethodBase.GetCurrentMethod().Name, 2, 0, b);
|
|
#endif
|
|
}
|
|
public virtual void Interpolation(string s)
|
|
{
|
|
#if UNITY_EDITOR
|
|
if (SettingsMain.Ins) SettingsMain.Ins.Add_Event(System.Reflection.MethodBase.GetCurrentMethod().Name, 3, 0, 0f, s);
|
|
#endif
|
|
}
|
|
public virtual void Dodge(int a)
|
|
{
|
|
#if UNITY_EDITOR
|
|
if (SettingsMain.Ins) SettingsMain.Ins.Add_Event(System.Reflection.MethodBase.GetCurrentMethod().Name, 1, a);
|
|
#endif
|
|
}
|
|
public virtual void Immortal(int a)
|
|
{
|
|
#if UNITY_EDITOR
|
|
if (SettingsMain.Ins) SettingsMain.Ins.Add_Event(System.Reflection.MethodBase.GetCurrentMethod().Name, 1, a);
|
|
#endif
|
|
}
|
|
public virtual void Hit(float b)
|
|
{
|
|
#if UNITY_EDITOR
|
|
if (SettingsMain.Ins) SettingsMain.Ins.Add_Event(System.Reflection.MethodBase.GetCurrentMethod().Name, 2, 0, b);
|
|
#endif
|
|
m_DamageRate = b;
|
|
}
|
|
public virtual void Projectile(string s)
|
|
{
|
|
#if UNITY_EDITOR
|
|
if (SettingsMain.Ins) SettingsMain.Ins.Add_Event(System.Reflection.MethodBase.GetCurrentMethod().Name, 3, 0, 0f, s);
|
|
#endif
|
|
}
|
|
public virtual void SkillEvent(int a)
|
|
{
|
|
#if UNITY_EDITOR
|
|
if (SettingsMain.Ins) SettingsMain.Ins.Add_Event(System.Reflection.MethodBase.GetCurrentMethod().Name, 1, a);
|
|
#endif
|
|
Shoot_Skill();
|
|
m_NavMeshAgent.isStopped = false;
|
|
}
|
|
public virtual void Indicator(int a)
|
|
{
|
|
#if UNITY_EDITOR
|
|
if (SettingsMain.Ins) SettingsMain.Ins.Add_Event(System.Reflection.MethodBase.GetCurrentMethod().Name, 1, a);
|
|
#endif
|
|
}
|
|
public virtual void FootR(string s)
|
|
{
|
|
#if UNITY_EDITOR
|
|
if (SettingsMain.Ins) SettingsMain.Ins.Add_Event(System.Reflection.MethodBase.GetCurrentMethod().Name, 3, 0, 0f, s);
|
|
#endif
|
|
}
|
|
public virtual void FootL(string s)
|
|
{
|
|
#if UNITY_EDITOR
|
|
if (SettingsMain.Ins) SettingsMain.Ins.Add_Event(System.Reflection.MethodBase.GetCurrentMethod().Name, 3, 0, 0f, s);
|
|
#endif
|
|
}
|
|
public virtual void ShowEffect(string s)
|
|
{
|
|
#if UNITY_EDITOR
|
|
if (SettingsMain.Ins) SettingsMain.Ins.Add_Event(System.Reflection.MethodBase.GetCurrentMethod().Name, 3, 0, 0f, s);
|
|
#endif
|
|
InGameInfo.Ins.Show_Effect(s, Get_Center_position(), transform.eulerAngles);
|
|
}
|
|
|
|
public virtual void Init_CurAnim(bool resetpos)
|
|
{
|
|
CurAnim = eAnim.Idle;
|
|
if (resetpos)
|
|
Set_Warp(Vector3.zero);
|
|
}
|
|
public eAnim Get_CurAnim() { return CurAnim; }
|
|
|
|
public virtual bool Is_Immortal() { return m_bImmortal; }
|
|
#endregion
|
|
} |