summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Enchantments.cpp208
-rw-r--r--src/Enchantments.h20
-rw-r--r--src/Item.cpp199
-rw-r--r--src/Item.h17
-rw-r--r--src/UI/SlotArea.cpp9
5 files changed, 418 insertions, 35 deletions
diff --git a/src/Enchantments.cpp b/src/Enchantments.cpp
index a18f6d68a..0047d09b8 100644
--- a/src/Enchantments.cpp
+++ b/src/Enchantments.cpp
@@ -167,6 +167,164 @@ bool cEnchantments::IsEmpty(void) const
+unsigned int cEnchantments::GetLevelCap(int a_EnchantmentID)
+{
+ switch (a_EnchantmentID)
+ {
+ case enchProtection: return 4;
+ case enchFireProtection: return 4;
+ case enchFeatherFalling: return 4;
+ case enchBlastProtection: return 4;
+ case enchProjectileProtection: return 4;
+ case enchRespiration: return 3;
+ case enchAquaAffinity: return 1;
+ case enchThorns: return 3;
+ case enchDepthStrider: return 3;
+ case enchSharpness: return 5;
+ case enchSmite: return 5;
+ case enchBaneOfArthropods: return 5;
+ case enchKnockback: return 2;
+ case enchFireAspect: return 2;
+ case enchLooting: return 3;
+ case enchEfficiency: return 5;
+ case enchSilkTouch: return 1;
+ case enchUnbreaking: return 3;
+ case enchFortune: return 3;
+ case enchPower: return 5;
+ case enchPunch: return 2;
+ case enchFlame: return 1;
+ case enchInfinity: return 1;
+ case enchLuckOfTheSea: return 3;
+ case enchLure: return 3;
+ }
+ LOGWARNING("Unknown enchantment ID %d", a_EnchantmentID);
+ return 0;
+}
+
+
+
+
+
+int cEnchantments::GetXPCostMultiplier(int a_EnchantmentID, bool FromBook)
+{
+ if (FromBook)
+ {
+ switch (a_EnchantmentID)
+ {
+ case enchProtection: return 1;
+ case enchFireProtection: return 1;
+ case enchFeatherFalling: return 1;
+ case enchBlastProtection: return 2;
+ case enchProjectileProtection: return 1;
+ case enchRespiration: return 2;
+ case enchAquaAffinity: return 2;
+ case enchThorns: return 4;
+ case enchDepthStrider: return 2;
+ case enchSharpness: return 1;
+ case enchSmite: return 1;
+ case enchBaneOfArthropods: return 1;
+ case enchKnockback: return 1;
+ case enchFireAspect: return 2;
+ case enchLooting: return 2;
+ case enchEfficiency: return 1;
+ case enchSilkTouch: return 4;
+ case enchUnbreaking: return 1;
+ case enchFortune: return 1;
+ case enchPower: return 1;
+ case enchPunch: return 2;
+ case enchFlame: return 2;
+ case enchInfinity: return 4;
+ case enchLuckOfTheSea: return 2;
+ case enchLure: return 2;
+ }
+ }
+ else // Without book
+ {
+ switch (a_EnchantmentID)
+ {
+ case enchProtection: return 1;
+ case enchFireProtection: return 2;
+ case enchFeatherFalling: return 2;
+ case enchBlastProtection: return 4;
+ case enchProjectileProtection: return 2;
+ case enchRespiration: return 4;
+ case enchAquaAffinity: return 4;
+ case enchThorns: return 8;
+ case enchDepthStrider: return 4;
+
+ case enchSharpness: return 1;
+ case enchSmite: return 2;
+ case enchBaneOfArthropods: return 2;
+ case enchKnockback: return 2;
+ case enchFireAspect: return 4;
+ case enchLooting: return 4;
+
+ case enchEfficiency: return 1;
+ case enchSilkTouch: return 8;
+ case enchUnbreaking: return 2;
+ case enchFortune: return 4;
+ case enchPower: return 1;
+ case enchPunch: return 4;
+ case enchFlame: return 4;
+ case enchInfinity: return 8;
+ case enchLuckOfTheSea: return 4;
+ case enchLure: return 4;
+ }
+ }
+ LOGWARNING("Unknown enchantment ID %d", a_EnchantmentID);
+ return 0;
+}
+
+
+
+
+
+bool cEnchantments::CanAddEnchantment(int a_EnchantmentID) const
+{
+ if (GetLevel(a_EnchantmentID) > 0)
+ {
+ return true;
+ }
+
+ static const std::vector<std::set<int> > IncompatibleEnchantments =
+ {
+ // Armor
+ { enchProtection, enchFireProtection, enchBlastProtection, enchProjectileProtection },
+
+ // Tool
+ { enchFortune, enchSilkTouch },
+
+ // Sword
+ { enchSharpness, enchSmite, enchBaneOfArthropods },
+
+ // Boots
+ // {enchDepthStrider, enchFrostWalker},
+
+ // Bow
+ // {enchInfinity, enchMending}
+ };
+
+ for (auto excl: IncompatibleEnchantments)
+ {
+ if (excl.count(a_EnchantmentID) != 0)
+ {
+ // See if we also have any of the enchantments
+ for (auto ench: excl)
+ {
+ if (GetLevel(ench) > 0)
+ {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+
+
+
+
int cEnchantments::StringToEnchantmentID(const AString & a_EnchantmentName)
{
static const struct
@@ -175,31 +333,31 @@ int cEnchantments::StringToEnchantmentID(const AString & a_EnchantmentName)
const char * m_Name;
} EnchantmentNames[] =
{
- { enchProtection, "Protection"},
- { enchFireProtection, "FireProtection"},
- { enchFeatherFalling, "FeatherFalling"},
- { enchBlastProtection, "BlastProtection"},
- { enchProjectileProtection, "ProjectileProtection"},
- { enchRespiration, "Respiration"},
- { enchAquaAffinity, "AquaAffinity"},
- { enchThorns, "Thorns"},
- { enchDepthStrider, "DepthStrider"},
- { enchSharpness, "Sharpness"},
- { enchSmite, "Smite"},
- { enchBaneOfArthropods, "BaneOfArthropods"},
- { enchKnockback, "Knockback"},
- { enchFireAspect, "FireAspect"},
- { enchLooting, "Looting"},
- { enchEfficiency, "Efficiency"},
- { enchSilkTouch, "SilkTouch"},
- { enchUnbreaking, "Unbreaking"},
- { enchFortune, "Fortune"},
- { enchPower, "Power"},
- { enchPunch, "Punch"},
- { enchFlame, "Flame"},
- { enchInfinity, "Infinity"},
- { enchLuckOfTheSea, "LuckOfTheSea"},
- { enchLure, "Lure"},
+ { enchProtection, "Protection" },
+ { enchFireProtection, "FireProtection" },
+ { enchFeatherFalling, "FeatherFalling" },
+ { enchBlastProtection, "BlastProtection" },
+ { enchProjectileProtection, "ProjectileProtection" },
+ { enchRespiration, "Respiration" },
+ { enchAquaAffinity, "AquaAffinity" },
+ { enchThorns, "Thorns" },
+ { enchDepthStrider, "DepthStrider" },
+ { enchSharpness, "Sharpness" },
+ { enchSmite, "Smite" },
+ { enchBaneOfArthropods, "BaneOfArthropods" },
+ { enchKnockback, "Knockback" },
+ { enchFireAspect, "FireAspect" },
+ { enchLooting, "Looting" },
+ { enchEfficiency, "Efficiency" },
+ { enchSilkTouch, "SilkTouch" },
+ { enchUnbreaking, "Unbreaking" },
+ { enchFortune, "Fortune" },
+ { enchPower, "Power" },
+ { enchPunch, "Punch" },
+ { enchFlame, "Flame" },
+ { enchInfinity, "Infinity" },
+ { enchLuckOfTheSea, "LuckOfTheSea" },
+ { enchLure, "Lure" },
} ;
// First try to parse as a number:
diff --git a/src/Enchantments.h b/src/Enchantments.h
index 1119f7e2f..3f7b1bfc8 100644
--- a/src/Enchantments.h
+++ b/src/Enchantments.h
@@ -45,6 +45,7 @@ public:
enum eEnchantment
{
+ // Currently missing: Frost walker, curse of binding, sweeping edge, mending, and curse of vanishing.
enchProtection = 0,
enchFireProtection = 1,
enchFeatherFalling = 2,
@@ -103,6 +104,9 @@ public:
/** Returns true if there are no enchantments */
bool IsEmpty(void) const;
+ /** Returns true if the given enchantment could be legally added to this object. Note that adding the enchantment may not actually increase the level. */
+ bool CanAddEnchantment(int a_EnchantmentID) const;
+
/** Converts enchantment name or ID (number in string) to the numeric representation; returns -1 if enchantment name not found; case insensitive */
static int StringToEnchantmentID(const AString & a_EnchantmentName);
@@ -111,6 +115,15 @@ public:
// tolua_end
+ /** Get the XP cost multiplier for the enchantment (for anvils).
+ If FromBook is true, then this function returns the XP multiplier if
+ the enchantment is coming from a book, otherwise it returns the normal
+ item multiplier. */
+ static int GetXPCostMultiplier(int a_EnchantmentID, bool FromBook);
+
+ /** Get the maximum level the enchantment can have */
+ static unsigned int GetLevelCap(int a_EnchantmentID);
+
/** Add enchantment weights from item to the vector */
static void AddItemEnchantmentWeights(cWeightedEnchantments & a_Enchantments, short a_ItemType, int a_EnchantmentLevel);
@@ -149,7 +162,12 @@ protected:
/** Currently stored enchantments */
cMap m_Enchantments;
-} ; // tolua_export
+
+public:
+ /** Make this class iterable */
+ cMap::const_iterator begin() const { return m_Enchantments.begin(); }
+ cMap::const_iterator end() const { return m_Enchantments.end(); }
+}; // tolua_export
diff --git a/src/Item.cpp b/src/Item.cpp
index d421a95ad..3d9efb3b3 100644
--- a/src/Item.cpp
+++ b/src/Item.cpp
@@ -228,14 +228,14 @@ void cItem::FromJson(const Json::Value & a_Value)
-bool cItem::IsEnchantable(short a_ItemType, bool a_WithBook)
+bool cItem::IsEnchantable(short a_ItemType, bool a_FromBook)
{
if (
ItemCategory::IsAxe(a_ItemType) ||
ItemCategory::IsSword(a_ItemType) ||
ItemCategory::IsShovel(a_ItemType) ||
ItemCategory::IsPickaxe(a_ItemType) ||
- (a_WithBook && ItemCategory::IsHoe(a_ItemType)) ||
+ (a_FromBook && ItemCategory::IsHoe(a_ItemType)) ||
ItemCategory::IsArmor(a_ItemType)
)
{
@@ -255,7 +255,7 @@ bool cItem::IsEnchantable(short a_ItemType, bool a_WithBook)
case E_ITEM_SHEARS:
case E_ITEM_FLINT_AND_STEEL:
{
- return a_WithBook;
+ return a_FromBook;
}
}
@@ -419,6 +419,199 @@ bool cItem::EnchantByXPLevels(int a_NumXPLevels)
+int cItem::AddEnchantment(int a_EnchantmentID, unsigned int a_Level, bool a_FromBook)
+{
+ unsigned int OurLevel = m_Enchantments.GetLevel(a_EnchantmentID);
+ int Multiplier = cEnchantments::GetXPCostMultiplier(a_EnchantmentID, a_FromBook);
+ unsigned int NewLevel = 0;
+ if (OurLevel > a_Level)
+ {
+ // They don't add anything to us
+ NewLevel = OurLevel;
+ }
+ else if (OurLevel == a_Level)
+ {
+ // Bump it by 1
+ NewLevel = OurLevel + 1;
+ }
+ else
+ {
+ // Take the sacrifice's level
+ NewLevel = a_Level;
+ }
+ unsigned int LevelCap = cEnchantments::GetLevelCap(a_EnchantmentID);
+ if (NewLevel > LevelCap)
+ {
+ NewLevel = LevelCap;
+ }
+
+ m_Enchantments.SetLevel(a_EnchantmentID, NewLevel);
+ return static_cast<int>(NewLevel) * Multiplier;
+}
+
+
+
+
+
+bool cItem::CanHaveEnchantment(int a_EnchantmentID)
+{
+ if (m_ItemType == E_ITEM_ENCHANTED_BOOK)
+ {
+ // Enchanted books can take anything
+ return true;
+ }
+
+ // The organization here is based on the summary at:
+ // http://minecraft.gamepedia.com/Enchanting
+ // as of July 2017 (Minecraft 1.12).
+
+ // Hand tool enchantments
+ static const std::set<int> SwordEnchantments =
+ {
+ cEnchantments::enchBaneOfArthropods,
+ cEnchantments::enchFireAspect,
+ cEnchantments::enchKnockback,
+ cEnchantments::enchLooting,
+ cEnchantments::enchSharpness,
+ cEnchantments::enchSmite,
+ cEnchantments::enchUnbreaking
+ };
+ static const std::set<int> AxeEnchantments =
+ {
+ cEnchantments::enchBaneOfArthropods,
+ cEnchantments::enchEfficiency,
+ cEnchantments::enchFortune,
+ cEnchantments::enchSharpness,
+ cEnchantments::enchSilkTouch,
+ cEnchantments::enchSmite,
+ cEnchantments::enchUnbreaking
+ };
+ static const std::set<int> ToolEnchantments =
+ {
+ cEnchantments::enchEfficiency,
+ cEnchantments::enchFortune,
+ cEnchantments::enchSilkTouch,
+ cEnchantments::enchUnbreaking
+ };
+ static const std::set<int> ShearEnchantments =
+ {
+ cEnchantments::enchEfficiency,
+ cEnchantments::enchUnbreaking
+ };
+ static const std::set<int> BowEnchantments =
+ {
+ cEnchantments::enchFlame,
+ cEnchantments::enchInfinity,
+ cEnchantments::enchPower,
+ cEnchantments::enchPunch
+ };
+ static const std::set<int> FishingEnchantments =
+ {
+ cEnchantments::enchLuckOfTheSea,
+ cEnchantments::enchLure
+ };
+ static const std::set<int> MiscEnchantments =
+ {
+ cEnchantments::enchUnbreaking
+ };
+
+ if (ItemCategory::IsSword(m_ItemType))
+ {
+ return SwordEnchantments.count(a_EnchantmentID) > 0;
+ }
+ if (ItemCategory::IsAxe(m_ItemType))
+ {
+ return AxeEnchantments.count(a_EnchantmentID) > 0;
+ }
+ if (ItemCategory::IsPickaxe(m_ItemType) || ItemCategory::IsShovel(m_ItemType))
+ {
+ return ToolEnchantments.count(a_EnchantmentID) > 0;
+ }
+ if (m_ItemType == E_ITEM_SHEARS)
+ {
+ return ShearEnchantments.count(a_EnchantmentID) > 0;
+ }
+ if (m_ItemType == E_ITEM_BOW)
+ {
+ return BowEnchantments.count(a_EnchantmentID) > 0;
+ }
+ if (m_ItemType == E_ITEM_FISHING_ROD)
+ {
+ return FishingEnchantments.count(a_EnchantmentID) > 0;
+ }
+ if (ItemCategory::IsHoe(m_ItemType) || (m_ItemType == E_ITEM_FLINT_AND_STEEL) || (m_ItemType == E_ITEM_CARROT_ON_STICK) || (m_ItemType == E_ITEM_SHIELD))
+ {
+ return MiscEnchantments.count(a_EnchantmentID) > 0;
+ }
+
+ // Armor enchantments
+ static const std::set<int> ArmorEnchantments =
+ {
+ cEnchantments::enchBlastProtection,
+ cEnchantments::enchFireProtection,
+ cEnchantments::enchProjectileProtection,
+ cEnchantments::enchProtection,
+ cEnchantments::enchThorns,
+ cEnchantments::enchUnbreaking
+ };
+ static const std::set<int> HatOnlyEnchantments =
+ {
+ cEnchantments::enchAquaAffinity,
+ cEnchantments::enchRespiration
+ };
+ static const std::set<int> BootOnlyEnchantments =
+ {
+ cEnchantments::enchDepthStrider,
+ cEnchantments::enchFeatherFalling
+ };
+
+ if (ItemCategory::IsBoots(m_ItemType))
+ {
+ return (BootOnlyEnchantments.count(a_EnchantmentID) > 0) || (ArmorEnchantments.count(a_EnchantmentID) > 0);
+ }
+ if (ItemCategory::IsHelmet(m_ItemType))
+ {
+ return (HatOnlyEnchantments.count(a_EnchantmentID) > 0) || (ArmorEnchantments.count(a_EnchantmentID) > 0);
+ }
+ if (ItemCategory::IsArmor(m_ItemType))
+ {
+ return ArmorEnchantments.count(a_EnchantmentID) > 0;
+ }
+ return false;
+}
+
+
+
+
+
+int cItem::AddEnchantmentsFromItem(const cItem & a_Other)
+{
+ bool FromBook = (a_Other.m_ItemType == E_ITEM_ENCHANTED_BOOK);
+
+ // Consider each enchantment seperately
+ int EnchantingCost = 0;
+ for (auto & Enchantment : a_Other.m_Enchantments)
+ {
+ if (CanHaveEnchantment(Enchantment.first))
+ {
+ if (!m_Enchantments.CanAddEnchantment(Enchantment.first))
+ {
+ // Cost of incompatible enchantments
+ EnchantingCost += 1;
+ }
+ else
+ {
+ EnchantingCost += AddEnchantment(Enchantment.first, Enchantment.second, FromBook);
+ }
+ }
+ }
+ return EnchantingCost;
+}
+
+
+
+
+
////////////////////////////////////////////////////////////////////////////////
// cItems:
diff --git a/src/Item.h b/src/Item.h
index 8d0e9d9a0..18a1e69c0 100644
--- a/src/Item.h
+++ b/src/Item.h
@@ -190,9 +190,9 @@ public:
void FromJson(const Json::Value & a_Value);
/** Returns true if the specified item type is enchantable.
- If WithBook is true, the function is used in the anvil inventory with book enchantments.
+ If FromBook is true, the function is used in the anvil inventory with book enchantments.
So it checks the "only book enchantments" too. Example: You can only enchant a hoe with a book. */
- static bool IsEnchantable(short a_ItemType, bool a_WithBook = false); // tolua_export
+ static bool IsEnchantable(short a_ItemType, bool a_FromBook = false); // tolua_export
/** Returns the enchantability of the item. When the item hasn't a enchantability, it will returns 0 */
int GetEnchantability(); // tolua_export
@@ -201,6 +201,19 @@ public:
Returns true if the item was enchanted, false if not (not enchantable / too many enchantments already). */
bool EnchantByXPLevels(int a_NumXPLevels); // tolua_export
+ /** Adds this specific enchantment to this item, returning the cost.
+ FromBook specifies whether the enchantment should be treated as coming
+ from a book. If true, then the cost returned uses the book values, if
+ false it uses the normal item multipliers. */
+ int AddEnchantment(int a_EnchantmentID, unsigned int a_Level, bool a_FromBook); // tolua_export
+
+ /** Adds the enchantments on a_Other to this item, returning the
+ XP cost of the transfer. */
+ int AddEnchantmentsFromItem(const cItem & a_Other); // tolua_export
+
+ /** Returns whether or not this item is allowed to have the given enchantment. Note: Does not check whether the enchantment is exclusive with the current enchantments on the item. */
+ bool CanHaveEnchantment(int a_EnchantmentID);
+
// tolua_begin
short m_ItemType;
diff --git a/src/UI/SlotArea.cpp b/src/UI/SlotArea.cpp
index 24c9bfb03..3729e8dc3 100644
--- a/src/UI/SlotArea.cpp
+++ b/src/UI/SlotArea.cpp
@@ -730,7 +730,6 @@ void cSlotAreaCrafting::UpdateRecipe(cPlayer & a_Player)
cCraftingRecipe & Recipe = GetRecipeForPlayer(a_Player);
cRoot::Get()->GetCraftingRecipes()->GetRecipe(a_Player, Grid, Recipe);
SetSlot(0, a_Player, Recipe.GetResult());
- m_ParentWindow.SendSlot(a_Player, this, 0);
}
@@ -1136,7 +1135,9 @@ void cSlotAreaAnvil::UpdateResult(cPlayer & a_Player)
}
}
- // TODO: Add enchantments.
+ // Add the enchantments from the sacrifice to the target
+ int EnchantmentCost = Input.AddEnchantmentsFromItem(SecondInput);
+ NeedExp += EnchantmentCost;
}
}
@@ -1166,8 +1167,6 @@ void cSlotAreaAnvil::UpdateResult(cPlayer & a_Player)
Input.m_CustomName = RepairedItemName;
}
- // TODO: Add enchantment exp cost.
-
m_MaximumCost = RepairCost + NeedExp;
if (NeedExp < 0)
@@ -2522,6 +2521,8 @@ void cSlotAreaTemporary::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem
}
itr->second[static_cast<size_t>(a_SlotNum)] = a_Item;
+
+ m_ParentWindow.SendSlot(a_Player, this, a_SlotNum);
}