#include "stdafx.h" #include "GameZone.h" #include "GameField.h" #include #include "World/HighLoadBalancer.h" #include "FieldInfo.h" #include "Field.h" #include "Sector.h" //#include "View.h" #include "MovementChecker.h" #include "TargetFinder.h" #include "Status/StatusManager.h" #include "ItemManager.h" #include "Item.h" #ifdef _NA_007667_20141001_WITCHBLADE_SKILL #include "Summoned.h" #endif //_NA_007667_20141001_WITCHBLADE_SKILL //================================================================================================== // @history // 2008.08.22, waverix, created // 2009.11.xx~2010.04.22, lst1024, worked related to domination // 2011.02.23, waverix, applied about the enhanced target search algorithm // 2011.05.31, waverix, remove the broadcast upper boundary limitation using 1toN broadcaster. // f110531.4L, this module was defined that current version grater than 0906 //-------------------------------------------------------------------------------------------------- void GameField::SendExPacket(Player* const receiver, const DWORD number_of_msgs, BYTE** const IN msg_array, WORD* const msg_size_array) { GameZone* const game_zone = (receiver == NULL) ? NULL : receiver->GetGameZonePtr(); if (game_zone == NULL) { return; }; // NOTE: f110531.4L m_pHLB->AppendTimeSyncMsgArray(number_of_msgs, msg_array, msg_size_array); //receiver->GetServerSession()->SendEx(number_of_msgs, msg_array, msg_size_array); } //-------------------------------------------------------------------------------------------------- void GameField::SendExPacketAround(const SECTOR_IDX coordinates, const DWORD number_of_msgs, BYTE** const IN msg_array, WORD* const msg_size_array, const DWORD except_object_key) { SectorGrid3x3 OUT grid; if (m_SectorGrid.GetAroundSectors(coordinates, &grid) == false) { return; }; const bool check_skip_object = !except_object_key; MSG_AG_CONNECTION_PACKET_BROADCASTER_ONEtoN_SYN& msg_brd = MSG_ONEtoN_BROADCASTER_INTERNAL::InstanceOfBase(); msg_brd.InitByArray(msg_array, msg_size_array, number_of_msgs); uint16_t* const user_begin = msg_brd.GetUsersStartPtr(); // Center¸¦ ¸ÕÀú ó¸®Çϸ鼭 ÁÖº¯ ¼½ÅÍ Ã³¸® GRID3x3_CENTER_PRIOR_SWAP(grid); FOREACH_ARRAY(const SECTOR_IDX sector_idx, grid.Grid, SectorGrid3x3::GridArray) { if (sector_idx == INVALID_SECTOR_INDEX) { continue; }; Sector* const sector = m_SectorGrid.GetSector(sector_idx); if (sector->GetPlayerNum() == 0) { continue; }; ns_object::SearchNodeConstIterator it = sector->GetPlayerRoot(); const ns_object::SearchNodeConstIterator end = sector->GetPlayerEnd(); while (const ns_object::SearchNode::LinkNode* link_node = it.ClimbToNext(end)) { Player* player = link_node->select.player_node.player; if (obj_util::IsValid(player) == false) { continue; } const DWORD object_key = player->GetObjectKey(); if (!(check_skip_object || (object_key != except_object_key))) { continue; }; DWORD bound_check = msg_brd.AddUser(user_begin, object_key); if (bound_check == msg_brd.MAX_USER_COUNT) { break; }; } }; if (msg_brd.number_of_users) { // NOTE: f110531.4L m_pHLB->AppendTimeSyncMsg(&msg_brd, msg_brd.GetSendingSize()); //g_pGameServer->SendToServer(AGENT_SERVER, &msg_brd, msg_brd.GetSendingSize()); return; } } //================================================================================================== //_NA000000_110210_ENHANCED_TARGET_SEARCH_ALGORITHM //-------------------------------------------------------------------------------------------------- // CHANGES: f110308.1L, since version 1102, various resolution list had not // flexible broadcasting anymore. so that the below logic related to surroundings dynamic calculator // is more simple that is applied an optimization algorithm for sector searching. const SectorGridVR* GameField::GetSectorGridVR(const SECTOR_IDX coordinates, const float radius) const { Sector* const sector = m_SectorGrid.GetSector(coordinates); if (sector == NULL) { return NULL; }; typedef FieldInfo::Resolutions::array_type array_type; const array_type& resolutions = m_pFieldInfo->SectorGridVResolutions; const DWORD diameter = static_cast(radius * 2.f); const SectorGridVR* sector_grid_vr = &resolutions[FieldInfo::VRES_100_PERCENT]; if (false) {} // NOTE: f110308.1L, added new resolution to use more closed ranges else if (diameter <= resolutions[FieldInfo::VRES_800_PERCENT].SectorXYDistance * 3) { sector_grid_vr = &resolutions[FieldInfo::VRES_800_PERCENT]; } else if (diameter <= resolutions[FieldInfo::VRES_400_PERCENT].SectorXYDistance * 3) { sector_grid_vr = &resolutions[FieldInfo::VRES_400_PERCENT]; } else if (diameter <= resolutions[FieldInfo::VRES_200_PERCENT].SectorXYDistance * 3) { sector_grid_vr = &resolutions[FieldInfo::VRES_200_PERCENT]; } return sector_grid_vr; } //================================================================================================== // private method void GameField::SendPacketAroundForSync_(const SECTOR_IDX coordinates, const BYTE _TAG, WAR_TAG_V* const IN war_msg, const WORD war_msg_size, BOOLEAN post_action) { __UNUSED(post_action); if (coordinates == INVALID_SECTOR_INDEX) { return; }; if (m_PlayerHash.empty()) { return; }; // SectorGrid3x3 OUT grid; if (!m_SectorGrid.GetAroundSectors(coordinates, &grid)) { return; } // NOTE: f110531.4L, set to default ''ENABLE_FIELD_HIGH_LOAD_BALANCING // redesigned by _NA001605_EP2_CHANGE_SKILL_BROADCASTER_ WAR_MSG_HDR* const header = m_pHLB->GetHeader(); WAR_TAG_V* war_msg_in_buffer = NULL; // assertion(header_size != 0) ;{ // prepare message WAR_MSG_HDR::MSG_SIZE_T offset = war_msg_size + header->size; if (HighLoadBalancer::kMaxRegionStreamSize <= (sizeof(nsGrid::SYNC_MSG_LINKEDLIST) * 9 + offset)) { HighLoadBalancer::Instance()->Update(this, true); offset = war_msg_size + header->size; }; war_msg_in_buffer = reinterpret_cast( reinterpret_cast(header + 1) + header->size); CopyMemory(war_msg_in_buffer, war_msg, war_msg_size); war_msg_in_buffer->_TAG = _TAG; header->size = offset; }; // assert(war_msg_in_buffer != NULL); FOREACH_ARRAY(const SECTOR_IDX sector_idx, grid.Grid, SectorGrid3x3::GridArray) { if (sector_idx == INVALID_SECTOR_INDEX) { continue; } Sector* const sector = m_SectorGrid.AddSectorMsgNode(\ header, sector_idx, war_msg_in_buffer, war_msg_size); __TOUCHED(sector); // for debug }; } //-------------------------------------------------------------------------------------------------- void GameField::SendExPacketToSector(const SECTOR_IDX sector_idx, const DWORD number_of_msgs, BYTE** const IN msg_array, WORD* const msg_size_array, const DWORD except_object_key) { if (sector_idx == INVALID_SECTOR_INDEX) return; Sector* const sector = m_SectorGrid.GetSector(sector_idx); if (sector->GetPlayerNum() == 0) return; const bool check_skip_object = !except_object_key; MSG_AG_CONNECTION_PACKET_BROADCASTER_ONEtoN_SYN& msg_brd = MSG_ONEtoN_BROADCASTER_INTERNAL::InstanceOfBase(); msg_brd.InitByArray(msg_array, msg_size_array, number_of_msgs); uint16_t* const user_begin = msg_brd.GetUsersStartPtr(); ns_object::SearchNodeConstIterator it = sector->GetPlayerRoot(); const ns_object::SearchNodeConstIterator end = sector->GetPlayerEnd(); while (const ns_object::SearchNode::LinkNode* link_node = it.ClimbToNext(end)) { Player* player = link_node->select.player_node.player; if (obj_util::IsValid(player) == false) { continue; } const DWORD object_key = player->GetObjectKey(); if (!(check_skip_object || (object_key != except_object_key))) continue; DWORD bound_check = msg_brd.AddUser(user_begin, object_key); if (bound_check == msg_brd.MAX_USER_COUNT) { break; }; } if (msg_brd.number_of_users) { // NOTE: f110531.4L m_pHLB->AppendTimeSyncMsg(&msg_brd, msg_brd.GetSendingSize()); //g_pGameServer->SendToServer(AGENT_SERVER, &msg_brd, msg_brd.GetSendingSize()); return; } } /* void View::SendExPacketAround(const DWORD number_of_msgs, BYTE** const IN msg_array, WORD* const msg_size_array, const BOOLEAN bSendToMe, const GUILDGUID other_guid_guid) { GameField* const pGameField = m_pOwner->GetField(); if (!pGameField) return; MSG_AG_CONNECTION_PACKET_BROADCASTER_ONEtoN_SYN& msg_brd = MSG_ONEtoN_BROADCASTER_INTERNAL::InstanceOfBase(); msg_brd.InitByArray(msg_array, msg_size_array, number_of_msgs); DWORD* const user_begin = msg_brd.GetUsersStartPtr(); DWORD bound_check = msg_brd.MAX_USER_COUNT; if (bSendToMe) msg_brd.AddUser(user_begin, m_pOwner->GetObjectKey()); FOREACH_CONTAINER(const view_hash::value_type& rNode, m_ReferedList, view_hash) { Player* const pReferedPlayer = rNode.second; bound_check = msg_brd.AddUser(user_begin, pReferedPlayer->GetObjectKey()); if (bound_check == msg_brd.MAX_USER_COUNT) { m_pHLB->AppendTimeSyncMsg(&msg_brd, msg_brd.GetSendingSize()); msg_brd.InitByArray(msg_array, msg_size_array, number_of_msgs); }; } if (msg_brd.m_byUserCount) { pGameField->SendPacket(m_pOwner, &msg_brd, msg_brd.GetSendingSize()); // GameField¸¦ ÀÌ¿ëÇÑ´Ù. } } */ //implemented by __NA001283_20090225_COLLECTION_USE // (CHANGES) (f100504.1L) change the interface specification to support a complex condition // the replacement facility for SendXXPacketToSector void GameField::SendExPacketToSector(const SECTOR_IDX sector_idx, const DWORD number_of_msgs, BYTE** const IN msg_array, WORD* const msg_size_array, IObjectFinder* finder) { if (sector_idx == INVALID_SECTOR_INDEX) { return; } Sector* const sector = m_SectorGrid.GetSector(sector_idx); if (sector->GetPlayerNum() == 0) { return; } MSG_AG_CONNECTION_PACKET_BROADCASTER_ONEtoN_SYN& msg_brd = \ MSG_ONEtoN_BROADCASTER_INTERNAL::InstanceOfBase(); msg_brd.InitByArray(msg_array, msg_size_array, number_of_msgs); uint16_t* const player_key_begin = msg_brd.GetUsersStartPtr(); // ns_object::SearchNodeConstIterator it = sector->GetPlayerRoot(); const ns_object::SearchNodeConstIterator end = sector->GetPlayerEnd(); while (const ns_object::SearchNode::LinkNode* link_node = it.ClimbToNext(end)) { Player* player = link_node->select.player_node.player; if (obj_util::IsValid(player) == false) { continue; } if (false == finder->operator()(player)) { continue; } //SUNLOG(eDEV_LOG, "[Collect Ȱ¼ºÈ­ Á¤º¸ º¸³¿] ObjectKey[%d], Name[%s]", // object_key, player->GetCharName()); const DWORD object_key = player->GetObjectKey(); DWORD bound_check = msg_brd.AddUser(player_key_begin, object_key); if (bound_check == msg_brd.MAX_USER_COUNT) { break; }; } if (msg_brd.number_of_users) { // NOTE: f110531.4L m_pHLB->AppendTimeSyncMsg(&msg_brd, msg_brd.GetSendingSize()); //g_pGameServer->SendToServer(AGENT_SERVER, &msg_brd, msg_brd.GetSendingSize()); return; } } //-------------------------------------------------------------------------------------------------- Character* GameField::SearchTarget(const TargetFinderArg& find_arg) { TargetFinder* target_finder = NULL; #define FINDER_STATIC_MARK(name) target_finder = new (stream) name(find_arg); BYTE stream[max(sizeof(LowHPRatioTargetFinder), max(sizeof(LowHPTargetFinder), max(sizeof(LowLevelTargetFinder), max(sizeof(HighMPTargetFinder), max(sizeof(DeadTargetFinder), max(sizeof(NearestTargetFinder), max(sizeof(UnitIDTargetFinder), max(sizeof(MapNpcObjectTargetFinder), max(sizeof(NearestMonsterCodeTargetFinder), 0))))))))) ]; Character* const owner = find_arg.owner_; switch (find_arg.search_type_) { case eRARGET_NOT_SEARCH: case eRARGET_SEARCH_NOT_SEARCH_AND_TARGET_CHNAGE: //_NA001385_20090924_DOMINATION_MAPNPC_AI_BUG return NULL; case eRARGET_SEARCH_NONTARGET_LOW_HP: if (owner->GetTargetChar()) return NULL; else FINDER_STATIC_MARK(LowHPRatioTargetFinder); break; case eRARGET_SEARCH_LOW_HPRATIO: FINDER_STATIC_MARK(LowHPRatioTargetFinder); break; case eRARGET_SEARCH_LOW_HP: FINDER_STATIC_MARK(LowHPTargetFinder); break; case eRARGET_SEARCH_LOW_LEVEL: FINDER_STATIC_MARK(LowLevelTargetFinder); break; case eRARGET_SEARCH_HIGH_MP: FINDER_STATIC_MARK(HighMPTargetFinder); break; case eRARGET_SEARCH_CORPSE: FINDER_STATIC_MARK(DeadTargetFinder); break; case eRARGET_SEARCH_UNITID: FINDER_STATIC_MARK(UnitIDTargetFinder); break; case eRARGET_SEARCH_MAPOBJECTID: FINDER_STATIC_MARK(MapNpcObjectTargetFinder); break; case eRARGET_SEARCH_MONSTERCODE: FINDER_STATIC_MARK(NearestMonsterCodeTargetFinder); break; default: FINDER_STATIC_MARK(NearestTargetFinder); break; } const float sight_range = owner->GetSightRange(); const SECTOR_IDX coordinates = owner->GetSectorIndex(); const SectorGridVR* const sector_grid_vr = GetSectorGridVR(coordinates, sight_range); const WzVector* const finder_pos = owner->GetPosPtr(); SectorGrid3x3 OUT grid; ViewBoxCubic sector_boundary; if (sector_grid_vr->GetAroundSectors(finder_pos, &grid, §or_boundary) == false) { return NULL; }; const WzVector sight_range_vector = { sight_range, sight_range, (sight_range < 10.f) ? 5.f : (sight_range / 2) }; ViewBoxCubic search_boundary; search_boundary.SetInit3D(finder_pos, &sight_range_vector); Character* found_satisfied_char = NULL; // Center¸¦ ¸ÕÀú ó¸®Çϸ鼭 ÁÖº¯ ¼½ÅÍ Ã³¸® GRID3x3_CENTER_PRIOR_SWAP(grid); FOREACH_ARRAY(const SECTOR_IDX sector_idx, grid.Grid, SectorGrid3x3::GridArray) { if (sector_idx == INVALID_SECTOR_INDEX) { continue; }; Sector* const sector = m_SectorGrid.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)) { Character* const character = link_node->select.character_node.character; if (obj_util::IsValid(character) == false) { continue; } if (character == owner) { continue; }; const WzVector* const target_pos = character->GetPosPtr(); if (!sector_boundary.IsValidCoordinateXY(target_pos)) { continue; }; if (!search_boundary.IsValidCoordinateXYZ(target_pos)) { continue; }; const StatusField& status_field = character->GetStatusManager()->Status; const StatusField::POLICY_T kSkipStatusField = status_field.ENABLE_OBSERVER_MODE | status_field.ENABLE_CHARACTER_TRANSPARENT; if (bool skipped = (status_field.FIELD & kSkipStatusField) != 0) { continue; }; //_NA_0_20100222_UNIT_TRIGGERS_FRIEND_MONSTER switch (find_arg.target_type_) { case SKILL_TARGET_ENEMY: case SKILL_TARGET_AREA: case SKILL_TARGET_REACHABLE_ENEMY: case SKILL_TARGET_ENEMY_PLAYER: case SKILL_TARGET_ENEMY_AND_ME: { const bool preemptived = !(status_field.FIELD & status_field.ENABLE_NON_PREEMPTIVE_ATTACK); if (false == preemptived && false == owner->IsEqualObjectKind(SUMMON_OBJECT) && false == owner->IsEqualObjectKind(SSQMONSTER_OBJECT)) { continue; } } break; }; #ifdef _NA_007667_20141001_WITCHBLADE_SKILL if (character->IsEqualObjectKind(SUMMON_OBJECT) == true && character->GetSummonType() == eSUMMON_IMMOVABLE) { continue; } #endif //_NA_007667_20141001_WITCHBLADE_SKILL if (bool stopped = ((*target_finder)(character)) != false) { found_satisfied_char = target_finder->GetTarget(); // for debug return found_satisfied_char; }; }; //end 'foreach char in sector' }; //end 'foreach sector in sectors' // found_satisfied_char = target_finder->GetTarget(); return found_satisfied_char; } //-------------------------------------------------------------------------------------------------- //#ifdef _NA000000_110210_ENHANCED_TARGET_SEARCH_ALGORITHM // returns : whether selected targets are exist. bool GameField::FindTargetsForNpcAI( const ns_object::SearchOption::NpcAI& search_option, ns_object::SearchOption::NpcAI::result_type* const search_result) { //_NA000000_110210_ENHANCED_TARGET_SEARCH_ALGORITHM using namespace ns_object; // const SECTOR_IDX coordinates = search_option.actor->GetSectorIndex(); const nsGrid::SectorGridVR* sector_grid_vr = 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, (is_war_castle_ ? 30.0f : 15.0f)) == false) { return false; }; // ;{ ZeroMemory(search_result, sizeof(*search_result)); }; #ifdef __WAVERIX_TEST_LOGIC_TASK_MACRO__ struct WxAutoClosure { WxAutoClosure(ns_object::SearchOption::NpcAI::result_type* input_search_result) : search_result(input_search_result) {} ~WxAutoClosure() { WAVERIX_WRAPPER((WAVERIX_LC_CALL1("waverix_log_npc_ai_search", search_result);)) }; ns_object::SearchOption::NpcAI::result_type* const search_result; } wx_closure(search_result); #endif const int& number_of_selected_chars = search_result->number_of_selected_chars; //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 (sector_idx == INVALID_SECTOR_INDEX) { continue; }; if (_countof(search_result->targets) <= number_of_selected_chars) { return true; }; Sector* const sector = m_SectorGrid.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)) { Character* const target = link_node->select.character_node.character; if (obj_util::IsValid(target) == false) { continue; } if (search_option.actor == target) { continue; }; // shared block if (search_option.IsSatisfiedObjectFindingConditions( &damage_box, search_option.actor, target) == false) { continue; } if (search_option.IsSatisfiedObjectIFFRelations( search_option.actor, target, NULL, NULL) == false) { continue; } if (search_option.AppendToResultIfIsSatisfiedObject( search_option.actor, target, search_result) == search_option.eStop) { return true; } } }; // return true; } bool GameField::FindTargetsUsingSingleSearch( const ns_object::SearchOption::SingleSearch& search_option, ns_object::SearchOption::SingleSearch::result_type* const search_result) { //_NA000000_110210_ENHANCED_TARGET_SEARCH_ALGORITHM using namespace ns_object; // NOTE: f110527.4L, set default value to prevent caller don't control this result. search_result->number_of_selected_chars = 0; // const SECTOR_IDX coordinates = search_option.actor->GetSectorIndex(); const nsGrid::SectorGridVR* sector_grid_vr = 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, (is_war_castle_ ? 30.0f : 15.0f)) == false) { return false; }; // if (search_option.PrefetchResult(search_result) == false) { return false; }; #ifdef __WAVERIX_TEST_LOGIC_TASK_MACRO__ struct WxAutoClosure { WxAutoClosure(ns_object::SearchOption::SingleSearch::result_type* input_search_result) : search_result(input_search_result) {} ~WxAutoClosure() { WAVERIX_WRAPPER((WAVERIX_LC_CALL1("waverix_log_singlesearch", search_result);)) }; ns_object::SearchOption::SingleSearch::result_type* const search_result; } wx_closure(search_result); #endif // const int& number_of_selected_chars = search_result->number_of_selected_chars; //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 (sector_idx == INVALID_SECTOR_INDEX) { continue; }; if (_countof(search_result->targets) <= number_of_selected_chars) { return true; }; Sector* const sector = m_SectorGrid.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)) { Character* const target = link_node->select.character_node.character; if (obj_util::IsValid(target) == false) { continue; } if (search_option.actor == target) { continue; } // shared block if (search_option.IsSatisfiedObjectFindingConditions( &damage_box, search_option.actor, target) == false) { continue; } if (search_option.IsSatisfiedObjectIFFRelations( search_option.actor, target, NULL, NULL) == false) { continue; } if (search_option.AppendToResultIfIsSatisfiedObject( search_option.actor, target, search_result) == search_option.eStop) { return true; } } }; // return true; } //-------------------------------------------------------------------------------------------------- void GameField::SendAIMessageAroundExceptMe(NPC* const sender, AI_MSG* const ai_msg, const WORD ai_msg_size, const DWORD delay) { __UNUSED(delay); // TODO: damduk, ³ªÁß¿¡´Â »ç¿ëÇÒ ¼ö ÀÖµµ·Ï ÇÏÀÚ. if (sender == NULL) { return; }; const SECTOR_IDX coordinates = sender->GetSectorIndex(); SectorGrid3x3 OUT grid; if (m_SectorGrid.GetAroundSectors(coordinates, &grid) == false) { return; }; // Center¸¦ ¸ÕÀú ó¸®Çϸ鼭 ÁÖº¯ ¼½ÅÍ Ã³¸® GRID3x3_CENTER_PRIOR_SWAP(grid); FOREACH_ARRAY(const SECTOR_IDX sector_idx, grid.Grid, SectorGrid3x3::GridArray) { if (sector_idx == INVALID_SECTOR_INDEX) { continue; }; Sector* const sector = m_SectorGrid.GetSector(sector_idx); ns_object::SearchNodeConstIterator it = sector->GetNpcRoot(); const ns_object::SearchNodeConstIterator end = sector->GetNpcEnd(); while (const ns_object::SearchNode::LinkNode* link_node = it.ClimbToNext(end)) { NPC* npc = link_node->select.npc_node.npc; if (obj_util::IsValid(npc) == false) { continue; } if (sender != npc) { npc->SendAIMessage(ai_msg, ai_msg_size); }; } }; return; } //-------------------------------------------------------------------------------------------------- void GameField::SendExPacketToAll(const DWORD number_of_msgs, BYTE** const IN msg_array, WORD* const msg_size_array, const DWORD except_object_key) { MSG_AG_CONNECTION_PACKET_BROADCASTER_ONEtoN_SYN& msg_brd = MSG_ONEtoN_BROADCASTER_INTERNAL::InstanceOfBase(); msg_brd.InitByArray(msg_array, msg_size_array, number_of_msgs); uint16_t* const user_begin = msg_brd.GetUsersStartPtr(); const bool check_skip_object = !except_object_key; FOREACH_CONTAINER(const _PLAYER_HASH::value_type& node, m_PlayerHash, _PLAYER_HASH) { Player* const player = node.second; const DWORD object_key = player->GetObjectKey(); if (!(check_skip_object || (object_key != except_object_key))) { continue; }; DWORD bound_check = msg_brd.AddUser(user_begin, object_key); if (bound_check == msg_brd.MAX_USER_COUNT) { break; }; }; if (msg_brd.number_of_users) { // NOTE: f110531.4L m_pHLB->AppendTimeSyncMsg(&msg_brd, msg_brd.GetSendingSize()); //g_pGameServer->SendToServer(AGENT_SERVER, &msg_brd, msg_brd.GetSendingSize()); }; } //-------------------------------------------------------------------------------------------------- // __NA000000_090716_HIGH_LOAD_BALANCED_NETWORK_TUNEUP__ // (WAVERIX) (090716) (ADD-IN) for High-load balanced network support void GameField::SendExPacketToAgent(const DWORD number_of_msgs, BYTE** const IN msg_array, WORD* const msg_size_array) { // NOTE: f110531.4L m_pHLB->AppendTimeSyncMsgArray(number_of_msgs, msg_array, msg_size_array); } //-------------------------------------------------------------------------------------------------- BOOL GameField::TeleportObject(Character* const character, const WzVector* dest_pos, const BOOLEAN forced) { if (WzVector_FiniteCheck(dest_pos) == false) { return false; }; if (IsMovableCoordinates(dest_pos) == false) { return false; }; const SECTOR_IDX prev_sector_index = character->GetSectorIndex(); character->SetPos(dest_pos); // f090218.x, waverix, fixed. 'À߸øµÈ ÁÂÇ¥ ¼³Á¤ ¹®Á¦ ¼öÁ¤' dest_pos = character->GetPosPtr(); const StatusField& status_field = character->GetStatusManager()->Status; const bool send_to_actor_only = !!(status_field.FIELD & status_field.ENABLE_OBSERVER_MODE); const bool is_player = character->IsEqualObjectKind(PLAYER_OBJECT); if (is_player) { Player* const player = static_cast(character); player->GetMovementChecker()->Reset(); #ifdef _NA000000_SPEED_HACK_PREVENTION_SUPPORT player->GetSyncTuner()->OnWarp(); #endif } MSG_CG_MAP_TELEPORT_BRD msg_brd; msg_brd.m_dwObjectKey = character->GetObjectKey(); msg_brd.vPos = *dest_pos; msg_brd.m_bForcedCommand = forced; if (send_to_actor_only) { character->SendPacket(&msg_brd, sizeof(msg_brd)); } else { SendPacketAround(prev_sector_index, &msg_brd, sizeof(msg_brd)); }; const SECTOR_IDX latest_sector_index = GetSectorIndex(dest_pos); CheckAndApplyChangeSector(character, latest_sector_index); // CHANGES: f110926.1L, if (is_player && (prev_sector_index != latest_sector_index)) { static_cast(character)->NotifyChangedStatus(PlayerStatEvent::eFlag_ChangedPos); }; return true; } //-------------------------------------------------------------------------------------------------- RC::eSYNC_RESULT GameField::JumpObject(Character* const player, const WzVector* const dest_pos) { if (WzVector_FiniteCheck(dest_pos) == false) { return RC::RC_SYNC_DEST_POSITION_IS_INVALID; } // (WAVERIX)(080817) add-check if (IsMovableCoordinates(dest_pos) == false) { return RC::RC_SYNC_DEST_POSITION_IS_INVALID; } const SECTOR_IDX prev_sector_index = player->GetSectorIndex(); // (CHANGE)(20100413)(hukim) // ÆòÁö¿¡¼­ Á¡ÇÁ¶óµµ ¾ð´ö °°Àº °æ¿ì ±â¿ï±â¸¦ °í·Á ³ôÀÌÂ÷¸¦ Çã¿ëÇϰí // °æ·Î Ž»ö±â¸¦ »ç¿ëÇÏ¿© Á¡ÇÁÇÒ °æ·Î¸¦ ãÀ» ¼ö ÀÖÀ¸¸é const WzVector* const cur_pos = player->GetPosPtr(); WzVector new_dest_pos = *dest_pos; const float height_diff = fabs(cur_pos->z - new_dest_pos.z); const float kMaxHeightTolerance = 3.0f; bool is_success_set_path = false; if ((is_success_set_path = (height_diff < kMaxHeightTolerance)) == true && (is_success_set_path = FindPath(player, &new_dest_pos) != false) == true) { // (CHANGE)(20100412)(hukim) // °æ·Î Ž»ö±â¸¦ »ç¿ëÇØ¼­ °æ·Î¸¦ ¼³Á¤ÇÑ´Ù. // ¼ø°£À̵¿ ó·³ Áï½Ã ¸ñÀûÁö·Î ÇöÀç À§Ä¡¸¦ ¼³Á¤ÇÏ´Â °ÍÀÌ ¾Æ´Ï±â ¶§¹®¿¡ // ¼½ÅÍ º¯°æ 󸮴 Ç÷¹ÀÌ¾î »óÅ °»½Å Áֱ⠵¿¾È ó¸®ÇÑ´Ù. int jump_tile_route_list[_MAX_PATHROUTE_SIZE]; CPathExplorer* const path_explorer = player->GetPathExplorer(); const int number_of_jump_tile_route = path_explorer->GetTileRoute(jump_tile_route_list); // (CHANGE)(20100413)(hukim) // °æ·Î Ž»ö±â°¡ ¹ß°ßÇÑ °æ·Î ¼ö°¡ ÃÖ´ëÄ¡¸¦ ³Ñ´Â´Ù´Â °ÍÀº ´Ù¸¥ °÷À¸·Î ºùµÑ·¯¼­ À̵¿ÇÏ¿© // Á¡ÇÁ ¸ñÀûÁö¿¡ µµÂûÇÒ ¼ö ÀÖÀ½À» ÀǹÌÇÑ´Ù. ÀÌ·± °æ¿ì´Â Á¡ÇÁ·Î ¿Ã¶ó°¥ ¼ö ÀÖ´Â ³­°£À̳ª // °Ç³Ê°¥ ¼ö ÀÖ´Â Àå¾Ö¹°¿¡ ÇØ´ç µÇ¹Ç·Î ÀÌÀü ¹æ½Ä´ë·Î ¸ñÀûÁö·Î ¹Ù·Î À̵¿½Ãų Çʿ䰡 ÀÖ´Ù. enum { kMaxJumpTileRouteTolerance = 7 }; if ((is_success_set_path = (number_of_jump_tile_route < kMaxJumpTileRouteTolerance)) == true) { nsAI::MovingPlanner* const moving_planner = GetMovingPlanner(); is_success_set_path = !!moving_planner->PathPlan_SetPath(player, &new_dest_pos, jump_tile_route_list, number_of_jump_tile_route); // (CHANGE)(20100412)(hukim) // °æ·Î ¼³Á¤¿¡ ½ÇÆÐÇϸé ÀÌ·¸°Ô ó¸®ÇÏ´Â °ÍÀÌ Á¤¼®À̶ó°í »ý°¢µÇÁö¸¸ ÀÌÀü Ç÷οì¿ÍÀÇ È£È¯¼º°ú // ¹æ¾îÀû ¸ñÀûÀ¸·Î ÀÌÀü ¹æ½Ä´ë·Î ¸ñÀûÁö·Î ¹Ù·Î À̵¿ÇÑ´Ù. //if (is_success_set_path == false) //{ // return RC::RC_SYNC_SETPATH_ERROR; //} } } // (CHANGE)(20100412)(hukim) // Á¡ÇÁ·Î ¿Ã¶ó°¥ ¼ö ÀÖ´Â ³­°£À̳ª Á¡ÇÁ·Î °Ç³Ê°¥ ¼ö ÀÖ´Â Àå¾Ö¹° °°Àº °æ¿ì °æ·Î Ž»ö¿¡ ½ÇÆÐÇϱ⠶§¹®¿¡ // °æ·Î Ž»ö±â¸¦ »ç¿ëÇÑ ¹æ½ÄÀ¸·Î ½ÇÆÐÇϸé ÀÌÀü ¹æ½Ä´ë·Î ¸ñÀûÁö·Î ¼ø°£À̵¿ ó·³ ¹Ù·Î À̵¿ÇÑ´Ù. if (is_success_set_path == false) { player->SetPos(dest_pos); new_dest_pos = *(player->GetPosPtr()); // (WAVERIX) (090218) (BUG-FIX) À߸øµÈ ÁÂÇ¥ ¼³Á¤ ¹®Á¦ ¼öÁ¤ const SECTOR_IDX dest_sector = GetSectorIndex(&new_dest_pos); CheckAndApplyChangeSector(player, dest_sector); // CHANGES: f110926.1L, if (prev_sector_index != dest_sector) { static_cast(player)->NotifyChangedStatus(PlayerStatEvent::eFlag_ChangedPos); }; } return RC::RC_SYNC_SUCCESS; } //-------------------------------------------------------------------------------------------------- // to support gm command 'pickup' bool GameField::PickupAllItems(Player* gm_player, bool use_duplicable_slot_mode) { // (NOTE) skipped all events // (WARNING) must be use only for test. if (gm_player->IsGMUser() == false) { SUNLOG(eCRITICAL_LOG, _T("|ÐÅÏ¢=try request 'pick up all of items', by not GM player|UserID=%s|"), gm_player->GetUserID()); return false; } // GameField* const game_field = gm_player->GetField(); if (game_field == NULL) { return false; } const SECTOR_IDX center_index = gm_player->GetSectorIndex(); if (center_index == INVALID_SECTOR_INDEX) { return false; } // SectorGrid3x3 OUT grid; if (m_SectorGrid.GetAroundSectors(center_index, &grid) == false) { return false; } // SCItemSlot* item_slots[nsSlot::ItemQuery::kMaxNumberOfPosSlots] = { 0, }; Item* items[_countof(item_slots)] = { NULL, }; MONEY sum_of_money = 0ull; int number_of_items = 0; FOREACH_ARRAY(const SECTOR_IDX sector_idx, grid.Grid, SectorGrid3x3::GridArray) { if (number_of_items >= _countof(item_slots)) { break; } if (sector_idx == INVALID_SECTOR_INDEX) { continue; } Sector* const sector = m_SectorGrid.GetSector(sector_idx); ns_object::SearchNodeConstIterator it = sector->GetItemRoot(); const ns_object::SearchNodeConstIterator end = sector->GetItemEnd(); while (const ns_object::SearchNode::LinkNode* link_node = it.ClimbToNext(end)) { if (number_of_items >= _countof(item_slots)) { break; } Item* item = link_node->select.item_node.item; if (obj_util::IsValid(item) == false) { continue; } if (item->IsDeleteReserved()) { continue; } if (item->IsEqualObjectKind(MONEY_OBJECT)) { sum_of_money += item->GetMoney(); // (NOTE) safe operation. request delete, not delete game_field->DestroyObject(item); } else { SCItemSlot& item_in_item = item->GetItemSlot(); const nsSlot::ItemRuleInfo& item_rule_info = item_in_item.GetItemRuleInfo(); if (item_rule_info.is_valid_item != false) { items[number_of_items] = item; item_slots[number_of_items] = &item_in_item; ++number_of_items; } } } }; //end 'FOREACH' if (sum_of_money != 0) { gm_player->PlusMoney(sum_of_money); ;{ MSG_CG_ITEM_PICK_MONEY_ACK money_msg; money_msg.m_Money = gm_player->GetMoney(); money_msg.m_BonusRatio = 0; gm_player->SendPacket(&money_msg, sizeof(money_msg)); }; } if (number_of_items == 0) { return true; } const char* error_occurred = NULL; ;{ // pick up all items using nsSlot::ItemQuery; ItemManager* const item_manager = gm_player->GetItemManager(); // MSG_CG_ITEM_PICK_ACK msg_ack; ItemQuery pickup_query; if (ItemQuery::MakeQueryInverseSelect(&pickup_query, gm_player, SI_INVENTORY) == false) { return false; } SCItemSlot** item_link = item_slots; for (int index = 0; index < number_of_items && item_link != &item_slots[_countof(item_slots)]; ++index, ++item_link) { // CHANGES: f101122.2L, fixed mismatch number of items problem // on the duplicated item pickup operation SCItemSlot* item_slot = *item_link; int registered_index = \ pickup_query.AddArg_ItemSlot(*item_slot, item_slot->GetNum(), false); if (registered_index < 0) { error_occurred = "index out of ranges after the calling of the AddArg_ItemSlot"; break; } } // if (use_duplicable_slot_mode == false) { pickup_query.ChangeToFindEmptySlotOnlyMode(); } const nsSlot::ItemQueryResult result = item_manager->Query(&pickup_query); if (result.result_of_inverse_select == 0) { return true; // inventory full } // TODO(WAVERIX) need more thinking about result handling const ItemQuery::QueryInfo& query_info = pickup_query.query_info(); if (result.result_of_select != query_info.sum_of_queried_items) { //error_occurred = "result.result_of_select != query_info.sum_of_queried_items"; ; // (NOTE) (f100727.2L) can delete process whether a real acquired item } for (int i = 0; i < query_info.number_of_queries; ++i) { const nsSlot::ItemQuery::QueryInfo::Node& node = query_info.nodes[i]; if (node.number_of_items == node.sum_of_items) { // (NOTE) safe operation. request delete, not delete game_field->DestroyObject(items[i]); } } if (item_manager->ObtainAllKindOfItemByQuery(pickup_query) == false) { error_occurred = "item_manager->ObtainAllKindOfItemByQuery"; } pickup_query.MakeSerializedStream(&msg_ack.m_ItemInfo); gm_player->SendPacket(&msg_ack, msg_ack.GetSize()); pickup_query.WriteToGameLogBasedOnResult(gm_player, ITEM_PICK); }; if (error_occurred != NULL) { SUNLOG(eCRITICAL_LOG, _T("|ÐÅÏ¢=the result of the 'pick up items' gm command cause a loss of items|") _T("|Detailed=%s|"), error_occurred); }; return true; } //-------------------------------------------------------------------------------------------------- // (CHANGES) (f100503.3L) change the interface specification so that // a finder has the same of the ObjectFinder only NPC* GameField::SearchNeighborNpc(SECTOR_IDX sector_index, DWORD object_key) const { SectorGrid3x3 OUT grid; if (m_SectorGrid.GetAroundSectors(sector_index, &grid) == false) { return NULL; } GRID3x3_CENTER_PRIOR_SWAP(grid); FOREACH_ARRAY(const SECTOR_IDX sector_idx, grid.Grid, SectorGrid3x3::GridArray) { if (sector_idx == INVALID_SECTOR_INDEX) { continue; } Sector* const sector = m_SectorGrid.GetSector(sector_idx); ns_object::SearchNodeConstIterator it = sector->GetNpcRoot(); const ns_object::SearchNodeConstIterator end = sector->GetNpcEnd(); while (const ns_object::SearchNode::LinkNode* link_node = it.ClimbToNext(end)) { NPC* npc = link_node->select.npc_node.npc; if (obj_util::IsValid(npc) == false) { continue; } // (NOTE) the logic before 2008 has the modification problem of the list in loop // currently the list is not modified. const DWORD npc_key = npc->GetObjectKey(); bool found = (npc_key == object_key); if (found == false) { continue; } // finded! if (npc->IsDeleteReserved()) { return NULL; } return npc; } } return NULL; } // (CHANGES) (f100503.3L) change the interface specification so that // a finder has the same of the ObjectFinder only NPC* GameField::SearchNeighborNpc(SECTOR_IDX sector_index, const IObjectFinder* filter) const { SectorGrid3x3 OUT grid; if (m_SectorGrid.GetAroundSectors(sector_index, &grid) == false) { return NULL; } GRID3x3_CENTER_PRIOR_SWAP(grid); FOREACH_ARRAY(const SECTOR_IDX sector_idx, grid.Grid, SectorGrid3x3::GridArray) { if (sector_idx == INVALID_SECTOR_INDEX) { continue; } Sector* const sector = m_SectorGrid.GetSector(sector_idx); ns_object::SearchNodeConstIterator it = sector->GetNpcRoot(); const ns_object::SearchNodeConstIterator end = sector->GetNpcEnd(); while (const ns_object::SearchNode::LinkNode* link_node = it.ClimbToNext(end)) { NPC* npc = link_node->select.npc_node.npc; if (obj_util::IsValid(npc) == false) { continue; } // (NOTE) the logic before 2008 has the modification problem of the list in loop // currently the list is not modified. if (npc->IsDeleteReserved()) { continue; } if (filter->operator()(npc)) { return npc; } } } return NULL; } //-------------------------------------------------------------------------------------------------- // (CHANGES) (f100503.3L) change the interface specification so that // a finder has the same of the ObjectFinder only Item* GameField::SearchNeighborItem(SECTOR_IDX sector_index, DWORD object_key) const { SectorGrid3x3 OUT grid; if (m_SectorGrid.GetAroundSectors(sector_index, &grid) == false) { return NULL; } GRID3x3_CENTER_PRIOR_SWAP(grid); FOREACH_ARRAY(const SECTOR_IDX sector_idx, grid.Grid, SectorGrid3x3::GridArray) { if (sector_idx == INVALID_SECTOR_INDEX) { continue; } Sector* const sector = m_SectorGrid.GetSector(sector_idx); ns_object::SearchNodeConstIterator it = sector->GetItemRoot(); const ns_object::SearchNodeConstIterator end = sector->GetItemEnd(); while (const ns_object::SearchNode::LinkNode* link_node = it.ClimbToNext(end)) { Item* item = link_node->select.item_node.item; if (obj_util::IsValid(item) == false) { continue; } const DWORD item_key = item->GetObjectKey(); bool found = item_key == object_key; if (found == false) { continue; } // found! if (item->IsDeleteReserved()) { return NULL; } return item; } } return NULL; } // (CHANGES) (f100503.3L) change the interface specification so that // a finder has the same of the ObjectFinder only Item* GameField::SearchNeighborItem(SECTOR_IDX sector_index, const IObjectFinder* filter) const { SectorGrid3x3 OUT grid; if (m_SectorGrid.GetAroundSectors(sector_index, &grid) == false) { return NULL; } GRID3x3_CENTER_PRIOR_SWAP(grid); FOREACH_ARRAY(const SECTOR_IDX sector_idx, grid.Grid, SectorGrid3x3::GridArray) { if (sector_idx == INVALID_SECTOR_INDEX) { continue; } Sector* const sector = m_SectorGrid.GetSector(sector_idx); ns_object::SearchNodeConstIterator it = sector->GetItemRoot(); const ns_object::SearchNodeConstIterator end = sector->GetItemEnd(); while (const ns_object::SearchNode::LinkNode* link_node = it.ClimbToNext(end)) { Item* item = link_node->select.item_node.item; if (obj_util::IsValid(item) == false) { continue; } if (item->IsDeleteReserved()) { continue; } if (filter->operator()(item)) { return item; } } } return NULL; } //-------------------------------------------------------------------------------------------------- void GameZone::SendPacketToAll(MSG_BASE_FORWARD* const msg_base, const WORD msg_size, const DWORD except_object_key) { // NOTE: f110531.5L, it enabled send all user without broadcast boundary MSG_AG_CONNECTION_PACKET_BROADCASTER_ONEtoN_SYN& msg_brd = \ MSG_ONEtoN_BROADCASTER_INTERNAL::InstanceOfBase(); msg_brd.Init(msg_base, msg_size); uint16_t* const user_begin = msg_brd.GetUsersStartPtr(); const bool check_skip_object = !except_object_key; FOREACH_CONTAINER(const _PLAYER_HASH::value_type& rNode, m_PlayerHash, _PLAYER_HASH) { Player* const player = rNode.second; if (obj_util::IsValid(player, __FUNCTION__, __LINE__) == false) { continue; } const DWORD object_key = player->GetObjectKey(); if (!(check_skip_object || (object_key != except_object_key))) { continue; }; DWORD bound_check = msg_brd.AddUser(user_begin, object_key); if (bound_check == msg_brd.MAX_USER_COUNT) { break; }; } if (msg_brd.number_of_users) { g_pGameServer->SendToServer(AGENT_SERVER, &msg_brd, msg_brd.GetSendingSize()); }; }