2025-02-13 04:40:04 +00:00
using CodeStage.AntiCheat.ObscuredTypes ;
2024-12-15 01:39:39 +00:00
using System ;
using System.Collections ;
using System.Collections.Generic ;
using System.Linq ;
using UnityEngine ;
using UnityEngine.AI ;
2024-12-28 00:20:09 +00:00
using UnityEngine.SceneManagement ;
2024-12-15 01:39:39 +00:00
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 ;
2025-05-05 04:07:34 +00:00
protected AfterImageBase m_AfterImage ;
2024-12-15 01:39:39 +00:00
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 ; // 내가 받은 피해, 내가 준 피해, 공격 쿨타임
2025-03-03 01:55:09 +00:00
protected Transform tf_HUD_HP , tf_HUD_Dmg , tf_Top ;
2025-01-13 02:40:17 +00:00
protected Vector3 ObjSize ;
2024-12-15 01:39:39 +00:00
[SerializeField]
2025-02-08 22:22:31 +00:00
Actor _target ;
protected Actor m_Target
{
get { return _target ; }
set
{
_target = value ;
isTarget = value ;
}
}
public bool isTarget = false ;
protected Actor m_MyPet ;
2024-12-15 01:39:39 +00:00
protected Dictionary < int , UseMagicInfo > dic_MagicData = new Dictionary < int , UseMagicInfo > ( ) ;
protected UseMagicInfo m_Magic ;
2025-02-25 06:28:10 +00:00
protected float m_NavSpeed , m_NavStopDistance , m_HitTime , FindOtherTargetTime = 7.5f , GoToTargetTime = 10f , m_fAttackMoveTime , m_DamageRate = 1f ;
2025-02-08 22:22:31 +00:00
protected bool m_Enemy , IsPlaySkill , m_Auto , DeadStatus , hasAppliedDamage ;
2024-12-15 01:39:39 +00:00
protected bool _isstop ;
protected bool IsStop
{
get { return _isstop ; }
set
{
_isstop = value ;
2025-02-21 07:10:14 +00:00
//if (IsSubRole(eSubRol.Boss))
2025-02-21 23:47:07 +00:00
//if (IsRole(eRole.PC)) Debug.Log("IsStop 변경 " + value);
2024-12-15 01:39:39 +00:00
}
}
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 ;
2025-02-13 01:13:22 +00:00
protected Dictionary < eSkillExtraType , bool > dic_skillextra = new Dictionary < eSkillExtraType , bool > ( ) ; // 부가 효과 적용 여부
2024-12-15 01:39:39 +00:00
protected Dictionary < eCC , CCData > dic_ccData = new Dictionary < eCC , CCData > ( ) ;
2025-02-13 22:49:30 +00:00
protected Dictionary < eCC , float > dic_ccValue = new Dictionary < eCC , float > ( ) ;
2024-12-15 01:39:39 +00:00
protected Dictionary < eCC , Dictionary < Actor , double > > dic_ccAddRate = new Dictionary < eCC , Dictionary < Actor , double > > ( ) ;
2025-02-12 04:39:37 +00:00
protected Dictionary < eCC , TurnOff_GO > dic_CCEffect = new Dictionary < eCC , TurnOff_GO > ( ) ;
2025-02-13 04:40:04 +00:00
protected ObscuredInt _Reincarnation ;
public int n_Reincarnation
{
get { return _Reincarnation ; }
2025-02-21 01:07:35 +00:00
set
{
_Reincarnation = value ;
_Reincarnation . RandomizeCryptoKey ( ) ;
}
2025-02-13 04:40:04 +00:00
}
2025-02-24 06:39:27 +00:00
protected int n_SummonCount ;
2025-02-21 01:07:35 +00:00
protected float NoDmgTime = 0f ;
2025-05-22 05:44:55 +00:00
protected Action < ProjectileBase > action_pb ;
2025-01-17 00:28:44 +00:00
2024-12-15 01:39:39 +00:00
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 ( ) ;
2025-02-13 22:49:30 +00:00
foreach ( var key in dic_ccValue . Keys . ToList ( ) )
dic_ccValue [ key ] = 0f ;
2024-12-15 01:39:39 +00:00
dic_ccAddRate . Clear ( ) ;
foreach ( var key in dic_skillextra . Keys . ToList ( ) )
dic_skillextra [ key ] = false ;
2025-05-14 06:24:19 +00:00
Off_CCEffect ( ) ;
}
protected void Off_CCEffect ( )
{
2025-03-01 03:41:56 +00:00
foreach ( var item in dic_CCEffect )
2026-09-06 23:24:25 +00:00
// PD #747 — 씬 언로드 순서 때문에 이미 파괴된 TurnOff_GO 에 접근하면 MissingReferenceException 이 난다.
if ( ! DSUtil . CheckNull ( item . Value ) ) item . Value . Off_Imm ( ) ;
2024-12-15 01:39:39 +00:00
}
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 ;
2025-03-05 03:47:01 +00:00
public HUDUI m_HUDUI ;
protected bool isHUDUI ;
2024-12-15 01:39:39 +00:00
protected virtual void Awake ( )
{
m_NavMeshAgent = GetComponent < NavMeshAgent > ( ) ;
m_BoxCollider = GetComponent < BoxCollider > ( ) ;
m_animation = GetComponent < Animator > ( ) ;
2025-05-23 18:58:37 +00:00
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 ( ) ;
2024-12-15 01:39:39 +00:00
arr_Effects = GetComponentsInChildren < TurnOff_GO > ( true ) ;
2025-05-05 04:07:34 +00:00
m_AfterImage = GetComponent < AfterImageBase > ( ) ;
if ( ! DSUtil . CheckNull ( m_AfterImage ) ) m_AfterImage . enabled = false ;
2024-12-15 01:39:39 +00:00
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 ;
}
}
2025-03-03 01:55:09 +00:00
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 ) ) ;
2024-12-15 01:39:39 +00:00
}
protected virtual void Start ( )
{
}
protected virtual void OnEnable ( )
{
if ( gos_Effect ! = null ) DSUtil . InActivateGameObjects ( gos_Effect ) ;
2025-02-12 04:39:37 +00:00
StartCoroutine ( Co_Update_ETC ( ) ) ;
2024-12-15 01:39:39 +00:00
}
protected virtual void OnDestroy ( )
{
2025-03-05 03:47:01 +00:00
Off_Common ( ) ;
2025-06-03 20:55:04 +00:00
Off_CCEffect ( ) ;
2025-05-23 22:18:32 +00:00
var enemies = ActorInfo . Ins . Get_EnemyActors ( m_Enemy , Get_position ( ) , 100f ) ;
for ( int i = 0 ; i < enemies . Count ; i + + )
enemies [ i ] . ForceDel ( this ) ;
2024-12-15 01:39:39 +00:00
}
2025-02-12 04:39:37 +00:00
virtual protected void Update ( ) { }
2024-12-15 01:39:39 +00:00
2025-03-05 03:47:01 +00:00
void Off_Common ( )
{
if ( isHUDUI )
{
2026-09-05 03:13:49 +00:00
// Unity 6: 씬 전환 시 HUDUI 가 액터보다 먼저 파괴되면 MissingReferenceException (2026-09-05 #744 실측) → 파괴된 HUD 는 건너뛴다
if ( m_HUDUI ! = null ) m_HUDUI . Off ( true ) ;
2025-03-05 03:47:01 +00:00
isHUDUI = false ;
}
}
2025-02-12 04:39:37 +00:00
IEnumerator Co_Update_ETC ( )
2024-12-15 01:39:39 +00:00
{
2025-02-12 04:39:37 +00:00
while ( true )
2024-12-15 01:39:39 +00:00
{
2025-02-12 04:39:37 +00:00
yield return null ;
2024-12-15 01:39:39 +00:00
2025-02-21 01:07:35 +00:00
if ( NoDmgTime > 0f ) NoDmgTime - = Time . deltaTime ;
2025-02-12 04:39:37 +00:00
foreach ( var item in dic_ccData )
2024-12-15 01:39:39 +00:00
{
2025-02-12 04:39:37 +00:00
if ( item . Value . CCReduceTime > 0f )
2024-12-15 01:39:39 +00:00
{
2025-02-12 04:39:37 +00:00
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 )
2024-12-15 01:39:39 +00:00
{
2025-02-12 04:39:37 +00:00
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 ;
2025-02-26 02:42:03 +00:00
case eCC . Barrier :
dic_ccData [ item . Key ] . CCDmg = 0 ;
dic_CCEffect [ item . Key ] . Off_Imm ( ) ;
break ;
2025-02-12 04:39:37 +00:00
}
2024-12-15 01:39:39 +00:00
}
}
}
}
}
2025-03-03 01:55:09 +00:00
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 ;
}
2025-01-17 00:28:44 +00:00
public bool isPlaying ( )
2024-12-15 01:39:39 +00:00
{
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 ;
}
2025-01-24 05:28:12 +00:00
public virtual void Set_Target ( bool forcefind = false ) { }
2025-05-09 04:27:04 +00:00
public virtual void Set_Target ( Actor target ) { m_Target = target ; isTarget = m_Target ; }
2024-12-15 01:39:39 +00:00
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 ( )
{
2025-03-05 03:47:01 +00:00
Off_Common ( ) ;
2024-12-15 01:39:39 +00:00
gameObject . SetActive ( false ) ;
}
2025-03-05 03:47:01 +00:00
protected HUDUI Get_HUDUI ( )
{
if ( ! isHUDUI )
{
m_HUDUI = InGameInfo . Ins . Get_HUDUI ( ) ;
isHUDUI = m_HUDUI ;
}
return m_HUDUI ;
}
2024-12-15 01:39:39 +00:00
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 )
{
2025-08-01 07:09:46 +00:00
if ( gameObject . activeInHierarchy )
2025-02-16 00:14:00 +00:00
{
2025-08-01 07:09:46 +00:00
if ( IsStop & & ! _stop )
{
Change_MobStatus ( eMobStatus . PatrolWait ) ;
Check_Battle ( ) ;
}
2025-02-16 00:14:00 +00:00
2025-08-01 07:09:46 +00:00
IsStop = _stop ;
if ( IsStop )
{
if ( m_NavMeshAgent & & m_NavMeshAgent . isOnNavMesh ) m_NavMeshAgent . ResetPath ( ) ;
Play_Idle ( ) ;
}
2024-12-15 01:39:39 +00:00
}
}
public void Set_AttackCoolTime ( float _rate )
{
var cooltime = m_Stat . Get_Stat ( eStat . AttackCoolTime ) * _rate ;
if ( m_attackCoolTime > cooltime )
m_attackCoolTime = cooltime ;
}
2025-02-19 04:14:09 +00:00
public virtual DamageInfo Get_DamageInfo ( ProjectileData _pd , bool forceCri = false )
2024-12-15 01:39:39 +00:00
{
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 ;
2025-02-05 00:47:02 +00:00
2025-01-10 05:27:03 +00:00
dinfo . BaseDmg = addAtk + ( _pd . BaseDmg > 0 ? _pd . BaseDmg : m_Stat . Get_Stat ( eStat . BaseDmg ) ) ;
2025-02-13 23:52:05 +00:00
if ( dic_ccValue . ContainsKey ( eCC . ATK_Mul ) ) dinfo . BaseDmg + = dinfo . BaseDmg * dic_ccValue [ eCC . ATK_Mul ] ;
2025-02-05 00:47:02 +00:00
var atkDownData = Get_CCData ( eCC . ATK_Down ) ;
if ( atkDownData . CCTime > 0f ) dinfo . BaseDmg - = dinfo . BaseDmg * atkDownData . CCDmg ;
2025-01-20 06:47:44 +00:00
if ( dic_ccData [ eCC . MultiShot ] . CCTime > 0f )
dinfo . BaseDmg - = dinfo . BaseDmg * dic_ccData [ eCC . MultiShot ] . CCDmg ;
2025-02-21 04:45:42 +00:00
dinfo . NoCriDmg = dinfo . Damage = dinfo . BaseDmg * _pd . AddDmgPercent ;
2025-01-10 05:27:03 +00:00
dinfo . PiercingDmg = dinfo . BaseDmg *
( m_Stat . Get_Stat ( eStat . PIERCING_Multiplier ) +
dinfo . Get_AddValue ( eAddExtraType . Add_Piercing_Multiplier_bySpiriBless ) ) ;
2024-12-15 01:39:39 +00:00
dinfo . TextColor = HUDBase . Get_TextColor ( m_AttackType ) ;
if ( dic_ccData [ eCC . PiercingBuff ] . CCTime > 0 )
2024-12-16 06:01:17 +00:00
dinfo . PiercingDmg + = dinfo . BaseDmg * m_Stat . Get_Stat ( eStat . PiercingBuff_byAttack ) ;
2024-12-15 01:39:39 +00:00
2024-12-16 06:01:17 +00:00
dinfo . Cal_Cri ( m_AttackType , m_Stat , forceCri ) ; // 크리티컬 판정
2024-12-15 01:39:39 +00:00
return dinfo ;
}
public virtual void Get_Damage ( DamageInfo _dinfo )
{
if ( IsDead ( ) | | m_bImmortal ) return ;
if ( _dinfo . Damage < 1 ) _dinfo . Damage = 1d ;
2025-01-10 05:27:03 +00:00
//if (Get_HP() >= Get_MaxHP() && Get_HP() <= _dinfo.Damage) // 한 방에 사망
// Kill_OnePunch(_dinfo);
//VeryStrongDamage(_dinfo.Damage);
2024-12-15 01:39:39 +00:00
//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 ) ;
2025-02-13 23:52:05 +00:00
if ( dic_ccValue . ContainsKey ( eCC . DEF_Mul ) ) def + = def * dic_ccValue [ eCC . DEF_Mul ] ;
2025-05-09 04:27:04 +00:00
if ( dic_ccData [ eCC . DEF_Mul ] . CCTime > 0f ) def + = def * dic_ccData [ eCC . DEF_Mul ] . CCDmg ;
2024-12-15 01:39:39 +00:00
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 ) ;
2025-02-13 23:52:05 +00:00
if ( dic_ccValue . ContainsKey ( eCC . MDEF_Mul ) ) def + = def * dic_ccValue [ eCC . MDEF_Mul ] ;
2025-05-09 04:27:04 +00:00
if ( dic_ccData [ eCC . MDEF_Mul ] . CCTime > 0f ) def + = def * dic_ccData [ eCC . MDEF_Mul ] . CCDmg ;
2025-02-13 23:52:05 +00:00
2024-12-15 01:39:39 +00:00
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
{
2025-01-10 05:27:03 +00:00
var cal1 = 2d - Math . Pow ( 0.94d , def * 0.01 ) ;
2024-12-15 01:39:39 +00:00
var cal2 = cal1 * 1000000 ;
var cal3 = cal2 * _dinfo . Damage ;
_dinfo . Damage = Math . Round ( cal3 * 0.000001 ) ; // /1000000을 *0.000001로 변경
}
// 몬스터 데미지 추가
2025-01-26 18:16:16 +00:00
if ( IsSubRole ( eSubRol . None ) )
2024-12-15 01:39:39 +00:00
{
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 ) ;
}
2025-01-26 18:16:16 +00:00
else if ( IsSubRole ( eSubRol . Boss ) )
2024-12-15 01:39:39 +00:00
{
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 ) ;
2025-02-12 23:16:26 +00:00
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 ) ;
2024-12-15 01:39:39 +00:00
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 ) ;
2025-02-11 00:15:02 +00:00
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 ) ;
2024-12-15 01:39:39 +00:00
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 ;
2025-02-12 04:39:37 +00:00
dic_CCEffect [ eCC . ManaShield ] . Off_Imm ( ) ;
2024-12-15 01:39:39 +00:00
}
}
2024-12-28 00:20:09 +00:00
Cal_Damage ( _dinfo ) ;
Check_CC_byAttack ( _dinfo ) ;
2025-02-13 04:40:04 +00:00
Check_Extra ( eSkillExtraTiming . GetHit ) ;
2024-12-28 00:20:09 +00:00
2025-05-03 23:06:28 +00:00
//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);
//}
2026-09-06 23:24:25 +00:00
// WL #797 — 타격 임팩트(히트스톱·카메라 셰이크·몹 스케일 펀치). OnHit 이 false 를 반환하면
// (설정 에셋 없음 · enabled=false) 기존 ShakeCamera(1) 경로가 그대로 실행된다 = 100% 롤백.
2026-09-08 14:30:57 +00:00
if ( ! WL . Combat . Core . CombatEvents . RaiseHitConfirmed ( this , _dinfo ) ) MyValue . m_RealCamera . ShakeCamera ( 1 ) ;
2025-05-01 21:41:01 +00:00
2024-12-15 01:39:39 +00:00
#if UNITY_EDITOR
2026-10-19 16:48:59 +00:00
if ( WL . SkillTest . WLTestScene . Active )
2024-12-15 01:39:39 +00:00
{
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 ;
2025-02-21 01:07:35 +00:00
if ( NoDmgTime > 0f ) _dinfo . Damage = 0d ;
2024-12-15 01:39:39 +00:00
2025-08-01 22:15:16 +00:00
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 ( ) ;
2025-02-13 01:13:22 +00:00
}
}
2025-08-01 22:15:16 +00:00
else
m_Stat . Set_Stat ( false , eStat . HP , _dinfo . Damage ) ;
2025-01-30 23:36:55 +00:00
2024-12-15 01:39:39 +00:00
_dinfo . act_returnLastDmg ? . Invoke ( _dinfo . Damage ) ;
_dinfo . act_CriAfterHit ? . Invoke ( _dinfo . Critical ) ;
Add_ReceiveDmg ( _dinfo . Damage ) ;
_dinfo . Beater ? . Add_MyRealDmg ( _dinfo . Damage ) ;
BossRaidCard_HP ( ) ;
2025-08-01 22:15:16 +00:00
bool nightmare = MyValue . MyChoiceMapData . Get_MapData ( ) . e_Dungeon = = eDungeon . d3_Nightmare & & IsSubRole ( eSubRol . Boss ) ;
if ( nightmare )
{
var diff = MyValue . MyChoiceMapData . n_Difficult ;
var addmul = diff = = 1 ? 1 : diff = = 2 ? 1.5d : 2.5d ;
DungeonInfo . Ins . Add_TotalDmg ( _dinfo . Damage * addmul ) ;
}
2024-12-15 01:39:39 +00:00
var pd = _dinfo . Beater_pd ;
2025-03-01 03:21:02 +00:00
bool ispd = ! DSUtil . CheckNull ( pd ) ;
2025-08-01 22:15:16 +00:00
if ( ! nightmare & & m_Stat . Get_Stat ( eStat . HP ) < = 0d )
2024-12-15 01:39:39 +00:00
{
Check_Drain_byKill ( _dinfo ) ;
DeadStatus = _dinfo . IsDeadByThisDamage = true ;
Set_Die ( ) ;
if ( _dinfo . Beater . IsRole ( eRole . BattleNPC ) )
ServerInfo . Ins . m_ServerData . Add_TitleCondition ( 72 , 73 ) ; // 칭호 : 승차감 좋은 버스, 택시
2025-03-01 03:21:02 +00:00
if ( ispd & & pd . IsSkill ( eEffect . Skill_Ignited ) & & pd . CC_AddVal2 > 0 )
2024-12-15 01:39:39 +00:00
{ // 버닝효과 체크, 버닝 효과 발동
var ccData = Get_CCData ( eCC . Burn ) ;
if ( ccData . CCTime > 0f )
{
2025-03-01 03:21:02 +00:00
var other = ActorInfo . Ins . Get_Nearest_AllyActor ( IsEnemy ( ) , Get_position ( ) , 5f ) ;
2024-12-15 01:39:39 +00:00
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 ) ;
2025-02-12 04:39:37 +00:00
pd . m_ProjectileBase . Show_Effect ( other , ( float ) ccData . CCTime ) ;
2024-12-15 01:39:39 +00:00
}
}
}
}
else
{
2025-03-01 03:21:02 +00:00
if ( ! ispd | | ispd & & ! pd . IsSkill ( eEffect . Skill_Ignited ) & & ! pd . IsSkill ( eEffect . Select_BodyonFire ) )
2024-12-15 01:39:39 +00:00
Play_Hit ( ) ;
}
2024-12-28 00:20:09 +00:00
#if UNITY_EDITOR
2026-10-19 16:48:59 +00:00
if ( WL . SkillTest . WLTestScene . Active )
2024-12-28 00:20:09 +00:00
return ;
#endif
2024-12-15 01:39:39 +00:00
// UI 설정
2025-03-05 03:47:01 +00:00
Get_HUDUI ( ) . Set_HPSlider ( this ) ;
2024-12-15 01:39:39 +00:00
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 ;
2025-05-06 22:41:40 +00:00
Del_Target ( ) ;
2024-12-15 01:39:39 +00:00
2025-05-14 06:24:19 +00:00
Off_CCEffect ( ) ;
2024-12-15 01:39:39 +00:00
Set_Outline ( false ) ;
}
protected virtual void After_Die ( )
{
2025-02-21 01:07:35 +00:00
Check_Extra ( eSkillExtraTiming . AfterDie ) ; // 부활 체크 (Reincarnation 은 여기서 처리)
2025-02-13 04:40:04 +00:00
if ( ! IsDead ( ) ) return ; // 부활한 경우
2024-12-15 01:39:39 +00:00
#if UNITY_EDITOR
2026-10-20 09:27:56 +00:00
if ( WL . SkillTest . WLTestScene . Active & & ! ( this is SummonActor ) ) // WL #950 — 소환체(골렘)는 원래 길로(테스트 씬 되살리기 분기가 MyActor.On_Regen → LoadMapMgr 없음 NRE 70회 · 실측)
2024-12-15 01:39:39 +00:00
{
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();
2025-03-07 23:45:34 +00:00
// PC 부활
On_Regen ( ) ;
2025-03-07 06:50:46 +00:00
if ( ! IsEnemy ( ) )
{
2025-03-07 23:45:34 +00:00
// 소환수 이동
var summ = ActorInfo . Ins . Get_Actor ( eRole . Summon ) ;
if ( ! DSUtil . CheckNull ( summ ) ) summ . On_Regen ( ) ;
// 펫 부활
if ( ! DSUtil . CheckNull ( m_MyPet ) ) m_MyPet . On_Regen ( ) ;
// 일꾼 이동
2025-03-07 06:50:46 +00:00
var worker = ActorInfo . Ins . Get_Actor ( eRole . Worker ) ;
if ( ! DSUtil . CheckNull ( worker ) ) worker . On_Regen ( ) ;
}
2024-12-15 01:39:39 +00:00
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 ( ) { }
2025-04-02 23:29:27 +00:00
public virtual bool Use_Skill ( SkillListTableData skilldata , bool autoskill , int index , bool testskill = false )
2024-12-15 01:39:39 +00:00
{
if ( skilldata . e_SkillType = = eSkillType . Passive ) return false ;
if ( CurAnim > = eAnim . Skill )
{
if ( ! autoskill ) MessageInfo . Ins . Add_Log ( 109 ) ;
return false ;
}
// 내 직업이 사용할 수 있는 스킬인가?
2026-10-18 15:09:05 +00:00
// WL #839 훅(1줄) — WL 추가 직업(대검 10801 등 · json skillsFromTemplate)은 템플릿 직업(한손검 10101)의 스킬을 쓴다.
if ( skilldata . n_SkillClassID > 0 & & skilldata . n_SkillClassID ! = m_ID & & ! WL . Combat . Weapon . WLWeaponClasses . IsTemplateSkillClass ( m_ID , skilldata . n_SkillClassID ) )
2024-12-15 01:39:39 +00:00
return false ;
2025-02-12 04:39:37 +00:00
// 침묵의 저주에 걸린 경우 스킬 사용 불가
var SilenceCurseData = Get_CCData ( eCC . SilenceCurse ) ;
if ( SilenceCurseData . CCTime > 0f ) return false ;
2024-12-15 01:39:39 +00:00
m_Magic = dic_MagicData [ index ] ;
2025-04-02 23:29:27 +00:00
if ( testskill ) m_Magic . m_SkillTableData = skilldata ;
2024-12-15 01:39:39 +00:00
if ( IsDead ( ) | | IsCC ( ) | | CurAnim > = eAnim . Skill )
{
Del_Magic ( ) ;
return false ;
}
if ( skilldata . e_SkillTarget = = eSkillTarget . Enemy )
{
2025-02-08 22:22:31 +00:00
if ( ! isTarget ) Set_Target ( true ) ;
if ( ! isTarget )
2024-12-15 01:39:39 +00:00
{
if ( IsRole ( eRole . PC ) ) ToastUI . Ins . Set ( 105 ) ;
Del_Magic ( ) ;
return false ;
}
2026-10-18 15:09:05 +00:00
else if ( ! WL . Combat . Weapon . WLSkillHotkey . IgnoreRangeNow & & ! DSUtil . WithInDistance ( m_Target . Get_position ( ) , Get_position ( ) , skilldata . f_SkillRange ) ) // WL #906 훅 — Q/E 치트키는 사거리 밖에서도 즉시 시전
2024-12-15 01:39:39 +00:00
{ // 마법 사용 사정거리보다 멀리 있다. 마법 쏘러 가자.
return false ;
}
}
2025-05-02 20:49:31 +00:00
if ( IsRole ( eRole . PC ) )
2024-12-15 01:39:39 +00:00
{
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 ;
}
}
2025-05-13 23:11:24 +00:00
if ( ! testskill & & ! Check_MagicCoolTime ( index ) )
return false ;
2024-12-15 01:39:39 +00:00
list_SkillData [ index ] . m_SkillListTableData = m_Magic . m_SkillTableData ;
list_SkillData [ index ] . target = m_Target ;
m_SkillData = list_SkillData [ index ] ;
2025-05-02 20:49:31 +00:00
if ( ! testskill ) m_Stat . Set_Stat ( false , eStat . MP , skilldata . n_MP ) ;
2025-05-13 23:11:24 +00:00
else m_SkillData . SetSkillExtraType = true ;
2024-12-15 01:39:39 +00:00
Set_MagicCoolTime ( skilldata . Get_CoolTime ( m_Stat . Get_Stat ( eStat . COOLTIME_Multiplier ) ) , index ) ;
2026-10-20 08:45:21 +00:00
if ( IsMainPC ( ) & & ! testskill & & ! DSUtil . CheckNull ( ServerInfo . Ins ) & & ServerInfo . Ins . m_ServerData ! = null ) ServerInfo . Ins . m_ServerData . Add_MissionCount ( ePurpose . USE_SKILL_ALL ) ; // WL #949 — 스킬 테스트 씬(서버 객체 없음)에서 NRE 로 스킬이 안 나가던 것(실측 Actor.cs:756) · 서버 있으면 원본 그대로
2024-12-15 01:39:39 +00:00
return Play_Skill ( skilldata . s_Animation ) ;
}
public virtual void Shoot_Skill ( )
{
if ( IsDead ( ) | | DSUtil . CheckNull ( m_Magic ) ) return ;
InGameInfo . Ins . Shoot_Effect ( m_SkillData ) ;
2024-12-28 00:20:09 +00:00
//SoundInfo.Ins.Play_OneShot(eSound.s009_CastSpell);
//if (IsRole(eRole.PC))
//NewGameUI.Ins.m_GuideQuestUI.Add_GuideQuest(ePurpose.ShootMagic);
2024-12-15 01:39:39 +00:00
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 )
{
2025-02-11 07:09:38 +00:00
if ( IsDead ( ) | | Get_HP ( ) > = Get_MaxHP ( ) ) return ;
2024-12-15 01:39:39 +00:00
2025-02-11 07:09:38 +00:00
var heal_hp = Math . Max ( m_Stat . Get_Stat ( eStat . MaxHP ) * _rate , 1d ) ;
2024-12-15 01:39:39 +00:00
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 ) ;
2025-03-05 03:47:01 +00:00
Get_HUDUI ( ) . Set_HPSlider ( this ) ;
2024-12-15 01:39:39 +00:00
}
}
public virtual void Heal ( double _hp , bool showhud = true )
{
2025-02-12 00:04:43 +00:00
if ( IsDead ( ) ) return ;
if ( _hp > 0 )
{
if ( Get_HP ( ) > = Get_MaxHP ( ) ) return ;
_hp = Math . Max ( 1 , _hp ) ;
}
2024-12-15 01:39:39 +00:00
m_Stat . Set_Stat ( true , eStat . HP , _hp ) ;
2024-12-28 00:20:09 +00:00
#if UNITY_EDITOR
2026-10-19 16:48:59 +00:00
if ( WL . SkillTest . WLTestScene . Active )
2024-12-28 00:20:09 +00:00
{
MainToStatTest . Ins . Set_Dmglabel ( $"HP : {_hp}" ) ;
return ;
}
#endif
2025-03-05 03:47:01 +00:00
Get_HUDUI ( ) . Set_HPSlider ( this ) ;
if ( showhud ) InGameInfo . Ins . Get_HUDDMGUI ( ) . Set ( tf_HUD_Dmg , "<color=#00ff00>" , _hp , eStat . None ) ;
2024-12-15 01:39:39 +00:00
}
public virtual void Heal_MP ( double _mp , bool showhud = true )
{
if ( IsDead ( ) ) return ;
2025-02-11 23:22:40 +00:00
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 ) ) ;
2024-12-28 00:20:09 +00:00
#if UNITY_EDITOR
2026-10-19 16:48:59 +00:00
if ( WL . SkillTest . WLTestScene . Active )
2024-12-28 00:20:09 +00:00
{
MainToStatTest . Ins . Set_Dmglabel ( $"MP : {_mp}" ) ;
return ;
}
#endif
2024-12-15 01:39:39 +00:00
if ( showhud )
2025-02-11 23:22:40 +00:00
{
2024-12-15 01:39:39 +00:00
InGameInfo . Ins . Get_HUDDMGUI ( ) . Set ( tf_HUD_Dmg , "<color=#0000ff>" , _mp , eStat . None ) ;
2025-02-11 23:22:40 +00:00
}
2024-12-15 01:39:39 +00:00
}
#region 스 킬 동 작
2025-05-02 03:38:11 +00:00
IEnumerator Co_JumpAttack ( float heightPos_y , float duration , float dist )
2024-12-15 01:39:39 +00:00
{
2025-03-06 23:52:59 +00:00
OptionInfo . Ins . Set_PCMove ( this , false ) ;
2024-12-15 01:39:39 +00:00
var startPos = Get_position ( ) ; // 현재 위치
2025-08-09 13:10:17 +00:00
if ( m_Target . Get_Agent ( ) . radius > dist ) dist = m_Target . Get_Agent ( ) . radius ;
2025-05-02 03:38:11 +00:00
var targetPos = Get_TeleportPostion ( m_Target , dist ) ;
2024-12-15 01:39:39 +00:00
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 ; // 최종 위치 설정
2025-05-23 06:44:50 +00:00
if ( ! DSUtil . IsValidPos_onNaveMesh ( targetPos ) )
transform . position = DSUtil . Get_RandomPos_onNavMesh ( targetPos ) ;
2026-09-06 23:24:25 +00:00
Snap_toNavMesh ( ) ; // #764 — 점프 공격 착지도 NavMesh 위로 고정(반경 0 인 액터는 무동작)
2024-12-15 01:39:39 +00:00
}
2025-01-15 22:32:07 +00:00
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 ;
2025-02-22 05:59:52 +00:00
Model_OnOff ( false ) ;
2025-01-15 22:32:07 +00:00
Weapon_OnOff ( false ) ;
2025-03-06 23:52:59 +00:00
OptionInfo . Ins . Set_PCMove ( this , false ) ;
2025-01-15 22:32:07 +00:00
yield return new WaitForSeconds ( invisibleTime ) ;
m_animation . speed = 1f ;
m_bImmortal = false ;
2025-02-22 05:59:52 +00:00
Model_OnOff ( true ) ;
2025-01-15 22:32:07 +00:00
Weapon_OnOff ( true ) ;
2025-03-06 23:52:59 +00:00
OptionInfo . Ins . Set_PCMove ( this , true ) ;
2025-01-15 22:32:07 +00:00
actVisible ? . Invoke ( ) ;
}
protected virtual void Weapon_OnOff ( bool active ) { }
2025-02-22 05:59:52 +00:00
protected void Model_OnOff ( bool active ) { for ( int i = 0 ; i < arr_Renderer . Length ; i + + ) arr_Renderer [ i ] . enabled = active ; }
2024-12-15 01:39:39 +00:00
#endregion
public int Get_ID ( ) { return m_ID ; }
public virtual int Get_Group ( ) { return 0 ; }
2025-01-11 03:20:35 +00:00
public virtual NavMeshAgent Get_Agent ( ) { return m_NavMeshAgent ; }
2024-12-15 01:39:39 +00:00
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 ) ;
}
2025-03-03 01:55:09 +00:00
public Vector3 Get_Top_postion ( ) { return tf_Top . position ; }
2025-01-20 06:47:44 +00:00
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 ; }
2025-04-06 23:50:39 +00:00
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 ;
}
2025-01-20 06:47:44 +00:00
public Vector3 Get_PositionBack ( float dist = 0.3f ) { return Get_position ( ) + - transform . forward * dist ; }
2025-06-05 00:11:03 +00:00
public Vector3 Get_PositionRight ( float dist = 0.3f ) { return Get_position ( ) + transform . right * dist ; }
2025-01-17 00:28:44 +00:00
public Vector3 Get_TeleportPostion ( Actor target , float dist = 1f )
2025-01-10 02:42:37 +00:00
{
var startPos = Get_position ( ) ; // 현재 위치
2025-01-17 00:28:44 +00:00
Vector3 direction = ( target . Get_position ( ) - Get_position ( ) ) . normalized ; // 목표와 현재 위치의 방향 벡터를 구함
2025-02-21 07:10:14 +00:00
var rtnPos = DSUtil . Get_RandomPos_onNavMesh ( target . Get_position ( ) - direction * dist , 0 , dist ) ;
return rtnPos ;
2025-01-10 02:42:37 +00:00
}
2024-12-15 01:39:39 +00:00
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 ) ; }
2025-03-05 03:47:01 +00:00
public float Get_HPPercent ( ) { return ( float ) ( Get_HP ( ) / Get_MaxHP ( ) ) ; }
2024-12-15 01:39:39 +00:00
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 )
{
2025-02-08 22:22:31 +00:00
if ( isTarget )
2024-12-15 01:39:39 +00:00
{
if ( ! forcedel & & m_Target . m_IsLockTarget & & ! m_Target . IsDead ( ) ) return ;
2025-03-20 00:21:10 +00:00
if ( IsMainPC ( ) ) m_Target . Set_Outline ( false ) ;
2024-12-15 01:39:39 +00:00
}
m_Target = null ;
2025-05-06 22:41:40 +00:00
isTarget = false ;
2024-12-15 01:39:39 +00:00
}
2025-05-23 22:18:32 +00:00
public virtual void ForceDel ( Actor target )
{
if ( isTarget )
{
if ( m_Target = = target )
Del_Target ( true ) ;
}
}
2024-12-15 01:39:39 +00:00
public bool IsDead ( ) { return DeadStatus | | ! gameObject . activeInHierarchy ; }
public bool IsOnlyDead ( ) { return DeadStatus ; }
2025-03-20 00:21:10 +00:00
public bool IsMainPC ( ) { return IsRole ( eRole . PC ) & & ! IsEnemy ( ) ; }
2024-12-15 01:39:39 +00:00
public bool IsRole ( eRole _role ) { return m_Role = = _role ; }
2025-01-26 18:16:16 +00:00
public bool IsSubRole ( eSubRol subRol ) { return m_SubRole = = subRol ; }
2024-12-15 01:39:39 +00:00
public bool IsEnemy ( ) { return m_Enemy ; }
public bool Get_SelectSkill ( eEffect _effect )
{
2025-03-20 00:21:10 +00:00
if ( IsMainPC ( ) )
2024-12-15 01:39:39 +00:00
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 )
{
2025-02-08 22:22:31 +00:00
if ( MyValue . MyPC . isTarget )
2024-12-15 01:39:39 +00:00
MyValue . MyPC . m_Target . Set_Outline ( false ) ;
}
if ( _ChangeTargetByHit )
{
2025-02-08 22:22:31 +00:00
if ( ! isTarget | | m_Target . IsDead ( ) )
2024-12-15 01:39:39 +00:00
{
m_Target = target ;
FindOtherTargetTime = 7.5f ;
}
}
else
{
m_Target = target ;
FindOtherTargetTime = 7.5f ;
}
if ( _ispc )
{
m_Target . Set_Outline ( true ) ;
2025-01-23 06:49:08 +00:00
// 퀘스트 몹은 선택 사항이므로 마녀가 터치로만 타겟을 잡을 수가 있다.
// 퀘스트몹을 선택했다는 것은 퀘스트를 하겠다는 뜻이므로 모든 펫이 같이 때린다.
// 강화석도 같이 캐자.
if ( m_Target . IsRole ( eRole . OBJ ) )
2024-12-15 01:39:39 +00:00
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 ;
2025-02-25 06:28:10 +00:00
m_animation . speed = 1f ;
2024-12-15 01:39:39 +00:00
}
}
#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
2025-02-22 23:13:50 +00:00
protected bool IsCC ( eCC cc ) { return cc > = eCC . Freeze & & cc < eCC . Max ; }
2025-05-12 00:24:26 +00:00
public bool IsCC ( bool ignoreknockback = false )
2024-12-15 01:39:39 +00:00
{
2025-02-11 00:15:02 +00:00
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 ;
2025-05-12 00:24:26 +00:00
if ( ! ignoreknockback & & dic_ccData [ eCC . KnockBack ] . CCTime > 0f ) return true ;
2025-05-23 06:44:50 +00:00
if ( ! ignoreknockback & & dic_ccData [ eCC . KnockBack_byHitter ] . CCTime > 0f ) return true ;
2025-02-11 00:15:02 +00:00
if ( dic_ccData [ eCC . Stun ] . CCTime > 0f ) return true ;
2024-12-15 01:39:39 +00:00
return false ;
}
protected virtual void Check_CC_byAttack ( DamageInfo _dinfo )
{
if ( dic_ccData [ eCC . Freeze ] . CCTime > 0f )
Off_Freeze ( ) ;
float knockbackTime = 0f ;
2025-01-26 18:16:16 +00:00
if ( IsRole ( eRole . Mob ) & & ! IsCC ( ) )
2024-12-15 01:39:39 +00:00
knockbackTime = table_GlobalValue . Ins . Get_Float ( "f_KnockbackTime" ) ;
var beaterActor = _dinfo . Beater ;
2025-02-10 06:47:31 +00:00
if ( ! DSUtil . CheckNull ( beaterActor ) )
2024-12-15 01:39:39 +00:00
{
2025-05-12 22:20:47 +00:00
//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);
//}
2024-12-15 01:39:39 +00:00
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 ) ) ;
2025-02-10 06:47:31 +00:00
2024-12-15 01:39:39 +00:00
var STCdata = _dinfo . Beater_pd . m_skillTypeConfigTable ;
if ( ! DSUtil . CheckNull ( STCdata ) & & STCdata . e_SkillExtraTiming = = eSkillExtraTiming . Hit )
Check_Extra ( _dinfo ) ;
Check_EnchantOption ( _dinfo ) ;
2026-10-18 15:09:05 +00:00
if ( knockbackTime > 0f & & ! WL . Combat . Reaction . WLSkillHitFeel . SkipKnockback ( _dinfo ) ) KnockBack ( knockbackTime ) ; // WL #918 검 궁극기 등 넉백 없는 스킬
2024-12-15 01:39:39 +00:00
}
}
}
2025-02-13 04:40:04 +00:00
protected virtual void Check_Extra ( eSkillExtraTiming timing ) { }
2024-12-15 01:39:39 +00:00
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 ( ) ;
2025-02-12 04:39:37 +00:00
eCC curCC = eCC . Max ;
2024-12-15 01:39:39 +00:00
float LifeTime = val1 + pd . CC_AddTime ;
switch ( skillExtraData . e_SkillExtraType )
{
// 적에게
case eSkillExtraType . Frost :
2025-02-12 04:39:37 +00:00
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 ;
2024-12-15 01:39:39 +00:00
case eSkillExtraType . Slow :
2025-02-12 04:39:37 +00:00
curCC = eCC . MOVSPD_Down ;
2024-12-15 01:39:39 +00:00
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 :
2025-05-12 22:20:47 +00:00
curCC = eCC . Freeze ;
2024-12-15 01:39:39 +00:00
Freeze ( LifeTime , _dinfo . act_AfterFreezing ) ;
if ( _dinfo . bExtraDmg ) Cal_Damage ( _dinfo ) ; // 추가 데미지
break ;
case eSkillExtraType . Burn :
2025-02-12 04:39:37 +00:00
curCC = eCC . Burn ;
2024-12-15 01:39:39 +00:00
DotDamage ( eCC . Burn , _dinfo . Copy ( ) , LifeTime , val2 + pd . CC_AddVal1 , val3 ) ;
break ;
case eSkillExtraType . Blood :
2025-02-12 04:39:37 +00:00
curCC = eCC . Blooding ;
2024-12-15 01:39:39 +00:00
DotDamage ( eCC . Blooding , _dinfo . Copy ( ) , LifeTime , val2 , val3 ) ;
break ;
case eSkillExtraType . Poison :
2025-02-12 04:39:37 +00:00
curCC = eCC . Poison ;
2024-12-15 01:39:39 +00:00
DotDamage ( eCC . Poison , _dinfo . Copy ( ) , LifeTime , val2 , val3 ) ;
break ;
case eSkillExtraType . Stun :
2025-02-12 04:39:37 +00:00
curCC = eCC . Stun ;
2024-12-15 01:39:39 +00:00
Stun ( LifeTime ) ;
if ( _dinfo . bExtraDmg )
{
_dinfo . Damage * = val2 + pd . CC_AddDmg ;
Cal_Damage ( _dinfo ) ; // 스턴 추가 데미지
}
break ;
case eSkillExtraType . DefenceDecrease :
2025-02-12 04:39:37 +00:00
curCC = eCC . DEF_Down ;
2024-12-15 01:39:39 +00:00
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 :
2025-02-12 04:39:37 +00:00
curCC = eCC . ManaShield ;
2024-12-15 01:39:39 +00:00
LifeTime + = LifeTime * pd . Get_SkillDamagePercent ( ) ;
2025-02-12 04:39:37 +00:00
Buff_or_Debuff ( curCC , LifeTime , val3 , 0 , val2 ) ;
2024-12-15 01:39:39 +00:00
break ;
case eSkillExtraType . SwordShield :
break ;
case eSkillExtraType . SpeedUp :
2025-02-12 04:39:37 +00:00
curCC = eCC . ATKSPD_Up ;
2024-12-15 01:39:39 +00:00
Buff_or_Debuff ( eCC . ATKSPD_Up , LifeTime , val2 ) ;
Buff_or_Debuff ( eCC . MOVSPD_Up , LifeTime , val3 ) ;
break ;
case eSkillExtraType . Aggro :
2025-02-05 00:47:02 +00:00
Buff_or_Debuff ( eCC . ATK_Down , LifeTime , val2 + pd . CC_AddVal1 ) ;
Buff_or_Debuff ( eCC . DEF_Down , LifeTime , val3 + pd . CC_AddVal1 ) ;
2025-05-09 04:27:04 +00:00
Set_Target ( _dinfo . Beater ) ;
2024-12-15 01:39:39 +00:00
break ;
case eSkillExtraType . VampirHeal :
2025-05-13 23:11:24 +00:00
LifeTime = 2f ; // 이펙트 시간
2024-12-15 01:39:39 +00:00
_dinfo . Beater . Heal ( val2 ) ;
_dinfo . Beater . Heal_MP ( val3 ) ;
break ;
case eSkillExtraType . Cure :
2025-02-12 04:39:37 +00:00
curCC = eCC . Cure ;
2024-12-15 01:39:39 +00:00
_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 ;
2025-02-11 23:22:40 +00:00
case eSkillExtraType . ManaBurn :
var manaburn = ( int ) ( Get_MaxMP ( ) * val1 ) ;
Heal_MP ( - manaburn ) ;
LifeTime = 1f ;
break ;
2025-02-12 00:04:43 +00:00
case eSkillExtraType . HPBurn :
var hpburn = ( Get_MaxHP ( ) * val1 ) ;
Heal ( - hpburn ) ;
LifeTime = 1f ;
break ;
2025-02-12 04:39:37 +00:00
case eSkillExtraType . SilenceCurse : // 스킬 사용 불가 및 최대 마나 감소
curCC = eCC . SilenceCurse ;
Buff_or_Debuff ( curCC , LifeTime , val2 ) ;
break ;
2025-02-13 06:10:00 +00:00
case eSkillExtraType . Blind :
GlobalVolumeMgr . Ins . Set ( 1 , LifeTime ) ;
break ;
2025-05-23 06:44:50 +00:00
case eSkillExtraType . KnockBack_byHitter :
{
2025-06-14 23:53:11 +00:00
Knockback_byHitter ( _dinfo . Beater . Get_position ( ) , val1 , 10.0f , ( ) = > { Stun ( val2 , true ) ; } ) ;
if ( IsMainPC ( ) ) TimeMgr . Ins . Set_TimeScale ( ) ;
2025-05-23 06:44:50 +00:00
}
2025-02-20 06:54:35 +00:00
break ;
2024-12-15 01:39:39 +00:00
}
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 ) ;
2025-02-12 04:39:37 +00:00
// 기존 이펙트가 있으면 재활용
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
2025-02-22 23:13:50 +00:00
{
if ( IsCC ( curCC ) & & m_Stat . Get_Stat ( eStat . CC_Immunity ) > 0 ) return ;
2025-02-12 04:39:37 +00:00
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 ;
}
} ) ;
2025-02-22 23:13:50 +00:00
}
2025-03-05 05:32:40 +00:00
Set_IngameBuffDebuffUI ( skillExtraData , LifeTime ) ;
}
void Set_IngameBuffDebuffUI ( SkillTypeConfigTableData skillExtraData , float time )
{
if ( MyInfoUI . isIns & & IsRole ( eRole . PC ) )
2025-03-05 07:08:07 +00:00
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 ) ;
2025-03-05 23:16:50 +00:00
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 ) ;
}
2024-12-15 01:39:39 +00:00
}
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 ) ;
}
}
2025-05-08 00:54:46 +00:00
public void Buff_or_Debuff ( eCC cc , double lifeTime , double val , int ival = 0 , float fval = 0f , string str1 = "" )
2024-12-15 01:39:39 +00:00
{
2025-01-26 18:16:16 +00:00
switch ( cc )
{
case eCC . ATKSPD_Down :
case eCC . MOVSPD_Down :
if ( IsSubRole ( eSubRol . Elite ) ) lifeTime - = lifeTime * 0.2f ;
2025-03-05 23:16:50 +00:00
else if ( IsSubRole ( eSubRol . Boss ) )
{
lifeTime - = lifeTime * 0.5f ;
MonsterSimpleInfo . Ins . m_IngameBuffDebuffBase . Add_IngameBuffDebuff ( "8_Slow" , ( float ) lifeTime ) ;
}
2025-01-26 18:16:16 +00:00
break ;
}
2024-12-15 01:39:39 +00:00
dic_ccData [ cc ] . TimeUpdate = true ;
dic_ccData [ cc ] . CCTime = lifeTime ;
dic_ccData [ cc ] . CCDmg = val ;
dic_ccData [ cc ] . CCVal = ival ;
dic_ccData [ cc ] . CCReduceTime = fval ;
}
public CCData Get_CCData ( eCC cc ) { return dic_ccData [ cc ] ; }
public bool IsSkillExtraStatus ( eSkillExtraType skillExtraType )
{ return dic_skillextra . ContainsKey ( skillExtraType ) & & dic_skillextra [ skillExtraType ] ; }
protected void Off_StatUpBuff ( eStat stat )
{
Get_StatInfo ( ) . Set_Buff ( MyValue . dic_StatUp_from_Stat [ stat ] , 0d ) ;
Get_StatInfo ( ) . Reset ( true ) ;
}
#endregion
#region 상 태 이 상 : 도 트 데 미 지 ( 출 혈 , 독 )
Dictionary < eCC , Coroutine > dic_ccCoroutine = new Dictionary < eCC , Coroutine > ( ) ;
public void DotDamage ( eCC cc , DamageInfo dinfocopy , double lifetime , double gaptime , double dmg )
{
if ( ! dic_ccCoroutine . ContainsKey ( cc ) ) dic_ccCoroutine . Add ( cc , null ) ;
dic_ccData [ cc ] . CCTime = lifetime ;
dic_ccData [ cc ] . CCDelay = gaptime ;
dic_ccData [ cc ] . CCDmg = dmg ;
if ( ! DSUtil . CheckNull ( dic_ccCoroutine [ cc ] ) ) StopCoroutine ( dic_ccCoroutine [ cc ] ) ;
dic_ccCoroutine [ cc ] = StartCoroutine ( Co_DotDamage ( cc , dinfocopy ) ) ;
}
IEnumerator Co_DotDamage ( eCC cc , DamageInfo dinfocopy )
{
var ccData = dic_ccData [ cc ] ;
var gapTime = ( float ) ccData . CCDelay ;
2025-02-19 04:14:09 +00:00
dinfocopy . Beater_pd . AddDmgPercent = ( float ) ccData . CCDmg ;
2024-12-15 01:39:39 +00:00
while ( ccData . CCTime > 0 )
{
2025-02-19 04:14:09 +00:00
var dinfo = dinfocopy . Beater . Get_DamageInfo ( dinfocopy . Beater_pd ) ;
2024-12-15 01:39:39 +00:00
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 ;
2025-06-08 20:45:18 +00:00
public void DotHeal ( double lifetime , float gaptime , double healhp , double healmp , string effect = "" )
2024-12-15 01:39:39 +00:00
{
if ( healhp < 1 ) healhp = 1 ;
2025-02-24 02:24:39 +00:00
//if (healmp < 1) healmp = 1;
2024-12-15 01:39:39 +00:00
if ( ! DSUtil . CheckNull ( co_dotheal ) ) StopCoroutine ( co_dotheal ) ;
2025-06-08 20:45:18 +00:00
co_dotheal = StartCoroutine ( Co_DotHeal ( lifetime , gaptime , healhp , healmp , effect ) ) ;
2024-12-15 01:39:39 +00:00
}
2025-06-08 20:45:18 +00:00
IEnumerator Co_DotHeal ( double lifetime , float gaptime , double healhp , double healmp , string effect = "" )
2024-12-15 01:39:39 +00:00
{
2025-06-08 20:45:18 +00:00
bool isEffect = ! string . IsNullOrEmpty ( effect ) ;
2024-12-15 01:39:39 +00:00
while ( lifetime > 0 )
{
yield return new WaitForSeconds ( gaptime ) ;
Heal ( healhp ) ;
Heal_MP ( healmp ) ;
2025-06-08 20:45:18 +00:00
if ( isEffect ) InGameInfo . Ins . Show_Effect ( effect , this , eEffectLocation . Bottom , 1f ) ;
2024-12-15 01:39:39 +00:00
lifetime - = gaptime ;
}
co_dotheal = null ;
}
#endregion
#region 상 태 이 상 : 프 리 즈
Action < Actor > act_AfterFreezing ;
public virtual void Freeze ( float _freezeTime , Action < Actor > act = null )
{
2025-02-22 23:13:50 +00:00
if ( m_Stat . Get_Stat ( eStat . CC_Immunity ) > 0 ) return ;
2024-12-15 01:39:39 +00:00
if ( DSUtil . CheckNull ( act_AfterFreezing ) ) act_AfterFreezing = act ;
dic_ccData [ eCC . Freeze ] . CCTime = 10f ;
2025-02-25 06:28:10 +00:00
if ( ! IsDead ( ) ) m_animation . speed = 0f ;
2024-12-15 01:39:39 +00:00
2025-01-14 06:51:57 +00:00
StopMove_Imm ( true ) ;
2024-12-15 01:39:39 +00:00
//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 ;
2025-01-26 18:16:16 +00:00
if ( IsSubRole ( eSubRol . Boss ) ) Cancel_Freeze ( _freezeTime * 0.5f ) ;
else if ( IsSubRole ( eSubRol . Elite ) ) Cancel_Freeze ( _freezeTime * 0.2f ) ;
2024-12-15 01:39:39 +00:00
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 ;
2025-05-12 22:20:47 +00:00
dic_CCEffect [ eCC . Freeze ] . Off_Imm ( ) ;
2024-12-15 01:39:39 +00:00
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 ;
2025-05-12 00:24:26 +00:00
public virtual void KnockBack ( double _time , float speed = 4f , Action after = null , bool ignorePreknockback = false )
2024-12-15 01:39:39 +00:00
{
2025-02-22 23:13:50 +00:00
if ( m_Stat . Get_Stat ( eStat . CC_Immunity ) > 0 ) return ;
2025-05-12 00:24:26 +00:00
if ( IsRole ( eRole . OBJ ) | | IsSubRole ( eSubRol . Boss ) | | DSUtil . CheckNull ( m_NavMeshAgent ) | | IsCC ( ignorePreknockback ) ) return ;
StartCoroutine ( Co_KnockBack ( _time , speed , after ) ) ;
2024-12-15 01:39:39 +00:00
}
2025-02-20 06:54:35 +00:00
IEnumerator Co_KnockBack ( double _time , float speed , Action after )
2024-12-15 01:39:39 +00:00
{
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 ;
2025-05-23 06:44:50 +00:00
StopMove_Imm ( false ) ;
2024-12-15 01:39:39 +00:00
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 ;
2025-02-20 06:54:35 +00:00
after ? . Invoke ( ) ;
2024-12-15 01:39:39 +00:00
if ( StunAfterKnockBackTime > 0 )
{
Stun ( StunAfterKnockBackTime , true ) ;
StunAfterKnockBackTime = 0f ;
}
2025-05-23 06:44:50 +00:00
else
On_NavMeshAgent ( ) ;
2024-12-28 00:20:09 +00:00
#if UNITY_EDITOR
if ( SceneManager . GetActiveScene ( ) . name = = "StatTest" )
{
transform . localPosition = Vector3 . zero ;
}
#endif
2024-12-15 01:39:39 +00:00
}
public void Stun_AfterKnockBack ( float time ) { StunAfterKnockBackTime = time ; }
#endregion
2025-05-23 06:44:50 +00:00
#region 상 태 이 상 : 상 대 기 준 으 로 밀 리 기
2025-06-14 23:53:11 +00:00
public virtual void Knockback_byHitter ( Vector3 beatorpos , double _time , float speed = 10f , Action after = null , bool ignorePreknockback = false )
2025-05-23 06:44:50 +00:00
{
if ( m_Stat . Get_Stat ( eStat . CC_Immunity ) > 0 ) return ;
if ( IsRole ( eRole . OBJ ) | | IsSubRole ( eSubRol . Boss ) | | DSUtil . CheckNull ( m_NavMeshAgent ) ) return ;
2025-06-14 23:53:11 +00:00
AnimationPlay ( MyValue . Get_AnimName ( eAnim . Hit ) ) ;
transform . LookAt ( beatorpos ) ;
2025-05-23 06:44:50 +00:00
StopCoroutine ( nameof ( Co_Knockback_byHitter ) ) ; // 중복 방지
2025-06-14 23:53:11 +00:00
StartCoroutine ( Co_Knockback_byHitter ( ( Get_position ( ) - beatorpos ) . normalized , _time , speed , after ) ) ;
2025-05-23 06:44:50 +00:00
}
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
2024-12-15 01:39:39 +00:00
#region 상 태 이 상 : 특 정 위 치 로 끌 어 당 기 기
public virtual void Attract ( Vector3 _pos )
{
2025-02-22 23:13:50 +00:00
if ( m_Stat . Get_Stat ( eStat . CC_Immunity ) > 0 ) return ;
2024-12-15 01:39:39 +00:00
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 )
{
2026-09-06 23:24:25 +00:00
// #764 — Set_Warp 를 거치면 NavMesh 밖으로 끌려가는 것을 막는다(반경 0 인 액터는 기존과 동일).
Set_Warp ( Vector3 . MoveTowards ( transform . position , _pos , Time . deltaTime * 2f ) ) ;
2024-12-15 01:39:39 +00:00
yield return null ;
t - = Time . deltaTime ;
}
dic_ccData [ eCC . Attract ] . CCTime = 0f ;
On_NavMeshAgent ( ) ;
}
#endregion
#region 상 태 이 상 : 공 중 으 로 에 어 본
public virtual void KnockedAirborne ( double _airborneTime = 1d )
{
2025-02-22 23:13:50 +00:00
if ( m_Stat . Get_Stat ( eStat . CC_Immunity ) > 0 | | dic_ccData [ eCC . KnockedAirborne ] . CCTime > 0 ) return ;
2024-12-15 01:39:39 +00:00
StartCoroutine ( Co_KnockedAirborne ( ( float ) _airborneTime ) ) ;
}
IEnumerator Co_KnockedAirborne ( float _airborneTime )
{
2025-01-26 18:16:16 +00:00
if ( IsSubRole ( eSubRol . Elite ) ) _airborneTime - = _airborneTime * 0.2f ;
else if ( IsSubRole ( eSubRol . Boss ) ) _airborneTime - = _airborneTime * 0.5f ;
2024-12-15 01:39:39 +00:00
dic_ccData [ eCC . KnockedAirborne ] . CCTime = _airborneTime ;
2025-01-14 06:51:57 +00:00
StopMove_Imm ( true ) ;
2024-12-15 01:39:39 +00:00
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 ;
2026-09-06 23:24:25 +00:00
Snap_toNavMesh ( ) ; // #764
2024-12-15 01:39:39 +00:00
On_NavMeshAgent ( ) ;
}
#endregion
#region 상 태 이 상 : 스 턴 ( 기 절 )
2025-02-11 00:15:02 +00:00
Coroutine m_CoStun ;
public void Stun ( double time , bool showEffect = false )
{
2025-05-22 20:37:51 +00:00
if ( IsDead ( ) | | m_Stat . Get_Stat ( eStat . CC_Immunity ) > 0 ) return ;
2025-02-11 00:15:02 +00:00
if ( ! DSUtil . CheckNull ( m_CoStun ) ) { StopCoroutine ( m_CoStun ) ; m_CoStun = null ; }
m_CoStun = StartCoroutine ( Co_Stun ( time , showEffect ) ) ;
}
2024-12-15 01:39:39 +00:00
IEnumerator Co_Stun ( double _time , bool showEffect = false )
{
2025-02-11 00:15:02 +00:00
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 ) ;
2025-01-26 18:16:16 +00:00
2024-12-15 01:39:39 +00:00
m_animation . speed = 1f ;
2025-02-11 00:15:02 +00:00
AnimationPlay ( "stun" ) ;
2024-12-15 01:39:39 +00:00
var cc = eCC . Stun ;
dic_ccData [ cc ] . CCTime = _time ;
2025-01-14 06:51:57 +00:00
StopMove_Imm ( true ) ;
2025-02-11 00:15:02 +00:00
yield return new WaitForSeconds ( stunTime ) ;
2024-12-15 01:39:39 +00:00
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 ;
2025-02-13 22:49:30 +00:00
if ( dic_ccValue . ContainsKey ( eCC . MOVSPD_Up ) )
speed + = speed * dic_ccValue [ eCC . MOVSPD_Up ] ;
2024-12-15 01:39:39 +00:00
if ( dic_ccData [ eCC . MOVSPD_Down ] . CCTime > 0 )
speed - = speed * ( float ) dic_ccData [ eCC . MOVSPD_Down ] . CCDmg ;
2026-10-18 15:09:05 +00:00
return speed * WL . Combat . Weapon . WLBladeStorm . MoveMul ( this ) ; // WL #924 대검 각성 칼날 폭풍 중 이동 속도 배율(Resources/WL/WLBladeStorm.json moveMul · 그 PC 만)
2024-12-15 01:39:39 +00:00
}
2025-01-16 06:07:21 +00:00
#endregion
#region 점 프
2025-02-20 00:40:21 +00:00
public virtual void Jump ( float jumpTime , float height = 1f , float jumpspeed1 = 5f , float jumpspeed2 = 2f , Action after = null )
2025-01-16 06:07:21 +00:00
{
2025-02-20 00:40:21 +00:00
StartCoroutine ( Co_Jump ( jumpTime , height , jumpspeed1 , jumpspeed2 , after ) ) ;
2025-01-16 06:07:21 +00:00
}
2025-02-20 00:40:21 +00:00
IEnumerator Co_Jump ( float jumpTime , float height = 1f , float jumpspeed1 = 5f , float jumpspeed2 = 2f , Action after = null )
2025-01-16 06:07:21 +00:00
{
StopMove_Imm ( false ) ;
m_NavMeshAgent . enabled = false ;
Vector3 v3_ori = new Vector3 ( transform . position . x , transform . position . y , transform . position . z ) ;
2025-02-20 00:40:21 +00:00
Vector3 v3_target = new Vector3 ( transform . position . x , transform . position . y + height , transform . position . z ) ;
float t = 0f , speed = jumpspeed1 ;
2025-01-16 06:07:21 +00:00
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 ;
2025-02-20 00:40:21 +00:00
t + = Time . deltaTime * speed * jumpspeed2 ;
2025-01-16 06:07:21 +00:00
transform . position = Vector3 . Lerp ( v3_target , v3_ori , t ) ;
}
m_NavMeshAgent . enabled = true ;
2026-09-06 23:24:25 +00:00
Snap_toNavMesh ( ) ; // #764
2025-01-16 06:07:21 +00:00
On_NavMeshAgent ( ) ;
after ? . Invoke ( ) ;
}
2024-12-15 01:39:39 +00:00
#endregion
2025-06-04 20:10:06 +00:00
#region 위 치 로 점 프 이 동
2025-06-07 23:44:27 +00:00
public void JumpToPosition ( Vector3 targetPos , float duration , float height = 1f , bool onNavMesh = false , Action after = null )
2025-06-04 20:10:06 +00:00
{
2025-06-07 23:44:27 +00:00
StartCoroutine ( Co_JumpToPosition ( targetPos , duration , height , onNavMesh , after ) ) ;
2025-06-04 20:10:06 +00:00
}
2025-06-07 23:44:27 +00:00
IEnumerator Co_JumpToPosition ( Vector3 targetPos , float duration , float height , bool onNavMesh = false , Action after = null )
2025-06-04 20:10:06 +00:00
{
StopMove_Imm ( false ) ;
m_NavMeshAgent . enabled = false ;
Vector3 start = transform . position ;
float time = 0f ;
while ( time < duration )
{
time + = Time . deltaTime ;
float t = Mathf . Clamp01 ( time / duration ) ;
// 수평 이동은 직선 보간
Vector3 horizontal = Vector3 . Lerp ( start , targetPos , t ) ;
// 수직 높이는 포물선 형태로 처리
float yOffset = Mathf . Sin ( Mathf . PI * t ) * height ;
transform . position = new Vector3 ( horizontal . x , horizontal . y + yOffset , horizontal . z ) ;
yield return null ;
}
transform . position = targetPos ;
m_NavMeshAgent . enabled = true ;
2026-09-06 23:24:25 +00:00
Snap_toNavMesh ( ) ; // #764 — 착지 지점이 NavMesh 밖이면 되돌린다(반경 0 인 액터는 무동작)
2025-06-07 23:44:27 +00:00
if ( onNavMesh ) On_NavMeshAgent ( ) ;
2025-06-04 20:10:06 +00:00
after ? . Invoke ( ) ;
}
#endregion
2024-12-15 01:39:39 +00:00
#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 ( ) ;
}
2025-05-01 21:41:01 +00:00
public virtual void Play_Attack ( int _attack , float _as , Action actsuccess = null )
2024-12-15 01:39:39 +00:00
{
2025-05-01 22:41:51 +00:00
if ( IsRole ( eRole . Pet ) & & CurAnim = = eAnim . Attack ) return ;
if ( IsRole ( eRole . PC ) & & CurAnim = = eAnim . Attack ) return ;
if ( IsDead ( ) | | CurAnim > = eAnim . Skill ) return ;
2024-12-15 01:39:39 +00:00
2025-01-14 06:51:57 +00:00
StopMove_Imm ( false ) ;
2024-12-15 01:39:39 +00:00
CurAnim = eAnim . Attack + _attack ;
var aniname = MyValue . Get_AnimName ( eAnim . Attack + _attack ) ;
m_animation . speed = _as < = 0f ? 1f : _as ;
2025-02-26 00:19:45 +00:00
if ( dic_ccData . ContainsKey ( eCC . ATKSPD_Up ) & & dic_ccData [ eCC . ATKSPD_Up ] . CCTime > 0f )
2024-12-15 01:39:39 +00:00
m_animation . speed + = m_animation . speed * ( float ) dic_ccData [ eCC . ATKSPD_Up ] . CCDmg ;
2025-02-13 22:49:30 +00:00
if ( dic_ccValue . ContainsKey ( eCC . ATKSPD_Up ) )
m_animation . speed + = m_animation . speed * dic_ccValue [ eCC . ATKSPD_Up ] ;
2025-02-26 00:19:45 +00:00
if ( dic_ccData . ContainsKey ( eCC . ATKSPD_Down ) & & dic_ccData [ eCC . ATKSPD_Down ] . CCTime > 0f )
2024-12-15 01:39:39 +00:00
m_animation . speed - = m_animation . speed * ( float ) dic_ccData [ eCC . ATKSPD_Down ] . CCDmg ;
AnimationPlay ( aniname ) ;
2025-05-01 21:41:01 +00:00
actsuccess ? . Invoke ( ) ;
2026-09-08 14:30:57 +00:00
WL . Combat . Core . CombatEvents . RaiseAttackStarted ( this , _attack , m_animation . speed , m_Target ) ;
2024-12-15 01:39:39 +00:00
2025-02-08 22:22:31 +00:00
if ( isTarget ) transform . LookAt ( m_Target . transform ) ;
2024-12-15 01:39:39 +00:00
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 ;
2026-09-08 14:30:57 +00:00
WL . Combat . Core . CombatEvents . RaiseSkillCast ( this , m_Magic ) ;
2026-10-18 15:09:05 +00:00
AnimationPlay ( IsRole ( eRole . Pet ) ? "casting" : WL . Combat . Weapon . WLSkillApproach . Motion ( this , motion ) ) ; // WL #905 훅 — 사정거리 안 = 바로 시전 동작 · 밖 = 대쉬 후 시전(표 WLSkillApproach)
2024-12-15 01:39:39 +00:00
2025-02-08 22:22:31 +00:00
if ( isTarget ) transform . LookAt ( m_Target . transform ) ;
2024-12-15 01:39:39 +00:00
2025-01-14 06:51:57 +00:00
StopMove_Imm ( false ) ;
2024-12-15 01:39:39 +00:00
2024-12-15 23:37:37 +00:00
if ( ! DSUtil . CheckNull ( m_Magic ) )
{
2025-03-01 03:21:02 +00:00
if ( IsRole ( eRole . Pet ) | | ! string . IsNullOrEmpty ( m_Magic . m_SkillTableData . s_NextAnimation ) )
2024-12-24 07:38:07 +00:00
{
co_casting = StartCoroutine ( Co_Casting ( m_Magic . m_SkillTableData . f_CastingTime ) ) ;
2024-12-28 00:20:09 +00:00
if ( m_NavMeshAgent . isOnNavMesh ) m_NavMeshAgent . ResetPath ( ) ;
2024-12-24 07:38:07 +00:00
}
2025-01-06 23:06:13 +00:00
switch ( m_Magic . m_SkillTableData . e_Skill )
{
2025-05-02 03:38:11 +00:00
case eEffect . Skill_JumpAttack : StartCoroutine ( Co_JumpAttack ( 1f , 0.8f , 1f ) ) ; break ;
2026-10-18 15:09:05 +00:00
case eEffect . Skill_FatalBlow : if ( ! WL . Combat . Weapon . WLGreatWave . NoJump ( this ) ) StartCoroutine ( Co_JumpAttack ( 0.8f , 0.6f , 0.5f ) ) ; break ; // WL #925 대검 주력 = 제자리 4타 내려치기(점프 없음 · WLGreatWave.json noJump)
2025-01-06 23:06:13 +00:00
}
2024-12-24 07:38:07 +00:00
2024-12-15 23:37:37 +00:00
if ( ! string . IsNullOrEmpty ( m_Magic . m_SkillTableData . s_CastingEffect ) )
InGameInfo . Ins . Show_Effect ( m_Magic . m_SkillTableData . s_CastingEffect , Get_position ( ) ) ;
}
2024-12-15 01:39:39 +00:00
return true ;
}
IEnumerator Co_Casting ( float castTime )
{
yield return new WaitForSeconds ( castTime ) ;
2024-12-24 07:38:07 +00:00
if ( ! DSUtil . CheckNull ( m_Magic ) )
2025-03-01 03:21:02 +00:00
AnimationPlay ( IsRole ( eRole . Pet ) ? "casting_end" : m_Magic . m_SkillTableData . s_NextAnimation ) ;
2024-12-15 01:39:39 +00:00
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 ;
2025-01-03 00:50:36 +00:00
m_animation . speed = 1f ;
2024-12-15 01:39:39 +00:00
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);
2025-05-21 03:40:41 +00:00
InGameInfo . Ins . Show_Effect ( "Effect_PCDash" , this , eEffectLocation . Bottom , 2f , effect = >
{
effect . transform . eulerAngles = transform . eulerAngles ;
2026-10-18 15:09:05 +00:00
WL . Combat . WLDoubleTapDodge . AdjustDust ( effect . gameObject ) ; // WL #892 구르기 흙먼지 크기(캐릭터 크기 기준 · 값 WLDoubleTapDodge.json dustScale)
2025-05-21 03:40:41 +00:00
} ) ;
2024-12-15 01:39:39 +00:00
}
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-08 22:22:31 +00:00
{ // 2025. 02. 09. 정인호 : GC Alloc을 2.3KB 발생으로 인해 사용 금지
//if (!ProjectileInfo.Ins || IsInvoking(nameof(Off_HitEffect_Color))) return;
2024-12-15 01:39:39 +00:00
2025-02-08 22:22:31 +00:00
//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();
//}
2024-12-15 01:39:39 +00:00
2025-02-08 22:22:31 +00:00
//Invoke(nameof(Off_HitEffect_Color), 0.1f);
2024-12-15 01:39:39 +00:00
}
void Off_HitEffect_Color ( )
2025-02-08 22:22:31 +00:00
{ // 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();
//}
2024-12-15 01:39:39 +00:00
}
#endregion
2025-01-13 02:40:17 +00:00
#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
2025-01-17 00:28:44 +00:00
#region 캐 릭 터 이 동
/// <summary>
/// TelepoetPosion으로 이동
/// </summary>
2025-01-17 03:48:51 +00:00
public virtual void MoveToTarget ( Actor target , float stopdist , float speed , Action _movecomplete = null , float dist = 1f )
2025-01-17 00:28:44 +00:00
{
2025-01-17 03:48:51 +00:00
StartCoroutine ( Co_MoveToTarget ( target , stopdist , speed , _movecomplete , dist ) ) ;
2025-01-17 00:28:44 +00:00
}
2025-01-17 03:48:51 +00:00
IEnumerator Co_MoveToTarget ( Actor target , float stopdist , float speed , Action _movecomplete , float dist )
2025-01-17 00:28:44 +00:00
{
m_NavMeshAgent . stoppingDistance = stopdist ;
Set_Path ( Get_TeleportPostion ( target , dist ) ) ;
2025-01-17 03:48:51 +00:00
while ( ! Complete_Destination ( ) )
{
m_NavMeshAgent . speed = speed ;
yield return null ;
}
2025-01-17 00:28:44 +00:00
_movecomplete ? . Invoke ( ) ;
m_NavMeshAgent . stoppingDistance = m_NavStopDistance ;
2025-01-17 03:48:51 +00:00
m_NavMeshAgent . speed = Get_MoveSpeed ( ) ;
2025-01-17 00:28:44 +00:00
}
public virtual void Set_JoystickStatus ( int status ) { }
public void Set_Warp ( Vector3 _pos )
{
2025-01-26 22:48:47 +00:00
if ( IsDead ( ) ) return ;
2025-01-17 00:28:44 +00:00
if ( m_BoxCollider ) m_BoxCollider . enabled = true ;
m_NavMeshAgent . enabled = true ; // 사망 시 off 됨. 다시 세팅될 때 켜줌
2026-09-06 23:24:25 +00:00
// #764 — 넉백·끌어당김·점프처럼 NavMeshAgent 를 끄고 트랜스폼을 직접 옮기는 경로가 지형을 뚫는 것을 막는다.
// 반경 0 이면 아무것도 하지 않는다 = 기존 동작(기본값). 몬스터만 MobActor 에서 값을 켠다.
float snap = Get_NavSnapRadius ( ) ;
if ( snap > 0f )
{
NavMeshHit navhit ;
if ( NavMesh . SamplePosition ( _pos , out navhit , snap , NavMesh . AllAreas ) ) _pos = navhit . position ;
}
2025-01-17 00:28:44 +00:00
m_NavMeshAgent . Warp ( _pos ) ;
}
2026-09-06 23:24:25 +00:00
/// <summary>
/// #764 — 이 액터를 NavMesh 위로 되돌릴 최대 거리(m). 0 = 되돌리지 않음(기존 동작).
/// 기본이 0 이라 PC·펫·NPC 는 지금과 똑같이 동작하고, MobActor 만 맵 데이터 값으로 덮어쓴다.
/// </summary>
protected virtual float Get_NavSnapRadius ( ) { return 0f ; }
/// <summary>#764 — 현재 위치가 NavMesh 밖이면 가장 가까운 NavMesh 위로 되돌린다. 반경 0 이면 무동작.</summary>
protected void Snap_toNavMesh ( )
{
float snap = Get_NavSnapRadius ( ) ;
if ( snap < = 0f | | DSUtil . CheckNull ( m_NavMeshAgent ) | | ! m_NavMeshAgent . enabled ) return ;
NavMeshHit navhit ;
if ( NavMesh . SamplePosition ( transform . position , out navhit , snap , NavMesh . AllAreas ) )
m_NavMeshAgent . Warp ( navhit . position ) ;
}
2025-01-17 00:28:44 +00:00
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 ;
2025-02-02 23:29:11 +00:00
//Debug.Log($"Path Partial. Attempt: {FindCount}");
2025-01-17 00:28:44 +00:00
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
2024-12-15 01:39:39 +00:00
2025-02-11 00:15:02 +00:00
public bool CanMove ( ) { return m_NavMeshAgent . isOnNavMesh & & ! m_NavMeshAgent . isStopped & & ! IsCC ( ) ; }
2024-12-15 01:39:39 +00:00
protected virtual bool IsAvoidCoolTime ( ) { return false ; }
protected virtual void Set_AvoidCoolTime ( ) { }
public virtual MonsterAppearData Get_MonsterStatTableData ( ) { return null ; }
2025-02-16 00:14:00 +00:00
public virtual void Change_MobStatus ( eMobStatus mobStatus ) { }
protected virtual void Check_Battle ( ) { }
2025-02-08 22:22:31 +00:00
public virtual bool isNoseeMob ( ) { return false ; }
2025-02-13 04:40:04 +00:00
public virtual void On_Regen ( bool reincarnation = false , float healrate = 1f )
2024-12-15 01:39:39 +00:00
{
gameObject . SetActive ( true ) ;
DeadStatus = false ;
m_Stat . Clear_BuffDebuff ( ) ;
m_Stat . Set_Stat ( eStat . HP , 0 ) ;
2025-02-13 04:40:04 +00:00
Heal ( healrate , false ) ;
2024-12-15 01:39:39 +00:00
Init_CC ( ) ;
2025-01-03 00:50:36 +00:00
m_animation . speed = 1f ;
2024-12-15 01:39:39 +00:00
}
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 ;
2025-03-17 00:08:46 +00:00
//NewGameUI.Ins.m_GuideQuestUI.Add_GuideQuest(ePurpose.AutoMagic);
2024-12-15 01:39:39 +00:00
}
else
{
Del_Magic ( ) ;
2025-02-08 22:22:31 +00:00
if ( isTarget )
2024-12-15 01:39:39 +00:00
{
if ( IsRole ( eRole . PC ) )
{
m_Target . Set_Outline ( false ) ;
m_NavMeshAgent . ResetPath ( ) ;
Play_Idle ( ) ;
}
}
Del_Target ( ) ;
}
}
public Actor Get_MyPet ( ) { return m_MyPet ; }
2025-05-15 07:33:01 +00:00
public virtual Actor Get_Owner ( ) { return null ; }
2024-12-15 01:39:39 +00:00
public void Set_MyPet ( Actor actor ) { m_MyPet = actor ; }
2025-05-05 06:29:10 +00:00
public void Set_AfterImage ( bool active )
{
if ( ! DSUtil . CheckNull ( m_AfterImage ) )
m_AfterImage . enabled = active ;
}
2024-12-15 01:39:39 +00:00
#region 애 니 메 이 션 이 벤 트
public virtual void Event_Idle ( int a )
{
CurAnim = eAnim . Idle ;
2025-07-13 20:48:26 +00:00
m_animation . speed = 1f ;
2024-12-15 01:39:39 +00:00
#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 ;
}
2025-06-24 03:25:01 +00:00
public virtual void Projectile ( AnimationEvent s )
2024-12-15 01:39:39 +00:00
{
#if UNITY_EDITOR
2025-06-24 03:25:01 +00:00
if ( SettingsMain . Ins ) SettingsMain . Ins . Add_Event ( System . Reflection . MethodBase . GetCurrentMethod ( ) . Name , 3 ,
s . intParameter , s . floatParameter , s . stringParameter ) ;
2024-12-15 01:39:39 +00:00
#endif
}
2025-06-24 03:25:01 +00:00
protected void Shoot_Projectile ( string proj , float adddmg , float lifetime , Vector3 startpos )
2025-02-24 00:17:44 +00:00
{
2026-09-08 14:30:57 +00:00
WL . Combat . Core . CombatEvents . RaiseHitboxSpawned ( this , proj , adddmg , lifetime , startpos ) ;
2025-03-04 23:42:24 +00:00
if ( ProjectileInfo . isIns )
2025-02-24 00:17:44 +00:00
{
2025-06-24 03:25:01 +00:00
if ( adddmg < = 0f ) adddmg = 1f ;
2025-02-24 00:17:44 +00:00
var ccData = Get_CCData ( eCC . MultiShot ) ;
var amount = 1 + ( ccData . CCTime > 0f ? ccData . CCVal : 0 ) ;
if ( amount > 1 )
{
2025-05-08 00:54:46 +00:00
float cal_angle = amount * 5f ;
2025-02-24 03:13:15 +00:00
float angleStep = cal_angle / ( amount - 1 ) ; // 전체 각도를 화살 개수로 나누기
2025-02-24 00:17:44 +00:00
for ( int i = 0 ; i < amount ; + + i )
{
2025-06-24 03:25:01 +00:00
ProjectileInfo . Ins . Shoot_Projectile ( proj , this , m_Target , adddmg , startpos , lifetime ,
2025-02-24 00:17:44 +00:00
pb = >
{
2025-02-24 03:13:15 +00:00
float angle = - ( cal_angle * 0.5f ) + ( angleStep * i ) ;
2025-02-24 00:17:44 +00:00
pb . transform . localEulerAngles = new Vector2 ( 0f , pb . transform . localEulerAngles . y + angle ) ;
} ) ;
}
}
else
{
2026-10-18 15:09:05 +00:00
ProjectileInfo . Ins . Shoot_Projectile ( proj , this , m_Target , adddmg , startpos , lifetime , pb = > { WL . Combat . Weapon . WLHitboxFit . Apply ( this , pb ) ; action_pb ? . Invoke ( pb ) ; } ) ; // WL #904 훅 — 판정 상자 = 궤적 길이(직업 · 타수 표)
2025-02-24 00:17:44 +00:00
}
}
}
2024-12-15 01:39:39 +00:00
public virtual void SkillEvent ( int a )
{
#if UNITY_EDITOR
if ( SettingsMain . Ins ) SettingsMain . Ins . Add_Event ( System . Reflection . MethodBase . GetCurrentMethod ( ) . Name , 1 , a ) ;
#endif
2026-09-08 14:30:57 +00:00
WL . Combat . Core . CombatEvents . RaiseSkillFired ( this , a , m_Magic ) ;
2024-12-15 01:39:39 +00:00
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
}
2025-01-15 22:32:07 +00:00
public virtual void ShowEffect ( AnimationEvent s )
2024-12-15 01:39:39 +00:00
{
#if UNITY_EDITOR
2025-01-15 22:32:07 +00:00
if ( SettingsMain . Ins ) SettingsMain . Ins . Add_Event ( System . Reflection . MethodBase . GetCurrentMethod ( ) . Name , 3 ,
s . intParameter , s . floatParameter , s . stringParameter ) ;
2024-12-15 01:39:39 +00:00
#endif
2026-09-06 23:24:25 +00:00
// PD #747 — 근접 PC 는 무기 궤적 트레일로 대체한다.
// 처리하지 못하면(몬스터·원거리·설정 없음·무기 없음) false 를 돌려 아래 기존 경로를 그대로 탄다.
if ( WL . Combat . WeaponTrailDriver . TryHandleShowEffect ( this , s ) ) return ;
2025-01-15 22:32:07 +00:00
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 ;
}
2024-12-15 01:39:39 +00:00
}
2025-08-05 03:50:59 +00:00
public virtual void LookAtTarget ( ) { if ( isTarget ) transform . LookAt ( m_Target . Get_position ( ) ) ; }
2024-12-15 01:39:39 +00:00
public virtual void Init_CurAnim ( bool resetpos )
{
CurAnim = eAnim . Idle ;
if ( resetpos )
Set_Warp ( Vector3 . zero ) ;
}
public eAnim Get_CurAnim ( ) { return CurAnim ; }
2025-01-16 06:07:21 +00:00
public void Set_CurAnim ( eAnim anim ) { CurAnim = anim ; }
2024-12-15 01:39:39 +00:00
public virtual bool Is_Immortal ( ) { return m_bImmortal ; }
#endregion
}