Core/Misc: Improvement item durability repair logic.

Port From (https://github.com/TrinityCore/TrinityCore/commit/72a4fccaab4e83801c33c533cbcfdba1c6c43022)
This commit is contained in:
hondacrx
2022-05-29 23:23:54 -04:00
parent ba7b2e42a3
commit 44b70d8db9
4 changed files with 156 additions and 84 deletions
+39
View File
@@ -850,6 +850,45 @@ namespace Game.Entities
}
}
public ulong CalculateDurabilityRepairCost(float discount)
{
uint maxDurability = m_itemData.MaxDurability;
if (maxDurability == 0)
return 0;
uint curDurability = m_itemData.Durability;
Cypher.Assert(maxDurability >= curDurability);
uint lostDurability = maxDurability - curDurability;
if (lostDurability == 0)
return 0;
ItemTemplate itemTemplate = GetTemplate();
var durabilityCost = CliDB.DurabilityCostsStorage.LookupByKey(GetItemLevel(GetOwner()));
if (durabilityCost == null)
return 0;
uint durabilityQualityEntryId = ((uint)GetQuality() + 1) * 2;
var durabilityQualityEntry = CliDB.DurabilityQualityStorage.LookupByKey(durabilityQualityEntryId);
if (durabilityQualityEntry == null)
return 0;
uint dmultiplier = 0;
if (itemTemplate.GetClass() == ItemClass.Weapon)
dmultiplier = durabilityCost.WeaponSubClassCost[itemTemplate.GetSubClass()];
else if (itemTemplate.GetClass() == ItemClass.Armor)
dmultiplier = durabilityCost.ArmorSubClassCost[itemTemplate.GetSubClass()];
ulong cost = (ulong)Math.Round(lostDurability * dmultiplier * durabilityQualityEntry.Data * GetRepairCostMultiplier());
cost = (ulong)(cost * discount * WorldConfig.GetFloatValue(WorldCfg.RateRepaircost));
if (cost == 0) // Fix for ITEM_QUALITY_ARTIFACT
cost = 1;
return cost;
}
bool HasEnchantRequiredSkill(Player player)
{
// Check all enchants for required skill
+107 -83
View File
@@ -385,102 +385,126 @@ namespace Game.Entities
if (pItem != null)
DurabilityPointsLoss(pItem, 1);
}
public uint DurabilityRepairAll(bool cost, float discountMod, bool guildBank)
public void DurabilityRepairAll(bool takeCost, float discountMod, bool guildBank)
{
uint TotalCost = 0;
// Collecting all items that can be repaired and repair costs
List<(Item item, ulong cost)> itemRepairCostStore = new();
// equipped, backpack, bags itself
int inventoryEnd = InventorySlots.ItemStart + GetInventorySlotCount();
for (byte i = EquipmentSlot.Start; i < inventoryEnd; i++)
TotalCost += DurabilityRepair((ushort)((InventorySlots.Bag0 << 8) | i), cost, discountMod, guildBank);
// items in inventory bags
for (byte j = InventorySlots.BagStart; j < InventorySlots.BagEnd; j++)
for (byte i = 0; i < ItemConst.MaxBagSize; i++)
TotalCost += DurabilityRepair((ushort)((j << 8) | i), cost, discountMod, guildBank);
return TotalCost;
}
public uint DurabilityRepair(ushort pos, bool cost, float discountMod, bool guildBank)
{
Item item = GetItemByPos(pos);
uint TotalCost = 0;
if (item == null)
return TotalCost;
uint maxDurability = item.m_itemData.MaxDurability;
if (maxDurability == 0)
return TotalCost;
uint curDurability = item.m_itemData.Durability;
if (cost)
{
uint LostDurability = maxDurability - curDurability;
if (LostDurability > 0)
Item item = GetItemByPos((ushort)((InventorySlots.Bag0 << 8) | i));
if (item != null)
{
ItemTemplate ditemProto = item.GetTemplate();
DurabilityCostsRecord dcost = CliDB.DurabilityCostsStorage.LookupByKey(ditemProto.GetBaseItemLevel());
if (dcost == null)
{
Log.outError(LogFilter.Player, "RepairDurability: Wrong item lvl {0}", ditemProto.GetBaseItemLevel());
return TotalCost;
}
uint dQualitymodEntryId = (uint)(ditemProto.GetQuality() + 1) * 2;
DurabilityQualityRecord dQualitymodEntry = CliDB.DurabilityQualityStorage.LookupByKey(dQualitymodEntryId);
if (dQualitymodEntry == null)
{
Log.outError(LogFilter.Player, "RepairDurability: Wrong dQualityModEntry {0}", dQualitymodEntryId);
return TotalCost;
}
uint dmultiplier = 0;
if (ditemProto.GetClass() == ItemClass.Weapon)
dmultiplier = dcost.WeaponSubClassCost[ditemProto.GetSubClass()];
else if (ditemProto.GetClass() == ItemClass.Armor)
dmultiplier = dcost.ArmorSubClassCost[ditemProto.GetSubClass()];
uint costs = (uint)(LostDurability * dmultiplier * (double)dQualitymodEntry.Data * item.GetRepairCostMultiplier());
costs = (uint)(costs * discountMod * WorldConfig.GetFloatValue(WorldCfg.RateRepaircost));
if (costs == 0) //fix for ITEM_QUALITY_ARTIFACT
costs = 1;
if (guildBank)
{
if (GetGuildId() == 0)
{
Log.outDebug(LogFilter.Player, "You are not member of a guild");
return TotalCost;
}
var guild = Global.GuildMgr.GetGuildById(GetGuildId());
if (guild == null)
return TotalCost;
if (!guild.HandleMemberWithdrawMoney(GetSession(), costs, true))
return TotalCost;
TotalCost = costs;
}
else if (!HasEnoughMoney(costs))
{
Log.outDebug(LogFilter.Player, "You do not have enough money");
return TotalCost;
}
else
ModifyMoney(-costs);
ulong cost = item.CalculateDurabilityRepairCost(discountMod);
if (cost != 0)
itemRepairCostStore.Add((item, cost));
}
}
item.SetDurability(maxDurability);
// items in inventory bags
for (byte j = InventorySlots.BagStart; j < InventorySlots.BagEnd; j++)
{
for (byte i = 0; i < ItemConst.MaxBagSize; i++)
{
Item item = GetItemByPos((ushort)((j << 8) | i));
if (item != null)
{
ulong cost = item.CalculateDurabilityRepairCost(discountMod);
if (cost != 0)
itemRepairCostStore.Add((item, cost));
}
}
}
// Handling a free repair case - just repair every item without taking cost.
if (!takeCost)
{
foreach (var (item, _) in itemRepairCostStore)
DurabilityRepair(item.GetPos(), false, 0.0f);
return;
}
if (guildBank)
{
// Handling a repair for guild money case.
// We have to repair items one by one until the guild bank has enough money available for withdrawal or until all items are repaired.
Guild guild = GetGuild();
if (guild == null)
return; // silent return, client shouldn't display this button for players without guild.
ulong availableGuildMoney = guild.GetMemberAvailableMoneyForRepairItems(GetGUID());
if (availableGuildMoney == 0)
return;
// Sort the items by repair cost from lowest to highest
itemRepairCostStore.OrderByDescending(a => a.cost);
// We must calculate total repair cost and take money once to avoid spam in the guild bank log and reduce number of transactions in the database
ulong totalCost = 0;
foreach (var (item, cost) in itemRepairCostStore)
{
ulong newTotalCost = totalCost + cost;
if (newTotalCost > availableGuildMoney || newTotalCost > PlayerConst.MaxMoneyAmount)
break;
totalCost = newTotalCost;
// Repair item without taking cost. We'll do it later.
DurabilityRepair(item.GetPos(), false, 0.0f);
}
// Take money for repairs from the guild bank
guild.HandleMemberWithdrawMoney(GetSession(), totalCost, true);
}
else
{
// Handling a repair for player's money case.
// Unlike repairing for guild money, in this case we must first check if player has enough money to repair all the items at once.
ulong totalCost = 0;
foreach (var (_, cost) in itemRepairCostStore)
totalCost += cost;
if (!HasEnoughMoney(totalCost))
return; // silent return, client should display error by itself and not send opcode.
ModifyMoney(-(int)totalCost);
// Payment for repair has already been taken, so just repair every item without taking cost.
foreach (var (item, cost) in itemRepairCostStore)
DurabilityRepair(item.GetPos(), false, 0.0f);
}
}
public void DurabilityRepair(ushort pos, bool takeCost, float discountMod)
{
Item item = GetItemByPos(pos);
if (item == null)
return;
if (takeCost)
{
ulong cost = item.CalculateDurabilityRepairCost(discountMod);
if (!HasEnoughMoney(cost))
{
Log.outDebug(LogFilter.PlayerItems, $"Player::DurabilityRepair: Player '{GetName()}' ({GetGUID()}) has not enough money to repair item");
return;
}
ModifyMoney(-(int)cost);
}
bool isBroken = item.IsBroken();
item.SetDurability(item.m_itemData.MaxDurability);
item.SetState(ItemUpdateState.Changed, this);
// reapply mods for total broken and repaired item if equipped
if (IsEquipmentPos(pos) && curDurability == 0)
if (IsEquipmentPos(pos) && isBroken)
_ApplyItemMods(item, (byte)(pos & 255), true);
return TotalCost;
}
//Store Item
+9
View File
@@ -1758,6 +1758,15 @@ namespace Game.Guilds
return m_members.ContainsKey(guid);
}
public ulong GetMemberAvailableMoneyForRepairItems(ObjectGuid guid)
{
Member member = GetMember(guid);
if (member == null)
return 0;
return Math.Min(m_bankMoney, (ulong)_GetMemberRemainingMoney(member));
}
public void SwapItems(Player player, byte tabId, byte slotId, byte destTabId, byte destSlotId, uint splitedAmount)
{
if (tabId >= _GetPurchasedTabsSize() || slotId >= GuildConst.MaxBankSlots ||
+1 -1
View File
@@ -570,7 +570,7 @@ namespace Game
Item item = GetPlayer().GetItemByGuid(packet.ItemGUID);
if (item)
GetPlayer().DurabilityRepair(item.GetPos(), true, discountMod, packet.UseGuildBank);
GetPlayer().DurabilityRepair(item.GetPos(), true, discountMod);
}
else
{