#include "stdafx.h" #include "./ItemManager.h" #include "./ItemManager_Util.h" // #include #include #include "Player.h" #include "InventorySlotContainer.h" //================================================================================================== // implemented by _NA000023_090323_ENHANCED_INVENTORY_SLOT_FUNCTIONALITY_ namespace nsSlot { ; //-------------------------------------------------------------------------------------------------- // f100903.4L, the SortStateNode structure support two phase search data caching. typedef SCSlotContainer::SLOT_LINEAR_LIST LinearList; struct SortStateNode { // uint8_t check_cache_; // use eNodeCheckState bool duplicable_; SLOTCODE item_code_; const SCSlotContainer::SlotNode* slot_node_; }; struct SortStateTable { enum eNodeCheckState { NotChecked = 0x00, Empty = 0x01, LockedState = 0x02, Skipped = 0x04, }; // void Clear(); void ChangeMode(SearchOffset::eSearchMode change_mode); // int current_mode_; // use SearchOffset::eSearchMode bool option_check_empty_; SortStateNode states_[ItemQuery::kMaxNumberOfPosSlots]; SearchOffset iterators_[ItemQuery::kMaxNumberOfPosSlots]; }; inline void SortStateTable::Clear() { // NOTE, dangerous routine, however a nessessary index node is realigned each query. const size_t size = static_cast(\ reinterpret_cast(iterators_) - reinterpret_cast(this)); ZeroMemory(this, size); } void SortStateTable::ChangeMode(SearchOffset::eSearchMode change_mode) { assert(change_mode >= 0 && change_mode < SearchOffset::eSearch_Mode_Counts); current_mode_ = change_mode; switch (current_mode_) { case SearchOffset::eSearch_1st_FindOverlappingSlot: option_check_empty_ = false; break; case SearchOffset::eSearch_2nd_FindEmptySlot: option_check_empty_ = true; break; case SearchOffset::eSearch_3rd_FindOverlappingSlot: option_check_empty_ = false; break; case SearchOffset::eSearch_4th_FindEmptySlot: option_check_empty_ = true; break; case SearchOffset::eSearch_5th_FindOverlappingSlot: option_check_empty_ = false; break; case SearchOffset::eSearch_6th_FindEmptySlot: option_check_empty_ = true; break; } } static SortStateTable sort_state_table; //-------------------------------------------------------------------------------------------------- static bool insert_slot_iterator_skip_cache_static[ItemQuery::kMaxNumberOfPosSlots]; static SCSlotContainer::SLOT_LINEAR_LIST::const_iterator insert_slot_iterators_static[\ ItemQuery::kMaxNumberOfPosSlots]; static SCSlotContainer::SLOT_LINEAR_LIST::const_iterator insert_slot_end_of_iterators_static[\ ItemQuery::kMaxNumberOfPosSlots]; // If the result is true, the item_info of a QueryInfo::Node is aligned reference. // If the result is false, can't find item info. bool ItemQuery::InterAlignItemInfo(ItemQuery::QueryInfo::Node* query_node, ItemInfoParser* item_parser, int query_node_index, bool sorting_insert) { if (query_node->item_info == NULL) { if (query_node->item_slot) { query_node->item_info = query_node->item_slot->GetItemInfo(); } else if (query_node->item_code) { query_node->item_info = item_parser->GetItemInfo(query_node->item_code); } } // const BASE_ITEMINFO* item_info = query_node->item_info; if (item_info == NULL || item_info->m_Code == 0) { return false; } const uint16_t item_type = item_info->m_wType; static const uint16_t kFunctionMaterials[] = { eITEMTYPE_ENCHANT, eITEMTYPE_CRYSTAL, eITEMTYPE_SOCKET, eITEMTYPE_SOCKETMATERIAL, eITEMTYPE_ACCESSORYMATERIAL, eITEMTYPE_SACCESSORYMATERIAL, eITEMTYPE_SOCKET_ZARD,eITEMTYPE_SOCKET_ZARD_RINGS, //xiaomi add bool ItemQuery::InterAlignItemInfo }; BOOST_STATIC_ASSERT(_countof(kFunctionMaterials) == 8); struct LocalUtil { static bool IsFunctionMaterials(const uint16_t* it, size_t size, uint16_t item_type) { const uint16_t* const end = it + size; for (; it != end; ++it) { if (*it == item_type) { return true; } } return false; } }; // select insert group // // set default query_node->item_insert_rule = nsSlot::InventoryConfig::eInsertRule_Normal; if (sorting_insert == false) { return true; } if (false) { // dummy } else if (item_type == eITEMTYPE_QUEST) { query_node->item_insert_rule = nsSlot::InventoryConfig::eInsertRule_Quest; } else if (item_info->m_wACCode != 0) { query_node->item_insert_rule = nsSlot::InventoryConfig::eInsertRule_AC; } else if (item_type == eITEMTYPE_WASTE || item_info->IsCanUseWaste()) { query_node->item_insert_rule = nsSlot::InventoryConfig::eInsertRule_Waste; } else if (SCItemSlot::CanEquip(item_info)) { query_node->item_insert_rule = nsSlot::InventoryConfig::eInsertRule_Equip; } else if (LocalUtil::IsFunctionMaterials(kFunctionMaterials, _countof(kFunctionMaterials), \ item_type)) { query_node->item_insert_rule = nsSlot::InventoryConfig::eInsertRule_Material; } // get insert rule if (slot_index_ == SI_INVENTORY) { CInventorySlotContainer* inventory = static_cast(item_container_); // f100904.4L, replace an old data structure that has one phase searching algorithm. nsSlot::SearchOffset* search_offset = &sort_state_table.iterators_[query_node_index]; inventory->GetConfiguredSlotList(\ static_cast(query_node->item_insert_rule), search_offset); } return true; } // if current operation is queried by the inverse_select query, call the below method. bool ItemQuery::PrefetchQueryInfoForInverseSelect() { bool success = true; // CHANGES, f100903.4L sort_state_table.Clear(); // ZeroMemory(insert_slot_iterator_skip_cache_static, \ sizeof(insert_slot_iterator_skip_cache_static)); ItemInfoParser* const item_parser = ItemInfoParser::Instance(); QueryInfo::Node* query_it = query_info_.nodes; for (int query_index = 0; query_index < query_info_.number_of_queries; ++query_index, ++query_it) { success = InterAlignItemInfo(query_it, item_parser, query_index, true); if (success == false) { break; } } assert(success != false); return success; } bool ItemQuery::SetupDefaults(eItemQueryType query_type, ItemQuery* item_query, Player* player, SLOTIDX slot_index) { // boundary rule check section BOOST_STATIC_ASSERT(sizeof(POSTYPE) == 2 && _countof(item_query->query_info_.nodes) == UCHAR_MAX); BOOST_STATIC_ASSERT(_countof(item_query->query_info_.nodes) == _countof(item_query->result_info_.nodes)); // input argument test if (item_query == NULL) { return false; } item_query->Clear(); // prevent query call on uncleared state by invalid inputs // eSlotContainerIndex if (player == NULL || slot_index >= SI_MAX) { } // query type test if ((query_type > eItemQuery_None && query_type < eItemQuery_Max) == false) { return false; } switch (slot_index) { case SI_INVENTORY: case SI_EQUIPMENT: case SI_WAREHOUSE: case SI_GUILDWAREHOUSE: break; default: return false; } // item_query->query_type_ = query_type; item_query->player_ = player; item_query->slot_index_ = slot_index; item_query->item_manager_ = player->GetItemManager(); item_query->item_container_ = item_query->item_manager_->GetItemSlotContainer(slot_index); return true; } //-------------------------------------------------- // target: // - eItemQuery_Select, // - eItemQuery_InverseSelect, // - eItemQuery_Obtain, // - eItemQuery_Delete, // select one query data = { ItemCode, ItemSlot } // if you want multi items query, use AddArg_ItemCode recursively int ItemQuery::AddArg_ItemCode(SLOTCODE item_code, int number_of_items) { if (query_info_.number_of_queries >= _countof(query_info_.nodes) || (number_of_items <= 0 || number_of_items > USHRT_MAX)) { return -1; } if (item_code == 0) { return -1; } if (QueryInfo::Node* info_node = &query_info_.nodes[query_info_.number_of_queries]) { // alignment info_node->position = INVALID_POSTYPE_VALUE; //info_node->item_insert_rule info_node->item_code = item_code; info_node->number_of_items = number_of_items; info_node->item_slot = NULL; query_info_.sum_of_queried_items += number_of_items; }; // int registered_index = query_info_.number_of_queries; ++query_info_.number_of_queries; return registered_index; } int ItemQuery::AddArg_ItemSlot(const SCItemSlot& item_slot, int number_of_items, bool non_constant_slot) { if (query_info_.number_of_queries >= _countof(query_info_.nodes) || (number_of_items <= 0 || number_of_items > USHRT_MAX)) { return -1; } SLOTCODE item_code = item_slot.GetItemCode(); if (item_code == 0) { return -1; } { // alignment QueryInfo::Node* info_node = &query_info_.nodes[query_info_.number_of_queries]; info_node->position = INVALID_POSTYPE_VALUE; //info_node->item_insert_rule info_node->item_code = item_code; info_node->number_of_items = number_of_items; info_node->item_slot = &item_slot; info_node->item_info = item_slot.GetItemInfo(); info_node->setting_guide = non_constant_slot == false ? info_node->eSettingGuid_ConstantSlot : info_node->eSettingGuid_NonConstantSlot; query_info_.sum_of_queried_items += number_of_items; }; // int registered_index = query_info_.number_of_queries; ++query_info_.number_of_queries; return registered_index; } int ItemQuery::AddArg_Custom(POSTYPE number_of_kinds, const SLOTCODE* item_code_it, const POSTYPE* item_num_it) { if (number_of_kinds == 0 || query_info_.number_of_queries + number_of_kinds >= _countof(query_info_.nodes)) { return -1; } QueryInfo::Node* dest_node = &query_info_.nodes[query_info_.number_of_queries]; int sum_of_queried_items = 0; int index = 0; // CHANGES, f100831.1L, add a missing increase operation. for (; index < int(number_of_kinds); ++index, ++dest_node) { if (*item_num_it == 0 || *item_code_it == 0) { break; } dest_node->position = INVALID_POSTYPE_VALUE; dest_node->item_code = *item_code_it++; dest_node->number_of_items = *item_num_it++; dest_node->item_slot = NULL; sum_of_queried_items += dest_node->number_of_items; } // if (index != number_of_kinds) { dest_node = &query_info_.nodes[query_info_.number_of_queries]; const size_t cleared_size = \ reinterpret_cast(&query_info_.nodes[_countof(query_info_.nodes)]) - reinterpret_cast(dest_node); ZeroMemory(dest_node, cleared_size); return -1; } query_info_.sum_of_queried_items += sum_of_queried_items; // to compatible interface, return last element index int registered_last_index = query_info_.number_of_queries + index - 1; query_info_.number_of_queries += index; return registered_last_index; } // for Debug routine bool ItemQuery::CheckResultValidatation_Custom(POSTYPE number_of_kinds, const SLOTCODE* item_code_array, const POSTYPE* item_num_array) const { __UNUSED((number_of_kinds, item_code_array)); // for iteration //const ItemQuery::QueryInfo& query_info = delete_query.query_info(); const ItemQuery::QueryInfo::Node* query_node = query_info_.nodes; // for debug section const POSTYPE* source_num_it = item_num_array; for (int index = 0; index < query_info_.number_of_queries; ++index, ++query_node, ++source_num_it) { if (!FlowControl::FCAssert(query_node->sum_of_items == *source_num_it)) { return false; } } return true; } // (f100525.4L) set up values of a LOCKED_INFO and lock slots automatically. // (WARNING) the result value must be checked with number_of_locked_info // after a calling of a 'Query' // assert(result_info.number_of_results <= number_of_locked_info) bool ItemQuery::SetupLockedInfoWithLock(POSTYPE* const number_of_used_locked_info, const int number_of_locked_info, LOCKED_INFO* const locked_info, POSTYPE number_of_kinds, const DWORD* datetime_array) { *number_of_used_locked_info = 0; if (number_of_locked_info < result_info_.number_of_results || number_of_locked_info < number_of_kinds) { return false; } const ItemQuery::ResultInfo::Node* query_result_it = result_info_.nodes, * query_result_end = \ &result_info_.nodes[_countof(result_info_.nodes)]; // const DWORD* datetime_it = datetime_array; LOCKED_INFO* locked_info_it = locked_info; for (int countdown = result_info_.number_of_results; countdown && query_result_it != query_result_end; ++query_result_it) { if (query_result_it->container_item_code == 0 || query_result_it->number_of_selected_items == 0) { continue; } ZeroMemory(locked_info_it, sizeof(*locked_info_it)); locked_info_it->m_code = query_result_it->container_item_code; locked_info_it->m_SlotIndex = this->slot_index_; locked_info_it->m_pos = query_result_it->position; locked_info_it->m_num = query_result_it->number_of_selected_items; // const SCItemSlot& query_item_slot = *query_result_it->query_item_slot; const nsSlot::ItemRuleInfo& item_rule_info = query_item_slot.GetItemRuleInfo(); locked_info_it->m_Enchant = query_item_slot.GetEnchant(); locked_info_it->m_Rank = query_item_slot.GetRank(); locked_info_it->m_ItemType = nsSlot::ItemTypeChanger::GetKindOfItemType(query_item_slot); if (datetime_it) { locked_info_it->m_DateTime = *datetime_it; ++datetime_it; } query_result_it->container_item_slot->SetLock(true); WAVERIX_WRAPPER((WAVERIX_LC_CALL3("waverix_logitemquery", countdown, locked_info_it->m_pos, locked_info_it->m_code))); ++locked_info_it; ++(*number_of_used_locked_info); --countdown; } return true; } // (f100607.5L) add the logging function after a result process completed. bool ItemQuery::WriteToGameLogBasedOnResult(Player* player, int log_type) { const ItemQuery::ResultInfo::Node* query_result_it = result_info_.nodes, * query_result_end = \ &result_info_.nodes[_countof(result_info_.nodes)]; // for (int countdown = result_info_.number_of_results; countdown && query_result_it != query_result_end; ++query_result_it) { if (query_result_it->container_item_code == 0 || query_result_it->number_of_selected_items == 0) { continue; } GAMELOG->LogItem(log_type, player, query_result_it->query_item_slot, 0, query_result_it->number_of_selected_items); } return true; } bool ItemQuery::MakeSerializedStream(INVENTORY_TOTAL_INFO* inven_total_info) const { // (NOTE) don't support temp inventory since 0902 version const ItemQuery::ResultInfo::Node* query_result_it = result_info_.nodes, * query_result_end = \ &result_info_.nodes[_countof(result_info_.nodes)]; // INVENTORY_TOTAL_INFO::SLOT_TYPE* serialize_result_it = inven_total_info->m_Slot; for (int countdown = result_info_.number_of_results; countdown && query_result_it != query_result_end; ++query_result_it) { if (query_result_it->container_item_code == 0 || query_result_it->number_of_selected_items == 0) { continue; } ZeroMemory(serialize_result_it, sizeof(*serialize_result_it)); serialize_result_it->m_Pos = query_result_it->position; query_result_it->container_item_slot->CopyOut(serialize_result_it->m_Stream); WAVERIX_WRAPPER((WAVERIX_LC_CALL3("waverix_logitemquery", countdown, serialize_result_it->m_Pos, serialize_result_it->m_Stream.Part.wCode))); ++serialize_result_it; --countdown; } inven_total_info->m_InvenCount = static_cast(result_info_.number_of_results); inven_total_info->m_TmpInvenCount = 0; return true; } bool ItemQuery::MakeSerializedStream(JUST_INVENTORY_TOTAL_INFO* inven_total_info) const { const ItemQuery::ResultInfo::Node* query_result_it = result_info_.nodes, * query_result_end = \ &result_info_.nodes[_countof(result_info_.nodes)]; // JUST_INVENTORY_TOTAL_INFO::SLOT_TYPE* serialize_result_it = inven_total_info->m_Slot; for (int countdown = result_info_.number_of_results; countdown && query_result_it != query_result_end; ++query_result_it) { if (query_result_it->container_item_code == 0 || query_result_it->number_of_selected_items == 0) { continue; } ZeroMemory(serialize_result_it, sizeof(*serialize_result_it)); serialize_result_it->m_Pos = query_result_it->position; query_result_it->container_item_slot->CopyOut(serialize_result_it->m_Stream); ++serialize_result_it; --countdown; } inven_total_info->m_Count = static_cast(result_info_.number_of_results); return true; } bool ItemQuery::MakeSerializedStream(INVEN_DELETE_TOTAL_INFO* inven_delete_total_info) const { const ItemQuery::ResultInfo::Node* query_result_it = result_info_.nodes, * query_result_end = \ &result_info_.nodes[_countof(result_info_.nodes)]; // INVEN_DELETE_TOTAL_INFO::SLOT_TYPE* serialize_result_it = inven_delete_total_info->m_Slot; for (int countdown = result_info_.number_of_results; countdown && query_result_it != query_result_end; ++query_result_it) { if (query_result_it->container_item_code == 0 || query_result_it->number_of_selected_items == 0) { continue; } serialize_result_it->m_Pos = query_result_it->position; serialize_result_it->m_Num = static_cast(query_result_it->number_of_selected_items); ++serialize_result_it; --countdown; } inven_delete_total_info->m_Count = static_cast(result_info_.number_of_results); return true; } bool ItemQuery::MakeSerializedStream(int number_of_kinds, POS_INFO* pos_info_array, POSTYPE* upperbound) const { // ... delete reserved const ItemQuery::ResultInfo::Node* query_result_it = result_info_.nodes, * query_result_end = \ &result_info_.nodes[_countof(result_info_.nodes)]; // for (int countdown = result_info_.number_of_results, countdown_for_kinds = number_of_kinds; countdown && countdown_for_kinds && query_result_it != query_result_end; ++query_result_it) { if (query_result_it->container_item_code == 0 || query_result_it->number_of_selected_items == 0) { continue; } pos_info_array->m_pos = query_result_it->position; pos_info_array->m_num = static_cast(query_result_it->number_of_selected_items); ++pos_info_array; --countdown; --countdown_for_kinds; } *upperbound = static_cast(result_info_.number_of_results); return true; } }; //end of namespace //================================================================================================== nsSlot::ItemQueryResult ItemManager::Query(nsSlot::ItemQuery* item_query) { bool is_valid = true; bool is_multi_select = false; nsSlot::ItemQueryResult query_result = { NULL, }; switch (item_query->query_type_) { case nsSlot::eItemQuery_Select: Query_Select(&query_result, item_query); break; case nsSlot::eItemQuery_InverseSelect: //Query_InverseSelect(&query_result, item_query); // change selector by inventory sorting configuration Query_InverseSelectSort(&query_result, item_query); break; default: is_valid = false; SUNLOG(eCRITICAL_LOG, "["__FUNCTION__"] Unexpected query type|Query=%d|", item_query->query_type_); break; }; if (is_valid == false) { ASSERT(!"Impossible reached point"); __debugbreak(); query_result.slot = NULL; } return query_result; } //-------------------------------------------------------------------------------------------------- // @description // The interface to find targets which are items with number // (WARNING) don't input same item code by AddArg_Item~() // // @note // If the function succeeds, the return value is true and has partial result based on query array // - the partial result has distributed in the result_info_array, \ // that corresponding to query_info_array. // - if item query result is accepted state, \ // number_of_items value, the pos_info of the result_info_array, is matched to it of \ // the query_info_array // - (pseudo) // item_query_ptr->AddArg_ItemCode(item_code1, item_num1); // 1st item index is 0 // item_query_ptr->AddArg_ItemCode(item_code2, item_num2); // 2nd item index is 1 // ... (continued) // // if (Query_Select(query_result_ptr, item_query_ptr) != false) { // if (item_query_ptr->result_info_array[0].number_of_items == item_num1) { // first query is succeeds. // } // if (item_query_ptr->result_info_array[1].number_of_items == item_num2) { // second query is succeeds. // } // ... (continued) // } // If the function fails, the return value is false // - can't search state // bool ItemManager::Query_Select(nsSlot::ItemQueryResult* query_result, nsSlot::ItemQuery* item_query) { using nsSlot::ItemQuery; if (item_query->query_info_.number_of_queries == 0) { // the number_of_query_items value must be not zero. query_result->result_of_select = 0; return false; } // SCItemSlotContainer* const item_container = item_query->item_container_; const SCSlotContainer::SLOT_LINEAR_LIST& slot_list = item_container->GetSlotList(); SCSlotContainer::SLOT_LINEAR_LIST::const_iterator it = slot_list.begin(), end = slot_list.end(); // int searched_items = 0, sum_of_selected_items = 0; // const int max_number_of_slots = item_container->GetMaxSlotNum(); for (int index = 0; index < max_number_of_slots && it != end; ++index, ++it) { if (item_query->query_info_.number_of_queries == searched_items) { // all items found, change condition by calculated result break; } const SCSlotContainer::SlotNode& slot_node = *it; SCItemSlot* container_item = static_cast(slot_node.slot); const SLOTCODE container_item_code = container_item->GetItemCode(); if (container_item_code == 0) { // SCItemSlot is empty if code equals zero. that's enough don't have call 'IsEmpty' continue; } if (container_item->IsBlocked() || container_item->IsLocked())// || //container_item->IsUserBlocked()) { continue; } // const int container_item_num = container_item->GetNum(); ItemQuery::QueryInfo::Node* query_it = item_query->query_info_.nodes; for (int search_index = 0; search_index < item_query->query_info_.number_of_queries; ++search_index, ++query_it) { if (query_it->item_code != container_item_code) { continue; } if (query_it->number_of_items == query_it->sum_of_items) { continue; } if (ItemQuery::ResultInfo::Node* result_it = &item_query->result_info_.nodes[index]) { //alignment result int remains_in_container = container_item_num - \ result_it->number_of_selected_items; if (remains_in_container == 0) { // all selected continue; } if (result_it->number_of_selected_items == 0) { ++item_query->result_info_.number_of_results; } result_it->position = static_cast(index); result_it->container_item_code = container_item_code; result_it->query_item_slot = query_it->item_slot; result_it->container_item_slot = container_item; result_it->update_guide = query_it->setting_guide; result_it->update_guide |= result_it->eUpdateGuide_Update; // assert(post_remains >= 1); if (query_it->sum_of_items + remains_in_container <= query_it->number_of_items) { result_it->number_of_selected_items += remains_in_container; query_it->sum_of_items += remains_in_container; sum_of_selected_items += remains_in_container; } else //if (sum_of_search_items + remains_in_container > query_it->number_of_items) { int increment = query_it->number_of_items - query_it->sum_of_items; result_it->number_of_selected_items += increment; query_it->sum_of_items = query_it->number_of_items; sum_of_selected_items += increment; } }; // if (query_it->sum_of_items == query_it->number_of_items) { ++searched_items; //break; (consider same item multi-selection) } } // already found state = if (search_index != item_query->query_info_.number_of_queries) } item_query->result_info_.sum_of_selected_items += sum_of_selected_items; // query_result.result_of_contains has only the number of 1st item to support custom logic query_result->result_of_select = item_query->result_info_.sum_of_selected_items; return true; }; bool ItemManager::Query_InverseSelect(nsSlot::ItemQueryResult* query_result, nsSlot::ItemQuery* item_query) { using nsSlot::ItemQuery; query_result->result_of_inverse_select = 0; // the number_of_query_items value must be not zero. if (item_query->query_info_.number_of_queries == 0) { return false; } // ItemInfoParser* const item_parser = ItemInfoParser::Instance(); SCItemSlotContainer* const item_container = item_query->item_container_; const SCSlotContainer::SLOT_LINEAR_LIST& slot_list = item_container->GetSlotList(); SCSlotContainer::SLOT_LINEAR_LIST::const_iterator it = slot_list.begin(), end = slot_list.end(); // int searched_items = 0, sum_of_selected_items = 0; const int max_number_of_slots = item_container->GetMaxSlotNum(); for (int index = 0; index < max_number_of_slots && it != end; ++index, ++it) { if (item_query->query_info_.number_of_queries == searched_items) { // all items found, change condition by calculated result break; } ItemQuery::QueryInfo::Node* query_it = item_query->query_info_.nodes; const SCSlotContainer::SlotNode& slot_node = *it; SCItemSlot* container_item = static_cast(slot_node.slot); const SLOTCODE container_item_code = container_item->GetItemCode(); // (CHANGES) (f100525.4L) prevent a search a locking state on a reserved space if (container_item->IsLocked()) { // no-check LaxLock continue; } if (container_item_code == 0) { ItemQuery::ResultInfo::Node* const result_it = &item_query->result_info_.nodes[index]; // SCItemSlot is empty if code equals zero. that's enough don't have call 'IsEmpty' // The InverseSelect find a surplus space, \ // like the empty or remains of a overlapped item for (int search_index = 0; search_index < item_query->query_info_.number_of_queries; ++search_index, ++query_it) { if (query_it->number_of_items == query_it->sum_of_items) { continue; } // the below condition check is needed, because this slot is empty if (result_it->container_item_code != 0 && result_it->container_item_code != query_it->item_code) { continue; } const bool reserved_slot = result_it->container_item_code != 0; // (CHANGES) (f100727.2L) the query of a 'InverseSelect' is // based on a obtain function directly or indirectly(reserve) // this function is to support a long term item transaction like a shop transaction if (item_query->find_empty_only_ && reserved_slot) { break; } if (item_query->InterAlignItemInfo(query_it, item_parser, search_index, false) == false) { continue; } // other states are filtered that is checked on start of a server, // or must be checked. int max_overlap_num = query_it->item_info->m_byDupNum <= 1 ? 1 : query_it->item_info->m_byDupNum; if (reserved_slot && max_overlap_num == 1) { break; // already this slot has reserved by another same an item } // change algorithm whether result_it->number_of_selected_items is zero or not zero. // if the value is zero, ok // if the value is not zero, must be considered it. ; max_overlap_num -= result_it->number_of_selected_items; if (max_overlap_num == 0) { break; } if (result_it->number_of_selected_items == 0) { ++item_query->result_info_.number_of_results; } int remains = query_it->number_of_items - query_it->sum_of_items; ;{ //alignment result if (max_overlap_num < remains) { result_it->number_of_selected_items += max_overlap_num; query_it->sum_of_items += max_overlap_num; sum_of_selected_items += max_overlap_num; } else { result_it->number_of_selected_items += remains; query_it->sum_of_items += remains; sum_of_selected_items += remains; } result_it->position = static_cast(index); result_it->container_item_code = query_it->item_code; // it's possible any same query item can replace result_it->query_item_slot result_it->query_item_slot = query_it->item_slot; result_it->container_item_slot = container_item; result_it->update_guide = query_it->setting_guide; result_it->update_guide |= result_it->eUpdateGuide_Insert; }; if (query_it->sum_of_items == query_it->number_of_items) { ++searched_items; //break; // <- to support multi - inverse select } } continue; } // (CHANGES) (f100525.4L) the query of a 'InverseSelect' is // based on a obtain function directly or indirectly(reserve) // this function is to support a long term item transaction like a shop transaction if (item_query->find_empty_only_) { continue; } // (CHANGES) (f100525.4L) move a lock check block // to prevent a search a locking state on a reserved space if (container_item->IsBlocked() || container_item->IsUserBlocked()) // no-check LaxLock { continue; } ItemQuery::ResultInfo::Node* const result_it = &item_query->result_info_.nodes[index]; const POSTYPE container_item_num = container_item->GetNum(); for (int search_index = 0; search_index < item_query->query_info_.number_of_queries; ++search_index, ++query_it) { if (query_it->item_code != container_item_code) { continue; } if (query_it->number_of_items == query_it->sum_of_items) { continue; } if (item_query->InterAlignItemInfo(query_it, item_parser, search_index, false) == false) { continue; } // f100909.1L, add a rule about an item tag if (item_query->option_tag_.value != container_item->GetOptionTag().value) { continue; } // int max_overlap_num = query_it->item_info->m_byDupNum <= 1 ? 1 : query_it->item_info->m_byDupNum; if ((max_overlap_num == 1 && result_it->container_item_code != 0) || !FlowControl::FCAssert(max_overlap_num >= container_item_num)) { break; } ; max_overlap_num -= container_item_num; ; max_overlap_num -= result_it->number_of_selected_items; assert(max_overlap_num >= 0); if (max_overlap_num <= 0) { break; } // if (result_it->number_of_selected_items == 0) { ++item_query->result_info_.number_of_results; } int remains = query_it->number_of_items - query_it->sum_of_items; { //alignment result if (max_overlap_num < remains) { result_it->number_of_selected_items += max_overlap_num; query_it->sum_of_items += max_overlap_num; sum_of_selected_items += max_overlap_num; } else { result_it->number_of_selected_items += remains; query_it->sum_of_items += remains; sum_of_selected_items += remains; } result_it->position = static_cast(index); result_it->container_item_code = query_it->item_code; result_it->query_item_slot = query_it->item_slot; result_it->container_item_slot = container_item; result_it->update_guide = query_it->setting_guide; result_it->update_guide |= result_it->eUpdateGuide_Update; }; if (query_it->sum_of_items == query_it->number_of_items) { ++searched_items; //break; // <- to support multi - inverse select } } // already found state = if (search_index != item_query->query_info_.number_of_queries) } item_query->result_info_.sum_of_selected_items += sum_of_selected_items; // query_result.result_of_contains has only the number of 1st item to support custom logic query_result->result_of_inverse_select = item_query->result_info_.sum_of_selected_items; return true; } // The interface to find empty slots or remains of stacked slots \ // which are considered items with number and inventory sorted insert configuration bool ItemManager::Query_InverseSelectSort(nsSlot::ItemQueryResult* query_result, nsSlot::ItemQuery* item_query) { using namespace nsSlot; if (item_query->query_info_.number_of_queries == 0) { // the number_of_query_items value must be not zero. query_result->result_of_inverse_select = 0; return false; } if (item_query->PrefetchQueryInfoForInverseSelect() == false) { query_result->result_of_inverse_select = 0; return false; } // bool success = false; for (int mode = 0; mode < nsSlot::SearchOffset::eSearch_Mode_Counts; ++mode) { sort_state_table.ChangeMode(static_cast(mode)); success = Query_InverseSelectSortInternal(query_result, item_query); if (success == false) { return success; } if (item_query->query_info_.sum_of_queried_items == query_result->result_of_inverse_select) { success = true; // any logic don't use the result, but support the correct result. break; } }; return success; }; // CHANGES, f100903.4L, the algorithm that a duplicable item must find an exist slot first has // six phase depth complexity. so that a below method is split from a Query_InverseSelectSort // to support a mode control like not-empty, empty, and that. considering a tab sort priority. bool ItemManager::Query_InverseSelectSortInternal(nsSlot::ItemQueryResult* query_result, nsSlot::ItemQuery* item_query) { // this method is depend on Query_InverseSelectSort. using namespace nsSlot; // SCItemSlotContainer* const item_container = item_query->item_container_; ItemQuery::QueryInfo::Node* query_it = item_query->query_info_.nodes; // const int current_search_mode = sort_state_table.current_mode_; int searched_items = 0, sum_of_selected_items = 0; const int max_number_of_slots = item_container->GetMaxSlotNum(); for (int search_index = 0; search_index < item_query->query_info_.number_of_queries; ++search_index, ++query_it) { if (query_it->number_of_items == query_it->sum_of_items) { continue; } // f100904.4L, is not overlapped item find overlapping slot if (query_it->item_info->m_byDupNum <= 1 && sort_state_table.option_check_empty_ == false) { continue; } // LinearList::const_iterator& it = \ sort_state_table.iterators_[search_index].search_it[current_search_mode]; LinearList::const_iterator& end = \ sort_state_table.iterators_[search_index].search_end[current_search_mode]; // prevent an index of out ranges problem because the third group do not always have counts if (it == end) { continue; } // for (int index = it->pos; index < ItemQuery::kMaxNumberOfPosSlots && it != end; // fix up an access violation about the end iterator index = ((++it) != end) ? it->pos : ItemQuery::kMaxNumberOfPosSlots) { if (item_query->query_info_.number_of_queries == searched_items) { // all items found, change condition by calculated result break; } // SortStateNode* const sort_state_node = &sort_state_table.states_[index]; if (sort_state_node->check_cache_ & (sort_state_table.Skipped | sort_state_table.LockedState)) { continue; } // if (sort_state_node->slot_node_ == NULL) { sort_state_node->slot_node_ = &(*it); sort_state_node->item_code_ = static_cast( sort_state_node->slot_node_->slot)->GetItemCode(); } // const SCSlotContainer::SlotNode& slot_node = *sort_state_node->slot_node_; SCItemSlot* container_item = static_cast(slot_node.slot); const SLOTCODE container_item_code = sort_state_node->item_code_; // (CHANGES) (f100525.4L) prevent a search a locking state on a reserved space if (container_item->IsLocked()) { // no-check LaxLock sort_state_node->check_cache_ |= sort_state_table.LockedState; continue; } // const bool empty_slot = (container_item_code == 0); if (empty_slot) { sort_state_node->check_cache_ |= sort_state_table.Empty; } // if (empty_slot && sort_state_table.option_check_empty_) { ItemQuery::ResultInfo::Node* const result_it = &item_query->result_info_.nodes[index]; // SCItemSlot is empty if code equals zero. that's enough don't have call 'IsEmpty' // The InverseSelect find a surplus space, \ // like the empty or remains of a overlapped item // the below condition check is needed, because this slot is empty if (result_it->container_item_code != 0 && result_it->container_item_code != query_it->item_code) { continue; } const bool reserved_slot = result_it->container_item_code != 0; // (CHANGES) (f100727.2L) the query of a 'InverseSelect' is // based on a obtain function directly or indirectly(reserve) // this function is to support a long term item transaction like a shop transaction if (item_query->find_empty_only_ && reserved_slot) { continue; } // other states are filtered that is checked on start of a server, // or must be checked. int max_overlap_num = query_it->item_info->m_byDupNum <= 1 ? 1 : query_it->item_info->m_byDupNum; if (reserved_slot && max_overlap_num == 1) { sort_state_node->check_cache_ |= sort_state_table.Skipped; continue; // already this slot has reserved by another same an item } // change algorithm whether result_it->number_of_selected_items is zero or not zero. // if the value is zero, ok // if the value is not zero, must be considered it. ; max_overlap_num -= result_it->number_of_selected_items; if (max_overlap_num == 0) { sort_state_node->check_cache_ |= sort_state_table.Skipped; continue; } if (result_it->number_of_selected_items == 0) { ++item_query->result_info_.number_of_results; } int remains = query_it->number_of_items - query_it->sum_of_items; { //alignment result if (max_overlap_num < remains) { result_it->number_of_selected_items += max_overlap_num; query_it->sum_of_items += max_overlap_num; sum_of_selected_items += max_overlap_num; } else { result_it->number_of_selected_items += remains; query_it->sum_of_items += remains; sum_of_selected_items += remains; } result_it->position = static_cast(index); result_it->container_item_code = query_it->item_code; // it's possible any same query item can replace result_it->query_item_slot result_it->query_item_slot = query_it->item_slot; result_it->container_item_slot = container_item; result_it->update_guide = query_it->setting_guide; result_it->update_guide |= result_it->eUpdateGuide_Insert; }; if (query_it->sum_of_items == query_it->number_of_items) { ++searched_items; break; } continue; } // (CHANGES) (f100525.4L) the query of a 'InverseSelect' is // based on a obtain function directly or indirectly(reserve) // this function is to support a long term item transaction like a shop transaction if (item_query->find_empty_only_) { continue; } // f100904.4L if (sort_state_table.option_check_empty_) { continue; } // (CHANGES) (f100525.4L) move a lock check block // to prevent a search a locking state on a reserved space if (container_item->IsBlocked() || container_item->IsUserBlocked()) // no-check LaxLock { sort_state_node->check_cache_ |= sort_state_table.LockedState; continue; } ItemQuery::ResultInfo::Node* const result_it = &item_query->result_info_.nodes[index]; const POSTYPE container_item_num = container_item->GetNum(); if (query_it->item_code != container_item_code) { continue; } if (query_it->number_of_items == query_it->sum_of_items) { continue; } // f100909.1L, add a rule about an item tag if (item_query->option_tag_.value != container_item->GetOptionTag().value) { continue; } // int max_overlap_num = query_it->item_info->m_byDupNum <= 1 ? 1 : query_it->item_info->m_byDupNum; if ((max_overlap_num == 1 && result_it->container_item_code != 0) || !FlowControl::FCAssert(max_overlap_num >= container_item_num)) { sort_state_node->check_cache_ |= sort_state_table.Skipped; continue; } ; max_overlap_num -= container_item_num; ; max_overlap_num -= result_it->number_of_selected_items; assert(max_overlap_num >= 0); if (max_overlap_num <= 0) { sort_state_node->check_cache_ |= sort_state_table.Skipped; continue; } if (result_it->number_of_selected_items == 0) { ++item_query->result_info_.number_of_results; } int remains = query_it->number_of_items - query_it->sum_of_items; { //alignment result if (max_overlap_num < remains) { result_it->number_of_selected_items += max_overlap_num; query_it->sum_of_items += max_overlap_num; sum_of_selected_items += max_overlap_num; } else { result_it->number_of_selected_items += remains; query_it->sum_of_items += remains; sum_of_selected_items += remains; } result_it->position = static_cast(index); result_it->container_item_code = query_it->item_code; result_it->query_item_slot = query_it->item_slot; result_it->container_item_slot = container_item; result_it->update_guide = query_it->setting_guide; result_it->update_guide |= result_it->eUpdateGuide_Update; }; if (query_it->sum_of_items == query_it->number_of_items) { ++searched_items; break; } } // already found state = if (search_index != item_query->query_info_.number_of_queries) } item_query->result_info_.sum_of_selected_items += sum_of_selected_items; // query_result.result_of_contains has only the number of 1st item to support custom logic query_result->result_of_inverse_select = item_query->result_info_.sum_of_selected_items; return true; } //==================================================================================================