1
0
Fork 0

Added anvil enchantment handling. (#3857)

+ Added anvil enchantment handling.
This commit is contained in:
Lane Kolbly 2017-07-28 12:00:20 -05:00 committed by Tiger Wang
parent 5402b214b3
commit 790e15f2e6
6 changed files with 480 additions and 37 deletions

View File

@ -2696,6 +2696,23 @@ local Hash = cCryptoHash.sha1HexString("DataToHash")
},
Notes = "Returns the level of the specified enchantment stored in this object; 0 if not stored",
},
CanAddEnchantment =
{
Params =
{
{
Name = "EnchantmentNumID",
Type = "number",
},
},
Returns =
{
{
Type = "boolean"
},
},
Notes = "Returns true if the specified enchantment is not mutually exclusive with any of the enchantments stored by the object.",
},
IsEmpty =
{
Returns =
@ -6599,6 +6616,49 @@ These ItemGrids are available in the API and can be manipulated by the plugins,
},
Notes = "Adds the specified amount to the item count. Returns self (useful for chaining).",
},
AddEnchantment =
{
Params =
{
{
Name = "Enchantment ID",
Type = "number",
},
{
Name = "Level",
Type = "number",
},
{
Name = "FromBook",
Type = "boolean",
},
},
Returns =
{
{
Type = "number",
},
},
Notes = "Adds the given enchantment at the given level to this item, following anvil enchantment combining rules. Returns the XP level cost of the addition. FromBook specifies whether to use the XP multiplier for books or the multiplier used for other items, if true it uses the multiplier for books.",
},
AddEnchantmentsFromItem =
{
Params =
{
{
Name = "Additive",
Type = "cItem",
},
},
Returns =
{
{
Name = "LevelCost",
Type = "number",
},
},
Notes = "Adds the enchantments from the specified item to this item, returning the cost as if this were an anvil.",
},
Clear =
{
Notes = "Resets the instance to an empty item",
@ -6803,7 +6863,7 @@ These ItemGrids are available in the API and can be manipulated by the plugins,
Type = "number",
},
{
Name = "WithBook",
Name = "FromBook",
Type = "boolean",
},
},
@ -6813,7 +6873,7 @@ These ItemGrids are available in the API and can be manipulated by the plugins,
Type = "boolean",
},
},
Notes = "Returns true if the specified item type is enchantable. If WithBook 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.",
Notes = "Returns true if the specified item type is enchantable. 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.",
},
IsEqual =
{

View File

@ -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:

View File

@ -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

View File

@ -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:

View File

@ -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;

View File

@ -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);
}