Files
Sun1602/Server/GameServer/SkillSystem/Skill.cpp
T
2022-10-26 12:25:11 +08:00

1049 lines
38 KiB
C++

#include "stdafx.h"
#include "Skill.h"
#include <Const.h>
#include <Struct.h>
#include <PublicMath.h>
#include <PacketStruct_CG.h>
#include <SkillInfoParser.h>
#include "AITypes.h"
#include "FieldInfo.h"
#include "GameField.h"
#include "Character.h"
#include "Player.h"
#include "NPC.h"
#include "Ability.h"
#include "AbilityFactory.h"
#include "StatusManager.h"
#include "ChaoState.h"
#include "MovingPlanner.h"
#ifdef _NA_008588_20151202_MODIFY_MOVEING_SKILL_LOGIC
#include "SkillSystem/CSSyncTuner.h"
#endif //_NA_008588_20151202_MODIFY_MOVEING_SKILL_LOGIC
#ifdef _NA_007514_20140828_NEW_CHARACTER_WITCHBLADE
#include "CharacterFormula.h"
#endif //_NA_007514_20140828_NEW_CHARACTER_WITCHBLADE
//==================================================================================================
// (History)
// - 09/07/09, waverix, 로직 정리
//==================================================================================================
//
RESULTMSG Skill::result_msg_array_[MAX_TARGET_NUM];
RESULTMSG Skill::result_effect_msg_array_[5];
//
void Skill::InitResultMsg()
{
number_of_hit_targets_ = 0;
number_of_effects_ = 0;
}
//==================================================================================================
//
//
Skill::~Skill()
{
Release();
}
void
Skill::Release()
{
if (is_applied_ether_bullet_option_ && owner_)
{
((Player*)owner_)->m_EtherManager.EndEtherEmissionDevice(this);
ether_combo_count_ = 0;
}
RemoveAbilities();
}
// represent a kind of 'Init'
void Skill::SetupDefault(Character* owner,
const SkillInfo* skill_info, const ROOT_SKILLINFO* root_skill_info)
{
ZeroMemory(field_block_start_, static_cast<size_t>(field_block_end_ - field_block_start_));
// step#1 : first operation block
;{
owner_ = owner;
if (owner) {
game_field_ = owner->GetField();
}
if (root_skill_info != NULL)
{
if (root_skill_info->IsSkill()) {
base_skill_info_ = static_cast<const SkillScriptInfo*>(root_skill_info);
}
else {
base_style_info_ = static_cast<const BASE_STYLEINFO*>(root_skill_info);
}
}
skill_info_data_ = *skill_info;
};
//
//
bonus_ability_effect_.Init(); // ... real operation is unless, clear memrory ...
// step#2 : timer control using polymorphism interface
;{
// ExecuteInterval 셋팅
SetExecuteInterval(); // virtual
ConfigureAnimationDelay();
// ExecuteTick 셋팅
execute_tick_ = GetTickCount() + GetExecuteInterval();
};
// pseudo : if is_passive_skill is false, that means an Active skill or a Normal(Style) skill
const bool is_passive_skill = base_skill_info_ ?
(base_skill_info_->m_bySkillType == SKILL_TYPE_PASSIVE) : false;
//
// step#3 : safety zone control, moved from Init(...)
// (CHANGES) (REF:#950) (WAVERIX) owner_가 없어지더라도 발동 시점의 위치는 한번 설정 후
// 변하지 않는다. 시전 시점의 위치이므로, 따라서 구해둔다.
// Character::GetTileAtt로 구하지 않는다. GetTileAtt는 Progress완료 이후 정확한 값이 설정된다.
is_invoked_in_safety_area_ = false;
if (is_passive_skill == false && game_field_)
{
const int invoked_tile = owner->PlanEvent->GetTile();
if ((invoked_tile < 0) == false)
{
const WORD invoked_tile_attr = game_field_->GetMovingPlanner()->GetAttribute(invoked_tile);
if (invoked_tile_attr & PTA_SAFETY_ZONE) {
is_invoked_in_safety_area_ = true;
}
}
}
// step#4 : ether device control block
ether_combo_count_ = tagEtherBulletInfo::eETHER_DEVICE_COMBO_NORMAL;
if (owner_ && owner_->IsEqualObjectKind(PLAYER_OBJECT))
{
CEtherEmissionDevice& rDevice = static_cast<Player*>(owner_)->m_EtherManager;
ether_combo_count_ = rDevice.StartEtherEmissionDevice(this, skill_info_data_.main_target_key_);
}
}
void
Skill::Init(Character* owner, const SkillInfo* skill_info, const ROOT_SKILLINFO* root_skill_info)
{
SetupDefault(owner, skill_info, root_skill_info);
//
// skills = { instant, delayed }
AddAbilities(); // register abilities
SetAttribution();
}
void Skill::ConfigureAnimationDelay(const DWORD /*empty_arg*/)
{
Character* const owner = GetOwner();
if (owner == NULL || owner->GetField() == NULL)
{
return;
}
if (base_skill_info_ == NULL)
{
return;
}
const WORD delay_tick = base_skill_info_->m_CSSyncDelay;
if (delay_tick == 0)
{
return;
}
if (owner->IsEqualObjectKind(PLAYER_OBJECT))
{
const Player* const player = static_cast<Player*>(owner);
StatusManager* const status_manager = player->GetStatusManager();
(status_manager->AnimationDelay).SetAnimationDelay(this, delay_tick);
}
}
void
Skill::EndExecute()
{
if (owner_->IsEqualObjectKind(PLAYER_OBJECT))
{
Player* pPlayer = (Player*)owner_;
pPlayer->SetForceAttack(false);
//SUNLOG(eDEV_LOG, "[Skill::EndExecute()] Name = %s, SkillCode = %d, Force Attack is false",
// pPlayer->GetCharName(), GetSkillCode());
}
}
void
Skill::AddAbilities()
{
// CHANGES, f100821.1L, change an ability hash table to a fixed size array.
if (abilities_.number_of_abilities_ != 0)
{
SUNLOG(eCRITICAL_LOG, _T("|["__FUNCTION__"]|斤口=MUST be zero|number_of_abilities=%d|"),
abilities_.number_of_abilities_);
RemoveAbilities();
};
// CHANGES, f100831.4L
const uint8_t event_code = this->skill_info_data_.event_code_;
AbilityFactory* const ability_factory = AbilityFactory::Instance();
//
AbilityContainer::Node* it = abilities_.nodes_;
base_skill_info_->SetFirst();
while (const BASE_ABILITYINFO* ability_info = base_skill_info_->GetNext())
{
int index = static_cast<int>(it - abilities_.nodes_);
assert(index == ability_info->m_byIndex && index < _countof(abilities_.nodes_));
if (index >= _countof(abilities_.nodes_)) {
break;
}
const eABILITY ability_id = static_cast<eABILITY>(ability_info->m_wAbilityID);
// Ability 생성
Ability* ability = ability_factory->AllocAbility(static_cast<WORD>(ability_id), GetSkillType());
if (!ability) {
SUNLOG(eCRITICAL_LOG, _T("["__FUNCTION__"] #1 %d, %d, %d",
GetSkillCode(), index, ability_id));
};
assert(ability);
ability->Init(this, ability_info);
ability->SetEventCode(event_code);
//
it->ability_id_ = ability_id;
it->ability_ = ability;
abilities_.number_of_abilities_ = (index + 1); // used by number
++it;
}
}
void
Skill::RemoveAbilities()
{
AbilityFactory* const ability_factory = AbilityFactory::Instance();
AbilityContainer::Node* it = abilities_.nodes_,
* const end = &abilities_.nodes_[_countof(abilities_.nodes_)];
for (int index = 0; index < abilities_.number_of_abilities_ && it != end; ++index, ++it)
{
Ability* ability = it->ability_;
ability_factory->FreeAbility(ability);
}
ZeroMemory(&abilities_, sizeof(abilities_));
}
void
Skill::DecreaseHPMP()
{
// use only a kind of Skill
if (base_skill_info_ == 0)
{
SUNLOG(eCRITICAL_LOG, "[Skill::DecreaseMP] base_skill_info_ is NULL!");
return;
}
// 플레이어의 HP, MP를 감소시킨다.
// formula
// MP spend = value * ratio + spend_change_value
int mp_spend = base_skill_info_->m_wMPSpend;
; mp_spend = static_cast<int>(mp_spend * (1 + owner_->GetMPSpendIncRatio()));
; mp_spend += owner_->GetMPSpendIncValue();
; mp_spend = max(0, mp_spend);
owner_->DecreaseHP(base_skill_info_->m_wHPSpend);
owner_->DecreaseMP(mp_spend);
}
void
Skill::SetAttribution()
{
switch(GetSkillClassCode())
{
case eSKILL_DOUBLE_ATTACK:
case eSKILL_AIRBLOW:
case eSKILL_BATTLERHONE:
skill_attribution_ |= SKILL_ATTRIBUTION_TARGETFLYING;
break;
case eSKILL_ILLUSION_DANCE:
skill_attribution_ |= SKILL_ATTRIBUTION_TARGETSTOP;
break;
}
}
BOOL
Skill::CheckMainTarget(BattleResult* const detailed_result)
{
BattleResult battle_result = { 0, };
BattleResult* result_handle = detailed_result ? detailed_result : &battle_result;
result_handle->result_code = RC::RC_BATTLE_SUCCESS;
//
if ((game_field_ && base_skill_info_) == 0)
{
SUNLOG(eCRITICAL_LOG, "["__FUNCTION__"] GameField [%u] BaseSkillInfo [%u]",
!!game_field_, !!base_skill_info_);
result_handle->result_code = RC::RC_BATTLE_INVALID_MAINTARGET;
return false;
}
const eSKILL_TARGET_TYPE skill_target_type = eSKILL_TARGET_TYPE(base_skill_info_->m_byTarget);
// 소환스킬은 메인타겟이 없다.
if (skill_target_type == SKILL_TARGET_SUMMON)
return true;
// 메인타겟 좌표를 설정한다.
float fSkillRange = base_skill_info_->m_wSkillRange / 10.0f;
Character* pMainTarget = NULL;
if (skill_target_type == SKILL_TARGET_ME)
{
skill_info_data_.main_target_key_ = owner_->GetObjectKey();
skill_info_data_.main_target_pos_ = skill_info_data_.cur_pos_;
}
else if (skill_target_type == SKILL_TARGET_AREA)
{
//_NA001385_20090924_DOMINATION_FINDPATH_ATTACK
if (false == owner_->CheckAttackFindPath(skill_info_data_.main_target_pos_)) {
result_handle->result_code = RC::RC_BATTLE_PLAYER_STATE_WHERE_CANNOT_ATTACK_ENEMY;
return false;
}
}
else if (base_skill_info_->m_byAttRangeform == SRF_PIERCE)
{ // 관통스킬인 경우 m_wvMainTargetPos을 그대로 쓴다.
// 관통스킬은 연출상 1m 정도 더 나가도록 한다.
fSkillRange += 1.f;
}
else
{
pMainTarget = game_field_->FindCharacter(skill_info_data_.main_target_key_);
if (pMainTarget)
skill_info_data_.main_target_pos_ = *pMainTarget->GetPosPtr();
}
// 메인타겟에 대해 스킬 사거리를 체크한다.
if (pMainTarget)
{
BOOL is_skill_range_ok =
owner_->CheckSkillRange(pMainTarget, skill_info_data_.main_target_pos_, fSkillRange);
if (is_skill_range_ok == 0) {
result_handle->result_code = RC::RC_BATTLE_OUT_OF_RANGE;
return false;
}
}
return true;
}
BOOL
Skill::IsMaxTargetNum(Character* pTarget)
{
if (pTarget == owner_)
is_target_owner_ = true;
if (!base_skill_info_->m_byMaxTargetNo)
return false;
int iCheckCount = number_of_hit_targets_;
if (is_target_owner_)
--iCheckCount;
if (iCheckCount >= base_skill_info_->m_byMaxTargetNo)
return true;
return false;
}
//_NA000000_110210_ENHANCED_TARGET_SEARCH_ALGORITHM
// NOTE: f110309.1L, this method is implemented for (Instant/Delayed) skill processing.
bool Skill::ExecuteSkill()
{
using namespace ns_object;
if ((game_field_ && base_skill_info_) == false)
{
SUNLOG(eCRITICAL_LOG, _T("["__FUNCTION__"] GameField [%u] BaseSkillInfo [%u]"),
!!game_field_, !!base_skill_info_);
return false;
}
//------------------------------------------------------------------------------------------
InitResultMsg();
assert(number_of_hit_targets_ == 0 && number_of_effects_ == 0);
//
struct AutoSearchOptionDestroyer {
~AutoSearchOptionDestroyer() { SearchOption::SkillBase::DestroyInstance(); };
} auto_search_option_destroyer;
//
SearchOption::SkillBase& search_option = *SearchOption::SkillBase::CreateInstance(this);
//
const SECTOR_IDX coordinates = search_option.actor->GetSectorIndex();
const nsGrid::SectorGridVR* sector_grid_vr =
game_field_->GetSectorGridVR(coordinates, search_option.arguments.ranges_of_application);
if (sector_grid_vr == NULL) {
return false;
};
const ns_object::SearchOption::Arguments& arguments = search_option.arguments;
nsGrid::SectorGrid3x3 OUT grid;
;{
bool success = sector_grid_vr->GetAroundSectors(
arguments.application_center_pos, &grid, NULL);
if (success == false) {
return false;
}
};
//----------------------------------------------------------------------------------------------
ViewBoxCubic damage_box;
// CHANGES: f110621.3L, changed the damage box ranges according to the attack range formation
if (search_option.GetApplicationRangeBox(&damage_box,
(game_field_->IsWarCastle() ? 30.0f : 5.0f)) == false)
{
return false;
};
//
SearchOption::SkillBase::result_type search_result = { 0, };
//----------------------------------------------------------------------------------------------
// 단일 타겟에게 사용하는 스킬인 경우 타겟을 검색하지 않고 메인타겟으로 셋팅한다.
if (base_skill_info_->m_byAttRangeform == SRF_FOWARD_ONE)
{
Character* main_target = game_field_->FindCharacter(skill_info_data_.main_target_key_);
if (main_target)
{
search_option.working.main_target = main_target;
// actor
if (main_target != owner_)
{
;{
SkillExecutionTransaction transaction; // CHANGES: f110303.3L
// execute abilities
if (Skill::ExecuteAbilities(&search_option, damage_box,
owner_, &result_msg_array_[number_of_hit_targets_]))
{
++number_of_hit_targets_;
};
};
};
// main_target
;{
SkillExecutionTransaction transaction; // CHANGES: f110303.3L
// execute abilities
if (Skill::ExecuteAbilities(&search_option, damage_box,
main_target, &result_msg_array_[number_of_hit_targets_]))
{
search_option.working.main_target = main_target;
// process about PK attack
owner_->ForcedAttack(main_target);
// send AI message if this attack is hit.
Skill::SendAIMessage(main_target);
++number_of_hit_targets_;
}
};
}
}
// 소환스킬은 타겟을 구하지 않는다.
else if (base_skill_info_->m_byTarget == SKILL_TARGET_SUMMON)
{
}
// 타겟이 없이 좌표만 있는 스킬인 경우 타겟을 구하지 않는다.
else if (base_skill_info_->m_byAttRangeform == SRF_AREA_POSITION)
{
}
// 랜덤영역선택 스킬이라면 타겟을 구하지 않는다.
else if (base_skill_info_->GetAbilityInfo(eABILITY_RANDOM_AREA_ATTACK))
{
}
else
{
search_result.multi_select = true;
};
//----------------------------------------------------------------------------------------------
if (search_result.multi_select == false)
{
// jump to next
}
else
{
//------------------------------------------------------------------------------------------
//const bool skip_target = (except_object_key != 0);
GRID3x3_CENTER_PRIOR_SWAP(grid); // Center를 먼저 처리하면서 주변 섹터 처리
FOREACH_ARRAY(const SECTOR_IDX sector_idx, grid.Grid, SectorGrid3x3::GridArray)
{
if (number_of_hit_targets_ >= MAX_TARGET_NUM) {
break;
};
if (sector_idx == INVALID_SECTOR_INDEX) {
continue;
};
Sector* const sector = game_field_->GetSector(sector_idx);
ns_object::SearchNodeConstIterator it = sector->GetCharacterRoot();
const ns_object::SearchNodeConstIterator end = sector->GetCharacterEnd();
while (const ns_object::SearchNode::LinkNode* link_node = it.ClimbToNext(end))
{
if (number_of_hit_targets_ >= MAX_TARGET_NUM) {
break;
};
const ns_object::SearchNode::CharacterNode& char_node =
link_node->select.character_node;
Character* const target = char_node.character;
;{
SkillExecutionTransaction transaction; // CHANGES: f110303.3L
// execute abilities
if (!Skill::ExecuteAbilities(&search_option, damage_box,
target, &result_msg_array_[number_of_hit_targets_]))
{
continue;
}
};
if (skill_info_data_.main_target_key_ == char_node.object_key) {
search_option.working.main_target = target;
};
// process about PK attack
owner_->ForcedAttack(target);
// send AI message if this attack is hit.
Skill::SendAIMessage(target);
++number_of_hit_targets_;
if (IsMaxTargetNum(target)) {
break;
};
} //end 'for' characters
}; //end 'for' sectors
};
//----------------------------------------------------------------------------------------------
// apply field effect abilities
number_of_effects_ = ExecuteEffectAbilities(&result_effect_msg_array_[0]);
//----------------------------------------------------------------------------------------------
// process internal events. this process may be to occur recovery process when anyone is damaged
if (search_option.working.main_target &&
skill_info_data_.skill_factor_ == SKILL_FACTOR_NOMAL)
{
owner_->OnAttack(search_option.working.main_target, skill_info_data_.skill_code_, 0);
}
if (FlowControl::FCAssert(number_of_hit_targets_ <= MAX_TARGET_NUM) == false) {
// WARNING: heap buffer overrun
number_of_hit_targets_ = MAX_TARGET_NUM;
}
// 연계 스킬 활성화 어빌리티가 실행되지 않았으면 활성화된 연계 스킬 상태를 제거한다.
const SkillScriptInfo* const skill_info = GetSkillBaseInfo();
if (skill_info != NULL)
{
const bool is_active_combo = (skill_info->GetAbilityInfo(kAbilityActiveComboSkill) != NULL);
if (is_active_combo == false)
{
const Character* const owner = GetOwner();
if (owner != NULL)
{
StatusManager* const status_manager = owner->GetStatusManager();
status_manager->Remove(kCharStateActiveComboSkill);
//#define DEBUG_COMBO_SKILL_
#ifdef DEBUG_COMBO_SKILL_
SUNLOG(
eCRITICAL_LOG,
_T("|%s|Removed active combo state.|SkillCode = %d|"),
__FUNCTION__,
base_skill_info_->m_SkillCode
);
#endif
}
}
}
#ifdef _NA_007514_20140828_NEW_CHARACTER_WITCHBLADE
if ( owner_->IsEqualObjectKind(PLAYER_OBJECT) == true )
{
Player* player = reinterpret_cast<Player*>(owner_);
if (player != NULL && player->GetCharType() == eCHAR_WITCHBLADE)
{
for (int count=0; count < number_of_hit_targets_; ++count)
{
SKILL_RESULT_BASE* base_msg = reinterpret_cast<SKILL_RESULT_BASE*>(result_msg_array_[count].m_byMsg);
//SKILL_RESULT_DAMAGE* result_msg = reinterpret_cast<SKILL_RESULT_DAMAGE*>(base_msg+1);
if (base_msg->isDamaged == true)
{
player->OnRecoverFP(CalcFPRecoverByAttack(player->GetCharType()));
break;
}
}
}
}
#endif //_NA_007514_20140828_NEW_CHARACTER_WITCHBLADE
#ifdef _NA_008588_20151202_MODIFY_MOVEING_SKILL_LOGIC
_ProcessSkillMove();
#endif //_NA_008588_20151202_MODIFY_MOVEING_SKILL_LOGIC
return true;
}
#ifdef _NA_0_20100819_AGGRO_RENEWAL
// NOTE: f110621.4L, this method is only specialied to acquire the aggressive dispersion counter.
// generally, it is not supposed to use this logic because such usages are rare.
BYTE Skill::FindTargetCount(eSKILL_TARGET_TYPE skill_target_type)
{
//_NA000000_110210_ENHANCED_TARGET_SEARCH_ALGORITHM
__UNUSED(skill_target_type);
GameField* const game_field = owner_->GetField();
if (game_field == NULL) {
return 0;
};
ns_object::SearchResult::SingleSearch search_result;
ns_object::SearchOption::SingleSearch search_option(
MAX_TARGET_NUM,//int max_number_of_selected_characters,
owner_, //Character* input_actor,
NULL, //Character* input_main_target_if_exist_it,
owner_->GetPosPtr(), //const WzVector* input_specific_application_center_pos_if_main_target_is_not_used_to_pos,
//
0.0f, //float attack_ranges,
base_skill_info_->m_wSkillArea / 10.0f, //float ranges_of_application,
SRF_FOWARD_360); //eSKILL_AREA_TYPE forms_of_application
bool success = game_field->FindTargetsUsingSingleSearch(search_option, &search_result);
if (success == false) {
return 0;
};
// ranges = [0, MAX_TARGET_NUM]
return static_cast<uint8_t>(search_result.number_of_selected_chars);
//----------------------------------------------------------------------------------------------
};
#endif //_NA_0_20100819_AGGRO_RENEWAL
//==================================================================================================
//_NA000000_110210_ENHANCED_TARGET_SEARCH_ALGORITHM
bool Skill::ExecuteAbilities(
ns_object::SearchOption::SkillBase* search_option,
const nsGrid::ViewBoxCubic& damage_box,
Character* target, RESULTMSG* const raw_result_msg)
{
ZeroMemory(raw_result_msg, sizeof(*raw_result_msg));
if (target == NULL) {
return false;
};
//----------------------------------------------------------------------------------------------
SKILL_RESULT_BASE* const result_msg =
reinterpret_cast<SKILL_RESULT_BASE*>(raw_result_msg->m_byMsg);
;{
raw_result_msg->m_wSize = result_msg->GetSize();
result_msg->m_dwTargetKey = target->GetObjectKey();
};
skill_info_data_.skill_effect_ = 0;
WORD msg_size = 0;
BYTE* msg_it = reinterpret_cast<BYTE*>(result_msg + 1);
ns_object::RelationHint relation = { 0, };
//
const AbilityContainer::Node* ability_it = abilities_.nodes_,
* const ability_end = &abilities_.nodes_[_countof(abilities_.nodes_)];
for (int index = 0;
index < abilities_.number_of_abilities_ && ability_it != ability_end;
++index, ++ability_it)
{
if (search_option->working.need_process_abilities[index] == false) {
continue;
}
// NOTE: register the IFF filter of a currently processing ability to check internally
search_option->iff_filter = search_option->working.ability_infos[index]->iff_filter;
//------------------------------------------------------------------------------------------
const bool himself = (search_option->actor == target);
if ((search_option->iff_filter.value & search_option->iff_filter.eIFF_CheckSkip) == 0)
{
bool skip_ranges_check = himself &&
(search_option->iff_filter.value & search_option->iff_filter.eIFF_AttackerOnly);
;;;; if (skip_ranges_check == false && himself)
{
const WzVector* const target_pos = target->GetPosPtr();
if (damage_box.IsValidCoordinateXYZ(target_pos) == false) {
continue;
};
}
else if (skip_ranges_check == false)
{
const WzVector* const target_pos = target->GetPosPtr();
if (damage_box.IsValidCoordinateXYZ(target_pos) == false) {
continue;
};
const ns_object::SearchOption::Arguments& arguments = \
search_option->arguments;
if (arguments.attack_ranges != 0.0f) {
if (search_option->actor->CheckSkillRange(target, *target_pos,
arguments.attack_ranges) == false) {
continue;
}
};
if (search_option->actor->CheckArea(*target_pos,
const_cast<WzVector&>(*arguments.application_center_pos),
arguments.forms_of_application, arguments.ranges_of_application) == false)
{
return false;
}
};
};
if (search_option->IsSatisfiedObjectAbilityConditions(search_option->actor, target,
search_option->working.ability_infos[index]) == false)
{
continue;
};
if (search_option->IsSatisfiedObjectIFFRelations(search_option->actor, target,
&relation, &relation) == false)
{
continue;
};
//------------------------------------------------------------------------------------------
Ability* const ability = ability_it->ability_;
#if SUN_CODE_BACKUP // NOTE: already tested
const eABILITY_TYPE ability_type = ability->GetAbilityType();
// changes a concept. designed by __NA_S00015_20080828_NEW_HOPE_SHADOW
if (ability_type == eABILITY_TYPE_EFFECT || ability_type == eABILITY_TYPE_MANUAL) {
continue;
};
#endif
// check whether enable execution ability
if (!ability->CanExecute(owner_, target, skill_info_data_.main_target_key_,
skill_info_data_.main_target_pos_))
{
continue;
}
ability->SetBonusEffect(&bonus_ability_effect_);
// CHANGES: f110218.1L, prevent probability of garbage data occurrence
msg_size = 0;
if (ability->Execute(target, msg_it, msg_size))
{
if (msg_size > 512)
{
SUNLOG(eCRITICAL_LOG,
_T("|["__FUNCTION__"]|Warnings=big message size(%d)|"), msg_size);
continue;
}
// CHANGES: f110218.1L, changes order, old comment "(WAVERIX) (090122) (BUG-FIX)"
if (msg_size)
{
raw_result_msg->m_wSize += msg_size;
++result_msg->m_byAbilityNum;
#ifdef _NA_007514_20140828_NEW_CHARACTER_WITCHBLADE
// 해당 어빌리티가 Damage어빌리티고, 이 어빌리티로 상대에게 피해를 주었으면
if (ability->GetAbilityID() == eABILITY_DAMAGE)
{
SKILL_RESULT_DAMAGE* damage_msg = reinterpret_cast<SKILL_RESULT_DAMAGE*>(msg_it);
if (damage_msg->m_wDamage != 0 )
{
result_msg->isDamaged = true;
#ifdef _NA_007667_20141001_WITCHBLADE_SKILL
StatusManager* status_manager = owner_->GetStatusManager();
if (status_manager != NULL) {
status_manager->UpdateStatusByAttack(target);
}
#endif //_NA_007667_20141001_WITCHBLADE_SKILL
}
}
#endif //_NA_007514_20140828_NEW_CHARACTER_WITCHBLADE
msg_it += msg_size;
};
}
} //end 'for' abilities
// 타겟 한명한테만 적용하는 거라면 다음타겟전에 초기화한다.
if (bonus_ability_effect_.m_dwObjectKey) {
bonus_ability_effect_.Init();
}
result_msg->m_bySkillEffect = skill_info_data_.skill_effect_;
return (result_msg->m_byAbilityNum != 0);
}
BYTE Skill::ExecuteEffectAbilities(RESULTMSG* const result_msg)
{
ZeroMemory(result_msg, sizeof(*result_msg));
WORD msg_size = 0;
BYTE excute_count = 0;
const AbilityContainer::Node* it = abilities_.nodes_;
const AbilityContainer::Node* const end = &abilities_.nodes_[_countof(abilities_.nodes_)];
for (int i = 0; i < abilities_.number_of_abilities_ && it != end; ++i, ++it)
{
Ability* const ability = it->ability_;
eABILITY_TYPE ability_type = ability->GetAbilityType();
if (ability_type != eABILITY_TYPE_EFFECT && ability_type != eABILITY_TYPE_ACTIVE_AND_EFFECT)
{
continue;
}
if (ability->ExecuteEffect(result_msg[excute_count].m_byMsg, msg_size))
{
result_msg[excute_count].m_wSize += msg_size;
++excute_count;
}
}
return excute_count;
}
void
Skill::Broadcast_Instant_Result()
{
if (game_field_ == 0)
{
SUNLOG(eCRITICAL_LOG, "[Skill::Broadcast_Instant_Result] GameField is NULL!");
return;
}
uint8_t variable_buffer[12 * 1024];
MSGSUB_SKILL_ACTION_INSTANT_RESULT_BRD& msg_brd = \
*reinterpret_cast<MSGSUB_SKILL_ACTION_INSTANT_RESULT_BRD*>(variable_buffer);
msg_brd.Clear();
;{
msg_brd.result.m_SkillCode = skill_info_data_.skill_code_;
msg_brd.result.m_dwClientSerial = skill_info_data_.client_serial_;
msg_brd.result.m_dwAttackerKey = owner_->GetObjectKey();
msg_brd.result.m_dwPrimaryTargetKey = skill_info_data_.main_target_key_;
msg_brd.result.m_wvMainTargetPos = skill_info_data_.main_target_pos_;
msg_brd.result.m_wvCurPos = skill_info_data_.cur_pos_;
msg_brd.result.m_wvDestPos = skill_info_data_.dest_pos_;
msg_brd.result.m_dwAttackerHP = owner_->GetHP();
msg_brd.result.m_dwAttackerMP = owner_->GetMP();
//msg_brd.result.m_byNumberOfTargets = number_of_hit_targets_;
//msg_brd.result.m_byNumberOfFieldEffect = number_of_effects_;
};
// DONE: need check 'number_of_hit_targets_' and 'number_of_effects_' boundaries
uint8_t* offset_it = variable_buffer + sizeof(msg_brd);
int additional_size = 0;
if (RESULTMSG* const end = &result_msg_array_[number_of_hit_targets_])
{
RESULTMSG* it = result_msg_array_;
for ( ; it != end; ++it)
{
const int size = it->m_wSize;
if (offset_it + size < &variable_buffer[_countof(variable_buffer)]) {
CopyMemory(offset_it, it->m_byMsg, size);
offset_it += size;
additional_size += size;
}
else {
SUNLOG(eCRITICAL_LOG,
_T("|["__FUNCTION__"]|additional target information size is too big"));
number_of_hit_targets_ = static_cast<uint8_t>(it - result_msg_array_);
break;
}
};
};
//
if (RESULTMSG* const end = &result_effect_msg_array_[number_of_effects_])
{
RESULTMSG* it = result_effect_msg_array_;
for ( ; it != end; ++it)
{
const int size = it->m_wSize;
if (offset_it + size < &variable_buffer[_countof(variable_buffer)]) {
CopyMemory(offset_it, it->m_byMsg, size);
offset_it += size;
additional_size += size;
}
else {
SUNLOG(eCRITICAL_LOG,
_T("|["__FUNCTION__"]|additional effect information size is too big"));
number_of_effects_ = static_cast<uint8_t>(it - result_effect_msg_array_);
break;
}
};
};
//-------------------------------------------------------------
;{
// lazy alignment
msg_brd.result.m_byNumberOfTargets = static_cast<uint8_t>(number_of_hit_targets_);
msg_brd.result.m_byNumberOfFieldEffect = static_cast<uint8_t>(number_of_effects_);
//
msg_brd.sizeof_additions = static_cast<uint16_t>(additional_size);
};
//
#ifdef _NA_008804_20160502_COMBAT_LOG
time_t t;
util::TimeSync::time(&t);
msg_brd.m_SvrTime = t;
#endif //(_NA_008804_20160502_COMBAT_LOG)
owner_->SendPacketAroundForSync(&msg_brd);
//
}
void
Skill::Broadcast_Delayed_Result()
{
if (game_field_ == 0)
{
SUNLOG(eCRITICAL_LOG, "[Skill::Broadcast_Delayed_Result] GameField is NULL!");
return;
}
uint8_t variable_buffer[12 * 1024];
MSGSUB_SKILL_ACTION_DELAY_RESULT_BRD& msg_brd = \
*reinterpret_cast<MSGSUB_SKILL_ACTION_DELAY_RESULT_BRD*>(variable_buffer);
msg_brd.Clear();
;{
msg_brd.result.m_SkillCode = skill_info_data_.skill_code_;
msg_brd.result.m_dwClientSerial = skill_info_data_.client_serial_;
msg_brd.result.m_dwAttackerKey = owner_->GetObjectKey();
msg_brd.result.m_dwPrimaryTargetKey = skill_info_data_.main_target_key_;
msg_brd.result.m_wvMainTargetPos = skill_info_data_.main_target_pos_;
msg_brd.result.m_dwAttackerHP = owner_->GetHP();
msg_brd.result.m_dwAttackerMP = owner_->GetMP();
//msg_brd.m_byNumberOfTargets = number_of_hit_targets_;
//msg_brd.m_byNumberOfFieldEffect = number_of_effects_;
};
// DONE: need check 'number_of_hit_targets_' and 'number_of_effects_' boundaries
uint8_t* offset_it = variable_buffer + sizeof(msg_brd);
int additional_size = 0;
if (RESULTMSG* const end = &result_msg_array_[number_of_hit_targets_])
{
RESULTMSG* it = result_msg_array_;
for ( ; it != end; ++it)
{
const int size = it->m_wSize;
if (offset_it + size < &variable_buffer[_countof(variable_buffer)]) {
CopyMemory(offset_it, it->m_byMsg, size);
offset_it += size;
additional_size += size;
}
else {
SUNLOG(eCRITICAL_LOG,
_T("|["__FUNCTION__"]|additional target information size is too big"));
number_of_hit_targets_ = static_cast<uint8_t>(it - result_msg_array_);
break;
}
};
};
//
if (RESULTMSG* const end = &result_effect_msg_array_[number_of_effects_])
{
RESULTMSG* it = result_effect_msg_array_;
for ( ; it != end; ++it)
{
const int size = it->m_wSize;
if (offset_it + size < &variable_buffer[_countof(variable_buffer)]) {
CopyMemory(offset_it, it->m_byMsg, size);
offset_it += size;
additional_size += size;
}
else {
SUNLOG(eCRITICAL_LOG,
_T("|["__FUNCTION__"]|additional effect information size is too big"));
number_of_effects_ = static_cast<uint8_t>(it - result_effect_msg_array_);
break;
}
};
};
//-------------------------------------------------------------
;{
// lazy alignment
msg_brd.result.m_byNumberOfTargets = static_cast<uint8_t>(number_of_hit_targets_);
msg_brd.result.m_byNumberOfFieldEffect = static_cast<uint8_t>(number_of_effects_);
//
msg_brd.sizeof_additions = static_cast<uint16_t>(additional_size);
};
//
#ifdef _NA_008804_20160502_COMBAT_LOG
time_t t;
util::TimeSync::time(&t);
msg_brd.m_SvrTime = t;
#endif //(_NA_008804_20160502_COMBAT_LOG)
owner_->SendPacketAroundForSync(&msg_brd);
}
void
Skill::SendAIMessage(Character* pTarget, DAMAGETYPE wDamage)
{
// 공격당한 NPC에게 AIMessage를 날린다.
AI_MSG_ATTACKED AIAttackedMsg;
AIAttackedMsg.dwAttackerKey = owner_->GetObjectKey();
AIAttackedMsg.wDamage = wDamage;
pTarget->SendAIMessage(&AIAttackedMsg, sizeof(AIAttackedMsg));
// NPC가 공중으로 뜨는 스킬일 경우 Flying Message를 날린다.
if (skill_attribution_ & SKILL_ATTRIBUTION_TARGETFLYING)
{
AI_MSG_FLYING flyMsg;
flyMsg.dwFlyingTick = GetExecuteInterval();
pTarget->SendAIMessage(&flyMsg, sizeof(flyMsg));
}
// 타겟을 멈춰놓는 스킬일 경우
if (skill_attribution_ & SKILL_ATTRIBUTION_TARGETSTOP)
{
AI_MSG_KNOCKDOWN downMsg;
downMsg.dwKnockDownTick = GetExecuteInterval();
pTarget->SendAIMessage(&downMsg, sizeof(downMsg));
}
}
#ifdef _NA_008588_20151202_MODIFY_MOVEING_SKILL_LOGIC
VOID Skill::_ProcessSkillMove()
{
// 스킬사용 후 이동처리 (돌진형 스킬들)
const BASE_ABILITYINFO* dmg_ability_info = base_skill_info_->GetAbilityInfo(eABILITY_DAMAGE);
Character* main_target = game_field_->FindCharacter(skill_info_data_.main_target_key_);
// 1번어빌리티가 없으면 그냥 넘어간다
if (dmg_ability_info != NULL && main_target != NULL)
{
//파라미터3의 값이 1이면 이동처리를 한다
bool is_moving_damage = (dmg_ability_info->m_iParam[2] == 1);
if (is_moving_damage)
{
const WzVector& attacker_pos = *owner_->GetPosPtr();
const WzVector& target_pos = *(main_target)->GetPosPtr();
WzVector distance = target_pos;
distance -= attacker_pos;
WzVector direction; // nomalized vector
VectorNormalize(&direction, &distance);
WzVector offset = direction;
offset *= 2.0f;
WzVector destination;
if (base_skill_info_ && base_skill_info_->m_byAttRangeform == SRF_PIERCE)
{
const ns_object::SearchOption::SkillBase& search_option = \
*ns_object::SearchOption::SkillBase::CreateInstance(this);
destination = *search_option.arguments.application_center_pos;
}
else
{
destination = attacker_pos;
destination += (distance - offset);
}
const WORD kFilteredAttr = (PTA_NO_WALK);
GameField* const game_field = owner_->GetField();
if (game_field == NULL) {
SUNLOG(eCRITICAL_LOG, "["__FUNCTION__"] Cannot Find Attacker's GameField Using Skill");
return;
}
nsAI::MovingPlanner* moving_planner = game_field->GetMovingPlanner();
if (moving_planner->PathPlan_FastMoved(owner_, destination, kFilteredAttr, &destination) != false)
{
if (owner_->IsEqualObjectKind(PLAYER_OBJECT))
{
static_cast<Player*>(owner_)->GetSyncTuner()->OnFastMoved(destination);
};
};
// changes attacker position
owner_->SetPos(&destination);
};
}
}
#endif //_NA_008588_20151202_MODIFY_MOVEING_SKILL_LOGIC