1
0
Fork 0

Enchanting table shows detail on hover. Enchanting is deterministic. (#4937)

* Use lapis for enchanting, subtract correct number of levels, ClientHandle now selects from pregenerated list.

Co-authored-by: Tiger Wang <ziwei.tiger@outlook.com>
This commit is contained in:
KingCol13 2020-10-02 00:33:32 +03:00 committed by GitHub
parent be841b4769
commit 8947147c25
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 219 additions and 115 deletions

View File

@ -2746,6 +2746,33 @@ static int tolua_get_cItem_m_LoreTable(lua_State * tolua_S)
static int tolua_cItem_EnchantByXPLevels(lua_State * tolua_S)
{
// Check params:
cLuaState L(tolua_S);
if (
!L.CheckParamSelf("cItem") ||
!L.CheckParamNumber(2)
)
{
return 0;
}
// Get the params:
cItem * Self;
int NumXPLevels;
L.GetStackValue(1, Self);
L.GetStackValue(2, NumXPLevels);
// Call:
L.Push(Self->EnchantByXPLevels(NumXPLevels, GetRandomProvider()));
return 1;
}
static int tolua_set_cItem_m_LoreTable(lua_State * tolua_S)
{
// Check params:
@ -4420,7 +4447,8 @@ void cManualBindings::Bind(lua_State * tolua_S)
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cItem");
tolua_variable(tolua_S, "m_LoreTable", tolua_get_cItem_m_LoreTable, tolua_set_cItem_m_LoreTable);
tolua_function(tolua_S, "EnchantByXPLevels", tolua_cItem_EnchantByXPLevels);
tolua_variable(tolua_S, "m_LoreTable", tolua_get_cItem_m_LoreTable, tolua_set_cItem_m_LoreTable);
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cItemGrid");

View File

@ -768,79 +768,67 @@ void cClientHandle::HandleEnchantItem(UInt8 a_WindowID, UInt8 a_Enchantment)
if (a_Enchantment > 2)
{
LOGWARNING("%s attempt to crash the server with invalid enchanting selection (%u)!", GetUsername().c_str(), a_Enchantment);
Kick("Invalid enchanting!");
Kick("Selected invalid enchantment - hacked client?");
return;
}
// Bail out if something's wrong with the window
// Bail out if something's wrong with the window:
if (
(m_Player->GetWindow() == nullptr) ||
(m_Player->GetWindow()->GetWindowID() != a_WindowID) ||
(m_Player->GetWindow()->GetWindowType() != cWindow::wtEnchantment)
)
{
Kick("Enchantment with invalid window - hacked client?");
return;
}
cEnchantingWindow * Window = static_cast<cEnchantingWindow *>(m_Player->GetWindow());
auto Item = *Window->m_SlotArea->GetSlot(0, *m_Player); // A copy of the item to be enchanted.
short BaseEnchantmentLevel = Window->GetPropertyValue(a_Enchantment);
const auto BaseEnchantmentLevel = Window->GetProperty(a_Enchantment);
if (!Item.EnchantByXPLevels(BaseEnchantmentLevel))
// Survival players must be checked they can afford enchantment and have lapis removed:
if (!m_Player->IsGameModeCreative())
{
// Item wasn't enchantable:
return;
const auto XpRequired = m_Player->XpForLevel(BaseEnchantmentLevel);
auto LapisStack = *Window->m_SlotArea->GetSlot(1, *m_Player); // A copy of the lapis stack.
const auto LapisRequired = a_Enchantment + 1;
// Only allow enchantment if the player has sufficient levels and lapis to enchant:
if ((m_Player->GetCurrentXp() >= XpRequired) && (LapisStack.m_ItemCount >= LapisRequired))
{
/** We need to reduce the player's level by the number of lapis required.
However we need to keep the resulting percentage filled the same. */
const auto TargetLevel = m_Player->GetXpLevel() - LapisRequired;
const auto CurrentFillPercent = m_Player->GetXpPercentage();
// The experience to remove in order to reach the start (0% fill) of the target level.
const auto DeltaForLevel = -m_Player->GetCurrentXp() + m_Player->XpForLevel(TargetLevel);
// The experience to add to get the same fill percent.
const auto DeltaForPercent = CurrentFillPercent * (m_Player->XpForLevel(TargetLevel + 1) - m_Player->XpForLevel(TargetLevel));
// Apply the experience delta:
m_Player->DeltaExperience(DeltaForLevel + DeltaForPercent);
// Now reduce the lapis in our stack and send it back:
LapisStack.AddCount(-LapisRequired);
Window->m_SlotArea->SetSlot(1, *m_Player, LapisStack);
}
else
{
// Not creative and can't afford enchantment, so exit:
Kick("Selected unavailable enchantment - hacked client?");
return;
}
}
const auto SetEnchantAndBroadcast = [this, &Item, Window]
{
// Set the item slot to our new enchanted item:
Window->m_SlotArea->SetSlot(0, *m_Player, Item);
Window->BroadcastWholeWindow();
// Retrieve the enchanted item corresponding to our chosen option (top, middle, bottom)
cItem EnchantedItem = Window->m_SlotArea->SelectEnchantedOption(a_Enchantment);
// Remove enchantment choices:
Window->SetProperty(0, 0, *m_Player);
Window->SetProperty(1, 0, *m_Player);
Window->SetProperty(2, 0, *m_Player);
};
// Creative players can always enchant:
if (m_Player->IsGameModeCreative())
{
SetEnchantAndBroadcast();
return;
}
const auto XpRequired = m_Player->XpForLevel(BaseEnchantmentLevel);
auto LapisStack = *Window->m_SlotArea->GetSlot(1, *m_Player); // A copy of the lapis stack.
const auto LapisRequired = a_Enchantment + 1;
// Only allow enchantment if the player has sufficient levels and lapis to enchant:
if ((m_Player->GetCurrentXp() >= XpRequired) && (LapisStack.m_ItemCount >= LapisRequired))
{
/*
We need to reduce the player's level by the number of lapis required.
However we need to keep the resulting percentage filled the same.
*/
const auto TargetLevel = m_Player->GetXpLevel() - LapisRequired;
const auto CurrentFillPercent = m_Player->GetXpPercentage();
// The experience to remove in order to reach the start (0% fill) of the target level.
const auto DeltaForLevel = -m_Player->GetCurrentXp() + m_Player->XpForLevel(TargetLevel);
// The experience to add to get the same fill percent.
const auto DeltaForPercent = CurrentFillPercent * (m_Player->XpForLevel(TargetLevel + 1) - m_Player->XpForLevel(TargetLevel));
// Apply the experience delta:
m_Player->DeltaExperience(DeltaForLevel + DeltaForPercent);
// Now reduce the lapis in our stack and send it back:
LapisStack.AddCount(-LapisRequired);
Window->m_SlotArea->SetSlot(1, *m_Player, LapisStack);
SetEnchantAndBroadcast();
}
// Set the item slot to our new enchanted item:
Window->m_SlotArea->SetSlot(0, *m_Player, EnchantedItem);
m_Player->PermuteEnchantmentSeed();
}

View File

@ -1170,14 +1170,14 @@ void cEnchantments::CheckEnchantmentConflictsFromVector(
cEnchantments cEnchantments::GetRandomEnchantmentFromVector(const cWeightedEnchantments & a_Enchantments)
cEnchantments cEnchantments::GetRandomEnchantmentFromVector(const cWeightedEnchantments & a_Enchantments, MTRand & a_Random)
{
int AllWeights = 0;
for (const auto & Enchantment: a_Enchantments)
{
AllWeights += Enchantment.m_Weight;
}
int RandomNumber = GetRandomProvider().RandInt(AllWeights - 1);
int RandomNumber = a_Random.RandInt(AllWeights - 1);
for (const auto & Enchantment: a_Enchantments)
{
RandomNumber -= Enchantment.m_Weight;

View File

@ -9,11 +9,11 @@
#pragma once
#include "Defines.h"
#include "FastRandom.h"
#include "WorldStorage/EnchantmentSerializer.h"
// fwd: "WorldStorage/FastNBT.h"
class cFastNBTWriter;
class cParsedNBT;
@ -27,6 +27,7 @@ typedef std::vector<cWeightedEnchantment> cWeightedEnchantments;
/** Class that stores item enchantments or stored-enchantments
The enchantments may be serialized to a stringspec and read back from such stringspec.
The format for the stringspec is "id=lvl;id=lvl;id=lvl...", with an optional semicolon at the end,
@ -139,8 +140,8 @@ public:
/** Check enchantment conflicts from enchantments from the vector */
static void CheckEnchantmentConflictsFromVector(cWeightedEnchantments & a_Enchantments, const cEnchantments & a_FirstEnchantment);
/** Gets random enchantment from Vector and returns it */
static cEnchantments GetRandomEnchantmentFromVector(const cWeightedEnchantments & a_Enchantments);
/** Gets random enchantment from Vector and returns it, with randomness derived from the provided PRNG. */
static cEnchantments GetRandomEnchantmentFromVector(const cWeightedEnchantments & a_Enchantments, MTRand & a_Random);
/** Selects one enchantment from a Vector using cNoise. Mostly used for generators.
Uses the enchantments' weights for the random distribution.

View File

@ -151,6 +151,8 @@ cPlayer::cPlayer(const cClientHandlePtr & a_Client, const AString & a_PlayerName
SetWorld(World); // Use default world
m_EnchantmentSeed = GetRandomProvider().RandInt<unsigned int>(); // Use a random number to seed the enchantment generator
FLOGD("Player \"{0}\" is connecting for the first time, spawning at default world spawn {1:.2f}",
a_PlayerName, GetPosition()
);
@ -1861,6 +1863,25 @@ void cPlayer::SetVisible(bool a_bVisible)
MTRand cPlayer::GetEnchantmentRandomProvider()
{
return m_EnchantmentSeed;
}
void cPlayer::PermuteEnchantmentSeed()
{
// Get a new random integer and save that as the seed:
m_EnchantmentSeed = GetRandomProvider().RandInt<unsigned int>();
}
bool cPlayer::HasPermission(const AString & a_Permission)
{
if (a_Permission.empty())
@ -2277,6 +2298,7 @@ bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World)
m_LifetimeTotalXp = root.get("xpTotal", 0).asInt();
m_CurrentXp = root.get("xpCurrent", 0).asInt();
m_IsFlying = root.get("isflying", 0).asBool();
m_EnchantmentSeed = root.get("enchantmentSeed", GetRandomProvider().RandInt<unsigned int>()).asUInt();
Json::Value & JSON_KnownItems = root["knownItems"];
for (UInt32 i = 0; i < JSON_KnownItems.size(); i++)
@ -2439,6 +2461,7 @@ bool cPlayer::SaveToDisk()
root["SpawnY"] = GetLastBedPos().y;
root["SpawnZ"] = GetLastBedPos().z;
root["SpawnWorld"] = m_SpawnWorld->GetName();
root["enchantmentSeed"] = m_EnchantmentSeed;
if (m_World != nullptr)
{

View File

@ -263,6 +263,13 @@ public:
// tolua_end
/** Get a copy of the PRNG for enchanting related generation, don't use this for other purposes.
The PRNG's state is initialised with an internal seed, such that until PermuteEnchantmentSeed is called, this function returns the same PRNG. */
MTRand GetEnchantmentRandomProvider();
/** Permute the seed for enchanting related PRNGs, don't use this for other purposes. */
void PermuteEnchantmentSeed();
/** Returns the SharedPtr to client handle associated with the player. */
cClientHandlePtr GetClientHandlePtr(void) const { return m_ClientHandle; }
@ -718,6 +725,7 @@ protected:
/** Player Xp level */
int m_LifetimeTotalXp;
int m_CurrentXp;
unsigned int m_EnchantmentSeed;
// flag saying we need to send a xp update to client
bool m_bDirtyExperience;

View File

@ -429,7 +429,7 @@ int cItem::GetEnchantability()
bool cItem::EnchantByXPLevels(int a_NumXPLevels)
bool cItem::EnchantByXPLevels(int a_NumXPLevels, MTRand & a_Random)
{
if (!cItem::IsEnchantable(m_ItemType))
{
@ -442,9 +442,8 @@ bool cItem::EnchantByXPLevels(int a_NumXPLevels)
return false;
}
auto & Random = GetRandomProvider();
int ModifiedEnchantmentLevel = a_NumXPLevels + Random.RandInt(Enchantability / 4) + Random.RandInt(Enchantability / 4) + 1;
float RandomBonus = 1.0F + (Random.RandReal() + Random.RandReal() - 1.0F) * 0.15F;
int ModifiedEnchantmentLevel = a_NumXPLevels + a_Random.RandInt(Enchantability / 4) + a_Random.RandInt(Enchantability / 4) + 1;
float RandomBonus = 1.0F + (a_Random.RandReal() + a_Random.RandReal() - 1.0F) * 0.15F;
int FinalEnchantmentLevel = static_cast<int>(ModifiedEnchantmentLevel * RandomBonus + 0.5F);
cWeightedEnchantments Enchantments;
@ -455,7 +454,7 @@ bool cItem::EnchantByXPLevels(int a_NumXPLevels)
m_ItemType = E_ITEM_ENCHANTED_BOOK;
}
cEnchantments Enchantment1 = cEnchantments::GetRandomEnchantmentFromVector(Enchantments);
cEnchantments Enchantment1 = cEnchantments::GetRandomEnchantmentFromVector(Enchantments, a_Random);
m_Enchantments.AddFromString(Enchantment1.ToString());
cEnchantments::RemoveEnchantmentWeightFromVector(Enchantments, Enchantment1);
@ -465,12 +464,12 @@ bool cItem::EnchantByXPLevels(int a_NumXPLevels)
// Next Enchantment (Second)
float NewEnchantmentLevel = a_NumXPLevels / 2.0f;
float SecondEnchantmentChance = (NewEnchantmentLevel + 1) / 50.0f;
if (Enchantments.empty() || !Random.RandBool(SecondEnchantmentChance))
if (Enchantments.empty() || !a_Random.RandBool(SecondEnchantmentChance))
{
return true;
}
cEnchantments Enchantment2 = cEnchantments::GetRandomEnchantmentFromVector(Enchantments);
cEnchantments Enchantment2 = cEnchantments::GetRandomEnchantmentFromVector(Enchantments, a_Random);
m_Enchantments.AddFromString(Enchantment2.ToString());
cEnchantments::RemoveEnchantmentWeightFromVector(Enchantments, Enchantment2);
@ -480,12 +479,12 @@ bool cItem::EnchantByXPLevels(int a_NumXPLevels)
// Next Enchantment (Third)
NewEnchantmentLevel = NewEnchantmentLevel / 2.0f;
float ThirdEnchantmentChance = (NewEnchantmentLevel + 1) / 50.0f;
if (Enchantments.empty() || !Random.RandBool(ThirdEnchantmentChance))
if (Enchantments.empty() || !a_Random.RandBool(ThirdEnchantmentChance))
{
return true;
}
cEnchantments Enchantment3 = cEnchantments::GetRandomEnchantmentFromVector(Enchantments);
cEnchantments Enchantment3 = cEnchantments::GetRandomEnchantmentFromVector(Enchantments, a_Random);
m_Enchantments.AddFromString(Enchantment3.ToString());
cEnchantments::RemoveEnchantmentWeightFromVector(Enchantments, Enchantment3);
@ -495,11 +494,11 @@ bool cItem::EnchantByXPLevels(int a_NumXPLevels)
// Next Enchantment (Fourth)
NewEnchantmentLevel = NewEnchantmentLevel / 2.0f;
float FourthEnchantmentChance = (NewEnchantmentLevel + 1) / 50.0f;
if (Enchantments.empty() || !Random.RandBool(FourthEnchantmentChance))
if (Enchantments.empty() || !a_Random.RandBool(FourthEnchantmentChance))
{
return true;
}
cEnchantments Enchantment4 = cEnchantments::GetRandomEnchantmentFromVector(Enchantments);
cEnchantments Enchantment4 = cEnchantments::GetRandomEnchantmentFromVector(Enchantments, a_Random);
m_Enchantments.AddFromString(Enchantment4.ToString());
return true;

View File

@ -141,8 +141,9 @@ public:
int GetEnchantability(); // tolua_export
/** Randomly enchants the item using the specified number of XP levels.
Returns true if the item was enchanted, false if not (not enchantable / too many enchantments already). */
bool EnchantByXPLevels(int a_NumXPLevels); // tolua_export
Returns true if the item was enchanted, false if not (not enchantable / too many enchantments already).
Randomness is derived from the provided PRNG. */
bool EnchantByXPLevels(int a_NumXPLevels, MTRand & a_Random); // Exported in ManualBindings.cpp
/** Adds this specific enchantment to this item, returning the cost.
FromBook specifies whether the enchantment should be treated as coming

View File

@ -175,21 +175,21 @@ public:
case 0:
{
cItem Bow(E_ITEM_BOW, 1, Random.RandInt<short>(50));
Bow.EnchantByXPLevels(Random.RandInt(22, 30));
Bow.EnchantByXPLevels(Random.RandInt(22, 30), GetRandomProvider());
Drops.Add(Bow);
break;
}
case 1:
{
cItem Book(E_ITEM_BOOK);
Book.EnchantByXPLevels(30);
Book.EnchantByXPLevels(30, GetRandomProvider());
Drops.Add(Book);
break;
}
case 2:
{
cItem Rod(E_ITEM_FISHING_ROD, 1, Random.RandInt<short>(50));
Rod.EnchantByXPLevels(Random.RandInt(22, 30));
Rod.EnchantByXPLevels(Random.RandInt(22, 30), GetRandomProvider());
Drops.Add(Rod);
break;
}

View File

@ -28,13 +28,12 @@ cEnchantingWindow::cEnchantingWindow(Vector3i a_BlockPos, const AString & a_Titl
void cEnchantingWindow::SetProperty(short a_Property, short a_Value, cPlayer & a_Player)
{
if ((a_Property < 0) || (static_cast<size_t>(a_Property) >= ARRAYCOUNT(m_PropertyValue)))
ASSERT(a_Property >= 0);
if (static_cast<size_t>(a_Property) < m_PropertyValue.size())
{
ASSERT(!"a_Property is invalid");
return;
m_PropertyValue[a_Property] = a_Value;
}
m_PropertyValue[a_Property] = a_Value;
Super::SetProperty(a_Property, a_Value, a_Player);
}
@ -44,13 +43,12 @@ void cEnchantingWindow::SetProperty(short a_Property, short a_Value, cPlayer & a
void cEnchantingWindow::SetProperty(short a_Property, short a_Value)
{
if ((a_Property < 0) || (static_cast<size_t>(a_Property) >= ARRAYCOUNT(m_PropertyValue)))
ASSERT(a_Property >= 0);
if (static_cast<size_t>(a_Property) < m_PropertyValue.size())
{
ASSERT(!"a_Property is invalid");
return;
m_PropertyValue[a_Property] = a_Value;
}
m_PropertyValue[a_Property] = a_Value;
Super::SetProperty(a_Property, a_Value);
}
@ -58,9 +56,9 @@ void cEnchantingWindow::SetProperty(short a_Property, short a_Value)
short cEnchantingWindow::GetPropertyValue(short a_Property)
short cEnchantingWindow::GetProperty(short a_Property)
{
if ((a_Property < 0) || (static_cast<size_t>(a_Property) >= ARRAYCOUNT(m_PropertyValue)))
if ((a_Property < 0) || (static_cast<size_t>(a_Property) >= m_PropertyValue.size()))
{
ASSERT(!"a_Property is invalid");
return 0;

View File

@ -15,6 +15,12 @@
class cSlotAreaEnchanting;
class cEnchantingWindow:
public cWindow
{
@ -24,22 +30,23 @@ public:
cEnchantingWindow(Vector3i a_BlockPos, const AString & a_Title);
/** Sends enchantment properties to the client.
If the property represents a level requirement, stores it for later GetProperty retrieval. */
virtual void SetProperty(short a_Property, short a_Value, cPlayer & a_Player) override;
/** Sends enchantment properties to the client.
If the property represents a level requirement, stores it for later GetProperty retrieval. */
virtual void SetProperty(short a_Property, short a_Value) override;
/** Return the value of a property */
short GetPropertyValue(short a_Property);
/** Return the level requirement of the given enchantment slot. */
short GetProperty(short a_Property);
virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) override;
cSlotArea * m_SlotArea;
cSlotAreaEnchanting * m_SlotArea;
protected:
short m_PropertyValue[3];
std::array<short, 3> m_PropertyValue;
Vector3i m_BlockPos;
};

View File

@ -1678,25 +1678,59 @@ void cSlotAreaEnchanting::UpdateResult(cPlayer & a_Player)
{
cItem Item = *GetSlot(0, a_Player);
if (cItem::IsEnchantable(Item.m_ItemType) && Item.m_Enchantments.IsEmpty())
if (!cItem::IsEnchantable(Item.m_ItemType) || !Item.m_Enchantments.IsEmpty())
{
int Bookshelves = std::min(GetBookshelvesCount(*a_Player.GetWorld()), 15);
auto & Random = GetRandomProvider();
int Base = (Random.RandInt(1, 8) + (Bookshelves / 2) + Random.RandInt(0, Bookshelves));
int TopSlot = std::max(Base / 3, 1);
int MiddleSlot = (Base * 2) / 3 + 1;
int BottomSlot = std::max(Base, Bookshelves * 2);
m_ParentWindow.SetProperty(0, static_cast<short>(TopSlot), a_Player);
m_ParentWindow.SetProperty(1, static_cast<short>(MiddleSlot), a_Player);
m_ParentWindow.SetProperty(2, static_cast<short>(BottomSlot), a_Player);
return;
}
else
// Pseudocode found at: https://minecraft.gamepedia.com/Enchanting_mechanics
const auto Bookshelves = std::min(GetBookshelvesCount(*a_Player.GetWorld()), 15U);
// A PRNG initialised using the player's enchantment seed.
auto Random = a_Player.GetEnchantmentRandomProvider();
// Calculate the levels for the offered enchantment options:
const auto Base = (Random.RandInt(1U, 8U) + (Bookshelves / 2) + Random.RandInt(0U, Bookshelves));
const std::array<unsigned int, 3> OptionLevels
{
m_ParentWindow.SetProperty(0, 0, a_Player);
m_ParentWindow.SetProperty(1, 0, a_Player);
m_ParentWindow.SetProperty(2, 0, a_Player);
std::max(Base / 3, 1U),
(Base * 2) / 3 + 1,
std::max(Base, Bookshelves * 2)
};
// Properties set according to: https://wiki.vg/Protocol#Window_Property
// Fake a "seed" for the client to draw Standard Galactic Alphabet glyphs:
m_ParentWindow.SetProperty(3, Random.RandInt<short>(), a_Player);
// Calculate an enchanting possibility for each option (top, middle and bottom) and send details to window:
for (short i = 0; i != OptionLevels.size(); i++)
{
// A copy of the item.
cItem EnchantedItem = Item.CopyOne();
// Enchant based on the number of levels:
EnchantedItem.EnchantByXPLevels(OptionLevels[i], Random);
LOGD("Generated enchanted item %d with enchantments: %s", i, EnchantedItem.m_Enchantments.ToString());
// Send the level requirement for the enchantment option:
m_ParentWindow.SetProperty(i, static_cast<short>(OptionLevels[i]), a_Player);
// Get the first enchantment ID, which must exist:
ASSERT(EnchantedItem.m_Enchantments.begin() != EnchantedItem.m_Enchantments.end());
const short EnchantmentID = static_cast<short>(EnchantedItem.m_Enchantments.begin()->first);
// Send the enchantment ID of the first enchantment on our item:
m_ParentWindow.SetProperty(4 + i, EnchantmentID, a_Player);
const short EnchantmentLevel = static_cast<short>(EnchantedItem.m_Enchantments.GetLevel(EnchantmentID));
ASSERT(EnchantmentLevel > 0);
// Send the level for the first enchantment on our item:
m_ParentWindow.SetProperty(7 + i, EnchantmentLevel, a_Player);
// Store the item we've enchanted as an option to be retrieved later:
m_EnchantedItemOptions[i] = std::move(EnchantedItem);
}
}
@ -1704,9 +1738,8 @@ void cSlotAreaEnchanting::UpdateResult(cPlayer & a_Player)
int cSlotAreaEnchanting::GetBookshelvesCount(cWorld & a_World)
unsigned cSlotAreaEnchanting::GetBookshelvesCount(cWorld & a_World)
{
int Bookshelves = 0;
cBlockArea Area;
Area.Read(a_World, m_BlockPos - Vector3i(2, 0, 2), m_BlockPos + Vector3i(2, 1, 2));
@ -1751,6 +1784,8 @@ int cSlotAreaEnchanting::GetBookshelvesCount(cWorld & a_World)
{ 1, 1, 0, 1, 1, 1 }, // Bookcase at {1, 1, 0}, air at {1, 1, 1}
};
unsigned Bookshelves = 0;
for (size_t i = 0; i < ARRAYCOUNT(CheckCoords); i++)
{
if (
@ -1769,6 +1804,16 @@ int cSlotAreaEnchanting::GetBookshelvesCount(cWorld & a_World)
cItem cSlotAreaEnchanting::SelectEnchantedOption(size_t a_EnchantOption)
{
ASSERT(a_EnchantOption < m_EnchantedItemOptions.size());
return std::move(m_EnchantedItemOptions[a_EnchantOption]);
}
////////////////////////////////////////////////////////////////////////////////
// cSlotAreaEnderChest:

View File

@ -397,14 +397,20 @@ public:
virtual void OnPlayerAdded (cPlayer & a_Player) override;
virtual void OnPlayerRemoved(cPlayer & a_Player) override;
/* Get the count of bookshelves who stand in the near of the enchanting table */
int GetBookshelvesCount(cWorld & a_World);
/* Get the number of bookshelves which are near the enchanting table */
unsigned GetBookshelvesCount(cWorld & a_World);
/* Return the enchanted item matching the chosen option (0, 1, 2)
Ownership of the cItem is transferred to the caller. */
cItem SelectEnchantedOption(size_t a_EnchantOption);
protected:
/** Handles a click in the item slot. */
void UpdateResult(cPlayer & a_Player);
Vector3i m_BlockPos;
std::array<cItem, 3> m_EnchantedItemOptions;
};