2024-12-15 01:39:39 +00:00
using UnityEngine ;
public class MyActor : Actor
{
protected float f_FindEnemyRange ;
protected Actor OwnerActor ;
2025-05-06 22:41:40 +00:00
protected bool isFollowOwner ;
2024-12-15 01:39:39 +00:00
2026-09-06 23:24:25 +00:00
#region PD 지 시 # 770 — 플 레 이 어 타 겟 팅 규 칙 ( 수 치 · 스 위 치 SOT = WLTargetingSettings 에 셋 · C45 )
// 1. 공격 시작 시점에 자신과 가장 가까운 대상(바라보는 방향 우선)을 타겟팅
// 2. 콤보 중 적이 사망하면 그 시점에 가장 가까운 적으로 재타겟
// 3. 타겟을 바꿔도 이동부터 하지 않고 사거리 안이면 즉시 공격
// 4. 피격 어그로 금지 (자신을 때린 대상 우선 공격 금지)
// 에셋 = Assets/WL/Settings/Resources/WL/WLTargetingSettings.asset
/// <summary>#770 규칙 적용 대상인지. PC 만 적용하고 펫·소환수는 기존 동작(거리만·피격 어그로)을 유지한다.</summary>
protected bool UseWLTargeting ( ) { return IsRole ( eRole . PC ) ; }
/// <summary>재타겟 탐색 거리. 설정이 0 이면 기존 f_FindEnemyRange(table_GlobalValue + AttackRange_Up)를 쓴다.</summary>
float Get_WLFindRange ( )
{
if ( ! UseWLTargeting ( ) ) return f_FindEnemyRange ;
var r = WL . Settings . WLTargetingSettings . RetargetSearchRange ;
return r > 0f ? r : f_FindEnemyRange ;
}
/// <summary>수평면 기준, 캐릭터 정면과 대상 방향 사이의 각도(도).</summary>
float Get_FacingAngle ( Vector3 targetPos )
{
var dir = targetPos - Get_position ( ) ; dir . y = 0f ;
if ( dir . sqrMagnitude < 0.0001f ) return 0f ;
var fwd = transform . forward ; fwd . y = 0f ;
if ( fwd . sqrMagnitude < 0.0001f ) return 0f ;
return Vector3 . Angle ( fwd , dir ) ;
}
/// <summary>
/// 지금 이 자리에서 대상을 때릴 수 있는 거리(m).
/// Update() 의 정지 거리 계산(m_NavStopDistance + AttackRange_Up · 상대 반경 보정 · 상대 크기 보정)과
/// 같은 식이며 설정의 여유(immediateAttackMargin · #770-3)를 더한다.
/// </summary>
float Get_AttackReach ( Actor target )
{
float stop = m_NavStopDistance + ( float ) m_Stat . Get_Stat ( eStat . AttackRange_Up ) ;
var agent = target . Get_Agent ( ) ;
if ( ! DSUtil . CheckNull ( agent ) & & agent . radius > stop ) stop = agent . radius + 0.5f ;
return stop + target . Get_ScaleSizeDist ( ) + WL . Settings . WLTargetingSettings . ImmediateAttackMargin ;
}
/// <summary>
/// #770-1 정면 우선 + 최근접 선택.
/// 점수 = 거리 x (1 + facingWeight x (1 - cosθ)) 가 가장 낮은 후보를 고른다.
/// 정면 원뿔(frontConeDeg) 안에 후보가 하나라도 있으면 그 안에서만 고르고, 없으면 전체에서 고른다.
/// inReachOnly = true 면 "지금 이동 없이 때릴 수 있는" 후보만 본다(#770-1 공격 시작 시점 · #770-3).
/// </summary>
Actor Find_Target_Facing ( float searchRange , bool inReachOnly )
{
if ( DSUtil . CheckNull ( ActorInfo . Ins ) ) return null ;
var actors = ActorInfo . Ins . Get_EnemyActors ( IsEnemy ( ) , Get_position ( ) , searchRange ) ;
Actor best = null ;
float bestScore = float . MaxValue ;
float halfCone = WL . Settings . WLTargetingSettings . FrontConeDeg * 0.5f ;
float weight = WL . Settings . WLTargetingSettings . FacingWeight ;
bool useCone = halfCone > 0f & & halfCone < 180f ;
for ( int pass = 0 ; pass < 2 ; pass + + )
{ // pass 0 = 정면 원뿔 안에서만 · pass 1 = 전체(원뿔 안에 후보가 없을 때만 돈다)
for ( int i = 0 ; i < actors . Count ; i + + )
{
var a = actors [ i ] ;
if ( a = = null ) continue ;
float dist = Vector3 . Distance ( Get_position ( ) , a . Get_position ( ) ) ;
if ( inReachOnly & & dist > Get_AttackReach ( a ) ) continue ;
float angle = Get_FacingAngle ( a . Get_position ( ) ) ;
if ( pass = = 0 & & useCone & & angle > halfCone ) continue ;
float score = dist * ( 1f + weight * ( 1f - Mathf . Cos ( angle * Mathf . Deg2Rad ) ) ) ;
if ( score < bestScore ) { bestScore = score ; best = a ; }
}
if ( best ! = null ) break ; // 원뿔 안에서 찾았으면 끝
if ( ! useCone ) break ; // 원뿔 제한이 없으면 2회전은 같은 결과다
}
ActorInfo . Ins . Release_Actors ( actors ) ;
return best ;
}
/// <summary>수평 회전만으로 대상을 바라본다(콤보를 끊지 않고 방향만 돌릴 때 · #770-2).</summary>
void LookAt_Horizontal ( Vector3 pos )
{
var dir = pos - Get_position ( ) ; dir . y = 0f ;
if ( dir . sqrMagnitude < 0.0001f ) return ;
transform . rotation = Quaternion . LookRotation ( dir ) ;
}
/// <summary>
/// #770-1 "공격 시작 시점" 재타겟. 이동 없이 때릴 수 있는 거리 안에서만 다시 고르며,
/// 그 안에 아무도 없으면 기존 타겟을 그대로 둔다(달려가던 대상을 잃지 않게).
/// </summary>
void Retarget_InReach ( )
{
if ( isTarget & & m_Target . m_IsLockTarget & & ! m_Target . IsDead ( ) ) return ; // 락 타겟 유지
var found = Find_Target_Facing ( Get_WLFindRange ( ) , true ) ;
if ( found = = null | | found = = m_Target ) return ;
if ( isTarget & & IsMainPC ( ) ) m_Target . Set_Outline ( false ) ;
m_Target = found ;
if ( IsMainPC ( ) ) m_Target . Set_Outline ( true ) ;
}
#endregion
2025-01-24 05:28:12 +00:00
public override void Set_Target ( bool forcefind = false )
2024-12-15 01:39:39 +00:00
{
2025-02-08 22:22:31 +00:00
if ( isTarget )
2024-12-15 01:39:39 +00:00
{
if ( m_Target . m_IsLockTarget & & ! m_Target . IsDead ( ) ) return ;
2025-05-06 07:05:38 +00:00
if ( IsMainPC ( ) ) m_Target . Set_Outline ( false ) ;
2024-12-15 01:39:39 +00:00
}
2026-09-06 23:24:25 +00:00
// #770-1 : PC 는 "정면 우선 + 가장 가까운" 대상을 고른다. 펫·소환수는 기존(거리만) 그대로.
float range = forcefind ? float . MaxValue : Get_WLFindRange ( ) ;
m_Target = UseWLTargeting ( )
? Find_Target_Facing ( range , false )
: ActorInfo . Ins . Get_Nearest_EnemyActor_orNull ( IsEnemy ( ) , Get_position ( ) , forcefind ? float . MaxValue : f_FindEnemyRange ) ;
2024-12-15 01:39:39 +00:00
2025-02-08 22:22:31 +00:00
if ( isTarget )
2024-12-15 01:39:39 +00:00
{
2025-05-06 07:05:38 +00:00
if ( IsMainPC ( ) )
2024-12-15 01:39:39 +00:00
{
m_Target . Set_Outline ( true ) ;
//MonsterSimpleInfo.Ins.Set(m_Target);
//PCInfo.Ins.Get_PetsWithDie(m_Enemy).ForEach(f => f.Add_FindEnemy(m_Target.m_Role, m_Target.Get_Group()));
}
else
{ // 모두 하나의 적만 노리지 않게 타겟 주위의 몹을 타겟
//var enemies = DSUtil.Get_Enemies(IsEnemy(), m_Target.Get_position(), 3f, Get_EnemyList());
//if (enemies != null && enemies.Count > 0) m_Target = enemies[Random.Range(0, enemies.Count)];
}
}
}
public override void Change_Target ( bool _ispc , Actor target , bool _ChangeTargetByHit = false )
{
base . Change_Target ( _ispc , target , _ChangeTargetByHit ) ;
//if (target) MonsterSimpleInfo.Ins.Set(target);
}
protected override void Update ( )
{
if ( IsDead ( ) ) return ;
m_attackCoolTime - = Time . deltaTime ;
if ( m_HitTime > 0f )
{
m_HitTime - = Time . deltaTime ;
return ;
}
if ( IsStop | | IsCC ( ) | | ( GameUI . Ins & & GameUI . Ins . IngameUI . Active_SelectSkillUI ( ) ) ) return ;
2025-05-06 22:41:40 +00:00
if ( isFollowOwner )
{
if ( Complete_Destination ( ) )
{
Play_Idle ( ) ;
isFollowOwner = false ;
}
return ;
}
2024-12-15 01:39:39 +00:00
base . Update ( ) ;
2026-09-06 23:24:25 +00:00
2024-12-15 01:39:39 +00:00
Check_Target ( ) ;
2026-09-06 23:24:25 +00:00
// #770-2 : 타겟이 죽는 즉시(공격 애니메이션 재생 중이어도) 가장 가까운 적으로 다시 타겟팅한다.
// m_Auto 와 무관하다 — 공격 버튼만 쓰는 계정도 콤보가 끊기면 안 된다.
if ( isTarget & & m_Target . IsDead ( ) & & UseWLTargeting ( ) & & WL . Settings . WLTargetingSettings . RetargetOnKill )
{
Set_Target ( ) ;
// 콤보 재생 중이면 애니메이션을 끊지 않고 몸만 돌린다 — 다음 타격 판정이 새 타겟 쪽으로 나간다.
if ( isTarget & & CurAnim > = eAnim . Attack & & CurAnim < = eAnim . Attack4 )
LookAt_Horizontal ( m_Target . Get_position ( ) ) ;
}
// #787 : 대쉬 중에는 접근/공격 판정을 건너뛴다. DashDriver 가 이동을 쥐고 있고
// 도착하면 스스로 Play_Attack 을 건다. 여기서 Play_Run 을 다시 부르면
// NavMesh 경로가 생겨 대쉬 이동과 싸운다(위 재타겟 처리는 그대로 돈다).
if ( WL . Combat . DashDriver . IsDashingNow ( this ) ) return ;
2025-02-08 22:22:31 +00:00
if ( isTarget )
2024-12-15 01:39:39 +00:00
{
if ( m_Target . IsDead ( ) )
Del_Target ( ) ;
else
{
if ( IsNotPC ( ) & & Vector3 . Distance ( OwnerActor . Get_position ( ) , Get_position ( ) ) > 7 )
{
Del_Target ( ) ;
return ;
}
2025-01-14 06:51:57 +00:00
if ( CurAnim < eAnim . Attack )
{
2026-10-18 15:09:05 +00:00
// WL #902 훅 — 우선 시전 스킬(무기 기술 표 first_cast · 해머 강력한 일격)은 쿨이 차 있으면 다가가거나 때리기 전에 먼저 쓴다(사거리 밖 적은 스킬이 점프로 붙는다)
if ( DSUtil . CheckNull ( m_Magic ) & & UseWLTargeting ( ) & & this is PCActor wlFirst & & WL . Combat . Weapon . WLWeaponSkillSlots . TryFirstCast ( wlFirst ) ) return ;
2025-01-14 06:51:57 +00:00
if ( DSUtil . CheckNull ( m_Magic ) )
m_NavMeshAgent . stoppingDistance = m_NavStopDistance + ( float ) m_Stat . Get_Stat ( eStat . AttackRange_Up ) ;
else
m_NavMeshAgent . stoppingDistance = m_Magic . m_SkillTableData . f_SkillRange ;
2025-08-09 13:10:17 +00:00
// 상대 크기 보정해서 공격할 수 있게 함
if ( m_Target . Get_Agent ( ) . radius > m_NavMeshAgent . stoppingDistance )
m_NavMeshAgent . stoppingDistance = m_Target . Get_Agent ( ) . radius + 0.5f ;
2026-09-06 23:24:25 +00:00
// #770-3 : 사거리(+여유) 안이면 접근하지 않고 즉시 공격한다.
// 정지 거리보다 실제 타격 박스가 조금 길어서 생기던 "한 걸음 다가갔다 때리기"를 없앤다.
float reach = m_NavMeshAgent . stoppingDistance + m_Target . Get_ScaleSizeDist ( ) ;
if ( UseWLTargeting ( ) & & DSUtil . CheckNull ( m_Magic ) )
reach + = WL . Settings . WLTargetingSettings . ImmediateAttackMargin ;
float distToTarget = Vector3 . Distance ( Get_position ( ) , m_Target . Get_position ( ) ) ;
if ( distToTarget > reach )
{
// #790 : 사정거리 안에 다른 적이 있으면 대쉬/달리기 대신 그 적을 즉시 공격한다(대쉬 남발 방지).
if ( UseWLTargeting ( ) & & DSUtil . CheckNull ( m_Magic ) & & WL . Combat . WLCombatMotionSettings . DashBlockIfEnemyInReach )
{
var nearInReach = Find_Target_Facing ( Get_WLFindRange ( ) , true ) ;
if ( nearInReach ! = null & & nearInReach ! = m_Target )
{
if ( IsMainPC ( ) ) m_Target . Set_Outline ( false ) ;
m_Target = nearInReach ;
if ( IsMainPC ( ) ) m_Target . Set_Outline ( true ) ;
if ( CurAnim ! = eAnim . Skill & & CurAnim ! = eAnim . Dodge )
Play_Attack ( 0 , ( float ) m_Stat . Get_Stat ( eStat . FinalAttackSpeed ) ) ;
return ;
}
}
// #787 : 중간 거리 창([dashMin, dashMax]) 안이면 대쉬로 붙고 도착 즉시 공격한다.
// 창 밖(너무 멀다)이거나 대쉬 조건이 안 맞으면 기존 달리기 추적 그대로.
// 타겟은 여기서 이미 정해져 있다 — 대쉬는 타겟을 고르지 않는다(어그로 금지).
if ( ! ( UseWLTargeting ( ) & & DSUtil . CheckNull ( m_Magic ) & &
WL . Combat . DashDriver . TryBegin ( this as PCActor , m_Target , distToTarget , reach ) ) )
Play_Run ( ) ;
}
2025-01-14 06:51:57 +00:00
else if ( ! DSUtil . CheckNull ( m_Magic ) )
2025-05-03 23:06:28 +00:00
{
if ( ! Use_Skill ( m_Magic . m_SkillTableData , false , m_Magic . m_uiIndex ) )
Play_Idle ( ) ;
}
2025-01-14 06:51:57 +00:00
//else if (CurAnim != eAnim.Attack && m_attackCoolTime > 0f)
// Play_Idle();
else if ( CurAnim ! = eAnim . Skill & & CurAnim ! = eAnim . Dodge )
Play_Attack ( 0 , ( float ) m_Stat . Get_Stat ( eStat . FinalAttackSpeed ) ) ;
}
2024-12-15 01:39:39 +00:00
}
}
2025-02-11 00:15:02 +00:00
else if ( CurAnim ! = eAnim . Run & & CurAnim < eAnim . Dodge )
2025-01-27 04:24:29 +00:00
Play_Idle ( ) ;
2024-12-15 01:39:39 +00:00
}
protected virtual void Update_Move ( )
{
if ( m_fAttackMoveTime > 0f )
{
m_fAttackMoveTime - = Time . deltaTime ;
m_NavMeshAgent . stoppingDistance = 0 ;
if ( Complete_Destination ( ) | | m_fAttackMoveTime < = 0f )
{
m_fAttackMoveTime = 0f ;
if ( ! DSUtil . CheckNull ( act_MoveEnd ) )
{
m_bCanDodge = false ;
Play_Run ( ) ;
act_MoveEnd ( true ) ;
act_MoveEnd ( false ) ;
IsStop = false ;
act_MoveEnd = null ;
}
else
m_NavMeshAgent . angularSpeed = 300f ;
}
}
}
protected void Follow_PC ( )
{
2025-05-06 22:41:40 +00:00
if ( isFollowOwner )
{
2026-09-06 23:24:25 +00:00
2025-05-06 22:41:40 +00:00
}
else
{
isFollowOwner = true ;
Del_Target ( true ) ;
m_NavMeshAgent . stoppingDistance = 1f ;
Play_Run ( ) ;
Set_Path ( OwnerActor . Get_position ( ) ) ;
}
2024-12-15 01:39:39 +00:00
}
bool IsNotPC ( ) { return ! IsRole ( eRole . PC ) ; }
public override void Get_Damage ( DamageInfo _dinfo )
{
2026-09-06 23:24:25 +00:00
// PD 지시 #765 (임시) — 테스트 편의용 플레이어 무적. 값은 설정 에셋에서 읽는다(C45).
// 끄는 법: Assets/WL/Settings/Resources/WL/WLGameplaySettings.asset 의 playerInvincible 체크 해제.
// #767 (2026-09-06 회귀 수정): 무적은 '피해'만 건너뛴다. 아래 "피격 시 타겟 획득" 은 자동 공격의 시작점이라
// 무적이어도 그대로 돌아야 한다 — 가드가 함수 맨 앞에서 return 하던 동안 플레이어가 공격을 시작하지 못했다.
bool wlInvincible = IsRole ( eRole . PC ) & & WL . Settings . WLGameplaySettings . PlayerInvincible ;
if ( ! wlInvincible ) base . Get_Damage ( _dinfo ) ;
2026-09-08 14:30:57 +00:00
WL . Combat . Core . CombatEvents . RaiseDamaged ( this , _dinfo , wlInvincible ) ;
2026-09-06 23:24:25 +00:00
if ( _dinfo = = null | | DSUtil . CheckNull ( _dinfo . Beater ) ) return ;
// PD 지시 #770-4 (2026-09-06): 플레이어 자동 공격 AI 에 "나를 때린 대상을 우선 공격"(어그로) 개념이
// 있어서는 안 된다 → PC 는 피격으로 타겟을 잡지도, 바꾸지도 않는다. 펫·소환수는 기존 동작 유지.
// 🔴 귀결: Auto 가 꺼진 계정은 맞아도 스스로 공격을 시작하지 않는다(#767 이 이 경로를 시작점으로 쓰고 있었다).
// 공격 버튼(BattleUI.OnClick_Attack → Set_Target) 또는 Auto ON 이 타겟 획득 경로다.
// 되돌리는 법: WLTargetingSettings.asset 의 pcAggroOnHit 체크.
if ( UseWLTargeting ( ) & & ! WL . Settings . WLTargetingSettings . PcAggroOnHit ) return ;
2025-02-08 22:22:31 +00:00
if ( ! isTarget | | m_Target . IsDead ( ) )
2024-12-15 01:39:39 +00:00
{
2025-05-06 22:41:40 +00:00
Change_Target ( false , _dinfo . Beater , true ) ;
}
else
{
var dist1 = DSUtil . Get_Distance ( m_Target . Get_position ( ) , Get_position ( ) ) ;
var dist2 = DSUtil . Get_Distance ( _dinfo . Beater . Get_position ( ) , Get_position ( ) ) ;
if ( dist1 > dist2 ) Change_Target ( false , _dinfo . Beater , true ) ;
2024-12-15 01:39:39 +00:00
}
}
public override void Play_Run ( )
{
base . Play_Run ( ) ;
2025-02-08 22:22:31 +00:00
if ( isTarget ) Set_Path ( m_Target . Get_position ( ) ) ;
//if (isTarget)
2025-01-14 06:51:57 +00:00
//{
// if (!Set_Path(m_Target.Get_position()))
// { // 적이 내비메시 위에 없다고 판단됨
// m_Target.ReCal_NaveMesh();
// }
//}
2024-12-15 01:39:39 +00:00
}
2026-09-06 23:24:25 +00:00
/// <summary>
/// #770-1 "공격 시작 시점" : 콤보를 시작하는 순간 이동 없이 때릴 수 있는 범위에서 타겟을 다시 고른다.
/// 자동 전투 루프(Update)와 공격 버튼(BattleUI.OnClick_Attack) 양쪽 모두 이 경로를 지난다.
/// base.Play_Attack 이 곧바로 새 타겟을 LookAt 하므로 방향 전환도 같은 프레임에 끝난다.
/// </summary>
public override void Play_Attack ( int _attack , float _as , System . Action actsuccess = null )
{
if ( _attack = = 0 & & UseWLTargeting ( ) & & WL . Settings . WLTargetingSettings . AttackStartRetarget
& & ! IsDead ( ) & & CurAnim ! = eAnim . Attack & & CurAnim < eAnim . Skill )
Retarget_InReach ( ) ;
base . Play_Attack ( _attack , _as , actsuccess ) ;
}
2024-12-15 01:39:39 +00:00
public void Check_Target ( bool forcefind = false )
{
2026-09-06 23:24:25 +00:00
// #773 (2026-09-06 회귀 수정): 규칙 4 로 피격 어그로가 사라진 뒤 Auto OFF 계정은 교전 시작점이 없었다.
// → PC 는 설정(autoEngage · 기본 ON)이 켜져 있으면 계정 Auto 값과 무관하게 탐색 거리 안의 적을 자동 타겟한다.
bool autoEngage = UseWLTargeting ( ) & & WL . Settings . WLTargetingSettings . AutoEngage ;
if ( forcefind | | m_Auto | | autoEngage )
2024-12-15 01:39:39 +00:00
{
2025-02-08 22:22:31 +00:00
if ( ! isTarget )
2024-12-15 01:39:39 +00:00
Set_Target ( forcefind ) ;
else
{
2026-09-06 23:24:25 +00:00
// #770-2 : PC 는 공격/스킬 애니메이션 재생 중이어도 죽은 타겟을 즉시 교체한다(기존엔 애님이 끝날 때까지 보류).
bool waitForAnim = ! ( UseWLTargeting ( ) & & WL . Settings . WLTargetingSettings . RetargetOnKill )
& & isPlaying ( eAnim . Attack , eAnim . Attack2 , eAnim . Attack3 , eAnim . Attack4 , eAnim . Skill ) ;
if ( m_Target . IsDead ( ) & & ! waitForAnim )
2024-12-15 01:39:39 +00:00
Set_Target ( forcefind ) ;
}
}
}
2025-02-10 05:35:41 +00:00
public override void Play_Hit ( )
{
base . Play_Hit ( ) ;
if ( CurAnim = = eAnim . Run ) return ; // 달릴 때도 무시
if ( IsDead ( ) | | IsCC ( ) | | CurAnim > = eAnim . Attack ) return ; // 공격, 스킬 쓸 때는 피격 애님 무시
if ( IsRole ( eRole . Mob ) ) m_HitTime = 0.5f ;
var aniname = MyValue . Get_AnimName ( eAnim . Hit ) ;
m_animation . speed = 1f ;
AnimationPlay ( aniname ) ;
}
2024-12-15 01:39:39 +00:00
2025-02-13 04:40:04 +00:00
public override void On_Regen ( bool reincarnation = false , float healrate = 1f )
2024-12-15 01:39:39 +00:00
{
2025-02-13 04:40:04 +00:00
base . On_Regen ( reincarnation , healrate ) ;
2026-10-20 09:27:56 +00:00
if ( ! DSUtil . CheckNull ( LoadMapMgr . Ins ) ) Set_Warp ( LoadMapMgr . Ins . Get_ReincanationPos ( MyValue . MyChoiceMapData ) ) ; // WL #950 — 스킬 테스트 씬(맵 관리자 없음)에서 소환체 소멸 때 NRE(실측) · 맵 있으면 원본 그대로
2024-12-15 01:39:39 +00:00
}
public override void Set_Stat_SelectSkill ( SelectSkillTableData _sstd )
{
base . Set_Stat_SelectSkill ( _sstd ) ;
// TODO 정인호 : 3가지 카드에서 선택하는 스킬과 관련된 스탯 설정
//switch (_sstd.Stat)
//{
// case eStat.Attack:
// case eStat.CriDmg:
// if (IsRole(eRole.PC)) m_Stat.Set_Stat(true, _sstd.Stat, m_Stat.Get_Stat(_sstd.Stat) * _sstd.ValueBase);
// break;
// case eStat.HP:
// case eStat.MP:
// if (IsRole(eRole.PC)) m_Stat.Add_MaxHPMP(_sstd.Stat, _sstd.ValueBase);
// break;
// case eStat.MPRecover:
// break;
// case eStat.Defence_Melee:
// case eStat.Defence_Range:
// case eStat.Cri:
// if (IsRole(eRole.PC)) m_Stat.Set_Stat(true, _sstd.Stat, _sstd.ValueBase);
// break;
// case eStat.AttackSpeed:
// break;
// case eStat.AttackCoolTime:
// break;
// case eStat.PetAttack:
// if (IsRole(eRole.Pet)) m_Stat.Set_Stat(true, eStat.Attack, m_Stat.Get_Stat(eStat.Attack) * _sstd.ValueBase);
// break;
// case eStat.PetHP:
// if (IsRole(eRole.Pet)) m_Stat.Add_MaxHPMP(eStat.HP, _sstd.ValueBase);
// break;
// case eStat.PetDefence_Melee:
// if (IsRole(eRole.Pet)) m_Stat.Set_Stat(true, eStat.Defence_Melee, _sstd.ValueBase);
// break;
// case eStat.PetDefence_Range:
// if (IsRole(eRole.Pet)) m_Stat.Set_Stat(true, eStat.Defence_Range, _sstd.ValueBase);
// break;
// case eStat.PetBerserkAttack:
// if (IsRole(eRole.Pet) && IsBerserkPet()) m_Stat.Set_Stat(true, eStat.Attack, m_Stat.Get_Stat(eStat.Attack) * _sstd.ValueBase);
// break;
// case eStat.PetBerserkQuick:
// break;
// case eStat.PetBerserkTime:
// break;
//}
}
2025-06-24 03:25:01 +00:00
public override void Projectile ( AnimationEvent s )
2024-12-15 01:39:39 +00:00
{
base . Projectile ( s ) ;
2025-06-24 03:25:01 +00:00
if ( ! string . IsNullOrEmpty ( s . stringParameter ) )
2024-12-15 01:39:39 +00:00
{
2025-06-24 03:25:01 +00:00
var lifeTime = table_effectlist . Ins . Get_Data ( s . stringParameter ) . f_ProjectileLifeTime ;
Shoot_Projectile ( s . stringParameter , s . floatParameter , lifeTime , Get_CenterPositionFoward ( ) ) ;
2024-12-15 01:39:39 +00:00
}
}
2025-05-15 07:33:01 +00:00
public override Actor Get_Owner ( ) { return OwnerActor ; }
2024-12-15 01:39:39 +00:00
}