using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; public enum eMobStatus { None, PatrolWait, PatrolRot, Patroling, Battle, BattleStop, Attack, Chase, Skill } public class MobActor : Actor { public bool AttackPosIsTargetCenterPos; public Transform tf_MeeleAttackPos; public int MagicID, HealCount; public float HealRate = 0.1f; protected int m_SpawnerId; protected eMobStatus MobStatus; Vector3 OriginalPos, TargetPatrolPos; float m_PatrolDelay, m_PatrolTime, f_BaseChaseRange, HealPerSec, HPRegenPerSec; bool b_IsFirstAttack, NoUpdate_byTool; protected MonsterAppearData m_MonsterAppearData; protected MonsterTableData m_MonsterTableData; protected Dictionary dic_skill = new Dictionary(); Action m_actDie; protected NoSeeLongDMob m_NoSeeLongDMob; bool useMeeleAttackPos; bool isNSLDMob; protected List list_summon = new List(); protected Dictionary dic_mainMat = new Dictionary(); protected Dictionary dic_dissolveMat = new Dictionary(); protected override void Start() { base.Start(); GetComponent().isKinematic = true; m_NoSeeLongDMob = GetComponent(); isNSLDMob = !DSUtil.CheckNull(m_NoSeeLongDMob); useMeeleAttackPos = !DSUtil.CheckNull(tf_MeeleAttackPos); for (int i = 0; i < arr_Renderer.Length; i++) { dic_mainMat.Add(arr_Renderer[i].name, arr_Renderer[i].material); dic_dissolveMat.Add(arr_Renderer[i].name, new Material(InGameInfo.Ins.mat_Dissolve)); dic_dissolveMat[arr_Renderer[i].name].mainTexture = arr_Renderer[i].material.mainTexture; } } public override void Set(bool isEnemy, Actor owner, ServerData sdata, int _id, bool _stop, bool _ui = false) { if (dic_mainMat.Count > 0) { for (int i = 0; i < arr_Renderer.Length; i++) arr_Renderer[i].material = dic_mainMat[arr_Renderer[i].name]; } base.Set(isEnemy, owner, sdata, _id, _stop); m_PatrolTime = 0f; if (MagicID > 0) m_Magic = new UseMagicInfo { m_SkillTableData = table_skilllist.Ins.Get_Data_orNull(MagicID), m_uiIndex = -1 }; m_SpawnerId = _id; m_MonsterAppearData = table_monsterappear.Ins.Get_Data_orNull(m_SpawnerId); //var chapter = InGameInfo.Ins.Get_CurStageData().Chapter; if (transform.localScale.x > 1f) { m_NavMeshAgent.stoppingDistance = m_NavStopDistance * transform.localScale.x; if (m_AttackType == eAttackType.Physics && m_NavMeshAgent.stoppingDistance > 7f) m_NavMeshAgent.stoppingDistance = 7f; //else m_AttackType == eAttackType.Range && m_NavMeshAgent.stoppingDistance > 20f) m_NavMeshAgent.stoppingDistance = 20f; } //if (m_SubRole == eSubRol.Dummy) IsStop = true; // 가만히 맞아야 할 때 #if UNITY_EDITOR if (SceneManager.GetActiveScene().name == "StatTest") return; #endif Set_Warp(Get_position()); } public virtual void Set(MonsterTableData mobtable, Vector3 pos, Action actDie, int order, float noDmgTime = 0f) { m_Role = eRole.Mob; m_SubRole = mobtable.e_MonsterType; m_actDie = actDie; NoDmgTime = noDmgTime; bool IsBoss = IsSubRole(eSubRol.Boss); m_MonsterTableData = mobtable; transform.localScale = m_MonsterTableData.f_DefaultScale * Vector3.one; m_AttackType = m_MonsterTableData.e_AttackType; m_NavStopDistance = m_MonsterTableData.f_BaseATKRange; b_IsFirstAttack = m_MonsterTableData.b_IsFirstAttack; f_BaseChaseRange = m_MonsterTableData.f_BaseChaseRange; //var chapter = InGameInfo.Ins.Get_CurStageData().Chapter; //if (IsBoss && chapter < 101) { GameUI.Ins.IngameUI.m_BossInfoUI.Set(m_MonsterTableData.Get_Name()); } // 순찰 모드로 전환 transform.SetPositionAndRotation(pos, Quaternion.identity); OriginalPos = pos; Set_Warp(pos); Change_MobStatus(eMobStatus.PatrolWait); // 몹 스탯 설정 m_Stat = new ActorStatInfo(m_Role, m_MonsterTableData, m_MonsterAppearData); m_Stat.Set_Maze(); Reset_AttackCoolTime(); m_NavSpeed = m_NavMeshAgent.speed = (float)m_Stat.Get_Stat(eStat.FinalMoveSpeed); //m_NavMeshAgent.avoidancePriority = 51 + order; HealPerSec = 0f; HPRegenPerSec = (float)m_Stat.Get_Stat(eStat.HP_REGEN_Per1Sec); #if UNITY_EDITOR if (SceneManager.GetActiveScene().name == "StatTest") { transform.localPosition = Vector3.zero; return; } #endif //GameUI.Ins.IngameUI.m_ChapterQuest.Add_PurposeCount(new ChapterQuestAddData { purpose = ePurpose.MeetMob, mobid = m_MonsterTableData.n_MonsterID }); } public void Set(int iteration, bool isFirstAttack, float chaseRange) { b_IsFirstAttack = isFirstAttack; f_BaseChaseRange *= chaseRange; m_Stat.Set_Wave(iteration); } public override void On_Regen(bool reincarnation = false, float healrate = 1f) { base.On_Regen(reincarnation, healrate); Change_MobStatus(eMobStatus.PatrolWait); Check_Battle(); if (m_BoxCollider) m_BoxCollider.enabled = true; m_NavMeshAgent.enabled = true; if (!reincarnation) { Set_Warp(OriginalPos); InGameInfo.Ins.Show_Effect("Effect_MonsterSpawn", Get_position()); Reset_Extra(); list_summon.ForEach(f => f.Off()); } } protected override void Update() { if (IsDead()) return; Update_HealPerSec(); if (NoUpdate_byTool || IsStop || IsCC()) return; if (isNSLDMob && m_NoSeeLongDMob.IsNoSeeMob()) return; base.Update(); if (IsSubRole(eSubRol.Mimic)) Del_Target(); // 순찰 중 switch (MobStatus) { case eMobStatus.PatrolWait: if (m_PatrolDelay > 0f) m_PatrolDelay -= Time.deltaTime; else Change_MobStatus(eMobStatus.PatrolRot); //if (!IsSubRole(eSubRol.Mimic)) // Check_Battle(); break; case eMobStatus.PatrolRot: // TargetPatrolPos 위치를 바라봄 Vector3 directionToTarget = (TargetPatrolPos - transform.position).normalized; Quaternion lookRotation = Quaternion.LookRotation(new Vector3(directionToTarget.x, 0, directionToTarget.z)); transform.rotation = Quaternion.Slerp(transform.rotation, lookRotation, Time.deltaTime * 5f); if (Quaternion.Angle(transform.rotation, lookRotation) < 5f) Change_MobStatus(eMobStatus.Patroling); break; case eMobStatus.Patroling: // TargetPatrolPos 위치로 이동함 m_PatrolTime -= Time.deltaTime; if (m_PatrolTime <= 0f || Complete_Destination()) { Change_MobStatus(eMobStatus.PatrolWait); if (!IsSubRole(eSubRol.Mimic)) Check_Battle(); } break; case eMobStatus.BattleStop: Change_MobStatus(eMobStatus.PatrolRot); if (!IsSubRole(eSubRol.Mimic)) Heal(1f); break; case eMobStatus.Battle: if (isTarget) { if (CurAnim < eAnim.Attack && !DSUtil.WithInDistance(Get_position(), m_Target.Get_position(), m_NavMeshAgent.stoppingDistance)) { if (!isPlaying(eAnim.Attack, eAnim.Attack2, eAnim.Attack3)) Change_MobStatus(eMobStatus.Chase); } else Change_MobStatus(eMobStatus.Attack); } else Change_MobStatus(eMobStatus.BattleStop); Check_Patrol(); break; case eMobStatus.Attack: if (isTarget) { if (m_Target.IsDead()) { Check_Battle(); if (MobStatus == eMobStatus.Attack) // 다음 타겟 못 찾았음 Change_MobStatus(eMobStatus.PatrolWait); } else { if (!DSUtil.WithInDistance(m_Target.Get_position(), Get_position(), m_NavStopDistance)) Change_MobStatus(eMobStatus.Chase); else if (m_attackCoolTime <= 0f) { m_attackCoolTime = 1f; Play_Attack(0, (float)m_Stat.Get_Stat(eStat.FinalAttackSpeed)); } } } else Change_MobStatus(eMobStatus.BattleStop); break; case eMobStatus.Chase: if (isTarget) { Set_Path(m_Target.Get_position()); if (DSUtil.WithInDistance(Get_position(), m_Target.Get_position(), m_NavMeshAgent.stoppingDistance)) Change_MobStatus(eMobStatus.Attack); } Check_Patrol(); break; case eMobStatus.Skill: if (!isTarget || (isTarget && m_Target.IsDead())) Change_MobStatus(eMobStatus.PatrolWait); break; } { if (m_attackCoolTime > 0f) m_attackCoolTime -= Time.deltaTime; if (m_HitTime > 0f) { m_HitTime -= Time.deltaTime; if (m_attackCoolTime > 0f) return; } if (GoToTargetTime > 0f) GoToTargetTime -= Time.deltaTime; //// 일정 시간이 지나면 가장 가까운 적을 다시 찾아봄 //if (FindOtherTargetTime > 0f) //{ // FindOtherTargetTime -= Time.deltaTime; // if (FindOtherTargetTime <= 0f) // { // FindOtherTargetTime = 7.5f; // FindNextTarget(m_FindDist); // } //} } } void Update_HealPerSec() { if (HPRegenPerSec > 0f) { HealPerSec += Time.deltaTime; if (HealPerSec >= 1f) { HealPerSec = 0f; if (m_Stat.Get_Stat(eStat.HP_REGEN_Per1Sec) > 0) Heal(HPRegenPerSec); } } } public override void Change_MobStatus(eMobStatus mobStatus) { #if UNITY_EDITOR if (SceneManager.GetActiveScene().name == "StatTest") return; #endif MobStatus = mobStatus; switch (mobStatus) { case eMobStatus.PatrolWait: Del_Target(); Play_Idle(); m_PatrolDelay = m_MonsterTableData.f_PatrolWaitTime; if (m_NavMeshAgent.isOnNavMesh && !IsCC()) m_NavMeshAgent.isStopped = false; m_NavMeshAgent.stoppingDistance = 0.1f; break; case eMobStatus.PatrolRot: Del_Target(); if (m_NavMeshAgent.isOnNavMesh && !IsCC()) m_NavMeshAgent.isStopped = false; TargetPatrolPos = DSUtil.Get_RandomPos_onNavMesh(OriginalPos, m_MonsterAppearData.f_PatrolRange - 1f, m_MonsterAppearData.f_PatrolRange); break; case eMobStatus.Patroling: Del_Target(); Play_Run(); Set_Path(TargetPatrolPos); m_NavMeshAgent.stoppingDistance = 0.1f; m_PatrolTime = 5f; break; case eMobStatus.Battle: m_NavMeshAgent.stoppingDistance = m_NavStopDistance; FindNextTarget(10000f); break; case eMobStatus.BattleStop: Del_Target(); Play_Idle(); StopMove_Imm(true); Reset_Extra(); break; case eMobStatus.Attack: break; case eMobStatus.Chase: Play_Run(); break; case eMobStatus.Skill: StopMove_Imm(false); m_NavMeshAgent.isStopped = false; break; } } protected override void Check_Battle() { if (b_IsFirstAttack || isTarget) // 타겟이 있었던 상황도 전투 상태였음 { // 선공이고, 플레이어가 추적 범위 안에 들어오면 전투 상태로 전환 var enemies = ActorInfo.Ins.Get_EnemyActors(IsEnemy(), Get_position(), f_BaseChaseRange); if (enemies.Count > 0) Change_MobStatus(eMobStatus.Battle); } } void Check_Patrol() { // 플레이어가 추적 범위 밖으로 나가면 순찰 상태로 전환 if (MobStatus != eMobStatus.PatrolRot && !isPlaying(eAnim.Attack, eAnim.Skill) && (!isTarget || !DSUtil.WithInDistance(OriginalPos, Get_position(), m_MonsterTableData.f_BaseChaseRange))) Change_MobStatus(eMobStatus.BattleStop); } //protected override void Check_Attack() //{ // // 현재 애니메이션 상태 정보를 가져옴 // AnimatorStateInfo stateInfo = m_animation.GetCurrentAnimatorStateInfo(0); // // 애니메이션의 특정 구간 (예: 30%에서 40% 사이) 확인 // if (stateInfo.normalizedTime >= 0.3f && stateInfo.normalizedTime <= 0.4f && stateInfo.IsName("attack")) // { // // 데미지를 한 번만 적용 // if (!hasAppliedDamage) // { // switch (CurAnim) // { // case eAnim.Attack: // case eAnim.Attack2: // case eAnim.Attack3: // case eAnim.Attack4: // switch (m_AttackType) // { // // 근접 // case eAttackType.MeleePhysics: // case eAttackType.MeleeMagic: // case eAttackType.MeleePure: // m_Target.Get_Damage(Get_DamageInfo()); // break; // // 원거리 // case eAttackType.RangePhysics: // case eAttackType.RangeMagic: // case eAttackType.RangePure: // ProjectileInfo.Ins.Shoot_Projectile(m_MonsterTableData.s_Porjectile, this, m_Target, 1f, tfs_ProjectileStart[0].position); // break; // } // break; // case eAnim.Skill1: // case eAnim.Skill2: // break; // } // hasAppliedDamage = true; // 데미지가 적용되었음을 표시 // } // } // else // { // // 구간을 벗어나면 다시 데미지 적용 가능하도록 설정 // hasAppliedDamage = false; // } //} protected void FindNextTarget(float _dist) { switch (m_SubRole) { case eSubRol.Mimic: break; default: m_Target = ActorInfo.Ins.Get_Nearest_EnemyActor_orNull(IsEnemy(), Get_position()); break; } } protected override void Kill_OnePunch(DamageInfo dinfo) { base.Kill_OnePunch(dinfo); if (!DSUtil.CheckNull(dinfo.Beater_pd) && !DSUtil.CheckNull(dinfo.Beater_pd.m_SkillListTableData) && dinfo.Beater_pd.m_SkillListTableData.e_Skill == eEffect.Skill_FireBolt) ServerInfo.Ins.m_ServerData.Add_TitleCondition(59); // 칭호 : 두방은 사치 } protected override void VeryStrongDamage(double dmg) { base.VeryStrongDamage(dmg); if (dmg > 1000000000000d) ServerInfo.Ins.m_ServerData.Add_Item(new ItemData(eItem.Title, 68, 1)); // 칭호 : 한방있는 } protected override void Set_Die() { #if UNITY_EDITOR if (SceneManager.GetActiveScene().name == "StatTest") return; #endif var sdata = ServerInfo.Ins.m_ServerData; if (n_Reincarnation <= 0) { StartCoroutine(Co_Dissolve()); MonsterSimpleInfo.Ins.DeadTarget(this); // 킬 수 DropItemInfo.Ins.Add_MobKill(m_SubRole, m_MonsterTableData.n_MonsterID); // 골드 드랍 DropItemInfo.Ins.Drop_DropItem(2, m_MonsterAppearData.n_DropGold, Get_position()); // 경험치 var pcstat = MyValue.MyPC.Get_StatInfo(); var exp = m_MonsterAppearData.n_DropExp; exp += (uint)(exp * (pcstat.Get_Stat(eStat.EXP_Multiplier) + pcstat.Get_Stat(eStat.Battle_EXP_Multiplier))); sdata.PC.Add_Exp(exp); DropItemInfo.Ins.m_StageDropData.GetExp += exp; DropItemInfo.Ins.m_StageDropData.GetExp.RandomizeCryptoKey(); // 아이템 드랍 var rndBag = table_randombag.Ins.Get_DataList(m_MonsterAppearData.n_DropReward); for (int i = 0; i < rndBag.Count; i++) { if (DSUtil.RandomTrue(rndBag[i].f_DropRate)) { DropItemInfo.Ins.Drop_DropItem(rndBag[i].n_ItemId, rndBag[i].n_DropCount, Get_position()); break; } } #if UNITY_EDITOR if (SceneManager.GetActiveScene().name == "StatTest") return; #endif sdata.Add_CollectionMobKill(m_MonsterTableData.n_MonsterID); sdata.Add_MissionCount(ePurpose.KILL_MONSTER, m_MonsterTableData.n_MonsterID); sdata.Add_MissionCount(ePurpose.KILL_MONSTER_GRADE, IsSubRole(eSubRol.Boss) ? 3 : IsSubRole(eSubRol.Elite) ? 2 : 1); sdata.Add_MissionCount(ePurpose.KILL_MONSTER_GRADE_ACC, IsSubRole(eSubRol.Boss) ? 3 : IsSubRole(eSubRol.Elite) ? 2 : 1); sdata.Add_MissionCount(ePurpose.KILL_MONSTER_ALL); sdata.Add_MissionCount(ePurpose.KILL_MONSTER_ACC); } base.Set_Die(); } IEnumerator Co_Dissolve() { yield return new WaitForSeconds(0.5f); for (int i = 0; i < arr_Renderer.Length; i++) arr_Renderer[i].material = dic_dissolveMat[arr_Renderer[i].name]; float prog = 1f; while (prog > 0f) { yield return null; prog -= Time.deltaTime * 0.5f; for (int i = 0; i < arr_Renderer.Length; i++) arr_Renderer[i].sharedMaterial.SetFloat("_Progress", prog); } } protected override void After_Die() { base.After_Die(); if (n_Reincarnation == 0) m_actDie?.Invoke(m_MonsterTableData.n_MonsterID); } public override void Shoot_Skill() { base.Shoot_Skill(); Reset_AttackCoolTime(); } public override DamageInfo Get_DamageInfo(ProjectileData _pd, bool forceCri = false) { var dinfo = base.Get_DamageInfo(_pd, forceCri); if (m_Stat.Get_Stat(eStat.DMG_TO_HP) > 0d) dinfo.act_returnLastDmg = Get_LastDmg; if (m_MonsterTableData.n_PassiveSkillID > 0) { var skilldata = table_skilllist.Ins.Get_Data_orNull(m_MonsterTableData.n_PassiveSkillID); if (skilldata.e_SkillExtraType != eSkillExtraType.None) { _pd.m_SkillListTableData = skilldata; _pd.e_SkillExtraType = skilldata.e_SkillExtraType; _pd.m_skillTypeConfigTable = table_skilltypeconfig.Ins.Get_Data_orNull(_pd.e_SkillExtraType); } } return dinfo; } private void Get_LastDmg(double dmg) { if (m_Stat.Get_Stat(eStat.DMG_TO_HP) > 0d) Heal(dmg * m_Stat.Get_Stat(eStat.DMG_TO_HP)); } public override void Get_Damage(DamageInfo _dinfo) { //_dinfo.Damage = 1f; // 테스트 : 몹 안 죽게 base.Get_Damage(_dinfo); // 멀리서 맞았으면 맞은 몹을 타겟으로 잡고 주변 몹들도 같이 간다. if (!isTarget) { Change_Target(false, _dinfo.Beater, true); // TODO 정인호 : 몬스터 주변 몬스터 부르기 //var allies = ActorInfo.Ins.Get_EnemyActors(m_Role, transform, m_byHitDist); //for (int i = 0; i < allies.Count; i++) // if (allies[i].Get_Target() == null) // allies[i].Change_Target(false, m_Target, true); } else { var dist1 = DSUtil.Get_Distance(m_Target.Get_position(), Get_position()); var dist2 = DSUtil.Get_Distance(_dinfo.Beater.Get_position(), Get_position()); if (dist1 > dist2) Change_Target(false, _dinfo.Beater, true); } if (m_MonsterTableData.n_PassiveSkillID > 0 && !IsDead()) { var skilldata = table_skilllist.Ins.Get_Data_orNull(m_MonsterTableData.n_PassiveSkillID); if (skilldata.e_SkillEnhance == eSkillEnhance.SummonMob) { int perval1 = (int)(Get_hpLostPercentage() / skilldata.f_ExtraValue1); if (n_SummonCount < perval1) { ++n_SummonCount; if (list_summon.Count >= n_SummonCount * skilldata.f_ExtraValue2) { int startindex = (n_SummonCount - 1) * (int)skilldata.f_ExtraValue2; for (int i = startindex; i < skilldata.f_ExtraValue2; i++) list_summon[i].On_Regen(); } else { var mobData = table_monsterlist.Ins.Get_Data_orNull((int)skilldata.f_ExtraValue4); for (int i = 0; i < skilldata.f_ExtraValue2; i++) { AddrResourceMgr.Ins.LoadObject(mobData.s_MonsterPrefab, handle => { var mob = DSUtil.Get_Clone(handle.Result, transform.parent); AddrResourceMgr.Ins.Set_AddressableReleaseSelf(mob.gameObject); ActorInfo.Ins.Add_Actor(mob, eRole.Mob, m_MonsterAppearData.n_SpawnerID, mobData.s_MonsterPrefab); mob.Set(true, null, null, m_MonsterAppearData.n_SpawnerID, false); mob.Set(mobData, DSUtil.Get_RandomPos_onNavMesh(Get_position(), 2f), null, 0); mob.m_SubRole = eSubRol.Summon; mob.On_Regen(); mob.Get_StatInfo().Reset_byStatInfo(Get_StatInfo(), skilldata.f_ExtraValue3); list_summon.Add(mob); }); } } } } else if (skilldata.e_SkillEnhance == eSkillEnhance.AcidicLiquid) { ProjectileInfo.Ins.Shoot_Projectile("Projectile_Mob_AcidcLiquid", this, null, 0f, Get_position(), 4f, pb => { var pc = pb.GetComponent(); var endpos = DSUtil.Get_RandomPos_onNavMesh(Get_position(), 1.8f, 4f); pc.Launch_Curved(Get_position(), endpos, 1.5f, 4f, true, () => { ProjectileInfo.Ins.Shoot_Projectile("Projectile_Mob_AcidcLiquidPoison", this, null, 1f, pc.transform.position, 3f, pb => { var pd = pb.Get_ProjectTileData(); pd.e_SkillExtraType = eSkillExtraType.Poison; pd.m_skillTypeConfigTable = table_skilltypeconfig.Ins.Get_Data_orNull(pd.e_SkillExtraType); pd.m_SkillListTableData = skilldata; }); }); }); } } } public override void Change_Target(bool _ispc, Actor target, bool _ChangeTargetByHit = false) { base.Change_Target(_ispc, target, _ChangeTargetByHit); if (isTarget) b_IsFirstAttack = true; if (b_IsFirstAttack) Change_MobStatus(eMobStatus.Battle); } protected override void Heal_To_Target() { base.Heal_To_Target(); if (isTarget) m_Target.Heal(HealRate); if (HealCount > 0) { var allies = ActorInfo.Ins.Get_AlliesActors(IsEnemy(), Get_position()); var hurts = allies.FindAll(f => f != m_Target && f.Get_HP() < f.Get_MaxHP()); for (int i = 0; i < hurts.Count; i++) { hurts[i].Heal(HealRate); if (i + 1 == HealCount - 1) break; } } } public override MonsterAppearData Get_MonsterStatTableData() { return m_MonsterAppearData; } public override bool isNoseeMob() { return isNSLDMob ? m_NoSeeLongDMob.IsNoSeeMob() : false; } public override int Get_Group() { return m_SpawnerId; } protected override void Set_MobMark(bool active) { InGameInfo.Ins.Get_Obj(eObj.MobMark).GetComponent().Set(active, this); } public override void Play_Hit() { base.Play_Hit(); switch (m_SubRole) { default: switch (CurAnim) { case eAnim.Die: case eAnim.Hit: case eAnim.Attack: case eAnim.Skill: return; } break; case eSubRol.Elite: switch (CurAnim) { case eAnim.Run: case eAnim.Die: case eAnim.Hit: case eAnim.Attack: case eAnim.Skill: return; } break; case eSubRol.Boss: return; } if (IsDead() || IsCC()) return; if (IsRole(eRole.Mob)) m_HitTime = 0.5f; m_animation.speed = 1f; AnimationPlay(MyValue.Get_AnimName(eAnim.Hit)); } public override void Projectile(AnimationEvent s) { base.Projectile(s); if (ProjectileInfo.isIns) { if (m_MonsterTableData.n_PassiveSkillID > 0) { var skilldata = table_skilllist.Ins.Get_Data_orNull(m_MonsterTableData.n_PassiveSkillID); if (skilldata.e_SkillEnhance == eSkillEnhance.RageOfFairy) { int perval1 = (int)(Get_hpLostPercentage() / skilldata.f_ExtraValue1); if (perval1 > 0) Buff_or_Debuff(eCC.MultiShot, 1000f, 0d, perval1); else Buff_or_Debuff(eCC.MultiShot, 0f, 0d, 0); } } Shoot_Projectile(m_MonsterTableData.s_Porjectile1, 1f, m_MonsterTableData.f_ProjectileLifeTime1, useMeeleAttackPos ? tf_MeeleAttackPos.position : AttackPosIsTargetCenterPos ? m_Target.Get_Center_position() : Get_CenterPositionFoward()); } } public override void Set_Target(Actor target) { base.Set_Target(target); m_NavMeshAgent.stoppingDistance = m_NavStopDistance; Change_MobStatus(eMobStatus.Attack); } protected override void Check_Extra(eSkillExtraTiming timing) { base.Check_Extra(timing); // Dictionary의 Key를 배열로 가져오기 (복사본을 사용하여 수정 방지) var keys = new List(dic_skill.Keys); for (int i = 0; i < keys.Count; i++) { var temp = keys[i]; if (temp.e_SkillExtraType == eSkillExtraType.None) continue; var skilltype = table_skilltypeconfig.Ins.Get_Data_orNull(temp.e_SkillExtraType); if (skilltype.e_SkillExtraTiming == timing) { switch (skilltype.e_SkillExtraType) { case eSkillExtraType.Barrier: // 배리어 On if (dic_skill[temp] > 0 && !IsDead()) { var conditionHP = Get_MaxHP() * temp.f_ExtraValue1; if (conditionHP >= Get_HP()) Show_Barrier(temp, 10000); } break; case eSkillExtraType.Reincarnation: // 부활 체크 if (n_Reincarnation > 0 && IsDead()) { --n_Reincarnation; On_Regen(true, temp.f_ExtraValue2); InGameInfo.Ins.Show_Effect(skilltype.e_SkillEffect, Get_position(), null, skilltype.s_SkillEffectWhenOff); Check_Battle(); } break; case eSkillExtraType.PerHPBuff_SPD: if (!IsDead()) PerHPBuff(eCC.PerHPBuff_SPD, eCC.ATKSPD_Up, eCC.MOVSPD_Up, temp, skilltype); break; case eSkillExtraType.PerHPBuff_ADEF: if (!IsDead()) PerHPBuff(eCC.PerHPBuff_ADEF, eCC.ATK_Mul, eCC.DEF_Mul, temp, skilltype); break; case eSkillExtraType.PerHPBuff_AMDEF: if (!IsDead()) PerHPBuff(eCC.PerHPBuff_AMDEF, eCC.ATK_Mul, eCC.MDEF_Mul, temp, skilltype); break; case eSkillExtraType.PerHPBuff_AASPD: if (!IsDead()) PerHPBuff(eCC.PerHPBuff_AASPD, eCC.ATK_Mul, eCC.ATKSPD_Up, temp, skilltype); break; case eSkillExtraType.PerHPBuff_AMSPD: if (!IsDead()) PerHPBuff(eCC.PerHPBuff_AMSPD, eCC.ATK_Mul, eCC.MOVSPD_Up, temp, skilltype); break; case eSkillExtraType.Revive30: if (!IsDead()) { bool revive = false; if (n_Reincarnation == 3 && Get_hpLostPercentage() >= temp.f_ExtraValue1) revive = true; else if (n_Reincarnation == 2 && Get_hpLostPercentage() >= temp.f_ExtraValue1 * 2f) revive = true; else if (n_Reincarnation == 1 && Get_hpLostPercentage() >= temp.f_ExtraValue1 * 3f) revive = true; if (revive) { if (!DSUtil.CheckNull(revive_co)) StopCoroutine(revive_co); revive_co = StartCoroutine(Co_Revive(3f, temp.f_ExtraValue2, temp.f_ExtraValue3, skilltype)); } } else n_Reincarnation = 0; break; } } } } ProjectileData pd_Barrier = new ProjectileData(); protected void Show_Barrier(SkillListTableData skilldata, float lifeTime) { pd_Barrier.e_SkillExtraType = skilldata.e_SkillExtraType; pd_Barrier.m_skillTypeConfigTable = table_skilltypeconfig.Ins.Get_Data_orNull(skilldata.e_SkillExtraType); dic_skill[skilldata] = 0; dic_skillextra[skilldata.e_SkillExtraType] = true; InGameInfo.Ins.Show_SkillTypeEffect(pd_Barrier, this, lifeTime, effect => { effect.Set_EffectwhenOff(pd_Barrier.m_skillTypeConfigTable.s_SkillEffectWhenOff); if (!dic_CCEffect.ContainsKey(eCC.Barrier)) dic_CCEffect.Add(eCC.Barrier, null); dic_CCEffect[eCC.Barrier] = effect; dic_ccData[eCC.Barrier].TimeUpdate = true; dic_ccData[eCC.Barrier].CCTime = lifeTime; dic_ccData[eCC.Barrier].CCDmg = Get_MaxHP() * skilldata.f_ExtraValue2; }); } Coroutine revive_co; IEnumerator Co_Revive(float revivetime, float nodmgtime, float dmg, SkillTypeConfigTableData skilltype) { --n_Reincarnation; DeadStatus = true; Play_Die(); var raisetime = 0.5f; var showindigatetime = 0.5f; yield return new WaitForSeconds(showindigatetime); IndicatorInfo.Ins.Show_Indicator("Revive30_Indicator", Get_position(), revivetime - showindigatetime + raisetime); yield return new WaitForSeconds(revivetime - showindigatetime); // 무적 쉴드 NoDmgTime = nodmgtime + raisetime; InGameInfo.Ins.Show_Effect(skilltype.e_SkillEffect, this, skilltype.e_EffectLocation, nodmgtime, effect => { effect.Set_EffectwhenOff(skilltype.s_SkillEffectWhenOff); }); yield return new WaitForSeconds(raisetime); Play_Idle(); DeadStatus = false; ProjectileInfo.Ins.Shoot_Projectile("Projectile_Revive30", this, null, dmg, Get_position(), 2f); } void PerHPBuff(eCC skilltypecc, eCC buffcc1, eCC buffcc2, SkillListTableData temp, SkillTypeConfigTableData skilltype) { if (!dic_ccValue.ContainsKey(buffcc1)) dic_ccValue.Add(buffcc1, 0f); if (!dic_ccValue.ContainsKey(buffcc2)) dic_ccValue.Add(buffcc2, 0f); int perval1 = (int)(Get_hpLostPercentage() / temp.f_ExtraValue1); if (temp.f_ExtraValue1 > 0) // val1이 0이 아니어야 함 (0으로 나누는 오류 방지) { dic_ccValue[buffcc1] = perval1 * temp.f_ExtraValue2; dic_ccValue[buffcc2] = perval1 * temp.f_ExtraValue3; } if (perval1 > 0) Show_TargetEffect_NoTime(skilltypecc, skilltype.e_SkillEffect, skilltype.e_EffectLocation); } void Show_TargetEffect_NoTime(eCC cc, string effectprefab, eEffectLocation location) { if (!dic_CCEffect.ContainsKey(cc) || !dic_CCEffect[cc].IsMyEffect(this)) InGameInfo.Ins.Show_Effect(effectprefab, this, location, 1000000, effect => { if (!dic_CCEffect.ContainsKey(cc)) dic_CCEffect.Add(cc, null); dic_CCEffect[cc] = effect; }); else if (!dic_CCEffect[cc].isActiveAndEnabled) { dic_CCEffect[cc].gameObject.SetActive(true); dic_CCEffect[cc].Reset_Time(100000); } } float Get_hpLostPercentage() { var maxHP = Get_MaxHP(); return (float)((maxHP - Get_HP()) / maxHP); // 체력 감소 비율 (0.0 ~ 1.0) } void Reset_Extra() { if (m_MonsterTableData.n_PassiveSkillID <= 0) return; var pSkill = table_skilllist.Ins.Get_Data_orNull(m_MonsterTableData.n_PassiveSkillID); if (!dic_skill.ContainsKey(pSkill)) dic_skill[pSkill] = 0; n_SummonCount = n_Reincarnation = 0; list_summon.ForEach(f => f.Off()); switch (pSkill.e_SkillExtraType) { case eSkillExtraType.Barrier: dic_skill[pSkill] = 1; dic_skillextra[pSkill.e_SkillExtraType] = false; // dic_ccData[eCC.Barrier]가 존재하는지 확인 후 초기화 if (dic_ccData.ContainsKey(eCC.Barrier)) dic_ccData[eCC.Barrier].CCDmg = 0; // dic_CCEffect[eCC.Barrier]가 존재하는지 확인 후 제거 if (dic_CCEffect.ContainsKey(eCC.Barrier) && dic_CCEffect[eCC.Barrier].IsMyEffect(this)) dic_CCEffect[eCC.Barrier].Off_Imm(); break; case eSkillExtraType.Reincarnation: n_Reincarnation = (int)pSkill.f_ExtraValue1; // 부활 횟수 break; case eSkillExtraType.Revive30: n_Reincarnation = 3; break; } } public void Set_NoUpdate_byTool(MonsterTableData mobdata) { NoUpdate_byTool = true; m_MonsterTableData = mobdata; } }