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

626 lines
22 KiB
C++

#include "stdafx.h"
#include "./ItemManager_Durability.h"
#include "ItemManager.h"
//==================================================================================================
#include <ItemInfoParser.h>
#include <ItemAttrCalculator.h>
#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<CEquipmentSlotContainer*>(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<CEquipmentSlotContainer*>(root_->GetItemSlotContainer(SI_EQUIPMENT));
SCItemSlot& item_slot = static_cast<SCItemSlot&>(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<CEquipmentSlotContainer*>(root_->GetItemSlotContainer(SI_EQUIPMENT));
SCItemSlot& item_slot = static_cast<SCItemSlot&>(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<CEquipmentSlotContainer*>(root_->GetItemSlotContainer(SI_EQUIPMENT));
SCItemSlot& item_slot = static_cast<SCItemSlot&>(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<eEQUIPCONTAINER_POS>(last_equip_pos + 1)
: eEQUIPCONTAINER_MAX;
//
CEquipmentSlotContainer* const equip_container =
static_cast<CEquipmentSlotContainer*>(root_->GetItemSlotContainer(SI_EQUIPMENT));
const SLOT_LINEAR_LIST& slot_list = equip_container->GetSlotList();
assert(slot_list.size() >= static_cast<SLOT_LINEAR_LIST::size_type>(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<SCItemSlot*>(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<CEquipmentSlotContainer*>(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<SCItemSlot*>(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<DURATYPE>(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<SCItemSlot&>(*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<SCItemSlot&>(*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<CEquipmentSlotContainer*>(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<SCItemSlot*>(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<CEquipmentSlotContainer*>(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<SCItemSlot&>(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
}
//==================================================================================================