Files
2026-06-01 12:46:52 +02:00

931 lines
32 KiB
C++

#include <toolkit/XConsole.h>
#include <toolkit/XEnv.h>
#include <mmo/ArcadiaServer.h>
#include "StructSkillProp.h"
#include "GameAllocator.h"
#include "StructCreature.h"
#include "StructSummon.h"
#include "StructNPC.h"
#include "StructMonster.h"
#include "SendMessage.h"
#include "GameMessage.h"
#include "StructSkill.h"
StructSkillProp* StructSkillProp::Create( AR_HANDLE caster, StructSkill* pSkill, int nMagicPoint, float fHateRatio )
{
StructSkillProp* pProp = new StructSkillProp( caster, pSkill, nMagicPoint, fHateRatio );
return pProp;
}
StructSkillProp::~StructSkillProp()
{
FreeMiscHandle( m_hHandle );
#ifdef _MEM_USAGE_DEBUG
XSEH::DecreaseAllocCount( "StructSkillProp" );
#endif
}
StructSkillProp::StructSkillProp( AR_HANDLE caster, StructSkill* pSkill, int nMagicPoint, float fHateRatio )
: m_hCaster( caster ), m_Skill( *pSkill ), m_nOwnerMagicPoint( nMagicPoint ), m_fHateRatio( fHateRatio )
{
m_hHandle = AllocMiscHandle( this );
m_nArObjectType = ArObject::STATIC_OBJECT;
m_bFired = false;
m_bProcessEnd = false;
m_bIsRemovePended = false;
const SkillBase *pSkillBase = m_Skill.GetSkillBase();
switch( pSkillBase->GetSkillEffectType() )
{
case SkillBase::EF_AREA_EFFECT_MAGIC_DAMAGE_OLD:
{
INIT_AREA_EFFECT_MAGIC_DAMAGE();
break;
}
case SkillBase::EF_AREA_EFFECT_HEAL:
{
INIT_AREA_EFFECT_HEAL();
break;
}
case SkillBase::EF_AREA_EFFECT_HEAL_BY_FIELD_PROP:
{
INIT_AREA_EFFECT_HEAL_BY_FIELD_PROP();
break;
}
// 신규로 추가된 유형의 일반형 초기화 함수 사용 유형
case SkillBase::EF_AREA_EFFECT_MAGIC_DAMAGE:
case SkillBase::EF_AREA_EFFECT_MAGIC_DAMAGE_AND_HEAL:
{
INIT_SKILL_PROP_PARAMETER( ( m_Skill.GetVar(6) + m_Skill.GetVar(7) * m_Skill.GetRequestedSkillLevel() ) * 100, m_Skill.GetVar(8) * 100 );
break;
}
case SkillBase::EF_AREA_EFFECT_MAGIC_DAMAGE_AND_HEAL_T2:
{
INIT_SKILL_PROP_PARAMETER( ( m_Skill.GetVar(12) + m_Skill.GetVar(13) * m_Skill.GetRequestedSkillLevel() ) * 100, m_Skill.GetVar(14) * 100 );
break;
}
// 트랩
case SkillBase::EF_TRAP_PHYSICAL_DAMAGE:
case SkillBase::EF_TRAP_MAGICAL_DAMAGE:
case SkillBase::EF_TRAP_MULTIPLE_PHYSICAL_DAMAGE:
case SkillBase::EF_TRAP_MULTIPLE_MAGICAL_DAMAGE:
{
// 0.3초 주기로 반경 내에 대상이 있는지 없는지 검사하도록 초기화
INIT_SKILL_PROP_PARAMETER( ( m_Skill.GetVar(3) + m_Skill.GetVar(4) * m_Skill.GetRequestedSkillLevel() ) * 100, 30 );
break;
}
default:
assert( 0 );
}
#ifdef _MEM_USAGE_DEBUG
XSEH::IncreaseAllocCount( "StructSkillProp" );
#endif
}
bool StructSkillProp::ProcDelete()
{
delete this;
return true;
}
void StructSkillProp::PendRemove()
{
if( !m_bProcessEnd && !m_bIsRemovePended )
{
m_bIsRemovePended = true;
}
}
void StructSkillProp::onProcess( int nThreadIdx )
{
char buf[255];
s_sprintf( buf, _countof( buf ), "thread.scheduler.%d.proc", nThreadIdx );
ENV().Set( buf, "StructSkillProp" );
AR_TIME current_time = GetArTime();
extern __declspec( thread ) XSEH::THREAD_INFO s_ThreadInfo;
s_sprintf( s_ThreadInfo.job_info, _countof( s_ThreadInfo.job_info ), "StructSkillProp(0x%08X)", (UINT_PTR)this );
s_ThreadInfo.last_execute_time = current_time;
if( m_bProcessEnd )
{
return;
}
// Lock
ARCADIA_LOCK( ArcadiaServer::Instance().LockObjectWithVisibleRange( this ) );
if( current_time > m_Info.m_nEndTime || m_bIsRemovePended )
{
m_bProcessEnd = true;
// 해당 플레이어가 트랩을 설치할 수 있도록 현재 설치한 트랩 핸들 초기화
StructCreature::iterator itPlayer = StructCreature::get( m_hCaster );
StructCreature *pCaster = static_cast< StructCreature * >( *itPlayer );
if( pCaster && pCaster->GetTrapHandle() == m_hHandle )
{
pCaster->SetTrapHandle( NULL );
}
ArcadiaServer::Instance().RemoveObject( this );
ArcadiaServer::Instance().DeleteObject( this );
return;
}
if( current_time < m_Info.m_nLastFireTime + m_Info.m_nInterval )
{
// 아직 다음 시전 시간 아님
return;
}
m_Info.m_nLastFireTime = current_time;
// 시전자 찾기
GameObject::iterator git = GameObject::get( m_hCaster );
StructCreature *pCaster = static_cast< StructCreature * >( *git );
if( !pCaster || pCaster->IsSummon() && static_cast< StructSummon * >( pCaster )->GetMaster() == NULL )
{
m_bProcessEnd = true;
// 해당 플레이어가 트랩을 설치할 수 있도록 현재 설치한 트랩 핸들 초기화 불필요(이미 유저가 없음)
ArcadiaServer::Instance().RemoveObject( this );
ArcadiaServer::Instance().DeleteObject( this );
return;
}
m_Skill.m_targetPos = GetPos();
m_Skill.m_targetLayer = GetLayer();
m_Skill.m_vResultList.clear();
m_Skill.m_nTargetCount = 0;
switch( m_Skill.GetSkillBase()->GetSkillEffectType() )
{
case SkillBase::EF_AREA_EFFECT_MAGIC_DAMAGE_OLD:
{
FIRE_AREA_EFFECT_MAGIC_DAMAGE_OLD( pCaster );
break;
}
case SkillBase::EF_AREA_EFFECT_HEAL:
{
FIRE_AREA_EFFECT_HEAL( pCaster );
break;
}
case SkillBase::EF_AREA_EFFECT_HEAL_BY_FIELD_PROP:
{
FIRE_AREA_EFFECT_HEAL_BY_FIELD_PROP( pCaster );
break;
}
case SkillBase::EF_AREA_EFFECT_MAGIC_DAMAGE:
{
FIRE_AREA_EFFECT_MAGIC_DAMAGE( pCaster );
break;
}
case SkillBase::EF_AREA_EFFECT_MAGIC_DAMAGE_AND_HEAL:
{
FIRE_AREA_EFFECT_MAGIC_DAMAGE_AND_HEAL( pCaster );
break;
}
case SkillBase::EF_AREA_EFFECT_MAGIC_DAMAGE_AND_HEAL_T2:
{
FIRE_AREA_EFFECT_MAGIC_DAMAGE_AND_HEAL_T2( pCaster );
break;
}
case SkillBase::EF_TRAP_PHYSICAL_DAMAGE:
case SkillBase::EF_TRAP_MAGICAL_DAMAGE:
{
FIRE_TRAP_DAMAGE( pCaster );
break;
}
case SkillBase::EF_TRAP_MULTIPLE_PHYSICAL_DAMAGE:
case SkillBase::EF_TRAP_MULTIPLE_MAGICAL_DAMAGE:
{
FIRE_TRAP_MULTIPLE_DAMAGE( pCaster );
break;
}
default:
{
assert( 0 );
break;
}
}
// 본래는 스킬 사용 이후 관련 로직들이 일괄적으로 적용되야 하겠지만
// 수정 시의 영향력을 평가하기가 어려워 당장은 Hate, Havok만 적용한다.
// 이에 따른 다소의 코드 중복은 양해 바람
for( std::vector< SkillResult >::iterator it = m_Skill.m_vResultList.begin(); it != m_Skill.m_vResultList.end(); ++it )
{
if( it->GetType() == SkillResult::DAMAGE || it->GetType() == SkillResult::MAGIC_DAMAGE || it->GetType() == SkillResult::DAMAGE_WITH_KNOCK_BACK ||
it->GetType() == SkillResult::ADD_HP || it->GetType() == SkillResult::ADD_MP || it->GetType() == SkillResult::ADD_HP_MP_SP || it->GetType() == SkillResult::ADD_STATE ||
it->GetType() == SkillResult::CHAIN_DAMAGE || it->GetType() == SkillResult::CHAIN_MAGIC_DAMAGE || it->GetType() == SkillResult::CHAIN_HEAL )
{
StructCreature::iterator cit = StructCreature::get( it->damage.hTarget );
StructCreature * pDealTarget = (*cit);
m_Skill.ApplyHateAndHavokFromSkillResult( pCaster, pDealTarget, *it );
}
}
if( m_Skill.m_nTargetCount )
m_Skill.broadcastSkillMessage( GetRX(), GetRY(), GetLayer(), 0, 0, TS_SC_SKILL::REGION_FIRE );
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
void StructSkillProp::INIT_AREA_EFFECT_MAGIC_DAMAGE()
{
m_Info.m_nStartTime = GetArTime();
m_Info.m_nEndTime = m_Info.m_nStartTime + ( m_Skill.GetVar(3) + m_Skill.GetVar(4) * m_Skill.GetRequestedSkillLevel() ) * 100;
m_Info.m_nInterval = m_Skill.GetVar(6) * 100;
m_Info.m_nLastFireTime = 0;
}
void StructSkillProp::FIRE_AREA_EFFECT_MAGIC_DAMAGE_OLD( struct StructCreature * pCaster )
{
std::vector< AR_HANDLE > vResult;
vResult.reserve( 30 );
float fRange = m_Skill.GetVar(5) * GameRule::DEFAULT_UNIT_SIZE;
// { 데미지 계산
int nDamage = m_nOwnerMagicPoint + m_Skill.GetVar(0) + m_Skill.GetRequestedSkillLevel() * m_Skill.GetVar(1) + m_Skill.GetEnhance() * m_Skill.GetVar(7);
nDamage *= m_Skill.GetVar(2) + m_Skill.GetEnhance() * m_Skill.GetVar(8);
// }
// 근처 유닛 리스트를 vResult에 긁어옴
ArcadiaServer::Instance().EnumMovableObject( GetPos(), GetLayer(), fRange, &vResult );
StructCreature *pCreature;
std::vector< AR_HANDLE >::iterator it;
SkillResult skill_result;
for( it = vResult.begin(); it != vResult.end(); ++it )
{
// 크리쳐 핸들 얻는다
pCreature = static_cast< StructCreature * >( GameObject::raw_get( *it ) );
if( !pCreature || pCreature->IsPet() ) continue;
if( !pCaster->IsEnemy( pCreature, true ) )
continue;
if( pCreature->IsDead() )
continue;
int flag = 0;
int elemental_type = m_Skill.GetSkillBase()->GetElementalType();
// 데미지 주기
StructCreature::_DAMAGE Damage = pCreature->DealMagicalDamage( m_Skill.m_pOwner, nDamage, (Elemental::Type) elemental_type, m_Skill.GetCriticalBonus( m_Skill.GetRequestedSkillLevel() ) );
if( Damage.bCritical ) flag |= SkillResult::CRITICAL;
if( Damage.bBlock ) flag |= SkillResult::BLOCK;
if( Damage.bMiss ) flag |= SkillResult::MISS;
if( Damage.bPerfectBlock) flag |= SkillResult::PERFECT_BLOCK;
skill_result.damage.type = SkillResult::MAGIC_DAMAGE;
skill_result.damage.damage_type = elemental_type;
skill_result.damage.damage = Damage.nDamage;
skill_result.damage.hTarget = pCreature->GetHandle();
skill_result.damage.flag = flag;
skill_result.damage.target_hp = pCreature->GetHP();
m_Skill.m_vResultList.push_back( skill_result );
++m_Skill.m_nTargetCount;
if( !Damage.bMiss && m_Skill.m_pOwner->GetPos().GetDistance( GetPos() ) <= GameRule::VISIBLE_RANGE )
{
if( pCreature->IsMonster() )
{
int nHate = Damage.nDamage;
std::pair< float, int > HateMod = pCaster->GetHateMod( ( m_Skill.GetSkillBase()->IsPhysicalSkill() ) ? 1 : 2, m_Skill.GetSkillBase()->IsHarmful() );
nHate += HateMod.second;
nHate *= HateMod.first;
static_cast< StructMonster * >( pCreature )->AddHate( m_Skill.m_pOwner->GetHandle(), m_fHateRatio * nHate );
}
else if( pCreature->IsNPC() )
static_cast< StructNPC * >( pCreature )->SetAttacker( m_Skill.m_pOwner );
}
}
}
void StructSkillProp::INIT_AREA_EFFECT_HEAL()
{
m_Info.m_nStartTime = GetArTime();
m_Info.m_nEndTime = m_Info.m_nStartTime + ( m_Skill.GetVar(7) + m_Skill.GetVar(8) * m_Skill.GetRequestedSkillLevel() ) * 100;
m_Info.m_nInterval = m_Skill.GetVar(10) * 100;
m_Info.m_nLastFireTime = 0;
}
void StructSkillProp::FIRE_AREA_EFFECT_HEAL( struct StructCreature * pCaster )
{
std::vector< AR_HANDLE > vResult;
AR_UNIT fRange = m_Skill.GetVar(9) * GameRule::DEFAULT_UNIT_SIZE;
m_Skill.m_fRange = fRange;
int nHPHeal = m_Skill.GetVar(0) + m_Skill.GetRequestedSkillLevel() * m_Skill.GetVar(1);
int nMPHeal = m_Skill.GetVar(2) + m_Skill.GetRequestedSkillLevel() * m_Skill.GetVar(3);
int nSPHeal = m_Skill.GetVar(4) + m_Skill.GetRequestedSkillLevel() * m_Skill.GetVar(5);
nHPHeal *= m_Skill.GetVar(11) * m_Skill.GetEnhance() + 1;
nMPHeal *= m_Skill.GetVar(11) * m_Skill.GetEnhance() + 1;
nSPHeal *= m_Skill.GetVar(11) * m_Skill.GetEnhance() + 1;
// 근처 유닛 리스트를 vResult에 긁어옴
ArcadiaServer::Instance().EnumMovableObject( GetPos(), GetLayer(), fRange, &vResult );
StructCreature *pCreature;
std::vector< AR_HANDLE >::iterator it;
int nTargetLimit = m_Skill.GetVar(6);
SkillResult skill_result;
for( it = vResult.begin(); it != vResult.end(); ++it )
{
// 크리쳐 핸들 얻는다
pCreature = static_cast< StructCreature * >( GameObject::raw_get( *it ) );
if( !pCreature || pCreature->IsPet() ) continue;
if( nTargetLimit == SkillBase::SKILL_EFFECT_TARGET_LIMIT_NOT_ENEMY && pCaster->IsEnemy( pCreature, true ) )
continue;
if( nTargetLimit == SkillBase::SKILL_EFFECT_TARGET_LIMIT_ONLY_ALLY && !pCaster->IsAlly( pCreature ) )
continue;
if( pCreature->IsDead() )
continue;
// HEAL~
nHPHeal = pCreature->Heal( nHPHeal );
nMPHeal = pCreature->MPHeal( nMPHeal );
if( pCreature->IsSummon() )
{
StructSummon * pSummon = static_cast< StructSummon * >( pCreature );
pSummon->SetSP( pSummon->GetSP() + nSPHeal );
}
skill_result.add_hp_mp_sp.type = SkillResult::ADD_HP_MP_SP;
skill_result.add_hp_mp_sp.hTarget = pCreature->GetHandle();
skill_result.add_hp_mp_sp.target_hp = pCreature->GetHP();
skill_result.add_hp_mp_sp.target_mp = pCreature->GetMP();
skill_result.add_hp_mp_sp.nIncHP = nHPHeal;
skill_result.add_hp_mp_sp.nIncMP = nMPHeal;
skill_result.add_hp_mp_sp.nIncSP = nSPHeal;
m_Skill.m_vResultList.push_back( skill_result );
++m_Skill.m_nTargetCount;
}
}
void StructSkillProp::INIT_AREA_EFFECT_HEAL_BY_FIELD_PROP()
{
m_Info.m_nStartTime = GetArTime();
m_Info.m_nEndTime = m_Info.m_nStartTime + ( m_Skill.GetVar(7) + m_Skill.GetVar(8) * m_Skill.GetRequestedSkillLevel() ) * 100;
m_Info.m_nInterval = m_Skill.GetVar(5) * 100;
m_Info.m_nLastFireTime = 0;
}
void StructSkillProp::FIRE_AREA_EFFECT_HEAL_BY_FIELD_PROP( struct StructCreature * pCaster )
{
std::vector< AR_HANDLE > vResult;
AR_UNIT fRange = m_Skill.GetVar(4) * GameRule::DEFAULT_UNIT_SIZE;
m_Skill.m_fRange = fRange;
c_fixed10 fHPHeal = m_Skill.GetVar(0) + m_Skill.GetVar(1) * m_Skill.GetRequestedSkillLevel();
c_fixed10 fMPHeal = m_Skill.GetVar(2) + m_Skill.GetVar(3) * m_Skill.GetRequestedSkillLevel();
// 근처 유닛 리스트를 vResult에 긁어옴
ArcadiaServer::Instance().EnumMovableObject( GetPos(), GetLayer(), fRange, &vResult );
StructCreature *pCreature;
std::vector< AR_HANDLE >::iterator it;
int nTargetLimit = m_Skill.GetVar(6);
SkillResult skill_result;
for( it = vResult.begin(); it != vResult.end(); ++it )
{
// 크리쳐 핸들 얻는다
pCreature = static_cast< StructCreature * >( GameObject::raw_get( *it ) );
if( !pCreature || pCreature->IsPet() ) continue;
switch( nTargetLimit )
{
case SkillBase::SKILL_EFFECT_TARGET_LIMIT_NOT_ENEMY:
if( pCaster->IsEnemy( pCreature, true ) )
continue;
break;
case SkillBase::SKILL_EFFECT_TARGET_LIMIT_ONLY_ALLY:
if( !pCaster->IsAlly( pCreature ) )
continue;
break;
case SkillBase::SKILL_EFFECT_TARGET_LIMIT_ONLY_ENEMY:
if( !pCaster->IsEnemy( pCreature ) )
continue;
break;
}
if( pCreature->IsDead() )
continue;
// HEAL~
int nHPHeal = pCreature->Heal( fHPHeal * pCreature->GetMaxHP() );
int nMPHeal = pCreature->MPHeal( fMPHeal * pCreature->GetMaxMP() );
skill_result.add_hp_mp_sp.type = SkillResult::ADD_HP_MP_SP;
skill_result.add_hp_mp_sp.hTarget = pCreature->GetHandle();
skill_result.add_hp_mp_sp.target_hp = pCreature->GetHP();
skill_result.add_hp_mp_sp.target_mp = pCreature->GetMP();
skill_result.add_hp_mp_sp.nIncHP = nHPHeal;
skill_result.add_hp_mp_sp.nIncMP = nMPHeal;
skill_result.add_hp_mp_sp.nIncSP = 0;
m_Skill.m_vResultList.push_back( skill_result );
++m_Skill.m_nTargetCount;
}
}
void StructSkillProp::INIT_SKILL_PROP_PARAMETER( AR_TIME nDuration, AR_TIME nInterval )
{
m_Info.m_nStartTime = GetArTime();
m_Info.m_nEndTime = m_Info.m_nStartTime + nDuration;
m_Info.m_nInterval = nInterval;
m_Info.m_nLastFireTime = 0;
}
void StructSkillProp::FIRE_AREA_EFFECT_MAGIC_DAMAGE( struct StructCreature * pCaster )
{
std::vector< StructCreature * > vResult;
vResult.reserve( 30 );
float fRange = m_Skill.GetVar(9) * GameRule::DEFAULT_UNIT_SIZE;
// { 데미지 계산
int nDamage = m_nOwnerMagicPoint * ( m_Skill.GetVar(0) + m_Skill.GetRequestedSkillLevel() * m_Skill.GetVar(1) + m_Skill.GetEnhance() * m_Skill.GetVar(2) ) + m_Skill.GetVar(3) + m_Skill.GetVar(4) * m_Skill.GetRequestedSkillLevel() + m_Skill.GetVar(5) * m_Skill.GetEnhance();
nDamage *= m_Skill.GetVar(10) + m_Skill.GetVar(11) * m_Skill.GetEnhance();
// }
// 근처 유닛 리스트를 vResult에 긁어옴
nDamage = EnumSkillTargetsAndCalcDamage( GetPos(), GetLayer(), GetPos(), false, fRange, -1, 0, nDamage, true, pCaster, m_Skill.GetVar(16), m_Skill.GetVar(17), vResult );
StructCreature *pCreature;
std::vector< StructCreature * >::iterator it;
SkillResult skill_result;
AR_TIME t = GetArTime();
for( it = vResult.begin(); it != vResult.end(); ++it )
{
// 크리쳐 핸들 얻는다
pCreature = *it;
if( !pCreature || pCreature->IsPet() ) continue;
if( !pCaster->IsEnemy( pCreature, true ) )
continue;
if( pCreature->IsDead() )
continue;
int flag = 0;
int elemental_type = m_Skill.GetSkillBase()->GetElementalType();
int accuary_bonus = m_Skill.GetHitBonus(m_Skill.GetEnhance(), pCaster->GetLevel() - pCreature->GetLevel()) ;
if (m_Skill.GetSkillId() == 21103)
accuary_bonus = 10;
// 데미지 주기
StructCreature::_DAMAGE Damage = pCreature->DealMagicalDamage( m_Skill.m_pOwner, nDamage, (Elemental::Type) elemental_type, accuary_bonus , m_Skill.GetCriticalBonus( m_Skill.GetRequestedSkillLevel() ), 0, pCreature->GetMagicalSkillStatePenalty() );
if( Damage.bCritical ) flag |= SkillResult::CRITICAL;
if( Damage.bBlock ) flag |= SkillResult::BLOCK;
if( Damage.bMiss ) flag |= SkillResult::MISS;
if( Damage.bPerfectBlock) flag |= SkillResult::PERFECT_BLOCK;
skill_result.damage.type = SkillResult::MAGIC_DAMAGE;
skill_result.damage.damage_type = elemental_type;
skill_result.damage.damage = Damage.nDamage;
skill_result.damage.hTarget = pCreature->GetHandle();
skill_result.damage.flag = flag;
skill_result.damage.target_hp = pCreature->GetHP();
m_Skill.m_vResultList.push_back( skill_result );
++m_Skill.m_nTargetCount;
if( !Damage.bMiss )
{
if( pCreature->IsMonster() )
{
int nHate = Damage.nDamage;
std::pair< float, int > HateMod = pCaster->GetHateMod( ( m_Skill.GetSkillBase()->IsPhysicalSkill() ) ? 1 : 2, m_Skill.GetSkillBase()->IsHarmful() );
nHate += HateMod.second;
nHate *= HateMod.first;
static_cast< StructMonster * >( pCreature )->AddHate( m_Skill.m_pOwner->GetHandle(), m_fHateRatio * nHate );
}
else if( pCreature->IsNPC() )
static_cast< StructNPC * >( pCreature )->SetAttacker( m_Skill.m_pOwner );
if( !Damage.bMiss && pCreature->IsAlive() )
{
StructState::StateCode nStateCode = static_cast< StructState::StateCode >(m_Skill.GetStateId());
if( nStateCode )
{
int nLevel = m_Skill.GetStateLevel( m_Skill.GetRequestedSkillLevel(), m_Skill.GetEnhance() );
AR_TIME nDuration = m_Skill.GetStateSecond( m_Skill.GetRequestedSkillLevel(), m_Skill.GetEnhance() );
pCreature->AddState( nStateCode, pCaster->GetHandle(), nLevel, t, t + nDuration );
}
}
}
}
// 확률로 상태이상 부여
}
void StructSkillProp::FIRE_AREA_EFFECT_MAGIC_DAMAGE_AND_HEAL( struct StructCreature * pCaster )
{
std::vector< AR_HANDLE > vResult;
vResult.reserve( 30 );
float fRange = m_Skill.GetVar(9) * GameRule::DEFAULT_UNIT_SIZE;
// { 데미지 계산
int nDamage = m_nOwnerMagicPoint * ( m_Skill.GetVar(0) + m_Skill.GetRequestedSkillLevel() * m_Skill.GetVar(1) + m_Skill.GetEnhance() * m_Skill.GetVar(2) );
nDamage *= m_Skill.GetVar(10) + m_Skill.GetEnhance() * m_Skill.GetVar(11);
int nHPHeal = m_Skill.GetVar(3) + m_Skill.GetRequestedSkillLevel() * m_Skill.GetVar(4) + m_Skill.GetEnhance() * m_Skill.GetVar(5);
// }
// 근처 유닛 리스트를 vResult에 긁어옴
ArcadiaServer::Instance().EnumMovableObject( GetPos(), GetLayer(), fRange, &vResult );
StructCreature *pCreature;
std::vector< AR_HANDLE >::iterator it;
for( it = vResult.begin(); it != vResult.end(); ++it )
{
SkillResult skill_result;
// 크리쳐 핸들 얻는다
pCreature = static_cast< StructCreature * >( GameObject::raw_get( *it ) );
if( !pCreature || pCreature->IsPet() ) continue;
if( pCreature->IsDead() )
continue;
// 적이면 뎀쥐 드셈
if( pCaster->IsEnemy( pCreature, true ) )
{
int flag = 0;
int elemental_type = m_Skill.GetSkillBase()->GetElementalType();
// 데미지 주기
StructCreature::_DAMAGE Damage = pCreature->DealMagicalDamage( m_Skill.m_pOwner, nDamage, (Elemental::Type) elemental_type, m_Skill.GetCriticalBonus( m_Skill.GetRequestedSkillLevel() ) );
if( Damage.bCritical ) flag |= SkillResult::CRITICAL;
if( Damage.bBlock ) flag |= SkillResult::BLOCK;
if( Damage.bMiss ) flag |= SkillResult::MISS;
if( Damage.bPerfectBlock) flag |= SkillResult::PERFECT_BLOCK;
skill_result.damage.type = SkillResult::MAGIC_DAMAGE;
skill_result.damage.damage_type = elemental_type;
skill_result.damage.damage = Damage.nDamage;
skill_result.damage.hTarget = pCreature->GetHandle();
skill_result.damage.flag = flag;
skill_result.damage.target_hp = pCreature->GetHP();
m_Skill.m_vResultList.push_back( skill_result );
++m_Skill.m_nTargetCount;
if( !Damage.bMiss )
{
if( pCreature->IsMonster() )
{
int nHate = Damage.nDamage;
std::pair< float, int > HateMod = pCaster->GetHateMod( ( m_Skill.GetSkillBase()->IsPhysicalSkill() ) ? 1 : 2, m_Skill.GetSkillBase()->IsHarmful() );
nHate += HateMod.second;
nHate *= HateMod.first;
static_cast< StructMonster * >( pCreature )->AddHate( m_Skill.m_pOwner->GetHandle(), m_fHateRatio * nHate );
}
else if( pCreature->IsNPC() )
static_cast< StructNPC * >( pCreature )->SetAttacker( m_Skill.m_pOwner );
}
}
// 아군이면 힐 드셈
else if( pCaster->IsAlly( pCreature ) )
{
nHPHeal = pCreature->Heal( nHPHeal );
skill_result.add_hp_mp_sp.type = SkillResult::ADD_HP;
skill_result.add_hp_mp_sp.hTarget = pCreature->GetHandle();
skill_result.add_hp_mp_sp.target_hp = pCreature->GetHP();
skill_result.add_hp_mp_sp.target_mp = pCreature->GetMP();
skill_result.add_hp_mp_sp.nIncHP = nHPHeal;
skill_result.add_hp_mp_sp.nIncMP = 0;
skill_result.add_hp_mp_sp.nIncSP = 0;
m_Skill.m_vResultList.push_back( skill_result );
++m_Skill.m_nTargetCount;
}
}
}
void StructSkillProp::FIRE_AREA_EFFECT_MAGIC_DAMAGE_AND_HEAL_T2( struct StructCreature * pCaster )
{
std::vector< StructCreature * > vResult;
vResult.reserve( 30 );
float fRange = m_Skill.GetVar(15) * GameRule::DEFAULT_UNIT_SIZE;
// { 데미지 계산
int nHPHeal = m_nOwnerMagicPoint * ( m_Skill.GetVar(0) + m_Skill.GetRequestedSkillLevel() * m_Skill.GetVar(1) + m_Skill.GetEnhance() * m_Skill.GetVar(2) ) + m_Skill.GetVar(3) + m_Skill.GetVar(4) * m_Skill.GetRequestedSkillLevel() + m_Skill.GetVar(5) * m_Skill.GetEnhance();
int nDamage = m_nOwnerMagicPoint * ( m_Skill.GetVar(6) + m_Skill.GetRequestedSkillLevel() * m_Skill.GetVar(7) + m_Skill.GetEnhance() * m_Skill.GetVar(8) ) + m_Skill.GetVar(9) + m_Skill.GetVar(10) * m_Skill.GetRequestedSkillLevel() + m_Skill.GetVar(11) * m_Skill.GetEnhance();
nDamage *= m_Skill.GetVar(18) + m_Skill.GetEnhance() * m_Skill.GetVar(19);
// }
// 근처 유닛 리스트를 vResult에 긁어옴
nDamage = EnumSkillTargetsAndCalcDamage( GetPos(), GetLayer(), GetPos(), false, fRange, -1, 0, nDamage,
true, pCaster, m_Skill.GetVar(16), m_Skill.GetVar(17), vResult, false );
StructCreature *pCreature;
AR_TIME t = GetArTime();
for( std::vector< StructCreature * >::iterator it = vResult.begin(); it != vResult.end(); ++it )
{
SkillResult skill_result;
// 크리쳐 핸들 얻는다
pCreature = (*it);
if( pCreature->IsDead() )
continue;
// 적이면 뎀쥐 드셈
if( pCaster->IsEnemy( pCreature, true ) )
{
int flag = 0;
int elemental_type = m_Skill.GetSkillBase()->GetElementalType();
// 데미지 주기
StructCreature::_DAMAGE Damage = pCreature->DealMagicalDamage( m_Skill.m_pOwner, nDamage, (Elemental::Type) elemental_type, m_Skill.GetCriticalBonus( m_Skill.GetRequestedSkillLevel() ) );
if( Damage.bCritical ) flag |= SkillResult::CRITICAL;
if( Damage.bBlock ) flag |= SkillResult::BLOCK;
if( Damage.bMiss ) flag |= SkillResult::MISS;
if( Damage.bPerfectBlock) flag |= SkillResult::PERFECT_BLOCK;
skill_result.damage.type = SkillResult::MAGIC_DAMAGE;
skill_result.damage.damage_type = elemental_type;
skill_result.damage.damage = Damage.nDamage;
skill_result.damage.hTarget = pCreature->GetHandle();
skill_result.damage.flag = flag;
skill_result.damage.target_hp = pCreature->GetHP();
m_Skill.m_vResultList.push_back( skill_result );
++m_Skill.m_nTargetCount;
if( !Damage.bMiss && pCreature->IsAlive() )
{
StructState::StateCode nStateCode = static_cast< StructState::StateCode >(m_Skill.GetStateId());
if( nStateCode )
{
int nLevel = m_Skill.GetStateLevel( m_Skill.GetRequestedSkillLevel(), m_Skill.GetEnhance() );
AR_TIME nDuration = m_Skill.GetStateSecond( m_Skill.GetRequestedSkillLevel(), m_Skill.GetEnhance() );
pCreature->AddState( nStateCode, pCaster->GetHandle(), nLevel, t, t + nDuration );
}
if( pCreature->IsMonster() )
{
int nHate = Damage.nDamage;
std::pair< float, int > HateMod = pCaster->GetHateMod( ( m_Skill.GetSkillBase()->IsPhysicalSkill() ) ? 1 : 2, m_Skill.GetSkillBase()->IsHarmful() );
nHate += HateMod.second;
nHate *= HateMod.first;
static_cast< StructMonster * >( pCreature )->AddHate( m_Skill.m_pOwner->GetHandle(), m_fHateRatio * nHate );
}
else if( pCreature->IsNPC() )
static_cast< StructNPC * >( pCreature )->SetAttacker( m_Skill.m_pOwner );
}
}
// 아군이면 힐 드셈
else if( pCaster->IsAlly( pCreature ) )
{
nHPHeal = pCreature->Heal( nHPHeal );
skill_result.add_hp_mp_sp.type = SkillResult::ADD_HP;
skill_result.add_hp_mp_sp.hTarget = pCreature->GetHandle();
skill_result.add_hp_mp_sp.target_hp = pCreature->GetHP();
skill_result.add_hp_mp_sp.target_mp = pCreature->GetMP();
skill_result.add_hp_mp_sp.nIncHP = nHPHeal;
skill_result.add_hp_mp_sp.nIncMP = 0;
skill_result.add_hp_mp_sp.nIncSP = 0;
m_Skill.m_vResultList.push_back( skill_result );
++m_Skill.m_nTargetCount;
}
}
}
void StructSkillProp::FIRE_TRAP_DAMAGE( StructCreature *pCaster )
{
float fFireRange = m_Skill.GetVar(0) * GameRule::DEFAULT_UNIT_SIZE;
float fDamageRange = m_Skill.GetVar(5) * GameRule::DEFAULT_UNIT_SIZE;
// 발동 반경 내의 유닛 리스트를 vResult에 긁어옴
std::vector< StructCreature * > vTarget;
EnumSkillTargetsAndCalcDamage( GetPos(), GetLayer(), GetPos(), true, fFireRange, -1, 0, 0,
true, pCaster, SkillBase::DISTRIBUTION_TYPE_NO_LIMIT, 0, vTarget );
std::vector< StructCreature * >::iterator it;
for( it = vTarget.begin() ; it != vTarget.end() ; ++it )
{
if( (*it) && pCaster->IsEnemy( *it, true ) )
break;
}
if( vTarget.empty() || it == vTarget.end() )
return;
// { 데미지 계산
int elemental_type = m_Skill.GetSkillBase()->GetElementalType();
int nDamage = m_Skill.GetVar(1) + m_Skill.GetRequestedSkillLevel() * m_Skill.GetVar(2);
// }
AR_TIME t = GetArTime();
// 근처 유닛 리스트를 vTargetList에 긁어오고, 데미지 계산
std::vector< StructCreature * > vTargetList;
std::vector< StructCreature * >::iterator itTargetList;
// 분산 유형 바꿀 것 -> SkillBase::DISTRIBUTION_TYPE_DISTRIBUTE
nDamage = EnumSkillTargetsAndCalcDamage( GetPos(), GetLayer(), GetPos(), true, fDamageRange, -1, 0, nDamage,
true, pCaster, SkillBase::DISTRIBUTION_TYPE_NO_LIMIT, m_Skill.GetVar(6), vTargetList );
// 데미지 주기
for( itTargetList = vTargetList.begin() ; itTargetList != vTargetList.end() ; ++itTargetList )
{
StructCreature* pDealTarget = (*itTargetList);
switch( m_Skill.GetSkillBase()->GetSkillEffectType() )
{
case SkillBase::EF_TRAP_PHYSICAL_DAMAGE:
{
StructCreature::_DAMAGE_INFO Damage = pDealTarget->DealPhysicalSkillDamage( pCaster, nDamage, (Elemental::Type)elemental_type, m_Skill.GetHitBonus( m_Skill.GetEnhance(), pCaster->GetLevel() - pDealTarget->GetLevel() ), m_Skill.GetCriticalBonus( m_Skill.GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_Skill.m_vResultList, SkillResult::DAMAGE, elemental_type, Damage, pDealTarget->GetHandle() );
}
break;
case SkillBase::EF_TRAP_MAGICAL_DAMAGE:
{
StructCreature::_DAMAGE_INFO Damage = pDealTarget->DealMagicalSkillDamage( pCaster, nDamage, (Elemental::Type)elemental_type, m_Skill.GetHitBonus( m_Skill.GetEnhance(), pCaster->GetLevel() - pDealTarget->GetLevel() ), m_Skill.GetCriticalBonus( m_Skill.GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_Skill.m_vResultList, SkillResult::MAGIC_DAMAGE, elemental_type, Damage, pDealTarget->GetHandle() );
}
break;
}
++m_Skill.m_nTargetCount;
// 확률로 상태이상 부여
if( XRandom( 0, 99 ) < m_Skill.GetProbabilityOnHit( m_Skill.GetRequestedSkillLevel() ) )
{
int nLevel = m_Skill.GetStateLevel( m_Skill.GetRequestedSkillLevel(), m_Skill.GetEnhance() );
AR_TIME nDuration = m_Skill.GetStateSecond( m_Skill.GetRequestedSkillLevel(), m_Skill.GetEnhance() );
StructState::StateCode nStateCode = static_cast< StructState::StateCode >(m_Skill.GetStateId());
(*itTargetList)->AddState( nStateCode, pCaster->GetHandle(), nLevel, t, t + nDuration );
AddSkillResult( &m_Skill.m_vResultList, true, SkillResult::ADD_STATE, (*itTargetList)->GetHandle() );
++m_Skill.m_nTargetCount;
}
}
// 개채를 여기서 삭제하면 RemoveObject의 방송을 위한 락과 여기 onProcess에서의 락이 중첩되어 서버가 멎음
// m_Info.m_nEndTime을 현재로 세팅하면 onProcess에서 알아서 없애줌
//m_bProcessEnd = true;
//ArcadiaServer::Instance().DeleteObject( this );
m_Info.m_nEndTime = t;
return;
}
void StructSkillProp::FIRE_TRAP_MULTIPLE_DAMAGE( StructCreature *pCaster )
{
float fFireRange = m_Skill.GetVar(0) * GameRule::DEFAULT_UNIT_SIZE;
float fDamageRange = m_Skill.GetVar(5) * GameRule::DEFAULT_UNIT_SIZE;
if( !m_bFired )
{
// 발동 반경 내의 유닛 리스트를 vResult에 긁어옴
std::vector< StructCreature * > vTarget;
EnumSkillTargetsAndCalcDamage( GetPos(), GetLayer(), GetPos(), true, fFireRange, -1, 0, 0,
true, pCaster, SkillBase::DISTRIBUTION_TYPE_NO_LIMIT, 0, vTarget );
std::vector< StructCreature * >::iterator it;
for( it = vTarget.begin() ; it != vTarget.end() ; ++it )
{
if( (*it) && pCaster->IsEnemy( *it, true ) )
break;
}
if( vTarget.empty() || it == vTarget.end() )
return;
AR_TIME t = GetArTime();
AR_TIME nFireEndTime = t + ( m_Skill.GetVar(7) + m_Skill.GetRequestedSkillLevel() * m_Skill.GetVar(8) ) * 100;
m_bFired = true;
m_Info.m_nInterval = m_Skill.GetVar(9) * 100;
// 밟았으면 지속시간만큼만 지속되고 없어져야 함
// 밟은 후 지속시간이 10초인데 트랩 지속시간이 1초 남았어도 10초간 지속되며 데미지를 모두 줘야 함
m_Info.m_nEndTime = nFireEndTime;
}
// { 데미지 계산
int elemental_type = m_Skill.GetSkillBase()->GetElementalType();
int nDamage = m_Skill.GetVar(1) + m_Skill.GetRequestedSkillLevel() * m_Skill.GetVar(2);
// }
AR_TIME t = GetArTime();
// 근처 유닛 리스트를 vTargetList에 긁어오고, 데미지 계산
std::vector< StructCreature * > vTargetList;
std::vector< StructCreature * >::iterator itTargetList;
nDamage = EnumSkillTargetsAndCalcDamage( GetPos(), GetLayer(), GetPos(), true, fDamageRange, -1, 0, nDamage,
true, pCaster, SkillBase::DISTRIBUTION_TYPE_DISTRIBUTE, m_Skill.GetVar(6), vTargetList );
// 데미지 주기
for( itTargetList = vTargetList.begin() ; itTargetList != vTargetList.end() ; ++itTargetList )
{
StructCreature* pDealTarget = (*itTargetList);
switch( m_Skill.GetSkillBase()->GetSkillEffectType() )
{
case SkillBase::EF_TRAP_MULTIPLE_PHYSICAL_DAMAGE:
{
StructCreature::_DAMAGE_INFO Damage = pDealTarget->DealPhysicalSkillDamage( pCaster, nDamage, (Elemental::Type)elemental_type, m_Skill.GetHitBonus( m_Skill.GetEnhance(), pCaster->GetLevel() - pDealTarget->GetLevel() ), m_Skill.GetCriticalBonus( m_Skill.GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_Skill.m_vResultList, SkillResult::DAMAGE, elemental_type, Damage, pDealTarget->GetHandle() );
}
break;
case SkillBase::EF_TRAP_MULTIPLE_MAGICAL_DAMAGE:
{
StructCreature::_DAMAGE_INFO Damage = pDealTarget->DealMagicalSkillDamage( pCaster, nDamage, (Elemental::Type)elemental_type, m_Skill.GetHitBonus( m_Skill.GetEnhance(), pCaster->GetLevel() - pDealTarget->GetLevel() ), m_Skill.GetCriticalBonus( m_Skill.GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_Skill.m_vResultList, SkillResult::MAGIC_DAMAGE, elemental_type, Damage, pDealTarget->GetHandle() );
}
break;
}
++m_Skill.m_nTargetCount;
// 확률로 상태이상 부여
if( XRandom( 0, 99 ) < m_Skill.GetProbabilityOnHit( m_Skill.GetRequestedSkillLevel() ) )
{
int nLevel = m_Skill.GetStateLevel( m_Skill.GetRequestedSkillLevel(), m_Skill.GetEnhance() );
AR_TIME nDuration = m_Skill.GetStateSecond( m_Skill.GetRequestedSkillLevel(), m_Skill.GetEnhance() );
StructState::StateCode nStateCode = static_cast< StructState::StateCode >(m_Skill.GetStateId());
(*itTargetList)->AddState( nStateCode, pCaster->GetHandle(), nLevel, t, t + nDuration );
AddSkillResult( &m_Skill.m_vResultList, true, SkillResult::ADD_STATE, (*itTargetList)->GetHandle() );
++m_Skill.m_nTargetCount;
}
}
}