2330 lines
91 KiB
C#
2330 lines
91 KiB
C#
using CodeStage.AntiCheat.ObscuredTypes;
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEngine;
|
|
using UnityEngine.AI;
|
|
using UnityEngine.SceneManagement;
|
|
|
|
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;
|
|
protected AfterImageBase m_AfterImage;
|
|
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, tf_Top;
|
|
protected Vector3 ObjSize;
|
|
[SerializeField]
|
|
Actor _target;
|
|
protected Actor m_Target
|
|
{
|
|
get { return _target; }
|
|
set
|
|
{
|
|
_target = value;
|
|
isTarget = value;
|
|
}
|
|
}
|
|
public bool isTarget = false;
|
|
protected Actor 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, m_fAttackMoveTime, m_DamageRate = 1f;
|
|
protected bool m_Enemy, IsPlaySkill, m_Auto, DeadStatus, hasAppliedDamage;
|
|
protected bool _isstop;
|
|
protected bool IsStop
|
|
{
|
|
get { return _isstop; }
|
|
set
|
|
{
|
|
_isstop = value;
|
|
//if (IsSubRole(eSubRol.Boss))
|
|
//if (IsRole(eRole.PC)) 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, float> dic_ccValue = new Dictionary<eCC, float>();
|
|
protected Dictionary<eCC, Dictionary<Actor, double>> dic_ccAddRate = new Dictionary<eCC, Dictionary<Actor, double>>();
|
|
protected Dictionary<eCC, TurnOff_GO> dic_CCEffect = new Dictionary<eCC, TurnOff_GO>();
|
|
protected ObscuredInt _Reincarnation;
|
|
public int n_Reincarnation
|
|
{
|
|
get { return _Reincarnation; }
|
|
set
|
|
{
|
|
_Reincarnation = value;
|
|
_Reincarnation.RandomizeCryptoKey();
|
|
}
|
|
}
|
|
protected int n_SummonCount;
|
|
protected float NoDmgTime = 0f;
|
|
protected Action<ProjectileBase> action_pb;
|
|
|
|
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();
|
|
foreach (var key in dic_ccValue.Keys.ToList())
|
|
dic_ccValue[key] = 0f;
|
|
dic_ccAddRate.Clear();
|
|
foreach (var key in dic_skillextra.Keys.ToList())
|
|
dic_skillextra[key] = false;
|
|
Off_CCEffect();
|
|
}
|
|
protected void Off_CCEffect()
|
|
{
|
|
foreach (var item in dic_CCEffect)
|
|
dic_CCEffect[item.Key].Off_Imm();
|
|
}
|
|
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 Action<bool> act_MoveEnd;
|
|
protected List<ProjectileData> list_SkillData = new List<ProjectileData>();
|
|
ProjectileData m_SkillData;
|
|
|
|
public HUDUI m_HUDUI;
|
|
protected bool isHUDUI;
|
|
|
|
|
|
protected virtual void Awake()
|
|
{
|
|
m_NavMeshAgent = GetComponent<NavMeshAgent>();
|
|
m_BoxCollider = GetComponent<BoxCollider>();
|
|
m_animation = GetComponent<Animator>();
|
|
|
|
Renderer[] allRenderers = GetComponentsInChildren<Renderer>(true); // true면 비활성화 포함
|
|
List<Renderer> filteredRenderers = new List<Renderer>();
|
|
foreach (var r in allRenderers)
|
|
if (!(r is ParticleSystemRenderer))
|
|
filteredRenderers.Add(r);
|
|
arr_Renderer = filteredRenderers.ToArray();
|
|
|
|
arr_Effects = GetComponentsInChildren<TurnOff_GO>(true);
|
|
m_AfterImage = GetComponent<AfterImageBase>();
|
|
if (!DSUtil.CheckNull(m_AfterImage)) m_AfterImage.enabled = false;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
Set_TF_HUD("HUD_HP", ref tf_HUD_HP, Get_position() + Vector3.down * 0.1f);
|
|
Set_TF_HUD("HUD_Dmg", ref tf_HUD_Dmg, Get_position() + Vector3.up * 0.5f);
|
|
Set_TF_HUD("HUD_Top", ref tf_Top, new Vector3(transform.position.x, maxY, transform.position.z));
|
|
}
|
|
|
|
protected virtual void Start()
|
|
{
|
|
}
|
|
|
|
protected virtual void OnEnable()
|
|
{
|
|
if (gos_Effect != null) DSUtil.InActivateGameObjects(gos_Effect);
|
|
StartCoroutine(Co_Update_ETC());
|
|
}
|
|
|
|
protected virtual void OnDestroy()
|
|
{
|
|
Off_Common();
|
|
var enemies = ActorInfo.Ins.Get_EnemyActors(m_Enemy, Get_position(), 100f);
|
|
for (int i = 0; i < enemies.Count; i++)
|
|
enemies[i].ForceDel(this);
|
|
}
|
|
|
|
virtual protected void Update() { }
|
|
|
|
void Off_Common()
|
|
{
|
|
if (isHUDUI)
|
|
{
|
|
m_HUDUI.Off(true);
|
|
isHUDUI = false;
|
|
}
|
|
}
|
|
|
|
IEnumerator Co_Update_ETC()
|
|
{
|
|
while (true)
|
|
{
|
|
yield return null;
|
|
|
|
if (NoDmgTime > 0f) NoDmgTime -= Time.deltaTime;
|
|
|
|
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;
|
|
}
|
|
|
|
if (item.Value.TimeUpdate && item.Value.CCTime > 0)
|
|
{
|
|
item.Value.CCTime -= Time.deltaTime;
|
|
|
|
if (item.Key == eCC.SilenceCurse)
|
|
{
|
|
item.Value.CC1Sec += Time.deltaTime;
|
|
if (item.Value.CC1Sec >= 1f)
|
|
{
|
|
item.Value.CC1Sec = 0f;
|
|
Heal_MP(-(Get_MaxMP() * item.Value.CCDmg));
|
|
}
|
|
}
|
|
|
|
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;
|
|
case eCC.Barrier:
|
|
dic_ccData[item.Key].CCDmg = 0;
|
|
dic_CCEffect[item.Key].Off_Imm();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Set_TF_HUD(string goname, ref Transform tf, Vector3 pos)
|
|
{
|
|
var hud = new GameObject(goname);
|
|
tf = hud.transform;
|
|
tf.parent = transform;
|
|
tf.position = pos;
|
|
}
|
|
|
|
public 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;
|
|
}
|
|
|
|
public virtual void Set_Target(bool forcefind = false) { }
|
|
public virtual void Set_Target(Actor target) { m_Target = target; isTarget = m_Target; }
|
|
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;
|
|
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()
|
|
{
|
|
Off_Common();
|
|
gameObject.SetActive(false);
|
|
}
|
|
protected HUDUI Get_HUDUI()
|
|
{
|
|
if (!isHUDUI)
|
|
{
|
|
m_HUDUI = InGameInfo.Ins.Get_HUDUI();
|
|
isHUDUI = m_HUDUI;
|
|
}
|
|
return m_HUDUI;
|
|
}
|
|
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)
|
|
{
|
|
if (IsStop && !_stop)
|
|
{
|
|
Change_MobStatus(eMobStatus.PatrolWait);
|
|
Check_Battle();
|
|
}
|
|
|
|
IsStop = _stop;
|
|
if (IsStop)
|
|
{
|
|
if (m_NavMeshAgent && m_NavMeshAgent.isOnNavMesh) m_NavMeshAgent.ResetPath();
|
|
Play_Idle();
|
|
}
|
|
}
|
|
|
|
public void Set_AttackCoolTime(float _rate)
|
|
{
|
|
var cooltime = m_Stat.Get_Stat(eStat.AttackCoolTime) * _rate;
|
|
if (m_attackCoolTime > cooltime)
|
|
m_attackCoolTime = cooltime;
|
|
}
|
|
|
|
public virtual DamageInfo Get_DamageInfo(ProjectileData _pd, 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 = addAtk + (_pd.BaseDmg > 0 ? _pd.BaseDmg : m_Stat.Get_Stat(eStat.BaseDmg));
|
|
if (dic_ccValue.ContainsKey(eCC.ATK_Mul)) dinfo.BaseDmg += dinfo.BaseDmg * dic_ccValue[eCC.ATK_Mul];
|
|
var atkDownData = Get_CCData(eCC.ATK_Down);
|
|
if (atkDownData.CCTime > 0f) dinfo.BaseDmg -= dinfo.BaseDmg * atkDownData.CCDmg;
|
|
|
|
if (dic_ccData[eCC.MultiShot].CCTime > 0f)
|
|
dinfo.BaseDmg -= dinfo.BaseDmg * dic_ccData[eCC.MultiShot].CCDmg;
|
|
dinfo.NoCriDmg = dinfo.Damage = dinfo.BaseDmg * _pd.AddDmgPercent;
|
|
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);
|
|
if (dic_ccValue.ContainsKey(eCC.DEF_Mul)) def += def * dic_ccValue[eCC.DEF_Mul];
|
|
if (dic_ccData[eCC.DEF_Mul].CCTime > 0f) def += def * dic_ccData[eCC.DEF_Mul].CCDmg;
|
|
|
|
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);
|
|
if (dic_ccValue.ContainsKey(eCC.MDEF_Mul)) def += def * dic_ccValue[eCC.MDEF_Mul];
|
|
if (dic_ccData[eCC.MDEF_Mul].CCTime > 0f) def += def * dic_ccData[eCC.MDEF_Mul].CCDmg;
|
|
|
|
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 (IsSubRole(eSubRol.None))
|
|
{
|
|
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 (IsSubRole(eSubRol.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.Beater.m_Stat.Get_Stat(eStat.ADD_ATK_FIXED) > 0)
|
|
_dinfo.Damage += Get_MaxHP() * _dinfo.Beater.m_Stat.Get_Stat(eStat.ADD_ATK_FIXED);
|
|
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 (_dinfo.Beater_pd.IsSkill())
|
|
_dinfo.Damage -= _dinfo.Damage * m_Stat.Get_Stat(eStat.SkillDmg_DECREASE);
|
|
else
|
|
_dinfo.Damage -= _dinfo.Damage * m_Stat.Get_Stat(eStat.BaseDmg_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_CCEffect[eCC.ManaShield].Off_Imm();
|
|
}
|
|
}
|
|
|
|
Cal_Damage(_dinfo);
|
|
Check_CC_byAttack(_dinfo);
|
|
Check_Extra(eSkillExtraTiming.GetHit);
|
|
|
|
//if (IsRole(eRole.Mob))
|
|
//{ // 카메라 흔들림
|
|
// bool camShake = true;
|
|
// if (_dinfo.Beater_pd.IsSkill())
|
|
// {
|
|
// switch (_dinfo.Beater_pd.m_Skill)
|
|
// {
|
|
// case eEffect.Skill_SweepAttack: camShake = false; break;
|
|
// }
|
|
// }
|
|
// if (camShake) MyValue.m_RealCamera.ShakeCamera(1);
|
|
//}
|
|
if (_dinfo.Beater.IsMainPC()) MyValue.m_RealCamera.ShakeCamera(1);
|
|
|
|
#if UNITY_EDITOR
|
|
if (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
|
|
}
|
|
public virtual void Cal_Damage(DamageInfo _dinfo)
|
|
{
|
|
if (IsDead()) return;
|
|
if (NoDmgTime > 0f) _dinfo.Damage = 0d;
|
|
|
|
if (MyValue.MyChoiceMapData.Get_MapData().e_Dungeon == eDungeon.d3_Nightmare && IsSubRole(eSubRol.Boss))
|
|
{
|
|
var diff = MyValue.MyChoiceMapData.n_Difficult;
|
|
var addmul = diff == 1 ? 1 : diff == 2 ? 1.5d : 2.5d;
|
|
DungeonInfo.Ins.Add_TotalDmg(_dinfo.Damage * addmul);
|
|
}
|
|
else
|
|
{
|
|
if (dic_ccData[eCC.Barrier].CCDmg > 0)
|
|
{ // 배리어가 있으면 배리어부터 계산
|
|
_dinfo.TextColor = "Barrier";
|
|
dic_ccData[eCC.Barrier].CCDmg -= _dinfo.Damage;
|
|
if (dic_ccData[eCC.Barrier].CCDmg <= 0)
|
|
{ // 배리어를 넘어가는 데미지 계산
|
|
var dmg = Math.Abs(dic_ccData[eCC.Barrier].CCDmg);
|
|
if (dmg > 0) m_Stat.Set_Stat(false, eStat.HP, dmg);
|
|
dic_ccData[eCC.Barrier].CCDmg = 0;
|
|
dic_CCEffect[eCC.Barrier].Off_Imm();
|
|
}
|
|
}
|
|
else
|
|
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;
|
|
bool ispd = !DSUtil.CheckNull(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 (ispd && pd.IsSkill(eEffect.Skill_Ignited) && pd.CC_AddVal2 > 0)
|
|
{ // 버닝효과 체크, 버닝 효과 발동
|
|
var ccData = Get_CCData(eCC.Burn);
|
|
if (ccData.CCTime > 0f)
|
|
{
|
|
var other = ActorInfo.Ins.Get_Nearest_AllyActor(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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!ispd || ispd && !pd.IsSkill(eEffect.Skill_Ignited) && !pd.IsSkill(eEffect.Select_BodyonFire))
|
|
Play_Hit();
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
if (SceneManager.GetActiveScene().name == "StatTest")
|
|
return;
|
|
#endif
|
|
// UI 설정
|
|
Get_HUDUI().Set_HPSlider(this);
|
|
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;
|
|
Del_Target();
|
|
|
|
Off_CCEffect();
|
|
Set_Outline(false);
|
|
}
|
|
protected virtual void After_Die()
|
|
{
|
|
Check_Extra(eSkillExtraTiming.AfterDie); // 부활 체크 (Reincarnation 은 여기서 처리)
|
|
if (!IsDead()) return; // 부활한 경우
|
|
|
|
#if UNITY_EDITOR
|
|
if (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.OBJ:
|
|
Off();
|
|
break;
|
|
case eRole.PC:
|
|
//InGameInfo.Ins.Defeat_byMonster();
|
|
// PC 부활
|
|
On_Regen();
|
|
if (!IsEnemy())
|
|
{
|
|
// 소환수 이동
|
|
var summ = ActorInfo.Ins.Get_Actor(eRole.Summon);
|
|
if (!DSUtil.CheckNull(summ)) summ.On_Regen();
|
|
// 펫 부활
|
|
if (!DSUtil.CheckNull(m_MyPet)) m_MyPet.On_Regen();
|
|
// 일꾼 이동
|
|
var worker = ActorInfo.Ins.Get_Actor(eRole.Worker);
|
|
if (!DSUtil.CheckNull(worker)) worker.On_Regen();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
protected void Reset_AttackCoolTime() { m_attackCoolTime = m_Stat.Get_Stat(eStat.AttackCoolTime); }
|
|
|
|
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, bool testskill = false)
|
|
{
|
|
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;
|
|
|
|
// 침묵의 저주에 걸린 경우 스킬 사용 불가
|
|
var SilenceCurseData = Get_CCData(eCC.SilenceCurse);
|
|
if (SilenceCurseData.CCTime > 0f) return false;
|
|
|
|
m_Magic = dic_MagicData[index];
|
|
if (testskill) m_Magic.m_SkillTableData = skilldata;
|
|
|
|
if (IsDead() || IsCC() || CurAnim >= eAnim.Skill)
|
|
{
|
|
Del_Magic();
|
|
return false;
|
|
}
|
|
|
|
if (skilldata.e_SkillTarget == eSkillTarget.Enemy)
|
|
{
|
|
if (!isTarget) Set_Target(true);
|
|
if (!isTarget)
|
|
{
|
|
if (IsRole(eRole.PC)) ToastUI.Ins.Set(105);
|
|
Del_Magic();
|
|
return false;
|
|
}
|
|
else if (!DSUtil.WithInDistance(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 (!testskill && !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];
|
|
if (!testskill) m_Stat.Set_Stat(false, eStat.MP, skilldata.n_MP);
|
|
else m_SkillData.SetSkillExtraType = true;
|
|
Set_MagicCoolTime(skilldata.Get_CoolTime(m_Stat.Get_Stat(eStat.COOLTIME_Multiplier)), index);
|
|
if (IsMainPC() && !testskill) ServerInfo.Ins.m_ServerData.Add_MissionCount(ePurpose.USE_SKILL_ALL);
|
|
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() || Get_HP() >= Get_MaxHP()) return;
|
|
|
|
var heal_hp = Math.Max(m_Stat.Get_Stat(eStat.MaxHP) * _rate, 1d);
|
|
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);
|
|
Get_HUDUI().Set_HPSlider(this);
|
|
}
|
|
}
|
|
public virtual void Heal(double _hp, bool showhud = true)
|
|
{
|
|
if (IsDead()) return;
|
|
if (_hp > 0)
|
|
{
|
|
if (Get_HP() >= Get_MaxHP()) return;
|
|
_hp = Math.Max(1, _hp);
|
|
}
|
|
|
|
m_Stat.Set_Stat(true, eStat.HP, _hp);
|
|
#if UNITY_EDITOR
|
|
if (SceneManager.GetActiveScene().name == "StatTest")
|
|
{
|
|
MainToStatTest.Ins.Set_Dmglabel($"HP : {_hp}");
|
|
return;
|
|
}
|
|
#endif
|
|
Get_HUDUI().Set_HPSlider(this);
|
|
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;
|
|
|
|
if (_mp < 0) // MP 감소 로직
|
|
{
|
|
double curMp = m_Stat.Get_Stat(eStat.MP);
|
|
if (curMp <= 0) return; // MP가 이미 0이면 종료
|
|
|
|
// 감소할 MP가 현재 MP보다 크면, 현재 MP만큼만 감소
|
|
if (Math.Abs(_mp) > curMp)
|
|
_mp = -curMp;
|
|
|
|
m_Stat.Set_Stat(true, eStat.MP, _mp);
|
|
}
|
|
else
|
|
m_Stat.Set_Stat(true, eStat.MP, Math.Abs(_mp));
|
|
|
|
#if UNITY_EDITOR
|
|
if (SceneManager.GetActiveScene().name == "StatTest")
|
|
{
|
|
MainToStatTest.Ins.Set_Dmglabel($"MP : {_mp}");
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (showhud)
|
|
{
|
|
InGameInfo.Ins.Get_HUDDMGUI().Set(tf_HUD_Dmg, "<color=#0000ff>", _mp, eStat.None);
|
|
}
|
|
}
|
|
|
|
#region 스킬 동작
|
|
IEnumerator Co_JumpAttack(float heightPos_y, float duration, float dist)
|
|
{
|
|
OptionInfo.Ins.Set_PCMove(this, false);
|
|
var startPos = Get_position(); // 현재 위치
|
|
var targetPos = Get_TeleportPostion(m_Target, dist);
|
|
|
|
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; // 최종 위치 설정
|
|
if (!DSUtil.IsValidPos_onNaveMesh(targetPos))
|
|
transform.position = DSUtil.Get_RandomPos_onNavMesh(targetPos);
|
|
}
|
|
public void Set_Invisible(float invisibleTime, Action actVisible) { StartCoroutine(Co_Invisible(invisibleTime, actVisible)); }
|
|
IEnumerator Co_Invisible(float invisibleTime, Action actVisible)
|
|
{
|
|
m_animation.speed = 0f;
|
|
m_bImmortal = true;
|
|
Model_OnOff(false);
|
|
Weapon_OnOff(false);
|
|
OptionInfo.Ins.Set_PCMove(this, false);
|
|
|
|
yield return new WaitForSeconds(invisibleTime);
|
|
|
|
m_animation.speed = 1f;
|
|
m_bImmortal = false;
|
|
Model_OnOff(true);
|
|
Weapon_OnOff(true);
|
|
OptionInfo.Ins.Set_PCMove(this, true);
|
|
|
|
actVisible?.Invoke();
|
|
}
|
|
protected virtual void Weapon_OnOff(bool active) { }
|
|
protected void Model_OnOff(bool active) { for (int i = 0; i < arr_Renderer.Length; i++) arr_Renderer[i].enabled = active; }
|
|
#endregion
|
|
|
|
public int Get_ID() { return m_ID; }
|
|
public virtual int Get_Group() { return 0; }
|
|
public virtual NavMeshAgent Get_Agent() { return m_NavMeshAgent; }
|
|
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_Top.position; }
|
|
public Vector3 Get_CenterPositionFoward(float dist = 0.3f) { return Get_Center_position() + transform.forward * dist; }
|
|
public Vector3 Get_PositionFoward(float dist = 0.3f) { return Get_position() + transform.forward * dist; }
|
|
public Vector3 Get_PositionFoward_OnNavMesh(float maxDist = 1f)
|
|
{
|
|
Vector3 start = Get_position();
|
|
Vector3 end = start + transform.forward * maxDist;
|
|
|
|
// NavMesh Raycast: 시작부터 목표까지 장애물이 있는지 확인
|
|
if (NavMesh.Raycast(start, end, out NavMeshHit hit, NavMesh.AllAreas))
|
|
{
|
|
// 장애물에 부딪힘 → 부딪힌 위치까지는 NavMesh
|
|
return hit.position;
|
|
}
|
|
|
|
// Raycast는 통과했지만, 목표 위치가 NavMesh 바깥일 수 있음
|
|
if (NavMesh.SamplePosition(end, out hit, 2f, NavMesh.AllAreas))
|
|
{
|
|
return hit.position;
|
|
}
|
|
|
|
// 목표 지점도 없으면, 직선상의 가장 가까운 NavMesh 위 위치 찾기
|
|
for (float d = maxDist - 1f; d >= 1f; d -= 1f)
|
|
{
|
|
Vector3 checkPos = start + transform.forward * d;
|
|
if (NavMesh.SamplePosition(checkPos, out hit, 1f, NavMesh.AllAreas))
|
|
{
|
|
return hit.position;
|
|
}
|
|
}
|
|
|
|
// 정말 없으면 제자리
|
|
return start;
|
|
}
|
|
|
|
public Vector3 Get_PositionBack(float dist = 0.3f) { return Get_position() + -transform.forward * dist; }
|
|
public Vector3 Get_TeleportPostion(Actor target, float dist = 1f)
|
|
{
|
|
var startPos = Get_position(); // 현재 위치
|
|
Vector3 direction = (target.Get_position() - Get_position()).normalized; // 목표와 현재 위치의 방향 벡터를 구함
|
|
var rtnPos = DSUtil.Get_RandomPos_onNavMesh(target.Get_position() - direction * dist, 0, dist);
|
|
return rtnPos;
|
|
}
|
|
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 float Get_HPPercent() { return (float)(Get_HP() / Get_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 (isTarget)
|
|
{
|
|
if (!forcedel && m_Target.m_IsLockTarget && !m_Target.IsDead()) return;
|
|
if (IsMainPC()) m_Target.Set_Outline(false);
|
|
}
|
|
m_Target = null;
|
|
isTarget = false;
|
|
}
|
|
public virtual void ForceDel(Actor target)
|
|
{
|
|
if (isTarget)
|
|
{
|
|
if (m_Target == target)
|
|
Del_Target(true);
|
|
}
|
|
}
|
|
public bool IsDead() { return DeadStatus || !gameObject.activeInHierarchy; }
|
|
public bool IsOnlyDead() { return DeadStatus; }
|
|
public bool IsMainPC() { return IsRole(eRole.PC) && !IsEnemy(); }
|
|
public bool IsRole(eRole _role) { return m_Role == _role; }
|
|
public bool IsSubRole(eSubRol subRol) { return m_SubRole == subRol; }
|
|
public bool IsEnemy() { return m_Enemy; }
|
|
public bool Get_SelectSkill(eEffect _effect)
|
|
{
|
|
if (IsMainPC())
|
|
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 (MyValue.MyPC.isTarget)
|
|
MyValue.MyPC.m_Target.Set_Outline(false);
|
|
}
|
|
|
|
if (_ChangeTargetByHit)
|
|
{
|
|
if (!isTarget || 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.OBJ))
|
|
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 = 1f;
|
|
}
|
|
}
|
|
#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
|
|
protected bool IsCC(eCC cc) { return cc >= eCC.Freeze && cc < eCC.Max; }
|
|
public bool IsCC(bool ignoreknockback = false)
|
|
{
|
|
if (dic_ccData[eCC.Freeze].CCTime > 0f) return true;
|
|
if (dic_ccData[eCC.Attract].CCTime > 0f) return true;
|
|
if (dic_ccData[eCC.KnockedAirborne].CCTime > 0f) return true;
|
|
if (!ignoreknockback && dic_ccData[eCC.KnockBack].CCTime > 0f) return true;
|
|
if (!ignoreknockback && dic_ccData[eCC.KnockBack_byHitter].CCTime > 0f) return true;
|
|
if (dic_ccData[eCC.Stun].CCTime > 0f) 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) && !IsCC())
|
|
knockbackTime = table_GlobalValue.Ins.Get_Float("f_KnockbackTime");
|
|
|
|
var beaterActor = _dinfo.Beater;
|
|
|
|
if (!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);
|
|
}
|
|
}
|
|
}
|
|
protected virtual void Check_Extra(eSkillExtraTiming timing) { }
|
|
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();
|
|
|
|
eCC curCC = eCC.Max;
|
|
float LifeTime = val1 + pd.CC_AddTime;
|
|
switch (skillExtraData.e_SkillExtraType)
|
|
{
|
|
// 적에게
|
|
case eSkillExtraType.Frost:
|
|
curCC = eCC.ATKSPD_Down;
|
|
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.Slow:
|
|
curCC = eCC.MOVSPD_Down;
|
|
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:
|
|
curCC = eCC.Freeze;
|
|
Freeze(LifeTime, _dinfo.act_AfterFreezing);
|
|
if (_dinfo.bExtraDmg) Cal_Damage(_dinfo); // 추가 데미지
|
|
break;
|
|
case eSkillExtraType.Burn:
|
|
curCC = eCC.Burn;
|
|
DotDamage(eCC.Burn, _dinfo.Copy(), LifeTime, val2 + pd.CC_AddVal1, val3);
|
|
break;
|
|
case eSkillExtraType.Blood:
|
|
curCC = eCC.Blooding;
|
|
DotDamage(eCC.Blooding, _dinfo.Copy(), LifeTime, val2, val3);
|
|
break;
|
|
case eSkillExtraType.Poison:
|
|
curCC = eCC.Poison;
|
|
DotDamage(eCC.Poison, _dinfo.Copy(), LifeTime, val2, val3);
|
|
break;
|
|
case eSkillExtraType.Stun:
|
|
curCC = eCC.Stun;
|
|
Stun(LifeTime);
|
|
if (_dinfo.bExtraDmg)
|
|
{
|
|
_dinfo.Damage *= val2 + pd.CC_AddDmg;
|
|
Cal_Damage(_dinfo); // 스턴 추가 데미지
|
|
}
|
|
break;
|
|
case eSkillExtraType.DefenceDecrease:
|
|
curCC = eCC.DEF_Down;
|
|
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:
|
|
curCC = eCC.ManaShield;
|
|
LifeTime += LifeTime * pd.Get_SkillDamagePercent();
|
|
Buff_or_Debuff(curCC, LifeTime, val3, 0, val2);
|
|
break;
|
|
case eSkillExtraType.SwordShield:
|
|
break;
|
|
case eSkillExtraType.SpeedUp:
|
|
curCC = eCC.ATKSPD_Up;
|
|
Buff_or_Debuff(eCC.ATKSPD_Up, LifeTime, val2);
|
|
Buff_or_Debuff(eCC.MOVSPD_Up, LifeTime, val3);
|
|
break;
|
|
case eSkillExtraType.Aggro:
|
|
Buff_or_Debuff(eCC.ATK_Down, LifeTime, val2 + pd.CC_AddVal1);
|
|
Buff_or_Debuff(eCC.DEF_Down, LifeTime, val3 + pd.CC_AddVal1);
|
|
Set_Target(_dinfo.Beater);
|
|
break;
|
|
case eSkillExtraType.VampirHeal:
|
|
LifeTime = 2f; // 이펙트 시간
|
|
_dinfo.Beater.Heal(val2);
|
|
_dinfo.Beater.Heal_MP(val3);
|
|
break;
|
|
case eSkillExtraType.Cure:
|
|
curCC = eCC.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;
|
|
case eSkillExtraType.ManaBurn:
|
|
var manaburn = (int)(Get_MaxMP() * val1);
|
|
Heal_MP(-manaburn);
|
|
LifeTime = 1f;
|
|
break;
|
|
case eSkillExtraType.HPBurn:
|
|
var hpburn = (Get_MaxHP() * val1);
|
|
Heal(-hpburn);
|
|
LifeTime = 1f;
|
|
break;
|
|
case eSkillExtraType.SilenceCurse: // 스킬 사용 불가 및 최대 마나 감소
|
|
curCC = eCC.SilenceCurse;
|
|
Buff_or_Debuff(curCC, LifeTime, val2);
|
|
break;
|
|
case eSkillExtraType.Blind:
|
|
GlobalVolumeMgr.Ins.Set(1, LifeTime);
|
|
break;
|
|
case eSkillExtraType.KnockBack_byHitter:
|
|
{
|
|
var a = _dinfo.Beater.Get_position();
|
|
var b = Get_position();
|
|
// 왼쪽 a == 0,0,0 b == -1,0,0
|
|
// 오른쪽 a == 0,0.0 b == 1,0,0
|
|
// 앞 a == 0,0,0 b == 0,0,1
|
|
// 뒤 a == 0,0,0 b == 0,0,-1
|
|
Knockback_byHitter((b - a).normalized, val1, 10.0f, () => { Stun(val2, true); });
|
|
TimeMgr.Ins.Set_TimeScale(0.025f, 0.05f);
|
|
}
|
|
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);
|
|
|
|
// 기존 이펙트가 있으면 재활용
|
|
if (curCC != eCC.Max && dic_CCEffect.ContainsKey(curCC) && dic_CCEffect[curCC].IsMyEffect(this) &&
|
|
dic_CCEffect[curCC].gameObject.activeInHierarchy)
|
|
dic_CCEffect[curCC].Reset_Time(LifeTime);
|
|
else
|
|
{
|
|
if (IsCC(curCC) && m_Stat.Get_Stat(eStat.CC_Immunity) > 0) return;
|
|
pd.m_ProjectileBase.Show_Effect(this, LifeTime, ccffect =>
|
|
{
|
|
if (curCC != eCC.Max)
|
|
{
|
|
if (!dic_CCEffect.ContainsKey(curCC))
|
|
dic_CCEffect.Add(curCC, null);
|
|
dic_CCEffect[curCC] = ccffect;
|
|
}
|
|
});
|
|
}
|
|
|
|
Set_IngameBuffDebuffUI(skillExtraData, LifeTime);
|
|
}
|
|
void Set_IngameBuffDebuffUI(SkillTypeConfigTableData skillExtraData, float time)
|
|
{
|
|
if (MyInfoUI.isIns && IsRole(eRole.PC))
|
|
MyInfoUI.Ins.m_IngameBuffDebuffBase.Add_IngameBuffDebuff(skillExtraData.s_SkillNoticeIconPath, time);
|
|
else if (IngamePetCard.isIns && IsRole(eRole.Pet))
|
|
IngamePetCard.Ins.m_IngameBuffDebuffBase.Add_IngameBuffDebuff(skillExtraData.s_SkillNoticeIconPath, time);
|
|
else if (IsSubRole(eSubRol.Boss) && MonsterSimpleInfo.Ins.IsMe(this))
|
|
{
|
|
switch (skillExtraData.e_SkillExtraType)
|
|
{
|
|
case eSkillExtraType.Stun:
|
|
case eSkillExtraType.Airborne:
|
|
case eSkillExtraType.Freezing:
|
|
time *= 0.5f;
|
|
break;
|
|
}
|
|
MonsterSimpleInfo.Ins.m_IngameBuffDebuffBase.Add_IngameBuffDebuff(skillExtraData.s_SkillNoticeIconPath, time);
|
|
}
|
|
}
|
|
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, string str1 = "")
|
|
{
|
|
switch (cc)
|
|
{
|
|
case eCC.ATKSPD_Down:
|
|
case eCC.MOVSPD_Down:
|
|
if (IsSubRole(eSubRol.Elite)) lifeTime -= lifeTime * 0.2f;
|
|
else if (IsSubRole(eSubRol.Boss))
|
|
{
|
|
lifeTime -= lifeTime * 0.5f;
|
|
MonsterSimpleInfo.Ins.m_IngameBuffDebuffBase.Add_IngameBuffDebuff("8_Slow", (float)lifeTime);
|
|
}
|
|
break;
|
|
}
|
|
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;
|
|
dic_ccData[cc].str1 = str1;
|
|
}
|
|
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;
|
|
dinfocopy.Beater_pd.AddDmgPercent = (float)ccData.CCDmg;
|
|
while (ccData.CCTime > 0)
|
|
{
|
|
var dinfo = dinfocopy.Beater.Get_DamageInfo(dinfocopy.Beater_pd);
|
|
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 (m_Stat.Get_Stat(eStat.CC_Immunity) > 0) return;
|
|
if (DSUtil.CheckNull(act_AfterFreezing)) act_AfterFreezing = act;
|
|
|
|
dic_ccData[eCC.Freeze].CCTime = 10f;
|
|
if (!IsDead()) m_animation.speed = 0f;
|
|
|
|
StopMove_Imm(true);
|
|
|
|
//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 (IsSubRole(eSubRol.Boss)) Cancel_Freeze(_freezeTime * 0.5f);
|
|
else if (IsSubRole(eSubRol.Elite)) Cancel_Freeze(_freezeTime * 0.2f);
|
|
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;
|
|
dic_CCEffect[eCC.Freeze].Off_Imm();
|
|
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, Action after = null, bool ignorePreknockback = false)
|
|
{
|
|
if (m_Stat.Get_Stat(eStat.CC_Immunity) > 0) return;
|
|
if (IsRole(eRole.OBJ) || IsSubRole(eSubRol.Boss) || DSUtil.CheckNull(m_NavMeshAgent) || IsCC(ignorePreknockback)) return;
|
|
StartCoroutine(Co_KnockBack(_time, speed, after));
|
|
}
|
|
IEnumerator Co_KnockBack(double _time, float speed, Action after)
|
|
{
|
|
dic_ccData[eCC.KnockBack].CCTime = _time;
|
|
|
|
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;
|
|
|
|
StopMove_Imm(false);
|
|
|
|
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;
|
|
after?.Invoke();
|
|
|
|
if (StunAfterKnockBackTime > 0)
|
|
{
|
|
Stun(StunAfterKnockBackTime, true);
|
|
StunAfterKnockBackTime = 0f;
|
|
}
|
|
else
|
|
On_NavMeshAgent();
|
|
|
|
#if UNITY_EDITOR
|
|
if (SceneManager.GetActiveScene().name == "StatTest")
|
|
{
|
|
transform.localPosition = Vector3.zero;
|
|
}
|
|
#endif
|
|
}
|
|
public void Stun_AfterKnockBack(float time) { StunAfterKnockBackTime = time; }
|
|
#endregion
|
|
#region 상태이상 : 상대 기준으로 밀리기
|
|
public virtual void Knockback_byHitter(Vector3 dir, double _time, float speed = 4f, Action after = null, bool ignorePreknockback = false)
|
|
{
|
|
if (m_Stat.Get_Stat(eStat.CC_Immunity) > 0) return;
|
|
if (IsRole(eRole.OBJ) || IsSubRole(eSubRol.Boss) || DSUtil.CheckNull(m_NavMeshAgent)) return;
|
|
StopCoroutine(nameof(Co_Knockback_byHitter)); // 중복 방지
|
|
StartCoroutine(Co_Knockback_byHitter(dir, _time, speed, after));
|
|
}
|
|
IEnumerator Co_Knockback_byHitter(Vector3 dir, double _time, float speed, Action after)
|
|
{
|
|
dic_ccData[eCC.KnockBack_byHitter].CCTime = _time;
|
|
|
|
m_NavMeshAgent.enabled = false;
|
|
|
|
while (dic_ccData[eCC.KnockBack_byHitter].CCTime > 0f)
|
|
{
|
|
dic_ccData[eCC.KnockBack_byHitter].CCTime -= Time.deltaTime;
|
|
transform.position += (Time.deltaTime * speed * dir);
|
|
Set_Warp(transform.position);
|
|
yield return null;
|
|
}
|
|
|
|
after?.Invoke();
|
|
|
|
if (StunAfterKnockBackTime > 0)
|
|
{
|
|
Stun(StunAfterKnockBackTime, true);
|
|
StunAfterKnockBackTime = 0f;
|
|
}
|
|
else
|
|
On_NavMeshAgent();
|
|
|
|
#if UNITY_EDITOR
|
|
if (SceneManager.GetActiveScene().name == "StatTest")
|
|
transform.localPosition = Vector3.zero;
|
|
#endif
|
|
}
|
|
#endregion
|
|
#region 상태이상 : 특정 위치로 끌어당기기
|
|
public virtual void Attract(Vector3 _pos)
|
|
{
|
|
if (m_Stat.Get_Stat(eStat.CC_Immunity) > 0) 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 (m_Stat.Get_Stat(eStat.CC_Immunity) > 0 || dic_ccData[eCC.KnockedAirborne].CCTime > 0) return;
|
|
StartCoroutine(Co_KnockedAirborne((float)_airborneTime));
|
|
}
|
|
IEnumerator Co_KnockedAirborne(float _airborneTime)
|
|
{
|
|
if (IsSubRole(eSubRol.Elite)) _airborneTime -= _airborneTime * 0.2f;
|
|
else if (IsSubRole(eSubRol.Boss)) _airborneTime -= _airborneTime * 0.5f;
|
|
dic_ccData[eCC.KnockedAirborne].CCTime = _airborneTime;
|
|
|
|
StopMove_Imm(true);
|
|
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 상태이상 : 스턴(기절)
|
|
Coroutine m_CoStun;
|
|
public void Stun(double time, bool showEffect = false)
|
|
{
|
|
if (IsDead() || m_Stat.Get_Stat(eStat.CC_Immunity) > 0) return;
|
|
if (!DSUtil.CheckNull(m_CoStun)) { StopCoroutine(m_CoStun); m_CoStun = null; }
|
|
m_CoStun = StartCoroutine(Co_Stun(time, showEffect));
|
|
}
|
|
IEnumerator Co_Stun(double _time, bool showEffect = false)
|
|
{
|
|
float stunTime = (float)_time;
|
|
if (IsSubRole(eSubRol.Elite)) stunTime -= stunTime * 0.2f;
|
|
else if (IsSubRole(eSubRol.Boss)) stunTime -= stunTime * 0.5f;
|
|
|
|
if (showEffect) InGameInfo.Ins.Show_Effect("Effect_Stun", this, eEffectLocation.Top, stunTime);
|
|
|
|
m_animation.speed = 1f;
|
|
AnimationPlay("stun");
|
|
var cc = eCC.Stun;
|
|
dic_ccData[cc].CCTime = _time;
|
|
StopMove_Imm(true);
|
|
yield return new WaitForSeconds(stunTime);
|
|
dic_ccData[cc].CCTime = 0f;
|
|
On_NavMeshAgent();
|
|
}
|
|
#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_ccValue.ContainsKey(eCC.MOVSPD_Up))
|
|
speed += speed * dic_ccValue[eCC.MOVSPD_Up];
|
|
if (dic_ccData[eCC.MOVSPD_Down].CCTime > 0)
|
|
speed -= speed * (float)dic_ccData[eCC.MOVSPD_Down].CCDmg;
|
|
return speed;
|
|
}
|
|
|
|
#endregion
|
|
#region 점프
|
|
public virtual void Jump(float jumpTime, float height = 1f, float jumpspeed1 = 5f, float jumpspeed2 = 2f, Action after = null)
|
|
{
|
|
StartCoroutine(Co_Jump(jumpTime, height, jumpspeed1, jumpspeed2, after));
|
|
}
|
|
IEnumerator Co_Jump(float jumpTime, float height = 1f, float jumpspeed1 = 5f, float jumpspeed2 = 2f, Action after = null)
|
|
{
|
|
StopMove_Imm(false);
|
|
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 + height, transform.position.z);
|
|
float t = 0f, speed = jumpspeed1;
|
|
while (t < 1f)
|
|
{
|
|
yield return null;
|
|
t += Time.deltaTime * speed;
|
|
transform.position = Vector3.Lerp(v3_ori, v3_target, t);
|
|
}
|
|
|
|
yield return new WaitForSeconds(jumpTime);
|
|
|
|
t = 0f;
|
|
while (t < 1f)
|
|
{
|
|
yield return null;
|
|
t += Time.deltaTime * speed * jumpspeed2;
|
|
transform.position = Vector3.Lerp(v3_target, v3_ori, t);
|
|
}
|
|
|
|
m_NavMeshAgent.enabled = true;
|
|
On_NavMeshAgent();
|
|
after?.Invoke();
|
|
}
|
|
#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));
|
|
}
|
|
if (CurAnim == eAnim.Run && !Get_AnimStatus(eAnim.Run))
|
|
AnimationPlay(MyValue.Get_AnimName(CurAnim));
|
|
}
|
|
public virtual void Play_Hit()
|
|
{
|
|
HitEffect_Color();
|
|
}
|
|
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, Action actsuccess = null)
|
|
{
|
|
if (IsRole(eRole.Pet) && CurAnim == eAnim.Attack) return;
|
|
if (IsRole(eRole.PC) && CurAnim == eAnim.Attack) return;
|
|
if (IsDead() || CurAnim >= eAnim.Skill) return;
|
|
|
|
StopMove_Imm(false);
|
|
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 > 0f)
|
|
m_animation.speed += m_animation.speed * (float)dic_ccData[eCC.ATKSPD_Up].CCDmg;
|
|
if (dic_ccValue.ContainsKey(eCC.ATKSPD_Up))
|
|
m_animation.speed += m_animation.speed * dic_ccValue[eCC.ATKSPD_Up];
|
|
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);
|
|
actsuccess?.Invoke();
|
|
|
|
if (isTarget) 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(IsRole(eRole.Pet) ? "casting" : motion);
|
|
|
|
if (isTarget) transform.LookAt(m_Target.transform);
|
|
|
|
StopMove_Imm(false);
|
|
|
|
if (!DSUtil.CheckNull(m_Magic))
|
|
{
|
|
if (IsRole(eRole.Pet) || !string.IsNullOrEmpty(m_Magic.m_SkillTableData.s_NextAnimation))
|
|
{
|
|
co_casting = StartCoroutine(Co_Casting(m_Magic.m_SkillTableData.f_CastingTime));
|
|
if (m_NavMeshAgent.isOnNavMesh) m_NavMeshAgent.ResetPath();
|
|
}
|
|
|
|
switch (m_Magic.m_SkillTableData.e_Skill)
|
|
{
|
|
case eEffect.Skill_JumpAttack: StartCoroutine(Co_JumpAttack(1f, 0.8f, 1f)); break;
|
|
case eEffect.Skill_FatalBlow: StartCoroutine(Co_JumpAttack(0.8f, 0.6f, 0.5f)); break;
|
|
}
|
|
|
|
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 (!DSUtil.CheckNull(m_Magic))
|
|
AnimationPlay(IsRole(eRole.Pet) ? "casting_end" : 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 = 1f;
|
|
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);
|
|
|
|
InGameInfo.Ins.Show_Effect("Effect_PCDash", this, eEffectLocation.Bottom, 2f, effect =>
|
|
{
|
|
effect.transform.eulerAngles = transform.eulerAngles;
|
|
});
|
|
}
|
|
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()
|
|
{ // 2025. 02. 09. 정인호 : GC Alloc을 2.3KB 발생으로 인해 사용 금지
|
|
//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()
|
|
{ // 2025. 02. 09. 정인호 : GC Alloc을 2.3KB 발생으로 인해 사용 금지
|
|
//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
|
|
#region 캐릭터 크기 조절
|
|
public void Set_ScaleControl(bool returntoOriginalScale, float targetscale = 1f, Action actComplete = null)
|
|
{
|
|
StartCoroutine(Co_ScaleControl(returntoOriginalScale, targetscale, actComplete));
|
|
}
|
|
protected virtual IEnumerator Co_ScaleControl(bool returntoOriginalScale, float targetscale, Action actComplete = null)
|
|
{
|
|
yield break;
|
|
}
|
|
#endregion
|
|
#region 캐릭터 이동
|
|
/// <summary>
|
|
/// TelepoetPosion으로 이동
|
|
/// </summary>
|
|
public virtual void MoveToTarget(Actor target, float stopdist, float speed, Action _movecomplete = null, float dist = 1f)
|
|
{
|
|
StartCoroutine(Co_MoveToTarget(target, stopdist, speed, _movecomplete, dist));
|
|
}
|
|
IEnumerator Co_MoveToTarget(Actor target, float stopdist, float speed, Action _movecomplete, float dist)
|
|
{
|
|
m_NavMeshAgent.stoppingDistance = stopdist;
|
|
Set_Path(Get_TeleportPostion(target, dist));
|
|
while (!Complete_Destination())
|
|
{
|
|
m_NavMeshAgent.speed = speed;
|
|
yield return null;
|
|
}
|
|
_movecomplete?.Invoke();
|
|
m_NavMeshAgent.stoppingDistance = m_NavStopDistance;
|
|
m_NavMeshAgent.speed = Get_MoveSpeed();
|
|
}
|
|
public virtual void Set_JoystickStatus(int status) { }
|
|
public void Set_Warp(Vector3 _pos)
|
|
{
|
|
if (IsDead()) return;
|
|
|
|
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(bool setIdle)
|
|
{
|
|
if (setIdle) 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 = 1f;
|
|
#if UNITY_EDITOR
|
|
if (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);
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
public bool CanMove() { return m_NavMeshAgent.isOnNavMesh && !m_NavMeshAgent.isStopped && !IsCC(); }
|
|
protected virtual bool IsAvoidCoolTime() { return false; }
|
|
protected virtual void Set_AvoidCoolTime() { }
|
|
public virtual MonsterAppearData Get_MonsterStatTableData() { return null; }
|
|
public virtual void Change_MobStatus(eMobStatus mobStatus) { }
|
|
protected virtual void Check_Battle() { }
|
|
public virtual bool isNoseeMob() { return false; }
|
|
public virtual void On_Regen(bool reincarnation = false, float healrate = 1f)
|
|
{
|
|
gameObject.SetActive(true);
|
|
|
|
DeadStatus = false;
|
|
m_Stat.Clear_BuffDebuff();
|
|
m_Stat.Set_Stat(eStat.HP, 0);
|
|
Heal(healrate, false);
|
|
Init_CC();
|
|
m_animation.speed = 1f;
|
|
}
|
|
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 (isTarget)
|
|
{
|
|
if (IsRole(eRole.PC))
|
|
{
|
|
m_Target.Set_Outline(false);
|
|
m_NavMeshAgent.ResetPath();
|
|
Play_Idle();
|
|
}
|
|
}
|
|
Del_Target();
|
|
}
|
|
}
|
|
public Actor Get_MyPet() { return m_MyPet; }
|
|
public virtual Actor Get_Owner() { return null; }
|
|
public void Set_MyPet(Actor actor) { m_MyPet = actor; }
|
|
public void Set_AfterImage(bool active)
|
|
{
|
|
if (!DSUtil.CheckNull(m_AfterImage))
|
|
m_AfterImage.enabled = active;
|
|
}
|
|
|
|
#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
|
|
}
|
|
protected void Shoot_Projectile(string proj, float lifetime, Vector3 startpos)
|
|
{
|
|
if (ProjectileInfo.isIns)
|
|
{
|
|
var ccData = Get_CCData(eCC.MultiShot);
|
|
var amount = 1 + (ccData.CCTime > 0f ? ccData.CCVal : 0);
|
|
if (amount > 1)
|
|
{
|
|
float cal_angle = amount * 5f;
|
|
float angleStep = cal_angle / (amount - 1); // 전체 각도를 화살 개수로 나누기
|
|
for (int i = 0; i < amount; ++i)
|
|
{
|
|
ProjectileInfo.Ins.Shoot_Projectile(ccData.str1, this, m_Target, 1f, startpos, lifetime,
|
|
pb =>
|
|
{
|
|
float angle = -(cal_angle * 0.5f) + (angleStep * i);
|
|
pb.transform.localEulerAngles = new Vector2(0f, pb.transform.localEulerAngles.y + angle);
|
|
});
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ProjectileInfo.Ins.Shoot_Projectile(proj, this, m_Target, 1f, startpos, lifetime, action_pb);
|
|
}
|
|
}
|
|
}
|
|
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();
|
|
}
|
|
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(AnimationEvent s)
|
|
{
|
|
#if UNITY_EDITOR
|
|
if (SettingsMain.Ins) SettingsMain.Ins.Add_Event(System.Reflection.MethodBase.GetCurrentMethod().Name, 3,
|
|
s.intParameter, s.floatParameter, s.stringParameter);
|
|
#endif
|
|
switch (s.intParameter)
|
|
{
|
|
case 0: InGameInfo.Ins.Show_Effect(s.stringParameter, Get_Center_position(), transform.eulerAngles); break;
|
|
case 1: InGameInfo.Ins.Show_Effect(s.stringParameter, Get_position(), transform.eulerAngles); break;
|
|
case 2: InGameInfo.Ins.Show_Effect(s.stringParameter, Get_Center_position()); break;
|
|
case 3: InGameInfo.Ins.Show_Effect(s.stringParameter, Get_position()); break;
|
|
}
|
|
}
|
|
|
|
public virtual void Init_CurAnim(bool resetpos)
|
|
{
|
|
CurAnim = eAnim.Idle;
|
|
if (resetpos)
|
|
Set_Warp(Vector3.zero);
|
|
}
|
|
public eAnim Get_CurAnim() { return CurAnim; }
|
|
public void Set_CurAnim(eAnim anim) { CurAnim = anim; }
|
|
|
|
public virtual bool Is_Immortal() { return m_bImmortal; }
|
|
#endregion
|
|
} |