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

9571 lines
318 KiB
C++

#include <mmo/ArcadiaServer.h>
#include <toolkit/XEnv.h>
#include <toolkit/XConsole.h>
#include <toolkit/XRandom.h>
#include <logging/FileLog.h>
#include <toolkit/XSTLUtil.h>
#include "ErrorCode/ErrorCode.h"
#include "LogClient/LogClient.h"
#include "PartyManager.h"
#include "GuildManager.h"
#include "StructSkill.h"
#include "StructBase.h"
#include "GameContent.h"
#include "GameProc.h"
#include "NPCProc.h"
#include "GameAllocator.h"
#include "GameMessage.h"
#include "SendMessage.h"
#include "StructMonster.h"
#include "StructPlayer.h"
#include "StructSummon.h"
#include "StructPet.h"
#include "StructSkillProp.h"
#include "ChannelManager.h"
#include "DungeonManager.h"
#include "HuntaholicManager.h"
#include "InstanceDungeonManager.h"
#include "BattleArenaManager.h"
#include "StructNPC.h"
#include "StructFieldProp.h"
#include "DB_Commands.h"
#include "StructWorldLocation.h"
#include "Extern.h"
const int PREDICTION_AIMING_TIME = 200; // 예측사격
StructSkill * StructSkill::AllocSkill( struct StructCreature * pOwner, int uid, int id )
{
StructSkill *pObj = allocSkillStruct();
if( !pObj )
return NULL;
new (pObj) StructSkill( pOwner, uid, id );
return pObj;
}
void StructSkill::FreeSkill()
{
prepareFreeSkillStruct( this );
StructSkill::~StructSkill();
freeSkillStruct( this );
}
// Add Skill Result Functions
inline void AddSkillDamageResult( std::vector< SkillResult > * pvList, unsigned char type, char damage_type, const StructCreature::_DAMAGE_INFO & damage_info, AR_HANDLE handle )
{
SkillResult skill_result;
skill_result.damage.type = type;
skill_result.damage.damage_type = damage_type;
skill_result.damage.flag = 0;
if( damage_info.bCritical ) skill_result.damage.flag |= SkillResult::CRITICAL;
if( damage_info.bBlock ) skill_result.damage.flag |= SkillResult::BLOCK;
if( damage_info.bMiss ) skill_result.damage.flag |= SkillResult::MISS;
if( damage_info.bPerfectBlock) skill_result.damage.flag |= SkillResult::PERFECT_BLOCK;
skill_result.damage.damage = damage_info.nDamage;
skill_result.damage.hTarget = handle;
skill_result.damage.target_hp = damage_info.target_hp;
skill_result.damage.elemental_damage[0] = damage_info.elemental_damage[0];
skill_result.damage.elemental_damage[1] = damage_info.elemental_damage[1];
skill_result.damage.elemental_damage[2] = damage_info.elemental_damage[2];
skill_result.damage.elemental_damage[3] = damage_info.elemental_damage[3];
skill_result.damage.elemental_damage[4] = damage_info.elemental_damage[4];
skill_result.damage.elemental_damage[5] = damage_info.elemental_damage[5];
skill_result.damage.elemental_damage[6] = damage_info.elemental_damage[6];
pvList->push_back( skill_result );
}
inline void AddSkillResult( std::vector< SkillResult > * pvList, bool bIsSuccess, int nSuccessType , AR_HANDLE handle )
{
SkillResult skill_result;
skill_result.result.type = SkillResult::RESULT;
skill_result.result.bResult = bIsSuccess;
skill_result.result.hTarget = handle;
skill_result.result.success_type = nSuccessType;
pvList->push_back( skill_result );
}
inline void AddSkillDamageWithKnockBackResult( std::vector< SkillResult > * pvList, unsigned char type, char damage_type, const StructCreature::_DAMAGE_INFO & damage_info, AR_HANDLE handle, AR_UNIT x, AR_UNIT y, AR_TIME knock_back_time )
{
SkillResult skill_result;
skill_result.damage_kb.type = type;
skill_result.damage_kb.damage_type = damage_type;
skill_result.damage_kb.flag = 0;
if( damage_info.bCritical ) skill_result.damage_kb.flag |= SkillResult::CRITICAL;
if( damage_info.bBlock ) skill_result.damage_kb.flag |= SkillResult::BLOCK;
if( damage_info.bMiss ) skill_result.damage_kb.flag |= SkillResult::MISS;
if( damage_info.bPerfectBlock) skill_result.damage_kb.flag |= SkillResult::PERFECT_BLOCK;
skill_result.damage_kb.damage = damage_info.nDamage;
skill_result.damage_kb.hTarget = handle;
skill_result.damage_kb.target_hp = damage_info.target_hp;
skill_result.damage_kb.elemental_damage[0] = damage_info.elemental_damage[0];
skill_result.damage_kb.elemental_damage[1] = damage_info.elemental_damage[1];
skill_result.damage_kb.elemental_damage[2] = damage_info.elemental_damage[2];
skill_result.damage_kb.elemental_damage[3] = damage_info.elemental_damage[3];
skill_result.damage_kb.elemental_damage[4] = damage_info.elemental_damage[4];
skill_result.damage_kb.elemental_damage[5] = damage_info.elemental_damage[5];
skill_result.damage_kb.elemental_damage[6] = damage_info.elemental_damage[6];
skill_result.damage_kb.x = x;
skill_result.damage_kb.y = y;
skill_result.damage_kb.knock_back_time = knock_back_time;
skill_result.damage_kb.speed = GameRule::DEFAULT_KNOCK_BACK_SPEED;
pvList->push_back( skill_result );
}
inline void AddChainSkillDamageResult( std::vector< SkillResult > * pvList, unsigned char type, char damage_type, const StructCreature::_DAMAGE_INFO & damage_info, AR_HANDLE handle, AR_HANDLE from_handle )
{
SkillResult skill_result;
skill_result.damage.type = type;
skill_result.damage.damage_type = damage_type;
skill_result.damage.flag = 0;
if( damage_info.bCritical ) skill_result.damage.flag |= SkillResult::CRITICAL;
if( damage_info.bBlock ) skill_result.damage.flag |= SkillResult::BLOCK;
if( damage_info.bMiss ) skill_result.damage.flag |= SkillResult::MISS;
if( damage_info.bPerfectBlock) skill_result.damage.flag |= SkillResult::PERFECT_BLOCK;
skill_result.damage.damage = damage_info.nDamage;
skill_result.damage.hTarget = handle;
skill_result.damage.target_hp = damage_info.target_hp;
skill_result.damage.elemental_damage[0] = damage_info.elemental_damage[0];
skill_result.damage.elemental_damage[1] = damage_info.elemental_damage[1];
skill_result.damage.elemental_damage[2] = damage_info.elemental_damage[2];
skill_result.damage.elemental_damage[3] = damage_info.elemental_damage[3];
skill_result.damage.elemental_damage[4] = damage_info.elemental_damage[4];
skill_result.damage.elemental_damage[5] = damage_info.elemental_damage[5];
skill_result.damage.elemental_damage[6] = damage_info.elemental_damage[6];
skill_result.chain_damage.hFrom = from_handle;
pvList->push_back( skill_result );
}
struct SKILL_TARGET_FUNCTOR
{
virtual bool onCreature( StructSkill* pSkill, AR_TIME t, struct StructCreature* pCaster, struct StructCreature* pTarget ) = 0;
};
static int chip_limit_level[8] = { 20, 50, 80, 100, 120, 150, 180, 200 };
struct STATE_SKILL_FUNCTOR : public SKILL_TARGET_FUNCTOR
{
STATE_SKILL_FUNCTOR( std::vector< SkillResult > * p, StructState::StateCode _nForcedCode = StructState::StateCode( 0 ) ) : pvList( p ), nForcedCode( _nForcedCode ) {}
bool onCreature( StructSkill* pSkill, AR_TIME t, struct StructCreature* pCaster, struct StructCreature* pTarget )
{
bool bResult = true;
// 나쁜 상태이상 걸때는 확률체크 한다
if( pSkill->GetSkillBase()->IsHarmful() )
{
if( pSkill->GetSkillId() == StructSkill::SKILL_FORCE_CHIP || pSkill->GetSkillId() == StructSkill::SKILL_SOUL_CHIP || pSkill->GetSkillId() == StructSkill::SKILL_LUNA_CHIP )
{
// 포스칩 소울칩 우선 예외처리
if( chip_limit_level[ pSkill->GetRequestedSkillLevel() - 1 ] < pTarget->GetLevel() )
{
int ratio = 100 - ( pTarget->GetLevel() - chip_limit_level[ pSkill->GetRequestedSkillLevel() - 1 ] ) * 10;
if( ratio < XRandom() % 100 ) bResult = false;
}
}
else if( pSkill->GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ADD_STATE ||
pSkill->GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ADD_REGION_STATE ||
pSkill->GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ADD_STATE_BY_SELF_COST ||
pSkill->GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ADD_REGION_STATE_BY_SELF_COST ||
pSkill->GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ADD_STATE_BY_TARGET_TYPE ||
pSkill->GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ADD_STATES_WITH_EACH_DIFF_LV ||
pSkill->GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ADD_STATES_WITH_EACH_DIFF_LV_DURATION ||
pSkill->GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ADD_STATE_STEP_BY_STEP ||
pSkill->GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ADD_STATE_TO_CASTER_AND_TARGET ||
pSkill->GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ADD_RANDOM_STATE ||
pSkill->GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ADD_RANDOM_REGION_STATE )
{
int accuracy_bonus = 0;
accuracy_bonus = pSkill->GetHitBonus( pSkill->GetEnhance(), pCaster->GetLevel() - pTarget->GetLevel() );
// 실패~
if( 50 + pCaster->GetMagicAccuracy() - pTarget->GetMagicAvoid() + accuracy_bonus < XRandom()%100 ) bResult = false;
}
else
{
if( XRandom() % 100 > pSkill->GetProbabilityOnHit( pSkill->GetRequestedSkillLevel() ) ) bResult = false;
}
if( bResult && XRandom() % 100 < pTarget->GetResistStateRate( static_cast< StructState::StateCode >( pSkill->GetStateId() ) ) * 100 )
{
bResult = false;
}
}
if( bResult )
{
AR_TIME end_time = 0;
int nEndTime = pSkill->GetStateSecond( pSkill->GetRequestedSkillLevel(), pSkill->GetEnhance() );
// TODO : 일단 - 인 시간은 1분으로 고정
// -1이면 오오라
if( nEndTime / 100 == -1 ) end_time = AR_TIME(-1);
if( nEndTime < 0 ) end_time = t + 6000;
else end_time = t + nEndTime;
if( pSkill->GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ADD_STATE ||
pSkill->GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ADD_REGION_STATE ||
pSkill->GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ADD_STATE_BY_SELF_COST ||
pSkill->GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ADD_REGION_STATE_BY_SELF_COST ||
pSkill->GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ADD_STATE_TO_CASTER_AND_TARGET )
{
int count = 5;
if( pSkill->GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ADD_STATE_BY_SELF_COST ||
pSkill->GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ADD_REGION_STATE_BY_SELF_COST )
count = 0;
int nLevel = pSkill->GetStateLevel( pSkill->GetRequestedSkillLevel(), pSkill->GetEnhance() );
for( int i = -1; i < count; ++i )
{
StructState::StateCode code;
if( i < 0 ) code = (StructState::StateCode)pSkill->GetStateId();
else code = (StructState::StateCode)( (int)pSkill->GetVar( i ) );
if( !code ) continue;
bResult = pTarget->AddState( code, pCaster->GetHandle(), nLevel, t, end_time ) == RESULT_SUCCESS;
}
if( pSkill->GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ADD_STATE_TO_CASTER_AND_TARGET )
{
for( int i = 0; i < count; ++i )
{
StructState::StateCode code = (StructState::StateCode)( (int)pSkill->GetVar( i + count ) );
if( !code ) continue;
bResult = pCaster->AddState( code, pCaster->GetHandle(), nLevel, t, end_time ) == RESULT_SUCCESS;
}
}
}
else if( pSkill->GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ADD_STATE_BY_TARGET_TYPE )
{
int count = 5;
// 계열별 지속효과 부여 스킬이므로 지정 계열에 속하는지 체크
bResult = false;
for( int i = -1 ; i < count ; ++i )
{
if( pTarget->GetCreatureGroup() == pSkill->GetVar( 7 + i ) )
{
bResult = true;
break;
}
}
if( bResult )
{
int nLevel = pSkill->GetStateLevel( pSkill->GetRequestedSkillLevel(), pSkill->GetEnhance() );
for( int i = -1 ; i < count ; ++i )
{
StructState::StateCode code;
if( i < 0 ) code = (StructState::StateCode)pSkill->GetStateId();
else code = (StructState::StateCode)( (int)pSkill->GetVar( i ) );
if( !code ) continue;
bResult = pTarget->AddState( code, pCaster->GetHandle(), nLevel, t, end_time ) == RESULT_SUCCESS;
}
}
}
else if( pSkill->GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ADD_STATES_WITH_EACH_DIFF_LV )
{
int count = 3;
for( int i = -1 ; i < count ; ++i )
{
StructState::StateCode code = (StructState::StateCode)0;
int nLevel = 0;
if( i < 0 )
{
code = (StructState::StateCode)pSkill->GetStateId();
nLevel = pSkill->GetStateLevel( pSkill->GetRequestedSkillLevel(), pSkill->GetEnhance() );
}
else
{
code = (StructState::StateCode)( (int)pSkill->GetVar( i ) );
nLevel = pSkill->GetVar( 3 + ( i * 3 ) ) + ( pSkill->GetVar( 3 + ( i * 3 ) + 1 ) * pSkill->GetRequestedSkillLevel() ) + ( pSkill->GetVar( 3 + ( i * 3 ) + 2 ) * pSkill->GetEnhance() );
}
if( !code || !nLevel ) continue;
bResult = pTarget->AddState( code, pCaster->GetHandle(), nLevel, t, end_time ) == RESULT_SUCCESS;
}
}
else if( pSkill->GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ADD_STATES_WITH_EACH_DIFF_LV_DURATION )
{
for( int i = 0 ; i < 2 ; ++i )
{
StructState::StateCode code = (StructState::StateCode)0;
int nLevel = 0;
if( i < 0 )
{
code = (StructState::StateCode)pSkill->GetStateId();
nLevel = pSkill->GetStateLevel( pSkill->GetRequestedSkillLevel(), pSkill->GetEnhance() );
}
else
{
code = (StructState::StateCode)((int)pSkill->GetVar(0));
nLevel = pSkill->GetVar(1) + ( pSkill->GetVar(2) * pSkill->GetRequestedSkillLevel() ) + ( pSkill->GetVar(3) * pSkill->GetEnhance() );
end_time = t + 100 * ( pSkill->GetVar(4) + ( pSkill->GetVar(5) * pSkill->GetRequestedSkillLevel() ) + ( pSkill->GetVar(6) * pSkill->GetEnhance() ) );
}
if( !code || !nLevel || end_time <= t ) continue;
bResult = pTarget->AddState( code, pCaster->GetHandle(), nLevel, t, end_time ) == RESULT_SUCCESS;
}
}
else if( pSkill->GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ADD_STATE_STEP_BY_STEP )
{
bResult = false;
end_time = t + 100 * ( pSkill->GetVar(15) + pSkill->GetVar(16) * pSkill->GetRequestedSkillLevel() + pSkill->GetVar(17) * pSkill->GetEnhance() );
int i;
for( i = 9 ; i >= 0; i-=3 )
if( pSkill->GetVar(i) != 0 )
break;
for( i-=3; i >= 0; i-=3 )
{
// 순차적이므로 데이터가 무조건 차 있어야 한다.
if( pSkill->GetVar( i ) == 0 ) assert( 0 );
StructState::StateCode code = static_cast< StructState::StateCode >( static_cast< int >( pSkill->GetVar(i) ) );
if( pTarget->GetState( code ) )
{
if( pSkill->GetVar(i+3) == 0 ) assert( 0 );
if( pSkill->GetVar(i+1) + pSkill->GetVar(i+2) * pSkill->GetRequestedSkillLevel() > XRandom( 0, 99 ) )
{
bResult = pTarget->AddState( static_cast< StructState::StateCode >( static_cast< int >( pSkill->GetVar(i+3) ) ), pCaster->GetHandle(), pSkill->GetRequestedSkillLevel(), t, end_time ) == RESULT_SUCCESS;
if( bResult ) pTarget->RemoveState( code, GameRule::MAX_STATE_LEVEL );
}
else
bResult = false;
break;
}
}
// 어떠한 버프도 안 걸려있다면 첫번째로 부여될 버프를 건다.
if( i < 0 )
{
if( pSkill->GetVar(1) + pSkill->GetVar(2) * pSkill->GetRequestedSkillLevel() > XRandom( 0, 99 ) )
bResult = pTarget->AddState( static_cast< StructState::StateCode >( static_cast< int >( pSkill->GetVar(0) ) ),
pCaster->GetHandle(), pSkill->GetRequestedSkillLevel(), t, end_time ) == RESULT_SUCCESS;
else
bResult = false;
}
}
else if( pSkill->GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ADD_RANDOM_STATE ||
pSkill->GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ADD_RANDOM_REGION_STATE )
{
StructState::StateCode code = static_cast< StructState::StateCode >( pSkill->GetStateId() );
int nLevel = pSkill->GetStateLevel( pSkill->GetRequestedSkillLevel(), pSkill->GetEnhance() );
if( nForcedCode )
{
code = nForcedCode;
}
else
{
int count;
for( count = 6; count >= 2; --count ) if( pSkill->GetVar( count - 2 ) ) break;
int index = XRandom( -1, count - 2 );
if( index != -1 ) code = static_cast< StructState::StateCode >( static_cast< int >( pSkill->GetVar(index) ) );
}
bResult = pTarget->AddState( code, pCaster->GetHandle(), nLevel, t, end_time ) == RESULT_SUCCESS;
}
else if( pSkill->GetSkillBase()->GetSkillEffectType() == SkillBase::EF_MAGIC_SINGLE_DAMAGE_ADD_RANDOM_STATE || pSkill->GetSkillBase()->GetSkillEffectType() == SkillBase::EF_MAGIC_SINGLE_REGION_DAMAGE_ADD_RANDOM_STATE)
{
std::vector< int > vStateID;
vStateID.reserve( 7 );
vStateID.push_back( pSkill->GetStateId() );
for( int i = 0 ; i < 6 ; ++i )
{
if( pSkill->GetVar( 6 + i ) )
vStateID.push_back( pSkill->GetVar( 6 + i ) );
}
if( !vStateID.empty() )
{
StructState::StateCode code = static_cast< StructState::StateCode >( vStateID[ XRandom( 0, static_cast< int >( vStateID.size() ) - 1 ) ] );
bResult = pTarget->AddState( code,
pCaster->GetHandle(), pSkill->GetStateLevel( pSkill->GetRequestedSkillLevel(), pSkill->GetEnhance() ),
t, end_time ) == RESULT_SUCCESS;
}
}
else
{
if( pSkill->GetSkillBase()->GetEffectType() != SkillBase::EF_TOGGLE_AURA &&
pSkill->GetSkillBase()->GetEffectType() != SkillBase::EF_TOGGLE_DIFFERENTIAL_AURA )
{
// 아래 유형의 스킬들은 자기 자신에게는 지속효과를 걸면 안 됨
// 흡수 등을 위해 스킬 타겟에 자신이 포함되지만 지속효과의 부여 대상은 아닌 경우
if( pTarget != pCaster || (
pSkill->GetSkillBase()->GetSkillEffectType() != SkillBase::EF_PHYSICAL_ABSORB_DAMAGE &&
pSkill->GetSkillBase()->GetSkillEffectType() != SkillBase::EF_MAGIC_ABSORB_DAMAGE_OLD &&
pSkill->GetSkillBase()->GetSkillEffectType() != SkillBase::EF_MAGIC_DAMAGE_WITH_ABSORB_HP_MP &&
pSkill->GetSkillBase()->GetSkillEffectType() != SkillBase::EF_ADD_HP_MP_BY_ABSORB_HP_MP &&
pSkill->GetSkillBase()->GetSkillEffectType() != SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_ABSORB ) )
{
bResult = pTarget->AddState( (StructState::StateCode)pSkill->GetStateId(),
pCaster->GetHandle(), pSkill->GetStateLevel( pSkill->GetRequestedSkillLevel(), pSkill->GetEnhance() ),
t, end_time ) == RESULT_SUCCESS;
}
}
}
if( pSkill->GetSkillId() != StructSkill::SKILL_FORCE_CHIP && pSkill->GetSkillId() != StructSkill::SKILL_SOUL_CHIP && pSkill->GetSkillId() != StructSkill::SKILL_LUNA_CHIP )
{
if( pTarget->IsMonster() && pTarget->IsEnemy( pCaster, true ) )
{
StructMonster * pMonster = static_cast< StructMonster *>( pTarget );
pMonster->AddHate( pCaster->GetHandle(), 1 );
}
else if( pTarget->IsNPC() && pTarget->IsEnemy( pCaster ) )
{
StructNPC * pNPC = static_cast< StructNPC *>( pTarget );
pNPC->SetAttacker( pCaster );
}
}
}
AddSkillResult( pvList, bResult, SkillResult::ADD_STATE, pTarget->GetHandle() );
return true;
}
std::vector< SkillResult > * pvList;
StructState::StateCode nForcedCode;
};
struct REMOVE_GOOD_STATE_SKILL_FUNCTOR : public SKILL_TARGET_FUNCTOR
{
REMOVE_GOOD_STATE_SKILL_FUNCTOR( std::vector< SkillResult > * p ) : pvList( p ) {}
bool onCreature( StructSkill* pSkill, AR_TIME t, struct StructCreature* pCaster, struct StructCreature* pTarget )
{
int nStateLevel = pSkill->GetVar(1) * pSkill->GetRequestedSkillLevel() + pSkill->GetVar(2) * pSkill->GetEnhance();
int bResult = true;
int accuracy_bonus = pSkill->GetVar(9) + pSkill->GetVar(10) * pSkill->GetRequestedSkillLevel();
// 실패~
if( accuracy_bonus <= XRandom()%100 ) bResult = false;
if( pSkill->GetVar( 8 ) )
{
if( bResult ) pTarget->RemoveGoodState( nStateLevel );
AddSkillResult( pvList, bResult, SkillResult::REMOVE_STATE, pTarget->GetHandle() );
}
else
{
if( bResult ) pTarget->RemoveState( (StructState::StateCode)((int)pSkill->GetVar(0)), nStateLevel );
AddSkillResult( pvList, bResult, SkillResult::REMOVE_STATE, pTarget->GetHandle() );
for( int nIndex = 3 ; nIndex < 8 && pSkill->GetVar(nIndex) ; nIndex++ )
{
if( bResult ) pTarget->RemoveState( (StructState::StateCode)(int)pSkill->GetVar(nIndex), nStateLevel );
AddSkillResult( pvList, bResult, SkillResult::REMOVE_STATE, pTarget->GetHandle() );
}
}
return true;
}
std::vector< SkillResult > * pvList;
};
struct REMOVE_BAD_STATE_SKILL_FUNCTOR : public SKILL_TARGET_FUNCTOR
{
REMOVE_BAD_STATE_SKILL_FUNCTOR( std::vector< SkillResult > * p ) : pvList( p ) {}
bool onCreature( StructSkill* pSkill, AR_TIME t, struct StructCreature* pCaster, struct StructCreature* pTarget )
{
int nStateLevel = pSkill->GetVar(1) + pSkill->GetVar(2) * pSkill->GetRequestedSkillLevel() + pSkill->GetVar(3) * pSkill->GetEnhance();
pTarget->RemoveState( (StructState::StateCode)(int)pSkill->GetVar(0), nStateLevel );
AddSkillResult( pvList, true, SkillResult::REMOVE_STATE, pTarget->GetHandle() );
for( int nIndex = 4 ; nIndex < 9 && pSkill->GetVar(nIndex) ; nIndex++ )
{
pTarget->RemoveState( (StructState::StateCode)(int)pSkill->GetVar(nIndex), nStateLevel );
AddSkillResult( pvList, true, SkillResult::REMOVE_STATE, pTarget->GetHandle() );
}
return true;
}
std::vector< SkillResult > * pvList;
};
struct REMOVE_STATE_GROUP_SKILL_FUNCTOR : public SKILL_TARGET_FUNCTOR
{
REMOVE_STATE_GROUP_SKILL_FUNCTOR( std::vector< SkillResult > * p, StructCreature * _pOwner ) : pvList( p ), pOwner( _pOwner ), nGroupID( 0 ) {}
bool onCreature( StructSkill* pSkill, AR_TIME t, struct StructCreature* pCaster, struct StructCreature* pTarget )
{
int nRatio = pSkill->GetVar(0) + pSkill->GetVar(1) * pSkill->GetRequestedSkillLevel() + pSkill->GetVar(10) * pSkill->GetEnhance();
if( XRandom( 1, 100 ) > nRatio )
return false;
int nRemoveCount = pSkill->GetVar(3) + pSkill->GetVar(4) * pSkill->GetRequestedSkillLevel() + pSkill->GetVar(11) * pSkill->GetEnhance();
if( nRemoveCount <= 0 )
return false;
int nGroupList[5] = { pSkill->GetVar(5), pSkill->GetVar(6), pSkill->GetVar(7), pSkill->GetVar(8), pSkill->GetVar(9) };
typedef std::pair< StructState::StateCode, AR_TIME > REMOVE_INFO;
std::vector< REMOVE_INFO > vRemoveInfoList;
const std::vector< StructState > &vStateList = pTarget->GetStateList();
for( std::vector< StructState >::const_iterator it = vStateList.begin() ; it != vStateList.end() && nRemoveCount > static_cast< int >( vRemoveInfoList.size() ) ; ++it )
{
if( ( nGroupList[0] && (*it).GetStateGroup() == nGroupList[0] ) || ( nGroupList[1] && (*it).GetStateGroup() == nGroupList[1] ) || ( nGroupList[2] && (*it).GetStateGroup() == nGroupList[2] ) ||
( nGroupList[3] && (*it).GetStateGroup() == nGroupList[3] ) || ( nGroupList[4] && (*it).GetStateGroup() == nGroupList[4] ) )
{
if( (*it).GetTimeType() & StateInfo::ERASE_ON_DEAD )
vRemoveInfoList.push_back( REMOVE_INFO( (*it).GetCode(), (*it).GetEndTime() ) );
}
}
if( nRemoveCount < static_cast< int >( vRemoveInfoList.size() ) )
{
if( pSkill->GetVar(2) == 0 )
{
std::random_shuffle( vRemoveInfoList.begin(), vRemoveInfoList.end() );
}
else if( pSkill->GetVar(2) == 1 )
{
struct RemoveInfoCompare
{
bool operator()( REMOVE_INFO &RemoveInfo1, REMOVE_INFO &RemoveInfo2 )
{
return RemoveInfo2.second > RemoveInfo1.second;
}
};
std::sort( vRemoveInfoList.begin(), vRemoveInfoList.end(), RemoveInfoCompare() );
}
else if( pSkill->GetVar(2) == 2 )
{
struct RemoveInfoCompare
{
bool operator()( REMOVE_INFO &RemoveInfo1, REMOVE_INFO &RemoveInfo2 )
{
return RemoveInfo2.second <= RemoveInfo1.second;
}
};
std::sort( vRemoveInfoList.begin(), vRemoveInfoList.end(), RemoveInfoCompare() );
}
vRemoveInfoList.erase( vRemoveInfoList.begin() + nRemoveCount, vRemoveInfoList.end() );
}
for( std::vector< REMOVE_INFO >::iterator rit = vRemoveInfoList.begin() ; rit != vRemoveInfoList.end() ; ++rit )
{
pTarget->RemoveState( (*rit).first );
}
AR_TIME t = GetArTime();
AR_TIME end_time = 0;
int nEndTime = pSkill->GetStateSecond( pSkill->GetRequestedSkillLevel(), pSkill->GetEnhance() );
if( nEndTime < 0 ) end_time = t + 6000;
else end_time = t + nEndTime;
if( pSkill->GetStateId() )
{
pTarget->AddState( (StructState::StateCode)pSkill->GetStateId(),
pOwner->GetHandle(),
pSkill->GetStateLevel( pSkill->GetRequestedSkillLevel(), pSkill->GetEnhance() ),
t, end_time, true );
}
for( int i = 12; i < 15; ++i )
{
if( pSkill->GetVar(i) )
{
pTarget->AddState( (StructState::StateCode) (int) pSkill->GetVar(i),
pOwner->GetHandle(),
pSkill->GetStateLevel( pSkill->GetRequestedSkillLevel(), pSkill->GetEnhance() ),
t, end_time, true );
}
}
return true;
}
std::vector< SkillResult > * pvList;
StructCreature * pOwner;
int nGroupID;
};
struct HEALING_SKILL_FUNCTOR : public SKILL_TARGET_FUNCTOR
{
HEALING_SKILL_FUNCTOR( std::vector< SkillResult > * p, bool bIsByItem = false ) : pvList( p ), m_bIsByItem( bIsByItem ), nResult( 0 ) {}
bool onCreature( StructSkill* pSkill, AR_TIME t, struct StructCreature* pCaster, struct StructCreature* pTarget )
{
int slv = pSkill->GetRequestedSkillLevel();
nResult =
pCaster->GetMagicPoint( (Elemental::Type)pSkill->GetSkillBase()->GetElementalType(), pSkill->GetSkillBase()->IsPhysicalSkill(), pSkill->GetSkillBase()->IsHarmful() ) * ( pSkill->GetVar(0) + pSkill->GetVar(1) * slv )
+ pSkill->GetVar(2) + pSkill->GetVar(3) * slv
+ pSkill->GetVar(6) * pSkill->GetEnhance()
+ pTarget->GetMaxHP() * ( pSkill->GetVar(4) + pSkill->GetVar(5) * slv + pSkill->GetVar(7) * pSkill->GetEnhance() );
if( pTarget->IsDead() ) nResult = 0;
else if( m_bIsByItem )
nResult = pTarget->HealByItem( nResult );
else
nResult = pTarget->Heal( nResult );
SkillResult skill_result;
skill_result.add_hp.type = SkillResult::ADD_HP;
skill_result.add_hp.hTarget = pTarget->GetHandle();
skill_result.add_hp.nIncHP = nResult;
skill_result.add_hp.target_hp = pTarget->GetHP();
pvList->push_back( skill_result );
return true;
}
std::vector< SkillResult > * pvList;
int nResult;
bool m_bIsByItem;
};
struct RECOVERY_MP_SKILL_FUNCTOR : public SKILL_TARGET_FUNCTOR
{
RECOVERY_MP_SKILL_FUNCTOR( std::vector< SkillResult > * p, bool bIsByItem = false ) : pvList( p ), m_bIsByItem( bIsByItem ), nResult( 0 ) {}
bool onCreature( StructSkill* pSkill, AR_TIME t, struct StructCreature* pCaster, struct StructCreature* pTarget )
{
int slv = pSkill->GetRequestedSkillLevel();
int enhance = pSkill->GetEnhance();
nResult =
pCaster->GetMagicPoint((Elemental::Type)pSkill->GetSkillBase()->GetElementalType(), pSkill->GetSkillBase()->IsPhysicalSkill(), pSkill->GetSkillBase()->IsHarmful() ) * ( pSkill->GetVar(0) + slv*pSkill->GetVar(1) )
+ pSkill->GetVar(2) + slv*pSkill->GetVar(3) + enhance*pSkill->GetVar( 6 ) +
+ pTarget->GetMaxMP() * ( pSkill->GetVar(4) + slv*pSkill->GetVar(5) + enhance*pSkill->GetVar(7) );
if( pTarget->IsDead() ) nResult = 0;
else if( m_bIsByItem )
nResult = pTarget->MPHealByItem( nResult );
else
nResult = pTarget->MPHeal( nResult );
SkillResult skill_result;
skill_result.add_hp.type = SkillResult::ADD_MP;
skill_result.add_hp.hTarget = pTarget->GetHandle();
skill_result.add_hp.nIncHP = nResult;
skill_result.add_hp.target_hp = pTarget->GetMP();
pvList->push_back( skill_result );
return true;
}
std::vector< SkillResult > * pvList;
int nResult;
bool m_bIsByItem;
};
// Struct Skill Members
StructSkill::StructSkill( struct StructCreature* pOwner, int uid, int id )
{
m_pOwner = pOwner;
m_fRushFace = 0;
m_nRushDamage = 0;
m_targetLayer = 0;
m_nSkillUID = uid;
m_nSkillId = id;
m_nEnhance = 0;
m_nSkillLevel= 0;
m_nSkillLevelAdd = 0;
m_bNeedUpdateToDB = false;
m_bMultiple = false;
m_hCastItem = 0;
m_nAuraMPDecTime = 0;
m_nNextCoolTime = 0;
m_nDBCoolTime = 0;
m_nAuraRefreshTime = 0;
m_Modifier = NULL;
bindSkillInfo( id );
Init();
#ifdef _MEM_USAGE_DEBUG
XSEH::IncreaseAllocCount( "StructSkill" );
#endif
}
StructSkill::~StructSkill()
{
#ifdef _MEM_USAGE_DEBUG
XSEH::DecreaseAllocCount( "StructSkill" );
#endif
}
StructSkill::StructSkill( const StructSkill & rhs )
{
m_pOwner = rhs.m_pOwner;
m_nSkillUID = rhs.m_nSkillUID;
m_nSkillId = rhs.m_nSkillId;
m_fRushFace = 0;
m_nRushDamage = 0;
m_nEnhance = rhs.m_nEnhance;
m_nSkillLevel = rhs.m_nSkillLevel;
m_nSkillLevelAdd = rhs.m_nSkillLevelAdd;
m_pSkillBase = rhs.m_pSkillBase;
m_bMultiple = rhs.m_bMultiple;
m_hCastItem = rhs.m_hCastItem;
m_nRequestedSkillLevel = rhs.m_nRequestedSkillLevel;
m_bNeedUpdateToDB = false;
m_nAuraMPDecTime = 0;
m_nNextCoolTime = 0;
m_nDBCoolTime = 0;
m_nErrorCode = RESULT_SUCCESS;
m_Status = STATUS_IDLE;
m_nCastTime = 0;
m_nCastingDelay = 0;
m_nFireTime = 0;
m_nCurrentFire = 0;
m_nTotalFire = 0;
m_nTargetCount = 1;
m_nFireCount = 1;
m_Modifier = NULL;
#ifdef _MEM_USAGE_DEBUG
XSEH::IncreaseAllocCount( "StructSkill", false );
#endif
}
const bool StructSkill::Cancel()
{
if( IsPassiveSkill() ) return false;
switch( m_Status )
{
case STATUS_CAST:
{
if( GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ACTIVATE_FIELD_PROP ||
GetSkillBase()->GetSkillEffectType() == SkillBase::EF_REGION_HEAL_BY_FIELD_PROP ||
GetSkillBase()->GetSkillEffectType() == SkillBase::EF_AREA_EFFECT_HEAL_BY_FIELD_PROP )
{
StructFieldProp::iterator it = StructFieldProp::get( m_hTarget );
StructFieldProp * pProp = static_cast< StructFieldProp * >( *it );
if( pProp )
pProp->CancelCast();
}
broadcastSkillMessage( m_pOwner->GetRX(), m_pOwner->GetRY(), m_pOwner->GetLayer(), 0, 0, TS_SC_SKILL::CANCEL );
break;
}
case STATUS_COMPLETE:
case STATUS_FIRE:
{
if( GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ACTIVATE_FIELD_PROP ||
GetSkillBase()->GetSkillEffectType() == SkillBase::EF_REGION_HEAL_BY_FIELD_PROP ||
GetSkillBase()->GetSkillEffectType() == SkillBase::EF_AREA_EFFECT_HEAL_BY_FIELD_PROP )
{
StructFieldProp::iterator it = StructFieldProp::get( m_hTarget );
StructFieldProp * pProp = static_cast< StructFieldProp * >( *it );
if( pProp )
pProp->CancelCast();
}
broadcastSkillMessage( m_pOwner->GetRX(), m_pOwner->GetRY(), m_pOwner->GetLayer(), 0, 0, TS_SC_SKILL::COMPLETE );
break;
}
default:
{
assert( 0 );
break;
}
}
Init();
// 펫 삽질의 경우 삽질 상태 초기화
if( m_pOwner->IsPet() && GetSkillId() == SKILL_SHOVELING )
{
static_cast< StructPet * >( m_pOwner )->SetShovelingStatus( StructPet::SHOVELING_STATUS_IDLE );
BroadcastStatusMessage( m_pOwner );
}
return true;
}
const bool StructSkill::IsRandomSkill() const
{
// 현재 이 루틴은 소환수에만 적용됨 (일반 캐릭터는 랜덤 스킬을 배울 수 없음.)
if( !m_pOwner->IsSummon() )
return false;
// 크리쳐가 진화를 한 후 강화를 하면 일반형으로 돌아오는 데 이때 예전 진화했을 때 배웠던 스킬을 기억하려면
// 이 크리쳐가 진화가능한 3종의 종류 스킬트리를 다 찾아야 한다.
StructSummon * pSummon = static_cast < StructSummon * >( m_pOwner );
int SummonID[3];
if( pSummon->GetTransformLevel() == SummonBase::EVOLVE_NORMAL )
{
SummonID[0] = pSummon->GetJobId();
SummonID[1] = pSummon->GetEvolveTarget();
SummonID[2] = GameContent::GetSummonInfo( pSummon->GetEvolveTarget() )->evolve_target;
}
else if( pSummon->GetTransformLevel() == SummonBase::EVOLVE_GROWTH )
{
SummonID[0] = pSummon->GetPrevJobId( 0 );
SummonID[1] = pSummon->GetJobId();
SummonID[2] = pSummon->GetEvolveTarget();
}
else if( pSummon->GetTransformLevel() == SummonBase::EVOLVE_EVOLVE )
{
SummonID[0] = pSummon->GetPrevJobId( 0 );
SummonID[1] = pSummon->GetPrevJobId( 1 );
SummonID[2] = pSummon->GetJobId();
}
else
{
_cprint( "Invalid Summon Info on Summon[%d]\n", pSummon->GetSummonCode() );
FILELOG( "Invalid Summon Info on Summon[%d]", pSummon->GetSummonCode() );
}
// 일반, 성장, 진화형에 대한 스킬트리를 전체 검사
for( int i = 0 ; i < SummonBase::EVOLVE_EVOLVE ; ++i )
{
SummonBase *pBase = GameContent::GetSummonInfo( SummonID[i] );
if( !pBase )
continue;
for( int j = 0 ; j < GameRule::MAX_SUMMON_SKILL_TREE ; ++j )
{
if( !pBase->skill_tree[j] )
continue;
for( std::vector< SkillTree >::iterator it = (*pBase->skill_tree[j]).begin() ; it != (*pBase->skill_tree[j]).end() ; ++it )
{
if( it->skill_group_id )
{
for( int i = 0 ; i < GameRule::MAX_RANDOM_SKILL_COUNT ; ++i )
{
int nSkillID = GameContent::GetRandomSkillIDFromGroupID( it->skill_group_id, i );
if( !nSkillID )
break;
if( nSkillID == m_nSkillId )
return true;
}
}
}
}
}
return false;
}
AR_TIME StructSkill::GetSkillCoolTime() const
{
// AziaMafia Instant Skill in location
StructPlayer* pPlayer = static_cast<StructPlayer*>(m_pOwner);
if (//town
(pPlayer->GetLocationId() == 90401
)
//skill
&&
(GetSkillBase()->GetID() == 4001
|| GetSkillBase()->GetID() == 4002
)
)
return 0;
AR_TIME nCoolTime = GetCoolTime( GetRequestedSkillLevel(), ( m_pOwner->IsSummon() ) ? GetRequestedSkillLevel() : GetEnhance() );
nCoolTime *= m_pOwner->GetCoolTimeMod( (Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
return nCoolTime * m_pOwner->GetCoolTimeSpeed();
}
void StructSkill::assembleMessage( struct TS_SC_SKILL *pResultMsg, int buf_len, int nType, int cost_hp, int cost_mp )
{
char *pResultData = reinterpret_cast< char* >( pResultMsg+1 );
buf_len -= sizeof( *pResultMsg );
pResultMsg->type = nType;
pResultMsg->layer = m_targetLayer;
pResultMsg->x = m_targetPos.x;
pResultMsg->y = m_targetPos.y;
pResultMsg->z = m_targetPos.z;
pResultMsg->caster = m_pOwner->GetHandle();
pResultMsg->skill_id = GetSkillId();
pResultMsg->skill_level = GetRequestedSkillLevel();
pResultMsg->target = m_hTarget;
pResultMsg->hp_cost = cost_hp;
pResultMsg->mp_cost = cost_mp;
pResultMsg->caster_hp = m_pOwner->GetHP();
pResultMsg->caster_mp = m_pOwner->GetMP();
switch( nType )
{
case TS_SC_SKILL::CASTING:
case TS_SC_SKILL::CASTING_UPDATE:
{
pResultMsg->cast.tm = GetCastingTime();
pResultMsg->cast.nErrorCode = m_nErrorCode;
break;
}
case TS_SC_SKILL::CANCEL:
case TS_SC_SKILL::COMPLETE:
// 할 것 없음
break;
case TS_SC_SKILL::FIRE:
case TS_SC_SKILL::REGION_FIRE:
{
pResultMsg->fire.bMultiple = m_bMultiple;
pResultMsg->fire.target_count = m_nTargetCount;
pResultMsg->fire.fire_count = m_nFireCount;
pResultMsg->fire.count = static_cast< unsigned short >( m_vResultList.size() );
pResultMsg->fire.range = m_fRange;
if( pResultMsg->fire.count )
{
std::vector< SkillResult >::iterator it;
for( it = m_vResultList.begin(); it != m_vResultList.end(); ++it )
{
switch( (*it).GetType() )
{
case SkillResult::DAMAGE:
case SkillResult::MAGIC_DAMAGE:
s_memcpy( pResultData, buf_len, &(*it), sizeof( SkillResult::DamageType ) );
pResultData += sizeof( SkillResult::DamageType );
buf_len -= sizeof( SkillResult::DamageType );
break;
case SkillResult::DAMAGE_WITH_KNOCK_BACK:
s_memcpy( pResultData, buf_len, &(*it), sizeof( SkillResult::DamageWithKnockBackType ) );
pResultData += sizeof( SkillResult::DamageWithKnockBackType );
buf_len -= sizeof( SkillResult::DamageWithKnockBackType );
break;
case SkillResult::RUSH:
s_memcpy( pResultData, buf_len, &(*it), sizeof( SkillResult::RushType ) );
pResultData += sizeof( SkillResult::RushType );
buf_len -= sizeof( SkillResult::RushType );
break;
case SkillResult::RESULT:
s_memcpy( pResultData, buf_len, &(*it), sizeof( SkillResult::ResultType ) );
pResultData += sizeof( SkillResult::ResultType );
buf_len -= sizeof( SkillResult::ResultType );
break;
case SkillResult::ADD_HP:
case SkillResult::ADD_MP:
s_memcpy( pResultData, buf_len, &(*it), sizeof( SkillResult::AddHPType ) );
pResultData += sizeof( SkillResult::AddHPType );
buf_len -= sizeof( SkillResult::AddHPType );
break;
case SkillResult::REBIRTH:
s_memcpy( pResultData, buf_len, &(*it), sizeof( SkillResult::RebirthType ) );
pResultData += sizeof( SkillResult::RebirthType );
buf_len -= sizeof( SkillResult::RebirthType );
break;
case SkillResult::ADD_HP_MP_SP:
s_memcpy( pResultData, buf_len, &(*it), sizeof( SkillResult::AddHPMPSPType ) );
pResultData += sizeof( SkillResult::AddHPMPSPType );
buf_len -= sizeof( SkillResult::AddHPMPSPType );
break;
case SkillResult::CHAIN_DAMAGE:
case SkillResult::CHAIN_MAGIC_DAMAGE:
s_memcpy( pResultData, buf_len, &(*it), sizeof( SkillResult::ChainDamageType ) );
pResultData += sizeof( SkillResult::ChainDamageType );
buf_len -= sizeof( SkillResult::ChainDamageType );
break;
case SkillResult::CHAIN_HEAL:
s_memcpy( pResultData, buf_len, &(*it), sizeof( SkillResult::ChainDamageType ) );
pResultData += sizeof( SkillResult::ChainHealType );
buf_len -= sizeof( SkillResult::ChainHealType );
break;
}
}
}
pResultMsg->size += sizeof(SkillResult)*pResultMsg->fire.count;
break;
};
}
}
void StructSkill::SendSkillCastFailMessage( ArObject * pObject, AR_HANDLE caster, AR_HANDLE target, int skill_id, char skill_lv, const ArPosition & pos, int error_code )
{
TS_SC_SKILL msg;
msg.type = TS_SC_SKILL::CASTING;
msg.layer = 0;
msg.x = pos.x;
msg.y = pos.y;
msg.z = pos.z;
msg.caster = caster;
msg.skill_id = skill_id;
msg.skill_level = skill_lv;
msg.target = target;
msg.hp_cost = 0;
msg.mp_cost = 0;
msg.caster_hp = 0;
msg.caster_mp = 0;
msg.cast.tm = 0;
msg.cast.nErrorCode = error_code;
PendMessage( pObject, &msg );
}
void StructSkill::sendSkillMessage( int nType )
{
if( !m_pOwner->IsPlayer() )
return;
char *pBuf = NULL;
char buf[1024];
int buf_len = 0;
if( sizeof(SkillResult) * 1 + sizeof( TS_SC_SKILL ) < sizeof( buf ) )
{
pBuf = buf;
buf_len = sizeof( buf );
}
else
{
buf_len = sizeof(SkillResult) * 1 + sizeof( TS_SC_SKILL );
pBuf = new char[ buf_len ];
}
TS_SC_SKILL *pResultMsg = new (buf) TS_SC_SKILL();
assembleMessage( pResultMsg, buf_len, nType, 0, 0 );
PendMessage( m_pOwner, pResultMsg );
if( pBuf != buf )
delete [] pBuf;
}
void StructSkill::broadcastSkillMessage( int rx, int ry, unsigned char layer, int cost_hp, int cost_mp, int nType )
{
char *pBuf = NULL;
char szBuf[ SENDMSG_BUFFER_SIZE ] = "";
int buf_len = 0;
if( sizeof( SkillResult ) * m_vResultList.size() + sizeof( TS_SC_SKILL ) < SENDMSG_BUFFER_SIZE )
{
pBuf = szBuf;
buf_len = sizeof( szBuf );
}
else
{
buf_len = sizeof( SkillResult ) * static_cast< int >( m_vResultList.size() ) + sizeof( TS_SC_SKILL );
pBuf = new char[ buf_len ];
}
TS_SC_SKILL *pResultMsg = new (pBuf) TS_SC_SKILL();
assembleMessage( pResultMsg, buf_len, nType, cost_hp, cost_mp );
ArcadiaServer::Instance().Broadcast( rx, ry, layer, pResultMsg );
if( pBuf != szBuf )
delete [] pBuf;
}
void StructSkill::broadcastSkillMessage( int rx1, int ry1, int rx2, int ry2, unsigned char layer, int cost_hp, int cost_mp, int nType )
{
char *pBuf = NULL;
char szBuf[ SENDMSG_BUFFER_SIZE ] = "";
int buf_len = 0;
if( sizeof( SkillResult ) * m_vResultList.size() + sizeof( TS_SC_SKILL ) < SENDMSG_BUFFER_SIZE )
{
pBuf = szBuf;
buf_len = sizeof( szBuf );
}
else
{
buf_len = sizeof( SkillResult ) * static_cast< int >( m_vResultList.size() ) + sizeof( TS_SC_SKILL );
pBuf = new char[ buf_len ];
}
TS_SC_SKILL *pResultMsg = new (pBuf) TS_SC_SKILL();
assembleMessage( pResultMsg, buf_len, nType, cost_hp, cost_mp );
ArcadiaServer::Instance().Broadcast( rx1, ry1, rx2, ry2, layer, pResultMsg );
if( pBuf != szBuf )
delete [] pBuf;
}
void StructSkill::broadcastSkillMessage( int rx, int ry, unsigned int range, unsigned char layer, int cost_hp, int cost_mp, int nType )
{
char *pBuf = NULL;
char szBuf[ SENDMSG_BUFFER_SIZE ] = "";
int buf_len = 0;
if( sizeof( SkillResult ) * m_vResultList.size() + sizeof( TS_SC_SKILL ) < SENDMSG_BUFFER_SIZE )
{
pBuf = szBuf;
buf_len = sizeof( szBuf );
}
else
{
buf_len = sizeof( SkillResult ) * static_cast< int >( m_vResultList.size() ) + sizeof( TS_SC_SKILL );
pBuf = new char[ buf_len ];
}
TS_SC_SKILL *pResultMsg = new (pBuf) TS_SC_SKILL();
assembleMessage( pResultMsg, buf_len, nType, cost_hp, cost_mp );
ArcadiaServer::Instance().BroadcastToSpecificRegion( rx, ry, range, layer, pResultMsg );
if( pBuf != szBuf )
delete [] pBuf;
}
int StructSkill::Cast( int nSkillLevel, AR_HANDLE handle, const ArPosition & pos, unsigned char layer, AR_HANDLE hCastItem )
{
assert( m_Status == STATUS_IDLE );
AR_TIME current_time = GetArTime();
AR_TIME delay = -1;
SetRequestedSkillLevel( std::min( nSkillLevel, GetCurrentSkillLevel() ) );
m_Status = STATUS_CAST;
m_hCastItem = hCastItem;
int mana_cost = 0;
do
{
// 스킬 쿨타임 체크
if( !CheckCoolTime( current_time ) && !m_hCastItem )
{
m_nErrorCode = RESULT_COOL_TIME;
break;
}
if( GetSkillBase()->IsNeedWeapon() )
{
// 스킬에 적합한 무기인지?
if( IsUseableWeapon( ItemBase::CLASS_SHIELD ) )
{
if( !m_pOwner->IsWearShield() )
{
m_nErrorCode = RESULT_LIMIT_WEAPON;
break;
}
}
else if( !IsUseableWeapon( m_pOwner->GetWeaponClass() ) )
{
m_nErrorCode = RESULT_LIMIT_WEAPON;
break;
}
}
// 테이밍 관련 체크
if( GetSkillId() == SKILL_CREATURE_TAMING || GetSkillId() == SKILL_ITEM_CREATURE_TAMING_SCROLL || GetSkillId() == SKILL_PET_TAMING )
{
if( !m_pOwner->IsPlayer() )
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
StructCreature::iterator itMonster = StructCreature::get( handle );
StructCreature * pCreature = (*itMonster);
if( !pCreature || !pCreature->IsMonster() )
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
StructMonster * pMonster = static_cast< StructMonster * >( pCreature );
// 테이밍 안되는 녀석
ItemBase::ItemCode nTameItemCode = pMonster->GetTameItemCode();
if( !nTameItemCode )
{
m_nErrorCode = RESULT_NOT_TAMABLE;
break;
}
// 이미 테이밍 되어 있으면 KIN
if( pMonster->GetTamer() )
{
m_nErrorCode = RESULT_TARGET_ALREADY_BEING_TAMED;
break;
}
// 체력이 100%가 아닌 대상은 테이밍 불가
if( pMonster->GetHP() != pMonster->GetMaxHP() )
{
m_nErrorCode = RESULT_NOT_ENOUGH_TARGET_HP;
break;
}
if( GetSkillId() == SKILL_CREATURE_TAMING || GetSkillId() == SKILL_ITEM_CREATURE_TAMING_SCROLL )
{
// 빈 카드 없으면 테이밍 불가
if( !static_cast< StructPlayer * >( m_pOwner )->FindEmptySummonCard( nTameItemCode ) )
{
if( pMonster->IsMonsterCreatureTame() )
m_nErrorCode = RESULT_NOT_ENOUGH_SOUL_TAMING_CARD;
else
m_nErrorCode = RESULT_NOT_ENOUGH_SUMMON_CARD;
break;
}
// 이미 다른 몬스터를 테이밍 중일 경우 테이밍 불가
AR_HANDLE hTamingTarget = static_cast< StructPlayer * >( m_pOwner )->GetTamingTarget();
if( hTamingTarget )
{
m_nErrorCode = RESULT_ALREADY_TAMING;
break;
}
}
else if( GetSkillId() == SKILL_PET_TAMING )
{
// 환경 몬스터만 가능
if( !pMonster->IsEnvironmentMonster() )
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
// 펫으로 테이밍 가능 환경 몬스터에게만 가능
if( !pMonster->IsEnvironmentMonster() || !nTameItemCode || ( !pMonster->IsMonsterCreatureTame() && !pMonster->GetTameCode() )
|| StructItem::GetItemBase( nTameItemCode ).nGroup != ItemBase::GROUP_PET_CAGE )
{
m_nErrorCode = RESULT_NOT_TAMABLE;
break;
}
// 테이밍 가능 우리 보유 체크
if( !static_cast< StructPlayer * >( m_pOwner )->FindEmptyPetCage( nTameItemCode ) )
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
}
}
else if( GetSkillId() == SKILL_SHOVELING )
{
if( !m_pOwner->IsPet() )
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
}
else if( GetSkillBase()->GetSkillEffectType() == SkillBase::EF_SUMMON )
{
if( !m_pOwner->IsPlayer() || static_cast< StructPlayer * >( m_pOwner )->GetSubSummon() )
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
if( !static_cast< StructPlayer * >( m_pOwner )->IsSummonable() )
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
// { 소환시 처리 해줄게 좀 있네.-_-;
StructItem *pItem = StructItem::FindItem( handle );
// 카드가 없거나 카드의 내구도가 다 함
if( !pItem || !pItem->IsSummonCard() || pItem->GetOwnerHandle() != m_pOwner->GetHandle() || ( pItem->GetItemEnhance() && !pItem->GetCurrentEtherealDurability() ) )
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
if( m_pOwner->IsPlayer() )
{
StructPlayer * pPlayer = static_cast< StructPlayer * >( m_pOwner );
bool bFind = false;
for( int i = 0; i < 6; ++i )
{
if( pItem == pPlayer->GetSummonCardAt( i ) )
{
bFind = true;
break;
}
}
if( !bFind )
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
}
StructSummon *pSummon = pItem->GetSummonStruct();
if( !pSummon )
{
m_nErrorCode = RESULT_NOT_EXIST;
break;
}
if( pSummon->IsInWorld() )
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
ArPosition pos = m_pOwner->GetCurrentPosition( GetArTime() );
pSummon->SetCurrentXY( pos.x, pos.y );
pSummon->SetCurrentLayer( m_pOwner->GetLayer() );
pSummon->StopMove();
int prevRX = GetRegionX( pos.x );
int prevRY = GetRegionX( pos.y );
int nPosPickCount = 0;
do
{
// ArObject::AddNoise 함수를 사용하지 않고 고정 위치(pos) 기준 사각형 내부(pos +- StructSummon::SUMMON_SPAWN_LENGTH)에서 랜덤하게 좌표 추출
// * 대부분의 내용은 AddNoise 함수와 유사하게 동작하지만 랜덤하게 이동된 위치에서 다시 추가 이동하지 않고 원점 기준으로 반복해서 좌표를 추출한다는 것만 다름
m_targetPos.x = pos.x + ( XRandom() % ( StructSummon::SUMMON_SPAWN_LENGTH * 2 ) ) - StructSummon::SUMMON_SPAWN_LENGTH;
// 지역락 때문에 RX가 바뀌지 않는게 가장 좋지만 진입 불가 영역이 현재 Region에 너무 크게 걸쳐있으면 소환이 거의 불가능하게 되기 때문에
// 1칸 까지는 이동을 허용함(VisibleRange 락 걸 때 2칸 여유 주기 때문에 일단 괜찮을 듯)
if( abs( prevRX - (int)GetRegionX( m_targetPos.x ) ) > 1 )
m_targetPos.x = pos.x;
m_targetPos.y = pos.y + ( XRandom() % ( StructSummon::SUMMON_SPAWN_LENGTH * 2 ) ) - StructSummon::SUMMON_SPAWN_LENGTH;
// 지역락 때문에 RX가 바뀌지 않는게 가장 좋지만 진입 불가 영역이 현재 Region에 너무 크게 걸쳐있으면 소환이 거의 불가능하게 되기 때문에
// 1칸 까지는 이동을 허용함(VisibleRange 락 걸 때 2칸 여유 주기 때문에 일단 괜찮을 듯)
if( abs( prevRY - (int)GetRegionY( m_targetPos.y ) ) > 1 )
m_targetPos.y = pos.y;
++nPosPickCount;
}
while( (
( pos.x == m_targetPos.x && pos.y == m_targetPos.y ) || // 캐릭터랑 완전히 겹쳤으면 다시 좌표 선정
//pos.GetDistance( m_targetPos ) < StructSummon::SUMMON_SPAWN_MIN_LENGTH || // 2미터 이상 떨어져서 나오게 한다.
GameContent::CollisionToLine( pos.x, pos.y, m_targetPos.x, m_targetPos.y ) // 주인 시야에 들어와야 한다.
) &&
nPosPickCount < 10 ); // 좌표 선정 재시도는 10번까지만
// 유효한 좌표를 100번 이내에 선정하지 못했다면 주인하고 겹침
if( nPosPickCount >= 10 )
{
m_targetPos = pos;
}
pSummon->SetCurrentXY( m_targetPos.x, m_targetPos.y );
m_targetLayer = pSummon->GetLayer();
// }
}
else if( GetSkillBase()->GetSkillEffectType() == SkillBase::EF_UNSUMMON ||
GetSkillBase()->GetSkillEffectType() == SkillBase::EF_UNSUMMON_AND_ADD_STATE )
{
if( !m_pOwner->IsPlayer() || !static_cast< StructPlayer * >( m_pOwner )->GetMainSummon() )
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
}
// 헌터홀릭 입장 가능 조건 체크가 필요한 스킬
else if( GetSkillId() == StructSkill::SKILL_WARP_TO_HUNTAHOLIC_LOBBY )
{
m_nErrorCode = IsHuntaholicLobbyEnterableOwner( GetTargetHuntaholicID() );
if( m_nErrorCode != RESULT_SUCCESS )
break;
}
// 데스매치 입장 가능 조건 체크가 필요한 스킬
else if( GetSkillId() == StructSkill::SKILL_RANKED_DEATHMATCH_ENTER || GetSkillId() == StructSkill::SKILL_FREED_DEATHMATCH_ENTER )
{
m_nErrorCode = IsInstanceGameEnterableOwner();
if( m_nErrorCode != RESULT_SUCCESS )
break;
}
// 보유 루피를 체크 해야하는 스킬
else if( GetSkillBase()->GetSkillEffectType() == SkillBase::EF_LOTTO )
{
StructPlayer *pPlayer = NULL;
if( m_pOwner->IsPlayer() )
pPlayer = static_cast< StructPlayer * >(m_pOwner);
else if( m_pOwner->IsSummon() )
pPlayer = static_cast< StructSummon * >(m_pOwner)->GetMaster();
if( !pPlayer || pPlayer->GetGold() < StructGold( GetVar( 0 ) + GetVar( 1 ) * GetRequestedSkillLevel() ) )
{
m_nErrorCode = RESULT_NOT_ENOUGH_MONEY;
break;
}
}
// 대상 몬스터의 그룹을 가려서 사용해야 하는 스킬
else if( GetSkillBase()->GetSkillEffectType() == SkillBase::EF_MAGIC_SINGLE_DAMAGE_OR_DEATH )
{
StructCreature::iterator it = StructCreature::get( handle );
if( !(*it) || !(*it)->IsMonster() )
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
StructMonster *pTarget = static_cast< StructMonster * >(*it);
// Skill 모든 계열
if( GameRule::USE_SKILL_ALL_OBJECT != GetVar( 8 ) )
{
if( GetVar( 8 ) != pTarget->GetCreatureGroup() )
{
m_nErrorCode = RESULT_LIMIT_RACE;
break;
}
}
// 보스 몬스터에겐 100% 실패
if( pTarget->IsBossMonster() )
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
bool bIsInvalidTarget = false;
if( GetVar( 9 ) != 0 && GetVar( 9 ) + GetVar( 16 ) * GetRequestedSkillLevel() > (float)pTarget->GetHP()/pTarget->GetMaxHP()*100 ) bIsInvalidTarget = true;
if( GetVar( 10 ) != 0 && GetVar( 10 ) + GetVar( 17 ) * GetRequestedSkillLevel() < (float)pTarget->GetHP()/pTarget->GetMaxHP()*100 ) bIsInvalidTarget = true;
for( int i = 11 ; i < 15 && !bIsInvalidTarget ; ++i )
{
if( GetVar( i ) == 0 ) continue;
if( !pTarget->GetState( StructState::StateCode( static_cast< int >( GetVar( i ) ) ) ) )
{
bIsInvalidTarget = true;
break;
}
}
if( bIsInvalidTarget )
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
}
// 대상의 크리처 그룹을 가려서 사용해야 하는 스킬(여러 종류 그룹 가능)
else if( GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ADD_STATE_BY_TARGET_TYPE )
{
StructCreature::iterator it = StructCreature::get( handle );
if( !(*it) )
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
int nTargetCreatureGroup = (*it)->GetCreatureGroup();
bool bIsInvalidTarget = true;
for( int i = -1 ; i < 5 ; ++i )
{
if( nTargetCreatureGroup == GetVar( i + 7 ) || nTargetCreatureGroup == CREATURE_ALL )
{
bIsInvalidTarget = false;
break;
}
}
if( bIsInvalidTarget )
{
m_nErrorCode = RESULT_LIMIT_RACE;
break;
}
}
// 방패 공격
else if( GetSkillBase()->GetSkillEffectType() == SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_WITH_SHIELD )
{
if( !m_pOwner->IsWearShield() )
break;
}
else if( GetSkillBase()->GetSkillEffectType() == SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_RUSH_KNOCKBACK_OLD ||
GetSkillBase()->GetSkillEffectType() == SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_WITHOUT_WEAPON_RUSH_KNOCK_BACK ||
GetSkillBase()->GetSkillEffectType() == SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_RUSH ||
GetSkillBase()->GetSkillEffectType() == SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_RUSH_KNOCKBACK )
{
StructCreature::iterator it = StructCreature::get( handle );
if( !(*it) )
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
ArPosition myPos = m_pOwner->GetCurrentPosition( current_time );
ArPosition targetPos = (*it)->GetCurrentPosition( current_time );
AR_UNIT nMinDistance;
switch( GetSkillBase()->GetSkillEffectType() )
{
case SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_RUSH_KNOCKBACK_OLD:
case SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_WITHOUT_WEAPON_RUSH_KNOCK_BACK:
nMinDistance = GetVar(3) * GameRule::DEFAULT_UNIT_SIZE;
break;
case SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_RUSH:
case SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_RUSH_KNOCKBACK:
nMinDistance = GetVar(4) * GameRule::DEFAULT_UNIT_SIZE;
break;
default:
nMinDistance = 0;
break;
}
if( myPos.GetDistance( targetPos ) < nMinDistance )
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
if( GameRule::bMonsterPathFinding && GameContent::CollisionToLine( myPos.x, myPos.y, targetPos.x, targetPos.y ) )
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
}
else if( GetSkillBase()->GetSkillEffectType() == SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_RUSH_KNOCKBACK_REGION )
{
ArPosition myPos = m_pOwner->GetCurrentPosition( current_time );
AR_UNIT nMinDistance = GetSkillBase()->GetCastRange() * GameRule::DEFAULT_UNIT_SIZE;
if (myPos.GetDistance( pos ) > nMinDistance)
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
if( GameContent::CollisionToLine( myPos.x, myPos.y, pos.x, pos.y ) )
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
}
else if( GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ACTIVATE_FIELD_PROP ||
GetSkillBase()->GetSkillEffectType() == SkillBase::EF_REGION_HEAL_BY_FIELD_PROP ||
GetSkillBase()->GetSkillEffectType() == SkillBase::EF_AREA_EFFECT_HEAL_BY_FIELD_PROP )
{
if( !m_pOwner->IsPlayer() )
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
StructFieldProp::iterator it = StructFieldProp::get( handle );
if( !(*it) )
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
if( !(*it)->IsFieldProp() )
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
StructFieldProp * pProp = static_cast< StructFieldProp * >( *it );
if( pProp->GetFieldPropBase()->nActivateSkillId != GetSkillId() )
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
if( pProp->GetRemainCount() < 1 )
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
if( !pProp->IsCastable( static_cast< StructPlayer * >( m_pOwner ) ) )
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
delay = pProp->GetCastingDelay();
}
// 특정 지속효과가 타겟에게 걸려 있어야만 사용할 수 있는 스킬
else if( GetSkillBase()->GetSkillEffectType() == SkillBase::EF_MAGIC_SINGLE_DAMAGE_BY_CONSUMING_TARGETS_STATE ||
GetSkillBase()->GetSkillEffectType() == SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_BY_CONSUMING_TARGETS_STATE )
{
StructCreature::iterator it = StructCreature::get( handle );
StructCreature * pTarget = (*it);
if( !pTarget )
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
bool bCastable = false;
for( int i = 0 ; i < 4 ; ++i )
{
if( pTarget->GetState( StructState::StateCode( static_cast< int >( GetVar( i ) ) ) ) )
{
bCastable = true;
break;
}
}
if( !bCastable )
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
}
// 몬스터가 아니면 사용할 수 없는 스킬(몬스터 전용)
else if( GetSkillBase()->GetSkillEffectType() == SkillBase::EF_RESPAWN_MONSTER_NEAR )
{
if( !m_pOwner->IsMonster() )
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
}
else if( GetSkillBase()->GetSkillEffectType() == SkillBase::EF_RESPAWN_MONSTER_RANDOMLY || GetSkillBase()->GetSkillEffectType() == SkillBase::EF_RESPAWN_MONSTER_WITH_DIFF_CODE )
{
if( m_pOwner->IsPlayer() && reinterpret_cast< StructPlayer * >( m_pOwner )->IsInBattleField() )
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
}
int nHP = m_pOwner->GetHP();
int nMP = m_pOwner->GetMP();
int nEnergy = m_pOwner->GetEnergyCount();
int nCurrentHPPercentage = m_pOwner->GetMaxHP() ? nHP * 100 / m_pOwner->GetMaxHP() : 0;
int nCurrentMPPercentage = m_pOwner->GetMaxMP() ? nMP * 100 / m_pOwner->GetMaxMP() : 0;
if( ( GetSkillBase()->GetEffectType() == SkillBase::EF_TOGGLE_AURA ||
GetSkillBase()->GetEffectType() == SkillBase::EF_TOGGLE_DIFFERENTIAL_AURA ) && m_pOwner->IsActiveAura( this ) )
{
mana_cost = 0;
}
else
{
mana_cost = CalculateMPCost( m_pOwner->GetSkillCostModifier() );
}
int nNeedHP = GetNeedHP();
if( nNeedHP > 0 )
{
if( nCurrentHPPercentage < nNeedHP ) { m_nErrorCode = RESULT_NOT_ENOUGH_HP; break; }
}
else if( nNeedHP < 0 )
{
if( nCurrentHPPercentage > 0 - nNeedHP ) { m_nErrorCode = RESULT_NOT_ACTABLE; break; }
}
if( nCurrentMPPercentage < GetNeedMP() ) { m_nErrorCode = RESULT_NOT_ENOUGH_MP; break; }
if( nHP - 1 < CalculateHPCost( m_pOwner->GetSkillCostModifier() ) ) { m_nErrorCode = RESULT_NOT_ENOUGH_HP; break; }
if( nMP < mana_cost ) { m_nErrorCode = RESULT_NOT_ENOUGH_MP; break; }
if( nEnergy < static_cast< int >( GetCostEnergy( GetRequestedSkillLevel() ) ) ) { m_nErrorCode = RESULT_NOT_ENOUGH_ENERGY; break; }
if( m_pOwner->GetLevel() < GetNeedLevel() ) { m_nErrorCode = RESULT_NOT_ENOUGH_LEVEL; break; }
if( m_pOwner->GetJobPoint() < GetCostJP(GetRequestedSkillLevel(), GetEnhance() ) ) { m_nErrorCode = RESULT_NOT_ENOUGH_JP; break; }
int nCostEXP = GetCostEXP( GetRequestedSkillLevel(), GetEnhance() );
if( nCostEXP )
{
if( m_pOwner->GetEXP() < nCostEXP ) { m_nErrorCode = RESULT_NOT_ENOUGH_EXP; break; }
__int64 nMinimumEXP = GameContent::GetNeedExp( m_pOwner->GetLevel() - 1 );
if( m_pOwner->GetEXP() - nCostEXP < nMinimumEXP ) { m_nErrorCode = RESULT_NOT_ENOUGH_EXP; break; }
}
StructCreature::iterator itTarget = StructCreature::get( handle );
StructCreature *pTarget = (*itTarget);
// 시전 대상이 소모해야 할 수치를 따로 지정하는 스킬에 대해서 체크
if( GetSkillBase()->GetEffectType() == SkillBase::EF_ADD_STATE_BY_SELF_COST || GetSkillBase()->GetEffectType() == SkillBase::EF_ADD_REGION_STATE_BY_SELF_COST )
{
float fCostMP = 0.0f;
float fCostHP = GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(2) * GetEnhance();
float fCostSP = GetVar(3) + GetVar(4) * GetRequestedSkillLevel() + GetVar(5) * GetEnhance();
float fCostEnergy = GetVar(6) + GetVar(7) * GetRequestedSkillLevel() + GetVar(8) * GetEnhance();
if( GetSkillBase()->GetEffectType() == SkillBase::EF_ADD_STATE_BY_SELF_COST )
fCostMP = GetVar(9) + GetVar(10) * GetRequestedSkillLevel() + GetVar(11) * GetEnhance();
if( fCostEnergy > GameRule::ENERGY_MAX )
fCostEnergy = GameRule::ENERGY_MAX;
// 사실 이거 스킬DB에서 제어해서 타겟 없으면 들어오지도 않음..
if( !pTarget || !pTarget->IsSummon() )
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
// 이 유형들의 소모 수치들은 시전 대상이 지정된 수치를 가지고 있는지를 체크
if( fCostHP && pTarget->GetHP() < fCostHP ) { m_nErrorCode = RESULT_NOT_ENOUGH_HP; break; }
if( fCostSP && static_cast< StructSummon * >(pTarget)->GetSP() < fCostSP ) { m_nErrorCode = RESULT_NOT_ENOUGH_SP; break; }
// 기공 소모는 -1이면 현재 1개라도 있기만 하면 모두 소모로 처리 됨
if( fCostEnergy > 0 && pTarget->GetEnergyCount() < fCostEnergy ) { m_nErrorCode = RESULT_NOT_ENOUGH_ENERGY; break; }
else if( fCostEnergy == -1 && pTarget->GetEnergyCount() == 0 ) { m_nErrorCode = RESULT_NOT_ENOUGH_ENERGY; break; }
if( fCostMP && pTarget->GetMP() < fCostMP ) { m_nErrorCode = RESULT_NOT_ENOUGH_MP; break; }
}
if( GetSkillBase()->GetSkillEffectType() == SkillBase::EF_MAGIC_MULTIPLE_DAMAGE_T1_DEAL_SUMMON_HP_OLD ||
GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ADD_HP_MP_BY_SUMMON_DAMAGE ||
GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ADD_HP_MP_BY_SUMMON_DEAD ||
GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ADD_HP_MP_BY_STEAL_SUMMON_HP_MP ||
GetSkillBase()->GetSkillEffectType() == SkillBase::EF_MAGIC_MULTIPLE_DAMAGE_DEAL_SUMMON_HP )
{
if( !m_pOwner->IsPlayer() ||
!static_cast< StructPlayer * >( m_pOwner )->GetMainSummon() )
{
m_nErrorCode = RESULT_NOT_ENOUGH_HP;
break;
}
int decHP = 0;
int decMP = 0;
if( GetSkillBase()->GetSkillEffectType() == SkillBase::EF_MAGIC_MULTIPLE_DAMAGE_T1_DEAL_SUMMON_HP_OLD )
decHP = GetVar( 6 ) + GetVar( 7 ) * GetRequestedSkillLevel() + GetVar( 8 ) * GetEnhance();
if( GetSkillBase()->GetSkillEffectType() == SkillBase::EF_MAGIC_MULTIPLE_DAMAGE_DEAL_SUMMON_HP )
decHP = GetVar( 9 ) + GetVar( 10 ) * GetRequestedSkillLevel() + GetVar( 11 ) * GetEnhance();
if( GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ADD_HP_MP_BY_SUMMON_DAMAGE )
decHP = static_cast< StructPlayer * >( m_pOwner )->GetMainSummon()->GetMaxHP() * GetVar( 10 );
if( GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ADD_HP_MP_BY_STEAL_SUMMON_HP_MP )
{
decHP = GetVar( 0 ) + GetVar( 1 ) * GetRequestedSkillLevel() + GetVar( 4 ) * GetEnhance();
decMP = GetVar( 2 ) + GetVar( 3 ) * GetRequestedSkillLevel() + GetVar( 5 ) * GetEnhance();
}
if( static_cast< StructPlayer * >( m_pOwner )->GetMainSummon()->GetHP() < decHP + 1 )
{
m_nErrorCode = RESULT_NOT_ENOUGH_HP;
break;
}
if( static_cast< StructPlayer * >( m_pOwner )->GetMainSummon()->GetMP() < decMP )
{
m_nErrorCode = RESULT_NOT_ENOUGH_MP;
break;
}
}
// 펫 삽질인 경우 Status 바꾸고 메시지 송신
if( m_pOwner->IsPet() && GetSkillId() == SKILL_SHOVELING )
{
static_cast< StructPet * >( m_pOwner )->SetShovelingStatus( StructPet::SHOVELING_STATUS_SEARCH );
BroadcastStatusMessage( m_pOwner );
}
// { 아이템 소모
ItemBase::ItemCode cost_item_code = GetCostItemCode();
StructPlayer *pInvenPlayer = NULL;
if( m_pOwner->IsPlayer() ) pInvenPlayer = static_cast< StructPlayer* >( m_pOwner );
if( m_pOwner->IsSummon() ) pInvenPlayer = static_cast< StructSummon* >( m_pOwner )->GetMaster();
// 화살 소모인경우
if( pInvenPlayer )
{
if( cost_item_code == ItemBase::ITEM_CODE_WEARED_BULLET )
{
/* AziaMafia Ammo
if (pInvenPlayer->GetWearedItem(ItemBase::WEAR_RIGHTHAND)->IsBow() ) // AziaMafia Crossbow no bullet skill
{
StructItem * pCostItem = NULL;
pCostItem = pInvenPlayer->GetWearedItem( ItemBase::WEAR_BULLET );
if( !pCostItem || !pCostItem->IsBullet() )
{
m_nErrorCode = RESULT_NOT_ENOUGH_BULLET;
break;
}
// 화살통이면 예상 소모 개수도 0
int nCostItemCount = 0;
if( pCostItem->GetItemClass() != ItemBase::CLASS_QUIVER )
nCostItemCount = GetCostItemCount( GetRequestedSkillLevel() );
if( !pCostItem || pCostItem->GetCount() < nCostItemCount )
{
m_nErrorCode = RESULT_NOT_ENOUGH_BULLET;
break;
}
}
*/
}
else if( cost_item_code )
{
StructItem * pCostItem = pInvenPlayer->FindItem( cost_item_code );
if( !pCostItem || pCostItem->GetCount() < GetCostItemCount( GetRequestedSkillLevel() ) )
{
{ m_nErrorCode = RESULT_NOT_ENOUGH_ITEM; break; }
}
}
if( GetSkillBase()->GetEffectType() == SkillBase::EF_ADD_STATE_BY_USING_ITEM )
{
if( GetRequestedSkillLevel() > 0 && GetRequestedSkillLevel() < 20 )
{
ItemBase::ItemCode requiredItemCode = GetVar( 0 );
__int64 requiredItemCount = GetVar( GetRequestedSkillLevel() );
StructItem* costItem = pInvenPlayer->FindItem( requiredItemCode );
if ( !costItem || requiredItemCount > costItem->GetCount() )
{
m_nErrorCode = RESULT_NOT_ENOUGH_ITEM;
break;
}
}
else
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
}
}
// }
if( GetNeedStateId() )
{
StructState * pState = m_pOwner->GetState( StructState::StateCode( GetNeedStateId() ) );
if( !pState || pState->GetLevel() < GetNeedStateLevel() )
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
if( NeedStateExhaust() == SkillBase::NEED_STATE_EXHAUSE_ALL )
{
m_pOwner->RemoveState( pState->GetCode() );
}
else if( NeedStateExhaust() == SkillBase::NEED_STATE_EXHAUSE_SPECIFIC )
{
m_pOwner->DecreaseState( pState->GetCode(), GetNeedStateLevel() );
}
}
if( GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ACTIVATE_FIELD_PROP ||
GetSkillBase()->GetSkillEffectType() == SkillBase::EF_REGION_HEAL_BY_FIELD_PROP ||
GetSkillBase()->GetSkillEffectType() == SkillBase::EF_AREA_EFFECT_HEAL_BY_FIELD_PROP )
{
StructFieldProp::iterator it = StructFieldProp::get( handle );
StructFieldProp * pProp = static_cast< StructFieldProp * >( *it );
if( !(*it) || !(*it)->IsFieldProp() || !IsVisibleRegion( m_pOwner->GetRX(), m_pOwner->GetRY(), (*it)->GetRX(), (*it)->GetRY() ) )
{
m_nErrorCode = RESULT_NOT_ACTABLE;
break;
}
pProp->Cast();
}
// 비용 소비
m_pOwner->SetMP( nMP - mana_cost * GameRule::SKILL_CAST_COST );
AR_TIME nOriginalCastingDelay = delay;
if( delay == -1 )
{
// 시전시간
nOriginalCastingDelay = delay = GetCastDelay( GetRequestedSkillLevel(), GetEnhance() );
// 시전속도 적용
// * 단, 아이템으로 시전된 스킬이면 시전 속도에 영향 안받음
if( GetSkillUID() >= 0 && !hCastItem )
{
delay /= ( std::max( m_pOwner->GetCastingSpeed(), 1 ) / 100.0f );
delay *= m_pOwner->GetCastingMod( (Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful(), nOriginalCastingDelay );
}
}
if( GetSkillBase()->GetSkillEffectType() != SkillBase::EF_SUMMON )
{
m_targetPos = pos;
m_targetLayer = layer;
}
m_hTarget = handle;
m_nCastTime = current_time;
m_nCastingDelay = nOriginalCastingDelay;
m_nFireTime = current_time + delay;
} while( false );
int nErrorCode = m_nErrorCode;
if( nErrorCode != RESULT_SUCCESS )
{
Init();
}
else
{
broadcastSkillMessage( m_pOwner->GetRX(), m_pOwner->GetRY(), m_pOwner->GetLayer(), 0, mana_cost * GameRule::SKILL_CAST_COST, TS_SC_SKILL::CASTING );
if( GetSkillBase()->IsHarmful() && !GetSkillBase()->IsPhysicalSkill() )
{
// 캐스트 반격
StructCreature::iterator itMonster = StructCreature::get( handle );
StructCreature * pCreature = (*itMonster);
if( pCreature && pCreature->IsMonster() && static_cast< StructMonster * >( pCreature )->IsCastRevenger() )
{
static_cast< StructMonster * >( pCreature )->AddHate( m_pOwner->GetHandle(), 1 );
}
}
}
return nErrorCode;
}
void StructSkill::Init()
{
m_nErrorCode = RESULT_SUCCESS;
m_Status = STATUS_IDLE;
m_nCastTime = 0;
m_nCastingDelay = 0;
m_nFireTime = 0;
m_nRequestedSkillLevel = 0;
m_hTarget = 0;
m_targetPos = ArPosition();
m_targetLayer = 0;
m_hCastItem = 0;
m_nCurrentFire = 0;
m_nTotalFire = 0;
m_nTargetCount = 1;
m_nFireCount = 1;
m_vResultList.clear();
}
void StructSkill::onDamage( int nDamage )
{
// 현재는 캐스트 캔슬/지연 지원
if( m_Status != STATUS_CAST )
return;
if( nDamage > m_pOwner->GetMaxHP() * GameRule::SKILL_CANCEL_RATE )
{
int nCastKeep = m_pOwner->GetCastKeep();
if( !nCastKeep )
nCastKeep = 100;
float fMod = ( 100 / nCastKeep ) * ( 1 + ( 0.4f * static_cast<int>( nDamage / m_pOwner->GetMaxHP() * 10 ) ) );
if( GetCastCancelType() == SkillBase::IT_DELAY )
{
AR_TIME delay = 100;
switch( GetCastCancelLevel() )
{
case SkillBase::IL_LOW: delay = GameRule::CASTING_DELAY_LOW; break;
case SkillBase::IL_MEDIUM: delay = GameRule::CASTING_DELAY_MEDIUM; break;
case SkillBase::IL_HIGH: delay = GameRule::CASTING_DELAY_HIGH; break;
}
// 딜레이~~!
delay *= fMod;
m_nFireTime += delay;
sendSkillMessage( TS_SC_SKILL::CASTING_UPDATE );
}
else if( GetCastCancelType() == SkillBase::IT_CANCEL )
{
int nCancelRate = 0;
switch( GetCastCancelLevel() )
{
case SkillBase::IL_LOW: nCancelRate = GameRule::CASTING_CANCEL_LOW; break;
case SkillBase::IL_MEDIUM: nCancelRate = GameRule::CASTING_CANCEL_MEDIUM; break;
case SkillBase::IL_HIGH: nCancelRate = GameRule::CASTING_CANCEL_HIGH; break;
}
nCancelRate *= fMod;
// 일정 확률로 캔슬~
if( XRandom() % 100 < nCancelRate ) m_pOwner->CancelSkill();
}
}
}
void StructSkill::bindSkillInfo( int skill_id )
{
m_pSkillBase = GameContent::GetSkillBase( skill_id );
int nEffectType = m_pSkillBase->GetEffectType();
if( nEffectType == SkillBase::EF_PHYSICAL_MULTIPLE_DAMAGE_T3 ||
nEffectType == SkillBase::EF_MAGIC_MULTIPLE_DAMAGE_T1_OLD ||
nEffectType == SkillBase::EF_MAGIC_MULTIPLE_DAMAGE_T2_OLD ||
nEffectType == SkillBase::EF_MAGIC_MULTIPLE_DAMAGE_T1_DEAL_SUMMON_HP_OLD ||
nEffectType == SkillBase::EF_MAGIC_MULTIPLE_REGION_DAMAGE_OLD ||
nEffectType == SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_WITHOUT_WEAPON_RUSH_KNOCK_BACK ||
nEffectType == SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_RUSH_KNOCKBACK_OLD ||
nEffectType == SkillBase::EF_PHYSICAL_MULTIPLE_DAMAGE_TRIPLE_ATTACK_OLD ||
// 신규 연타 유형 - 2006/10/12
nEffectType == SkillBase::EF_MAGIC_MULTIPLE_DAMAGE ||
nEffectType == SkillBase::EF_MAGIC_MULTIPLE_DAMAGE_DEAL_SUMMON_HP ||
nEffectType == SkillBase::EF_MAGIC_MULTIPLE_REGION_DAMAGE ||
// 신규 연타 유형 - 2007/03/24
nEffectType == SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_RUSH ||
nEffectType == SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_RUSH_KNOCKBACK ||
nEffectType == SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_RUSH_KNOCKBACK_REGION ||
nEffectType == SkillBase::EF_PHYSICAL_REALTIME_MULTIPLE_DAMAGE ||
// 신규 연타 유형 - 2007/05/09
nEffectType == SkillBase::EF_PHYSICAL_REALTIME_MULTIPLE_DAMAGE_KNOCKBACK ||
// 신규 연타 유형 - 2007/07/04
nEffectType == SkillBase::EF_PHYSICAL_REALTIME_MULTIPLE_REGION_DAMAGE
)
{
m_bMultiple = true;
}
}
bool StructSkill::CheckCoolTime( AR_TIME t ) const
{
if( GameRule::bIgnoreSkillCoolTime ) return true;
return t > m_nNextCoolTime;
}
void StructSkill::FireSkill( struct StructCreature * pTarget, bool bIsSuccess )
{
AR_TIME t = m_nFireTime;
m_vResultList.clear();
// 은신 해제
if( m_pOwner->IsHiding() )
{
m_pOwner->RemoveState( StructState::HIDE, GameRule::MAX_STATE_LEVEL );
m_pOwner->RemoveState( StructState::TRACE_OF_FUGITIVE, GameRule::MAX_STATE_LEVEL );
}
int skillEffectType = GetSkillBase()->GetSkillEffectType();
switch( skillEffectType )
{
case SkillBase::EF_ADD_STATE:
case SkillBase::EF_ADD_STATE_BY_TARGET_TYPE:
case SkillBase::EF_ADD_STATE_TO_CASTER_AND_TARGET:
case SkillBase::EF_ADD_RANDOM_STATE:
case SkillBase::EF_ADD_STATE_BY_USING_ITEM:
{
// 지속효과 추가
STATE_SKILL_FUNCTOR mySkillFunctor( &m_vResultList );
process_target( t, mySkillFunctor, pTarget );
break;
}
case SkillBase::EF_ADD_REGION_STATE:
case SkillBase::EF_ADD_RANDOM_REGION_STATE:
{
ADD_REGION_STATE( pTarget );
break;
}
case SkillBase::EF_ADD_STATE_BY_SELF_COST:
{
ADD_STATE_BY_SELF_COST( pTarget );
break;
}
case SkillBase::EF_ADD_REGION_STATE_BY_SELF_COST:
{
ADD_REGION_STATE_BY_SELF_COST( pTarget );
break;
}
case SkillBase::EF_ADD_STATE_STEP_BY_STEP:
{
ADD_STATE_STEP_BY_STEP( pTarget );
break;
}
case SkillBase::EF_PHYSICAL_DIRECTIONAL_DAMAGE:
{
// 방향성 공격
PHYSICAL_DIRECTIONAL_DAMAGE( pTarget );
break;
}
case SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_T1:
{
// 공격력 증가 물리공격
SINGLE_PHYSICAL_DAMAGE_T1( pTarget );
break;
}
case SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_T2:
{
// 공격력 증폭 물리공격
SINGLE_PHYSICAL_DAMAGE_T2( pTarget );
break;
}
case SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_T3:
{
// 무기공격력 무시 물리공격
SINGLE_PHYSICAL_DAMAGE_T3( pTarget );
break;
}
case SkillBase::EF_PHYSICAL_MULTIPLE_DAMAGE_T1:
{
// 공격력 증폭 물리공격
MULTIPLE_PHYSICAL_DAMAGE_T1( pTarget );
break;
}
case SkillBase::EF_PHYSICAL_MULTIPLE_DAMAGE_T2:
{
MULTIPLE_PHYSICAL_DAMAGE_T2( pTarget );
break;
}
case SkillBase::EF_PHYSICAL_MULTIPLE_DAMAGE_T3:
{
MULTIPLE_PHYSICAL_DAMAGE_T3( pTarget );
break;
}
case SkillBase::EF_PHYSICAL_ABSORB_DAMAGE:
{
// 공격력 증가 물리공격 : 흡수
SINGLE_PHYSICAL_DAMAGE_ABSORB( pTarget );
break;
}
case SkillBase::EF_PHYSICAL_SINGLE_REGION_DAMAGE_OLD:
{
// 공격력 증가 물리공격 : 타겟 중심 범위
PHYSICAL_SINGLE_REGION_DAMAGE_OLD( pTarget );
break;
}
case SkillBase::EF_PHYSICAL_MULTIPLE_REGION_DAMAGE_OLD:
{
// 공격력 증가 물리공격 : 연타 : 타겟 중심 범위
PHYSICAL_MULTIPLE_REGION_DAMAGE_OLD( pTarget );
break;
}
case SkillBase::EF_PHYSICAL_MULTIPLE_SPECIAL_REGION_DAMAGE_OLD:
{
// 공격력 증가 물리공격 : 연타 : 타겟 중심 특수 범위
PHYSICAL_MULTIPLE_SPECIAL_REGION_DAMAGE( pTarget );
break;
}
case SkillBase::EF_PHYSICAL_SINGLE_SPECIAL_REGION_DAMAGE_OLD:
{
// 공격력 증가 물리공격 : 특수 범위
PHYSICAL_SPECIAL_REGION_DAMAGE( pTarget );
break;
}
case SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_KNOCKBACK_OLD:
{
// 공격력 증가 물리공격 : 대상밀어내기
SINGLE_PHYSICAL_DAMAGE_T1( pTarget );
break;
}
case SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_ADD_ENERGY_OLD:
{
// 공격력 증폭 물리공격 : 기공 충전
SINGLE_PHYSICAL_DAMAGE_T2_ADD_ENERGY( pTarget );
}
case SkillBase::EF_PHYSICAL_SINGLE_REGION_DAMAGE_KNOCKBACK_OLD:
{
// 공격력 증가 물리 공격 : 밀어내기 : 타겟 중심 범위
PHYSICAL_SINGLE_REGION_DAMAGE_OLD( pTarget );
break;
}
case SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_WITHOUT_WEAPON_RUSH_KNOCK_BACK:
{
// 돌진 : 무기공격력 무시 물리공격 : 밀어내기
SINGLE_PHYSICAL_DAMAGE_T3( pTarget );
break;
}
case SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_RUSH_KNOCKBACK_OLD:
{
// 돌진 : 공격력 증가 물리공격 : 밀어내기
SINGLE_PHYSICAL_DAMAGE_T1( pTarget );
break;
}
case SkillBase::EF_PHYSICAL_MULTIPLE_DAMAGE_TRIPLE_ATTACK_OLD:
{
// 공격력 증가 물리공격 : 한방연타 - 연타횟수 3단계 적용
MULTIPLE_PHYSICAL_DAMAGE_T4( pTarget );
break;
}
case SkillBase::EF_REMOVE_BAD_STATE:
{
// 나쁜 지속효과 제거
REMOVE_BAD_STATE_SKILL_FUNCTOR mySkillFunctor( &m_vResultList );
process_target( t, mySkillFunctor, pTarget );
break;
}
case SkillBase::EF_REMOVE_GOOD_STATE:
{
// 좋은 지속효과 제거
REMOVE_GOOD_STATE_SKILL_FUNCTOR mySkillFunctor( &m_vResultList );
process_target( t, mySkillFunctor, pTarget );
break;
}
case SkillBase::EF_AREA_EFFECT_MAGIC_DAMAGE_OLD:
case SkillBase::EF_AREA_EFFECT_MAGIC_DAMAGE:
case SkillBase::EF_AREA_EFFECT_MAGIC_DAMAGE_AND_HEAL:
case SkillBase::EF_AREA_EFFECT_MAGIC_DAMAGE_AND_HEAL_T2:
case SkillBase::EF_AREA_EFFECT_HEAL:
{
MAKE_AREA_EFFECT_PROP( pTarget, false );
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:
{
MAKE_AREA_EFFECT_PROP( pTarget, true );
break;
}
case SkillBase::EF_CREATE_ITEM:
{
CREATE_ITEM( pTarget, bIsSuccess );
break;
}
case SkillBase::EF_ACTIVATE_FIELD_PROP:
{
ACTIVATE_FIELD_PROP();
break;
}
case SkillBase::EF_REGION_HEAL_BY_FIELD_PROP:
{
REGION_HEAL_BY_FIELD_PROP();
break;
}
case SkillBase::EF_AREA_EFFECT_HEAL_BY_FIELD_PROP:
{
MAKE_AREA_EFFECT_PROP_BY_FIELD_PROP( false );
break;
}
case SkillBase::EF_ADD_HP:
{
// HP 증가
HEALING_SKILL_FUNCTOR mySkillFunctor( &m_vResultList );
mySkillFunctor.nResult = 0;
process_target( t, mySkillFunctor, pTarget );
break;
}
case SkillBase::EF_ADD_MP:
{
RECOVERY_MP_SKILL_FUNCTOR mySkillFunctor( &m_vResultList );
mySkillFunctor.nResult = 0;
process_target( t, mySkillFunctor, pTarget );
break;
}
case SkillBase::EF_ADD_HP_BY_ITEM:
{
// HP 증가
HEALING_SKILL_FUNCTOR mySkillFunctor( &m_vResultList, true );
mySkillFunctor.nResult = 0;
process_target( t, mySkillFunctor, pTarget );
break;
}
case SkillBase::EF_ADD_MP_BY_ITEM:
{
// MP 증가
RECOVERY_MP_SKILL_FUNCTOR mySkillFunctor( &m_vResultList, true );
mySkillFunctor.nResult = 0;
process_target( t, mySkillFunctor, pTarget );
break;
}
case SkillBase::EF_RESURRECTION:
{
// 부활
SKILL_RESURRECTION( pTarget );
break;
}
case SkillBase::EF_ADD_HP_MP:
case SkillBase::EF_ADD_HP_MP_BY_SUMMON_DAMAGE:
case SkillBase::EF_ADD_HP_MP_BY_SUMMON_DEAD:
case SkillBase::EF_ADD_HP_MP_BY_STEAL_SUMMON_HP_MP:
case SkillBase::EF_ADD_HP_MP_WITH_LIMIT_PERCENT:
{
SKILL_ADD_HP_MP( pTarget );
break;
}
case SkillBase::EF_ADD_REGION_HP_MP:
{
SKILL_ADD_REGION_HP_MP( pTarget );
break;
}
case SkillBase::EF_ADD_REGION_HP:
{
SKILL_ADD_REGION_HP( pTarget );
break;
}
case SkillBase::EF_ADD_REGION_MP:
{
SKILL_ADD_REGION_MP( pTarget );
break;
}
case SkillBase::EF_MAGIC_SINGLE_DAMAGE_T1_OLD:
{
if( !GetSkillBase()->GetSkillTargetType() == SkillBase::TARGET_TARGET ) assert( 0 );
SINGLE_MAGICAL_DAMAGE_T1( pTarget );
break;
}
case SkillBase::EF_MAGIC_SINGLE_DAMAGE_T2_OLD:
{
if( !GetSkillBase()->GetSkillTargetType() == SkillBase::TARGET_TARGET ) assert( 0 );
SINGLE_MAGICAL_DAMAGE_T2( pTarget );
break;
}
// 위의 두 유형을 대체한 새 유형
case SkillBase::EF_MAGIC_SINGLE_DAMAGE:
case SkillBase::EF_MAGIC_SINGLE_DAMAGE_ADD_RANDOM_STATE:
case SkillBase::EF_MAGIC_SINGLE_DAMAGE_WITH_PHYSICAL_DAMAGE:
{
if( !GetSkillBase()->GetSkillTargetType() == SkillBase::TARGET_TARGET ) assert( 0 );
SINGLE_MAGICAL_DAMAGE( pTarget );
break;
}
case SkillBase::EF_MAGIC_SINGLE_DAMAGE_BY_CONSUMING_TARGETS_STATE:
{
SINGLE_DAMAGE_BY_CONSUMING_TARGETS_STATE( pTarget );
break;
}
case SkillBase::EF_MAGIC_MULTIPLE_DAMAGE_T1_OLD:
case SkillBase::EF_MAGIC_MULTIPLE_DAMAGE_T1_DEAL_SUMMON_HP_OLD:
{
if( !GetSkillBase()->GetSkillTargetType() == SkillBase::TARGET_TARGET ) assert( 0 );
MULTIPLE_MAGICAL_DAMAGE_T1( pTarget );
break;
}
case SkillBase::EF_MAGIC_MULTIPLE_DAMAGE_AT_ONCE:
{
if( !GetSkillBase()->GetSkillTargetType() == SkillBase::TARGET_TARGET ) assert( 0 );
MULTIPLE_MAGICAL_DAMAGE_AT_ONCE( pTarget );
break;
}
// 위의 유형을 각각 대체한 새 유형
case SkillBase::EF_MAGIC_MULTIPLE_DAMAGE:
case SkillBase::EF_MAGIC_MULTIPLE_DAMAGE_DEAL_SUMMON_HP:
{
if( !GetSkillBase()->GetSkillTargetType() == SkillBase::TARGET_TARGET ) assert( 0 );
MULTIPLE_MAGICAL_DAMAGE( pTarget );
break;
}
// 재수 없으면 즉사하는 새 유형
case SkillBase::EF_MAGIC_SINGLE_DAMAGE_OR_DEATH:
{
if( !GetSkillBase()->GetSkillTargetType() == SkillBase::TARGET_TARGET ) assert( 0 );
SINGLE_MAGICAL_DAMAGE_OR_DEATH( pTarget );
break;
}
// 데미지 주고 HP/MP 흡수하는 새 유형
case SkillBase::EF_MAGIC_DAMAGE_WITH_ABSORB_HP_MP:
{
SINGLE_MAGICAL_DAMAGE_WITH_ABSORB( pTarget );
break;
}
// HP/MP 소모 후 HP/MP 흡수하는 새 유형
case SkillBase::EF_ADD_HP_MP_BY_ABSORB_HP_MP:
{
ADD_HP_MP_BY_ABSORB_HP_MP( pTarget );
break;
}
case SkillBase::EF_MAGIC_SINGLE_PERCENT_DAMAGE:
{
SINGLE_MAGICAL_TARGET_HP_PERCENT_DAMAGE( pTarget );
break;
}
case SkillBase::EF_MAGIC_SINGLE_PERCENT_MANABURN:
case SkillBase::EF_MAGIC_SINGLE_PERCENT_OF_MAX_MP_MANABURN:
{
SINGLE_MAGICAL_MANABURN( pTarget );
break;
}
case SkillBase::EF_MAGIC_MULTIPLE_DAMAGE_T2_OLD:
{
if( !GetSkillBase()->GetSkillTargetType() == SkillBase::TARGET_TARGET ) assert( 0 );
MULTIPLE_MAGICAL_DAMAGE_T2( pTarget );
break;
}
case SkillBase::EF_MAGIC_MULTIPLE_DAMAGE_T3_OLD:
{
MULTIPLE_MAGICAL_DAMAGE_T3( pTarget );
break;
}
case SkillBase::EF_MAGIC_SINGLE_REGION_DAMAGE_OLD:
{
MAGIC_SINGLE_REGION_DAMAGE_OLD( pTarget );
break;
}
// 범위마법공격: 표준 신규 스킬 유형 - 위의 유형 대체
case SkillBase::EF_MAGIC_SINGLE_REGION_DAMAGE:
case SkillBase::EF_MAGIC_SINGLE_REGION_DAMAGE_USING_CORPSE:
case SkillBase::EF_MAGIC_SINGLE_REGION_DAMAGE_ADD_RANDOM_STATE:
{
MAGIC_SINGLE_REGION_DAMAGE( pTarget );
break;
}
case SkillBase::EF_MAGIC_SINGLE_REGION_DAMAGE_BY_SUMMON_DEAD:
{
MAGIC_SINGLE_REGION_DAMAGE_BY_SUMMON_DEAD( pTarget );
break;
}
case SkillBase::EF_REGION_TAUNT:
{
REGION_TAUNT( pTarget );
break;
}
case SkillBase::EF_TAUNT:
{
TAUNT( pTarget );
break;
}
case SkillBase::EF_REMOVE_HATE:
{
REMOVE_HATE( pTarget );
break;
}
case SkillBase::EF_REGION_REMOVE_HATE:
case SkillBase::EF_REGION_REMOVE_HATE_OF_TARGET:
{
REGION_REMOVE_HATE( pTarget );
break;
}
case SkillBase::EF_MAGIC_MULTIPLE_REGION_DAMAGE_AT_ONCE:
{
MAGIC_MULTIPLE_REGION_DAMAGE_AT_ONCE( pTarget );
break;
}
case SkillBase::EF_MAGIC_MULTIPLE_REGION_DAMAGE_OLD:
{
MAGIC_MULTIPLE_REGION_DAMAGE_OLD( pTarget );
break;
}
case SkillBase::EF_MAGIC_MULTIPLE_REGION_DAMAGE_T2_OLD:
{
MAGIC_MULTIPLE_REGION_DAMAGE_T2( pTarget );
break;
}
case SkillBase::EF_MAGIC_SPECIAL_REGION_DAMAGE_OLD:
{
MAGIC_SPECIAL_REGION_DAMAGE_OLD( pTarget );
break;
}
case SkillBase::EF_MAGIC_SPECIAL_REGION_DAMAGE:
{
MAGIC_SPECIAL_REGION_DAMAGE( pTarget );
break;
}
case SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_WITH_SHIELD:
{
SINGLE_PHYSICAL_DAMAGE_WITH_SHIELD( pTarget );
break;
}
case SkillBase::EF_MAGIC_MULTIPLE_REGION_DAMAGE:
{
MAGIC_MULTIPLE_REGION_DAMAGE( pTarget );
break;
}
case SkillBase::EF_MAGIC_REGION_PERCENT_DAMAGE:
{
MAGIC_SINGLE_REGION_PERCENT_DAMAGE( pTarget );
break;
}
case SkillBase::EF_MAGIC_ABSORB_DAMAGE_OLD:
{
MAGIC_ABSORB_DAMAGE( pTarget );
break;
}
case SkillBase::EF_TOGGLE_AURA:
case SkillBase::EF_TOGGLE_DIFFERENTIAL_AURA:
{
TOGGLE_AURA( pTarget );
break;
}
case SkillBase::EF_SUMMON:
{
if( m_pOwner->IsPlayer() ) Summon();
break;
}
case SkillBase::EF_UNSUMMON:
{
if( m_pOwner->IsPlayer() ) UnSummon();
break;
}
case SkillBase::EF_UNSUMMON_AND_ADD_STATE:
{
if( m_pOwner->IsPlayer() ) UnSummonAndAddState();
break;
}
case SkillBase::EF_CORPSE_ABSORB:
{
CORPSE_ABSORB( pTarget );
break;
}
case SkillBase::EF_CORPSE_EXPLOSION:
{
CORPSE_EXPLOSION( pTarget );
break;
}
// 신규 유형 - 대체 구 유형: EF_PHYSICAL_SINGLE_DAMAGE_T1, EF_PHYSICAL_SINGLE_DAMAGE_T2, EF_PHYSICAL_SINGLE_DAMAGE_T3,
// EF_PHYSICAL_SINGLE_DAMAGE_RUSH_KNOCKBACK_OLD, EF_PHYSICAL_SINGLE_DAMAGE_KNOCKBACK_OLD
// 신규 추가 유형: EF_PHYSICAL_SINGLE_DAMAGE_RUSH
case SkillBase::EF_PHYSICAL_SINGLE_DAMAGE:
case SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_RUSH:
case SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_RUSH_KNOCKBACK:
case SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_KNOCKBACK:
{
PHYSICAL_SINGLE_DAMAGE( pTarget );
break;
}
case SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_RUSH_KNOCKBACK_REGION:
{
SINGLE_RUSH_REGION( m_targetPos );
break;
}
// 신규 유형 - 대체 구 유형: EF_PHYSICAL_ABSORB_DAMAGE
case SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_ABSORB:
{
PHYSICAL_SINGLE_DAMAGE_ABSORB( pTarget );
break;
}
// 신규 유형 - 대체 구 유형: EF_PHYSICAL_SINGLE_DAMAGE_ADD_ENERGY_OLD
case SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_ADD_ENERGY:
{
PHYSICAL_SINGLE_DAMAGE_ADD_ENERGY( pTarget );
break;
}
// 신규 유형 - 대체 구 유형: EF_PHYSICAL_SINGLE_REGION_DAMAGE_OLD, EF_PHYSICAL_SINGLE_REGION_DAMAGE_KNOCKBACK_OLD
// 신규 추가 유형: EF_PHYSICAL_SINGLE_REGION_DAMAGE_KNOCKBACK_SELF, EF_PHYSICAL_SINGLE_REGION_DAMAGE_WITH_CAST_CANCEL
case SkillBase::EF_PHYSICAL_SINGLE_REGION_DAMAGE:
case SkillBase::EF_PHYSICAL_SINGLE_REGION_DAMAGE_KNOCKBACK:
case SkillBase::EF_PHYSICAL_SINGLE_REGION_DAMAGE_KNOCKBACK_SELF:
case SkillBase::EF_PHYSICAL_SINGLE_REGION_DAMAGE_WITH_CAST_CANCEL:
case SkillBase::EF_PHYSICAL_SINGLE_REGION_DAMAGE_ADDING_MAGICAL_DAMAGE:
{
PHYSICAL_SINGLE_REGION_DAMAGE( pTarget );
break;
}
// 신규 유형 - 대체 구 유형: EF_PHYSICAL_MULTIPLE_DAMAGE_T3
// 신규 추가 유형: EF_PHYSICAL_REALTIME_MULTIPLE_DAMAGE_KNOCKBACK
case SkillBase::EF_PHYSICAL_REALTIME_MULTIPLE_DAMAGE:
case SkillBase::EF_PHYSICAL_REALTIME_MULTIPLE_DAMAGE_KNOCKBACK:
{
PHYSICAL_REALTIME_MULTIPLE_DAMAGE( pTarget );
break;
}
// 신규 추가 유형: EF_PHYSICAL_REALTIME_MULTIPLE_REGION_DAMAGE
case SkillBase::EF_PHYSICAL_REALTIME_MULTIPLE_REGION_DAMAGE:
{
PHYSICAL_REALTIME_MULTIPLE_REGION_DAMAGE( pTarget );
break;
}
case SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_BY_CONSUMING_TARGETS_STATE:
{
SINGLE_DAMAGE_BY_CONSUMING_TARGETS_STATE( pTarget );
break;
}
// 신규 유형 - 대체 구 유형: EF_PHYSICAL_MULTIPLE_DAMAGE_T1
case SkillBase::EF_PHYSICAL_MULTIPLE_DAMAGE:
{
PHYSICAL_MULTIPLE_DAMAGE( pTarget );
break;
}
// 신규 유형 - 대체 구 유형: EF_PHYSICAL_MULTIPLE_DAMAGE_TRIPLE_ATTACK_OLD
case SkillBase::EF_PHYSICAL_MULTIPLE_DAMAGE_TRIPLE_ATTACK:
{
PHYSICAL_MULTIPLE_DAMAGE_TRIPLE_ATTACK( pTarget );
break;
}
// 신규 유형 - 대체 구 유형: EF_PHYSICAL_MULTIPLE_REGION_DAMAGE_OLD, EF_PHYSICAL_MULTIPLE_SPECIAL_REGION_DAMAGE_OLD
// 신규 추가 유형: EF_PHYSICAL_MULTIPLE_SPECIAL_REGION_DAMAGE_SELF
case SkillBase::EF_PHYSICAL_MULTIPLE_REGION_DAMAGE:
case SkillBase::EF_PHYSICAL_MULTIPLE_SPECIAL_REGION_DAMAGE:
case SkillBase::EF_PHYSICAL_MULTIPLE_SPECIAL_REGION_DAMAGE_SELF:
{
PHYSICAL_MULTIPLE_REGION_DAMAGE( pTarget );
break;
}
// 신규 추가 유형 - 대체 구 유형: EF_PHYSICAL_SINGLE_SPECIAL_REGION_DAMAGE_OLD
case SkillBase::EF_PHYSICAL_SINGLE_SPECIAL_REGION_DAMAGE:
{
PHYSICAL_SINGLE_SPECIAL_REGION_DAMAGE( pTarget );
break;
}
// 신규 추가 유형: EF_RESURRECTION_WITH_RECOVER
case SkillBase::EF_RESURRECTION_WITH_RECOVER:
{
SKILL_RESURRECTION_WITH_RECOVER( pTarget );
break;
}
// 신규 추가 유형: EF_REMOVE_STATE_GROUP
case SkillBase::EF_REMOVE_STATE_GROUP:
{
REMOVE_STATE_GROUP_SKILL_FUNCTOR mySkillFunctor( &m_vResultList, m_pOwner );
process_target( t, mySkillFunctor, pTarget );
break;
}
case SkillBase::EF_PHYSICAL_CHAIN_DAMAGE:
{
PHYSICAL_CHAIN_DAMAGE( pTarget );
break;
}
case SkillBase::EF_MAGIC_CHAIN_DAMAGE:
{
MAGIC_CHAIN_DAMAGE( pTarget );
break;
}
case SkillBase::EF_CHAIN_HEAL:
{
CHAIN_HEAL( pTarget );
break;
}
case SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_DEMINISHED_HP_MP:
{
PHYSICAL_SINGLE_DAMAGE_DEMINISHED_HP_MP( pTarget );
break;
}
case SkillBase::EF_INC_SKILL_COOL_TIME:
{
INC_SKILL_COOL_TIME( pTarget );
break;
}
case SkillBase::EF_AMP_SKILL_COOL_TIME:
{
AMP_SKILL_COOL_TIME( pTarget );
break;
}
case SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_PROP_REMAIN_MP:
{
PHYSICAL_SINGLE_DAMAGE_PROP_REMAIN_MP( pTarget );
break;
}
case SkillBase::EF_REPLENISH_ENERGY_HP_MP:
{
REPLENISH_ENERGY_HP_MP();
break;
}
// 신규 추가 유형: EF_LOTTO
case SkillBase::EF_LOTTO:
{
SKILL_LOTTO();
break;
}
// 신규 추가 유형: EF_CASTING_CANCEL_WITH_ADD_STATE
case SkillBase::EF_CASTING_CANCEL_WITH_ADD_STATE:
{
CASTING_CANCEL_WITH_ADD_STATE( pTarget );
break;
}
// 신규 추가 유형: EF_RESPAWN_MONSTER_NEAR
case SkillBase::EF_RESPAWN_MONSTER_NEAR:
{
RESPAWN_NEAR_MONSTER();
break;
}
// 신규 추가 유형: EF_RESPAWN_MONSTER_RANDOMLY
// 신규 추가 유형: EF_RESPAWN_MONSTER_WITH_DIFF_CODE
case SkillBase::EF_RESPAWN_MONSTER_RANDOMLY:
case SkillBase::EF_RESPAWN_MONSTER_WITH_DIFF_CODE:
{
RESPAWN_MONSTER();
break;
}
default:
{
switch( GetSkillId() )
{
case SKILL_TOWN_PORTAL:
case SKILL_TOWN_PORTAL2:
case SKILL_RETURN: TOWN_PORTAL(); break;
case SKILL_RANKED_DEATHMATCH_ENTER:
case SKILL_FREED_DEATHMATCH_ENTER: INSTANCE_GAME_ENTER(); break;
case SKILL_WARP_TO_HUNTAHOLIC_LOBBY: WARP_TO_HUNTAHOLIC_LOBBY(); break;
case SKILL_INSTANCE_GAME_EXIT: INSTANCE_GAME_EXIT(); break;
case SKILL_RETURN_FEATHER: RETURN_FEATHER(); break;
case SKILL_RETURN_BACK_FEATHER: RETURN_BACK_FEATHER(); break;
case SKILL_CREATURE_TAMING:
case SKILL_ITEM_CREATURE_TAMING_SCROLL: CREATURE_TAMING( pTarget ); break;
case SKILL_PET_TAMING: PET_TAMING( pTarget ); break;
case SKILL_SHOVELING: SHOVELING(); break;
case SKILL_GAIA_FORCE_SAVING: ADD_ENERGY(); break;
default: break;
}
}
}
// 헤이트, 하보크 추가
{
std::vector< StructCreature * > vNeedStateList;
std::vector< SkillResult > vResultList = std::vector< SkillResult >( m_vResultList );
for( std::vector< SkillResult >::iterator it = vResultList.begin(); it != 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);
if( pDealTarget && pDealTarget->IsAlive() )
{
if( GetSkillBase()->GetSkillEffectType() != SkillBase::EF_ADD_STATE &&
GetSkillBase()->GetSkillEffectType() != SkillBase::EF_ADD_REGION_STATE &&
GetSkillBase()->GetSkillEffectType() != SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_WITH_SHIELD &&
GetSkillBase()->GetSkillEffectType() != SkillBase::EF_ADD_STATE_BY_SELF_COST &&
GetSkillBase()->GetSkillEffectType() != SkillBase::EF_ADD_REGION_STATE_BY_SELF_COST &&
GetSkillBase()->GetSkillEffectType() != SkillBase::EF_ADD_STATE_TO_CASTER_AND_TARGET &&
GetSkillBase()->GetSkillEffectType() != SkillBase::EF_ADD_RANDOM_STATE &&
GetSkillBase()->GetSkillEffectType() != SkillBase::EF_ADD_RANDOM_REGION_STATE &&
GetSkillBase()->GetSkillEffectType() != SkillBase::EF_REMOVE_STATE_GROUP &&
GetStateId() )
{
//if( ( (*it).GetType() == SkillResult::DAMAGE || (*it).GetType() == SkillResult::MAGIC_DAMAGE || (*it).GetType() == SkillResult::DAMAGE_WITH_KNOCK_BACK ) && (*it).damage.flag ^ SkillResult::MISS )
if( (*it).damage.flag ^ SkillResult::MISS )
{
// 지속효과 추가
vNeedStateList.push_back( pDealTarget );
}
}
ApplyHateAndHavokFromSkillResult( m_pOwner, pDealTarget, *it );
}
}
if( GetSkillUID() >= 0 )
{
// 추가 데미지와 추가 데미지의 속성이 아닌, 스킬의 데미지와 스킬의 속성이 핸들러에 영향을 미친다.
// 핸들러는 무조건 호출이 되고 데미지가 없어서 효과가 호출이 안 되는 경우는 핸들러 내부에서 처리한다.
int nDamage = 0;
// 스킬 결과 타입이 데미지를 주는 경우에만 데미지를 인자로 넘겨준다.
if( (*it).GetType() == SkillResult::DAMAGE || (*it).GetType() == SkillResult::MAGIC_DAMAGE || (*it).GetType() == SkillResult::DAMAGE_WITH_KNOCK_BACK || (*it).GetType() == SkillResult::CHAIN_DAMAGE || (*it).GetType() == SkillResult::CHAIN_MAGIC_DAMAGE )
nDamage = (*it).GetDamage();
DWORD nAttackType = 0;
nAttackType |= GetSkillBase()->IsHarmful() ? StructState::Harmful : StructState::Helpful;
nAttackType |= GetSkillBase()->IsPhysicalSkill() ? StructState::PhysicalSkill : StructState::MagicalSkill;
int nElementalType = GetSkillBase()->GetElementalType();
// 성공/회피/크리티컬/블럭/퍼펙트블럭
if( (*it).damage.flag ^ SkillResult::MISS ) m_pOwner->OnAttack( pTarget, nDamage, nAttackType, nElementalType, GetSkillId() );
else pTarget->OnAvoid( m_pOwner, nAttackType, nElementalType );
if( (*it).damage.flag & SkillResult::CRITICAL ) m_pOwner->OnCritical( pTarget, nDamage, nAttackType, nElementalType );
if( (*it).damage.flag & SkillResult::BLOCK ) pTarget->OnBlock( m_pOwner, nDamage, nAttackType, nElementalType );
if( (*it).damage.flag & SkillResult::PERFECT_BLOCK ) pTarget->OnPerfectBlock( m_pOwner, nAttackType, nElementalType );
}
}
STATE_SKILL_FUNCTOR myStateSkillFunctor( &vResultList );
for( std::vector< StructCreature * >::iterator it = vNeedStateList.begin(); it != vNeedStateList.end(); ++it )
{
process_target( t, myStateSkillFunctor, (*it) );
}
}
// 지속 효과 중 스킬 사용시 효과를 주고 소모성인 녀석들 제거
int nElementalType = GetSkillBase()->GetElementalType();
m_pOwner->RemoveExhaustiveSkillStateMod( GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful(), nElementalType, GetOriginalCastingDelay() );
// C급 보스 이상의 몬스터가 스킬을 발사한 경우 로그 남김
if( m_pOwner->IsMonster() && static_cast< StructMonster * >( m_pOwner )->GetMonsterType() >= MonsterBase::MONSTER_TYPE_HIGHEST_1_STAR && bIsSuccess )
{
StructMonster * pMonster = static_cast< StructMonster * >( m_pOwner );
if( pTarget )
{
StructPlayer * pPlayer = NULL;
if( pTarget->IsPlayer() )
{
pPlayer = static_cast< StructPlayer * >( pTarget );
}
else if( pTarget->IsSummon() )
{
pPlayer = static_cast< StructSummon * >( pTarget )->GetMaster();
}
if( pPlayer )
{
LOG::Log11N4S( LM_MONSTER_SKILL_FIRE, pPlayer->GetAccountID(), pPlayer->GetSID(), pTarget->GetX(), pTarget->GetY(), pTarget->GetLayer(),
pMonster->GetMonsterId(), pMonster->GetX(), pMonster->GetY(), pMonster->GetLayer(),
GetSkillId(), GetRequestedSkillLevel(),
pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS,
pTarget->GetName(), LOG::STR_NTS, pMonster->GetName(), LOG::STR_NTS );
}
else
{
LOG::Log11N4S( LM_MONSTER_SKILL_FIRE, 0, 0, pTarget->GetX(), pTarget->GetY(), pTarget->GetLayer(),
pMonster->GetMonsterId(), pMonster->GetX(), pMonster->GetY(), pMonster->GetLayer(),
GetSkillId(), GetRequestedSkillLevel(),
"", 0, "", 0,
pTarget->GetName(), LOG::STR_NTS, pMonster->GetName(), LOG::STR_NTS );
}
}
else
{
LOG::Log11N4S( LM_MONSTER_SKILL_FIRE, 0, 0, 0, 0, 0,
pMonster->GetMonsterId(), pMonster->GetX(), pMonster->GetY(), pMonster->GetLayer(),
GetSkillId(), GetRequestedSkillLevel(),
"", 0, "", 0,
"", 0, pMonster->GetName(), LOG::STR_NTS );
}
}
}
void StructSkill::ApplyHateAndHavokFromSkillResult( StructCreature* source, StructCreature* target, const SkillResult& result )
{
if( !source->IsMonster() && GetSkillBase()->GetEffectType() != SkillBase::EF_REMOVE_HATE && GetSkillBase()->GetEffectType() != SkillBase::EF_REGION_REMOVE_HATE && GetSkillBase()->GetEffectType() != SkillBase::EF_REGION_REMOVE_HATE_OF_TARGET )
{
int pt = 0;
if( result.GetType() == SkillResult::DAMAGE || result.GetType() == SkillResult::MAGIC_DAMAGE || result.GetType() == SkillResult::DAMAGE_WITH_KNOCK_BACK || result.GetType() == SkillResult::CHAIN_DAMAGE || result.GetType() == SkillResult::CHAIN_MAGIC_DAMAGE )
{
pt = result.GetDamage();
}
else if( result.GetType() == SkillResult::ADD_HP || result.GetType() == SkillResult::ADD_MP || result.GetType() == SkillResult::CHAIN_HEAL )
{
pt = result.add_hp.nIncHP;
}
else if( result.GetType() == SkillResult::ADD_HP_MP_SP )
{
pt = result.add_hp_mp_sp.nIncHP;
pt += result.add_hp_mp_sp.nIncMP;
pt += result.add_hp_mp_sp.nIncSP;
}
int nAddHate = source->GetHateRatio() * GetHatePoint( GetRequestedSkillLevel(), pt, GetEnhance() ) * source->GetMagicalHateMod( (Elemental::Type) GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
std::pair< float, int > HateMod = source->GetHateMod( ( GetSkillBase()->IsPhysicalSkill() ) ? 1 : 2, GetSkillBase()->IsHarmful() );
nAddHate += HateMod.second;
nAddHate *= HateMod.first;
// Hate 수치를 먹긴 해야 하는데 0으로 나오면 데미지는 주지 않고 디버프만 거는 스킬이므로 최소한 1의 hate를 줌
if( !nAddHate )
++nAddHate;
if( target->IsMonster() )
{
StructMonster *pMonster = static_cast< StructMonster * >( target );
pMonster->AddHate( source->GetHandle(), nAddHate );
}
else if( target->IsNPC() )
{
StructNPC * pNPC = static_cast< StructNPC * >( target );
pNPC->SetAttacker( source );
}
else if( !target->IsEnemy( source ) && !source->IsEnemy( target ) )
{
target->AddHateToEnemyList( source->GetHandle(), nAddHate );
}
StructState::StateCode nCode = static_cast<StructState::StateCode>(9903);
if( target )
{
if( source->IsActiveLuna() && !target->GetState( nCode ) && source->IsEnemy(target , false) )
{
target->AddState( nCode, source->GetHandle(), 1, GetArTime(), GetArTime() + 18000 );
}
}
}
}
void StructSkill::ProcSkill()
{
if( m_Status == STATUS_IDLE )
return;
AR_TIME t = GetArTime();
StructCreature *pTarget = NULL;
GameObject * pRawTarget = NULL;
GameObject::iterator itSkillTarget;
{
ARCADIA_LOCK( ArcadiaServer::Instance().LockObjectWithVisibleRange( m_pOwner ) );
if( m_pOwner->IsDead() )
{
m_pOwner->CancelSkill();
return;
}
itSkillTarget = GameObject::get( m_hTarget );
pRawTarget = *itSkillTarget;
if( m_Status != STATUS_COMPLETE && pRawTarget )
{
if( pRawTarget->IsCreature() )
{
pTarget = static_cast< StructCreature * >( *itSkillTarget );
// 시야 거리 밖으로 나갔으면 스킬 캔슬
if( pTarget->GetPos().GetDistance( m_pOwner->GetPos() ) > GameRule::VISIBLE_RANGE )
{
m_pOwner->CancelSkill();
return;
}
// 대상이 죽었으면 시전 불가
if( pTarget->IsDead() && !GetSkillBase()->IsValidToCorpse() )
{
m_pOwner->CancelSkill();
return;
}
// 시체 전용 스킬인데 대상이 살아있으면(캐스팅 중에 부활할 수도 있다) 중지
if( !pTarget->IsDead() && GetSkillBase()->IsValidToCorpse() )
{
m_pOwner->CancelSkill();
return;
}
}
else if( GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ACTIVATE_FIELD_PROP ||
GetSkillBase()->GetSkillEffectType() == SkillBase::EF_REGION_HEAL_BY_FIELD_PROP ||
GetSkillBase()->GetSkillEffectType() == SkillBase::EF_AREA_EFFECT_HEAL_BY_FIELD_PROP )
{
// 있을 리가 없지만 -_ -;
if( !m_pOwner->IsPlayer() || !pRawTarget->IsFieldProp() )
{
m_pOwner->CancelSkill();
return;
}
StructFieldProp * pProp = static_cast< StructFieldProp * >( pRawTarget );
if( !pProp || !IsVisibleRegion( m_pOwner->GetRX(), m_pOwner->GetRY(), pProp->GetRX(), pProp->GetRY() ) ||
!pProp->IsUsable( static_cast< StructPlayer * >( m_pOwner ) ) )
{
m_pOwner->CancelSkill();
return;
}
}
}
if( m_hTarget && !pRawTarget )
{
m_pOwner->CancelSkill();
return;
}
// 펫 삽질일 경우 추가 처리
if( m_pOwner->IsPet() && GetSkillId() == SKILL_SHOVELING && m_Status == STATUS_CAST )
{
StructPet *pPet = static_cast< StructPet * >( m_pOwner );
StructPlayer *pMaster = pPet->GetMaster();
// 주인과 일정 거리 이상 멀어지면 중지
if( !pMaster || !pMaster->IsInWorld() || pMaster->GetPos().GetDistance( m_pOwner->GetPos() ) > GameRule::PET_SHOVELING_LIMIT_DISTANCE_FROM_MASTER )
{
m_pOwner->CancelSkill();
return;
}
// 단계 변화 처리
switch( pPet->GetShovelingStatus() )
{
case StructPet::SHOVELING_STATUS_SEARCH:
if( t >= m_nCastTime + GameRule::PET_SHOVELING_SEARCH_DURATION )
{
// 이동 경로 설정
std::vector< ArPosition > vPath;
ArPosition pos = m_pOwner->GetPos();
while( vPath.size() < 2 )
{
int nDistance = ( GameRule::PET_SHOVELING_LIMIT_ROAMING_DISTANCE / 2 );
pos.x += ( XRandom() % nDistance - nDistance/2 );
pos.y += ( XRandom() % nDistance - nDistance/2 );
vPath.push_back( pos );
}
m_pOwner->SetMultipleMove( vPath, GameRule::PET_SHOVELING_APPROACH_SPEED );
static_cast< StructPet * >( m_pOwner )->SetShovelingStatus( StructPet::SHOVELING_STATUS_APPROACH );
BroadcastStatusMessage( m_pOwner );
}
break;
case StructPet::SHOVELING_STATUS_APPROACH:
if( t >= m_nCastTime + GameRule::PET_SHOVELING_SEARCH_DURATION + GameRule::PET_SHOVELING_APPROACH_DURATION )
{
static_cast< StructPet * >( m_pOwner )->SetShovelingStatus( StructPet::SHOVELING_STATUS_DIG );
BroadcastStatusMessage( m_pOwner );
}
break;
default:
assert( 0 );
break;
}
}
if( t < m_nFireTime )
return;
if( m_Status == STATUS_CAST )
{
int nElementalType = GetSkillBase()->GetElementalType();
m_pOwner->PrepareRemoveExhaustiveSkillStateMod( GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful(), nElementalType, GetOriginalCastingDelay() );
// 캐스팅 중이면
m_Status = STATUS_FIRE;
}
}
ArcadiaAutoLock _lock;
if( pRawTarget && pRawTarget->IsInWorld() )
{
ArPosition top_left, bottom_right;
const SkillBase *pSkillBase = GetSkillBase();
int nEnhance = GetEnhance();
int nSLV = GetRequestedSkillLevel();
unsigned int rx1 = m_pOwner->GetRX();
unsigned int ry1 = m_pOwner->GetRY();
short layer1 = m_pOwner->GetLayer();
unsigned int rx2 = pRawTarget->GetRX();
unsigned int ry2 = pRawTarget->GetRY();
short layer2 = pRawTarget->GetLayer();
// 최초에는 락이 걸리지 않은 기본 값으로 락 핸들을 -1을 지정함(Unlock의 영향을 받지 않음)
ArcadiaLock _region_lock( -1 );
do
{
ArcadiaServer::Instance().UnLock( &_region_lock );
rx1 = m_pOwner->GetRX();
ry1 = m_pOwner->GetRY();
layer1 = m_pOwner->GetLayer();
rx2 = pRawTarget->GetRX();
ry2 = pRawTarget->GetRY();
layer2 = pRawTarget->GetLayer();
short lock_layer = ( layer1 == layer2 ) ? layer1 : -1;
top_left.x = std::min( m_pOwner->GetX(), pRawTarget->GetX() );
top_left.y = std::min( m_pOwner->GetY(), pRawTarget->GetY() );
bottom_right.x = std::max( m_pOwner->GetX(), pRawTarget->GetX() );
bottom_right.y = std::max( m_pOwner->GetY(), pRawTarget->GetY() );
if( ( pSkillBase->GetSkillEffectType() == SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_KNOCKBACK_OLD ||
pSkillBase->GetSkillEffectType() == SkillBase::EF_PHYSICAL_SINGLE_REGION_DAMAGE_KNOCKBACK_OLD ||
pSkillBase->GetSkillEffectType() == SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_WITHOUT_WEAPON_RUSH_KNOCK_BACK ||
pSkillBase->GetSkillEffectType() == SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_RUSH_KNOCKBACK_OLD ) &&
( !pRawTarget->IsCreature() || static_cast< StructCreature * >(pRawTarget)->IsKnockbackable() ) )
{
float fKnockBackRange = pSkillBase->GetVar(5) + nSLV * pSkillBase->GetVar(6) + pSkillBase->GetVar(10) * nEnhance;
fKnockBackRange *= GameRule::DEFAULT_UNIT_SIZE;
top_left.x = std::min( top_left.x, pRawTarget->GetX() - fKnockBackRange );
top_left.y = std::min( top_left.y, pRawTarget->GetY() - fKnockBackRange );
bottom_right.x = std::max( bottom_right.x, pRawTarget->GetX() + fKnockBackRange );
bottom_right.y = std::max( bottom_right.y, pRawTarget->GetY() + fKnockBackRange );
}
else if( pSkillBase->GetSkillEffectType() == SkillBase::EF_PHYSICAL_CHAIN_DAMAGE ||
pSkillBase->GetSkillEffectType() == SkillBase::EF_MAGIC_CHAIN_DAMAGE ||
pSkillBase->GetSkillEffectType() == SkillBase::EF_CHAIN_HEAL )
{
// 대상의 범위는 시전자를 기준으로 시전 범위의 두 배이다.
// 시전자와 대상은 무조건 락 안에 들어간다.
float fChainRange = pSkillBase->GetCastRange() * 2 * GameRule::DEFAULT_UNIT_SIZE;
top_left.x = m_pOwner->GetX() - fChainRange;
top_left.y = m_pOwner->GetY() - fChainRange;
bottom_right.x = m_pOwner->GetX() + fChainRange;
bottom_right.y = m_pOwner->GetY() + fChainRange;
}
_region_lock = ArcadiaServer::Instance().LockArea( GetRegionX( top_left.x ), GetRegionY( top_left.y ), GetRegionX( bottom_right.x ), GetRegionY( bottom_right.y ), lock_layer );
}
while( rx1 != m_pOwner->GetRX() || ry1 != m_pOwner->GetRY() || layer1 != m_pOwner->GetLayer() ||
rx2 != pRawTarget->GetRX() || ry2 != pRawTarget->GetRY() || layer2 != pRawTarget->GetLayer() );
_lock.set( _region_lock, __FILE__, __LINE__ );
// 락 걸고 난 이후에 타겟이 월드에 없다면 스킬 캔슬
if( !pRawTarget->IsInWorld() )
{
m_pOwner->CancelSkill();
m_hTarget = 0;
broadcastSkillMessage( rx1, ry1, m_pOwner->GetLayer(), 0, 0, TS_SC_SKILL::CANCEL );
return;
}
}
// 역소환 관련 처리가 StructPlayer::UnSummon를 직접 호출하는 게 아니라 StructPlayer::PendUnSummon 함수로 모두 대체되었으므로
// 소환수가 있는 리전을 포함하는 락을 걸 필요 없이 역소환 스킬 시전자(소환수 주인) 시야 범위 내에만 락을 걸면 됨
// * 스킬이야 여기서 락 범위 예측해서 조정하면 되지만 사망 시 역소환같은 건 락 범위를 미리 조절할 방법이 없으므로
// 역소환 처리 자체가 해당 쓰레드에서 바로 처리되지 못하고 Pend만 걸어두고 StructPlayer::onProcess에서 처리하도록 해야 함
// * EF_UNSUMMON_AND_ADD_STATE의 지속효과 방송 때문에 시야 범위 락은 필요
/*else if( GetSkillBase()->GetSkillEffectType() == SkillBase::EF_UNSUMMON ||
GetSkillBase()->GetSkillEffectType() == SkillBase::EF_UNSUMMON_AND_ADD_STATE )
{
// exception process when unsummon
ArPosition top_left, bottom_right;
StructSummon * pMainSummon = static_cast< StructPlayer * >( m_pOwner )->GetMainSummon();
if( !m_pOwner->IsPlayer() || !pMainSummon || !pMainSummon->IsInWorld() )
{
m_pOwner->CancelSkill();
return;
}
top_left.x = std::min( m_pOwner->GetX(), pMainSummon->GetX() );
top_left.y = std::min( m_pOwner->GetY(), pMainSummon->GetY() );
bottom_right.x = std::max( m_pOwner->GetX(), pMainSummon->GetX() );
bottom_right.y = std::max( m_pOwner->GetY(), pMainSummon->GetY() );
StructSummon * pSubSummon = static_cast< StructPlayer * >( m_pOwner )->GetSubSummon();
if( GetSkillBase()->GetSkillEffectType() == SkillBase::EF_UNSUMMON_AND_ADD_STATE &&
pSubSummon && pSubSummon->IsInWorld() )
{
top_left.x = std::min( top_left.x, pSubSummon->GetX() );
top_left.y = std::min( top_left.y, pSubSummon->GetY() );
bottom_right.x = std::max( bottom_right.x, pSubSummon->GetX() );
bottom_right.y = std::max( bottom_right.y, pSubSummon->GetY() );
}
_lock.set( ArcadiaServer::Instance().LockArea( GetRegionX( top_left.x ), GetRegionY( top_left.y ), GetRegionX( bottom_right.x ), GetRegionY( bottom_right.y ) ), __FILE__, __LINE__ );
pMainSummon = static_cast< StructPlayer * >( m_pOwner )->GetMainSummon();
if( !pMainSummon || !pMainSummon->IsInWorld() )
{
m_pOwner->CancelSkill();
return;
}
}*/
// 지면 타겟 스킬인 경우 시전자와 지면 타겟 지점 기준으로 지역 락 범위 증가
else if( GetSkillBase()->IsNeedTarget() == 2 )
{
// 최초에는 락이 걸리지 않은 기본 값으로 락 핸들을 -1을 지정함(Unlock의 영향을 받지 않음)
ArcadiaLock _region_lock( -1 );
unsigned int rx1 = m_pOwner->GetRX();
unsigned int ry1 = m_pOwner->GetRY();
unsigned int rx2 = m_targetPos.GetRX();
unsigned int ry2 = m_targetPos.GetRY();
short layer = m_pOwner->GetLayer();
do
{
ArcadiaServer::Instance().UnLock( &_region_lock );
rx1 = m_pOwner->GetRX();
ry1 = m_pOwner->GetRY();
layer = m_pOwner->GetLayer();
_region_lock = ArcadiaServer::Instance().LockArea( rx1, ry1, rx2, ry2, layer );
}
while( rx1 != m_pOwner->GetRX() || ry1 != m_pOwner->GetRY() || layer != m_pOwner->GetLayer() );
_lock.set( _region_lock, __FILE__, __LINE__ );
}
else
{
_lock.set( ArcadiaServer::Instance().LockObjectWithVisibleRange( m_pOwner ), __FILE__, __LINE__ );
}
if( pTarget && !pTarget->IsInWorld() )
{
m_pOwner->CancelSkill();
m_hTarget = 0;
broadcastSkillMessage( m_pOwner->GetRX(), m_pOwner->GetRY(), m_pOwner->GetLayer(), 0, 0, TS_SC_SKILL::CANCEL );
return;
}
// 필드 프랍 사용 스킬의 경우 위의 조건으로 프랍이 월드에서 없어졌는지 체크가 불가능(pTarget은 pRawTarget이 Creature일 때만 세팅)
if( ( GetSkillBase()->GetSkillEffectType() == SkillBase::EF_ACTIVATE_FIELD_PROP ||
GetSkillBase()->GetSkillEffectType() == SkillBase::EF_REGION_HEAL_BY_FIELD_PROP ||
GetSkillBase()->GetSkillEffectType() == SkillBase::EF_AREA_EFFECT_HEAL_BY_FIELD_PROP ) &&
( !pRawTarget || !pRawTarget->IsInWorld() ) )
{
m_pOwner->CancelSkill();
m_hTarget = 0;
broadcastSkillMessage( m_pOwner->GetRX(), m_pOwner->GetRY(), m_pOwner->GetLayer(), 0, 0, TS_SC_SKILL::CANCEL );
return;
}
if( m_Status == STATUS_FIRE )
{
bool bSuccess = true;
int cost_hp = 0;
int cost_mp = 0;
int cost_exp = 0;
int cost_jp = 0;
if( !m_bMultiple || m_nCurrentFire == 0 )
{
// 아이템으로 스킬 사용시에는 쿨타임을 세팅하지 않음
if( !m_hCastItem )
{
AR_TIME next_cool_time = GetSkillCoolTime();
SetRemainCoolTime( next_cool_time );
if( GetSkillBase()->GetCoolTimeGroupId() )
{
struct _mySkillFunctor : StructCreature::SKILL_POINTER_FUNCTOR
{
_mySkillFunctor( int _cool_time_group_id, AR_TIME _next_cool_time ) : cool_time_group_id( _cool_time_group_id ), next_cool_time( _next_cool_time ) {}
virtual void onSkill( StructSkill* pSkill )
{
if( pSkill->GetSkillBase()->GetCoolTimeGroupId() == cool_time_group_id )
pSkill->SetRemainCoolTime( next_cool_time );
}
int cool_time_group_id;
AR_TIME next_cool_time;
} _fo( GetSkillBase()->GetCoolTimeGroupId(), next_cool_time );
m_pOwner->EnumActiveSkill( _fo );
}
}
StructPlayer * pClient = NULL;
if( m_pOwner->IsPlayer() )
{
pClient = static_cast< StructPlayer * >( m_pOwner );
}
else if( m_pOwner->IsSummon() )
{
pClient = static_cast< StructSummon * >( m_pOwner )->GetMaster();
}
if( pClient )
{
SendSkillMessage( pClient, m_pOwner, GetSkillId() );
}
// 비용 소비
if( ( GetSkillBase()->GetEffectType() == SkillBase::EF_TOGGLE_AURA ||
GetSkillBase()->GetEffectType() == SkillBase::EF_TOGGLE_DIFFERENTIAL_AURA ) && m_pOwner->IsActiveAura( this ) )
{
cost_mp = 0;
}
else
{
cost_mp = CalculateMPCost( m_pOwner->GetSkillCostModifier() );
cost_mp -= cost_mp * GameRule::SKILL_CAST_COST;
}
cost_hp = CalculateHPCost( m_pOwner->GetSkillCostModifier() );
cost_exp = GetCostEXP( GetRequestedSkillLevel(), GetEnhance() );
cost_jp = GetCostJP( GetRequestedSkillLevel(), GetEnhance() );
if( cost_hp > 0 ) m_pOwner->SetHP( m_pOwner->GetHP() - cost_hp );
if( cost_mp > 0 ) m_pOwner->SetMP( m_pOwner->GetMP() - cost_mp );
if( cost_exp > 0 ) m_pOwner->SetEXP( m_pOwner->GetEXP() - cost_exp );
if( cost_jp > 0 ) m_pOwner->SetJP( m_pOwner->GetJobPoint() - cost_jp );
m_pOwner->RemoveEnergy( static_cast< int >( GetCostEnergy( GetRequestedSkillLevel() ) ) );
// 크리처 피 빼야하는 스킬들
if( GetSkillBase()->GetSkillEffectType() == SkillBase::EF_MAGIC_MULTIPLE_DAMAGE_T1_DEAL_SUMMON_HP_OLD )
{
int decHP = GetVar( 6 ) + GetVar( 7 ) * GetRequestedSkillLevel() + GetVar( 8 ) * GetEnhance();
if( m_pOwner->IsPlayer() ||
static_cast< StructPlayer * >( m_pOwner )->GetMainSummon() )
{
StructSummon * pSummon = static_cast< StructPlayer * >( m_pOwner )->GetMainSummon();
int nPrevHP = pSummon->GetHP();
pSummon->AddHP( 0 - std::min( pSummon->GetHP() - 1, decHP ) );
BroadcastHPMPMsg( pSummon, pSummon->GetHP() - nPrevHP, 0 );
}
}
else if( GetSkillBase()->GetSkillEffectType() == SkillBase::EF_MAGIC_MULTIPLE_DAMAGE_DEAL_SUMMON_HP )
{
int decHP = GetVar( 9 ) + GetVar( 10 ) * GetRequestedSkillLevel() + GetVar( 11 ) * GetEnhance();
if( m_pOwner->IsPlayer() ||
static_cast< StructPlayer * >( m_pOwner )->GetMainSummon() )
{
StructSummon * pSummon = static_cast< StructPlayer * >( m_pOwner )->GetMainSummon();
int nPrevHP = pSummon->GetHP();
pSummon->AddHP( 0 - std::min( pSummon->GetHP() - 1, decHP ) );
BroadcastHPMPMsg( pSummon, pSummon->GetHP() - nPrevHP, 0 );
}
}
ItemBase::ItemCode cost_item_code = GetCostItemCode();
__int64 nItemCostCount = GetCostItemCount( GetRequestedSkillLevel() );
StructPlayer *pInvenPlayer = NULL;
if( m_pOwner->IsPlayer() ) pInvenPlayer = static_cast< StructPlayer* >( m_pOwner );
if( m_pOwner->IsSummon() ) pInvenPlayer = static_cast< StructSummon* >( m_pOwner )->GetMaster();
if( pInvenPlayer )
{
if( cost_item_code )
{
if( cost_item_code == ItemBase::ITEM_CODE_WEARED_BULLET )
{
bSuccess = pInvenPlayer->EraseBullet( nItemCostCount );
}
else
{
StructItem * pCostItem = NULL;
pCostItem = pInvenPlayer->FindItem( cost_item_code );
if( pCostItem && pCostItem->GetCount() >= nItemCostCount )
{
pInvenPlayer->EraseItem( pCostItem, nItemCostCount );
}
else
{
bSuccess = false;
}
}
}
if( GetSkillBase()->GetEffectType() == SkillBase::EF_ADD_STATE_BY_USING_ITEM )
{
if( GetRequestedSkillLevel() > 0 && GetRequestedSkillLevel() < 20 )
{
ItemBase::ItemCode requiredItemCode = GetVar( 0 );
__int64 requiredItemCount = GetVar( GetRequestedSkillLevel() );
StructItem* costItem = pInvenPlayer->FindItem( requiredItemCode );
if ( costItem && requiredItemCount <= costItem->GetCount() )
{
pInvenPlayer->EraseItem( costItem, requiredItemCount );
}
else
{
bSuccess = false;
}
}
else
{
bSuccess = false;
}
}
}
}
FireSkill( pTarget, bSuccess );
if( GetSkillBase()->GetSkillEffectType() == SkillBase::EF_PHYSICAL_CHAIN_DAMAGE ||
GetSkillBase()->GetSkillEffectType() == SkillBase::EF_MAGIC_CHAIN_DAMAGE ||
GetSkillBase()->GetSkillEffectType() == SkillBase::EF_CHAIN_HEAL )
{
broadcastSkillMessage( m_pOwner->GetRX(), m_pOwner->GetRY(), GetCastRange() * 2 * GameRule::DEFAULT_UNIT_SIZE, m_pOwner->GetLayer(), cost_hp, cost_mp, TS_SC_SKILL::FIRE );
}
else if( pTarget )
{
broadcastSkillMessage( m_pOwner->GetRX(), m_pOwner->GetRY(), pTarget->GetRX(), pTarget->GetRY(), m_pOwner->GetLayer(), cost_hp, cost_mp, TS_SC_SKILL::FIRE );
}
else
{
broadcastSkillMessage( m_pOwner->GetRX(), m_pOwner->GetRY(), m_pOwner->GetLayer(), cost_hp, cost_mp, TS_SC_SKILL::FIRE );
}
if( !m_bMultiple || m_nCurrentFire == m_nTotalFire )
{
m_nFireTime += GetCommonDelay();
m_Status = STATUS_COMPLETE;
m_hCastItem = 0;
/* AziaMafia Delete Pierre name durability
// Diminue la durabilité de la pierre d'âme
for( int pos = 0 ; pos < 2 ; ++pos )
{
const ItemBase::ItemWearType WeaponIndices[ 2 ] = { ItemBase::WEAR_RIGHTHAND, ItemBase::WEAR_LEFTHAND };
ItemBase::ItemWearType idx = WeaponIndices[ pos ];
StructItem *pItem = m_pOwner->GetWearedItem( idx );
if( pItem && pItem->GetCurrentEndurance() > 0 && pItem->GetUsingSocketCount() > 0 )
{
pItem->SetCurrentEndurance( pItem->GetCurrentEndurance() - GameRule::ENDURANCE_REDUCE_ON_SKILL );
if( m_pOwner->IsPlayer() )
SendItemMessage( static_cast< StructPlayer * >( m_pOwner ), pItem );
if( pItem->GetCurrentEndurance() == 0 )
m_pOwner->SetNeedCalculateStat();
}
}
*/
}
}
if( m_Status == STATUS_COMPLETE )
{
// 스킬 시전할 때 이미 시간이 좀 흘렀을테니, 한번 더 체크해주자.
if( GetArTime() < m_nFireTime )
return;
// 스킬 시전 완료~
if( pTarget )
{
broadcastSkillMessage( m_pOwner->GetRX(), m_pOwner->GetRY(), pTarget->GetRX(), pTarget->GetRY(), m_pOwner->GetLayer(), 0, 0, TS_SC_SKILL::COMPLETE );
}
else
{
broadcastSkillMessage( m_pOwner->GetRX(), m_pOwner->GetRY(), m_pOwner->GetLayer(), 0, 0, TS_SC_SKILL::COMPLETE );
}
m_pOwner->OnCompleteSkill();
Init();
}
}
void StructSkill::process_target( AR_TIME t, struct SKILL_TARGET_FUNCTOR & functor, struct StructCreature * pTarget )
{
switch( GetSkillBase()->GetSkillTargetType() )
{
case SkillBase::TARGET_TARGET:
case SkillBase::TARGET_TARGET_EXCEPT_CASTER:
{
if( GetSkillBase()->GetSkillTargetType() == SkillBase::TARGET_TARGET_EXCEPT_CASTER &&
pTarget == m_pOwner )
{
break;
}
if( IsNeedTarget() == SkillBase::NEED_TARGET_TARGET )
{
// 대상에게 사용가능하다면
if( pTarget ) functor.onCreature( this, t, m_pOwner, pTarget );
}
else if( IsNeedTarget() == SkillBase::NEED_TARGET_SELF )
{
// 아니면 스스로에게 사용
functor.onCreature( this, t, m_pOwner, m_pOwner );
}
break;
}
case SkillBase::TARGET_REGION:
case SkillBase::TARGET_REGION_WITH_TARGET:
case SkillBase::TARGET_REGION_WITHOUT_TARGET:
{
if( pTarget ) functor.onCreature( this, t, m_pOwner, pTarget );
break;
}
case SkillBase::TARGET_PARTY:
{
if( !m_pOwner->IsPlayer() || !pTarget->IsPlayer() ) break;
StructPlayer* pPlayer = static_cast< StructPlayer* >( m_pOwner );
StructPlayer* pTargetPlayer = static_cast< StructPlayer* >( pTarget );
// 파티가 있는데 대상이 내 파티가 아니면 즐
if( pPlayer != pTargetPlayer && pPlayer->GetPartyID() && pPlayer->GetPartyID() != pTargetPlayer->GetPartyID() ) break;
struct myPartyFunctor : public PartyManager::PartyFunctor
{
myPartyFunctor( StructSkill *_pSkill, AR_TIME _t, StructCreature *_pOwner, SKILL_TARGET_FUNCTOR &_functor ) : pSkill( _pSkill ), t( _t ), pOwner( _pOwner ), partyFunctor( _functor ) {};
virtual bool operator()( AR_HANDLE handle )
{
StructCreature::iterator it = StructCreature::get( handle );
if( !pSkill || !(*it) || !(*it)->IsPlayer() )
return false;
// 적용 거리 체크
ArPosition pos = pOwner->GetPos();
if( pSkill->GetFireRange() < pos.GetDistance( (*it)->GetPos() ) )
return false;
return partyFunctor.onCreature( pSkill, t, pOwner, (*it) );
}
private:
struct SKILL_TARGET_FUNCTOR &partyFunctor;
StructSkill *pSkill;
StructCreature *pOwner;
AR_TIME t;
} fo( this, t, m_pOwner, functor );
// 파티가 없으면 자기 자신만 타겟
if( pPlayer->GetPartyID() == 0 )
{
fo( pPlayer->GetHandle() );
}
// 있으면 파티가 타겟
else
{
m_nTargetCount = static_cast<int>( PartyManager::GetInstance().DoEachMember( pPlayer->GetPartyID(), fo ) );
}
break;
}
case SkillBase::TARGET_GUILD:
{
if( !m_pOwner->IsPlayer() || !pTarget->IsPlayer() ) break;
StructPlayer* pPlayer = static_cast< StructPlayer* >( m_pOwner );
StructPlayer* pTargetPlayer = static_cast< StructPlayer* >( pTarget );
// 길드가 있는데 대상이 내 길드가 아니면 즐
if( pPlayer != pTargetPlayer && pPlayer->GetGuildID() && pPlayer->GetGuildID() != pTargetPlayer->GetGuildID() ) break;
struct myGuildFunctor : public GuildManager::GuildFunctor
{
myGuildFunctor( StructSkill *_pSkill, AR_TIME _t, StructCreature *_pOwner, SKILL_TARGET_FUNCTOR &_functor ) : pSkill( _pSkill ), t( _t ), pOwner( _pOwner ), guildFunctor( _functor ) {};
virtual bool operator()( AR_HANDLE handle )
{
StructCreature::iterator it = StructCreature::get( handle );
if( !pSkill || !(*it) || !(*it)->IsPlayer() )
return false;
// 적용 거리 체크
ArPosition pos = pOwner->GetPos();
if( pSkill->GetFireRange() < pos.GetDistance( (*it)->GetPos() ) )
return false;
return guildFunctor.onCreature( pSkill, t, pOwner, (*it) );
}
private:
struct SKILL_TARGET_FUNCTOR &guildFunctor;
StructSkill *pSkill;
StructCreature *pOwner;
AR_TIME t;
} fo( this, t, m_pOwner, functor );
// 길드가 없으면 자기 자신만 타겟
if( pPlayer->GetGuildID() == 0 )
{
fo( pPlayer->GetHandle() );
}
// 있으면 길드가 타겟
else
{
m_nTargetCount = static_cast<int>( GuildManager::GetInstance().DoEachMember( pPlayer->GetGuildID(), fo ) );
}
break;
}
case SkillBase::TARGET_ATTACKTEAM:
{
if( !m_pOwner->IsPlayer() || !pTarget->IsPlayer() ) break;
StructPlayer* pPlayer = static_cast< StructPlayer* >( m_pOwner );
StructPlayer* pTargetPlayer = static_cast< StructPlayer* >( pTarget );
// 파티가 있는데 대상이 내 파티가 아니면 즐
if( pPlayer != pTargetPlayer && pPlayer->GetPartyID() && pPlayer->GetPartyID() != pTargetPlayer->GetPartyID() ) break;
struct myPartyFunctor : public PartyManager::PartyFunctor
{
myPartyFunctor( StructSkill *_pSkill, AR_TIME _t, StructCreature *_pOwner, SKILL_TARGET_FUNCTOR &_functor ) : pSkill( _pSkill ), t( _t ), pOwner( _pOwner ), partyFunctor( _functor ) {};
virtual bool operator()( AR_HANDLE handle )
{
StructCreature::iterator it = StructCreature::get( handle );
if( !pSkill || !(*it) || !(*it)->IsPlayer() )
return false;
// 적용 거리 체크
ArPosition pos = pOwner->GetPos();
if( pSkill->GetFireRange() < pos.GetDistance( (*it)->GetPos() ) )
return false;
return partyFunctor.onCreature( pSkill, t, pOwner, (*it) );
}
private:
struct SKILL_TARGET_FUNCTOR &partyFunctor;
StructSkill *pSkill;
StructCreature *pOwner;
AR_TIME t;
} fo( this, t, m_pOwner, functor );
// 파티/공대가 있으면 파티가 타겟
int nPartyID = pPlayer->GetPartyID();
int nAttackTeamLeadPartyID = PartyManager::GetInstance().GetLinkedPartyLeadPartyID( pPlayer->GetPartyID() );
if( nAttackTeamLeadPartyID )
{
m_nTargetCount = static_cast<int>( PartyManager::GetInstance().DoEachLinkedPartyMember( nAttackTeamLeadPartyID, fo ) );
}
else if( nPartyID )
{
m_nTargetCount = static_cast<int>( PartyManager::GetInstance().DoEachMember( nPartyID, fo ) );
}
// 파티/공대가 없으면 자기 자신만 타겟
else
{
fo( pPlayer->GetHandle() );
m_nTargetCount = 1;
}
break;
}
case SkillBase::TARGET_SUMMON:
{
StructSummon *pSummon = NULL;
if( pTarget->IsSummon() && pTarget->IsInWorld() )
pSummon = static_cast< StructSummon * >( pTarget );
else if( pTarget->IsPlayer() && static_cast< StructPlayer * >( pTarget )->GetMainSummon() && static_cast< StructPlayer * >( pTarget )->GetMainSummon()->IsInWorld() )
pSummon = static_cast< StructSummon * >( static_cast< StructPlayer * >( pTarget )->GetMainSummon() );
if( !pSummon )
{
break;
}
ArPosition pos = pSummon->GetPos();
if( GetFireRange() < pos.GetDistance( pTarget->GetPos() ) )
break;
functor.onCreature( this, t, m_pOwner, pSummon );
break;
}
case SkillBase::TARGET_PARTY_SUMMON:
{
if( !m_pOwner->IsPlayer() ) break;
StructPlayer* pPlayer = static_cast< StructPlayer* >( m_pOwner );
struct myPartyFunctor : public PartyManager::PartyFunctor
{
myPartyFunctor( StructSkill *_pSkill, AR_TIME _t, StructCreature *_pOwner, SKILL_TARGET_FUNCTOR &_functor ) : pSkill( _pSkill ), t( _t ), pOwner( _pOwner ), partyFunctor( _functor ) {};
virtual bool operator()( AR_HANDLE handle )
{
StructCreature::iterator it = StructCreature::get( handle );
if( !pSkill || !(*it) || !(*it)->IsPlayer() || !static_cast< StructPlayer *>(*it)->GetMainSummon() || !static_cast< StructPlayer *>(*it)->GetMainSummon()->IsInWorld() )
return false;
// 적용 거리 체크
ArPosition pos = pOwner->GetPos();
if( pSkill->GetFireRange() < pos.GetDistance( static_cast< StructPlayer *>(*it)->GetMainSummon()->GetPos() ) )
return false;
return partyFunctor.onCreature( pSkill, t, pOwner, static_cast< StructPlayer * >(*it)->GetMainSummon() );
}
private:
struct SKILL_TARGET_FUNCTOR &partyFunctor;
StructSkill *pSkill;
StructCreature *pOwner;
AR_TIME t;
} fo( this, t, m_pOwner, functor );
// 파티가 없으면 자기 자신만 타겟
if( pPlayer->GetPartyID() == 0 )
{
fo( pPlayer->GetHandle() );
}
// 있으면 파티가 타겟
else
{
m_nTargetCount = static_cast<int>( PartyManager::GetInstance().DoEachMember( pPlayer->GetPartyID(), fo ) );
}
break;
}
case SkillBase::TARGET_SELF_WITH_SUMMON:
{
StructPlayer *pTargetPlayer = NULL;
if( pTarget->IsPlayer() )
pTargetPlayer = static_cast< StructPlayer * >( pTarget );
else if( pTarget->IsSummon() && static_cast< StructSummon * >( pTarget )->GetMaster() )
pTargetPlayer = static_cast< StructSummon * >( pTarget )->GetMaster();
if( !pTargetPlayer || !pTargetPlayer->IsInWorld() )
break;
functor.onCreature( this, t, m_pOwner, pTargetPlayer );
ArPosition pos = m_pOwner->GetPos();
StructSummon *pSummon = pTargetPlayer->GetMainSummon();
if( pSummon && pSummon->IsInWorld() && pSummon->IsAlive() && GetFireRange() >= pos.GetDistance( pSummon->GetPos() ) )
functor.onCreature( this, t, m_pOwner, pSummon );
pSummon = pTargetPlayer->GetSubSummon();
if( pSummon && pSummon->IsInWorld() && pSummon->IsAlive() && GetFireRange() >= pos.GetDistance( pSummon->GetPos() ) )
functor.onCreature( this, t, m_pOwner, pTargetPlayer->GetSubSummon() );
break;
}
case SkillBase::TARGET_PARTY_WITH_SUMMON:
{
if( !( m_pOwner->IsPlayer() || m_pOwner->IsSummon() ) || !( pTarget->IsPlayer() || pTarget->IsSummon() ) ) break;
StructPlayer* pPlayer = NULL;
if( m_pOwner->IsPlayer() )
pPlayer = static_cast< StructPlayer * >( m_pOwner );
else if( m_pOwner->IsSummon() )
pPlayer = static_cast< StructSummon * >( m_pOwner )->GetMaster();
if( !pPlayer )
break;
StructPlayer* pTargetPlayer = NULL;
if( pTarget->IsPlayer() )
pTargetPlayer = static_cast< StructPlayer * >( pTarget );
else if( pTarget->IsSummon() )
pTargetPlayer = static_cast< StructSummon * >( pTarget )->GetMaster();
if( !pTargetPlayer )
break;
// 파티가 있는데 대상이 내 파티가 아니면 즐
if( pPlayer != pTargetPlayer && pPlayer->GetPartyID() && pPlayer->GetPartyID() != pTargetPlayer->GetPartyID() ) break;
struct myPartyFunctor : public PartyManager::PartyFunctor
{
myPartyFunctor( StructSkill *_pSkill, AR_TIME _t, StructCreature *_pOwner, SKILL_TARGET_FUNCTOR &_functor ) : pSkill( _pSkill ), t( _t ), pOwner( _pOwner ), partyFunctor( _functor ) {};
virtual bool operator()( AR_HANDLE handle )
{
StructCreature::iterator it = StructCreature::get( handle );
StructCreature * pPartyTarget = (*it);
if( !pSkill || !pPartyTarget || !pPartyTarget->IsInWorld() )
return false;
bool bResult = false;
// { 파티원 처리
// 적용 거리 체크
ArPosition pos = pOwner->GetPos();
if( pSkill->GetFireRange() >= pos.GetDistance( pPartyTarget->GetPos() ) )
{
bResult = partyFunctor.onCreature( pSkill, t, pOwner, pPartyTarget );
}
// } 파티원 처리
// { 파티원 소환수 처리
pPartyTarget = static_cast< StructPlayer * >( pPartyTarget )->GetMainSummon();
if( pPartyTarget && pPartyTarget->IsInWorld() &&
pSkill->GetFireRange() >= pos.GetDistance( pPartyTarget->GetPos() ) ) // 적용 거리 체크
{
bResult |= partyFunctor.onCreature( pSkill, t, pOwner, pPartyTarget );
}
// } 파티원 소환수 처리
return bResult;
}
private:
struct SKILL_TARGET_FUNCTOR &partyFunctor;
StructSkill *pSkill;
StructCreature *pOwner;
AR_TIME t;
} fo( this, t, m_pOwner, functor );
// 파티가 없으면 자기 자신만 타겟
if( pPlayer->GetPartyID() == 0 )
{
fo( pPlayer->GetHandle() );
}
// 있으면 파티가 타겟
else
{
m_nTargetCount = static_cast<int>( PartyManager::GetInstance().DoEachMember( pPlayer->GetPartyID(), fo ) );
}
break;
}
case SkillBase::TARGET_MASTER:
{
if( !m_pOwner->IsSummon() ) break;
StructPlayer *pTargetPlayer = static_cast< StructSummon * >( m_pOwner )->GetMaster();
if( !pTargetPlayer || !pTargetPlayer->IsInWorld() ) break;
functor.onCreature( this, t, m_pOwner, pTargetPlayer );
break;
}
case SkillBase::TARGET_SELF_WITH_MASTER:
{
if( !m_pOwner->IsSummon() || !m_pOwner->IsInWorld() ) break;
functor.onCreature( this, t, m_pOwner, m_pOwner );
StructPlayer *pTargetPlayer = static_cast< StructSummon * >( m_pOwner )->GetMaster();
if( !pTargetPlayer || !pTargetPlayer->IsInWorld() ) break;
if( pTargetPlayer->GetPos().GetDistance( m_pOwner->GetPos() ) > GameRule::VISIBLE_RANGE )
break;
functor.onCreature( this, t, m_pOwner, pTargetPlayer );
break;
}
}
return;
}
int StructSkill::CalculateMPCost( const SkillCostModifier& modifier )
{
int absCost = GetCostMP( GetRequestedSkillLevel(), GetEnhance() );
float percentCost = GetCostMPPercent( GetRequestedSkillLevel() ) * m_pOwner->GetMP() / 100;
float costRatio = m_pOwner->GetManaCostRatio( (Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
float costBeforeModify = ( absCost + percentCost ) * costRatio;
SkillCostModifier appliedModifier( m_CostModifier );
appliedModifier.add( modifier );
return appliedModifier.calculateMPCost( costBeforeModify );
}
int StructSkill::CalculateHPCost( const SkillCostModifier& modifier )
{
int absCost = GetCostHP( GetRequestedSkillLevel() );
float percentCost = GetCostHPPercent( GetRequestedSkillLevel() ) * m_pOwner->GetMaxHP() / 100;
float costBeforeModify = absCost + percentCost;
SkillCostModifier appliedModifier( m_CostModifier );
appliedModifier.add( modifier );
return appliedModifier.calculateHPCost( costBeforeModify );
}
struct lessByDistantFromTarget : public std::binary_function< StructCreature *, StructCreature *, bool >
{
lessByDistantFromTarget( const ArPosition * _pTarget ) : pTarget( _pTarget ) {}
result_type operator() ( first_argument_type a, second_argument_type b )
{
return pTarget->GetDistance( a->GetPos() ) < pTarget->GetDistance( b->GetPos() );
}
const ArPosition * pTarget;
};
struct RegionTester
{
virtual void Init( const ArPosition & OriginalPos, const ArPosition & TargetPos, const float RegionProperty ) = 0;
virtual bool IsInRegion( const ArPosition & pos ) = 0;
};
struct CircleRegionTester : public RegionTester
{
void Init( const ArPosition & OriginalPos, const ArPosition & TargetPos, const float RegionProperty )
{
}
bool IsInRegion( const ArPosition & pos )
{
return true;
}
};
struct ArcCircleRegionTester : public RegionTester
{
void Init( const ArPosition & OriginalPos, const ArPosition & TargetPos, const float RegionProperty )
{
float _V1x = TargetPos.x - OriginalPos.x;
float _V1y = TargetPos.y - OriginalPos.y;
// normalize 해서 저장한다.
float m = sqrt( _V1x * _V1x + _V1y * _V1y );
if( m == 0 )
{
V1x = 1;
V1y = 0;
}
else
{
V1x = _V1x / m;
V1y = _V1y / m;
}
x = OriginalPos.x;
y = OriginalPos.y;
fCos = cos( RegionProperty );
}
bool IsInRegion( const ArPosition & pos )
{
float _V2x = pos.x - x;
float _V2y = pos.y - y;
float V2x, V2y;
float m = sqrt( _V2x * _V2x + _V2y * _V2y );
if( m == 0 )
return true;
V2x = _V2x / m;
V2y = _V2y / m;
return fCos <= (V1x * V2x + V1y * V2y);
}
float V1x; // 기준->타겟의 노말 벡터
float V1y; // 기준->타겟의 노말 벡터
float x; // 기준점
float y; // 기준점
float fCos; // 기준 Cosine 값
};
struct DirectionRegionTester : public RegionTester
{
void Init( const ArPosition & OriginalPos, const ArPosition & TargetPos, const float RegionProperty )
{
dx = TargetPos.x - OriginalPos.x;
dy = TargetPos.y - OriginalPos.y;
c = dy * OriginalPos.x - dx * OriginalPos.y;
denominator = sqrt( dx * dx + dy * dy );
thickness = RegionProperty * GameRule::DEFAULT_UNIT_SIZE / 2;
float _V1x = TargetPos.x - OriginalPos.x;
float _V1y = TargetPos.y - OriginalPos.y;
// normalize 해서 저장한다.
float m = sqrt( _V1x * _V1x + _V1y * _V1y );
V1x = _V1x / m;
V1y = _V1y / m;
ori_x = OriginalPos.x;
ori_y = OriginalPos.y;
/*
x = TargetPos.y - OriginalPos.y;
y = OriginalPos.x - TargetPos.x;
c = 0 - x * OriginalPos.x - y * OriginalPos.y;
denominator = sqrt( x * x + y * y );
thickness = RegionProperty * GameRule::DEFAULT_UNIT_SIZE / 2;
float _V1x = TargetPos.x - OriginalPos.x;
float _V1y = TargetPos.y - OriginalPos.y;
// normalize 해서 저장한다.
float m = sqrt( _V1x * _V1x + _V1y * _V1y );
V1x = _V1x / m;
V1y = _V1y / m;
ori_x = OriginalPos.x;
ori_y = OriginalPos.y;
*/
}
// 점과 직선사이의 거리 공식
// d = │ap+bq+c│/√( a²+b²)
bool IsInRegion( const ArPosition & pos )
{
float dist = abs( -dy * pos.x + dx * pos.y + c ) / denominator ;
if( thickness > dist )
{
float _V2x = pos.x - ori_x;
float _V2y = pos.y - ori_y;
float V2x, V2y;
float m = sqrt( _V2x * _V2x + _V2y * _V2y );
V2x = _V2x / m;
V2y = _V2y / m;
return 0 <= (V1x * V2x + V1y * V2y);
}
/*
if( thickness > ( abs( x * pos.x + y * pos.y + c ) / denominator ) )
{
float _V2x = pos.x - ori_x;
float _V2y = pos.y - ori_y;
float V2x, V2y;
float m = sqrt( _V2x * _V2x + _V2y * _V2y );
V2x = _V2x / m;
V2y = _V2y / m;
return 0 <= (V1x * V2x + V1y * V2y);
}
*/
return false;
}
float V1x; // 기준->타겟의 노말 벡터
float V1y; // 기준->타겟의 노말 벡터
float ori_x; // 기준점
float ori_y; // 기준점
/*
float x; // x 변이
float y; // y 변이
*/
float dx;
float dy;
float c; // 상수 -_-
float thickness; // 직선 두께
float denominator; // sqrt( x * x + y * y ) 미리 계산해놓은 것.
};
struct CrossRegionTester : public RegionTester
{
void Init( const ArPosition & OriginalPos, const ArPosition & TargetPos, const float RegionProperty )
{
x1 = TargetPos.y - OriginalPos.y;
y1 = OriginalPos.x - TargetPos.x;
c1 = 0 - x1 * OriginalPos.x - y1 * OriginalPos.y;
x2 = y1;
y2 = -x1;
c2 = 0 - x2 * OriginalPos.x - y2 * OriginalPos.y;
denominator = sqrt( x1 * x1 + y1 * y1 ); // 두 직선에 대해 같이 사용할 수 있음
thickness = RegionProperty * GameRule::DEFAULT_UNIT_SIZE / 2;
}
// 점과 직선사이의 거리 공식
// d = │ap+bq+c│/√( a²+b²)
bool IsInRegion( const ArPosition & pos )
{
return ( thickness > ( abs( x1 * pos.x + y1 * pos.y + c1 ) / denominator ) ) || ( thickness > ( abs( x2 * pos.x + y2 * pos.y + c2 ) / denominator ) );
}
// OriginalPos와 TargetPos를 가로지르는 직선
float x1; // x 변이
float y1; // y 변이
float c1; // 상수 -_-
// OriginalPos를 지나고 위 직선에 직교하는 직선
float x2;
float y2;
float c2;
float thickness; // 직선 두께
float denominator; // sqrt( x * x + y * y ) 미리 계산해놓은 것.
};
// nRegionType 이 -1 이면 기본 원형
int EnumSkillTargetsAndCalcDamage( const ArPosition & _OriginalPos, unsigned char layer, const ArPosition & _TargetPos, bool bTargetOrigin, const float fEffectLength, const int nRegionType, const float fRegionProperty, const int nOriginalDamage, const bool bIncludeOriginalPos, StructCreature * pCaster, const int nDistributeType, const int nTargetMax, /*out*/ std::vector< StructCreature * > & vTargetList, bool bEnemyOnly )
{
int nResult = nOriginalDamage;
// 타겟 기준인지, 아닌지
const ArPosition & OriginalPos = bTargetOrigin ? _TargetPos : _OriginalPos;
const ArPosition & TargetPos = bTargetOrigin ? _OriginalPos : _TargetPos;
std::vector< AR_HANDLE > vList;
{
ArcadiaServer::Instance().EnumMovableObject( OriginalPos, layer, fEffectLength, &vList );
}
vTargetList.clear();
vTargetList.reserve( vList.size() );
RegionTester * pTester = NULL;
ArcCircleRegionTester RegionTester_ArcCircle;
DirectionRegionTester RegionTester_Direction;
CrossRegionTester RegionTester_Cross;
CircleRegionTester RegionTester_Circle;
if( nRegionType == SkillBase::REGION_TYPE_ARC_CIRCLE )
{
pTester = &RegionTester_ArcCircle;
}
else if( nRegionType == SkillBase::REGION_TYPE_DIRECTION )
{
pTester = &RegionTester_Direction;
}
else if( nRegionType == SkillBase::REGION_TYPE_CROSS )
{
pTester = &RegionTester_Cross;
}
else
{
pTester = &RegionTester_Circle;
}
pTester->Init( OriginalPos, TargetPos, fRegionProperty );
int nTargetCount = 0;
int nAllyCount = 0;
// { 공격할 애들 enumeration
for( std::vector< AR_HANDLE >::iterator itRawTargetList = vList.begin(); itRawTargetList != vList.end(); ++itRawTargetList )
{
bool bIsAlly = false;
GameObject *pObj = GameObject::raw_get( *itRawTargetList );
if( !pObj ) continue;
if( !pObj->IsCreature() || pObj->IsPet() ) continue;
StructCreature * pTarget = static_cast< StructCreature * > ( pObj );
if( pTarget->IsDead() ) continue;
if( !pCaster->IsEnemy( pTarget, true ) )
{
if( bEnemyOnly )
continue;
bIsAlly = true;
}
ArPosition current_pos = pObj->GetCurrentPosition( GetArTime() );
if( !pTester->IsInRegion( current_pos ) )
{
continue;
}
if( !bIncludeOriginalPos && OriginalPos == current_pos )
{
continue;
}
if( bIsAlly )
{
vTargetList.insert( vTargetList.begin(), pTarget );
++nAllyCount;
}
else
{
vTargetList.push_back( pTarget );
++nTargetCount;
}
}
// }
// { 분산 타입 처리
if( nDistributeType == SkillBase::DISTRIBUTION_TYPE_SEQUENTIAL_TARGET )
{
std::sort( vTargetList.begin() + nAllyCount, vTargetList.end(), lessByDistantFromTarget( &_TargetPos ) );
}
else if( nDistributeType == SkillBase::DISTRIBUTION_TYPE_SEQUENTIAL_CASTER )
{
std::sort( vTargetList.begin() + nAllyCount, vTargetList.end(), lessByDistantFromTarget( &_OriginalPos ) );
}
if( nDistributeType == SkillBase::DISTRIBUTION_TYPE_RANDOM ||
nDistributeType == SkillBase::DISTRIBUTION_TYPE_SEQUENTIAL_TARGET ||
nDistributeType == SkillBase::DISTRIBUTION_TYPE_SEQUENTIAL_CASTER )
{
if( nTargetCount > nTargetMax )
vTargetList.resize( nAllyCount + nTargetMax );
}
if( nDistributeType == SkillBase::DISTRIBUTION_TYPE_DISTRIBUTE )
{
if( nTargetCount > nTargetMax )
nResult = nResult * nTargetMax / nTargetCount;
}
// }
return nResult;
}
int EnumChainSkillTargetsAndCalcDamage( const float fEffectLength, const float fChainLength, const int nChainType, const bool bIncludeTarget, StructCreature * pCaster, StructCreature * & pCurrentTarget, const int nChainMax, /*out*/ std::vector< StructCreature * > & vTargetList, const int nTargetType )
{
AR_TIME t = GetArTime();
std::vector< AR_HANDLE > vList;
{
// 연쇄 이동 방식은 대상을 타고 여러 번 이동해가므로 범위를 예측하기 어렵다.
// 따라서 대상의 전체 목록은 시전자를 기준으로 제한한다.
ArcadiaServer::Instance().EnumMovableObject( pCaster->GetCurrentPosition( t ), pCaster->GetLayer(), fEffectLength, &vList );
}
vTargetList.clear();
vTargetList.reserve( vList.size() );
int nCount = 0;
std::vector< AR_HANDLE >::iterator it = std::find( vList.begin(), vList.end(), pCurrentTarget->GetHandle() );
if( it == vList.end() ) return false;
vector_fast_erase( &vList, it );
if( bIncludeTarget )
{
vTargetList.push_back( pCurrentTarget );
++nCount;
}
while( nCount < nChainMax )
{
std::vector< AR_HANDLE >::iterator itExpectedTarget;
StructCreature * pExpectedTarget = NULL;
AR_UNIT fMinLength = FLT_MAX;
int nMaxHPLoss = -1;
int nMaxHPLossPercentage = -1;
// { 공격할 애들 enumeration
for( it = vList.begin(); vList.empty() == false && it != vList.end(); ++it )
{
GameObject *pObj = GameObject::raw_get( *it );
if( !pObj ) continue;
if( !pObj->IsCreature() || pObj->IsPet() ) continue;
StructCreature * pTarget = static_cast< StructCreature * > ( pObj );
if( pTarget->IsDead() ) continue;
// 효과 대상 1 : 동료 & 중립
if( nTargetType == 1 && pCaster->IsEnemy( pTarget, true ) )
continue;
// 효과 대상 2 : 동료
else if( nTargetType == 2 && !pCaster->IsAlly( pTarget ) )
continue;
// 효과 대상 3 : 적
else if( nTargetType == 3 && !pCaster->IsEnemy( pTarget, true ) )
continue;
float fLength = pTarget->GetCurrentPosition( t ).GetDistance( pCurrentTarget->GetCurrentPosition( t ) );
if( fLength >= fChainLength )
{
continue;
}
// { 체인 대상 선정 방식
if( nChainType == SkillBase::CHAIN_TYPE_DISTANCE )
{
if( fLength < fMinLength )
{
itExpectedTarget = it;
pExpectedTarget = pTarget;
fMinLength = fLength;
}
}
else if( nChainType == SkillBase::CHAIN_TYPE_RANDOM )
{
itExpectedTarget = it;
pExpectedTarget = pTarget;
break;
}
else if( nChainType == SkillBase::CHAIN_TYPE_HP_LOSS )
{
if( nMaxHPLoss < pTarget->GetMaxHP() - pTarget->GetHP() )
{
itExpectedTarget = it;
pExpectedTarget = pTarget;
nMaxHPLoss = pTarget->GetMaxHP() - pTarget->GetHP();
}
}
else if( nChainType == SkillBase::CHAIN_TYPE_HP_LOSS_PERCENTAGE )
{
if( nMaxHPLossPercentage < 100 - pTarget->GetHPPercentage() )
{
itExpectedTarget = it;
pExpectedTarget = pTarget;
nMaxHPLossPercentage = 100 - pTarget->GetHPPercentage();
}
}
}
// }
// 더 이상 대상이 없다
if( pExpectedTarget == NULL ) return nCount;
StructCreature *pCurrentTarget = pExpectedTarget;
vector_fast_erase( &vList, itExpectedTarget );
vTargetList.push_back( pCurrentTarget );
++nCount;
}
return nCount;
}
bool StructSkill::ProcAura()
{
AR_TIME t = GetArTime();
StructSummon *pSummon = NULL;
if( GetAuraMPDecTime() < t + GameRule::DEFAULT_MP_DEC_TIME )
{
float fDecMP = GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(2) * GetEnhance();
if( m_pOwner->GetMP() < fDecMP )
{
return false;
}
m_pOwner->SetMP( m_pOwner->GetMP() - fDecMP );
SetAuraMPDecTime( t );
if( m_pOwner->IsInWorld() )
BroadcastHPMPMsg( m_pOwner, 0, m_pOwner->GetMP() - fDecMP );
if( m_pOwner->IsSummon() )
{
pSummon = static_cast< StructSummon * >( m_pOwner );
if( pSummon->GetMaster() && pSummon->IsInWorld() )
{
SendHPMPMsg( pSummon->GetMaster(), m_pOwner, 0, m_pOwner->GetMP() - fDecMP );
}
}
}
if( !m_pOwner->IsInWorld() )
return true;
std::vector< AR_HANDLE > vList;
ArcadiaServer::Instance().EnumMovableObject( m_pOwner->GetCurrentPosition( t ), m_pOwner->GetLayer(), GetFireRange(), &vList, true, true );
// 대상 없으면 KIN
if( vList.empty() ) return true;
AR_TIME et = t + SkillBase::TOGGLE_LIVE_TIME;
std::vector< AR_HANDLE >::iterator it;
StructCreature *pTarget;
int target_type = GetSkillBase()->GetSkillTargetType();
bool bApplySummon = false;
for( it = vList.begin(); it != vList.end(); ++it )
{
GameObject * pObj = GameObject::raw_get( *it );
if( !pObj || pObj->IsPet() ) continue;
pTarget = static_cast< StructCreature* >( pObj );
// 아니면 KIN
if( GetSkillBase()->GetSkillEffectType() == SkillBase::EF_TOGGLE_DIFFERENTIAL_AURA )
{
// 시전자가 소환수이면 무조건 자신에게 건다
if( pSummon )
{
pTarget = pSummon;
}
else if( pTarget->IsSummon() )
{
if( static_cast< StructPlayer* >( m_pOwner ) != static_cast< StructSummon* >( pTarget )->GetMaster() ) continue;
}
else continue;
if( GetVar(5) )
{
pTarget->PendAddState( (StructState::StateCode) (int) GetVar(5),
m_pOwner->GetHandle(),
GetStateLevel( GetRequestedSkillLevel(), GetEnhance() ),
t, et, true );
}
if( GetVar(6) )
{
pTarget->PendAddState( (StructState::StateCode) (int) GetVar(6),
m_pOwner->GetHandle(),
GetStateLevel( GetRequestedSkillLevel(), GetEnhance() ),
t, et, true );
}
if( GetVar(7) )
{
pTarget->PendAddState( (StructState::StateCode) (int) GetVar(7),
m_pOwner->GetHandle(),
GetStateLevel( GetRequestedSkillLevel(), GetEnhance() ),
t, et, true );
}
bApplySummon = true;
// 분할 토글형 스킬은 반드시 타겟이 한 마리이기 때문에 지속효과가 걸린다면 break 처리한다.
break;
}
else if( target_type == SkillBase::TARGET_TARGET )
{
if( pTarget != m_pOwner )
continue;
}
else if( pTarget->IsSummon() )
{
if( target_type != SkillBase::TARGET_PARTY_SUMMON
&& target_type != SkillBase::TARGET_PARTY_WITH_SUMMON
&& target_type != SkillBase::TARGET_SUMMON
&& target_type != SkillBase::TARGET_CREATURE_TYPE_NONE
&& target_type != SkillBase::TARGET_CREATURE_TYPE_FIRE
&& target_type != SkillBase::TARGET_CREATURE_TYPE_WATER
&& target_type != SkillBase::TARGET_CREATURE_TYPE_WIND
&& target_type != SkillBase::TARGET_CREATURE_TYPE_EARTH
&& target_type != SkillBase::TARGET_CREATURE_TYPE_LIGHT
&& target_type != SkillBase::TARGET_CREATURE_TYPE_DARK
&& target_type != SkillBase::TARGET_SELF_WITH_SUMMON
&& target_type != SkillBase::TARGET_SELF_WITH_MASTER ) continue;
if( target_type == SkillBase::TARGET_PARTY_SUMMON || target_type == SkillBase::TARGET_PARTY_WITH_SUMMON )
{
if( static_cast< StructPlayer* >( m_pOwner )->GetPartyID() )
{
if( static_cast< StructPlayer* >( m_pOwner )->GetPartyID() != static_cast< StructSummon* >( pTarget )->GetMaster()->GetPartyID() ) continue;
}
else
{
if( static_cast< StructPlayer* >( m_pOwner ) != static_cast< StructSummon* >( pTarget )->GetMaster() ) continue;
}
}
else if( target_type == SkillBase::TARGET_SELF_WITH_MASTER )
{
// 현재 타겟이 소환수 타입이니 자신 아니면 안 걸림
if( m_pOwner != pTarget ) continue;
}
else
{
if( static_cast< StructPlayer* >( m_pOwner ) != static_cast< StructSummon* >( pTarget )->GetMaster() ) continue;
if( target_type == SkillBase::TARGET_CREATURE_TYPE_NONE && static_cast< StructSummon* >( pTarget )->GetSummonElementalType() != Elemental::TYPE_NONE ) continue;
if( target_type == SkillBase::TARGET_CREATURE_TYPE_FIRE && static_cast< StructSummon* >( pTarget )->GetSummonElementalType() != Elemental::TYPE_FIRE ) continue;
if( target_type == SkillBase::TARGET_CREATURE_TYPE_WATER && static_cast< StructSummon* >( pTarget )->GetSummonElementalType() != Elemental::TYPE_WATER ) continue;
if( target_type == SkillBase::TARGET_CREATURE_TYPE_WIND && static_cast< StructSummon* >( pTarget )->GetSummonElementalType() != Elemental::TYPE_WIND ) continue;
if( target_type == SkillBase::TARGET_CREATURE_TYPE_EARTH && static_cast< StructSummon* >( pTarget )->GetSummonElementalType() != Elemental::TYPE_EARTH ) continue;
if( target_type == SkillBase::TARGET_CREATURE_TYPE_LIGHT && static_cast< StructSummon* >( pTarget )->GetSummonElementalType() != Elemental::TYPE_LIGHT ) continue;
if( target_type == SkillBase::TARGET_CREATURE_TYPE_DARK && static_cast< StructSummon* >( pTarget )->GetSummonElementalType() != Elemental::TYPE_DARK ) continue;
}
}
else if( pTarget->IsPlayer() )
{
if( target_type != SkillBase::TARGET_PARTY &&
target_type != SkillBase::TARGET_PARTY_WITH_SUMMON &&
target_type != SkillBase::TARGET_SELF_WITH_SUMMON &&
target_type != SkillBase::TARGET_MASTER &&
target_type != SkillBase::TARGET_SELF_WITH_MASTER ) continue;
if( target_type == SkillBase::TARGET_PARTY || target_type == SkillBase::TARGET_PARTY_WITH_SUMMON )
{
StructPlayer * pOwnPlayer = NULL;
if( m_pOwner->IsPlayer() ) pOwnPlayer = static_cast< StructPlayer* >( m_pOwner );
if( m_pOwner->IsSummon() ) pOwnPlayer = static_cast< StructSummon* >( m_pOwner )->GetMaster();
if( !pOwnPlayer ) continue;
if( !pOwnPlayer->IsInParty() && pTarget != m_pOwner ) continue;
if( pOwnPlayer ->GetPartyID() != static_cast< StructPlayer* >( pTarget )->GetPartyID() ) continue;
}
else if( target_type == SkillBase::TARGET_SELF_WITH_SUMMON )
{
if( pTarget != m_pOwner ) continue;
}
else if( target_type == SkillBase::TARGET_SELF_WITH_MASTER )
{
if( pTarget != static_cast< StructSummon * >( m_pOwner )->GetMaster() ) continue;
}
}
else
{
continue;
}
pTarget->PendAddState( (StructState::StateCode)GetStateId(),
m_pOwner->GetHandle(),
GetStateLevel( GetRequestedSkillLevel(), GetEnhance() ),
t, et, true );
if( GetVar(3) )
{
pTarget->PendAddState( (StructState::StateCode) (int) GetVar(3),
m_pOwner->GetHandle(),
GetStateLevel( GetRequestedSkillLevel(), GetEnhance() ),
t, et, true );
}
if( GetVar(4) )
{
pTarget->PendAddState( (StructState::StateCode) (int) GetVar(4),
m_pOwner->GetHandle(),
GetStateLevel( GetRequestedSkillLevel(), GetEnhance() ),
t, et, true );
}
}
if( GetSkillBase()->GetSkillEffectType() == SkillBase::EF_TOGGLE_DIFFERENTIAL_AURA )
{
if( !bApplySummon )
return false; // 적용된 크리처 없으면 토글 해제
if( pSummon )
pTarget = pSummon->GetMaster();
else
pTarget = m_pOwner;
pTarget->PendAddState( (StructState::StateCode)GetStateId(),
m_pOwner->GetHandle(),
GetStateLevel( GetRequestedSkillLevel(), GetEnhance() ),
t, et, true );
if( GetVar(3) )
{
pTarget->PendAddState( (StructState::StateCode) (int) GetVar(3),
m_pOwner->GetHandle(),
GetStateLevel( GetRequestedSkillLevel(), GetEnhance() ),
t, et, true );
}
if( GetVar(4) )
{
pTarget->PendAddState( (StructState::StateCode) (int) GetVar(4),
m_pOwner->GetHandle(),
GetStateLevel( GetRequestedSkillLevel(), GetEnhance() ),
t, et, true );
}
}
return true;
}
void StructSkill::SINGLE_MAGICAL_DAMAGE_T1( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int nDamage = 0;
nDamage = m_pOwner->GetMagicPoint((Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() ) + GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(2) * GetEnhance();
int elemental_type = GetSkillBase()->GetElementalType();
StructCreature::_DAMAGE_INFO Damage = pTarget->DealMagicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::MAGIC_DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
return;
}
void StructSkill::SINGLE_MAGICAL_DAMAGE_T2( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int nDamage = 0;
int nMagicPoint = m_pOwner->GetMagicPoint((Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
nDamage = nMagicPoint * ( GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(4) * GetEnhance() );
int nMax = nMagicPoint + GetVar(2) + GetVar(3) * GetRequestedSkillLevel() + GetVar(5) * GetEnhance();
nDamage = std::min( nDamage, nMax );
int elemental_type = GetSkillBase()->GetElementalType();
StructCreature::_DAMAGE_INFO Damage = pTarget->DealMagicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::MAGIC_DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
return;
}
void StructSkill::SINGLE_MAGICAL_DAMAGE( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int nDamage = m_pOwner->GetMagicPoint( (Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
nDamage *= GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(2) * GetEnhance();
nDamage += GetVar(3) + GetVar(4) * GetRequestedSkillLevel() + GetVar(5) * GetEnhance();
if( GetSkillBase()->GetEffectType() == SkillBase::EF_MAGIC_SINGLE_DAMAGE_WITH_PHYSICAL_DAMAGE )
{
int nPhysicalDamage = m_pOwner->GetAttackPointRight( (Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
nPhysicalDamage *= GetVar(6) + GetVar(7) * GetRequestedSkillLevel() + GetVar(10) * GetEnhance();
nPhysicalDamage += GetVar(8) + GetVar(9) * GetRequestedSkillLevel() + GetVar(11) * GetEnhance();
nDamage += nPhysicalDamage;
}
int elemental_type = GetSkillBase()->GetElementalType();
StructCreature::_DAMAGE_INFO Damage = pTarget->DealMagicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::MAGIC_DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
return;
}
void StructSkill::MULTIPLE_MAGICAL_DAMAGE_AT_ONCE( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int nMagicPoint = m_pOwner->GetMagicPoint((Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
int nDamage = ( GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(2) * GetEnhance() ) * nMagicPoint + GetVar(3) + GetVar(4) * GetRequestedSkillLevel() + GetVar(5) * GetEnhance();
int nCount = GetVar(6) + GetVar(7)*GetRequestedSkillLevel();
int elemental_type = GetSkillBase()->GetElementalType();
// 데미지
for( int i = 0; i < nCount; ++i )
{
StructCreature::_DAMAGE_INFO Damage = pTarget->DealMagicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::MAGIC_DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
}
m_nFireCount = nCount;
// 연타 횟수 만큼 행동 불가
m_nFireTime += GetVar(8) * nCount * 100;
return;
}
void StructSkill::MULTIPLE_MAGICAL_DAMAGE_T2( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int nMagicPoint = m_pOwner->GetMagicPoint((Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
int nDamage = nMagicPoint * ( GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(5) * GetEnhance() );
if( m_nCurrentFire == 0 )
{
// { 횟수 계산
m_nTotalFire = GetVar(2) + GetVar(3)*GetRequestedSkillLevel();
// }
m_nCurrentFire = 1;
}
else
{
++m_nCurrentFire;
}
int elemental_type = GetSkillBase()->GetElementalType();
// 데미지
StructCreature::_DAMAGE_INFO Damage = pTarget->DealMagicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::MAGIC_DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
m_nFireTime += GetVar(4) * 100;
return;
}
void StructSkill::MULTIPLE_MAGICAL_DAMAGE_T1( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int nDamage = m_pOwner->GetMagicPoint((Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() ) + GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(5) * GetEnhance();
if( m_nCurrentFire == 0 )
{
// { 횟수 계산
m_nTotalFire = GetVar(2) + GetVar(3)*GetRequestedSkillLevel();
// }
m_nCurrentFire = 1;
}
else
{
++m_nCurrentFire;
}
int elemental_type = GetSkillBase()->GetElementalType();
// 데미지
StructCreature::_DAMAGE_INFO Damage = pTarget->DealMagicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::MAGIC_DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
m_nFireTime += GetVar(4) * 100;
return;
}
void StructSkill::SINGLE_MAGICAL_DAMAGE_OR_DEATH( struct StructCreature* pTarget )
{
if( !pTarget || !pTarget->IsMonster() ) return;
int elemental_type = GetSkillBase()->GetElementalType();
// 대상이 해당 종족에 맞는지 다시 체크(Cast에서 이미 한 번 체크하게 되어있음)
// 99:CREATURE_ALL(모든 크리처 종족 대상)이면 체크 안함
if( GetVar( 8 ) != CREATURE_ALL && GetVar( 8 ) != static_cast< StructMonster * >(pTarget)->GetCreatureGroup() )
return;
// 보스 몬스터에겐 100% 실패
if( static_cast< StructMonster * >(pTarget)->IsBossMonster() ) return;
if( GetVar( 9 ) != 0 && GetVar( 9 ) + GetVar( 16 ) * GetRequestedSkillLevel() > (float)pTarget->GetHP()/pTarget->GetMaxHP()*100 ) return;
if( GetVar( 10 ) != 0 && GetVar( 10 ) + GetVar( 17 ) * GetRequestedSkillLevel() < (float)pTarget->GetHP()/pTarget->GetMaxHP()*100 ) return;
for( int i = 11; i <= 14; i++ )
{
if( GetVar( i ) == 0 ) continue;
StructState::StateCode nCode = StructState::StateCode( static_cast< int >( GetVar( i ) ) );
if( !pTarget->GetState( nCode ) ) return;
}
float fDeathProbability = ( GetVar( 6 ) + GetVar( 7 ) * GetRequestedSkillLevel() ) * 100;
// 죽을래 살래
if( XRandom( 1, 10000 ) < (int)fDeathProbability )
{
// 죽었네
int nDamage = pTarget->GetHP() * 2;
StructCreature::_DAMAGE_INFO Damage = pTarget->DealMagicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ), StructCreature::IGNORE_DEFENCE | StructCreature::IGNORE_AVOID | StructCreature::IGNORE_BLOCK );
// TARGET_DEAD 로 변경 적용할 것. 이 경우 아이템을 드랍 등등이 보이지 않는 문제 발생함.
//AddSkillResult( &m_vResultList, true, SkillResult::TARGET_DEAD, pTarget->GetHandle() );
AddSkillDamageResult( &m_vResultList, SkillResult::MAGIC_DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
}
else
{
// 살았으면 뎀쥐만~
int nMagicPoint = m_pOwner->GetMagicPoint((Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
int nDamage = nMagicPoint * ( GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(2) * GetEnhance() ) + GetVar(3) + GetVar(4) * GetRequestedSkillLevel() + GetVar(5) * GetEnhance();
StructCreature::_DAMAGE_INFO Damage = pTarget->DealMagicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
if( GetVar( 15 ) )
{
for( int i = 11; i <= 14; i++ )
{
if( GetVar( i ) == 0 ) continue;
StructState::StateCode nCode = StructState::StateCode( static_cast< int >( GetVar( i ) ) );
if( pTarget->GetState( nCode ) )
pTarget->RemoveState( nCode, GameRule::MAX_STATE_LEVEL );
}
}
AddSkillDamageResult( &m_vResultList, SkillResult::MAGIC_DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
}
return;
}
void StructSkill::MULTIPLE_MAGICAL_DAMAGE( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int nMagicPoint = m_pOwner->GetMagicPoint((Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
int nDamage = nMagicPoint * ( GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(2) * GetEnhance() ) + GetVar(3) + GetVar(4) * GetRequestedSkillLevel() + GetVar(5) * GetEnhance();
if( m_nCurrentFire == 0 )
{
// { 횟수 계산
m_nTotalFire = GetVar(6) + GetVar(7)*GetRequestedSkillLevel();
// }
m_nCurrentFire = 1;
}
else
{
++m_nCurrentFire;
}
int elemental_type = GetSkillBase()->GetElementalType();
// 데미지
StructCreature::_DAMAGE_INFO Damage = pTarget->DealMagicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::MAGIC_DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
m_nFireTime += GetVar(8) * 100;
return;
}
void StructSkill::MULTIPLE_MAGICAL_DAMAGE_T3( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int nDamage = m_pOwner->GetMagicPoint((Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() ) + GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(5) * GetEnhance();
// { 횟수 계산
int nCount = GetVar(2) + GetVar(3)*GetRequestedSkillLevel();
// }
int elemental_type = GetSkillBase()->GetElementalType();
// 데미지
for( int i = 0; i < nCount; ++i )
{
StructCreature::_DAMAGE_INFO Damage = pTarget->DealMagicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::MAGIC_DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
}
m_nFireCount = nCount;
m_nFireTime += GetVar(4) * nCount * 100;
return;
}
void StructSkill::SINGLE_PHYSICAL_DAMAGE_ABSORB( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int nDamage = 0;
int nAttackPoint = m_pOwner->GetAttackPointRight( (Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() ); // 기본적으로, 모든 스킬은 오늘손 장비의 영향을 받는다. 이도류 스킬은 제외.
// 데미지
int elemental_type = GetSkillBase()->GetElementalType();
nDamage = nAttackPoint + GetVar(0) + GetVar(1) * GetRequestedSkillLevel();
StructCreature::_DAMAGE_INFO Damage = pTarget->DealPhysicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
float fHPMod = GetVar(4) * GetEnhance();
float fMPMod = GetVar(5) * GetEnhance();
// 흡수
int nAddHP = Damage.nDamage * GetVar(2) + Damage.nDamage * fHPMod;
int nAddMP = Damage.nDamage * GetVar(3) + Damage.nDamage * fMPMod;
m_pOwner->AddHP( nAddHP );
m_pOwner->AddMP( nAddMP );
SkillResult skill_result;
skill_result.add_hp.type = SkillResult::ADD_HP;
skill_result.add_hp.hTarget = m_pOwner->GetHandle();
skill_result.add_hp.nIncHP = nAddHP;
skill_result.add_hp.target_hp = m_pOwner->GetHP();
m_vResultList.push_back( skill_result );
skill_result.add_hp.type = SkillResult::ADD_MP;
skill_result.add_hp.hTarget = m_pOwner->GetHandle();
skill_result.add_hp.nIncHP = nAddMP;
skill_result.add_hp.target_hp = m_pOwner->GetMP();
m_vResultList.push_back( skill_result );
return;
}
void StructSkill::SINGLE_MAGICAL_DAMAGE_WITH_ABSORB( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int elemental_type = GetSkillBase()->GetElementalType();
// 데미지 처리
int nMagicPoint = m_pOwner->GetMagicPoint((Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
int nDamage = nMagicPoint * ( GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(2) * GetEnhance() ) + GetVar(3) + GetVar(4) * GetRequestedSkillLevel() + GetVar(5) * GetEnhance();
StructCreature::_DAMAGE_INFO Damage = pTarget->DealMagicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::MAGIC_DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
// 흡수
int nAddHP = Damage.nDamage * ( GetVar(6) + GetVar(7) * GetRequestedSkillLevel() + GetVar(8) * GetEnhance() );
int nAddMP = Damage.nDamage * ( GetVar(9) + GetVar(10) * GetRequestedSkillLevel() + GetVar(11) * GetEnhance() );
m_pOwner->AddHP( nAddHP );
m_pOwner->AddMP( nAddMP );
SkillResult skill_result;
skill_result.add_hp.type = SkillResult::ADD_HP;
skill_result.add_hp.hTarget = m_pOwner->GetHandle();
skill_result.add_hp.nIncHP = nAddHP;
skill_result.add_hp.target_hp = m_pOwner->GetHP();
m_vResultList.push_back( skill_result );
skill_result.add_hp.type = SkillResult::ADD_MP;
skill_result.add_hp.hTarget = m_pOwner->GetHandle();
skill_result.add_hp.nIncHP = nAddMP;
skill_result.add_hp.target_hp = m_pOwner->GetMP();
m_vResultList.push_back( skill_result );
}
void StructSkill::ADD_HP_MP_BY_ABSORB_HP_MP( struct StructCreature* pTarget )
{
if( !pTarget ) return;
// 데미지 처리
int nHPDamage = ( GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(2) * GetEnhance() );
int nMPDamage = ( GetVar(3) + GetVar(4) * GetRequestedSkillLevel() + GetVar(5) * GetEnhance() );
pTarget->AddHP( 0 - nHPDamage );
pTarget->AddMP( 0 - nMPDamage );
SkillResult skill_result;
skill_result.add_hp.type = SkillResult::ADD_HP;
skill_result.add_hp.hTarget = pTarget->GetHandle();
skill_result.add_hp.nIncHP = 0 - nHPDamage;
skill_result.add_hp.target_hp = pTarget->GetHP();
m_vResultList.push_back( skill_result );
skill_result.add_hp.type = SkillResult::ADD_MP;
skill_result.add_hp.hTarget = pTarget->GetHandle();
skill_result.add_hp.nIncHP = 0 - nMPDamage;
skill_result.add_hp.target_hp = pTarget->GetMP();
m_vResultList.push_back( skill_result );
// 흡수
int nHPAbsorb = ( GetVar(9) + GetVar(10) * GetRequestedSkillLevel() + GetVar(11) * GetEnhance() );
int nMPAbsorb = ( GetVar(6) + GetVar(7) * GetRequestedSkillLevel() + GetVar(8) * GetEnhance() );
m_pOwner->AddHP( nHPAbsorb );
m_pOwner->AddMP( nMPAbsorb );
skill_result.add_hp.type = SkillResult::ADD_HP;
skill_result.add_hp.hTarget = m_pOwner->GetHandle();
skill_result.add_hp.nIncHP = nHPAbsorb;
skill_result.add_hp.target_hp = m_pOwner->GetHP();
m_vResultList.push_back( skill_result );
skill_result.add_hp.type = SkillResult::ADD_MP;
skill_result.add_hp.hTarget = m_pOwner->GetHandle();
skill_result.add_hp.nIncHP = nMPAbsorb;
skill_result.add_hp.target_hp = m_pOwner->GetMP();
m_vResultList.push_back( skill_result );
}
void StructSkill::SINGLE_MAGICAL_TARGET_HP_PERCENT_DAMAGE( struct StructCreature* pTarget )
{
if( !pTarget ) return;
// 보스급 몬스터 이상이면 실패
if( !pTarget->IsMonster() || static_cast< StructMonster * >(pTarget)->IsBossMonster() ) return;
int elemental_type = GetSkillBase()->GetElementalType();
int nDamage = ( GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(2) * GetEnhance() ) * pTarget->GetHP();
StructCreature::_DAMAGE_INFO Damage = pTarget->DealMagicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ), StructCreature::IGNORE_DEFENCE | StructCreature::IGNORE_CRITICAL );
AddSkillDamageResult( &m_vResultList, SkillResult::MAGIC_DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
}
void StructSkill::SINGLE_MAGICAL_MANABURN( StructCreature *pTarget )
{
if( !pTarget ) return;
int elemental_type = GetSkillBase()->GetElementalType();
int nMPBurn = 0;
switch( GetSkillBase()->GetEffectType() )
{
case SkillBase::EF_MAGIC_SINGLE_PERCENT_MANABURN:
nMPBurn = GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(2) * GetEnhance();
break;
case SkillBase::EF_MAGIC_SINGLE_PERCENT_OF_MAX_MP_MANABURN:
nMPBurn = pTarget->GetMaxMP() * ( GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(2) * GetEnhance() );
break;
default:
assert( 0 );
return;
}
int nDamage = GetVar(3) + GetVar(4) * GetRequestedSkillLevel() + GetVar(5) * GetEnhance();
StructCreature::_DAMAGE_INFO Damage = pTarget->DealMagicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ), StructCreature::IGNORE_DEFENCE );
AddSkillDamageResult( &m_vResultList, SkillResult::MAGIC_DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
pTarget->AddMP( -nMPBurn );
SkillResult skill_result;
skill_result.add_hp.type = SkillResult::ADD_MP;
skill_result.add_hp.hTarget = pTarget->GetHandle();
skill_result.add_hp.nIncHP = -nMPBurn;
skill_result.add_hp.target_hp = pTarget->GetMP();
m_vResultList.push_back( skill_result );
}
void StructSkill::SINGLE_DAMAGE_BY_CONSUMING_TARGETS_STATE( struct StructCreature* pTarget )
{
bool bFirable = false;
for( int i = 0 ; i < 4 ; ++i )
{
StructState::StateCode nCode = StructState::StateCode( static_cast< int >( GetVar( i ) ) );
if( pTarget->GetState( nCode ) )
{
if( GetVar( 4 ) )
pTarget->RemoveState( nCode, GameRule::MAX_STATE_LEVEL );
AddSkillResult( &m_vResultList, true, SkillResult::REMOVE_STATE, pTarget->GetHandle() );
bFirable = true;
break;
}
}
if( !bFirable )
{
AddSkillResult( &m_vResultList, false, SkillResult::REMOVE_STATE, pTarget->GetHandle() );
return;
}
Elemental::Type elemental_type = (Elemental::Type)GetSkillBase()->GetElementalType();
int nDamage = 0;
if( GetSkillBase()->GetSkillEffectType() == SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_BY_CONSUMING_TARGETS_STATE )
nDamage = m_pOwner->GetAttackPointRight( elemental_type, GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
else
nDamage = m_pOwner->GetMagicPoint( elemental_type, GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
nDamage = ( GetVar( 5 ) + GetVar( 6 ) * GetRequestedSkillLevel() + GetVar( 9 ) * GetEnhance() ) * nDamage + ( GetVar( 7 ) + GetVar( 8 ) * GetRequestedSkillLevel() + GetVar( 10 ) * GetEnhance() );
StructCreature::_DAMAGE_INFO Damage;
if( GetSkillBase()->IsPhysicalSkill() )
Damage = pTarget->DealPhysicalSkillDamage( m_pOwner, nDamage, elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
else
Damage = pTarget->DealMagicalSkillDamage( m_pOwner, nDamage, elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, ( GetSkillBase()->IsPhysicalSkill() ) ? SkillResult::DAMAGE : SkillResult::MAGIC_DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
}
void StructSkill::MAGIC_SINGLE_REGION_DAMAGE_BY_SUMMON_DEAD( struct StructCreature* pTarget )
{
AR_TIME t = GetArTime();
if( !m_pOwner->IsPlayer() ) return;
StructSummon * pSummon = NULL;
pSummon = static_cast< StructPlayer * >( m_pOwner )->GetMainSummon();
if( !pSummon || pSummon->IsDead() || pSummon == pTarget )
{
AddSkillResult( &m_vResultList, false, SkillResult::SUMMON_DEAD );
return;
}
ArPosition targetPos = pSummon->GetCurrentPosition( t );
int nSummonMaxHP = pSummon->GetMaxHP();
pSummon->damage( m_pOwner, pSummon->GetHP(), false );
ArcadiaServer::Instance().SetObjectPriority( pSummon, ArSchedulerObject::UPDATE_PRIORITY_NORMAL );
StructCreature::_DAMAGE_INFO Damage;
Damage.nDamage = pSummon->GetHP();
Damage.target_hp = pSummon->GetHP();
AddSkillDamageResult( &m_vResultList, SkillResult::DAMAGE, Elemental::TYPE_NONE, Damage, pSummon->GetHandle() );
int nDamage = nSummonMaxHP * ( GetVar( 0 ) + GetVar( 1 ) * GetRequestedSkillLevel() + GetVar( 2 ) * GetEnhance() );
int elemental_type = GetSkillBase()->GetElementalType();
float fEffectLength = ( GetVar(7) + GetVar(8) * GetRequestedSkillLevel() ) * GameRule::DEFAULT_UNIT_SIZE;
m_fRange = fEffectLength;
std::vector< StructCreature * > vTargetList;
std::vector< StructCreature * >::iterator itTargetList;
nDamage = EnumSkillTargetsAndCalcDamage( m_pOwner->GetCurrentPosition( t ), m_pOwner->GetLayer(), targetPos, true, fEffectLength, -1, 0, nDamage, true, m_pOwner, GetVar( 9 ), GetVar( 10 ) + GetVar( 11 ) * GetRequestedSkillLevel(), vTargetList );
m_nTargetCount = static_cast<int>( vTargetList.size() );
// { 모든 타겟에 데미지 적용하세~
for( itTargetList = vTargetList.begin(); itTargetList != vTargetList.end(); ++itTargetList )
{
StructCreature* pDealTarget = (*itTargetList);
StructCreature::_DAMAGE_INFO Damage = pDealTarget->DealMagicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pDealTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::MAGIC_DAMAGE, elemental_type, Damage, pDealTarget->GetHandle() );
}
// }
}
void StructSkill::MAGIC_SINGLE_REGION_DAMAGE( struct StructCreature* pTarget )
{
if( !pTarget ) return;
if( GetSkillBase()->GetEffectType() == SkillBase::EF_MAGIC_SINGLE_REGION_DAMAGE_USING_CORPSE )
{
// 몬스터에게만 사용 가능
if( !pTarget->IsMonster() || !pTarget->IsDead() ) return;
RemoveMonsterFromWorld( static_cast< StructMonster * >( pTarget ) );
}
int nMagicPoint = m_pOwner->GetMagicPoint((Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
int nDamage = nMagicPoint * ( GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(2) * GetEnhance() ) + GetVar(3) + GetVar(4) * GetRequestedSkillLevel() + GetVar(5) * GetEnhance();
int elemental_type = GetSkillBase()->GetElementalType();
float effectLength = GetSkillBase()->GetEffectType() == SkillBase::EF_MAGIC_SINGLE_REGION_DAMAGE_ADD_RANDOM_STATE ? GetVar(12) : GetVar(9);
float distributeType = GetSkillBase()->GetEffectType() == SkillBase::EF_MAGIC_SINGLE_REGION_DAMAGE_ADD_RANDOM_STATE ? GetVar(13) : GetVar(10);
float targetMax = GetSkillBase()->GetEffectType() == SkillBase::EF_MAGIC_SINGLE_REGION_DAMAGE_ADD_RANDOM_STATE ? GetVar(14) : GetVar(11);
float fEffectLength = effectLength * GameRule::DEFAULT_UNIT_SIZE;
m_fRange = fEffectLength;
std::vector< StructCreature * > vTargetList;
std::vector< StructCreature * >::iterator itTargetList;
AR_TIME t = GetArTime();
nDamage = EnumSkillTargetsAndCalcDamage( m_pOwner->GetCurrentPosition( t ), m_pOwner->GetLayer(), pTarget->GetCurrentPosition( t ), true, fEffectLength, -1, 0, nDamage, true, m_pOwner, distributeType, targetMax, vTargetList );
m_nTargetCount = static_cast<int>( vTargetList.size() );
// { 모든 타겟에 데미지 적용하세~
for( itTargetList = vTargetList.begin(); itTargetList != vTargetList.end(); ++itTargetList )
{
StructCreature* pDealTarget = (*itTargetList);
StructCreature::_DAMAGE_INFO Damage = pDealTarget->DealMagicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pDealTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::MAGIC_DAMAGE, elemental_type, Damage, pDealTarget->GetHandle() );
}
// }
}
void StructSkill::ACTIVATE_FIELD_PROP()
{
StructFieldProp::iterator it = StructFieldProp::get( m_hTarget );
if( !(*it) || !(*it)->IsFieldProp() )
{
return;
}
StructFieldProp * pProp = static_cast< StructFieldProp * >( *it );
bool bRet = pProp->UseProp( static_cast< StructPlayer * >( m_pOwner ) );
AddSkillResult( &m_vResultList, bRet );
}
void StructSkill::REGION_HEAL_BY_FIELD_PROP()
{
StructFieldProp::iterator it = StructFieldProp::get( m_hTarget );
if( !(*it) || !(*it)->IsFieldProp() )
{
return;
}
StructFieldProp * pProp = static_cast< StructFieldProp * >( *it );
// UseProp에서 프랍이 삭제될 수 있으므로 위치는 미리 저장
ArPosition posTarget = pProp->GetPos();
unsigned char nTargetLayer = pProp->GetLayer();
bool bRet = pProp->UseProp( static_cast< StructPlayer * >( m_pOwner ) );
AddSkillResult( &m_vResultList, bRet );
// 프랍 사용에 실패했으면 추가 처리도 일어나지 않음
if( !bRet )
{
return;
}
int nHPHeal = GetVar(0) + GetVar(1) * GetRequestedSkillLevel();
float fHPHeal = GetVar(2) + GetVar(3) * GetRequestedSkillLevel();
m_fRange = GetVar( 4 ) * GameRule::DEFAULT_UNIT_SIZE;
int nTargetType = GetVar( 5 );
std::vector< AR_HANDLE > vResult;
ArcadiaServer::Instance().EnumMovableObject( posTarget, nTargetLayer, m_fRange, &vResult );
for( std::vector< AR_HANDLE >::iterator it = vResult.begin(); it != vResult.end(); ++it )
{
StructCreature::iterator itCreature = StructCreature::get( (*it) );
StructCreature * pCreature = (*itCreature);
if( !pCreature || pCreature->IsPet() || pCreature->IsDead() )
continue;
switch( nTargetType )
{
case SkillBase::SKILL_EFFECT_TARGET_LIMIT_NOT_ENEMY:
if( m_pOwner->IsEnemy( pCreature, true ) )
continue;
break;
case SkillBase::SKILL_EFFECT_TARGET_LIMIT_ONLY_ALLY:
if( !m_pOwner->IsAlly( pCreature ) )
continue;
break;
case SkillBase::SKILL_EFFECT_TARGET_LIMIT_ONLY_ENEMY:
if( !m_pOwner->IsEnemy( pCreature, true ) )
continue;
break;
}
int nIncHP = pCreature->Heal( nHPHeal + fHPHeal * pCreature->GetMaxHP() );
SkillResult skill_result;
skill_result.add_hp.type = SkillResult::ADD_HP;
skill_result.add_hp.hTarget = pCreature->GetHandle();
skill_result.add_hp.target_hp = pCreature->GetHP();
skill_result.add_hp_mp_sp.nIncHP = nIncHP;
m_vResultList.push_back( skill_result );
}
}
void StructSkill::MAKE_AREA_EFFECT_PROP_BY_FIELD_PROP( bool bIsTrap )
{
StructFieldProp::iterator it = StructFieldProp::get( m_hTarget );
if( !(*it) || !(*it)->IsFieldProp() )
{
return;
}
StructFieldProp * pProp = static_cast< StructFieldProp * >( *it );
// UseProp에서 프랍이 삭제될 수 있으므로 위치는 미리 저장
ArPosition posTarget = pProp->GetPos();
unsigned char nTargetLayer = pProp->GetLayer();
bool bRet = pProp->UseProp( static_cast< StructPlayer * >( m_pOwner ) );
AddSkillResult( &m_vResultList, bRet );
// 프랍 사용에 실패했으면 추가 처리도 일어나지 않음
if( !bRet )
{
return;
}
// 트랩은 1인당 동시 1개만 설치 가능(새로 설치하려면 기존꺼 제거)
if( bIsTrap && m_pOwner->GetTrapHandle() )
{
StructSkillProp *pTrap = static_cast< StructSkillProp * >( GameObject::raw_get( m_pOwner->GetTrapHandle() ) );
if( pTrap )
{
pTrap->PendRemove();
}
}
int nMagicPoint = m_pOwner->GetMagicPoint( (Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
float fHateRatio = m_pOwner->GetHateRatio();
// 스킬 프랍 생성
ArObject *pPtr = StructSkillProp::Create( m_pOwner->GetHandle() , this, nMagicPoint, fHateRatio );
if( bIsTrap && pPtr )
{
m_pOwner->SetTrapHandle( pPtr->GetHandle() );
}
pPtr->SetCurrentXY( posTarget.x, posTarget.y );
pPtr->SetCurrentLayer( nTargetLayer );
// 추가후 스케쥴러에 등록
ArcadiaServer::Instance().AddObject( pPtr );
ArcadiaServer::Instance().SetObjectPriority( pPtr, ArSchedulerObject::UPDATE_PRIORITY_HIGH );
return;
}
void StructSkill::CREATE_ITEM( struct StructCreature* pTarget, bool bSuccess )
{
if( bSuccess )
{
StructPlayer *pInvenPlayer = NULL;
if( m_pOwner->IsPlayer() ) pInvenPlayer = static_cast< StructPlayer* >( m_pOwner );
if( m_pOwner->IsSummon() ) pInvenPlayer = static_cast< StructSummon* >( m_pOwner )->GetMaster();
// TODO : LOCK 해야되는거 아닌가? by Testors
for( int i = 0; i < 3; ++i )
{
int nItemCode = GetVar( 4*i ) + GetVar( 4*i+1 )*GetRequestedSkillLevel();
__int64 nItemCount = GetVar( 4*i+2 ) + GetVar( 4*i+3 )*GetRequestedSkillLevel();
while( nItemCode < 0 )
{
GameContent::SelectItemIDFromDropGroup( nItemCode, nItemCode, nItemCount );
}
if( nItemCode )
{
StructItem *pItem = StructItem::AllocItem( 0, nItemCode, nItemCount, ItemInstance::BY_SKILL );
StructItem *pNewItem = pInvenPlayer->PushItem( pItem, pItem->GetCount() );
if( pNewItem )
{
LOG::Log11N4S( LM_ITEM_TAKE, pInvenPlayer->GetAccountID(), pInvenPlayer->GetSID(), pItem->GetItemEnhance() * 100 + pItem->GetItemLevel(), pItem->GetItemCode(), pItem->GetCount(), pNewItem ? pNewItem->GetCount() : 0, 0, 0, pInvenPlayer->GetX(), pInvenPlayer->GetY(), pItem->GetItemUID(), pInvenPlayer->GetAccountName(), LOG::STR_NTS, pInvenPlayer->GetName(), LOG::STR_NTS, "", 0, "IGEN", LOG::STR_NTS );
}
if( pNewItem != pItem )
{
StructItem::PendFreeItem( pItem );
}
}
}
}
SkillResult skill_result;
skill_result.result.type = SkillResult::RESULT;
skill_result.result.bResult = bSuccess;
m_vResultList.push_back( skill_result );
}
void StructSkill::PHYSICAL_SINGLE_REGION_DAMAGE_OLD( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int nAttackPoint = m_pOwner->GetAttackPointRight( (Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
// { 데미지 계산
int elemental_type = GetSkillBase()->GetElementalType();
int nDamage = nAttackPoint + GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(4) * GetEnhance();
// }
float fEffectLength = GetVar(2) * GameRule::DEFAULT_UNIT_SIZE;
if( GetSkillBase()->GetSkillEffectType() == SkillBase::EF_PHYSICAL_SINGLE_REGION_DAMAGE_OLD )
fEffectLength += GetVar(7) * GetEnhance() * GameRule::DEFAULT_UNIT_SIZE;
m_fRange = fEffectLength;
std::vector< StructCreature * > vTargetList;
std::vector< StructCreature * >::iterator itTargetList;
AR_TIME t = GetArTime();
nDamage = EnumSkillTargetsAndCalcDamage( m_pOwner->GetCurrentPosition( t ), m_pOwner->GetLayer(), pTarget->GetCurrentPosition( t ), true, fEffectLength, -1, 0, nDamage, true, m_pOwner, GetVar(3), GetVar(11), vTargetList );
m_nTargetCount = static_cast<int>( vTargetList.size() );
float fRange = 0;
AR_TIME knock_back_time = 0;
// { 넉백 스킬의 경우
if( GetSkillBase()->GetSkillEffectType() == SkillBase::EF_PHYSICAL_SINGLE_REGION_DAMAGE_KNOCKBACK_OLD )
{
fRange = GetVar(5) + GetVar(6) * GetRequestedSkillLevel() + GetVar(7) * GetEnhance();
fRange *= GameRule::DEFAULT_UNIT_SIZE;
knock_back_time = ( GetVar(8) + GetVar(9) * GetRequestedSkillLevel() + GetVar(10) * GetEnhance() ) * 100;
}
// } 넉백 스킬의 경우
// { 모든 타겟에 데미지 적용하세~
for( itTargetList = vTargetList.begin(); itTargetList != vTargetList.end(); ++itTargetList )
{
StructCreature* pDealTarget = (*itTargetList);
StructCreature::_DAMAGE_INFO Damage = pDealTarget->DealPhysicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pDealTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
if( !Damage.bBlock && !Damage.bMiss && !Damage.bPerfectBlock && GetSkillBase()->GetSkillEffectType() == SkillBase::EF_PHYSICAL_SINGLE_REGION_DAMAGE_KNOCKBACK_OLD && !( pDealTarget->IsMonster() && static_cast< StructMonster * >( pDealTarget )->IsBossMonster() ) )
{
// 넉백 스킬의 경우
AFFECT_KNOCK_BACK( pDealTarget, fRange, knock_back_time );
AddSkillDamageWithKnockBackResult( &m_vResultList, SkillResult::DAMAGE_WITH_KNOCK_BACK, elemental_type, Damage, pDealTarget->GetHandle(), pDealTarget->GetPos().x, pDealTarget->GetPos().y, knock_back_time );
}
else
{
AddSkillDamageResult( &m_vResultList, SkillResult::DAMAGE, elemental_type, Damage, pDealTarget->GetHandle() );
}
}
// }
}
void StructSkill::PHYSICAL_MULTIPLE_REGION_DAMAGE_OLD( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int nAttackPoint = m_pOwner->GetAttackPointRight( (Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
// { 데미지 계산
int elemental_type = GetSkillBase()->GetElementalType();
int nDamage = nAttackPoint + GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(8) * GetEnhance();
// }
// { 연타 횟수
int nCount = GetVar( 2 ) + GetVar(3) * GetRequestedSkillLevel();
// }
float fEffectLength = ( GetVar(5) + GetVar(9) * GetEnhance() ) * GameRule::DEFAULT_UNIT_SIZE;
m_fRange = fEffectLength;
std::vector< StructCreature * > vTargetList;
std::vector< StructCreature * >::iterator itTargetList;
AR_TIME t = GetArTime();
nDamage = EnumSkillTargetsAndCalcDamage( m_pOwner->GetCurrentPosition( t ), m_pOwner->GetLayer(), pTarget->GetCurrentPosition( t ), true, fEffectLength, -1, 0, nDamage, true, m_pOwner, GetVar( 6 ), GetVar( 7 ), vTargetList );
m_nTargetCount = static_cast<int>( vTargetList.size() );
// { 모든 타겟에 데미지 적용하세~
for( int i = 0; i < nCount; ++i )
{
for( itTargetList = vTargetList.begin(); itTargetList != vTargetList.end(); ++itTargetList )
{
StructCreature* pDealTarget = (*itTargetList);
StructCreature::_DAMAGE_INFO Damage = pDealTarget->DealPhysicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pDealTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::DAMAGE, elemental_type, Damage, pDealTarget->GetHandle() );
}
}
// }
m_nFireTime += GetVar(4) * nCount * 100;
}
// 타겟 중심 특수범위 다중 데미지(실시간 연타가 아닌 1개의 SkillResult로 여러대 치는 놈)
void StructSkill::PHYSICAL_MULTIPLE_SPECIAL_REGION_DAMAGE( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int nAttackPoint = m_pOwner->GetAttackPointRight( (Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
// { 데미지 계산
int elemental_type = GetSkillBase()->GetElementalType();
int nDamage = nAttackPoint + GetVar(0) + GetVar(1) * GetRequestedSkillLevel();
// }
// { 연타 횟수
int nCount = GetVar( 10 ) + GetVar( 11 ) * GetRequestedSkillLevel();
// }
float fEffectLength = GetVar(8) * GameRule::DEFAULT_UNIT_SIZE;
m_fRange = fEffectLength;
std::vector< StructCreature * > vTargetList;
std::vector< StructCreature * >::iterator itTargetList;
AR_TIME t = GetArTime();
nDamage = EnumSkillTargetsAndCalcDamage( m_pOwner->GetCurrentPosition( t ), m_pOwner->GetLayer(), pTarget->GetCurrentPosition( t ), GetVar( 6 ), fEffectLength, GetVar( 5 ), GetVar( 9 ), nDamage, GetVar( 7 ), m_pOwner, GetVar( 2 ), GetVar( 3 ), vTargetList );
m_nTargetCount = static_cast<int>( vTargetList.size() );
// { 모든 타겟에 데미지 적용하세~
for( int i = 0; i < nCount; ++i )
{
for( itTargetList = vTargetList.begin(); itTargetList != vTargetList.end(); ++itTargetList )
{
StructCreature* pDealTarget = (*itTargetList);
StructCreature::_DAMAGE_INFO Damage = pDealTarget->DealPhysicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pDealTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::DAMAGE, elemental_type, Damage, pDealTarget->GetHandle() );
}
}
// }
m_nFireCount = nCount;
m_nFireTime += GetVar(4) * nCount * 100;
}
void StructSkill::PHYSICAL_SPECIAL_REGION_DAMAGE( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int nAttackPoint = m_pOwner->GetAttackPointRight( (Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
// { 데미지 계산
int elemental_type = GetSkillBase()->GetElementalType();
int nDamage = nAttackPoint + GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(4) * GetEnhance();
// }
float fEffectLength = GetVar(8) * GameRule::DEFAULT_UNIT_SIZE;
m_fRange = fEffectLength;
std::vector< StructCreature * > vTargetList;
std::vector< StructCreature * >::iterator itTargetList;
AR_TIME t = GetArTime();
nDamage = EnumSkillTargetsAndCalcDamage( m_pOwner->GetCurrentPosition( t ), m_pOwner->GetLayer(), pTarget->GetCurrentPosition( t ), !!GetVar(6), fEffectLength, GetVar(5), GetVar(9), nDamage, GetVar(7), m_pOwner, GetVar( 2 ), GetVar( 3 ), vTargetList );
m_nTargetCount = static_cast<int>( vTargetList.size() );
// { 모든 타겟에 데미지 적용하세~
for( itTargetList = vTargetList.begin(); itTargetList != vTargetList.end(); ++itTargetList )
{
StructCreature* pDealTarget = (*itTargetList);
StructCreature::_DAMAGE_INFO Damage = pDealTarget->DealPhysicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pDealTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::DAMAGE, elemental_type, Damage, pDealTarget->GetHandle() );
}
// }
}
void StructSkill::ADD_REGION_STATE( struct StructCreature* pTarget )
{
if( !pTarget ) return;
AR_TIME t = GetArTime();
float fEffectLength = GetVar( 5 ) * GameRule::DEFAULT_UNIT_SIZE;
int nTargetType = GetVar( 6 );
int nRandomType = GetVar( 7 );
StructState::StateCode nForcedCode = StructState::StateCode( 0 );
if( nRandomType == StructState::COMMON_RANDOM_STATE )
{
nForcedCode = static_cast< StructState::StateCode >( GetStateId() );
int count;
for( count = 6; count >= 2; --count ) if( GetVar( count - 2 ) ) break;
int index = XRandom( -1, count - 2 );
if( index != -1 ) nForcedCode = static_cast< StructState::StateCode >( static_cast< int >( GetVar(index) ) );
}
std::vector< AR_HANDLE > vResult;
m_fRange = fEffectLength;
STATE_SKILL_FUNCTOR mySkillFunctor( &m_vResultList, nForcedCode );
ArcadiaServer::Instance().EnumMovableObject( pTarget->GetCurrentPosition( t ), pTarget->GetLayer(), fEffectLength, &vResult );
m_nTargetCount = 0;
for( std::vector< AR_HANDLE >::iterator it = vResult.begin(); it != vResult.end(); ++it )
{
StructCreature::iterator itCreature = StructCreature::get( (*it) );
StructCreature * pCreature = (*itCreature);
if( !pCreature || pCreature->IsPet() )
continue;
if( pCreature->IsPlayer() && !IsUseableOnAvatar() )
{
continue;
}
if( pCreature->IsMonster() && !IsUseableOnMonster() )
{
continue;
}
if( pCreature->IsSummon() && !IsUseableOnSummon() )
{
continue;
}
if( nTargetType == 1 )
{
if( m_pOwner->IsEnemy( pCreature, true ) )
continue;
}
else if( nTargetType == 2 )
{
if( !m_pOwner->IsAlly( pCreature ) )
continue;
}
else if( nTargetType == 3 )
{
if( !m_pOwner->IsEnemy( pCreature, true ) )
continue;
}
mySkillFunctor.onCreature( this, t, m_pOwner, pCreature );
++m_nTargetCount;
}
}
void StructSkill::ADD_STATE_BY_SELF_COST( struct StructCreature* pTarget )
{
if( !pTarget || !pTarget->IsSummon() )
return;
float fCostHP = GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(2) * GetEnhance();
float fCostSP = GetVar(3) + GetVar(4) * GetRequestedSkillLevel() + GetVar(5) * GetEnhance();
float fCostEnergy = GetVar(6) + GetVar(7) * GetRequestedSkillLevel() + GetVar(8) * GetEnhance();
float fCostMP = GetVar(9) + GetVar(10) * GetRequestedSkillLevel() + GetVar(11) * GetEnhance();
if( fCostEnergy > GameRule::ENERGY_MAX )
fCostEnergy = GameRule::ENERGY_MAX;
if( pTarget->GetHP() < fCostHP )
return;
if( static_cast< StructSummon * >(pTarget)->GetSP() < fCostSP )
return;
if( fCostEnergy > 0 && pTarget->GetEnergyCount() < fCostEnergy )
return;
if( pTarget->GetMP() < fCostMP )
return;
pTarget->AddHP( -fCostHP );
static_cast< StructSummon * >(pTarget)->AddSP( -fCostSP );
if( fCostEnergy > 0 )
pTarget->RemoveEnergy( fCostEnergy );
else if( fCostEnergy == -1 )
// fCostEnergy가 -1이면 모든 기공 소모
pTarget->RemoveEnergy( pTarget->GetEnergyCount() );
pTarget->AddMP( -fCostMP );
AR_TIME t = GetArTime();
STATE_SKILL_FUNCTOR mySkillFunctor( &m_vResultList );
mySkillFunctor.onCreature( this, t, m_pOwner, pTarget );
}
void StructSkill::ADD_REGION_STATE_BY_SELF_COST( struct StructCreature* pTarget )
{
if( !pTarget || !pTarget->IsSummon() )
return;
float fCostHP = GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(2) * GetEnhance();
float fCostSP = GetVar(3) + GetVar(4) * GetRequestedSkillLevel() + GetVar(5) * GetEnhance();
float fCostEnergy = GetVar(6) + GetVar(7) * GetRequestedSkillLevel() + GetVar(8) * GetEnhance();
if( fCostEnergy > GameRule::ENERGY_MAX )
fCostEnergy = GameRule::ENERGY_MAX;
float fEffectLength = GetVar(9) * GameRule::DEFAULT_UNIT_SIZE;
int nTargetType = GetVar(10);
AR_TIME t = GetArTime();
std::vector< AR_HANDLE > vResult;
m_fRange = fEffectLength;
STATE_SKILL_FUNCTOR mySkillFunctor( &m_vResultList );
ArcadiaServer::Instance().EnumMovableObject( pTarget->GetCurrentPosition( t ), pTarget->GetLayer(), fEffectLength, &vResult );
m_nTargetCount = 0;
for( std::vector< AR_HANDLE >::iterator it = vResult.begin(); it != vResult.end(); ++it )
{
StructCreature::iterator itCreature = StructCreature::get( (*it) );
StructCreature * pCreature = (*itCreature);
if( !m_pOwner->IsPlayer() || !pCreature || !pCreature->IsSummon() || !static_cast< StructSummon * >(pCreature)->GetMaster()->GetPartyID() ||
static_cast< StructSummon * >(pCreature)->GetMaster()->GetPartyID() != static_cast< StructPlayer * >(m_pOwner)->GetPartyID() )
continue;
if( nTargetType == 1 )
{
if( m_pOwner->IsEnemy( pCreature, true ) )
continue;
}
else if( nTargetType == 2 )
{
if( !m_pOwner->IsAlly( pCreature ) )
continue;
}
else if( nTargetType == 3 )
{
if( !m_pOwner->IsEnemy( pCreature, true ) )
continue;
}
if( pCreature->GetHP() < fCostHP )
continue;
if( static_cast< StructSummon * >(pCreature)->GetSP() < fCostSP )
continue;
if( pCreature->GetEnergyCount() < fCostEnergy )
continue;
pCreature->AddHP( -fCostHP );
static_cast< StructSummon * >(pCreature)->AddSP( -fCostSP );
pCreature->RemoveEnergy( -fCostEnergy );
mySkillFunctor.onCreature( this, t, m_pOwner, pCreature );
++m_nTargetCount;
}
}
void StructSkill::ADD_STATE_STEP_BY_STEP( struct StructCreature* pTarget )
{
if( !pTarget ) return;
for( int i = 9; i >= 0; i-=3 )
{
if( this->GetVar( i ) == 0 ) continue;
if( pTarget->GetState( static_cast< StructState::StateCode >( static_cast< int >( this->GetVar( i ) ) ) ) )
{
pTarget->RemoveState( static_cast< StructState::StateCode >( static_cast< int >( this->GetVar( i ) ) ), GameRule::MAX_STATE_LEVEL );
int nMagicPoint = m_pOwner->GetMagicPoint((Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
int nDamage = nMagicPoint * ( GetVar(12) + GetVar(13) * GetRequestedSkillLevel() + GetVar(14) * GetEnhance() );
int elemental_type = GetSkillBase()->GetElementalType();
StructCreature::_DAMAGE_INFO Damage = pTarget->DealMagicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::MAGIC_DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
}
else
{
AR_TIME t = GetArTime();
STATE_SKILL_FUNCTOR mySkillFunctor( &m_vResultList );
mySkillFunctor.onCreature( this, t, m_pOwner, pTarget );
}
return;
}
}
// 타겟이 파티원이면 파티원 주변의 몹을 파티원에게 끌어다 준다.
// 타겟이 몹이면 몹 주변 몹을 전부 시전자한테 끌어온다.
void StructSkill::REGION_TAUNT( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int nHate = GetVar( 0 ) + GetVar( 1 ) * GetRequestedSkillLevel() + GetVar( 7 ) * GetEnhance();
float fEffectLength = ( GetVar( 2 ) + GetVar( 8 ) * GetEnhance() ) * GameRule::DEFAULT_UNIT_SIZE;
int nRatio = GetVar( 5 ) + GetVar( 6 ) * GetRequestedSkillLevel() + GetVar( 9 ) * GetEnhance();
m_fRange = fEffectLength;
// 어그로 받을 대상은 기본적으로 시전자
StructCreature *pHateReceiver = m_pOwner;
int nPartyID = 0;
if( m_pOwner->IsPlayer() )
nPartyID = static_cast< StructPlayer * >(m_pOwner)->GetPartyID();
else if( m_pOwner->IsSummon() )
nPartyID = static_cast< StructSummon * >(m_pOwner)->GetMaster()->GetPartyID();
// 타겟이 유저이면서 같은 파티원이면 어그로 받는 대상이 타겟이 됨
if( pTarget->IsPlayer() && static_cast< StructPlayer * >(pTarget)->GetPartyID() &&
static_cast< StructPlayer * >(pTarget)->GetPartyID() == nPartyID )
{
pHateReceiver = pTarget;
}
std::vector< StructCreature * > vTargetList;
std::vector< StructCreature * >::iterator itTargetList;
AR_TIME t = GetArTime();
nHate = EnumSkillTargetsAndCalcDamage( pHateReceiver->GetCurrentPosition( t ), pHateReceiver->GetLayer(), pTarget->GetCurrentPosition( t ), true, fEffectLength, -1, 0, nHate, true, m_pOwner, GetVar( 3 ), GetVar( 4 ), vTargetList );
m_nTargetCount = static_cast<int>( vTargetList.size() );
// { 모든 타겟에 헤이트 적용하세~
for( itTargetList = vTargetList.begin(); itTargetList != vTargetList.end(); ++itTargetList )
{
StructCreature* pDealTarget = (*itTargetList);
bool bSuccess = XRandom() % 100 < nRatio;
if( bSuccess && pDealTarget->IsEnemy( pHateReceiver, true ) && pDealTarget->IsMonster() )
{
std::pair< float, int > HateMod = m_pOwner->GetHateMod( ( GetSkillBase()->IsPhysicalSkill() ) ? 1 : 2, GetSkillBase()->IsHarmful() );
nHate += HateMod.second;
nHate *= HateMod.first;
static_cast< StructMonster * >( pDealTarget )->AddHate( pHateReceiver->GetHandle(), pHateReceiver->GetHateRatio() * nHate );
}
AddSkillResult( &m_vResultList, bSuccess, SkillResult::ADD_HATE, pDealTarget->GetHandle() );
}
}
void StructSkill::TAUNT( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int nHate = GetVar( 0 ) + GetVar( 1 ) * GetRequestedSkillLevel() + GetVar( 7 ) * GetEnhance();
int nRatio = GetVar( 5 ) + GetVar( 6 ) * GetRequestedSkillLevel() + GetVar( 9 ) * GetEnhance();
bool bSuccess = XRandom() % 100 < nRatio;
if( bSuccess && pTarget->IsEnemy( m_pOwner ) && pTarget->IsMonster() )
{
std::pair< float, int > HateMod = m_pOwner->GetHateMod( ( GetSkillBase()->IsPhysicalSkill() ) ? 1 : 2, GetSkillBase()->IsHarmful() );
nHate += HateMod.second;
nHate *= HateMod.first;
static_cast< StructMonster * >( pTarget )->AddHate( m_pOwner->GetHandle(), m_pOwner->GetHateRatio() * nHate );
}
AddSkillResult( &m_vResultList, bSuccess, SkillResult::ADD_HATE, pTarget->GetHandle() );
}
void StructSkill::REMOVE_HATE( struct StructCreature* pTarget )
{
if( !pTarget ) return;
if( !pTarget->IsMonster() )
return;
float fHateRemoveRate = GetVar( 0 ) + GetVar( 1 ) * GetRequestedSkillLevel() + GetVar( 7 ) * GetEnhance();
int nSuccessRate = GetVar( 5 ) + GetVar( 6 ) * GetRequestedSkillLevel() + GetVar( 9 ) * GetEnhance();
bool bSuccess = XRandom( 0, 99 ) < nSuccessRate;
if( bSuccess && m_pOwner->IsEnemy( pTarget ) )
{
StructMonster *pMonster = static_cast< StructMonster * >( pTarget );
int nHate = pMonster->GetHate( m_pOwner->GetHandle() );
pMonster->RemoveHate( m_pOwner->GetHandle(), nHate * fHateRemoveRate / 100.0f );
}
AddSkillResult( &m_vResultList, bSuccess, SkillResult::ADD_HATE, pTarget->GetHandle() );
}
void StructSkill::REGION_REMOVE_HATE( struct StructCreature* pTarget )
{
if( !pTarget ) return;
StructCreature *pCaster = NULL;
switch( GetSkillBase()->GetEffectType() )
{
case SkillBase::EF_REGION_REMOVE_HATE:
pCaster = m_pOwner;
break;
case SkillBase::EF_REGION_REMOVE_HATE_OF_TARGET:
pCaster = pTarget;
break;
}
if( GetVar( 10 ) != 0 && GetVar( 13 ) + GetVar( 14 ) * GetRequestedSkillLevel() > XRandom( 0, 99 ) )
{
AR_TIME t = GetArTime();
pTarget->AddState( static_cast< StructState::StateCode >( static_cast< int >( GetVar( 10 ) ) ), m_pOwner->GetHandle(), GetVar( 11 ) + GetVar( 12 ) * GetRequestedSkillLevel(), t, t + 100 * ( GetVar( 15 ) + ( GetVar( 16 ) * GetRequestedSkillLevel() ) ) );
}
float fHateRemoveRate = GetVar( 0 ) + GetVar( 1 ) * GetRequestedSkillLevel() + GetVar( 7 ) * GetEnhance();
float fEffectLength = ( GetVar( 2 ) + GetVar( 8 ) * GetEnhance() ) * GameRule::DEFAULT_UNIT_SIZE;
int nSuccessRate = GetVar( 5 ) + GetVar( 6 ) * GetRequestedSkillLevel() + GetVar( 9 ) * GetEnhance();
std::vector< StructCreature * > vTargetList;
std::vector< StructCreature * >::iterator itTargetList;
AR_TIME t = GetArTime();
EnumSkillTargetsAndCalcDamage( pCaster->GetCurrentPosition( t ), pCaster->GetLayer(), pTarget->GetCurrentPosition( t ), true, fEffectLength, -1, 0, 0, true, pCaster, GetVar( 3 ), GetVar( 4 ), vTargetList );
m_nTargetCount = static_cast<int>( vTargetList.size() );
// { 모든 타겟에 헤이트 적용하세~
for( itTargetList = vTargetList.begin(); itTargetList != vTargetList.end(); ++itTargetList )
{
if( !(*itTargetList)->IsMonster() )
continue;
StructMonster *pMonster = static_cast< StructMonster * >( *itTargetList );
bool bSuccess = XRandom( 0, 99 ) < nSuccessRate;
if( bSuccess && pCaster->IsEnemy( pMonster ) )
{
int nHate = pMonster->GetHate( pCaster->GetHandle() );
pMonster->RemoveHate( pCaster->GetHandle(), nHate * fHateRemoveRate / 100.0f );
}
AddSkillResult( &m_vResultList, bSuccess, SkillResult::ADD_HATE, pMonster->GetHandle() );
}
}
void StructSkill::MAGIC_SINGLE_REGION_DAMAGE_OLD( struct StructCreature* pTarget )
{
if( !pTarget ) return;
// { 데미지 계산
int elemental_type = GetSkillBase()->GetElementalType();
int nDamage = m_pOwner->GetMagicPoint((Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() ) + GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(5) * GetEnhance();
// }
float fEffectLength = GetVar(2) * GameRule::DEFAULT_UNIT_SIZE;
m_fRange = fEffectLength;
std::vector< StructCreature * > vTargetList;
std::vector< StructCreature * >::iterator itTargetList;
AR_TIME t = GetArTime();
nDamage = EnumSkillTargetsAndCalcDamage( m_pOwner->GetCurrentPosition( t ), m_pOwner->GetLayer(), pTarget->GetCurrentPosition( t ), true, fEffectLength, -1, 0, nDamage, true, m_pOwner, GetVar( 3 ), GetVar( 4 ), vTargetList );
m_nTargetCount = static_cast<int>( vTargetList.size() );
// { 모든 타겟에 데미지 적용하세~
for( itTargetList = vTargetList.begin(); itTargetList != vTargetList.end(); ++itTargetList )
{
StructCreature* pDealTarget = (*itTargetList);
StructCreature::_DAMAGE_INFO Damage = pDealTarget->DealMagicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pDealTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::MAGIC_DAMAGE, elemental_type, Damage, pDealTarget->GetHandle() );
}
// }
}
void StructSkill::MAGIC_MULTIPLE_REGION_DAMAGE_AT_ONCE( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int nMagicPoint = m_pOwner->GetMagicPoint((Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
int nDamage = ( GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(2) * GetEnhance() ) * nMagicPoint + GetVar(3) + GetVar(4) * GetRequestedSkillLevel() + GetVar(5) * GetEnhance();
int nCount = GetVar(6) + GetVar(7)*GetRequestedSkillLevel();
int elemental_type = GetSkillBase()->GetElementalType();
float effectLength = GetVar(9);
float distributeType = GetVar(10);
float targetMax = GetVar(11);
float fEffectLength = effectLength * GameRule::DEFAULT_UNIT_SIZE;
m_fRange = fEffectLength;
std::vector< StructCreature * > vTargetList;
std::vector< StructCreature * >::iterator itTargetList;
AR_TIME t = GetArTime();
nDamage = EnumSkillTargetsAndCalcDamage( m_pOwner->GetCurrentPosition( t ), m_pOwner->GetLayer(), pTarget->GetCurrentPosition( t ), true, fEffectLength, -1, 0, nDamage, true, m_pOwner, distributeType, targetMax, vTargetList );
m_nTargetCount = static_cast<int>( vTargetList.size() );
// { 모든 타겟에 데미지 적용하세~
for( int i = 0; i < nCount; ++i )
{
for( itTargetList = vTargetList.begin(); itTargetList != vTargetList.end(); ++itTargetList )
{
StructCreature* pDealTarget = (*itTargetList);
StructCreature::_DAMAGE_INFO Damage = pDealTarget->DealMagicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pDealTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::MAGIC_DAMAGE, elemental_type, Damage, pDealTarget->GetHandle() );
}
}
m_nFireCount = nCount;
m_nFireTime += GetVar(8) * nCount * 100;
}
void StructSkill::MAGIC_MULTIPLE_REGION_DAMAGE_OLD( struct StructCreature* pTarget )
{
if( !pTarget ) return;
// { 데미지 계산
int elemental_type = GetSkillBase()->GetElementalType();
int nDamage = m_pOwner->GetMagicPoint((Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() ) + GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(8) * GetEnhance();
// }
if( m_nCurrentFire == 0 )
{
// { 횟수 계산
m_nTotalFire = GetVar(2) + GetVar(3) * GetRequestedSkillLevel();
// }
m_nCurrentFire = 1;
}
else
{
++m_nCurrentFire;
}
float fEffectLength = ( GetVar(5) + GetVar(9) * GetEnhance() ) * GameRule::DEFAULT_UNIT_SIZE;
m_fRange = fEffectLength;
std::vector< StructCreature * > vTargetList;
std::vector< StructCreature * >::iterator itTargetList;
AR_TIME t = GetArTime();
nDamage = EnumSkillTargetsAndCalcDamage( m_pOwner->GetCurrentPosition( t ), m_pOwner->GetLayer(), pTarget->GetCurrentPosition( t ), true, fEffectLength, -1, 0, nDamage, true, m_pOwner, GetVar(6), GetVar(7), vTargetList );
m_nTargetCount = static_cast<int>( vTargetList.size() );
// { 모든 타겟에 데미지 적용하세~
for( itTargetList = vTargetList.begin(); itTargetList != vTargetList.end(); ++itTargetList )
{
StructCreature* pDealTarget = (*itTargetList);
StructCreature::_DAMAGE_INFO Damage = pDealTarget->DealMagicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pDealTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::MAGIC_DAMAGE, elemental_type, Damage, pDealTarget->GetHandle() );
}
// }
m_nFireTime += GetVar(4) * 100;
}
void StructSkill::MAGIC_MULTIPLE_REGION_DAMAGE( struct StructCreature* pTarget )
{
if( !pTarget ) return;
// { 데미지 계산
int elemental_type = GetSkillBase()->GetElementalType();
int nDamage = ( GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(2) * GetEnhance() ) * m_pOwner->GetMagicPoint((Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() ) + GetVar(3) + GetVar(4) * GetRequestedSkillLevel() + GetVar(5) * GetEnhance();
// }
if( m_nCurrentFire == 0 )
{
// { 횟수 계산
m_nTotalFire = GetVar(6) + GetVar(7) * GetRequestedSkillLevel();
// }
m_nCurrentFire = 1;
}
else
{
++m_nCurrentFire;
}
float fEffectLength = GetVar(9) * GameRule::DEFAULT_UNIT_SIZE;
m_fRange = fEffectLength;
std::vector< StructCreature * > vTargetList;
std::vector< StructCreature * >::iterator itTargetList;
AR_TIME t = GetArTime();
nDamage = EnumSkillTargetsAndCalcDamage( m_pOwner->GetCurrentPosition( t ), m_pOwner->GetLayer(), pTarget->GetCurrentPosition( t ), true, fEffectLength, -1, 0, nDamage, true, m_pOwner, GetVar(10), GetVar(11), vTargetList );
m_nTargetCount = static_cast<int>( vTargetList.size() );
// { 모든 타겟에 데미지 적용하세~
for( itTargetList = vTargetList.begin(); itTargetList != vTargetList.end(); ++itTargetList )
{
StructCreature* pDealTarget = (*itTargetList);
StructCreature::_DAMAGE_INFO Damage = pDealTarget->DealMagicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pDealTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::MAGIC_DAMAGE, elemental_type, Damage, pDealTarget->GetHandle() );
}
// }
m_nFireTime += GetVar(8) * 100;
}
void StructSkill::MAGIC_SINGLE_REGION_PERCENT_DAMAGE( struct StructCreature* pTarget )
{
if( !pTarget ) return;
// { 데미지 계산
int elemental_type = GetSkillBase()->GetElementalType();
int nDamage = 0; // 대상별 상대치로 계산되어야 하므로 데미지 적용 루프에서 계산한다
// }
float fEffectLength = GetVar(9) * GameRule::DEFAULT_UNIT_SIZE;
m_fRange = fEffectLength;
std::vector< StructCreature * > vTargetList;
std::vector< StructCreature * >::iterator itTargetList;
AR_TIME t = GetArTime();
EnumSkillTargetsAndCalcDamage( m_pOwner->GetCurrentPosition( t ), m_pOwner->GetLayer(), pTarget->GetCurrentPosition( t ), true, fEffectLength, -1, 0, nDamage, true, m_pOwner, GetVar( 10 ), GetVar( 11 ), vTargetList );
m_nTargetCount = static_cast<int>( vTargetList.size() );
// { 모든 타겟에 데미지 적용하세~
for( itTargetList = vTargetList.begin(); itTargetList != vTargetList.end(); ++itTargetList )
{
StructCreature* pDealTarget = (*itTargetList);
// 보스급 몬스터 이상이면 실패
if( !pDealTarget->IsMonster() || static_cast< StructMonster * >(pDealTarget)->IsBossMonster() )
{
--m_nTargetCount;
continue;
}
int nTargetDamage = ( GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(2) * GetEnhance() ) * pDealTarget->GetHP();
StructCreature::_DAMAGE_INFO Damage = pDealTarget->DealMagicalSkillDamage( m_pOwner, nTargetDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pDealTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ), StructCreature::IGNORE_DEFENCE );
AddSkillDamageResult( &m_vResultList, SkillResult::MAGIC_DAMAGE, elemental_type, Damage, pDealTarget->GetHandle() );
}
// }
}
void StructSkill::MAGIC_MULTIPLE_REGION_DAMAGE_T2( struct StructCreature* pTarget )
{
if( !pTarget ) return;
// { 데미지 계산
int elemental_type = GetSkillBase()->GetElementalType();
int nDamage = m_pOwner->GetMagicPoint((Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() ) + GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(8) * GetEnhance();
// }
// { 횟수 계산
int nCount = GetVar(2) + GetVar(3) * GetRequestedSkillLevel();
// }
float fEffectLength = ( GetVar(5) + GetVar(9) * GetEnhance() ) * GameRule::DEFAULT_UNIT_SIZE;
m_fRange = fEffectLength;
std::vector< StructCreature * > vTargetList;
std::vector< StructCreature * >::iterator itTargetList;
AR_TIME t = GetArTime();
nDamage = EnumSkillTargetsAndCalcDamage( m_pOwner->GetCurrentPosition( t ), m_pOwner->GetLayer(), pTarget->GetCurrentPosition( t ), true, fEffectLength, -1, 0, nDamage, true, m_pOwner, GetVar(6), GetVar(7), vTargetList );
m_nTargetCount = static_cast<int>( vTargetList.size() );
// { 모든 타겟에 데미지 적용하세~
for( int i = 0; i < nCount; ++i )
{
for( itTargetList = vTargetList.begin(); itTargetList != vTargetList.end(); ++itTargetList )
{
StructCreature* pDealTarget = (*itTargetList);
StructCreature::_DAMAGE_INFO Damage = pDealTarget->DealMagicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pDealTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::MAGIC_DAMAGE, elemental_type, Damage, pDealTarget->GetHandle() );
}
}
// }
m_nFireCount = nCount;
m_nFireTime += GetVar(4) * nCount * 100;
}
void StructSkill::MAGIC_SPECIAL_REGION_DAMAGE_OLD( struct StructCreature* pTarget )
{
if( !pTarget ) return;
// { 데미지 계산
int elemental_type = GetSkillBase()->GetElementalType();
int nDamage = m_pOwner->GetMagicPoint((Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() ) + GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(4) * GetEnhance();
// }
float fEffectLength = GetVar(8) * GameRule::DEFAULT_UNIT_SIZE;
m_fRange = fEffectLength;
std::vector< StructCreature * > vTargetList;
std::vector< StructCreature * >::iterator itTargetList;
AR_TIME t = GetArTime();
nDamage = EnumSkillTargetsAndCalcDamage( m_pOwner->GetCurrentPosition( t ), m_pOwner->GetLayer(), pTarget->GetCurrentPosition( t ), !!GetVar(6), fEffectLength, GetVar(5), GetVar(9), nDamage, GetVar(7), m_pOwner, GetVar(2), GetVar(3), vTargetList );
m_nTargetCount = static_cast<int>( vTargetList.size() );
// { 모든 타겟에 데미지 적용하세~
for( itTargetList = vTargetList.begin(); itTargetList != vTargetList.end(); ++itTargetList )
{
StructCreature* pDealTarget = (*itTargetList);
StructCreature::_DAMAGE_INFO Damage = pDealTarget->DealMagicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pDealTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::MAGIC_DAMAGE, elemental_type, Damage, pDealTarget->GetHandle() );
}
// }
}
void StructSkill::MAGIC_SPECIAL_REGION_DAMAGE( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int nMagicPoint = m_pOwner->GetMagicPoint((Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
int nDamage = nMagicPoint * ( GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(2) * GetEnhance() ) + GetVar(3) + GetVar(4) * GetRequestedSkillLevel();
int elemental_type = GetSkillBase()->GetElementalType();
// }
float fEffectLength = GetVar(9) * GameRule::DEFAULT_UNIT_SIZE;
m_fRange = fEffectLength;
std::vector< StructCreature * > vTargetList;
std::vector< StructCreature * >::iterator itTargetList;
AR_TIME t = GetArTime();
nDamage = EnumSkillTargetsAndCalcDamage( m_pOwner->GetCurrentPosition( t ), m_pOwner->GetLayer(), pTarget->GetCurrentPosition( t ), !!GetVar(6), fEffectLength, GetVar(5), GetVar(8), nDamage, GetVar(7), m_pOwner, GetVar(10), GetVar(11), vTargetList );
m_nTargetCount = static_cast<int>( vTargetList.size() );
// { 모든 타겟에 데미지 적용하세~
for( itTargetList = vTargetList.begin(); itTargetList != vTargetList.end(); ++itTargetList )
{
StructCreature* pDealTarget = (*itTargetList);
StructCreature::_DAMAGE_INFO Damage = pDealTarget->DealMagicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pDealTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::MAGIC_DAMAGE, elemental_type, Damage, pDealTarget->GetHandle() );
}
// }
}
void StructSkill::SINGLE_PHYSICAL_DAMAGE_WITH_SHIELD( struct StructCreature* pTarget )
{
if( !pTarget || !m_pOwner->IsWearShield() ) return;
// 데미지가 스텟에 의한 공격력만 계산하고 거기에 증가다 -┏
int nAttackPoint = GameRule::GetAttackPoint( m_pOwner->GetStrength(), m_pOwner->GetLevel(), m_pOwner->GetFCM() );
int nDamage = nAttackPoint + GetVar(0) + ( GetVar(1) * GetRequestedSkillLevel() ) + ( GetVar(4) * GetEnhance() );
int elemental_type = GetSkillBase()->GetElementalType();
int nCastingCancelRate = GetVar(2) + ( GetVar(3) * GetRequestedSkillLevel() ) + ( GetVar(5) * GetEnhance() );
// 캐스팅 캔슬 성공~
StructSkill *pCancelSkill = pTarget->GetCastSkill();
if( pCancelSkill && pCancelSkill->m_Status == STATUS_CAST && XRandom( 0, 99 ) < nCastingCancelRate )
{
pTarget->CancelSkill();
AR_TIME t = GetArTime();
pTarget->AddState( static_cast< StructState::StateCode >(GetStateId()), m_pOwner->GetHandle(), GetRequestedSkillLevel(), t, t + GetVar(7) + ( GetVar(8) * GetRequestedSkillLevel() ) );
}
StructCreature::_DAMAGE_INFO Damage = pTarget->DealPhysicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
return;
}
void StructSkill::PHYSICAL_DIRECTIONAL_DAMAGE( struct StructCreature * pTarget )
{
if( !pTarget ) return;
int nDamage = 0;
int nAttackPoint = m_pOwner->GetAttackPointRight( (Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
// 데미지
int elemental_type = GetSkillBase()->GetElementalType();
nDamage = nAttackPoint + GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(5) * GetEnhance();
// 방향성 체크
int nAccAdd = 0;
if( m_pOwner->IsBackOf( *pTarget ) ) nAccAdd = GetVar( 2 );
else if( m_pOwner->IsSideOf( *pTarget ) ) nAccAdd = GetVar( 3 );
else nAccAdd = GetVar( 4 );
StructCreature::_DAMAGE_INFO Damage = pTarget->DealPhysicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ) + nAccAdd, GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
return;
}
void StructSkill::SINGLE_PHYSICAL_DAMAGE_T1( struct StructCreature * pTarget )
{
if( !pTarget ) return;
int nDamage = 0;
int nAttackPoint = m_pOwner->GetAttackPointRight( (Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
if( GetSkillBase()->GetEffectType() == SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_RUSH_KNOCKBACK_OLD )
{
if( m_nCurrentFire == 0 )
{
m_nCurrentFire = 1;
m_nTotalFire = 2;
if( !PHYSICAL_DAMAGE_RUSH( pTarget, &m_nRushDamage ) )
{
m_nCurrentFire = 2;
}
return;
}
else
{
ArcadiaServer::Instance().MoveObject( m_pOwner, m_RushPos, m_fRushFace );
nDamage += m_nRushDamage;
++m_nCurrentFire;
}
}
// 데미지
int elemental_type = GetSkillBase()->GetElementalType();
nDamage += nAttackPoint + GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(4) * GetEnhance();
StructCreature::_DAMAGE_INFO Damage = pTarget->DealPhysicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
if( !Damage.bBlock && !Damage.bMiss && !Damage.bPerfectBlock
&& ( GetSkillBase()->GetSkillEffectType() == SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_KNOCKBACK_OLD
|| GetSkillBase()->GetSkillEffectType() == SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_RUSH_KNOCKBACK_OLD )
&& !( pTarget->IsMonster() && static_cast< StructMonster * >( pTarget )->IsBossMonster() ) )
{
float fRange = GetVar(5) + GetVar(6) * GetRequestedSkillLevel() + GetVar(7) * GetEnhance();
fRange *= GameRule::DEFAULT_UNIT_SIZE;
AR_TIME knock_back_time = ( GetVar(8) + GetVar(9) * GetRequestedSkillLevel() + GetVar(10) * GetEnhance() ) * 100;
AFFECT_KNOCK_BACK( pTarget, fRange, knock_back_time );
AddSkillDamageWithKnockBackResult( &m_vResultList, SkillResult::DAMAGE_WITH_KNOCK_BACK, elemental_type, Damage, pTarget->GetHandle(), pTarget->GetPos().x, pTarget->GetPos().y, knock_back_time );
}
else
{
AddSkillDamageResult( &m_vResultList, SkillResult::DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
}
return;
}
void StructSkill::SINGLE_PHYSICAL_DAMAGE_T2( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int nDamage = 0;
int nAttackPoint = m_pOwner->GetAttackPointRight( (Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
nDamage = nAttackPoint * ( GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(4) * GetEnhance() );
int nMax = nAttackPoint + GetVar(2) + GetVar(3) * GetRequestedSkillLevel() + GetVar(5) * GetEnhance();
nDamage = std::min( nDamage, nMax );
int elemental_type = GetSkillBase()->GetElementalType();
StructCreature::_DAMAGE_INFO Damage = pTarget->DealPhysicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
return;
}
void StructSkill::SINGLE_PHYSICAL_DAMAGE_T2_ADD_ENERGY( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int nDamage = 0;
int nAttackPoint = m_pOwner->GetAttackPointRight( (Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
nDamage = nAttackPoint * ( GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(4) * GetEnhance() );
int nMax = nAttackPoint + GetVar(2) + GetVar(3) * GetRequestedSkillLevel() + GetVar(5) * GetEnhance();
nDamage = std::min( nDamage, nMax );
int elemental_type = GetSkillBase()->GetElementalType();
StructCreature::_DAMAGE_INFO Damage = pTarget->DealPhysicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
for( int i = 0 ; i < GetVar(6) + GetVar(7) * GetRequestedSkillLevel() + GetVar(8) * GetEnhance() ; ++i )
m_pOwner->AddEnergy();
AddSkillDamageResult( &m_vResultList, SkillResult::DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
return;
}
void StructSkill::SINGLE_PHYSICAL_DAMAGE_T3( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int nDamage = 0;
int nAttackPoint = m_pOwner->GetAttackPointRightWithoutWeapon( (Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
if( GetSkillBase()->GetEffectType() == SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_WITHOUT_WEAPON_RUSH_KNOCK_BACK )
{
if( m_nCurrentFire == 0 )
{
m_nCurrentFire = 1;
m_nTotalFire = 2;
if( !PHYSICAL_DAMAGE_RUSH( pTarget, &m_nRushDamage ) )
{
m_nCurrentFire = 2;
}
return;
}
else
{
ArcadiaServer::Instance().MoveObject( m_pOwner, m_RushPos, m_fRushFace );
nDamage += m_nRushDamage;
++m_nCurrentFire;
}
}
nDamage += nAttackPoint + GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(4) * GetEnhance();
int elemental_type = GetSkillBase()->GetElementalType();
StructCreature::_DAMAGE_INFO Damage = pTarget->DealPhysicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
if( !Damage.bBlock && !Damage.bMiss && !Damage.bPerfectBlock && GetSkillBase()->GetEffectType() == SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_WITHOUT_WEAPON_RUSH_KNOCK_BACK
&& !( pTarget->IsMonster() && static_cast< StructMonster * >( pTarget )->IsBossMonster() ) )
{
float fRange = GetVar(5) + GetVar(6) * GetRequestedSkillLevel() + GetVar(7) * GetEnhance();
fRange *= GameRule::DEFAULT_UNIT_SIZE;
AR_TIME knock_back_time = ( GetVar(8) + GetVar(9) * GetRequestedSkillLevel() + GetVar(10) * GetEnhance() ) * 100;
AFFECT_KNOCK_BACK( pTarget, fRange, knock_back_time );
AddSkillDamageWithKnockBackResult( &m_vResultList, SkillResult::DAMAGE_WITH_KNOCK_BACK, elemental_type, Damage, pTarget->GetHandle(), pTarget->GetPos().x, pTarget->GetPos().y, knock_back_time );
}
else
{
AddSkillDamageResult( &m_vResultList, SkillResult::DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
}
return;
}
void StructSkill::MULTIPLE_PHYSICAL_DAMAGE_T1( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int nAttackPoint = m_pOwner->GetAttackPointRight( (Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
int nDamage = nAttackPoint + GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(5) * GetEnhance();
int nCount = GetVar(2) + GetVar(3) * GetRequestedSkillLevel();
int elemental_type = GetSkillBase()->GetElementalType();
// 데미지
for( int i = 0; i < nCount; ++i )
{
StructCreature::_DAMAGE_INFO Damage = pTarget->DealPhysicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
}
m_nFireCount = nCount;
m_nFireTime += GetVar(4) * nCount * 100;
return;
}
void StructSkill::MULTIPLE_PHYSICAL_DAMAGE_T2( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int nAttackPoint = m_pOwner->GetAttackPointRight( (Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
int nDamage = nAttackPoint * ( GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(5) * GetEnhance() );
int nMax = nAttackPoint + GetVar(2) + GetVar(3) * GetRequestedSkillLevel();
nDamage = std::min( nDamage, nMax );
int nCount = GetVar(2) + GetVar(3)*GetRequestedSkillLevel();
// 데미지
for( int i = 0; i < nCount; ++i )
{
int elemental_type = GetSkillBase()->GetElementalType();
StructCreature::_DAMAGE_INFO Damage = pTarget->DealPhysicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::DAMAGE, GetSkillBase()->GetElementalType(), Damage, pTarget->GetHandle() );
}
m_nFireCount = nCount;
// 연타 횟수 만큼 행동 불가
m_nFireTime += GetVar(4) * nCount * 100;
return;
}
void StructSkill::MULTIPLE_PHYSICAL_DAMAGE_T3( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int nAttackPoint = m_pOwner->GetAttackPointRight( (Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
int nDamage = nAttackPoint + GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(5) * GetEnhance();
if( m_nCurrentFire == 0 )
{
m_nTotalFire = GetVar(2) + GetVar(3) * GetRequestedSkillLevel();
m_nCurrentFire = 1;
}
else
{
++m_nCurrentFire;
}
int elemental_type = GetSkillBase()->GetElementalType();
// 데미지
StructCreature::_DAMAGE_INFO Damage = pTarget->DealPhysicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
m_nFireTime += GetVar(4) * 100;
return;
}
void StructSkill::MULTIPLE_PHYSICAL_DAMAGE_T4( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int nAttackPoint = m_pOwner->GetAttackPointRight( (Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
int nDamage = nAttackPoint + GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(5) * GetEnhance();
if( m_nCurrentFire == 0 )
{
m_nTotalFire = std::min( (int)( ( GetRequestedSkillLevel() - 1 ) / GetVar(2) + 1 ), 3 );
m_nCurrentFire = 1;
}
else
{
++m_nCurrentFire;
}
int elemental_type = GetSkillBase()->GetElementalType();
int nCount = GetVar( m_nCurrentFire + 5 );
// 데미지
for( int i = 0; i < nCount; ++i )
{
elemental_type = GetSkillBase()->GetElementalType();
StructCreature::_DAMAGE_INFO Damage = pTarget->DealPhysicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
}
m_nFireCount = nCount;
// 연타 횟수 만큼 행동 불가
m_nFireTime += GetVar( 9 ) + m_nCurrentFire - 1;
return;
}
void StructSkill::MAGIC_ABSORB_DAMAGE( struct StructCreature* pTarget )
{
// 실제마법공격력 = 마법공격력 + [값1] + (SLv*[값2]), HP흡수량 = (데미지*[값3]) + (데미지*EP*[값5]), MP흡수량 = (데미지*[값4]) + (데미지*EP*[값6])
if( !pTarget ) return;
int nDamage = 0;
nDamage = m_pOwner->GetMagicPoint((Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() ) + GetVar(0) + GetVar(1) * GetRequestedSkillLevel();
// + GetEnhance() * GetVar(2);
int elemental_type = GetSkillBase()->GetElementalType();
int nRealDamage = 0;
nRealDamage = pTarget->GetHP();
StructCreature::_DAMAGE_INFO Damage = pTarget->DealMagicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::MAGIC_DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
nRealDamage = std::min( nRealDamage, Damage.nDamage );
// HP흡수량 = (데미지*[값3]) + (데미지*EP*[값5]), MP흡수량 = (데미지*[값4]) + (데미지*EP*[값6])
int nHPAbsorb = GetVar( 2 ) * nRealDamage + GetVar( 4 ) * Damage.nDamage * GetEnhance();
int nMPAbsorb = GetVar( 3 ) * nRealDamage + GetVar( 5 ) * Damage.nDamage * GetEnhance();
m_pOwner->AddHP( nHPAbsorb );
m_pOwner->AddMP( nMPAbsorb );
SkillResult skill_result;
skill_result.add_hp.type = SkillResult::ADD_HP;
skill_result.add_hp.hTarget = m_pOwner->GetHandle();
skill_result.add_hp.nIncHP = nHPAbsorb;
skill_result.add_hp.target_hp = m_pOwner->GetHP();
m_vResultList.push_back( skill_result );
skill_result.add_hp.type = SkillResult::ADD_MP;
skill_result.add_hp.hTarget = m_pOwner->GetHandle();
skill_result.add_hp.nIncHP = nMPAbsorb;
skill_result.add_hp.target_hp = m_pOwner->GetMP();
m_vResultList.push_back( skill_result );
}
void StructSkill::CORPSE_ABSORB( struct StructCreature* pTarget )
{
if( !pTarget ) return;
// 몬스터에게만 사용 가능
if( !pTarget->IsMonster() || !pTarget->IsDead() ) return;
RemoveMonsterFromWorld( static_cast< StructMonster * >( pTarget ) );
int nHP = GetVar(0) + GetRequestedSkillLevel()*GetVar(1) + GetEnhance()*GetVar(4);
int nMP = GetVar(2) + GetRequestedSkillLevel()*GetVar(3) + GetEnhance()*GetVar(5);
StructCreature *pHealTarget = m_pOwner;
if( GetVar(6) && m_pOwner->IsPlayer() )
pHealTarget = static_cast< StructSummon * >( static_cast< StructPlayer * >(m_pOwner)->GetMainSummon() );
nHP = pHealTarget->Heal( nHP );
nMP = pHealTarget->MPHeal( nMP );
SkillResult skill_result;
skill_result.add_hp_mp_sp.type = SkillResult::ADD_HP_MP_SP;
skill_result.add_hp_mp_sp.hTarget = pHealTarget->GetHandle();
skill_result.add_hp_mp_sp.target_hp = pHealTarget->GetHP();
skill_result.add_hp_mp_sp.nIncHP = nHP;
skill_result.add_hp_mp_sp.nIncMP = nMP;
skill_result.add_hp_mp_sp.nIncSP = 0;
skill_result.add_hp_mp_sp.target_mp = pHealTarget->GetMP();
m_vResultList.push_back( skill_result );
return;
}
void StructSkill::CORPSE_EXPLOSION( struct StructCreature* pTarget )
{
if( !pTarget ) return;
// 몬스터에게만 사용 가능
if( !pTarget->IsMonster() || !pTarget->IsDead() ) return;
int elemental_type = GetVar( 6 );
int nDamage = GetVar(0) + GetVar(1) * GetRequestedSkillLevel();
// }
float fEffectLength = ( GetVar( 2 ) + GetVar( 5 ) * GetEnhance() ) * GameRule::DEFAULT_UNIT_SIZE;
m_fRange = fEffectLength;
std::vector< StructCreature * > vTargetList;
std::vector< StructCreature * >::iterator itTargetList;
AR_TIME t = GetArTime();
nDamage = EnumSkillTargetsAndCalcDamage( m_pOwner->GetCurrentPosition( t ), m_pOwner->GetLayer(), pTarget->GetCurrentPosition( t ), true, fEffectLength, -1, 0, nDamage, true, m_pOwner, GetVar(3), GetVar(4), vTargetList );
RemoveMonsterFromWorld( static_cast< StructMonster * >( pTarget ) );
m_nTargetCount = 0;
// { 모든 타겟에 데미지 적용하세~
for( itTargetList = vTargetList.begin(); itTargetList != vTargetList.end(); ++itTargetList )
{
StructCreature* pDealTarget = (*itTargetList);
StructCreature::_DAMAGE_INFO Damage = pDealTarget->DealMagicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pDealTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::MAGIC_DAMAGE, elemental_type, Damage, pDealTarget->GetHandle() );
++m_nTargetCount;
}
// }
return;
}
const unsigned short StructSkill::IsInstanceGameEnterableOwner() const
{
StructPlayer * pPlayer = static_cast< StructPlayer * >( m_pOwner );
// 배틀 아레나에 참여 중일 경우 추가 체크/처리
int nArenaID = pPlayer->GetBattleArenaID();
if( nArenaID )
{
// 이미 시작된 경기(혹은 아직 시작되진 않았지만 연습 경기)에 참여 중인 경우에는 입장 불가
// * 위의 경우들은 파티에 참여 중
int nPartyID = pPlayer->GetPartyID();
if( nPartyID )
{
assert( PartyManager::GetInstance().IsBattleArenaTeamParty( nPartyID ) );
return RESULT_NOT_ACTABLE_IN_BATTLE_ARENA;
}
// 아직 파티에 소속은 아닌데 아레나ID가 세팅되어 있다면 대기열에 대기 중인 경우. 대기열 자동 탈퇴시켜 줌.
BattleArenaManager::Instance().QuitGame( pPlayer, false, false, ALT_ENTER_HUNTAHOLIC );
// 대기열 이탈 처리도 제대로 안 됐다면 실패 처리
if( pPlayer->GetBattleArenaID() )
{
// 근데 그럼 이거 무슨 경우지;?
assert( 0 );
return RESULT_NOT_ACTABLE_IN_BATTLE_ARENA;
}
}
return RESULT_SUCCESS;
}
// 인스턴스 게임 입장
void StructSkill::INSTANCE_GAME_ENTER()
{
if( !m_pOwner->IsPlayer() )
return;
unsigned short nErrorCode = IsInstanceGameEnterableOwner();
if( nErrorCode != RESULT_SUCCESS )
{
AddSkillResult( &m_vResultList, false, nErrorCode );
return;
}
StructPlayer *pPlayer = static_cast< StructPlayer* >( m_pOwner );
// 입장시 상태 저장(좌표, HP/MP)
pPlayer->StoreCurrentStatesOnEnterInstanceGame( true );
switch( GetSkillId() )
{
case SKILL_RANKED_DEATHMATCH_ENTER:
pPlayer->PendWarpToDeathmatch( 1 );
break;
case SKILL_FREED_DEATHMATCH_ENTER:
pPlayer->PendWarpToDeathmatch( 2 );
break;
default:
// 아무 결과 메시지도 없이 씹어버리기
return;
}
AddSkillResult( &m_vResultList, true, RESULT_SUCCESS );
}
void StructSkill::INSTANCE_GAME_EXIT()
{
if( !m_pOwner->IsPlayer() )
return;
StructPlayer *pPlayer = static_cast< StructPlayer* >( m_pOwner ) ;
ArPosition pos;
pPlayer->GetPositionOnEnterInstanceGame( &pos );
unsigned short nErrorCode = RESULT_SUCCESS;
unsigned char layer = 0;
int current_channel = ChannelManager::GetChannelId( pPlayer->GetX(), pPlayer->GetY() );
int target_channel = ChannelManager::GetChannelId( pos.x, pos.y );
if( current_channel && current_channel == target_channel )
{
layer = pPlayer->GetLayer();
}
else if( target_channel )
{
layer = ChannelManager::GetProperLayer( pos.x, pos.y );
}
pPlayer->PendWarp( pos.x, pos.y, layer );
pPlayer->SetMove( pPlayer->GetCurrentPosition( GetArTime() ), 0 );
AddSkillResult( &m_vResultList, nErrorCode == RESULT_SUCCESS, nErrorCode );
}
const int StructSkill::GetTargetHuntaholicID() const
{
if( GetSkillId() != SKILL_WARP_TO_HUNTAHOLIC_LOBBY )
return 0;
// 현재는 어떠한 조건에도 무조건 베어로드로 ㄱㄱ임
return 10000;
}
const unsigned short StructSkill::IsHuntaholicLobbyEnterableOwner( const int nHuntaholicID ) const
{
StructPlayer * pPlayer = static_cast< StructPlayer * >( m_pOwner );
// 배틀 아레나에 참여 중일 경우 추가 체크/처리
int nArenaID = pPlayer->GetBattleArenaID();
if( nArenaID )
{
// 이미 시작된 경기(혹은 아직 시작되진 않았지만 연습 경기)에 참여 중인 경우에는 입장 불가
// * 위의 경우들은 파티에 참여 중
int nPartyID = pPlayer->GetPartyID();
if( nPartyID )
{
assert( PartyManager::GetInstance().IsBattleArenaTeamParty( nPartyID ) );
return RESULT_NOT_ACTABLE_IN_BATTLE_ARENA;
}
// 아직 파티에 소속은 아닌데 아레나ID가 세팅되어 있다면 대기열에 대기 중인 경우. 대기열 자동 탈퇴시켜 줌.
BattleArenaManager::Instance().QuitGame( pPlayer, false, false, ALT_ENTER_HUNTAHOLIC );
// 대기열 이탈 처리도 제대로 안 됐다면 실패 처리
if( pPlayer->GetBattleArenaID() )
{
// 근데 그럼 이거 무슨 경우지;?
assert( 0 );
return RESULT_NOT_ACTABLE_IN_BATTLE_ARENA;
}
}
AR_TIME t = GetArTime();
// 이미 헌터홀릭 로비 또는 던전 안에 있으면 입장 워프 불가
if( HuntaholicManager::Instance().GetHuntaholicID( pPlayer->GetPos() ) )
return RESULT_NOT_ACTABLE_IN_HUNTAHOLIC;
// PK On 상태면 입장 불가
if( pPlayer->IsPKOn() || pPlayer->IsPKOning() )
return RESULT_PK_LIMIT;
ArPosition posLobby( HuntaholicManager::Instance().GetLobbyPosition( nHuntaholicID ) );
// 데이터에 없는 놈이라면 처리 불가
if( !posLobby.GetX() || !posLobby.GetY() )
return RESULT_NOT_EXIST;
unsigned char nLayer = HuntaholicManager::Instance().GetProperLobbyLayer( nHuntaholicID, pPlayer->GetLevel() );
if( nLayer == GameRule::HUNTAHOLIC_UNUSABLE_LOBBY_LAYER )
return RESULT_NOT_ENOUGH_LEVEL;
// 중독 방지 시간으로 인한 입장 제한 처리
if( pPlayer->IsGameTimeLimited() && pPlayer->GetContinuousPlayTime() >= GameRule::nMaxTiredGameTime )
return RESULT_GAMETIME_LIMITED;
return RESULT_SUCCESS;
}
void StructSkill::WARP_TO_HUNTAHOLIC_LOBBY()
{
if( !m_pOwner->IsPlayer() )
return;
int nTargetHuntaholicID = GetTargetHuntaholicID();
unsigned short nErrorCode = IsHuntaholicLobbyEnterableOwner( nTargetHuntaholicID );
if( nErrorCode == RESULT_SUCCESS )
nErrorCode = static_cast< StructPlayer * >( m_pOwner )->PendWarpToHuntaholicLobby( nTargetHuntaholicID );
AddSkillResult( &m_vResultList, nErrorCode == RESULT_SUCCESS, nErrorCode );
}
// 타운 포탈
void StructSkill::TOWN_PORTAL()
{
if( m_pOwner->IsPlayer() )
{
StructPlayer *pPlayer = static_cast< StructPlayer* >( m_pOwner );
ArPosition pos;
pPlayer->GetLastTownPosition( &pos );
// 귀환 지점 설정 전 유효성 검사
AR_UNIT targetX = pos.x, targetY = pos.y;
bool bValidPos = true;
unsigned short try_cnt = 0;
while( GameContent::IsBlocked( targetX, targetY ) )
{
targetX = XRandom( pos.x - 60, pos.x + 60 );
targetY = XRandom( pos.y - 60, pos.y + 60 );
if( ++try_cnt > 300 )
{
bValidPos = false;
break;
}
}
// 유효하지 않은 지점으로 이동시켜주느니 그냥 제자리로 이동시켜준다.
if( !bValidPos )
{
_cprint( "Fail To Warp With Given Position [%d, %d], PlayerSID[%d] (TownPortal)\n", pos.x, pos.y, pPlayer->GetPlayerUID() );
FILELOG( "Fail To Warp With Given Position [%d, %d], PlayerSID[%d] (TownPortal)", pos.x, pos.y, pPlayer->GetPlayerUID() );
targetX = pPlayer->GetX();
targetY = pPlayer->GetY();
}
unsigned char layer = 0;
int current_channel = ChannelManager::GetChannelId( m_pOwner->GetX(), m_pOwner->GetY() );
int target_channel = ChannelManager::GetChannelId( targetX, targetY );
if( current_channel && current_channel == target_channel )
{
layer = pPlayer->GetLayer();
}
else if( target_channel )
{
layer = ChannelManager::GetProperLayer( targetX, targetY );
}
pPlayer->PendWarp( targetX, targetY, layer );
pPlayer->SetMove( pPlayer->GetCurrentPosition( GetArTime() ), 0 );
ArcadiaServer::Instance().SetObjectPriority( pPlayer, ArSchedulerObject::UPDATE_PRIORITY_HIGHEST );
}
}
// 귀환의 깃털
void StructSkill::RETURN_FEATHER()
{
if( m_pOwner->IsPlayer() )
{
StructPlayer *pPlayer = static_cast< StructPlayer* >( m_pOwner );
ArPosition curPos = pPlayer->GetCurrentPosition( GetArTime() );
pPlayer->SetMove( curPos, 0 );
StructItem *pItem = StructItem::FindItem( m_hTarget );
if( !pItem )
return;
// 귀환/복귀의 깃털은 캐스팅 완료로 스킬 시전시 아이템이 소모됨.
if( !pPlayer->EraseItem( pItem, 1 ) )
return;
// 복귀의 깃털에 세팅될 좌표를 세팅하고 해당 아이템을 지급
StructItem *pBackItem = StructItem::AllocItem( 0, ItemBase::ITEM_CODE_RETURN_BACK_FEATHER, 1, ItemInstance::BY_ITEM, -1, -1, -1, curPos.x, curPos.y, pPlayer->GetLayer(), pPlayer->GetLocationId() );
pPlayer->PushItem( pBackItem, pBackItem->GetCount() );
// 여기부터는 일반 타운 포탈이랑 동일
TOWN_PORTAL();
// 테섭이면 귀환 깃털 써도 저장 안 함
if( ENV().GetInt( "game.ServiceServer" ) )
pPlayer->Save();
}
}
// 복귀의 깃털
void StructSkill::RETURN_BACK_FEATHER()
{
if( m_pOwner->IsPlayer() )
{
StructPlayer *pPlayer = static_cast< StructPlayer* >( m_pOwner );
ArPosition pos;
unsigned char layer = 0;
StructItem *pItem = StructItem::FindItem( m_hTarget );
if( !pItem )
return;
pos.x = pItem->GetSocketCode( 0 );
pos.y = pItem->GetSocketCode( 1 );
layer = pItem->GetSocketCode( 2 );
pPlayer->PendWarp( pos.x, pos.y, layer );
pPlayer->SetMove( pPlayer->GetCurrentPosition( GetArTime() ), 0 );
ArcadiaServer::Instance().SetObjectPriority( pPlayer, ArSchedulerObject::UPDATE_PRIORITY_HIGHEST );
// 귀환/복귀의 깃털은 캐스팅 완료로 스킬 시전시 아이템이 소모됨.
pPlayer->EraseItem( pItem, 1 );
pPlayer->Save();
}
}
void StructSkill::ADD_ENERGY()
{
for( int nAddEnergyCount = 0; nAddEnergyCount < GetRequestedSkillLevel() && m_pOwner->GetEnergyCount() < m_pOwner->GetMaxEnergyCount(); ++nAddEnergyCount )
m_pOwner->AddEnergy();
AddSkillResult( &m_vResultList, true );
}
void StructSkill::CASTING_CANCEL_WITH_ADD_STATE( struct StructCreature* pTarget )
{
int nCastingCancelRate = GetVar( 1 ) + GetVar( 2 ) * GetRequestedSkillLevel() + GetVar( 3 ) * GetEnhance();
bool bSuccess = false;
// 캐스팅 캔슬 성공~
StructSkill *pCancelSkill = pTarget->GetCastSkill();
if( pCancelSkill && pCancelSkill->m_Status == STATUS_CAST )
{
// 캔슬될 스킬의 타입을 구분, 물리/마법
switch( static_cast< int >( GetVar( 0 ) ) )
{
case SkillBase::SC_PHYSICAL: if( pCancelSkill->GetSkillBase()->IsPhysicalSkill() ) bSuccess = true; break;
case SkillBase::SC_MAGICAL: if( pCancelSkill->GetSkillBase()->IsMagicalSkill() ) bSuccess = true; break;
case SkillBase::SC_EVERY: bSuccess = true; break;
default: bSuccess = false; break;
}
if( bSuccess && XRandom( 0, 99 ) < nCastingCancelRate )
{
pTarget->CancelSkill();
if( XRandom( 0, 99 ) < GetVar(11) + GetVar(12) * GetRequestedSkillLevel() + GetVar(13) * GetEnhance() )
{
AR_TIME t = GetArTime();
pTarget->AddState( static_cast< StructState::StateCode >( static_cast< int >( GetVar(4) ) ), m_pOwner->GetHandle(), GetVar(5) + GetVar(6) * GetRequestedSkillLevel() + GetVar(7) * GetEnhance(), t, t + GetVar(8) + GetVar(9) * GetEnhance() );
}
}
}
AddSkillResult( &m_vResultList, bSuccess, 0, pTarget->GetHandle() );
}
void StructSkill::RESPAWN_NEAR_MONSTER()
{
// 몬스터 전용 스킬 유형임(respawn_near_monster 스크립트 함수 대용)
if( !m_pOwner->IsMonster() )
return;
ArPosition pos = m_pOwner->GetCurrentPosition( GetArTime() );
if( GameContent::IsBlocked( pos.GetX(), pos.GetY() ) )
return;
for( int nRespawnTypeIndex = 0 ; nRespawnTypeIndex < 3 ; ++nRespawnTypeIndex )
{
int nMonsterID = GetVar( nRespawnTypeIndex * 6 + 0 );
int nRespawnCount = GetVar( nRespawnTypeIndex * 6 + 1 ) + GetVar( nRespawnTypeIndex * 6 + 2 ) * GetRequestedSkillLevel();
bool bShareEnemyOnRespawn = GetVar( nRespawnTypeIndex * 6 + 3 );
AR_HANDLE hInitialEnemy = ( bShareEnemyOnRespawn && m_pOwner->IsAttacking() ) ? m_pOwner->GetEnemyHandle() : 0;
AR_TIME nLifeTime = GetVar( nRespawnTypeIndex * 6 + 4 ) + GetVar( nRespawnTypeIndex * 6 + 5 ) * GetRequestedSkillLevel();
if( !nLifeTime )
nLifeTime = INFINITE_TIME;
// 헌터홀릭 던전 내부에서 몬스터가 스킬을 통해 몬스터를 소환한 경우에는 직접 리젠 처리하지 않고 HuntaholicManager에 몹 리젠 처리 요청
int nHuntaholicID = HuntaholicManager::Instance().GetHuntaholicID( pos );
int nInstanceDungeonID = InstanceDungeonManager::Instance().GetInstanceDungeonID( pos );
if( nHuntaholicID && HuntaholicManager::Instance().IsHuntaholicDungeon( pos ) )
{
HuntaholicManager::Instance().PendRespawnMonster( nHuntaholicID, pos.x, pos.y, m_pOwner->GetLayer(), nMonsterID, nRespawnCount, nLifeTime, hInitialEnemy );
}
else if( nInstanceDungeonID )
{
InstanceDungeonManager::Instance().PendRespawnMonster( nInstanceDungeonID, pos.x, pos.y, m_pOwner->GetLayer(), nMonsterID, nRespawnCount, nLifeTime, hInitialEnemy );
}
else
{
for( int nProcCount = 0 ; nProcCount < nRespawnCount ; ++nProcCount )
{
// 락 문제로 일단 시전자 위치에 리젠시키고 바로 랜덤 범위 내 위치로 이동 시킴
StructMonster * pMob = respawnMonster( pos.x, pos.y, m_pOwner->GetLayer(), nMonsterID, true, 0, NULL, false );
// 리스폰 안 될 경우 체크 (대부분 MonsterID가 잘못 들어간 경우가 많다.)
if( !pMob )
{
_cprint( "Failed to respawn a monster by monster skill(summoning): CasterMonsterID[%d] SummonedMonsterID[%d] SkillId[%d]\n",
static_cast< StructMonster *>( m_pOwner )->GetMonsterId(), nMonsterID, GetSkillId() );
FILELOG( "Failed to respawn a monster by monster skill(summoning): CasterMonsterID[%d] SummonedMonsterID[%d] SkillId[%d]",
static_cast< StructMonster *>( m_pOwner )->GetMonsterId(), nMonsterID, GetSkillId() );
break;
}
// 랜덤 이동 위치 설정
AR_UNIT x, y;
x = XRandom( pos.x - 60, pos.x + 60 );
y = XRandom( pos.y - 60, pos.y + 60 );
if( GameContent::CollisionToLine( pos.x, pos.y, x, y ) )
{
x = pos.x;
y = pos.y;
}
ArPosition targetPos( x, y );
pMob->SetMove( targetPos, pMob->GetRealMoveSpeed() * 3 );
pMob->SetRespawnByScript(); // 선공 몹의 경우 바로 적 인식하도록 함
ArcadiaServer::Instance().SetObjectPriority( pMob, ArObject::UPDATE_PRIORITY_HIGHEST );
StructMonster * pOnwerMonster = static_cast< StructMonster * >( m_pOwner );
if( pOnwerMonster->IsDungeonRaidMonster() )
{
int nDungeonID = DungeonManager::Instance().GetDungeonID( x, y );
if( nDungeonID )
{
pMob->SetDungeonRaidMonster();
pMob->CalculateStat();
DungeonManager::Instance().AddToDungeonRaidMonster( pMob, nDungeonID, m_pOwner->GetLayer() );
}
}
if( hInitialEnemy )
{
pMob->AddHate( hInitialEnemy, 1 );
}
// 시간 제한 몬스터 소환의 경우 시간 제한 설정 적용
if( nLifeTime )
pMob->SetLifeTime( nLifeTime );
}
}
}
AddSkillResult( &m_vResultList, true );
}
void StructSkill::RESPAWN_MONSTER()
{
ArPosition pos = m_pOwner->GetCurrentPosition( GetArTime() );
if( GameContent::IsBlocked( pos.GetX(), pos.GetY() ) )
return;
int nMonsterID = 0;
switch( GetSkillBase()->GetEffectType() )
{
case SkillBase::EF_RESPAWN_MONSTER_RANDOMLY:
nMonsterID = GameContent::GetRandomMonsterID( GetVar( 0 ) );
break;
case SkillBase::EF_RESPAWN_MONSTER_WITH_DIFF_CODE:
if( GetRequestedSkillLevel() >= 1 && GetRequestedSkillLevel() <= 20 )
nMonsterID = GetVar( GetRequestedSkillLevel() - 1 );
break;
}
if( !nMonsterID )
return;
// 헌터홀릭 던전 내부에서 스킬을 통해 몬스터를 소환한 경우에는 직접 리젠 처리하지 않고 HuntaholicManager에 몹 리젠 처리 요청
int nHuntaholicID = HuntaholicManager::Instance().GetHuntaholicID( pos );
int nInstanceDungeonID = InstanceDungeonManager::Instance().GetInstanceDungeonID( pos );
if( nHuntaholicID && HuntaholicManager::Instance().IsHuntaholicDungeon( pos ) )
{
HuntaholicManager::Instance().PendRespawnMonster( nHuntaholicID, pos.x, pos.y, m_pOwner->GetLayer(), nMonsterID, 1, INFINITE_TIME, 0 );
}
else if( nInstanceDungeonID )
{
InstanceDungeonManager::Instance().PendRespawnMonster( nInstanceDungeonID, pos.x, pos.y, m_pOwner->GetLayer(), nMonsterID, 1, INFINITE_TIME, 0 );
}
else
{
// 락 문제로 일단 시전자 위치에 리젠
StructMonster * pMob = respawnMonster( pos.x, pos.y, m_pOwner->GetLayer(), nMonsterID, true, 0, NULL, false );
if( !pMob ) return;
pMob->SetRespawnByScript(); // 선공 몹의 경우 바로 적 인식하도록 함
ArcadiaServer::Instance().SetObjectPriority( pMob, ArObject::UPDATE_PRIORITY_HIGHEST );
}
AddSkillResult( &m_vResultList, true );
}
// 크리쳐 테이밍
void StructSkill::CREATURE_TAMING( struct StructCreature* pTarget )
{
// 타겟 없으면 KIN
if( !pTarget ) return;
// 몬스터에게만 걸린다~
if( !pTarget->IsMonster() ) return;
StructMonster *pMonster = static_cast< StructMonster * >( pTarget );
// 플레이어만 쓸수 있는거야~!
if( !m_pOwner->IsPlayer() ) return;
if( m_hCastItem )
{
StructItem * pCastItem = StructItem::FindItem( m_hCastItem );
if( !pCastItem || ( pCastItem->GetOwnerHandle() && pCastItem->GetOwnerHandle() != m_pOwner->GetHandle() ) || !pCastItem->IsInInventory() )
return;
if( !static_cast< StructPlayer * >( m_pOwner )->EraseItem( pCastItem, 1 ) )
return;
}
bool bResult = SetTamer( pMonster, static_cast< StructPlayer* >( m_pOwner ), this );
// 테이밍 성공~
StructPlayer *pOwner = static_cast< StructPlayer * >( m_pOwner );
if( bResult )
{
pMonster->AddHate( pOwner->GetHandle(), 1 );
}
AddSkillResult( &m_vResultList, bResult );
}
// 펫 테이밍
void StructSkill::PET_TAMING( struct StructCreature* pTarget )
{
// 타겟 없으면 KIN
if( !pTarget ) return;
// 플레이어가 몬스터에게만 사용 가능
if( !m_pOwner->IsPlayer() || !pTarget->IsMonster() )
return;
StructPlayer *pPlayer = static_cast< StructPlayer * >( m_pOwner );
StructMonster *pMonster = static_cast< StructMonster * >( pTarget );
// 펫으로 테이밍 가능 환경 몬스터에게만 가능
ItemBase::ItemCode nTameItemCode = pMonster->GetTameItemCode();
if( !pMonster->IsEnvironmentMonster() || ( !pMonster->GetTameCode() && !pMonster->IsMonsterCreatureTame() ) || !nTameItemCode
|| StructItem::GetItemBase( nTameItemCode ).nGroup != ItemBase::GROUP_PET_CAGE )
return;
// 테이밍 가능 우리 보유 체크
StructItem *pItem = pPlayer->FindEmptyPetCage( nTameItemCode );
if( !pItem )
return;
// 테이밍 성공 확률 체크
float fProbability = pMonster->GetTamePercetage() * 100;
bool bResult = false;
if( fProbability >= XRandom( 1, 100 ) )
{
// 테이밍 성공
StructPet *pPet = NULL;
if( pMonster->GetTameCode() )
{
pPet = AllocNewPet( pPlayer, pItem, pMonster->GetTameCode() );
}
if( !pPet )
{
bResult = false;
}
else
{
// 클라한테 추가 펫 정보 송신
pPlayer->AddPet( pPet, false );
// DB에 신규 펫 정보 추가, 펫 우리 아이템 업데이트
pPet->DBQuery( new DB_InsertPet( pPet ) );
pItem->DBQuery( new DB_UpdateItem( pItem ) );
bResult = true;
}
}
else
{
// 테이밍 실패(우리 삭제)
pPlayer->EraseItem( pItem, 1 );
}
pPlayer->Save();
AddSkillResult( &m_vResultList, bResult );
}
void StructSkill::SHOVELING()
{
if( !m_pOwner->IsPet() )
return;
StructPet *pPet = static_cast< StructPet * >( m_pOwner );
static_cast< StructPet * >( m_pOwner )->SetShovelingStatus( StructPet::SHOVELING_STATUS_IDLE );
BroadcastStatusMessage( m_pOwner );
if( !pPet->GetMaster() || !pPet->GetMaster()->IsLogin() || !pPet->GetMaster()->IsInWorld() )
return;
// 성공 여부 확률 체크
int nProbability = XRandom( 1, 100 );
bool bIsRarePet = pPet->IsRare();
if( ( !bIsRarePet && nProbability > GameRule::PET_SHOVELING_SUCCESS_PERCENT_NORMAL ) ||
( bIsRarePet && nProbability > GameRule::PET_SHOVELING_SUCCESS_PERCENT_RARE ) )
{
AddSkillResult( &m_vResultList, false );
return;
}
int nRewardTypeSeed = XRandom( 1, 100 );
GameRule::PET_SHOVELING_REWARD_TYPE nRewardType = GameRule::PET_SHOVELING_REWARD_UNKNOWN;
for( int i = 0 ; i < GameRule::PET_SHOVELING_REWARD_TYPE_COUNT ; ++i )
{
if( nRewardTypeSeed <= GameRule::PET_SHOVELING_REWARD_TYPE_PERCENT[ i ] )
{
nRewardType = static_cast< GameRule::PET_SHOVELING_REWARD_TYPE >( i );
break;
}
}
if( nRewardType == GameRule::PET_SHOVELING_REWARD_UNKNOWN )
{
// 보상 확률 총 합이 100보다 적음(아무 보상도 선택되지 않았음)
assert( 0 );
AddSkillResult( &m_vResultList, false );
return;
}
// SkillResult 송신용 임시 변수
bool bSuccess = false;
int nResultType = NULL;
AR_HANDLE hTarget = NULL;
switch( nRewardType )
{
case GameRule::PET_SHOVELING_REWARD_ITEM:
{
nResultType = SkillResult::CREATE_ITEM;
int nLocationID = GameContent::GetLocationId( pPet->GetX(), pPet->GetY() );
if( !nLocationID )
break;
ItemBase::ItemCode nCode = WorldLocationManager::Instance().GetShovelableItem( nLocationID );
__int64 nCount = 1;
while( nCode < 0 )
GameContent::SelectItemIDFromDropGroup( nCode, nCode, nCount );
if( !nCode )
break;
StructItem *pNewItem = StructItem::AllocItem( 0, nCode, nCount, ItemInstance::BY_SHOVELING );
if( !pNewItem )
break;
if( !pPet->GetMaster()->PushItem( pNewItem, pNewItem->GetCount() ) )
{
StructItem::PendFreeItem( pNewItem );
break;
}
bSuccess = true;
hTarget = pNewItem->GetHandle();
}
break;
case GameRule::PET_SHOVELING_REWARD_STATE:
{
nResultType = SkillResult::ADD_STATE;
StructState::StateCode nStateCode = static_cast< StructState::StateCode >( GameRule::GetPetShovelingRewardStateCode() );
AR_TIME t = GetArTime();
// 보상 지속효과 부여에 실패 가능(타 지속효과와 중첩 그룹 문제 혹은 현재 더 강한 동종의 지속효과 보유 중)
bSuccess = pPet->GetMaster()->AddState( nStateCode, NULL, 1, t, t + GameRule::PET_SHOVELING_REWARD_STATE_DURATION ) != RESULT_SUCCESS;
hTarget = pPet->GetMaster()->GetHandle();
}
break;
case GameRule::PET_SHOVELING_REWARD_MONSTER:
{
nResultType = SkillResult::RESPAWN_MONSTER;
int nLocationID = GameContent::GetLocationId( pPet->GetX(), pPet->GetY() );
if( !nLocationID )
break;
int nMonsterID = WorldLocationManager::Instance().GetShovelableMonster( nLocationID );
// 레어 펫일 경우 테이밍 가능한 야생 펫 소환 확률 체크
if( pPet->IsRare() && XRandom( 1, 100 ) <= GameRule::PET_SHOVELING_REWARD_MONSTER_PET_PERCENT )
{
nMonsterID = 50001; // 테이밍 가능한 야생 펫의 결정 방식이 결정된 후에 변경할 것
}
if( !nMonsterID )
break;
// 몬스터 생성
StructMonster *pMonster = StructMonster::AllocMonster( nMonsterID );
if( !pMonster )
break;
pMonster->SetGenerateCode( StructMonster::BY_SHOVELING );
pMonster->SetCurrentXY( pPet->GetX(), pPet->GetY() );
AddMonsterToWorld( pMonster );
bSuccess = true;
hTarget = pMonster->GetHandle();
}
break;
default:
assert( 0 );
break;
}
AddSkillResult( &m_vResultList, bSuccess, nResultType, hTarget );
}
void StructSkill::SINGLE_RUSH_REGION( ArPosition RushPos )
{
if( GetSkillBase()->GetEffectType() == SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_RUSH_KNOCKBACK_REGION )
{
if( m_nCurrentFire == 0 )
{
SkillResult result;
result.rush.type = SkillResult::RUSH;
result.rush.hTarget = m_pOwner->GetHandle();
ArPosition RushPos;
float face;
float fDistance;
m_nCurrentFire = 1;
m_nTotalFire = 2;
if ( !AFFECT_RUSH( NULL, &fDistance, &RushPos, &face, GameRule::DEFAULT_RUSH_SPEED ) )
{
result.rush.bResult = false;
m_vResultList.push_back( result );
m_nCurrentFire = 2;
return;
}
result.rush.bResult = true;
result.rush.x = RushPos.x;
result.rush.y = RushPos.y;
result.rush.speed = GameRule::DEFAULT_RUSH_SPEED;
m_vResultList.push_back( result );
return;
}
else
{
ArcadiaServer::Instance().MoveObject( m_pOwner, m_RushPos, m_fRushFace );
if( GetVar( 3 ) != 0 )
{
AR_TIME t = GetArTime();
AR_TIME end_time = 0;
int nEndTime = GetStateSecond( GetRequestedSkillLevel(), GetEnhance() );
if (nEndTime < 0) end_time = t + 6000;
else end_time = t + nEndTime;
m_pOwner->AddState( (StructState::StateCode) (int) GetVar(3),
m_pOwner->GetHandle(), 1, t, end_time, false );
}
ArcadiaServer::Instance().MoveObject( m_pOwner, m_RushPos, m_fRushFace );
++m_nCurrentFire;
}
}
int elemental_type = GetSkillBase()->GetElementalType();
int nDamage = m_pOwner->GetMagicPoint( (Elemental::Type)elemental_type, GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
float fKnockbackRange = 0.0f;
AR_TIME nKnockbackTime = 0;
switch( GetSkillBase()->GetEffectType() )
{
case SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_RUSH_KNOCKBACK_REGION:
nDamage *= GetVar(0) + GetVar(1) * GetRequestedSkillLevel();
fKnockbackRange = GetVar( 9 ) * GameRule::DEFAULT_UNIT_SIZE;
nKnockbackTime = GetVar( 10 ) * GetRequestedSkillLevel() * 100;
break;
default:
assert( 0 );
return;
}
std::vector< AR_HANDLE > vResult;
float fEffectLength = 4.2f * GameRule::DEFAULT_UNIT_SIZE;
ArcadiaServer::Instance().EnumMovableObject( m_RushPos, m_targetLayer, fEffectLength, &vResult );
m_nTargetCount = 0;
for (std::vector< AR_HANDLE >::iterator it = vResult.begin(); it != vResult.end(); ++it)
{
StructCreature::iterator itCreature = StructCreature::get( (*it) );
StructCreature* pDealTarget = (*itCreature);
if( !pDealTarget || pDealTarget->IsPet() || pDealTarget->IsDead() )
continue;
if( m_pOwner == pDealTarget )
continue;
if( !m_pOwner->IsEnemy( pDealTarget, true ) )
continue;
StructCreature::_DAMAGE_INFO Damage = pDealTarget->DealMagicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pDealTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
if( !Damage.bBlock && !Damage.bMiss && !Damage.bPerfectBlock )
{
if( GetSkillBase()->GetEffectType() == SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_RUSH_KNOCKBACK_REGION )
{
AFFECT_KNOCK_BACK( pDealTarget, fEffectLength, GameRule::DEFAULT_KNOCK_BACK_SPEED );
AddSkillDamageWithKnockBackResult( &m_vResultList, SkillResult::DAMAGE_WITH_KNOCK_BACK, elemental_type, Damage, pDealTarget->GetHandle(), pDealTarget->GetPos().x, pDealTarget->GetPos().y, nKnockbackTime );
}
}
else
{
AddSkillDamageResult( &m_vResultList, SkillResult::DAMAGE, elemental_type, Damage, pDealTarget->GetHandle() );
}
}
}
ArPosition GetMovableKnockBackPosition( ArPosition & OriginalPos, ArPosition & TargetPos )
{
if( GameContent::IsBlocked( TargetPos.x, TargetPos.y ) )
return OriginalPos;
return TargetPos;
}
bool StructSkill::AFFECT_RUSH_OLD( struct StructCreature * pTarget, float * pfRushDistance, ArPosition * pRushPos, float * pface )
{
AR_TIME t = GetArTime();
ArPosition original_pos = m_pOwner->GetCurrentPosition( t );
int nDelay = original_pos.GetDistance( pTarget->GetCurrentPosition( t ) ) / ( (float) GameRule::DEFAULT_RUSH_SPEED / SPEED_UNIT );
ArPosition target_pos = pTarget->GetCurrentPosition( t + nDelay );
// TODO : 벡터 클래스 따로 하나 만든 다음, 벡터 연산은 그걸 통해서 하도록 한다.
// 노말벡터 구한 다음 거리만큼 곱한다.
float x, y, m, face;
x = target_pos.x - original_pos.x;
y = target_pos.y - original_pos.y;
face = atan2( y, x );
m = sqrt( x * x + y * y );
if( m <= ( m_pOwner->GetUnitSize() + pTarget->GetUnitSize() ) / 2 )
{
return false;
}
x /= m;
y /= m;
m -= ( m_pOwner->GetUnitSize() + pTarget->GetUnitSize() ) / 2;
ArPosition pos;
pos.x = original_pos.x + x * m;
pos.y = original_pos.y + y * m;
if( GameContent::CollisionToLine( original_pos.x, original_pos.y, pos.x, pos.y ) )
return false;
m_RushPos = pos;
m_fRushFace = face;
*pfRushDistance = m;
*pRushPos = pos;
*pface = face;
m_nFireTime += m / ( (float) GameRule::DEFAULT_RUSH_SPEED / SPEED_UNIT ) + 20;
return true;
}
int StructSkill::AFFECT_KNOCK_BACK( struct StructCreature * pTarget, float fRange, AR_TIME knock_back_time )
{
ArPosition caster_pos = m_pOwner->GetCurrentPosition( GetArTime() );
ArPosition OriginalPos = pTarget->GetCurrentPosition( GetArTime() );
// 노말벡터 구한 다음 거리만큼 곱한다.
float x, y, m;
x = OriginalPos.x - caster_pos.x;
y = OriginalPos.y - caster_pos.y;
m = sqrt( x * x + y * y );
if( m > 0.0f )
{
x /= m;
y /= m;
}
else
{
x = 1.0f;
y = 0.0f;
}
ArPosition pos;
pos.x = OriginalPos.x + x * fRange;
pos.y = OriginalPos.y + y * fRange;
AR_TIME next_movable_time = pTarget->GetMovableTime();
if( pTarget->IsKnockbackable() )
{
next_movable_time = (std::max)( next_movable_time, GetArTime() + knock_back_time );
ArPosition newPos = GetMovableKnockBackPosition( OriginalPos, pos );
ArcadiaServer::Instance().MoveObject( pTarget, newPos, pTarget->GetFace() );
pTarget->SetMovableTime( next_movable_time );
// 타겟이 크리처 탑승 상태의 플레이어 혹은 플레이어를 태운 소환수였으면 낙상 처리
StructPlayer * pTargetPlayer = NULL;
if( pTarget->IsPlayer() )
pTargetPlayer = static_cast< StructPlayer * >( pTarget );
else if( pTarget->IsSummon() )
{
pTargetPlayer = static_cast< StructSummon * >( pTarget )->GetMaster();
// 탑승 상태이긴 했는데 얻어맞은 소환수가 아닌 다른 놈을 탑승 중이었으면 낙상 처리 안 함
if( pTargetPlayer && pTargetPlayer->GetRideObject() != pTarget )
pTargetPlayer = NULL;
}
if( pTargetPlayer )
{
if( pTargetPlayer->IsRiding() || pTargetPlayer->HasRidingState() )
pTargetPlayer->UnMount( StructPlayer::UNMOUNT_FALL, m_pOwner );
else if( pTargetPlayer->IsSitDown() )
{
pTargetPlayer->StandUp();
BroadcastStatusMessage( pTargetPlayer );
AR_TIME t = GetArTime();
pTargetPlayer->AddState( StructState::CARELESSNESS, 0, 1, t, t + 300 );
}
}
}
return next_movable_time;
}
void StructSkill::TOGGLE_AURA( struct StructCreature* pTarget )
{
m_pOwner->ToggleAura( this );
// 캐스터에게 보내는 SkillResult는 맨 아래에서 Add하고 함수를 빠져 나가고,
// 타겟 조사 루프에서는 자신을 제외한 타겟들만 추가시킴
switch( GetSkillBase()->GetSkillTargetType() )
{
case SkillBase::TARGET_PARTY:
{
if( !m_pOwner->IsPlayer() ) break;
StructPlayer* pPlayer = static_cast< StructPlayer* >( m_pOwner );
// 파티 없으면 KIN
if( pPlayer->GetPartyID() == 0 ) break;
struct myPartyFunctor : public PartyManager::PartyFunctor
{
myPartyFunctor( StructSkill *_pSkill, StructCreature *_pOwner, std::vector< SkillResult > *_pvResultList )
: pSkill( _pSkill ), pOwner( _pOwner ), pvResultList( _pvResultList ), t( GetArTime() )
{
if( pOwner )
{
posCaster.x = pOwner->GetX();
posCaster.y = pOwner->GetY();
}
}
virtual bool operator()( AR_HANDLE handle )
{
StructCreature *pPartyTarget = static_cast< StructCreature * >(StructCreature::raw_get( handle ));
if( !pSkill || !pPartyTarget || !pPartyTarget->IsCreature() )
return false;
// 시전자는 아래에서 처리, 여기선 제외. 우선 성공으로 가정
if( pPartyTarget == pOwner )
return true;
// 오오라 유효 범위 밖이면 버리기
if( posCaster.GetDistance( pPartyTarget->GetCurrentPosition( t ) ) > pSkill->GetValidRange() )
return false;
AddSkillResult( pvResultList, true, 0, handle );
return true;
}
private:
StructSkill *pSkill;
StructCreature *pOwner;
ArPosition posCaster;
std::vector< SkillResult > *pvResultList;
AR_TIME t;
} _fo( this, m_pOwner, &m_vResultList );
PartyManager::GetInstance().DoEachMember( pPlayer->GetPartyID(), _fo );
break;
}
}
// 자기 자신에 대한 SkillResult 처리
AddSkillResult( &m_vResultList, true, 0, m_pOwner->GetHandle() );
return;
}
void StructSkill::MAKE_AREA_EFFECT_PROP( struct StructCreature* pTarget, bool bIsTrap )
{
// 어디다 터트릴 것인지 위치를 얻는다.
ArPosition pos = m_targetPos;
if( pTarget )
{
pos = pTarget->GetCurrentPosition( GetArTime() + PREDICTION_AIMING_TIME );
}
if( pos.x < 0 || pos.y < 0 || pos.x > g_nMapWidth || pos.y > g_nMapHeight ) return;
// _cprint( "SKILL PROP ADDED\n" );
// 트랩은 1인당 동시 1개만 설치 가능(새로 설치하려면 기존꺼 제거)
if( bIsTrap && m_pOwner->GetTrapHandle() )
{
StructSkillProp *pTrap = static_cast< StructSkillProp * >( GameObject::raw_get( m_pOwner->GetTrapHandle() ) );
if( pTrap )
{
pTrap->PendRemove();
}
}
int nMagicPoint = m_pOwner->GetMagicPoint( (Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
float fHateRatio = m_pOwner->GetHateRatio();
// 스킬 프랍 생성
ArObject *pPtr = StructSkillProp::Create( m_pOwner->GetHandle() , this, nMagicPoint, fHateRatio );
if( bIsTrap && pPtr )
{
m_pOwner->SetTrapHandle( pPtr->GetHandle() );
}
pPtr->SetCurrentXY( pos.x, pos.y );
pPtr->SetCurrentLayer( m_pOwner->GetLayer() );
// 추가후 스케쥴러에 등록
ArcadiaServer::Instance().AddObject( pPtr );
ArcadiaServer::Instance().SetObjectPriority( pPtr, ArSchedulerObject::UPDATE_PRIORITY_HIGH );
return;
}
bool StructSkill::PHYSICAL_DAMAGE_RUSH( struct StructCreature* pTarget, int *pnAdditionalDamage )
{
SkillResult result;
result.rush.type = SkillResult::RUSH;
result.rush.hTarget = m_pOwner->GetHandle();
ArPosition RushPos;
float face;
float fDistance;
*pnAdditionalDamage = 0;
if( !AFFECT_RUSH_OLD( pTarget, &fDistance, &RushPos, &face ) )
{
result.rush.bResult = false;
m_vResultList.push_back( result );
return false;
}
*pnAdditionalDamage = ( fDistance / GameRule::DEFAULT_UNIT_SIZE ) * GetVar(2);
result.rush.bResult = true;
result.rush.x = RushPos.x;
result.rush.y = RushPos.y;
result.rush.speed = GameRule::DEFAULT_RUSH_SPEED;
m_vResultList.push_back( result );
return true;
}
void StructSkill::SKILL_RESURRECTION( struct StructCreature* pTarget )
{
if( !pTarget || !pTarget->IsDead() ) return;
// 내구도가 다한 크리처는 부활이 되지 않아야 한다.
if( pTarget->IsSummon() )
{
StructItem *pCard = static_cast< StructSummon * >( pTarget )->GetParentCard();
if( pCard )
{
if( pCard->GetItemEnhance() && !pCard->GetCurrentEtherealDurability() )
return;
}
}
int nIncHP = pTarget->GetMaxHP() * GetVar(0) * GetRequestedSkillLevel();
int nIncMP = pTarget->GetMaxMP() * GetVar(1) * GetRequestedSkillLevel();
__int64 nRecoveryEXP = 0;
if( pTarget->IsPlayer() || pTarget->IsSummon() )
{
nRecoveryEXP = pTarget->GetLastDecreasedEXP() * ( GetVar(2) * GetRequestedSkillLevel() + GetVar(3) * GetEnhance() );
pTarget->SetEXP( pTarget->GetEXP() + nRecoveryEXP );
}
pTarget->AddHP( nIncHP );
pTarget->AddMP( nIncMP );
//AziaMafia KeepBuff & fix
pTarget->ClearRemovedStateByDead();
if( pTarget->IsPlayer() )
{
StructPlayer *pPlayer = static_cast< StructPlayer * >( pTarget );
pPlayer->Save( true );
if( pPlayer->IsCompeteDead() )
pPlayer->SetCompeteDead( false );
StructPlayer *pCaster = ( m_pOwner->IsPlayer() ) ? static_cast< StructPlayer * >( m_pOwner ) : NULL;
LOG::Log11N4S( LM_CHARACTER_RESURRECTION, pPlayer->GetAccountID(), pPlayer->GetSID(),
CRT_SKILL, ( pCaster ) ? pCaster->GetAccountID() : 0, ( pCaster ) ? pCaster->GetPlayerUID() : 0, 0, pPlayer->GetX(), pPlayer->GetY(), pPlayer->GetLayer(), nRecoveryEXP, pPlayer->GetEXP(),
pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, ( pCaster ) ? pCaster->GetAccountName() : "", LOG::STR_NTS, ( pCaster ) ? pCaster->GetName() : "" , LOG::STR_NTS );
}
else if( pTarget->IsSummon() )
{
StructSummon * pSummon = static_cast< StructSummon * >( pTarget );
pSummon->DBQuery( new DB_UpdateSummon( pSummon ) );
StructPlayer *pPlayer = static_cast< StructPlayer * >(pSummon->GetMaster());
LOG::Log11N4S( LM_SUMMON_RESURRECTION, pPlayer->GetAccountID(), pPlayer->GetSID(), pSummon->GetSID(), 0, 0, 0, 0, 0, (nRecoveryEXP > 0), nRecoveryEXP, pSummon->GetEXP(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", LOG::STR_NTS, "", LOG::STR_NTS );
}
SkillResult result;
result.rebirth.type = SkillResult::REBIRTH;
result.rebirth.nIncHP = nIncHP;
result.rebirth.nIncMP = nIncMP;
result.rebirth.nRecoveryEXP = nRecoveryEXP;
result.rebirth.target_hp = pTarget->GetHP();
result.rebirth.target_mp = pTarget->GetMP();
result.rebirth.hTarget = pTarget->GetHandle();
m_vResultList.push_back( result );
}
void StructSkill::SKILL_RESURRECTION_WITH_RECOVER( struct StructCreature* pTarget )
{
if( !pTarget || !pTarget->IsDead() ) return;
// 내구도가 다한 크리처는 부활이 되지 않아야 한다.
if( pTarget->IsSummon() )
{
StructItem *pCard = static_cast< StructSummon * >( pTarget )->GetParentCard();
if( pCard )
{
if( pCard->GetItemEnhance() && !pCard->GetCurrentEtherealDurability() )
return;
}
}
int nIncHP = pTarget->GetMaxHP() * ( GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(9) * GetEnhance() );
int nIncMP = pTarget->GetMaxMP() * ( GetVar(2) + GetVar(3) * GetRequestedSkillLevel() + GetVar(10) * GetEnhance() );
__int64 nRecoveryEXP = 0;
if( pTarget->IsPlayer() || pTarget->IsSummon() )
{
nRecoveryEXP = pTarget->GetLastDecreasedEXP() * ( GetVar(4) + GetVar(5) * GetRequestedSkillLevel() + GetVar(11) * GetEnhance() );
pTarget->SetEXP( pTarget->GetEXP() + nRecoveryEXP );
}
pTarget->AddHP( nIncHP );
pTarget->AddMP( nIncMP );
//AziaMafia KeepBuff & fix
pTarget->ClearRemovedStateByDead();
if( pTarget->IsPlayer() )
{
StructPlayer *pPlayer = static_cast< StructPlayer * >( pTarget );
pPlayer->Save( true );
if( pPlayer->IsCompeteDead() )
pPlayer->SetCompeteDead( false );
StructPlayer *pCaster = ( m_pOwner->IsPlayer() ) ? static_cast< StructPlayer * >( m_pOwner ) : NULL;
LOG::Log11N4S( LM_CHARACTER_RESURRECTION, pPlayer->GetAccountID(), pPlayer->GetSID(),
CRT_SKILL_WITH_RECOVER, ( pCaster ) ? pCaster->GetAccountID() : 0, ( pCaster ) ? pCaster->GetPlayerUID() : 0, 0, pPlayer->GetX(), pPlayer->GetY(), pPlayer->GetLayer(), nRecoveryEXP, pPlayer->GetEXP(),
pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, ( pCaster ) ? pCaster->GetAccountName() : "", LOG::STR_NTS, ( pCaster ) ? pCaster->GetName() : "" , LOG::STR_NTS );
}
else if( pTarget->IsSummon() )
{
StructSummon * pSummon = static_cast< StructSummon * >( pTarget );
pSummon->DBQuery( new DB_UpdateSummon( pSummon ) );
StructPlayer *pPlayer = static_cast< StructPlayer * >(pSummon->GetMaster());
LOG::Log11N4S( LM_SUMMON_RESURRECTION, pPlayer->GetAccountID(), pPlayer->GetSID(), pSummon->GetSID(), 0, 0, 0, 0, 0, (nRecoveryEXP > 0), nRecoveryEXP, pSummon->GetEXP(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", LOG::STR_NTS, "", LOG::STR_NTS );
}
SkillResult result;
result.rebirth.type = SkillResult::REBIRTH;
result.rebirth.nIncHP = nIncHP;
result.rebirth.nIncMP = nIncMP;
result.rebirth.nRecoveryEXP = nRecoveryEXP;
result.rebirth.target_hp = pTarget->GetHP();
result.rebirth.target_mp = pTarget->GetMP();
result.rebirth.hTarget = pTarget->GetHandle();
m_vResultList.push_back( result );
}
void StructSkill::SKILL_LOTTO()
{
StructPlayer *pPlayer = NULL;
if( m_pOwner->IsPlayer() )
pPlayer = static_cast< StructPlayer * >(m_pOwner);
else if( m_pOwner->IsSummon() )
pPlayer = static_cast< StructSummon * >(m_pOwner)->GetMaster();
// 한 번 더 소지금 체크
StructGold nSkillCost = GetVar( 0 ) + GetVar( 1 ) * GetRequestedSkillLevel();
if( !pPlayer || pPlayer->GetGold() < nSkillCost )
return;
int nCum = 0;
int nKey = XRandom( 1, 10000 );
float rate( -1 );
for( int i=0; i<5; ++i )
{
nCum += ( GetVar( i*3+2 ) + GetVar( i*3+3 ) * GetRequestedSkillLevel() ) * 100;
if( nKey > nCum ) continue;
rate += GetVar( i*3+4 );
break;
}
StructGold nGold( rate * nSkillCost.GetRawData() );
StructGold nPrevGold = pPlayer->GetGold();
if( pPlayer->GetGold() + nGold > GameRule::MAX_GOLD_FOR_INVENTORY )
pPlayer->ChangeGold( GameRule::MAX_GOLD_FOR_INVENTORY );
else if( pPlayer->GetGold() + nGold < 0 )
pPlayer->ChangeGold( StructGold( 0 ) );
else
pPlayer->ChangeGold( pPlayer->GetGold() + nGold );
LOG::Log11N4S( LM_SKILL_LOTTO, pPlayer->GetAccountID(), pPlayer->GetPlayerUID(), ( m_pOwner->IsPlayer() ) ? 1 : 0, m_pOwner->GetSID(), GetSkillId(), GetRequestedSkillLevel(), GetEnhance(),
nPrevGold.GetRawData(), nSkillCost.GetRawData(), ( nGold + nSkillCost ).GetRawData(), pPlayer->GetGold().GetRawData(),
pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, m_pOwner->GetName(), LOG::STR_NTS, "", 0 );
}
void StructSkill::SKILL_ADD_REGION_HP_MP( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int nIncHP = 0;
int nIncMP = 0;
int nMagicPoint = m_pOwner->GetMagicPoint((Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
float fEffectLength = GetVar( 10 ) * GameRule::DEFAULT_UNIT_SIZE;
int nTargetType = GetVar( 11 );
std::vector< AR_HANDLE > vResult;
m_fRange = fEffectLength;
m_nTargetCount = 0;
AR_TIME t = GetArTime();
ArcadiaServer::Instance().EnumMovableObject( pTarget->GetCurrentPosition( t ), pTarget->GetLayer(), fEffectLength, &vResult );
for( std::vector< AR_HANDLE >::iterator it = vResult.begin(); it != vResult.end(); ++it )
{
StructCreature::iterator itCreature = StructCreature::get( (*it) );
StructCreature * pCreature = (*itCreature);
if( !pCreature || pCreature->IsPet() || pCreature->IsDead() )
continue;
if( nTargetType == 1 )
{
if( m_pOwner->IsEnemy( pCreature, true ) )
continue;
}
else if( nTargetType == 2 )
{
if( !m_pOwner->IsAlly( pCreature ) )
continue;
}
else if( nTargetType == 3 )
{
if( !m_pOwner->IsEnemy( pCreature, true ) )
continue;
}
switch( static_cast< int >( GetVar(12) ) )
{
case 1:
if( !pCreature->IsPlayer() )
continue;
break;
case 2:
if( !pCreature->IsSummon() )
continue;
break;
case 3:
if( !pCreature->IsPlayer() && !pCreature->IsSummon() )
continue;
break;
}
nIncHP = nMagicPoint * ( GetVar(0) + GetVar(1) * GetRequestedSkillLevel() )
+ GetVar(2) * GetRequestedSkillLevel()
+ pCreature->GetMaxHP() * GetVar(3) * GetRequestedSkillLevel()
+ GetVar(4) * GetEnhance();
nIncMP = nMagicPoint * ( GetVar(5) + GetVar(6) * GetRequestedSkillLevel() )
+ GetVar(7) * GetRequestedSkillLevel()
+ pCreature->GetMaxMP() * GetVar(8) * GetRequestedSkillLevel()
+ GetVar(9) * GetEnhance();
nIncHP = pCreature->Heal( nIncHP );
nIncMP = pCreature->MPHeal( nIncMP );
SkillResult skill_result;
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 = nIncHP;
skill_result.add_hp_mp_sp.nIncMP = nIncMP;
skill_result.add_hp_mp_sp.nIncSP = 0;
m_vResultList.push_back( skill_result );
++m_nTargetCount;
}
}
void StructSkill::SKILL_ADD_REGION_HP( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int nIncHP = 0;
int nMagicPoint = m_pOwner->GetMagicPoint((Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
float fEffectLength = GetVar( 10 ) * GameRule::DEFAULT_UNIT_SIZE;
int nTargetType = GetVar( 11 );
std::vector< AR_HANDLE > vResult;
m_fRange = fEffectLength;
AR_TIME t = GetArTime();
ArcadiaServer::Instance().EnumMovableObject( pTarget->GetCurrentPosition( t ), pTarget->GetLayer(), fEffectLength, &vResult );
for( std::vector< AR_HANDLE >::iterator it = vResult.begin(); it != vResult.end(); ++it )
{
StructCreature::iterator itCreature = StructCreature::get( (*it) );
StructCreature * pCreature = (*itCreature);
if( !pCreature || pCreature->IsPet() || pCreature->IsDead() )
continue;
if( nTargetType == 1 )
{
if( m_pOwner->IsEnemy( pCreature, true ) )
continue;
}
else if( nTargetType == 2 )
{
if( !m_pOwner->IsAlly( pCreature ) )
continue;
}
else if( nTargetType == 3 )
{
if( !m_pOwner->IsEnemy( pCreature, true ) )
continue;
}
nIncHP = nMagicPoint * ( GetVar(0) + GetVar(1) * GetRequestedSkillLevel() )
+ GetVar(2) + GetVar(3) * GetRequestedSkillLevel()
+ pCreature->GetMaxHP() * ( GetVar(4) + GetVar(5) * GetRequestedSkillLevel() + GetVar(7) * GetEnhance() )
+ GetVar(6) * GetEnhance();
nIncHP = pCreature->Heal( nIncHP );
SkillResult skill_result;
skill_result.add_hp.type = SkillResult::ADD_HP;
skill_result.add_hp.hTarget = pCreature->GetHandle();
skill_result.add_hp.target_hp = pCreature->GetHP();
skill_result.add_hp_mp_sp.nIncHP = nIncHP;
m_vResultList.push_back( skill_result );
}
}
void StructSkill::SKILL_ADD_REGION_MP( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int nIncMP = 0;
int nMagicPoint = m_pOwner->GetMagicPoint((Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
float fEffectLength = GetVar( 10 ) * GameRule::DEFAULT_UNIT_SIZE;
int nTargetType = GetVar( 11 );
std::vector< AR_HANDLE > vResult;
m_fRange = fEffectLength;
AR_TIME t = GetArTime();
ArcadiaServer::Instance().EnumMovableObject( pTarget->GetCurrentPosition( t ), pTarget->GetLayer(), fEffectLength, &vResult );
m_nTargetCount = 0;
for( std::vector< AR_HANDLE >::iterator it = vResult.begin(); it != vResult.end(); ++it )
{
StructCreature::iterator itCreature = StructCreature::get( (*it) );
StructCreature * pCreature = (*itCreature);
if( !pCreature || pCreature->IsPet() || pCreature->IsDead() )
continue;
if( nTargetType == 1 )
{
if( m_pOwner->IsEnemy( pCreature, true ) )
continue;
}
else if( nTargetType == 2 )
{
if( !m_pOwner->IsAlly( pCreature ) )
continue;
}
else if( nTargetType == 3 )
{
if( !m_pOwner->IsEnemy( pCreature, true ) )
continue;
}
nIncMP = nMagicPoint * ( GetVar(0) + GetVar(1) * GetRequestedSkillLevel() )
+ GetVar(2) + GetVar(3) * GetRequestedSkillLevel()
+ pCreature->GetMaxMP() * ( GetVar(4) + GetVar(5) * GetRequestedSkillLevel() + GetVar(7) * GetEnhance() )
+ GetVar(6) * GetEnhance();
nIncMP = pCreature->MPHeal( nIncMP );
SkillResult skill_result;
skill_result.add_hp.type = SkillResult::ADD_MP;
skill_result.add_hp.hTarget = pCreature->GetHandle();
skill_result.add_hp.target_hp = pCreature->GetMP();
skill_result.add_hp_mp_sp.nIncHP = nIncMP;
m_vResultList.push_back( skill_result );
++m_nTargetCount;
}
}
void StructSkill::SKILL_ADD_HP_MP( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int nDecHP = 0;
int nDecMP = 0;
if( GetSkillBase()->GetEffectType() == SkillBase::EF_ADD_HP_MP_BY_SUMMON_DEAD )
{
if( !m_pOwner->IsPlayer() ) return;
StructSummon * pSummon = NULL;
pSummon = static_cast< StructPlayer * >( m_pOwner )->GetMainSummon();
if( !pSummon || pSummon->IsDead() || pSummon == pTarget )
{
AddSkillResult( &m_vResultList, false, SkillResult::SUMMON_DEAD );
return;
}
nDecHP = pSummon->GetHP();
pSummon->damage( m_pOwner, nDecHP, false );
ArcadiaServer::Instance().SetObjectPriority( pSummon, ArSchedulerObject::UPDATE_PRIORITY_NORMAL );
StructCreature::_DAMAGE_INFO Damage;
Damage.nDamage = nDecHP;
Damage.target_hp = pSummon->GetHP();
AddSkillDamageResult( &m_vResultList, SkillResult::DAMAGE, Elemental::TYPE_NONE, Damage, pSummon->GetHandle() );
}
else if( GetSkillBase()->GetEffectType() == SkillBase::EF_ADD_HP_MP_BY_SUMMON_DAMAGE )
{
if( !m_pOwner->IsPlayer() ) return;
StructSummon * pSummon = NULL;
pSummon = static_cast< StructPlayer * >( m_pOwner )->GetMainSummon();
if( !pSummon || pSummon->IsDead() )
{
AddSkillResult( &m_vResultList, false, SkillResult::SUMMON_DEAD );
return;
}
nDecHP = pSummon->GetMaxHP() * GetVar(10);
pSummon->AddHP( 0 - nDecHP );
SkillResult skill_result;
skill_result.add_hp.type = SkillResult::ADD_HP;
skill_result.add_hp.hTarget = pSummon->GetHandle();
skill_result.add_hp.target_hp = pSummon->GetMP();
skill_result.add_hp.nIncHP = 0 - nDecHP;
m_vResultList.push_back( skill_result );
}
else if( GetSkillBase()->GetEffectType() == SkillBase::EF_ADD_HP_MP_BY_STEAL_SUMMON_HP_MP )
{
if( !m_pOwner->IsPlayer() ) return;
StructSummon * pSummon = NULL;
nDecHP = GetVar( 0 ) + GetVar( 1 ) * GetRequestedSkillLevel() + GetVar( 4 ) * GetEnhance();
nDecMP = GetVar( 2 ) + GetVar( 3 ) * GetRequestedSkillLevel() + GetVar( 5 ) * GetEnhance();
pSummon = static_cast< StructPlayer * >( m_pOwner )->GetMainSummon();
if( !pSummon || pSummon->IsDead() )
{
AddSkillResult( &m_vResultList, false, SkillResult::SUMMON_DEAD );
return;
}
pSummon->AddHP( 0 - nDecHP );
pSummon->AddMP( 0 - nDecMP );
SkillResult skill_result;
skill_result.add_hp_mp_sp.type = SkillResult::ADD_HP_MP_SP;
skill_result.add_hp_mp_sp.hTarget = pSummon->GetHandle();
skill_result.add_hp_mp_sp.target_hp = pSummon->GetHP();
skill_result.add_hp_mp_sp.target_mp = pSummon->GetMP();
skill_result.add_hp_mp_sp.nIncHP = 0 - nDecHP;
skill_result.add_hp_mp_sp.nIncMP = 0 - nDecMP;
skill_result.add_hp_mp_sp.nIncSP = 0;
m_vResultList.push_back( skill_result );
}
int nIncHP = 0;
int nIncMP = 0;
if( GetSkillBase()->GetEffectType() == SkillBase::EF_ADD_HP_MP_BY_STEAL_SUMMON_HP_MP )
{
nIncHP = nDecHP * GetVar( 6 ) + GetVar( 7 ) * GetEnhance();
nIncMP = nDecMP * GetVar( 6 ) + GetVar( 7 ) * GetEnhance();
}
else if( GetSkillBase()->GetEffectType() == SkillBase::EF_ADD_HP_MP_WITH_LIMIT_PERCENT )
{
int nHealLimitHP = ( GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(2) * GetEnhance() ) * pTarget->GetMaxHP();
int nHealLimitMP = ( GetVar(3) + GetVar(4) * GetRequestedSkillLevel() + GetVar(5) * GetEnhance() ) * pTarget->GetMaxMP();
if( nHealLimitHP > pTarget->GetHP() )
nIncHP = nHealLimitHP - pTarget->GetHP();
if( nHealLimitMP > pTarget->GetMP() )
nIncMP = nHealLimitMP - pTarget->GetMP();
}
else
{
nIncHP = m_pOwner->GetMagicPoint((Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() ) * ( GetVar(0) + GetVar(1) * GetRequestedSkillLevel() )
+ GetVar(2) * GetRequestedSkillLevel()
+ pTarget->GetMaxHP() * GetVar(3) * GetRequestedSkillLevel()
+ GetVar(4) * GetEnhance();
nIncMP = m_pOwner->GetMagicPoint((Elemental::Type)GetSkillBase()->GetElementalType(), GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() ) * ( GetVar(5) + GetVar(6) * GetRequestedSkillLevel() )
+ GetVar(7) * GetRequestedSkillLevel()
+ pTarget->GetMaxMP() * GetVar(8) * GetRequestedSkillLevel()
+ GetVar(9) * GetEnhance();
if( GetSkillBase()->GetEffectType() == SkillBase::EF_ADD_HP_MP ||
GetSkillBase()->GetEffectType() == SkillBase::EF_ADD_HP_MP_BY_SUMMON_DEAD )
{
nIncHP += GetVar(10);
nIncMP += GetVar(11);
}
if( GetSkillBase()->GetEffectType() == SkillBase::EF_ADD_HP_MP_BY_SUMMON_DEAD )
{
nIncHP += pTarget->GetMaxHP() * GetVar(12);
nIncMP += pTarget->GetMaxMP() * GetVar(13);
}
}
// 아래로 진입하는 경우는 확인하진 못했지만, 만에 하나라도 의도된 동작은 아니므로 사망 여부 체크하도록 수정
if( !pTarget->IsDead() )
{
nIncHP = pTarget->Heal( nIncHP );
nIncMP = pTarget->MPHeal( nIncMP );
SkillResult skill_result;
skill_result.add_hp_mp_sp.type = SkillResult::ADD_HP_MP_SP;
skill_result.add_hp_mp_sp.hTarget = pTarget->GetHandle();
skill_result.add_hp_mp_sp.target_hp = pTarget->GetHP();
skill_result.add_hp_mp_sp.target_mp = pTarget->GetMP();
skill_result.add_hp_mp_sp.nIncHP = nIncHP;
skill_result.add_hp_mp_sp.nIncMP = nIncMP;
skill_result.add_hp_mp_sp.nIncSP = 0;
m_vResultList.push_back( skill_result );
}
}
void StructSkill::Summon()
{
// 소환은....스킬이 아니다.ㅜ.ㅡ
StructItem *pItem = StructItem::FindItem( m_hTarget );
// 카드가 없거나 카드의 내구도가 다 함
if( !pItem || !pItem->IsSummonCard() || ( pItem->GetItemEnhance() && !pItem->GetCurrentEtherealDurability() ) )
return;
StructSummon *pSummon = pItem->GetSummonStruct();
// 소환수 없으면 KIN
if( !pSummon || pSummon->GetMaster() != m_pOwner )
return;
if( !pSummon->IsInWorld() )
{
static_cast< StructPlayer * >( m_pOwner )->Summon( pSummon, m_targetPos );
}
}
void StructSkill::UnSummonAndAddState()
{
if( !m_pOwner->IsPlayer() )
return;
StructSummon * pMainSummon = static_cast< StructPlayer * >( m_pOwner )->GetMainSummon();
if( !pMainSummon )
return;
int nMainSummonCode = pMainSummon->GetSummonCode();
std::string szMainSummonName;
XStringUtil::Format( szMainSummonName, "%s|%d", pMainSummon->GetName(), pMainSummon->GetEnhance() );
StructSummon * pSubSummon = static_cast< StructPlayer * >( m_pOwner )->GetSubSummon();
if( !GetVar( 6 ) )
{
if( pSubSummon )
{
static_cast< StructPlayer * >( m_pOwner )->PendUnSummon( pSubSummon );
}
static_cast< StructPlayer * >( m_pOwner )->PendUnSummon( pMainSummon );
ArcadiaServer::Instance().SetObjectPriority( m_pOwner, ArSchedulerObject::UPDATE_PRIORITY_HIGHEST );
}
AR_TIME t = GetArTime();
AR_TIME end_time = 0;
int nEndTime = GetStateSecond( GetRequestedSkillLevel(), GetEnhance() );
if( nEndTime < 0 ) end_time = t + 6000;
else end_time = t + nEndTime;
for( int i = 0; i < 6; ++i )
{
if( !GetVar( i ) )
break;
const StateInfo * pStateInfo = GameContent::GetStateInfo( GetVar( i ) );
if( pStateInfo && pStateInfo->effect_type == StructState::EF_CHANGING_FORM )
{
m_pOwner->AddState( (StructState::StateCode) (int) GetVar(i),
m_pOwner->GetHandle(),
GetStateLevel( GetRequestedSkillLevel(), GetEnhance() ),
t, end_time, false, nMainSummonCode, szMainSummonName.c_str() );
}
else
{
m_pOwner->AddState( (StructState::StateCode) (int) GetVar(i),
m_pOwner->GetHandle(),
GetStateLevel( GetRequestedSkillLevel(), GetEnhance() ),
t, end_time, false );
}
}
}
void StructSkill::UnSummon()
{
if( !m_pOwner->IsPlayer() )
return;
// 소환은....스킬이 아니다.ㅜ.ㅡ
StructItem *pItem = StructItem::FindItem( m_hTarget );
// 카드 없음
if( !pItem || !pItem->IsSummonCard() )
return;
StructSummon *pSummon = pItem->GetSummonStruct();
// 소환수 없으면 KIN
if( !pSummon || pSummon->GetMaster() != m_pOwner )
return;
static_cast< StructPlayer * >( m_pOwner )->PendUnSummon( pSummon );
ArcadiaServer::Instance().SetObjectPriority( m_pOwner, ArSchedulerObject::UPDATE_PRIORITY_HIGHEST );
}
bool StructSkill::AFFECT_RUSH( struct StructCreature* pTarget, float* pfRushDistance, ArPosition* pRushPos, float* pface, float fSpeed )
{
AR_TIME t = GetArTime();
ArPosition original_pos = m_pOwner->GetCurrentPosition( t );
float x, y, m, face;
int nDelay;
if (pTarget)
{
nDelay = original_pos.GetDistance( pTarget->GetCurrentPosition( t ) ) / (fSpeed / SPEED_UNIT);
ArPosition target_pos = pTarget->GetCurrentPosition( t + nDelay );
x = target_pos.x - original_pos.x;
y = target_pos.y - original_pos.y;
face = atan2( y, x );
m = sqrt( x * x + y * y );
if (m <= (m_pOwner->GetUnitSize() + pTarget->GetUnitSize()) / 2)
{
return false;
}
x /= m;
y /= m;
m -= (m_pOwner->GetUnitSize() + pTarget->GetUnitSize()) / 2;
}
else
{
int nDelay = original_pos.GetDistance( m_targetPos ) / (fSpeed / SPEED_UNIT);
x = m_targetPos.x - original_pos.x;
y = m_targetPos.y - original_pos.y;
face = atan2( y, x );
m = sqrt( x * x + y * y );
if (m <= (m_pOwner->GetUnitSize() / 2))
{
return false;
}
x /= m;
y /= m;
m -= (m_pOwner->GetUnitSize()) / 2;
}
ArPosition pos;
pos.x = original_pos.x + x * m;
pos.y = original_pos.y + y * m;
if (GameContent::CollisionToLine( original_pos.x, original_pos.y, pos.x, pos.y ))
return false;
m_RushPos = pos;
m_fRushFace = face;
*pfRushDistance = m;
*pRushPos = pos;
*pface = face;
m_nFireTime += m / (fSpeed / SPEED_UNIT);
return true;
}
bool StructSkill::AFFECT_RUSH_OLD( struct StructCreature * pTarget, float * pfRushDistance, ArPosition * pRushPos, float * pface, float fSpeed )
{
AR_TIME t = GetArTime();
ArPosition original_pos = m_pOwner->GetCurrentPosition( t );
int nDelay = original_pos.GetDistance( pTarget->GetCurrentPosition( t ) ) / ( fSpeed / SPEED_UNIT );
ArPosition target_pos = pTarget->GetCurrentPosition( t + nDelay );
// TODO : 벡터 클래스 따로 하나 만든 다음, 벡터 연산은 그걸 통해서 하도록 한다.
// 노말벡터 구한 다음 거리만큼 곱한다.
float x, y, m, face;
x = target_pos.x - original_pos.x;
y = target_pos.y - original_pos.y;
face = atan2( y, x );
m = sqrt( x * x + y * y );
if( m <= ( m_pOwner->GetUnitSize() + pTarget->GetUnitSize() ) / 2 )
{
return false;
}
x /= m;
y /= m;
m -= ( m_pOwner->GetUnitSize() + pTarget->GetUnitSize() ) / 2;
ArPosition pos;
pos.x = original_pos.x + x * m;
pos.y = original_pos.y + y * m;
if( GameContent::CollisionToLine( original_pos.x, original_pos.y, pos.x, pos.y ) )
return false;
m_RushPos = pos;
m_fRushFace = face;
*pfRushDistance = m;
*pRushPos = pos;
*pface = face;
m_nFireTime += m / ( fSpeed / SPEED_UNIT );
return true;
}
void StructSkill::PHYSICAL_SINGLE_DAMAGE( struct StructCreature * pTarget )
{
if( !pTarget ) return;
// 돌진 처리
if( GetSkillBase()->GetEffectType() == SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_RUSH ||
GetSkillBase()->GetEffectType() == SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_RUSH_KNOCKBACK )
{
if( m_nCurrentFire == 0 )
{
m_nCurrentFire = 1;
m_nTotalFire = 2;
if( !RUSH( pTarget, GetVar(5) ) )
{
m_nCurrentFire = 2;
}
return;
}
else
{
ArcadiaServer::Instance().MoveObject( m_pOwner, m_RushPos, m_fRushFace );
++m_nCurrentFire;
}
}
// 데미지 처리
int elemental_type = GetSkillBase()->GetElementalType();
int nDamage = m_pOwner->GetAttackPointRight( (Elemental::Type)elemental_type, GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
switch( GetSkillBase()->GetEffectType() )
{
case SkillBase::EF_PHYSICAL_SINGLE_DAMAGE:
nDamage *= GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(10) * GetEnhance();
nDamage += GetVar(2) + GetVar(3) * GetRequestedSkillLevel() + GetVar(11) * GetEnhance();
break;
case SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_RUSH:
case SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_RUSH_KNOCKBACK:
nDamage *= GetVar(0) + GetVar(1) * GetRequestedSkillLevel();
nDamage += GetVar(2) + GetVar(3) * GetRequestedSkillLevel();
break;
case SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_KNOCKBACK:
nDamage *= GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(4) * GetEnhance();
nDamage += GetVar(2) + GetVar(3) * GetRequestedSkillLevel() + GetVar(5) * GetEnhance();
break;
default:
// 어느 유형인데 이쪽으로 오셨는가...?
assert( 0 );
return;
}
StructCreature::_DAMAGE_INFO Damage = pTarget->DealPhysicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
// 넉백 처리
if( !Damage.bBlock && !Damage.bMiss && !Damage.bPerfectBlock
&& ( GetSkillBase()->GetEffectType() == SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_KNOCKBACK
|| GetSkillBase()->GetEffectType() == SkillBase::EF_PHYSICAL_SINGLE_DAMAGE_RUSH_KNOCKBACK )
&& !( pTarget->IsMonster() && static_cast< StructMonster * >( pTarget )->IsBossMonster() ) )
{
float fRange = GetVar(8) + GetVar(9) * GetRequestedSkillLevel();
fRange *= GameRule::DEFAULT_UNIT_SIZE;
AR_TIME knock_back_time = ( GetVar(10) + GetVar(11) * GetRequestedSkillLevel() ) * 100;
AFFECT_KNOCK_BACK( pTarget, fRange, knock_back_time );
AddSkillDamageWithKnockBackResult( &m_vResultList, SkillResult::DAMAGE_WITH_KNOCK_BACK, elemental_type, Damage, pTarget->GetHandle(), pTarget->GetPos().x, pTarget->GetPos().y, knock_back_time );
}
else
AddSkillDamageResult( &m_vResultList, SkillResult::DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
return;
}
void StructSkill::PHYSICAL_SINGLE_DAMAGE_ABSORB( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int elemental_type = GetSkillBase()->GetElementalType();
int nDamage = m_pOwner->GetAttackPointRight( (Elemental::Type)elemental_type, GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() ); // 기본적으로, 모든 스킬은 오늘손 장비의 영향을 받는다. 이도류 스킬은 제외.
// 데미지
nDamage *= GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(10) * GetEnhance();
nDamage += GetVar(2) + GetVar(3) * GetRequestedSkillLevel() + GetVar(11) * GetEnhance();
StructCreature::_DAMAGE_INFO Damage = pTarget->DealPhysicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
// 흡수
int nAddHP = Damage.nDamage * GetVar(4);
int nAddMP = Damage.nDamage * GetVar(5);
m_pOwner->AddHP( nAddHP );
m_pOwner->AddMP( nAddMP );
SkillResult skill_result;
skill_result.add_hp.type = SkillResult::ADD_HP;
skill_result.add_hp.hTarget = m_pOwner->GetHandle();
skill_result.add_hp.nIncHP = nAddHP;
skill_result.add_hp.target_hp = m_pOwner->GetHP();
m_vResultList.push_back( skill_result );
skill_result.add_hp.type = SkillResult::ADD_MP;
skill_result.add_hp.hTarget = m_pOwner->GetHandle();
skill_result.add_hp.nIncHP = nAddMP;
skill_result.add_hp.target_hp = m_pOwner->GetMP();
m_vResultList.push_back( skill_result );
return;
}
void StructSkill::PHYSICAL_SINGLE_DAMAGE_ADD_ENERGY( struct StructCreature * pTarget )
{
if( !pTarget ) return;
int elemental_type = GetSkillBase()->GetElementalType();
int nDamage = m_pOwner->GetAttackPointRight( (Elemental::Type)elemental_type, GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
// 데미지
nDamage *= GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(10) * GetEnhance();
nDamage += GetVar(2) + GetVar(3) * GetRequestedSkillLevel() + GetVar(11) * GetEnhance();
StructCreature::_DAMAGE_INFO Damage = pTarget->DealPhysicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
int nEnergyCount = GetVar(4) + GetVar(5) * GetRequestedSkillLevel();
for( int i = 0 ; i < nEnergyCount ; ++i )
m_pOwner->AddEnergy();
AddSkillDamageResult( &m_vResultList, SkillResult::DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
return;
}
bool StructSkill::RUSH( struct StructCreature* pTarget, float fSpeed )
{
if( !pTarget ) return false;
SkillResult result;
result.rush.type = SkillResult::RUSH;
result.rush.hTarget = m_pOwner->GetHandle();
ArPosition RushPos;
float face;
float fDistance;
if( !AFFECT_RUSH( pTarget, &fDistance, &RushPos, &face, fSpeed ) )
{
result.rush.bResult = false;
m_vResultList.push_back( result );
return false;
}
result.rush.bResult = true;
result.rush.x = RushPos.x;
result.rush.y = RushPos.y;
result.rush.speed = fSpeed;
m_vResultList.push_back( result );
return true;
}
void StructSkill::PHYSICAL_SINGLE_REGION_DAMAGE( struct StructCreature* pTarget )
{
if( !pTarget ) return;
// 데미지 계산 + 유형별 파라미터 설정
int elemental_type = GetSkillBase()->GetElementalType();
int nDamage = m_pOwner->GetAttackPointRight( (Elemental::Type)elemental_type, GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
// 특수 범위 관련
bool bTargetOrigin = true; // 기본 타겟 중심
int nRegionType = -1; // 기본 원형
float fRegionProperty = 0.0f; // 기본 범위 유형 속성값 없음
// 넉백 관련
float fKnockbackRange = 0.0f; // 넉백 거리
AR_TIME nKnockbackTime = 0; // 넉백에 의한 행동 불가 시간
// 캐스팅 캔슬 관련
int nCastingCancelRate = 0; // 캐스팅 캔슬 확률(100 = 100%)
switch( GetSkillBase()->GetEffectType() )
{
case SkillBase::EF_PHYSICAL_SINGLE_REGION_DAMAGE:
nDamage *= GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(10) * GetEnhance();
nDamage += GetVar(2) + GetVar(3) * GetRequestedSkillLevel() + GetVar(11) * GetEnhance();
break;
case SkillBase::EF_PHYSICAL_SINGLE_REGION_DAMAGE_KNOCKBACK_SELF:
bTargetOrigin = false; // break 쓰면 데미지 증폭/증가 안되요;;
case SkillBase::EF_PHYSICAL_SINGLE_REGION_DAMAGE_KNOCKBACK:
nDamage *= GetVar(0) + GetVar(1) * GetRequestedSkillLevel();
nDamage += GetVar(2) + GetVar(3) * GetRequestedSkillLevel();
// 이 유형들은 특수범위 지정이 가능
nRegionType = static_cast< int >( GetVar(7) );
fRegionProperty = GetVar(8);
// 넉백 속성 설정
fKnockbackRange = ( GetVar(9) + GetVar(10) * GetRequestedSkillLevel() ) * GameRule::DEFAULT_UNIT_SIZE;
nKnockbackTime = ( GetVar(11) + GetVar(12) * GetRequestedSkillLevel() ) * 100;
break;
case SkillBase::EF_PHYSICAL_SINGLE_REGION_DAMAGE_WITH_CAST_CANCEL:
nCastingCancelRate = GetVar(12) + GetVar(13) * GetRequestedSkillLevel() + GetVar(14) * GetEnhance();
break;
case SkillBase::EF_PHYSICAL_SINGLE_REGION_DAMAGE_ADDING_MAGICAL_DAMAGE:
{
nDamage *= GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(10) * GetEnhance();
nDamage += GetVar(2) + GetVar(3) * GetRequestedSkillLevel() + GetVar(11) * GetEnhance();
int nMagicalDamage = m_pOwner->GetMagicPoint( (Elemental::Type)elemental_type, GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
nMagicalDamage *= GetVar(12) + GetVar(13) * GetRequestedSkillLevel() + GetVar(14) * GetEnhance();
nDamage += nMagicalDamage;
nDamage += GetVar(15) + GetVar(16) * GetRequestedSkillLevel() + GetVar(17) * GetEnhance();
break;
}
default:
// 어느 유형인데 이쪽으로 오셨는가...?
assert( 0 );
return;
}
float fEffectLength = GetVar(4) * GameRule::DEFAULT_UNIT_SIZE;
m_fRange = fEffectLength;
std::vector< StructCreature * > vTargetList;
std::vector< StructCreature * >::iterator itTargetList;
AR_TIME t = GetArTime();
nDamage = EnumSkillTargetsAndCalcDamage( m_pOwner->GetCurrentPosition( t ), m_pOwner->GetLayer(), pTarget->GetCurrentPosition( t ), bTargetOrigin, fEffectLength, nRegionType, fRegionProperty, nDamage, true, m_pOwner, GetVar(5), GetVar(6), vTargetList );
m_nTargetCount = static_cast<int>( vTargetList.size() );
// 데미지 적용
for( itTargetList = vTargetList.begin(); itTargetList != vTargetList.end(); ++itTargetList )
{
StructCreature* pDealTarget = (*itTargetList);
int accuary_bonus = GetHitBonus(GetEnhance(), m_pOwner->GetLevel() - pDealTarget->GetLevel());
if (GetSkillId() == 3022 )
accuary_bonus = 10 ;
StructCreature::_DAMAGE_INFO Damage = pDealTarget->DealPhysicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, accuary_bonus , GetCriticalBonus( GetRequestedSkillLevel() ) );
if( !Damage.bBlock && !Damage.bMiss && !Damage.bPerfectBlock )
{
if( ( GetSkillBase()->GetEffectType() == SkillBase::EF_PHYSICAL_SINGLE_REGION_DAMAGE_KNOCKBACK
|| GetSkillBase()->GetEffectType() == SkillBase::EF_PHYSICAL_SINGLE_REGION_DAMAGE_KNOCKBACK_SELF )
&& !( pDealTarget->IsMonster() && static_cast< StructMonster * >( pDealTarget )->IsBossMonster() ) )
{
// 넉백 적용
AFFECT_KNOCK_BACK( pDealTarget, fKnockbackRange, nKnockbackTime );
AddSkillDamageWithKnockBackResult( &m_vResultList, SkillResult::DAMAGE_WITH_KNOCK_BACK, elemental_type, Damage, pDealTarget->GetHandle(), pDealTarget->GetPos().x, pDealTarget->GetPos().y, nKnockbackTime );
}
else if( GetSkillBase()->GetEffectType() == SkillBase::EF_PHYSICAL_SINGLE_REGION_DAMAGE_WITH_CAST_CANCEL )
{
StructSkill *pCancelSkill = pDealTarget->GetCastSkill();
if( pCancelSkill && pCancelSkill->m_Status == STATUS_CAST && XRandom( 0, 99 ) < nCastingCancelRate )
{
pDealTarget->CancelSkill();
}
AddSkillDamageResult( &m_vResultList, SkillResult::DAMAGE, elemental_type, Damage, pDealTarget->GetHandle() );
}
else
{
AddSkillDamageResult( &m_vResultList, SkillResult::DAMAGE, elemental_type, Damage, pDealTarget->GetHandle() );
}
}
else
{
AddSkillDamageResult( &m_vResultList, SkillResult::DAMAGE, elemental_type, Damage, pDealTarget->GetHandle() );
}
}
}
void StructSkill::PHYSICAL_REALTIME_MULTIPLE_DAMAGE( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int nEffectType = GetSkillBase()->GetSkillEffectType();
int elemental_type = GetSkillBase()->GetElementalType();
int nDamage = m_pOwner->GetAttackPointRight( (Elemental::Type)elemental_type, GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
nDamage *= GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + ( ( nEffectType == SkillBase::EF_PHYSICAL_REALTIME_MULTIPLE_DAMAGE ) ? ( GetVar(10) * GetEnhance() ) : 0 );
nDamage += GetVar(2) + GetVar(3) * GetRequestedSkillLevel() + ( ( nEffectType == SkillBase::EF_PHYSICAL_REALTIME_MULTIPLE_DAMAGE ) ? ( GetVar(11) * GetEnhance() ) : 0 );
if( m_nCurrentFire == 0 )
{
m_nTotalFire = GetVar(4) + GetVar(5) * GetRequestedSkillLevel();
m_nCurrentFire = 1;
}
else
{
++m_nCurrentFire;
}
StructCreature::_DAMAGE_INFO Damage = pTarget->DealPhysicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
if( m_nCurrentFire == m_nTotalFire && nEffectType == SkillBase::EF_PHYSICAL_REALTIME_MULTIPLE_DAMAGE_KNOCKBACK
&& !Damage.bBlock && !Damage.bMiss && !Damage.bPerfectBlock
&& !( pTarget->IsMonster() && static_cast< StructMonster * >( pTarget )->IsBossMonster() ) )
{
float fRange = GetVar(8) + GetVar(9) * GetRequestedSkillLevel();
fRange *= GameRule::DEFAULT_UNIT_SIZE;
AR_TIME knock_back_time = ( GetVar(10) + GetVar(11) * GetRequestedSkillLevel() ) * 100;
AFFECT_KNOCK_BACK( pTarget, fRange, knock_back_time );
AddSkillDamageWithKnockBackResult( &m_vResultList, SkillResult::DAMAGE_WITH_KNOCK_BACK, elemental_type, Damage, pTarget->GetHandle(), pTarget->GetPos().x, pTarget->GetPos().y, knock_back_time );
}
else
{
AddSkillDamageResult( &m_vResultList, SkillResult::DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
}
m_nFireTime += GetVar(6) * 100;
return;
}
void StructSkill::PHYSICAL_REALTIME_MULTIPLE_REGION_DAMAGE( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int elemental_type = GetSkillBase()->GetElementalType();
int nDamage = m_pOwner->GetAttackPointRight( (Elemental::Type)elemental_type, GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
nDamage *= GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(10) * GetEnhance();
nDamage += GetVar(2) + GetVar(3) * GetRequestedSkillLevel() + GetVar(11) * GetEnhance();
if( m_nCurrentFire == 0 )
{
m_nTotalFire = GetVar(7) + GetVar(8) * GetRequestedSkillLevel();
m_nCurrentFire = 1;
}
else
{
++m_nCurrentFire;
}
bool bTargetOrigin = true;
int nRegionType = -1;
float fRegionProperty = 0.0f;
float fEffectLength = GetVar(4) * GameRule::DEFAULT_UNIT_SIZE;
m_fRange = fEffectLength;
std::vector< StructCreature * > vTargetList;
std::vector< StructCreature * >::iterator itTargetList;
AR_TIME t = GetArTime();
nDamage = EnumSkillTargetsAndCalcDamage( m_pOwner->GetCurrentPosition( t ), m_pOwner->GetLayer(), pTarget->GetCurrentPosition( t ), bTargetOrigin, fEffectLength, nRegionType, fRegionProperty, nDamage, true, m_pOwner, GetVar(5), GetVar(6), vTargetList );
m_nTargetCount = static_cast< int >( vTargetList.size() );
for( itTargetList = vTargetList.begin(); itTargetList != vTargetList.end(); ++itTargetList )
{
StructCreature* pDealTarget = (*itTargetList);
if( pDealTarget->IsDead() )
continue;
StructCreature::_DAMAGE_INFO Damage = pDealTarget->DealPhysicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pDealTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::DAMAGE, elemental_type, Damage, pDealTarget->GetHandle() );
}
m_nFireTime += GetVar(9) * 100;
return;
}
void StructSkill::PHYSICAL_MULTIPLE_DAMAGE( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int elemental_type = GetSkillBase()->GetElementalType();
int nDamage = m_pOwner->GetAttackPointRight( (Elemental::Type)elemental_type, GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
nDamage *= GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(10) * GetEnhance();
nDamage += GetVar(2) + GetVar(3) * GetRequestedSkillLevel() + GetVar(11) * GetEnhance();
int nCount = GetVar(4) + GetVar(5) * GetRequestedSkillLevel();
// 데미지
for( int i = 0; i < nCount; ++i )
{
StructCreature::_DAMAGE_INFO Damage = pTarget->DealPhysicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
}
m_nFireCount = nCount;
// 연타 횟수 만큼 행동 불가
m_nFireTime += GetVar(6) * nCount * 100;
return;
}
void StructSkill::PHYSICAL_MULTIPLE_DAMAGE_TRIPLE_ATTACK( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int elemental_type = GetSkillBase()->GetElementalType();
int nDamage = m_pOwner->GetAttackPointRight( (Elemental::Type)elemental_type, GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
nDamage *= GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(10) * GetEnhance();
nDamage += GetVar(2) + GetVar(3) * GetRequestedSkillLevel() + GetVar(11) * GetEnhance();
int nStep = std::min( (int)( ( GetRequestedSkillLevel() - 1 ) / GetVar(4) + 1 ), 3 );
int nCount = GetVar( nStep + 4 );
// 데미지
for( int i = 0; i < nCount; ++i )
{
StructCreature::_DAMAGE_INFO Damage = pTarget->DealPhysicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
}
m_nFireCount = nCount;
// 연타 횟수 만큼 행동 불가
m_nFireTime += GetVar( 8 ) * nCount * 100;
return;
}
void StructSkill::PHYSICAL_MULTIPLE_REGION_DAMAGE( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int elemental_type = GetSkillBase()->GetElementalType();
int nDamage = m_pOwner->GetAttackPointRight( (Elemental::Type)elemental_type, GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
// 유형별 파라미터 세팅
int nCount = 0; // 연타 횟수
bool bTargetOrigin = true; // 기본 타겟 중심
int nRegionType = -1; // 기본 원형
float fRegionProperty = 0.0f; // 기본 범위 유형 속성값 없음
AR_TIME nFireInterval = 0; // 연타간 딜레이
switch( GetSkillBase()->GetEffectType() )
{
case SkillBase::EF_PHYSICAL_MULTIPLE_REGION_DAMAGE:
nDamage *= GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(10) * GetEnhance();
nDamage += GetVar(2) + GetVar(3) * GetRequestedSkillLevel() + GetVar(11) * GetEnhance();
nCount = GetVar(7) + GetVar(8) * GetRequestedSkillLevel();
nFireInterval = GetVar(9) * 100;
break;
case SkillBase::EF_PHYSICAL_MULTIPLE_SPECIAL_REGION_DAMAGE_SELF:
bTargetOrigin = false; // break 쓰면 데미지 증폭/증가 안되요;;
case SkillBase::EF_PHYSICAL_MULTIPLE_SPECIAL_REGION_DAMAGE:
nDamage *= GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(11) * GetEnhance();
nDamage += GetVar(2) + GetVar(3) * GetRequestedSkillLevel() + GetVar(12) * GetEnhance();
nCount = GetVar(9) + GetVar(10) * GetRequestedSkillLevel();
nFireInterval = GetVar(13) * 100;
nRegionType = static_cast< int >( GetVar(7) );
fRegionProperty = GetVar(8);
break;
default:
// 어느 유형인데 이쪽으로 오셨는가...?
assert( 0 );
return;
}
float fEffectLength = GetVar(4) * GameRule::DEFAULT_UNIT_SIZE;
m_fRange = fEffectLength;
std::vector< StructCreature * > vTargetList;
std::vector< StructCreature * >::iterator itTargetList;
AR_TIME t = GetArTime();
nDamage = EnumSkillTargetsAndCalcDamage( m_pOwner->GetCurrentPosition( t ), m_pOwner->GetLayer(), pTarget->GetCurrentPosition( t ), bTargetOrigin, fEffectLength, nRegionType, fRegionProperty, nDamage, true, m_pOwner, GetVar(5), GetVar(6), vTargetList );
m_nTargetCount = static_cast< int >( vTargetList.size() );
// 데미지 적용
for( int i = 0; i < nCount; ++i )
{
for( itTargetList = vTargetList.begin(); itTargetList != vTargetList.end(); ++itTargetList )
{
StructCreature* pDealTarget = (*itTargetList);
if( pDealTarget->IsDead() )
continue;
StructCreature::_DAMAGE_INFO Damage = pDealTarget->DealPhysicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pDealTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::DAMAGE, elemental_type, Damage, pDealTarget->GetHandle() );
}
}
m_nFireTime += nFireInterval * nCount;
}
void StructSkill::PHYSICAL_SINGLE_SPECIAL_REGION_DAMAGE( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int elemental_type = GetSkillBase()->GetElementalType();
int nDamage = m_pOwner->GetAttackPointRight( (Elemental::Type)elemental_type, GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
nDamage *= GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(13) * GetEnhance();
nDamage += GetVar(2) + GetVar(3) * GetRequestedSkillLevel() + GetVar(11) * GetEnhance();
float fEffectLength = GetVar(4) * GameRule::DEFAULT_UNIT_SIZE;
m_fRange = fEffectLength;
std::vector< StructCreature * > vTargetList;
std::vector< StructCreature * >::iterator itTargetList;
AR_TIME t = GetArTime();
nDamage = EnumSkillTargetsAndCalcDamage( m_pOwner->GetCurrentPosition( t ), m_pOwner->GetLayer(), pTarget->GetCurrentPosition( t ), GetVar(8), fEffectLength, GetVar(7), GetVar(10), nDamage, GetVar(9), m_pOwner, GetVar(5), GetVar(6), vTargetList );
m_nTargetCount = static_cast< int >( vTargetList.size() );
// 데미지 적용
for( itTargetList = vTargetList.begin(); itTargetList != vTargetList.end(); ++itTargetList )
{
StructCreature* pDealTarget = (*itTargetList);
StructCreature::_DAMAGE_INFO Damage = pDealTarget->DealPhysicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pDealTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::DAMAGE, elemental_type, Damage, pDealTarget->GetHandle() );
}
}
void StructSkill::PHYSICAL_CHAIN_DAMAGE( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int elemental_type = GetSkillBase()->GetElementalType();
int nOriginalDamage = m_pOwner->GetAttackPointRight( (Elemental::Type)elemental_type, GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
nOriginalDamage *= GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(2) * GetEnhance();
nOriginalDamage += GetVar(3) * GetEnhance();
int nChainCount = GetVar(4) + GetVar(5) * GetRequestedSkillLevel();
int nTargetType = GetVar(12);
int nAdditionalChainDamage = nOriginalDamage;
nAdditionalChainDamage *= GetVar(6) + GetVar(7) * GetRequestedSkillLevel();
float fEffectLength = GetCastRange() * 2 * GameRule::DEFAULT_UNIT_SIZE;
float fChainLength = GetVar(9) * GameRule::DEFAULT_UNIT_SIZE;
bool bIsOverlapped = GetVar(10);
int nChainType = GetVar(11);
m_fRange = fEffectLength;
std::vector< StructCreature * > vTargetList;
AR_TIME t = GetArTime();
int nCount = 0;
int nDamage = nOriginalDamage;
StructCreature *pChainTarget = pTarget;
AR_HANDLE hFrom = m_pOwner->GetHandle();
do
{
// 첫 대상은 무조건 목록에 포함한다. bIncludeTarget = !nCount
int result = EnumChainSkillTargetsAndCalcDamage( fEffectLength, fChainLength, nChainType, !nCount, m_pOwner, pChainTarget, nChainCount, vTargetList, nTargetType );
if( !result ) break;
// 데미지 적용
for( int i = 0; i < result; ++i )
{
StructCreature* pDealTarget = vTargetList[i];
StructCreature::_DAMAGE_INFO Damage = pDealTarget->DealPhysicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pDealTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddChainSkillDamageResult( &m_vResultList, SkillResult::CHAIN_DAMAGE, elemental_type, Damage, pDealTarget->GetHandle(), hFrom );
nDamage += nAdditionalChainDamage;
pChainTarget = pDealTarget;
hFrom = pDealTarget->GetHandle();
}
nCount += result;
} while( bIsOverlapped && nCount < nChainCount );
m_nTargetCount = nCount;
}
void StructSkill::MAGIC_CHAIN_DAMAGE( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int elemental_type = GetSkillBase()->GetElementalType();
int nOriginalDamage = m_pOwner->GetMagicPoint( (Elemental::Type)elemental_type, GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
nOriginalDamage *= GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(2) * GetEnhance();
nOriginalDamage += GetVar(3) * GetEnhance();
int nChainCount = GetVar(4) + GetVar(5) * GetRequestedSkillLevel();
int nTargetType = GetVar(12);
int nAdditionalChainDamage = nOriginalDamage;
nAdditionalChainDamage *= GetVar(6) + GetVar(7) * GetRequestedSkillLevel();
float fEffectLength = GetCastRange() * 2 * GameRule::DEFAULT_UNIT_SIZE;
float fChainLength = GetVar(9) * GameRule::DEFAULT_UNIT_SIZE;
bool bIsOverlapped = GetVar(10);
int nChainType = GetVar(11);
m_fRange = fEffectLength;
std::vector< StructCreature * > vTargetList;
AR_TIME t = GetArTime();
int nCount = 0;
int nDamage = nOriginalDamage;
StructCreature *pChainTarget = pTarget;
AR_HANDLE hFrom = m_pOwner->GetHandle();
do
{
// 첫 대상은 무조건 목록에 포함한다. bIncludeTarget = !nCount
int result = EnumChainSkillTargetsAndCalcDamage( fEffectLength, fChainLength, nChainType, !nCount, m_pOwner, pChainTarget, nChainCount, vTargetList, nTargetType );
if( !result ) break;
// 데미지 적용
for( int i = 0; i < result; ++i )
{
StructCreature* pDealTarget = vTargetList[i];
StructCreature::_DAMAGE_INFO Damage = pDealTarget->DealMagicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pDealTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddChainSkillDamageResult( &m_vResultList, SkillResult::CHAIN_MAGIC_DAMAGE, elemental_type, Damage, pDealTarget->GetHandle(), hFrom );
nDamage += nAdditionalChainDamage;
pChainTarget = pDealTarget;
hFrom = pDealTarget->GetHandle();
}
nCount += result;
} while( bIsOverlapped && nCount < nChainCount );
m_nTargetCount = nCount;
}
void StructSkill::CHAIN_HEAL( struct StructCreature* pTarget )
{
if( !pTarget ) return;
int elemental_type = GetSkillBase()->GetElementalType();
int nOriginalDamage = m_pOwner->GetMagicPoint( (Elemental::Type)elemental_type, GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
nOriginalDamage *= GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(2) * GetEnhance();
nOriginalDamage += GetVar(3) * GetEnhance();
int nChainCount = GetVar(4) + GetVar(5) * GetRequestedSkillLevel();
int nTargetType = GetVar(12);
int nAdditionalChainDamage = nOriginalDamage;
nAdditionalChainDamage *= GetVar(6) + GetVar(7) * GetRequestedSkillLevel();
float fEffectLength = GetCastRange() * 2 * GameRule::DEFAULT_UNIT_SIZE;
float fChainLength = GetVar(9) * GameRule::DEFAULT_UNIT_SIZE;
bool bIsOverlapped = GetVar(10);
int nChainType = GetVar(11);
m_fRange = fEffectLength;
std::vector< StructCreature * > vTargetList;
AR_TIME t = GetArTime();
int nCount = 0;
int nDamage = nOriginalDamage;
StructCreature *pChainTarget = pTarget;
AR_HANDLE hFrom = m_pOwner->GetHandle();
do
{
// 첫 대상은 무조건 목록에 포함한다. bIncludeTarget = !nCount
int result = EnumChainSkillTargetsAndCalcDamage( fEffectLength, fChainLength, nChainType, !nCount, m_pOwner, pChainTarget, nChainCount, vTargetList, nTargetType );
if( !result ) break;
// 힐 적용
for( int i = 0; i < result; ++i )
{
StructCreature* pDealTarget = vTargetList[i];
int nIncHP = pDealTarget->Heal( nDamage );
SkillResult skill_result;
skill_result.chain_heal.type = SkillResult::CHAIN_HEAL;
skill_result.chain_heal.hTarget = pDealTarget->GetHandle();
skill_result.chain_heal.target_hp = pDealTarget->GetHP();
skill_result.chain_heal.nIncHP = nIncHP;
skill_result.chain_heal.hFrom = hFrom;
m_vResultList.push_back( skill_result );
nDamage += nAdditionalChainDamage;
pChainTarget = pDealTarget;
hFrom = pDealTarget->GetHandle();
}
nCount += result;
} while( bIsOverlapped && nCount < nChainCount );
m_nTargetCount = nCount;
}
void StructSkill::PHYSICAL_SINGLE_DAMAGE_DEMINISHED_HP_MP( struct StructCreature * pTarget )
{
if( !pTarget ) return;
// 데미지 처리
int elemental_type = GetSkillBase()->GetElementalType();
int nDamage = m_pOwner->GetAttackPointRight( (Elemental::Type)elemental_type, GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
nDamage *= GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(4) * GetEnhance();
nDamage += GetVar(2) + GetVar(3) * GetRequestedSkillLevel() + GetVar(5) * GetEnhance();
float DiminishedHPPercentage = 1.0f - (static_cast<float>(pTarget->GetHP()) / pTarget->GetMaxHP());
float DiminishedMPPercentage = 1.0f - (static_cast<float>(pTarget->GetMP()) / pTarget->GetMaxMP());
nDamage += (DiminishedHPPercentage / (GetVar(6) + GetVar(7))) * ((GetVar(8) + GetVar(9)) + (GetVar(10) + GetVar(11)));
nDamage += (DiminishedMPPercentage / (GetVar(12) + GetVar(13))) * ((GetVar(14) + GetVar(15)) + (GetVar(16) + GetVar(17)));
StructCreature::_DAMAGE_INFO Damage = pTarget->DealPhysicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
return;
}
void StructSkill::PHYSICAL_SINGLE_DAMAGE_PROP_REMAIN_MP( struct StructCreature* pTarget )
{
if( !pTarget ) return;
// 데미지 처리
int elemental_type = GetSkillBase()->GetElementalType();
int nDamage = m_pOwner->GetAttackPointRight( (Elemental::Type)elemental_type, GetSkillBase()->IsPhysicalSkill(), GetSkillBase()->IsHarmful() );
nDamage *= GetVar(0) + GetVar(1) * GetRequestedSkillLevel() + GetVar(18) * GetEnhance();
nDamage += GetVar(2) + GetVar(3) * GetRequestedSkillLevel() + GetVar(19) * GetEnhance();
int cost = std::min<int>( ( GetVar(6) + GetVar(7) * GetRequestedSkillLevel() ) * m_pOwner->GetMP(), m_pOwner->GetMP() );
nDamage += ( GetVar(4) + GetVar(5) * GetRequestedSkillLevel() ) * cost;
m_pOwner->AddMP( -cost );
StructCreature::_DAMAGE_INFO Damage = pTarget->DealPhysicalSkillDamage( m_pOwner, nDamage, (Elemental::Type)elemental_type, GetHitBonus( GetEnhance(), m_pOwner->GetLevel() - pTarget->GetLevel() ), GetCriticalBonus( GetRequestedSkillLevel() ) );
AddSkillDamageResult( &m_vResultList, SkillResult::DAMAGE, elemental_type, Damage, pTarget->GetHandle() );
return;
}
void StructSkill::INC_SKILL_COOL_TIME( struct StructCreature* pTarget )
{
if( !pTarget ) return;
if( GetVar( 0 ) )
{
// 시간의 지배자 이외의 스킬에서 모든 스킬의 쿨타임을 변경할 수 없다.
// 이와 같이 하는 이유는 모든 스킬의 쿨타임을 변경하는 스킬이 서로 상호적으로 영향을 준다면 예기치 못 한 상황이 발생할 수 있으며, 이 상황에 대해 고려되어있지 않기 때문이다.
// 추후 모든 스킬의 쿨타임을 변경하는 액티브 스킬은 유형에서 제외하여 비유형화하고 스킬 ID에 의해 예외 처리를 해야할 필요가 있다.
assert( GetSkillId() != StructSkill::SKILL_RULER_OF_TIME );
pTarget->AddRemainCoolTime( -1, GetVar( 1 ) + GetVar( 2 ) * GetRequestedSkillLevel(), 1.0f );
}
for( int i = 1; i < 6; ++i )
{
if( !GetVar( i * 3 ) ) break;
pTarget->AddRemainCoolTime( GetVar( i * 3 ), GetVar( i * 3 + 1 ) + GetVar( i * 3 + 2 ) * GetRequestedSkillLevel(), 1.0f );
}
}
void StructSkill::AMP_SKILL_COOL_TIME( struct StructCreature* pTarget )
{
if( !pTarget ) return;
if( GetVar( 0 ) )
{
// 시간의 지배자 이외의 스킬에서 모든 스킬의 쿨타임을 변경할 수 없다.
// 이와 같이 하는 이유는 모든 스킬의 쿨타임을 변경하는 스킬이 서로 상호적으로 영향을 준다면 예기치 못 한 상황이 발생할 수 있으며, 이 상황에 대해 고려되어있지 않기 때문이다.
// 추후 모든 스킬의 쿨타임을 변경하는 액티브 스킬은 유형에서 제외하여 비유형화하고 스킬 ID에 의해 예외 처리를 해야할 필요가 있다.
assert( GetSkillId() != StructSkill::SKILL_RULER_OF_TIME );
pTarget->AddRemainCoolTime( -1, 0, GetVar( 1 ) + GetVar( 2 ) * GetRequestedSkillLevel() );
}
for( int i = 1; i < 6; ++i )
{
if( !GetVar( i * 3 ) ) break;
pTarget->AddRemainCoolTime( GetVar( i * 3 ), 0, GetVar( i * 3 + 1 ) + GetVar( i * 3 + 2 ) * GetRequestedSkillLevel() );
}
}
void StructSkill::REPLENISH_ENERGY_HP_MP()
{
int energyCount = 0;
while (m_pOwner->GetEnergyCount() < m_pOwner->GetMaxEnergyCount()) {
m_pOwner->AddEnergy();
energyCount++;
}
// AziaMafia Fix Energy Hp+MP
energyCount = GameRule::ENERGY_MAX;
int ReplenishedHPValue = GetVar( 0 ) + GetRequestedSkillLevel() * GetVar( 1 );
c_fixed10 ReplenishedHPPercent = GetVar( 2 ) + GetRequestedSkillLevel() * GetVar( 3 );
c_fixed10 ReplenishedMPPercent = GetVar( 6 ) + GetRequestedSkillLevel() * GetVar( 7 );
int ReplenishedHP = energyCount * ( ReplenishedHPValue + ReplenishedHPPercent * m_pOwner->GetMaxHP() );
int ReplenishedMP = energyCount * ( ReplenishedHPValue + ReplenishedHPPercent * m_pOwner->GetMaxHP() );
m_pOwner->AddHP( ReplenishedHP );
m_pOwner->AddMP( ReplenishedMP );
SkillResult skill_result;
skill_result.add_hp.type = SkillResult::ADD_HP;
skill_result.add_hp.hTarget = m_pOwner->GetHandle();
skill_result.add_hp.nIncHP = ReplenishedHP;
skill_result.add_hp.target_hp = m_pOwner->GetHP();
m_vResultList.push_back( skill_result );
skill_result.add_hp.type = SkillResult::ADD_MP;
skill_result.add_hp.hTarget = m_pOwner->GetHandle();
skill_result.add_hp.nIncHP = ReplenishedMP;
skill_result.add_hp.target_hp = m_pOwner->GetMP();
m_vResultList.push_back( skill_result );
}