From b0b1ccddd1c37837213638d203d39d3af621a85d Mon Sep 17 00:00:00 2001 From: KingCol13 <48412633+KingCol13@users.noreply.github.com> Date: Sat, 10 Oct 2020 22:23:25 +0300 Subject: [PATCH] Anvil fixes (#4976) * Rewrite to use wiki target/sacrifice terminology. * Fix negative damages. * Prevent repairing if output matches target. * Make target and sacrifice const. * Pre-PR tidy-up * Keep m_MaximumCost updated. --- src/Enchantments.cpp | 2 +- src/Enchantments.h | 2 +- src/UI/SlotArea.cpp | 108 ++++++++++++++++++++++++------------------- 3 files changed, 63 insertions(+), 49 deletions(-) diff --git a/src/Enchantments.cpp b/src/Enchantments.cpp index e1609bf62..851fe3a0f 100644 --- a/src/Enchantments.cpp +++ b/src/Enchantments.cpp @@ -85,7 +85,7 @@ void cEnchantments::AddFromString(const AString & a_StringSpec) -size_t cEnchantments::Count(void) +size_t cEnchantments::Count(void) const { return m_Enchantments.size(); } diff --git a/src/Enchantments.h b/src/Enchantments.h index 1f4547058..bdb27d677 100644 --- a/src/Enchantments.h +++ b/src/Enchantments.h @@ -88,7 +88,7 @@ public: void AddFromString(const AString & a_StringSpec); /** Get the count of enchantments */ - size_t Count(void); + size_t Count(void) const; /** Serializes all the enchantments into a string */ AString ToString(void) const; diff --git a/src/UI/SlotArea.cpp b/src/UI/SlotArea.cpp index a452227f8..c6c06b084 100644 --- a/src/UI/SlotArea.cpp +++ b/src/UI/SlotArea.cpp @@ -1127,84 +1127,91 @@ void cSlotAreaAnvil::OnPlayerRemoved(cPlayer & a_Player) void cSlotAreaAnvil::UpdateResult(cPlayer & a_Player) { - cItem Input(*GetSlot(0, a_Player)); - cItem SecondInput(*GetSlot(1, a_Player)); - cItem Output(*GetSlot(2, a_Player)); + const cItem Target(*GetSlot(0, a_Player)); + const cItem Sacrifice(*GetSlot(1, a_Player)); - if (Input.IsEmpty()) + // Output initialised as copy of target + cItem Output(Target); + + if (Target.IsEmpty()) { Output.Empty(); SetSlot(2, a_Player, Output); m_ParentWindow.SetProperty(0, 0); + m_MaximumCost = 0; return; } m_MaximumCost = 0; m_StackSizeToBeUsedInRepair = 0; - int RepairCost = Input.m_RepairCost; + int RepairCost = Target.m_RepairCost; int NeedExp = 0; - if (!SecondInput.IsEmpty()) + if (!Sacrifice.IsEmpty()) { - bool IsEnchantBook = (SecondInput.m_ItemType == E_ITEM_ENCHANTED_BOOK); + bool IsEnchantBook = (Sacrifice.m_ItemType == E_ITEM_ENCHANTED_BOOK); - RepairCost += SecondInput.m_RepairCost; - if (Input.IsDamageable() && cItemHandler::GetItemHandler(Input)->CanRepairWithRawMaterial(SecondInput.m_ItemType)) + RepairCost += Sacrifice.m_RepairCost; + // Can we repair with sacrifce material? + if (Target.IsDamageable() && cItemHandler::GetItemHandler(Target)->CanRepairWithRawMaterial(Sacrifice.m_ItemType)) { // Tool and armor repair with special item (iron / gold / diamond / ...) - int DamageDiff = std::min(static_cast(Input.m_ItemDamage), static_cast(Input.GetMaxDamage()) / 4); + int DamageDiff = std::min(static_cast(Target.m_ItemDamage), static_cast(Target.GetMaxDamage()) / 4); if (DamageDiff <= 0) { // No enchantment Output.Empty(); SetSlot(2, a_Player, Output); m_ParentWindow.SetProperty(0, 0); + m_MaximumCost = 0; return; } - int x = 0; - while ((DamageDiff > 0) && (x < SecondInput.m_ItemCount)) + int NumItemsConsumed = 0; + // Repair until out of materials, or fully repaired + while ((DamageDiff > 0) && (NumItemsConsumed < Sacrifice.m_ItemCount)) { - Input.m_ItemDamage -= DamageDiff; - NeedExp += std::max(1, DamageDiff / 100) + static_cast(Input.m_Enchantments.Count()); - DamageDiff = std::min(static_cast(Input.m_ItemDamage), static_cast(Input.GetMaxDamage()) / 4); + Output.m_ItemDamage -= DamageDiff; + NeedExp += std::max(1, DamageDiff / 100) + static_cast(Target.m_Enchantments.Count()); + DamageDiff = std::min(static_cast(Output.m_ItemDamage), static_cast(Target.GetMaxDamage()) / 4); - ++x; + ++NumItemsConsumed; } - m_StackSizeToBeUsedInRepair = static_cast(x); + m_StackSizeToBeUsedInRepair = static_cast(NumItemsConsumed); } - else + else // Combining tools / armour { - // Tool and armor repair with two tools / armors - if (!IsEnchantBook && (!Input.IsSameType(SecondInput) || !Input.IsDamageable())) + // No result if we can't combine the items + if (!IsEnchantBook && (!Target.IsSameType(Sacrifice) || !Target.IsDamageable())) { // No enchantment Output.Empty(); SetSlot(2, a_Player, Output); m_ParentWindow.SetProperty(0, 0); + m_MaximumCost = 0; return; } - if ((Input.GetMaxDamage() > 0) && !IsEnchantBook) + // Can we repair with sacrifice tool / armour? + if (Target.IsDamageable() && !IsEnchantBook && (Target.m_ItemDamage!=0)) { - int FirstDamageDiff = Input.GetMaxDamage() - Input.m_ItemDamage; - int SecondDamageDiff = SecondInput.GetMaxDamage() - SecondInput.m_ItemDamage; - int Damage = SecondDamageDiff + Input.GetMaxDamage() * 12 / 100; + // Durability = MaxDamage - m_ItemDamage = how far from broken + const short TargetDurability = Target.GetMaxDamage() - Target.m_ItemDamage; + const short SacrificeDurability = Sacrifice.GetMaxDamage() - Sacrifice.m_ItemDamage; + // How much durability to repair by: + const short RepairDurability = SacrificeDurability + Target.GetMaxDamage() * 12 / 100; - int NewItemDamage = Input.GetMaxDamage() - (FirstDamageDiff + Damage); - if (NewItemDamage > 0) - { - NewItemDamage = 0; - } + // Don't give item a negative damage: + short NewItemDamage = std::max(Target.GetMaxDamage() - (TargetDurability + RepairDurability), 0); - if (NewItemDamage < Input.m_ItemDamage) + if (NewItemDamage < Target.m_ItemDamage) { - Input.m_ItemDamage = static_cast(NewItemDamage); - NeedExp += std::max(1, Damage / 100); + Output.m_ItemDamage = NewItemDamage; + NeedExp += std::max(1, RepairDurability / 100); } } // Add the enchantments from the sacrifice to the target - int EnchantmentCost = Input.AddEnchantmentsFromItem(SecondInput); + int EnchantmentCost = Output.AddEnchantmentsFromItem(Sacrifice); NeedExp += EnchantmentCost; } } @@ -1214,32 +1221,32 @@ void cSlotAreaAnvil::UpdateResult(cPlayer & a_Player) if (RepairedItemName.empty()) { // Remove custom name - if (!Input.m_CustomName.empty()) + if (!Target.m_CustomName.empty()) { - NameChangeExp = (Input.IsDamageable()) ? 7 : (Input.m_ItemCount * 5); + NameChangeExp = (Target.IsDamageable()) ? 7 : (Target.m_ItemCount * 5); NeedExp += NameChangeExp; - Input.m_CustomName = ""; + Output.m_CustomName = ""; } } - else if (RepairedItemName != Input.m_CustomName) + else if (RepairedItemName != Target.m_CustomName) { // Change custom name - NameChangeExp = (Input.IsDamageable()) ? 7 : (Input.m_ItemCount * 5); + NameChangeExp = (Target.IsDamageable()) ? 7 : (Target.m_ItemCount * 5); NeedExp += NameChangeExp; - if (!Input.m_CustomName.empty()) + if (!Target.m_CustomName.empty()) { RepairCost += NameChangeExp / 2; } - Input.m_CustomName = RepairedItemName; + Output.m_CustomName = RepairedItemName; } m_MaximumCost = RepairCost + NeedExp; if (NeedExp < 0) { - Input.Empty(); + Output.Empty(); } if ((NameChangeExp == NeedExp) && (NameChangeExp > 0) && (m_MaximumCost >= 40)) @@ -1248,22 +1255,29 @@ void cSlotAreaAnvil::UpdateResult(cPlayer & a_Player) } if ((m_MaximumCost >= 40) && !a_Player.IsGameModeCreative()) { - Input.Empty(); + Output.Empty(); } - if (!Input.IsEmpty()) + if (!Output.IsEmpty()) { - RepairCost = std::max(Input.m_RepairCost, SecondInput.m_RepairCost); - if (!Input.m_CustomName.empty()) + RepairCost = std::max(Target.m_RepairCost, Sacrifice.m_RepairCost); + if (!Output.m_CustomName.empty()) { RepairCost -= 9; } RepairCost = std::max(RepairCost, 0); RepairCost += 2; - Input.m_RepairCost = RepairCost; + Output.m_RepairCost = RepairCost; } - SetSlot(2, a_Player, Input); + // If after everything, output will be the same then no point enchanting + if (Target.IsEqual(Output)) + { + Output.Empty(); + m_MaximumCost = 0; + } + + SetSlot(2, a_Player, Output); m_ParentWindow.SetProperty(0, static_cast(m_MaximumCost)); }