#include "stdafx.h" #include "./ItemManager_Durability.h" #include "ItemManager.h" //================================================================================================== #include #include #include "ChaoState.h" #include "EquipmentSlotContainer.h" //_NA_007079_BUG_WEAPON_INACTIVATE_STATE #include "SkillSlotManager.h" //================================================================================================== namespace nsSlot { ; // NOTE: sub-manager is implemented to decompose ItemManager. // this object has not extra members and virtual function table except the instance of ItemManager. BOOST_STATIC_ASSERT(sizeof(nsSlot::DurabilityImpl) == sizeof(ItemManager*)); // NOTE: this object support the unified transaction // that is converted from duplicated decrease durability transactions. struct InterDurabilityTransaction { static const ItemManager::TransactionClosure::value_type kTransType = ItemManager::TransactionClosure::eTransType_ChangeDura; InterDurabilityTransaction(ItemManager* ptr) : root_(ptr) , transaction_(NULL) { ItemManager::TransactionClosure::value_type trans_type = root_->GetCurrentTransactionAction(); if (trans_type == ItemManager::TransactionClosure::eTransType_None) { transaction_ = new (buffer_) ItemManager::TransactionClosure(root_, kTransType); } }; ~InterDurabilityTransaction() { if (transaction_) { transaction_->~TransactionClosure(); } } private: // ItemManager* const root_; ItemManager::TransactionClosure* transaction_; char buffer_[sizeof(ItemManager::TransactionClosure)]; // __DISABLE_COPY(InterDurabilityTransaction); }; }; //end of namespace //================================================================================================== // work in EquipContainer void nsSlot::DurabilityImpl::UpdateItemAttributesOnChangesEquippedItemStatus( const SCItemSlot& equipped_item) { #ifdef _NA_002289_20110322_EQUIPMENTS_CALCULATOR_CHANGES_FOR_IMPROVEMENT SLOTIDX slot_index = equipped_item.GetSlotIdx(); if (slot_index != SI_EQUIPMENT) { return; }; CEquipmentSlotContainer* const equip_container = static_cast(root_->GetItemSlotContainer(SI_EQUIPMENT)); ns_object::ChangedValuesNotifier notifier(root_->player_); equip_container->OnChangedEquipItemDurability(equipped_item); #else //if !defined(_NA_002289_20110322_EQUIPMENTS_CALCULATOR_CHANGES_FOR_IMPROVEMENT) __UNUSED(equipped_item); root_->UpdateAttrForEquipItem(); #endif } bool nsSlot::DurabilityImpl::NotifyChangeDurability( POSTYPE equip_pos, SLOTCODE item_code, DURATYPE durability) { MSG_CG_ITEM_CHANGE_DURA_CMD msg_cmd; msg_cmd.m_EquipPos = equip_pos; msg_cmd.m_ItemCode = item_code; msg_cmd.m_Dura = durability; return (root_->player_->SendPacket(&msg_cmd, sizeof(msg_cmd)) != false); } bool nsSlot::DurabilityImpl::DeleteEquipByDura(POSTYPE equip_pos) { if (root_->player_ == NULL || root_->player_->GetField() == NULL) { return false; }; // InterDurabilityTransaction transaction(root_); // if (root_->ValidPos(SI_EQUIPMENT, equip_pos) == false) { return false; }; CEquipmentSlotContainer* const equip_container = static_cast(root_->GetItemSlotContainer(SI_EQUIPMENT)); SCItemSlot& item_slot = static_cast(equip_container->GetSlot(equip_pos)); // acceptable conditions = { durability = 0, armors, not lucky } if (const nsSlot::ItemRuleInfo* item_rule_info = &item_slot.GetItemRuleInfo()) { #ifdef _NA_0_20111130_DISTROY_WEAPON if (!(item_rule_info->is_armor || item_rule_info->is_weapon)) return false; if (item_rule_info->is_fate) return false; #else if (item_rule_info->is_armor == false || item_rule_info->is_fate) { return false; }; #endif DURATYPE durability = item_slot.GetDura(); if (durability != 0) { return false; }; }; // ;{ int deleted_ratio = item_slot.IsDivine() ? 5 : 10; // percentage int dice_value = RandomNumberGenerator::GetRandomNumberRange(1, 100); if (dice_value > deleted_ratio) { return false; // wait to next turn }; }; // ACTION: delete item that is not durable ;{ GAMELOG->LogItem(ITEM_DELETE_CAUSE_DURA, root_->player_, &item_slot, 0, item_slot.GetNum()); // notify to client about the item deletion by decreasing durability MSG_CG_ITEM_DESTROY_DURA_CMD msg_cmd; msg_cmd.m_SlotIndex = SI_EQUIPMENT; msg_cmd.m_SlotPos = equip_pos; root_->player_->SendPacket(&msg_cmd, sizeof(msg_cmd)); // NOTE: side-effect = { total item attributes calculation } equip_container->DeleteSlot(equip_pos, NULL); }; //----------------------------------------------------------- // WARNING: current state = { corrupted data 'item_slot' } //----------------------------------------------------------- return true; } bool nsSlot::DurabilityImpl::ChangeDurabilityForGM( eEQUIPCONTAINER_POS equip_pos, DURATYPE durability) { #if !defined(__NA_001062_20080623_STRENGTH_PENALTY) return false; #endif #ifdef __NA_001062_20080623_STRENGTH_PENALTY InterDurabilityTransaction transaction(root_); // if (!root_->ValidPos(SI_EQUIPMENT, equip_pos)) { return false; } // °ø°ÝÀÚÀÇ ¹«±â ³»±¸µµ °¨¼Ò CEquipmentSlotContainer* const equip_container = static_cast(root_->GetItemSlotContainer(SI_EQUIPMENT)); SCItemSlot& item_slot = static_cast(equip_container->GetSlot(equip_pos)); if (!item_slot.WithDura()) { return false; } // CHANGES: f080821.x, lst1024, ÃÖ´ë ³»±¸µµ ÀÌ»óÀ϶§ ÃÖ´ë³»±¸µµ·Î ¼ÂÆÃ. DURATYPE modified_durability = item_slot.GetMaxDura(); ; modified_durability = min(modified_durability, durability); // item_slot.SetDura(modified_durability); NotifyChangeDurability(equip_pos, item_slot.GetItemCode(), modified_durability); UpdateItemAttributesOnChangesEquippedItemStatus(item_slot); // return true; #endif } void nsSlot::DurabilityImpl::DecreaseDurabilityUsingReason( eDecDuraType decrease_dura_type, eEQUIPCONTAINER_POS equip_pos) { InterDurabilityTransaction transaction(root_); // if (!root_->ValidPos(SI_EQUIPMENT, equip_pos)) { return; } // °ø°ÝÀÚÀÇ ¹«±â ³»±¸µµ °¨¼Ò CEquipmentSlotContainer* const equip_container = static_cast(root_->GetItemSlotContainer(SI_EQUIPMENT)); SCItemSlot& item_slot = static_cast(equip_container->GetSlot(equip_pos)); if (!item_slot.WithDura()) { return; } const DURATYPE current_durability = item_slot.GetDura(); __UNUSED(current_durability); if (item_slot.DecreasingDura(decrease_dura_type)) { const DURATYPE changed_durability = item_slot.GetDura(); #ifdef _NA_007071_20140219_MODIFY_UNUSE_ARMOR_DESTROY_PENALTY #else // ¹æ¾î±¸ ³»±¸µµ°¡ 0Àε¥ ´Ù½Ã ¼±ÅõǴ °æ¿ì ¼Ò¸êµÈ´Ù. if (changed_durability == 0 && DeleteEquipByDura(equip_pos)) { // return; }; #endif //_NA_007071_20140219_MODIFY_UNUSE_ARMOR_DESTROY_PENALTY NotifyChangeDurability(equip_pos, item_slot.GetItemCode(), changed_durability); #if !defined(__NA_001062_20080623_STRENGTH_PENALTY) if (changed_durability == 0) #endif { UpdateItemAttributesOnChangesEquippedItemStatus(item_slot); }; } } // NOTE: this method use internal controller that skip the inventory lock instead of 'ValidPos'. bool nsSlot::DurabilityImpl::DecreaseDurabilityWithRanges( const DWORD damage, eEQUIPCONTAINER_POS start_equip_pos, eEQUIPCONTAINER_POS last_equip_pos) { typedef SCItemSlotContainer::SLOT_LINEAR_LIST SLOT_LINEAR_LIST; if (FlowControl::FCAssert(start_equip_pos <= last_equip_pos && start_equip_pos < eEQUIPCONTAINER_MAX) == false) { return false; }; // change a last position to make easy query. this last position change to end position eEQUIPCONTAINER_POS end_equip_pos = ((last_equip_pos + 1) < eEQUIPCONTAINER_MAX) ? static_cast(last_equip_pos + 1) : eEQUIPCONTAINER_MAX; // CEquipmentSlotContainer* const equip_container = static_cast(root_->GetItemSlotContainer(SI_EQUIPMENT)); const SLOT_LINEAR_LIST& slot_list = equip_container->GetSlotList(); assert(slot_list.size() >= static_cast(end_equip_pos)); SLOT_LINEAR_LIST::const_iterator it = slot_list.begin() + start_equip_pos; const SLOT_LINEAR_LIST::const_iterator end = it + end_equip_pos; int number_of_modified_items = 0; for (POSTYPE equip_pos = start_equip_pos; it != end; ++equip_pos, ++it) { SCItemSlot* item_slot = static_cast(it->slot); SLOTCODE item_code = item_slot->GetItemCode(); if (item_code == 0) { continue; }; // NOTE: 'IsLocked' is set game rules, and 'IsBlocked' is forced by abusing control if (item_slot->IsLocked() || item_slot->IsBlocked()) { continue; // no check LaxLock }; if (item_slot->WithDura() == false) { continue; }; // if (item_slot->StoreDamageNDecreaseDura(damage) == false) { continue; }; const DURATYPE changed_durability = item_slot->GetDura(); // NOTE: ¹æ¾î±¸ ³»±¸µµ°¡ 0Àε¥ ´Ù½Ã ¼±ÅõǴ °æ¿ì ¼Ò¸êµÈ´Ù. #ifdef _NA_007071_20140219_MODIFY_UNUSE_ARMOR_DESTROY_PENALTY #else if (changed_durability == 0) { if (DeleteEquipByDura(equip_pos)) { // WARNING: it's possible that 'item_slot' is corrupted ++number_of_modified_items; continue; }; }; #endif //_NA_007071_20140219_MODIFY_UNUSE_ARMOR_DESTROY_PENALTY NotifyChangeDurability(equip_pos, item_code, changed_durability); UpdateItemAttributesOnChangesEquippedItemStatus(*item_slot); // ++number_of_modified_items; }; // DEBUG_INFO: number_of_modified_items return true; } bool nsSlot::DurabilityImpl::DecreaseDuraAll(float decrease_ratio) { typedef SCItemSlotContainer::SLOT_LINEAR_LIST SLOT_LINEAR_LIST; // if (decrease_ratio == 0.0f) { return false; }; // PC¹æÀÎ °æ¿ì ³»±¸µµ CanApplyPCBangBenefit°¨¼Ò·® 50% if (root_->player_->CanApplyPCBangBenefit(true)) { decrease_ratio *= 0.5f; } // CEquipmentSlotContainer* const equip_container = static_cast(root_->GetItemSlotContainer(SI_EQUIPMENT)); const SLOT_LINEAR_LIST& slot_list = equip_container->GetSlotList(); SLOT_LINEAR_LIST::const_iterator it = slot_list.begin(); const SLOT_LINEAR_LIST::const_iterator end = slot_list.end(); int number_of_modified_items = 0; bool update_all = false; for (POSTYPE equip_pos = 0; it != end; ++equip_pos, ++it) { SCItemSlot* item_slot = static_cast(it->slot); SLOTCODE item_code = item_slot->GetItemCode(); if (item_code == 0) { continue; }; if (item_slot->WithDura() == false) { continue; }; // NOTE: 'IsLocked' is set game rules, and 'IsBlocked' is forced by abusing control if (item_slot->IsLocked() || item_slot->IsBlocked()) { continue; // no check LaxLock }; const DURATYPE durability = item_slot->GetDura(); #ifdef _NA_007071_20140219_MODIFY_UNUSE_ARMOR_DESTROY_PENALTY #else if (durability == 0) { if (DeleteEquipByDura(equip_pos)) { ++number_of_modified_items; continue; }; }; #endif //_NA_007071_20140219_MODIFY_UNUSE_ARMOR_DESTROY_PENALTY DURATYPE decreasing_dura = static_cast(item_slot->GetMaxDura() * decrease_ratio); ; decreasing_dura = max(1, decreasing_dura); DURATYPE changed_dura = ((durability > decreasing_dura) ? (durability - decreasing_dura) : (0)); // assertion (decreasing_dura != 0) item_slot->SetDura(changed_dura); NotifyChangeDurability(equip_pos, item_code, changed_dura); if (changed_dura == durability) { // SetDura(0) update_all = true; } else { #ifdef __NA_001062_20080623_STRENGTH_PENALTY if (update_all == false) { UpdateItemAttributesOnChangesEquippedItemStatus(*item_slot); }; #endif } ++number_of_modified_items; }; // DEBUG_INFO: number_of_modified_items #ifdef _NA_000000_20120417_EQUIPMENTS_CALCULATOR_CHANGES_FOR_DEAD //±×³É ¹«Á¶°Ç ¾÷µ¥ÀÌÅÍ ÇÑ´Ù. root_->UpdateItemAttributes(true, false); #else if (update_all) { #ifdef _NA_002289_20110322_EQUIPMENTS_CALCULATOR_CHANGES_FOR_IMPROVEMENT root_->UpdateItemAttributes(true, false); #else root_->UpdateAttrForEquipItem(); #endif } else { // already processed in loop }; #endif //_NA_000000_20120417_EQUIPMENTS_CALCULATOR_CHANGES_FOR_DEAD return true; } RC::eITEM_RESULT nsSlot::DurabilityImpl::Repair(bool total_repairs, SLOTIDX slot_index, POSTYPE slot_pos) { Player* const player = root_->player_; //---------------------------------------------------------------------------------------------- if (root_->IsRestrictFunction(eFUNCTIONALRESTRICT_REPAIR_ITEM)) { return RC::RC_ITEM_UNUSABLE_FUNCTION; }; if (player->GetChaoState().CanUseFuction(CHAO_USE_STORE) == false) { return RC::RC_ITEM_UNABLE_FUNCTION_FOR_CHAOSTATE; }; if (player->GetBehave() == PLAYER_BEHAVE_UNEXPECTED_WAITING) { return RC::RC_ITEM_INVALIDSTATEOFPLAYER; }; //---------------------------------------------------------------------------------------------- if (total_repairs) { slot_index = SI_EQUIPMENT; }; //---------------------------------------------------------------------------------------------- if (root_->ValidState() == false) { return RC::RC_ITEM_INVALIDSTATE; }; if (total_repairs == false && root_->ValidPos(slot_index, slot_pos) == false) { return RC::RC_ITEM_INVALIDPOS; }; //---------------------------------------------------------------------------------------------- MONEY cost_of_total_repairs = 0ULL; SCItemSlotContainer* const item_container = root_->GetItemSlotContainer(slot_index); typedef SCItemSlotContainer::SLOT_LINEAR_LIST SLOT_LINEAR_LIST; const SLOT_LINEAR_LIST& slot_list = item_container->GetSlotList(); //---------------------------------------------------------------------------------------------- if (total_repairs == false) { SCItemSlot& item_slot = static_cast(*slot_list[slot_pos].slot); if (item_slot.IsDestroyItem()) { return RC::RC_ITEM_CANNOT_REPAIR_ITEM; } if (item_slot.WithDura() == false) { return RC::RC_ITEM_CANNOT_REPAIR_ITEM; }; DURATYPE durability = item_slot.GetDura(); DURATYPE max_durability = item_slot.GetMaxDura(); if (durability == max_durability) { return RC::RC_ITEM_DONT_NEED_TO_REPAIR; }; if (item_slot.IsLimited()) { if (item_slot.DecreasingMaxDura() == false) { return RC::RC_ITEM_CANNOT_REPAIR_ITEM; } }; cost_of_total_repairs += item_slot.GetPriceForRepair(); #ifdef _NA_007330_20140620_GUILD_SYSTEM_EXTENSION // »óÁ¡ ¼ö¸® ÇÏÀÓ Áõ°¨ ¾îÆ®¸®ºäÆ® Àû¿ë cost_of_total_repairs = player->GetAttr().GetValueAppliedAttr(cost_of_total_repairs,\ eATTR_SHOP_REPAIR_HEIM_RATIO); #endif // _NA_007330_20140620_GUILD_SYSTEM_EXTENSION // ¼ö¸®ºñ°¡ ÀÖ´ÂÁö üũ if (player->GetMoney() < cost_of_total_repairs) { return RC::RC_ITEM_HAVENOTMONEY; } // item_slot.SetDura(max_durability); GAMELOG->LogItem(ITEM_DURA, player, &item_slot, cost_of_total_repairs); //_NA_007079_BUG_WEAPON_INACTIVATE_STATE // ¼ö¸®Çϱâ ÀüÀÇ ³»±¸µµ°¡ 0À̾ú´Ù¸é, ¼ö¸® ÈÄ AddWeaponÇÔ¼ö¸¦ È£ÃâÇÏ¿© ½ºÅ¸ÀÏÀ» º¯°æ½ÃŲ´Ù if (slot_pos == eEQUIPCONTAINER_WEAPON && durability == 0) { player->GetSkillSlotManager()->AddWeapon(item_slot.GetItemCode()); } // UpdateItemAttributesOnChangesEquippedItemStatus(item_slot); // } //---------------------------------------------------------------------------------------------- else //---------------------------------------------------------------------------------------------- { //total_repairs != false SLOT_LINEAR_LIST::const_iterator it = slot_list.begin(); const SLOT_LINEAR_LIST::const_iterator end = slot_list.end(); for ( ; it != end; ++it) { SCItemSlot& item_slot = static_cast(*it->slot); if (item_slot.GetItemCode() == 0) { continue; }; if (item_slot.IsDestroyItem()) { continue; } if (!item_slot.WithDura()) { continue; } if (item_slot.GetDura() == item_slot.GetMaxDura()) { continue; } if (item_slot.IsLimited()) { if (item_slot.GetLimitedDuraMax() <= 2) { continue; } } // cost_of_total_repairs += item_slot.GetPriceForRepair(); } #ifdef _NA_007330_20140620_GUILD_SYSTEM_EXTENSION // »óÁ¡ ¼ö¸® ÇÏÀÓ Áõ°¨ ¾îÆ®¸®ºäÆ® Àû¿ë cost_of_total_repairs = player->GetAttr().GetValueAppliedAttr(cost_of_total_repairs,\ eATTR_SHOP_REPAIR_HEIM_RATIO); #endif // _NA_007330_20140620_GUILD_SYSTEM_EXTENSION // check whether the player have money enough if (player->GetMoney() < cost_of_total_repairs) { return RC::RC_ITEM_HAVENOTMONEY; } // do repair RepairAllUsingByRepairsItem(); // }; //---------------------------------------------------------------------------------------------- // result arrangement ;{ player->MinusMoney(cost_of_total_repairs); GAMELOG->LogMoney(MONEY_SPEND_REPAIR, player, cost_of_total_repairs); g_pGameServer->NotifyHeimConsumption(player->GetCharGuid(), eHEIM_CONSUMPTION_REPAIR, cost_of_total_repairs); }; // return RC::RC_ITEM_SUCCESS; } // NOTE: the usage case is it of eITEMTYPE_REPAIR item or it of a NPC total repair request. // if the result is fails, any items have been repaired. bool nsSlot::DurabilityImpl::RepairAllUsingByRepairsItem() { #if defined(_NA_002289_20110322_EQUIPMENTS_CALCULATOR_CHANGES_FOR_IMPROVEMENT) typedef SCItemSlotContainer::SLOT_LINEAR_LIST SLOT_LINEAR_LIST; CEquipmentSlotContainer* const equip_container = static_cast(root_->GetItemSlotContainer(SI_EQUIPMENT)); const SLOT_LINEAR_LIST& slot_list = equip_container->GetSlotList(); SLOT_LINEAR_LIST::const_iterator it = slot_list.begin(); const SLOT_LINEAR_LIST::const_iterator end = slot_list.end(); int number_of_repairs = 0; for ( ; it != end; ++it) { SCItemSlot* item_slot = static_cast(it->slot); if (item_slot->GetItemCode() == 0) { continue; }; if (item_slot->IsBlocked()) { continue; }; if (item_slot->WithDura() == false) { continue; }; if (item_slot->IsLimited()) { if (item_slot->DecreasingMaxDura() == false) { continue; }; } const DURATYPE cur_durability = item_slot->GetDura(); const DURATYPE max_durability = item_slot->GetMaxDura(); if (cur_durability == max_durability) { continue; }; // item_slot->SetDura(max_durability); GAMELOG->LogItem(ITEM_DURA, root_->player_, item_slot, 0); //_NA_007079_BUG_WEAPON_INACTIVATE_STATE // ¼ö¸®Çϱâ ÀüÀÇ ³»±¸µµ°¡ 0À̾ú´Ù¸é, ¼ö¸® ÈÄ AddWeaponÇÔ¼ö¸¦ È£ÃâÇÏ¿© ½ºÅ¸ÀÏÀ» º¯°æ½ÃŲ´Ù POSTYPE item_pos = item_slot->GetPos(); if (item_pos == eEQUIPCONTAINER_WEAPON && cur_durability == 0) { root_->player_->GetSkillSlotManager()->AddWeapon(item_slot->GetItemCode()); } // ++number_of_repairs; // }; // if (number_of_repairs == 0) { return false; }; // success root_->UpdateItemAttributes(true, false); // return true; // #else //if !defined(_NA_002289_20110322_EQUIPMENTS_CALCULATOR_CHANGES_FOR_IMPROVEMENT) CEquipmentSlotContainer* const equip_container = static_cast(root_->GetItemSlotContainer(SI_EQUIPMENT)); ItemAttrCalculator itemCalc(root_->player_->GetPlayerAttr(), *equip_container); bool repaired = false; // ¼ö¸®ÇÑ´Ù. const POSTYPE number_of_max_slots = equip_container->GetMaxSlotNum(); DURATYPE current_durability = 0; for (POSTYPE i = 0 ; i < number_of_max_slots; ++i) { if (equip_container->IsEmpty(i)) { continue; }; SCItemSlot& item_slot = static_cast(equip_container->GetSlot(i)); if (item_slot.IsBlocked()) { continue; //< ºí·°µÈ ¾ÆÀÌÅÛÀϰæ¿ì ¼ö¸®¾ÈÇÔ }; if (!item_slot.WithDura()) { continue; }; current_durability = item_slot.GetDura(); if (current_durability == item_slot.GetMaxDura()) { continue; }; GAMELOG->LogItem(ITEM_DURA, root_->player_, &item_slot, 0); if (item_slot.IsLimited()) { if (!item_slot.DecreasingMaxDura()) { continue; } } item_slot.SetDura(item_slot.GetMaxDura()); repaired = true; } if (repaired) { root_->UpdateAttrForEquipItem(); }; return repaired; #endif //_NA_002289_20110322_EQUIPMENTS_CALCULATOR_CHANGES_FOR_IMPROVEMENT } //==================================================================================================