summaryrefslogtreecommitdiffstats
path: root/source/UI
diff options
context:
space:
mode:
authorbearbin <bearbin@gmail.com>2013-07-29 14:55:26 +0200
committerbearbin <bearbin@gmail.com>2013-07-29 14:55:26 +0200
commitfb1044c4e55c726753d094b66756a1cb3bc60ee8 (patch)
treec61e56725da7dff0154d566722651e2c39c9d6c6 /source/UI
parentWebAdmin: Removed the duplicate memory usage querying (diff)
parentChanged everyting to Unix line endings. (diff)
downloadcuberite-fb1044c4e55c726753d094b66756a1cb3bc60ee8.tar
cuberite-fb1044c4e55c726753d094b66756a1cb3bc60ee8.tar.gz
cuberite-fb1044c4e55c726753d094b66756a1cb3bc60ee8.tar.bz2
cuberite-fb1044c4e55c726753d094b66756a1cb3bc60ee8.tar.lz
cuberite-fb1044c4e55c726753d094b66756a1cb3bc60ee8.tar.xz
cuberite-fb1044c4e55c726753d094b66756a1cb3bc60ee8.tar.zst
cuberite-fb1044c4e55c726753d094b66756a1cb3bc60ee8.zip
Diffstat (limited to 'source/UI')
-rw-r--r--source/UI/SlotArea.cpp1630
-rw-r--r--source/UI/SlotArea.h606
2 files changed, 1118 insertions, 1118 deletions
diff --git a/source/UI/SlotArea.cpp b/source/UI/SlotArea.cpp
index fc3f248f5..dddb20885 100644
--- a/source/UI/SlotArea.cpp
+++ b/source/UI/SlotArea.cpp
@@ -1,815 +1,815 @@
-
-// SlotArea.cpp
-
-// Implements the cSlotArea class and its descendants
-
-#include "Globals.h"
-#include "SlotArea.h"
-#include "../Player.h"
-#include "../BlockEntities/ChestEntity.h"
-#include "../BlockEntities/DropSpenserEntity.h"
-#include "../BlockEntities/FurnaceEntity.h"
-#include "../Items/ItemHandler.h"
-#include "Window.h"
-#include "../CraftingRecipes.h"
-#include "../Root.h"
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cSlotArea:
-
-cSlotArea::cSlotArea(int a_NumSlots, cWindow & a_ParentWindow) :
- m_NumSlots(a_NumSlots),
- m_ParentWindow(a_ParentWindow)
-{
-}
-
-
-
-
-
-void cSlotArea::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem)
-{
- /*
- LOGD("Slot area with %d slots clicked at slot number %d, clicked item %s, slot item %s",
- GetNumSlots(), a_SlotNum,
- ItemToFullString(a_ClickedItem).c_str(),
- ItemToFullString(*GetSlot(a_SlotNum, a_Player)).c_str()
- );
- */
-
- ASSERT((a_SlotNum >= 0) && (a_SlotNum < GetNumSlots()));
-
- bool bAsync = false;
- if (GetSlot(a_SlotNum, a_Player) == NULL)
- {
- LOGWARNING("GetSlot(%d) returned NULL! Ignoring click", a_SlotNum);
- return;
- }
-
- if ((a_ClickAction == caShiftLeftClick) || (a_ClickAction == caShiftRightClick))
- {
- if (!a_Player.IsDraggingItem())
- {
- ShiftClicked(a_Player, a_SlotNum, a_ClickedItem);
- return;
- }
- LOGD("Shift clicked, but the player is dragging an item: %s", ItemToFullString(a_Player.GetDraggingItem()).c_str());
- return;
- }
-
- cItem Slot(*GetSlot(a_SlotNum, a_Player));
- if (!Slot.IsSameType(a_ClickedItem))
- {
- LOGWARNING("*** Window lost sync at item %d in SlotArea with %d items ***", a_SlotNum, m_NumSlots);
- LOGWARNING("My item: %s", ItemToFullString(Slot).c_str());
- LOGWARNING("Their item: %s", ItemToFullString(a_ClickedItem).c_str());
- bAsync = true;
- }
- cItem & DraggingItem = a_Player.GetDraggingItem();
- switch (a_ClickAction)
- {
- case caRightClick:
- {
- if (DraggingItem.m_ItemType <= 0) // Empty-handed?
- {
- DraggingItem.m_ItemCount = (char)(((float)Slot.m_ItemCount) / 2.f + 0.5f);
- Slot.m_ItemCount -= DraggingItem.m_ItemCount;
- DraggingItem.m_ItemType = Slot.m_ItemType;
- DraggingItem.m_ItemDamage = Slot.m_ItemDamage;
-
- if (Slot.m_ItemCount <= 0)
- {
- Slot.Empty();
- }
- }
- else if ((Slot.m_ItemType <= 0) || DraggingItem.IsEqual(Slot))
- {
- // Drop one item in slot
- cItemHandler * Handler = ItemHandler(Slot.m_ItemType);
- if ((DraggingItem.m_ItemCount > 0) && (Slot.m_ItemCount < Handler->GetMaxStackSize()))
- {
- Slot.m_ItemType = DraggingItem.m_ItemType;
- Slot.m_ItemCount++;
- Slot.m_ItemDamage = DraggingItem.m_ItemDamage;
- DraggingItem.m_ItemCount--;
- }
- if (DraggingItem.m_ItemCount <= 0)
- {
- DraggingItem.Empty();
- }
- }
- else if (!DraggingItem.IsEqual(Slot))
- {
- // Swap contents
- cItem tmp(DraggingItem);
- DraggingItem = Slot;
- Slot = tmp;
- }
- break;
- }
-
- case caLeftClick:
- {
- // Left-clicked
- if (!DraggingItem.IsEqual(Slot))
- {
- // Switch contents
- cItem tmp(DraggingItem);
- DraggingItem = Slot;
- Slot = tmp;
- }
- else
- {
- // Same type, add items:
- cItemHandler * Handler = ItemHandler(DraggingItem.m_ItemType);
- int FreeSlots = Handler->GetMaxStackSize() - Slot.m_ItemCount;
- if (FreeSlots < 0)
- {
- ASSERT(!"Bad item stack size - where did we get more items in a slot than allowed?");
- FreeSlots = 0;
- }
- int Filling = (FreeSlots > DraggingItem.m_ItemCount) ? DraggingItem.m_ItemCount : FreeSlots;
- Slot.m_ItemCount += (char)Filling;
- DraggingItem.m_ItemCount -= (char)Filling;
- if (DraggingItem.m_ItemCount <= 0)
- {
- DraggingItem.Empty();
- }
- }
- break;
- }
- default:
- {
- LOGWARNING("SlotArea: Unhandled click action: %d (%s)", a_ClickAction, ClickActionToString(a_ClickAction));
- m_ParentWindow.BroadcastWholeWindow();
- return;
- }
- } // switch (a_ClickAction
-
- SetSlot(a_SlotNum, a_Player, Slot);
- if (bAsync)
- {
- m_ParentWindow.BroadcastWholeWindow();
- }
-
-}
-
-
-
-
-
-void cSlotArea::ShiftClicked(cPlayer & a_Player, int a_SlotNum, const cItem & a_ClickedItem)
-{
- // Make a copy of the slot, distribute it among the other areas, then update the slot to contain the leftover:
- cItem Slot(*GetSlot(a_SlotNum, a_Player));
- m_ParentWindow.DistributeStack(Slot, a_Player, this, true);
- if (Slot.IsEmpty())
- {
- // Empty the slot completely, the cilent doesn't like left-over ItemType with zero count
- Slot.Empty();
- }
- SetSlot(a_SlotNum, a_Player, Slot);
-
- // Some clients try to guess our actions and not always right (armor slots in 1.2.5), so we fix them:
- m_ParentWindow.BroadcastWholeWindow();
-}
-
-
-
-
-
-void cSlotArea::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_Apply, bool a_KeepEmptySlots)
-{
- for (int i = 0; i < m_NumSlots; i++)
- {
- const cItem * Slot = GetSlot(i, a_Player);
- if (!Slot->IsSameType(a_ItemStack) && (!Slot->IsEmpty() || a_KeepEmptySlots))
- {
- // Different items
- continue;
- }
- int NumFit = ItemHandler(Slot->m_ItemType)->GetMaxStackSize() - Slot->m_ItemCount;
- if (NumFit <= 0)
- {
- // Full stack already
- continue;
- }
- if (NumFit > a_ItemStack.m_ItemCount)
- {
- NumFit = a_ItemStack.m_ItemCount;
- }
- if (a_Apply)
- {
- cItem NewSlot(a_ItemStack);
- NewSlot.m_ItemCount = Slot->m_ItemCount + NumFit;
- SetSlot(i, a_Player, NewSlot);
- }
- a_ItemStack.m_ItemCount -= NumFit;
- if (a_ItemStack.IsEmpty())
- {
- return;
- }
- } // for i - Slots
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cSlotAreaChest:
-
-cSlotAreaChest::cSlotAreaChest(cChestEntity * a_Chest, cWindow & a_ParentWindow) :
- cSlotArea(27, a_ParentWindow),
- m_Chest(a_Chest)
-{
-}
-
-
-
-
-
-const cItem * cSlotAreaChest::GetSlot(int a_SlotNum, cPlayer & a_Player) const
-{
- // a_SlotNum ranges from 0 to 26, use that to index the chest entity's inventory directly:
- return &(m_Chest->GetSlot(a_SlotNum));
-}
-
-
-
-
-
-void cSlotAreaChest::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
-{
- m_Chest->SetSlot(a_SlotNum, a_Item);
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cSlotAreaDoubleChest:
-
-cSlotAreaDoubleChest::cSlotAreaDoubleChest(cChestEntity * a_TopChest, cChestEntity * a_BottomChest, cWindow & a_ParentWindow) :
- cSlotArea(54, a_ParentWindow),
- m_TopChest(a_TopChest),
- m_BottomChest(a_BottomChest)
-{
-}
-
-
-
-
-
-const cItem * cSlotAreaDoubleChest::GetSlot(int a_SlotNum, cPlayer & a_Player) const
-{
- // a_SlotNum ranges from 0 to 53, use that to index the correct chest's inventory:
- if (a_SlotNum < 27)
- {
- return &(m_TopChest->GetSlot(a_SlotNum));
- }
- else
- {
- return &(m_BottomChest->GetSlot(a_SlotNum - 27));
- }
-}
-
-
-
-
-
-void cSlotAreaDoubleChest::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
-{
- if (a_SlotNum < 27)
- {
- m_TopChest->SetSlot(a_SlotNum, a_Item);
- }
- else
- {
- m_BottomChest->SetSlot(a_SlotNum - 27, a_Item);
- }
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cSlotAreaCrafting:
-
-cSlotAreaCrafting::cSlotAreaCrafting(int a_GridSize, cWindow & a_ParentWindow) :
- cSlotAreaTemporary(1 + a_GridSize * a_GridSize, a_ParentWindow),
- m_GridSize(a_GridSize)
-{
- ASSERT((a_GridSize == 2) || (a_GridSize == 3));
-}
-
-
-
-
-
-void cSlotAreaCrafting::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem)
-{
- // Override for craft result slot
- if (a_SlotNum == 0)
- {
- if ((a_ClickAction == caShiftLeftClick) || (a_ClickAction == caShiftRightClick))
- {
- ShiftClickedResult(a_Player);
- }
- else
- {
- ClickedResult(a_Player);
- }
- return;
- }
- super::Clicked(a_Player, a_SlotNum, a_ClickAction, a_ClickedItem);
- UpdateRecipe(a_Player);
-}
-
-
-
-
-
-void cSlotAreaCrafting::OnPlayerRemoved(cPlayer & a_Player)
-{
- // Toss all items on the crafting grid:
- TossItems(a_Player, 1, m_NumSlots);
-
- // Remove the current recipe from the player -> recipe map:
- for (cRecipeMap::iterator itr = m_Recipes.begin(), end = m_Recipes.end(); itr != end; ++itr)
- {
- if (itr->first == a_Player.GetUniqueID())
- {
- // Remove the player from the recipe map:
- m_Recipes.erase(itr);
- return;
- }
- } // for itr - m_Recipes[]
- // Player not found - that is acceptable
-}
-
-
-
-
-
-void cSlotAreaCrafting::ClickedResult(cPlayer & a_Player)
-{
- const cItem * ResultSlot = GetSlot(0, a_Player);
- cItem & DraggingItem = a_Player.GetDraggingItem();
-
- // Get the current recipe:
- cCraftingRecipe & Recipe = GetRecipeForPlayer(a_Player);
-
- cItem * PlayerSlots = GetPlayerSlots(a_Player) + 1;
- cCraftingGrid Grid(PlayerSlots, m_GridSize, m_GridSize);
-
- // If possible, craft:
- if (DraggingItem.IsEmpty())
- {
- DraggingItem = Recipe.GetResult();
- Recipe.ConsumeIngredients(Grid);
- Grid.CopyToItems(PlayerSlots);
- }
- else if (DraggingItem.IsEqual(Recipe.GetResult()))
- {
- cItemHandler * Handler = ItemHandler(Recipe.GetResult().m_ItemType);
- if (DraggingItem.m_ItemCount + Recipe.GetResult().m_ItemCount <= Handler->GetMaxStackSize())
- {
- DraggingItem.m_ItemCount += Recipe.GetResult().m_ItemCount;
- Recipe.ConsumeIngredients(Grid);
- Grid.CopyToItems(PlayerSlots);
- }
- }
-
- // Get the new recipe and update the result slot:
- UpdateRecipe(a_Player);
-
- // We're done. Send all changes to the client and bail out:
- m_ParentWindow.BroadcastWholeWindow();
-}
-
-
-
-
-
-void cSlotAreaCrafting::ShiftClickedResult(cPlayer & a_Player)
-{
- cItem Result(*GetSlot(0, a_Player));
- if (Result.IsEmpty())
- {
- return;
- }
- cItem * PlayerSlots = GetPlayerSlots(a_Player) + 1;
- do
- {
- // Try distributing the result. If it fails, bail out:
- cItem ResultCopy(Result);
- m_ParentWindow.DistributeStack(ResultCopy, a_Player, this, false);
- if (!ResultCopy.IsEmpty())
- {
- // Couldn't distribute all of it. Bail out
- return;
- }
-
- // Distribute the result, this time for real:
- ResultCopy = Result;
- m_ParentWindow.DistributeStack(ResultCopy, a_Player, this, true);
-
- // Remove the ingredients from the crafting grid and update the recipe:
- cCraftingRecipe & Recipe = GetRecipeForPlayer(a_Player);
- cCraftingGrid Grid(PlayerSlots, m_GridSize, m_GridSize);
- Recipe.ConsumeIngredients(Grid);
- Grid.CopyToItems(PlayerSlots);
- UpdateRecipe(a_Player);
- if (!Recipe.GetResult().IsEqual(Result))
- {
- // The recipe has changed, bail out
- return;
- }
- } while (true);
-}
-
-
-
-
-
-void cSlotAreaCrafting::UpdateRecipe(cPlayer & a_Player)
-{
- cCraftingGrid Grid(GetPlayerSlots(a_Player) + 1, m_GridSize, m_GridSize);
- 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);
-}
-
-
-
-
-
-cCraftingRecipe & cSlotAreaCrafting::GetRecipeForPlayer(cPlayer & a_Player)
-{
- for (cRecipeMap::iterator itr = m_Recipes.begin(), end = m_Recipes.end(); itr != end; ++itr)
- {
- if (itr->first == a_Player.GetUniqueID())
- {
- return itr->second;
- }
- } // for itr - m_Recipes[]
-
- // Not found. Add a new one:
- cCraftingGrid Grid(GetPlayerSlots(a_Player) + 1, m_GridSize, m_GridSize);
- cCraftingRecipe Recipe(Grid);
- cRoot::Get()->GetCraftingRecipes()->GetRecipe(&a_Player, Grid, Recipe);
- m_Recipes.push_back(std::make_pair(a_Player.GetUniqueID(), Recipe));
- return m_Recipes.back().second;
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cSlotAreaFurnace:
-
-cSlotAreaFurnace::cSlotAreaFurnace(cFurnaceEntity * a_Furnace, cWindow & a_ParentWindow) :
- cSlotArea(3, a_ParentWindow),
- m_Furnace(a_Furnace)
-{
- m_Furnace->GetContents().AddListener(*this);
-}
-
-
-
-
-
-cSlotAreaFurnace::~cSlotAreaFurnace()
-{
- m_Furnace->GetContents().RemoveListener(*this);
-}
-
-
-
-
-
-void cSlotAreaFurnace::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem)
-{
- super::Clicked(a_Player, a_SlotNum, a_ClickAction, a_ClickedItem);
-
- if (m_Furnace == NULL)
- {
- LOGERROR("cSlotAreaFurnace::Clicked(): m_Furnace == NULL");
- ASSERT(!"cSlotAreaFurnace::Clicked(): m_Furnace == NULL");
- return;
- }
-}
-
-
-
-
-
-const cItem * cSlotAreaFurnace::GetSlot(int a_SlotNum, cPlayer & a_Player) const
-{
- // a_SlotNum ranges from 0 to 2, query the items from the underlying furnace:
- return &(m_Furnace->GetSlot(a_SlotNum));
-}
-
-
-
-
-
-void cSlotAreaFurnace::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
-{
- m_Furnace->SetSlot(a_SlotNum, a_Item);
-}
-
-
-
-
-
-void cSlotAreaFurnace::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum)
-{
- // Something has changed in the window, broadcast the entire window to all clients
- ASSERT(a_ItemGrid == &(m_Furnace->GetContents()));
-
- m_ParentWindow.BroadcastWholeWindow();
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cSlotAreaInventoryBase:
-
-cSlotAreaInventoryBase::cSlotAreaInventoryBase(int a_NumSlots, int a_SlotOffset, cWindow & a_ParentWindow) :
- cSlotArea(a_NumSlots, a_ParentWindow),
- m_SlotOffset(a_SlotOffset)
-{
-}
-
-
-
-
-
-void cSlotAreaInventoryBase::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem)
-{
- if ((a_Player.GetGameMode() == eGameMode_Creative) && (m_ParentWindow.GetWindowType() == cWindow::Inventory))
- {
- // Creative inventory must treat a_ClickedItem as a DraggedItem instead, replacing the inventory slot with it
- SetSlot(a_SlotNum, a_Player, a_ClickedItem);
- return;
- }
-
- // Survival inventory and all other windows' inventory has the same handling as normal slot areas
- super::Clicked(a_Player, a_SlotNum, a_ClickAction, a_ClickedItem);
- return;
-}
-
-
-
-
-
-const cItem * cSlotAreaInventoryBase::GetSlot(int a_SlotNum, cPlayer & a_Player) const
-{
- // a_SlotNum ranges from 0 to 35, map that to the player's inventory slots according to the internal offset
- return &a_Player.GetInventory().GetSlot(a_SlotNum + m_SlotOffset);
-}
-
-
-
-
-
-void cSlotAreaInventoryBase::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
-{
- a_Player.GetInventory().SetSlot(a_SlotNum + m_SlotOffset, a_Item);
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cSlotAreaArmor:
-
-void cSlotAreaArmor::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots)
-{
- if (ItemCategory::IsHelmet(a_ItemStack.m_ItemType) && GetSlot(0, a_Player)->IsEmpty())
- {
- if (a_ShouldApply)
- {
- SetSlot(0, a_Player, a_ItemStack.CopyOne());
- }
- a_ItemStack.m_ItemCount -= 1;
- }
- else if (ItemCategory::IsChestPlate(a_ItemStack.m_ItemType) && GetSlot(1, a_Player)->IsEmpty())
- {
- if (a_ShouldApply)
- {
- SetSlot(1, a_Player, a_ItemStack.CopyOne());
- }
- a_ItemStack.m_ItemCount -= 1;
- }
- else if (ItemCategory::IsLeggings(a_ItemStack.m_ItemType) && GetSlot(2, a_Player)->IsEmpty())
- {
- if (a_ShouldApply)
- {
- SetSlot(2, a_Player, a_ItemStack.CopyOne());
- }
- a_ItemStack.m_ItemCount -= 1;
- }
- else if (ItemCategory::IsBoots(a_ItemStack.m_ItemType) && GetSlot(3, a_Player)->IsEmpty())
- {
- if (a_ShouldApply)
- {
- SetSlot(3, a_Player, a_ItemStack.CopyOne());
- }
- a_ItemStack.m_ItemCount -= 1;
- }
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cSlotAreaItemGrid:
-
-cSlotAreaItemGrid::cSlotAreaItemGrid(cItemGrid & a_ItemGrid, cWindow & a_ParentWindow) :
- super(a_ItemGrid.GetNumSlots(), a_ParentWindow),
- m_ItemGrid(a_ItemGrid)
-{
- m_ItemGrid.AddListener(*this);
-}
-
-
-
-
-
-cSlotAreaItemGrid::~cSlotAreaItemGrid()
-{
- m_ItemGrid.RemoveListener(*this);
-}
-
-
-
-
-
-const cItem * cSlotAreaItemGrid::GetSlot(int a_SlotNum, cPlayer & a_Player) const
-{
- return &m_ItemGrid.GetSlot(a_SlotNum);
-}
-
-
-
-
-
-void cSlotAreaItemGrid::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
-{
- m_ItemGrid.SetSlot(a_SlotNum, a_Item);
-}
-
-
-
-
-
-void cSlotAreaItemGrid::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum)
-{
- ASSERT(a_ItemGrid == &m_ItemGrid);
- m_ParentWindow.BroadcastSlot(this, a_SlotNum);
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cSlotAreaTemporary:
-
-cSlotAreaTemporary::cSlotAreaTemporary(int a_NumSlots, cWindow & a_ParentWindow) :
- cSlotArea(a_NumSlots, a_ParentWindow)
-{
-}
-
-
-
-
-
-const cItem * cSlotAreaTemporary::GetSlot(int a_SlotNum, cPlayer & a_Player) const
-{
- cItemMap::const_iterator itr = m_Items.find(a_Player.GetUniqueID());
- if (itr == m_Items.end())
- {
- LOGERROR("cSlotAreaTemporary: player \"%s\" not found for slot %d!", a_Player.GetName().c_str(), a_SlotNum);
- ASSERT(!"cSlotAreaTemporary: player not found!");
-
- // Player not found, this should not happen, ever! Return NULL, but things may break by this.
- return NULL;
- }
-
- if (a_SlotNum >= (int)(itr->second.size()))
- {
- LOGERROR("cSlotAreaTemporary: asking for more slots than actually stored!");
- ASSERT(!"cSlotAreaTemporary: asking for more slots than actually stored!");
- return NULL;
- }
-
- return &(itr->second[a_SlotNum]);
-}
-
-
-
-
-
-void cSlotAreaTemporary::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
-{
- cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID());
- if (itr == m_Items.end())
- {
- // Player not found
- LOGWARNING("cSlotAreaTemporary: player not found!");
- return;
- }
-
- if (a_SlotNum >= (int)(itr->second.size()))
- {
- LOGERROR("cSlotAreaTemporary: asking for more slots than actually stored!");
- return;
- }
-
- itr->second[a_SlotNum] = a_Item;
-}
-
-
-
-
-
-void cSlotAreaTemporary::OnPlayerAdded(cPlayer & a_Player)
-{
- ASSERT(m_Items.find(a_Player.GetUniqueID()) == m_Items.end()); // The player shouldn't be in the itemmap, otherwise we probably have a leak
- m_Items[a_Player.GetUniqueID()].resize(m_NumSlots); // Make the vector the specified size of empty items
-}
-
-
-
-
-
-void cSlotAreaTemporary::OnPlayerRemoved(cPlayer & a_Player)
-{
- cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID());
- ASSERT(itr != m_Items.end()); // The player should be in the list, otherwise a call to OnPlayerAdded() was mismatched
- m_Items.erase(itr);
-}
-
-
-
-
-
-void cSlotAreaTemporary::TossItems(cPlayer & a_Player, int a_Begin, int a_End)
-{
- cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID());
- if (itr == m_Items.end())
- {
- LOGWARNING("Player tossing items (%s) not found in the item map", a_Player.GetName().c_str());
- return;
- }
-
- cItems Drops;
- for (int i = a_Begin; i < a_End; i++)
- {
- cItem & Item = itr->second[i];
- if (!Item.IsEmpty())
- {
- Drops.push_back(Item);
- }
- Item.Empty();
- } // for i - itr->second[]
-
- double vX = 0, vY = 0, vZ = 0;
- EulerToVector(-a_Player.GetRotation(), a_Player.GetPitch(), vZ, vX, vY);
- vY = -vY * 2 + 1.f;
- a_Player.GetWorld()->SpawnItemPickups(Drops, a_Player.GetPosX(), a_Player.GetPosY() + 1.6f, a_Player.GetPosZ(), vX * 2, vY * 2, vZ * 2);
-}
-
-
-
-
-
-cItem * cSlotAreaTemporary::GetPlayerSlots(cPlayer & a_Player)
-{
- cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID());
- if (itr == m_Items.end())
- {
- return NULL;
- }
- return &(itr->second[0]);
-}
-
-
-
-
+
+// SlotArea.cpp
+
+// Implements the cSlotArea class and its descendants
+
+#include "Globals.h"
+#include "SlotArea.h"
+#include "../Player.h"
+#include "../BlockEntities/ChestEntity.h"
+#include "../BlockEntities/DropSpenserEntity.h"
+#include "../BlockEntities/FurnaceEntity.h"
+#include "../Items/ItemHandler.h"
+#include "Window.h"
+#include "../CraftingRecipes.h"
+#include "../Root.h"
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSlotArea:
+
+cSlotArea::cSlotArea(int a_NumSlots, cWindow & a_ParentWindow) :
+ m_NumSlots(a_NumSlots),
+ m_ParentWindow(a_ParentWindow)
+{
+}
+
+
+
+
+
+void cSlotArea::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem)
+{
+ /*
+ LOGD("Slot area with %d slots clicked at slot number %d, clicked item %s, slot item %s",
+ GetNumSlots(), a_SlotNum,
+ ItemToFullString(a_ClickedItem).c_str(),
+ ItemToFullString(*GetSlot(a_SlotNum, a_Player)).c_str()
+ );
+ */
+
+ ASSERT((a_SlotNum >= 0) && (a_SlotNum < GetNumSlots()));
+
+ bool bAsync = false;
+ if (GetSlot(a_SlotNum, a_Player) == NULL)
+ {
+ LOGWARNING("GetSlot(%d) returned NULL! Ignoring click", a_SlotNum);
+ return;
+ }
+
+ if ((a_ClickAction == caShiftLeftClick) || (a_ClickAction == caShiftRightClick))
+ {
+ if (!a_Player.IsDraggingItem())
+ {
+ ShiftClicked(a_Player, a_SlotNum, a_ClickedItem);
+ return;
+ }
+ LOGD("Shift clicked, but the player is dragging an item: %s", ItemToFullString(a_Player.GetDraggingItem()).c_str());
+ return;
+ }
+
+ cItem Slot(*GetSlot(a_SlotNum, a_Player));
+ if (!Slot.IsSameType(a_ClickedItem))
+ {
+ LOGWARNING("*** Window lost sync at item %d in SlotArea with %d items ***", a_SlotNum, m_NumSlots);
+ LOGWARNING("My item: %s", ItemToFullString(Slot).c_str());
+ LOGWARNING("Their item: %s", ItemToFullString(a_ClickedItem).c_str());
+ bAsync = true;
+ }
+ cItem & DraggingItem = a_Player.GetDraggingItem();
+ switch (a_ClickAction)
+ {
+ case caRightClick:
+ {
+ if (DraggingItem.m_ItemType <= 0) // Empty-handed?
+ {
+ DraggingItem.m_ItemCount = (char)(((float)Slot.m_ItemCount) / 2.f + 0.5f);
+ Slot.m_ItemCount -= DraggingItem.m_ItemCount;
+ DraggingItem.m_ItemType = Slot.m_ItemType;
+ DraggingItem.m_ItemDamage = Slot.m_ItemDamage;
+
+ if (Slot.m_ItemCount <= 0)
+ {
+ Slot.Empty();
+ }
+ }
+ else if ((Slot.m_ItemType <= 0) || DraggingItem.IsEqual(Slot))
+ {
+ // Drop one item in slot
+ cItemHandler * Handler = ItemHandler(Slot.m_ItemType);
+ if ((DraggingItem.m_ItemCount > 0) && (Slot.m_ItemCount < Handler->GetMaxStackSize()))
+ {
+ Slot.m_ItemType = DraggingItem.m_ItemType;
+ Slot.m_ItemCount++;
+ Slot.m_ItemDamage = DraggingItem.m_ItemDamage;
+ DraggingItem.m_ItemCount--;
+ }
+ if (DraggingItem.m_ItemCount <= 0)
+ {
+ DraggingItem.Empty();
+ }
+ }
+ else if (!DraggingItem.IsEqual(Slot))
+ {
+ // Swap contents
+ cItem tmp(DraggingItem);
+ DraggingItem = Slot;
+ Slot = tmp;
+ }
+ break;
+ }
+
+ case caLeftClick:
+ {
+ // Left-clicked
+ if (!DraggingItem.IsEqual(Slot))
+ {
+ // Switch contents
+ cItem tmp(DraggingItem);
+ DraggingItem = Slot;
+ Slot = tmp;
+ }
+ else
+ {
+ // Same type, add items:
+ cItemHandler * Handler = ItemHandler(DraggingItem.m_ItemType);
+ int FreeSlots = Handler->GetMaxStackSize() - Slot.m_ItemCount;
+ if (FreeSlots < 0)
+ {
+ ASSERT(!"Bad item stack size - where did we get more items in a slot than allowed?");
+ FreeSlots = 0;
+ }
+ int Filling = (FreeSlots > DraggingItem.m_ItemCount) ? DraggingItem.m_ItemCount : FreeSlots;
+ Slot.m_ItemCount += (char)Filling;
+ DraggingItem.m_ItemCount -= (char)Filling;
+ if (DraggingItem.m_ItemCount <= 0)
+ {
+ DraggingItem.Empty();
+ }
+ }
+ break;
+ }
+ default:
+ {
+ LOGWARNING("SlotArea: Unhandled click action: %d (%s)", a_ClickAction, ClickActionToString(a_ClickAction));
+ m_ParentWindow.BroadcastWholeWindow();
+ return;
+ }
+ } // switch (a_ClickAction
+
+ SetSlot(a_SlotNum, a_Player, Slot);
+ if (bAsync)
+ {
+ m_ParentWindow.BroadcastWholeWindow();
+ }
+
+}
+
+
+
+
+
+void cSlotArea::ShiftClicked(cPlayer & a_Player, int a_SlotNum, const cItem & a_ClickedItem)
+{
+ // Make a copy of the slot, distribute it among the other areas, then update the slot to contain the leftover:
+ cItem Slot(*GetSlot(a_SlotNum, a_Player));
+ m_ParentWindow.DistributeStack(Slot, a_Player, this, true);
+ if (Slot.IsEmpty())
+ {
+ // Empty the slot completely, the cilent doesn't like left-over ItemType with zero count
+ Slot.Empty();
+ }
+ SetSlot(a_SlotNum, a_Player, Slot);
+
+ // Some clients try to guess our actions and not always right (armor slots in 1.2.5), so we fix them:
+ m_ParentWindow.BroadcastWholeWindow();
+}
+
+
+
+
+
+void cSlotArea::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_Apply, bool a_KeepEmptySlots)
+{
+ for (int i = 0; i < m_NumSlots; i++)
+ {
+ const cItem * Slot = GetSlot(i, a_Player);
+ if (!Slot->IsSameType(a_ItemStack) && (!Slot->IsEmpty() || a_KeepEmptySlots))
+ {
+ // Different items
+ continue;
+ }
+ int NumFit = ItemHandler(Slot->m_ItemType)->GetMaxStackSize() - Slot->m_ItemCount;
+ if (NumFit <= 0)
+ {
+ // Full stack already
+ continue;
+ }
+ if (NumFit > a_ItemStack.m_ItemCount)
+ {
+ NumFit = a_ItemStack.m_ItemCount;
+ }
+ if (a_Apply)
+ {
+ cItem NewSlot(a_ItemStack);
+ NewSlot.m_ItemCount = Slot->m_ItemCount + NumFit;
+ SetSlot(i, a_Player, NewSlot);
+ }
+ a_ItemStack.m_ItemCount -= NumFit;
+ if (a_ItemStack.IsEmpty())
+ {
+ return;
+ }
+ } // for i - Slots
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSlotAreaChest:
+
+cSlotAreaChest::cSlotAreaChest(cChestEntity * a_Chest, cWindow & a_ParentWindow) :
+ cSlotArea(27, a_ParentWindow),
+ m_Chest(a_Chest)
+{
+}
+
+
+
+
+
+const cItem * cSlotAreaChest::GetSlot(int a_SlotNum, cPlayer & a_Player) const
+{
+ // a_SlotNum ranges from 0 to 26, use that to index the chest entity's inventory directly:
+ return &(m_Chest->GetSlot(a_SlotNum));
+}
+
+
+
+
+
+void cSlotAreaChest::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
+{
+ m_Chest->SetSlot(a_SlotNum, a_Item);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSlotAreaDoubleChest:
+
+cSlotAreaDoubleChest::cSlotAreaDoubleChest(cChestEntity * a_TopChest, cChestEntity * a_BottomChest, cWindow & a_ParentWindow) :
+ cSlotArea(54, a_ParentWindow),
+ m_TopChest(a_TopChest),
+ m_BottomChest(a_BottomChest)
+{
+}
+
+
+
+
+
+const cItem * cSlotAreaDoubleChest::GetSlot(int a_SlotNum, cPlayer & a_Player) const
+{
+ // a_SlotNum ranges from 0 to 53, use that to index the correct chest's inventory:
+ if (a_SlotNum < 27)
+ {
+ return &(m_TopChest->GetSlot(a_SlotNum));
+ }
+ else
+ {
+ return &(m_BottomChest->GetSlot(a_SlotNum - 27));
+ }
+}
+
+
+
+
+
+void cSlotAreaDoubleChest::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
+{
+ if (a_SlotNum < 27)
+ {
+ m_TopChest->SetSlot(a_SlotNum, a_Item);
+ }
+ else
+ {
+ m_BottomChest->SetSlot(a_SlotNum - 27, a_Item);
+ }
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSlotAreaCrafting:
+
+cSlotAreaCrafting::cSlotAreaCrafting(int a_GridSize, cWindow & a_ParentWindow) :
+ cSlotAreaTemporary(1 + a_GridSize * a_GridSize, a_ParentWindow),
+ m_GridSize(a_GridSize)
+{
+ ASSERT((a_GridSize == 2) || (a_GridSize == 3));
+}
+
+
+
+
+
+void cSlotAreaCrafting::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem)
+{
+ // Override for craft result slot
+ if (a_SlotNum == 0)
+ {
+ if ((a_ClickAction == caShiftLeftClick) || (a_ClickAction == caShiftRightClick))
+ {
+ ShiftClickedResult(a_Player);
+ }
+ else
+ {
+ ClickedResult(a_Player);
+ }
+ return;
+ }
+ super::Clicked(a_Player, a_SlotNum, a_ClickAction, a_ClickedItem);
+ UpdateRecipe(a_Player);
+}
+
+
+
+
+
+void cSlotAreaCrafting::OnPlayerRemoved(cPlayer & a_Player)
+{
+ // Toss all items on the crafting grid:
+ TossItems(a_Player, 1, m_NumSlots);
+
+ // Remove the current recipe from the player -> recipe map:
+ for (cRecipeMap::iterator itr = m_Recipes.begin(), end = m_Recipes.end(); itr != end; ++itr)
+ {
+ if (itr->first == a_Player.GetUniqueID())
+ {
+ // Remove the player from the recipe map:
+ m_Recipes.erase(itr);
+ return;
+ }
+ } // for itr - m_Recipes[]
+ // Player not found - that is acceptable
+}
+
+
+
+
+
+void cSlotAreaCrafting::ClickedResult(cPlayer & a_Player)
+{
+ const cItem * ResultSlot = GetSlot(0, a_Player);
+ cItem & DraggingItem = a_Player.GetDraggingItem();
+
+ // Get the current recipe:
+ cCraftingRecipe & Recipe = GetRecipeForPlayer(a_Player);
+
+ cItem * PlayerSlots = GetPlayerSlots(a_Player) + 1;
+ cCraftingGrid Grid(PlayerSlots, m_GridSize, m_GridSize);
+
+ // If possible, craft:
+ if (DraggingItem.IsEmpty())
+ {
+ DraggingItem = Recipe.GetResult();
+ Recipe.ConsumeIngredients(Grid);
+ Grid.CopyToItems(PlayerSlots);
+ }
+ else if (DraggingItem.IsEqual(Recipe.GetResult()))
+ {
+ cItemHandler * Handler = ItemHandler(Recipe.GetResult().m_ItemType);
+ if (DraggingItem.m_ItemCount + Recipe.GetResult().m_ItemCount <= Handler->GetMaxStackSize())
+ {
+ DraggingItem.m_ItemCount += Recipe.GetResult().m_ItemCount;
+ Recipe.ConsumeIngredients(Grid);
+ Grid.CopyToItems(PlayerSlots);
+ }
+ }
+
+ // Get the new recipe and update the result slot:
+ UpdateRecipe(a_Player);
+
+ // We're done. Send all changes to the client and bail out:
+ m_ParentWindow.BroadcastWholeWindow();
+}
+
+
+
+
+
+void cSlotAreaCrafting::ShiftClickedResult(cPlayer & a_Player)
+{
+ cItem Result(*GetSlot(0, a_Player));
+ if (Result.IsEmpty())
+ {
+ return;
+ }
+ cItem * PlayerSlots = GetPlayerSlots(a_Player) + 1;
+ do
+ {
+ // Try distributing the result. If it fails, bail out:
+ cItem ResultCopy(Result);
+ m_ParentWindow.DistributeStack(ResultCopy, a_Player, this, false);
+ if (!ResultCopy.IsEmpty())
+ {
+ // Couldn't distribute all of it. Bail out
+ return;
+ }
+
+ // Distribute the result, this time for real:
+ ResultCopy = Result;
+ m_ParentWindow.DistributeStack(ResultCopy, a_Player, this, true);
+
+ // Remove the ingredients from the crafting grid and update the recipe:
+ cCraftingRecipe & Recipe = GetRecipeForPlayer(a_Player);
+ cCraftingGrid Grid(PlayerSlots, m_GridSize, m_GridSize);
+ Recipe.ConsumeIngredients(Grid);
+ Grid.CopyToItems(PlayerSlots);
+ UpdateRecipe(a_Player);
+ if (!Recipe.GetResult().IsEqual(Result))
+ {
+ // The recipe has changed, bail out
+ return;
+ }
+ } while (true);
+}
+
+
+
+
+
+void cSlotAreaCrafting::UpdateRecipe(cPlayer & a_Player)
+{
+ cCraftingGrid Grid(GetPlayerSlots(a_Player) + 1, m_GridSize, m_GridSize);
+ 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);
+}
+
+
+
+
+
+cCraftingRecipe & cSlotAreaCrafting::GetRecipeForPlayer(cPlayer & a_Player)
+{
+ for (cRecipeMap::iterator itr = m_Recipes.begin(), end = m_Recipes.end(); itr != end; ++itr)
+ {
+ if (itr->first == a_Player.GetUniqueID())
+ {
+ return itr->second;
+ }
+ } // for itr - m_Recipes[]
+
+ // Not found. Add a new one:
+ cCraftingGrid Grid(GetPlayerSlots(a_Player) + 1, m_GridSize, m_GridSize);
+ cCraftingRecipe Recipe(Grid);
+ cRoot::Get()->GetCraftingRecipes()->GetRecipe(&a_Player, Grid, Recipe);
+ m_Recipes.push_back(std::make_pair(a_Player.GetUniqueID(), Recipe));
+ return m_Recipes.back().second;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSlotAreaFurnace:
+
+cSlotAreaFurnace::cSlotAreaFurnace(cFurnaceEntity * a_Furnace, cWindow & a_ParentWindow) :
+ cSlotArea(3, a_ParentWindow),
+ m_Furnace(a_Furnace)
+{
+ m_Furnace->GetContents().AddListener(*this);
+}
+
+
+
+
+
+cSlotAreaFurnace::~cSlotAreaFurnace()
+{
+ m_Furnace->GetContents().RemoveListener(*this);
+}
+
+
+
+
+
+void cSlotAreaFurnace::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem)
+{
+ super::Clicked(a_Player, a_SlotNum, a_ClickAction, a_ClickedItem);
+
+ if (m_Furnace == NULL)
+ {
+ LOGERROR("cSlotAreaFurnace::Clicked(): m_Furnace == NULL");
+ ASSERT(!"cSlotAreaFurnace::Clicked(): m_Furnace == NULL");
+ return;
+ }
+}
+
+
+
+
+
+const cItem * cSlotAreaFurnace::GetSlot(int a_SlotNum, cPlayer & a_Player) const
+{
+ // a_SlotNum ranges from 0 to 2, query the items from the underlying furnace:
+ return &(m_Furnace->GetSlot(a_SlotNum));
+}
+
+
+
+
+
+void cSlotAreaFurnace::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
+{
+ m_Furnace->SetSlot(a_SlotNum, a_Item);
+}
+
+
+
+
+
+void cSlotAreaFurnace::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum)
+{
+ // Something has changed in the window, broadcast the entire window to all clients
+ ASSERT(a_ItemGrid == &(m_Furnace->GetContents()));
+
+ m_ParentWindow.BroadcastWholeWindow();
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSlotAreaInventoryBase:
+
+cSlotAreaInventoryBase::cSlotAreaInventoryBase(int a_NumSlots, int a_SlotOffset, cWindow & a_ParentWindow) :
+ cSlotArea(a_NumSlots, a_ParentWindow),
+ m_SlotOffset(a_SlotOffset)
+{
+}
+
+
+
+
+
+void cSlotAreaInventoryBase::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem)
+{
+ if ((a_Player.GetGameMode() == eGameMode_Creative) && (m_ParentWindow.GetWindowType() == cWindow::Inventory))
+ {
+ // Creative inventory must treat a_ClickedItem as a DraggedItem instead, replacing the inventory slot with it
+ SetSlot(a_SlotNum, a_Player, a_ClickedItem);
+ return;
+ }
+
+ // Survival inventory and all other windows' inventory has the same handling as normal slot areas
+ super::Clicked(a_Player, a_SlotNum, a_ClickAction, a_ClickedItem);
+ return;
+}
+
+
+
+
+
+const cItem * cSlotAreaInventoryBase::GetSlot(int a_SlotNum, cPlayer & a_Player) const
+{
+ // a_SlotNum ranges from 0 to 35, map that to the player's inventory slots according to the internal offset
+ return &a_Player.GetInventory().GetSlot(a_SlotNum + m_SlotOffset);
+}
+
+
+
+
+
+void cSlotAreaInventoryBase::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
+{
+ a_Player.GetInventory().SetSlot(a_SlotNum + m_SlotOffset, a_Item);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSlotAreaArmor:
+
+void cSlotAreaArmor::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots)
+{
+ if (ItemCategory::IsHelmet(a_ItemStack.m_ItemType) && GetSlot(0, a_Player)->IsEmpty())
+ {
+ if (a_ShouldApply)
+ {
+ SetSlot(0, a_Player, a_ItemStack.CopyOne());
+ }
+ a_ItemStack.m_ItemCount -= 1;
+ }
+ else if (ItemCategory::IsChestPlate(a_ItemStack.m_ItemType) && GetSlot(1, a_Player)->IsEmpty())
+ {
+ if (a_ShouldApply)
+ {
+ SetSlot(1, a_Player, a_ItemStack.CopyOne());
+ }
+ a_ItemStack.m_ItemCount -= 1;
+ }
+ else if (ItemCategory::IsLeggings(a_ItemStack.m_ItemType) && GetSlot(2, a_Player)->IsEmpty())
+ {
+ if (a_ShouldApply)
+ {
+ SetSlot(2, a_Player, a_ItemStack.CopyOne());
+ }
+ a_ItemStack.m_ItemCount -= 1;
+ }
+ else if (ItemCategory::IsBoots(a_ItemStack.m_ItemType) && GetSlot(3, a_Player)->IsEmpty())
+ {
+ if (a_ShouldApply)
+ {
+ SetSlot(3, a_Player, a_ItemStack.CopyOne());
+ }
+ a_ItemStack.m_ItemCount -= 1;
+ }
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSlotAreaItemGrid:
+
+cSlotAreaItemGrid::cSlotAreaItemGrid(cItemGrid & a_ItemGrid, cWindow & a_ParentWindow) :
+ super(a_ItemGrid.GetNumSlots(), a_ParentWindow),
+ m_ItemGrid(a_ItemGrid)
+{
+ m_ItemGrid.AddListener(*this);
+}
+
+
+
+
+
+cSlotAreaItemGrid::~cSlotAreaItemGrid()
+{
+ m_ItemGrid.RemoveListener(*this);
+}
+
+
+
+
+
+const cItem * cSlotAreaItemGrid::GetSlot(int a_SlotNum, cPlayer & a_Player) const
+{
+ return &m_ItemGrid.GetSlot(a_SlotNum);
+}
+
+
+
+
+
+void cSlotAreaItemGrid::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
+{
+ m_ItemGrid.SetSlot(a_SlotNum, a_Item);
+}
+
+
+
+
+
+void cSlotAreaItemGrid::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum)
+{
+ ASSERT(a_ItemGrid == &m_ItemGrid);
+ m_ParentWindow.BroadcastSlot(this, a_SlotNum);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSlotAreaTemporary:
+
+cSlotAreaTemporary::cSlotAreaTemporary(int a_NumSlots, cWindow & a_ParentWindow) :
+ cSlotArea(a_NumSlots, a_ParentWindow)
+{
+}
+
+
+
+
+
+const cItem * cSlotAreaTemporary::GetSlot(int a_SlotNum, cPlayer & a_Player) const
+{
+ cItemMap::const_iterator itr = m_Items.find(a_Player.GetUniqueID());
+ if (itr == m_Items.end())
+ {
+ LOGERROR("cSlotAreaTemporary: player \"%s\" not found for slot %d!", a_Player.GetName().c_str(), a_SlotNum);
+ ASSERT(!"cSlotAreaTemporary: player not found!");
+
+ // Player not found, this should not happen, ever! Return NULL, but things may break by this.
+ return NULL;
+ }
+
+ if (a_SlotNum >= (int)(itr->second.size()))
+ {
+ LOGERROR("cSlotAreaTemporary: asking for more slots than actually stored!");
+ ASSERT(!"cSlotAreaTemporary: asking for more slots than actually stored!");
+ return NULL;
+ }
+
+ return &(itr->second[a_SlotNum]);
+}
+
+
+
+
+
+void cSlotAreaTemporary::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
+{
+ cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID());
+ if (itr == m_Items.end())
+ {
+ // Player not found
+ LOGWARNING("cSlotAreaTemporary: player not found!");
+ return;
+ }
+
+ if (a_SlotNum >= (int)(itr->second.size()))
+ {
+ LOGERROR("cSlotAreaTemporary: asking for more slots than actually stored!");
+ return;
+ }
+
+ itr->second[a_SlotNum] = a_Item;
+}
+
+
+
+
+
+void cSlotAreaTemporary::OnPlayerAdded(cPlayer & a_Player)
+{
+ ASSERT(m_Items.find(a_Player.GetUniqueID()) == m_Items.end()); // The player shouldn't be in the itemmap, otherwise we probably have a leak
+ m_Items[a_Player.GetUniqueID()].resize(m_NumSlots); // Make the vector the specified size of empty items
+}
+
+
+
+
+
+void cSlotAreaTemporary::OnPlayerRemoved(cPlayer & a_Player)
+{
+ cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID());
+ ASSERT(itr != m_Items.end()); // The player should be in the list, otherwise a call to OnPlayerAdded() was mismatched
+ m_Items.erase(itr);
+}
+
+
+
+
+
+void cSlotAreaTemporary::TossItems(cPlayer & a_Player, int a_Begin, int a_End)
+{
+ cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID());
+ if (itr == m_Items.end())
+ {
+ LOGWARNING("Player tossing items (%s) not found in the item map", a_Player.GetName().c_str());
+ return;
+ }
+
+ cItems Drops;
+ for (int i = a_Begin; i < a_End; i++)
+ {
+ cItem & Item = itr->second[i];
+ if (!Item.IsEmpty())
+ {
+ Drops.push_back(Item);
+ }
+ Item.Empty();
+ } // for i - itr->second[]
+
+ double vX = 0, vY = 0, vZ = 0;
+ EulerToVector(-a_Player.GetRotation(), a_Player.GetPitch(), vZ, vX, vY);
+ vY = -vY * 2 + 1.f;
+ a_Player.GetWorld()->SpawnItemPickups(Drops, a_Player.GetPosX(), a_Player.GetPosY() + 1.6f, a_Player.GetPosZ(), vX * 2, vY * 2, vZ * 2);
+}
+
+
+
+
+
+cItem * cSlotAreaTemporary::GetPlayerSlots(cPlayer & a_Player)
+{
+ cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID());
+ if (itr == m_Items.end())
+ {
+ return NULL;
+ }
+ return &(itr->second[0]);
+}
+
+
+
+
diff --git a/source/UI/SlotArea.h b/source/UI/SlotArea.h
index 516e6d644..943452feb 100644
--- a/source/UI/SlotArea.h
+++ b/source/UI/SlotArea.h
@@ -1,303 +1,303 @@
-
-// SlotArea.h
-
-// Interfaces to the cSlotArea class representing a contiguous area of slots in a UI window
-
-
-
-
-#pragma once
-
-#include "../Inventory.h"
-
-
-
-class cWindow;
-class cPlayer;
-class cChestEntity;
-class cDropSpenserEntity;
-class cFurnaceEntity;
-class cCraftingRecipe;
-
-
-
-
-
-class cSlotArea
-{
-public:
- cSlotArea(int a_NumSlots, cWindow & a_ParentWindow);
- virtual ~cSlotArea() {} // force a virtual destructor in all subclasses
-
- int GetNumSlots(void) const { return m_NumSlots; }
-
- /// Called to retrieve an item in the specified slot for the specified player. Must return a valid cItem.
- virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const = 0;
-
- /// Called to set an item in the specified slot for the specified player
- virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) = 0;
-
- /// Called when a player clicks in the window. Parameters taken from the click packet.
- virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem);
-
- /// Called from Clicked if it is a valid shiftclick
- virtual void ShiftClicked(cPlayer & a_Player, int a_SlotNum, const cItem & a_ClickedItem);
-
- /// Called when a new player opens the same parent window. The window already tracks the player. CS-locked.
- virtual void OnPlayerAdded(cPlayer & a_Player) {} ;
-
- /// Called when one of the players closes the parent window. The window already doesn't track the player. CS-locked.
- virtual void OnPlayerRemoved(cPlayer & a_Player) {} ;
-
- /** Called to store as much of a_ItemStack in the area as possible. a_ItemStack is modified to reflect the change.
- The default implementation searches each slot for available space and distributes the stack there.
- if a_ShouldApply is true, the changes are written into the slots;
- if a_ShouldApply is false, only a_ItemStack is modified to reflect the number of fits (for fit-testing purposes)
- If a_KeepEmptySlots is true, empty slots will be skipped and won't be filled
- */
- virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots);
-
-protected:
- int m_NumSlots;
- cWindow & m_ParentWindow;
-} ;
-
-
-
-
-
-/// Handles any part of the inventory, using parameters in constructor to distinguish between the parts
-class cSlotAreaInventoryBase :
- public cSlotArea
-{
- typedef cSlotArea super;
-
-public:
- cSlotAreaInventoryBase(int a_NumSlots, int a_SlotOffset, cWindow & a_ParentWindow);
-
- // Creative inventory's click handling is somewhat different from survival inventory's, handle that here:
- virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override;
-
- virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override;
- virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
-
-protected:
- int m_SlotOffset; // Index that this area's slot 0 has in the underlying cInventory
-} ;
-
-
-
-
-
-/// Handles the main inventory of each player, excluding the armor and hotbar
-class cSlotAreaInventory :
- public cSlotAreaInventoryBase
-{
- typedef cSlotAreaInventoryBase super;
-
-public:
- cSlotAreaInventory(cWindow & a_ParentWindow) :
- cSlotAreaInventoryBase(cInventory::invInventoryCount, cInventory::invInventoryOffset, a_ParentWindow)
- {
- }
-} ;
-
-
-
-
-
-/// Handles the hotbar of each player
-class cSlotAreaHotBar :
- public cSlotAreaInventoryBase
-{
- typedef cSlotAreaInventoryBase super;
-
-public:
- cSlotAreaHotBar(cWindow & a_ParentWindow) :
- cSlotAreaInventoryBase(cInventory::invHotbarCount, cInventory::invHotbarOffset, a_ParentWindow)
- {
- }
-} ;
-
-
-
-
-
-/// Handles the armor area of the player's inventory
-class cSlotAreaArmor :
- public cSlotAreaInventoryBase
-{
-public:
- cSlotAreaArmor(cWindow & a_ParentWindow) :
- cSlotAreaInventoryBase(cInventory::invArmorCount, cInventory::invArmorOffset, a_ParentWindow)
- {
- }
-
- // Distributing the stack is allowed only for compatible items (helmets into helmet slot etc.)
- virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override;
-} ;
-
-
-
-
-
-/// Handles any slot area that is representing a cItemGrid; same items for all the players
-class cSlotAreaItemGrid :
- public cSlotArea,
- public cItemGrid::cListener
-{
- typedef cSlotArea super;
-
-public:
- cSlotAreaItemGrid(cItemGrid & a_ItemGrid, cWindow & a_ParentWindow);
-
- virtual ~cSlotAreaItemGrid();
-
- virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override;
- virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
-
-protected:
- cItemGrid & m_ItemGrid;
-
- // cItemGrid::cListener overrides:
- virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override;
-} ;
-
-
-
-
-
-/** A cSlotArea with items layout that is private to each player and is temporary, such as
-a crafting grid or an enchantment table.
-This common ancestor stores the items in a per-player map. It also implements tossing items from the map.
-*/
-class cSlotAreaTemporary :
- public cSlotArea
-{
- typedef cSlotArea super;
-
-public:
- cSlotAreaTemporary(int a_NumSlots, cWindow & a_ParentWindow);
-
- // cSlotArea overrides:
- virtual const cItem * GetSlot (int a_SlotNum, cPlayer & a_Player) const override;
- virtual void SetSlot (int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
- virtual void OnPlayerAdded (cPlayer & a_Player) override;
- virtual void OnPlayerRemoved(cPlayer & a_Player) override;
-
- /// Tosses the player's items in slots [a_Begin, a_End) (ie. incl. a_Begin, but excl. a_End)
- void TossItems(cPlayer & a_Player, int a_Begin, int a_End);
-
-protected:
- typedef std::map<int, std::vector<cItem> > cItemMap; // Maps EntityID -> items
-
- cItemMap m_Items;
-
- /// Returns the pointer to the slot array for the player specified.
- cItem * GetPlayerSlots(cPlayer & a_Player);
-} ;
-
-
-
-
-
-class cSlotAreaCrafting :
- public cSlotAreaTemporary
-{
- typedef cSlotAreaTemporary super;
-
-public:
- /// a_GridSize is allowed to be only 2 or 3
- cSlotAreaCrafting(int a_GridSize, cWindow & a_ParentWindow);
-
- // cSlotAreaTemporary overrides:
- virtual void Clicked (cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override;
- virtual void OnPlayerRemoved(cPlayer & a_Player) override;
-
- // Distributing items into this area is completely disabled
- virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override {}
-
-protected:
- /// Maps player's EntityID -> current recipe; not a std::map because cCraftingGrid needs proper constructor params
- typedef std::list<std::pair<int, cCraftingRecipe> > cRecipeMap;
-
- int m_GridSize;
- cRecipeMap m_Recipes;
-
- /// Handles a click in the result slot. Crafts using the current recipe, if possible
- void ClickedResult(cPlayer & a_Player);
-
- /// Handles a shift-click in the result slot. Crafts using the current recipe until it changes or no more space for result.
- void ShiftClickedResult(cPlayer & a_Player);
-
- /// Updates the current recipe and result slot based on the ingredients currently in the crafting grid of the specified player
- void UpdateRecipe(cPlayer & a_Player);
-
- /// Retrieves the recipe for the specified player from the map, or creates one if not found
- cCraftingRecipe & GetRecipeForPlayer(cPlayer & a_Player);
-} ;
-
-
-
-
-
-class cSlotAreaChest :
- public cSlotArea
-{
-public:
- cSlotAreaChest(cChestEntity * a_Chest, cWindow & a_ParentWindow);
-
- virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override;
- virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
-
-protected:
- cChestEntity * m_Chest;
-} ;
-
-
-
-
-
-class cSlotAreaDoubleChest :
- public cSlotArea
-{
-public:
- cSlotAreaDoubleChest(cChestEntity * a_TopChest, cChestEntity * a_BottomChest, cWindow & a_ParentWindow);
-
- virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override;
- virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
-
-protected:
- cChestEntity * m_TopChest;
- cChestEntity * m_BottomChest;
-} ;
-
-
-
-
-
-class cSlotAreaFurnace :
- public cSlotArea,
- public cItemGrid::cListener
-{
- typedef cSlotArea super;
-
-public:
- cSlotAreaFurnace(cFurnaceEntity * a_Furnace, cWindow & a_ParentWindow);
-
- virtual ~cSlotAreaFurnace();
-
- virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override;
- virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override;
- virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
-
-protected:
- cFurnaceEntity * m_Furnace;
-
- // cItemGrid::cListener overrides:
- virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override;
-} ;
-
-
-
-
+
+// SlotArea.h
+
+// Interfaces to the cSlotArea class representing a contiguous area of slots in a UI window
+
+
+
+
+#pragma once
+
+#include "../Inventory.h"
+
+
+
+class cWindow;
+class cPlayer;
+class cChestEntity;
+class cDropSpenserEntity;
+class cFurnaceEntity;
+class cCraftingRecipe;
+
+
+
+
+
+class cSlotArea
+{
+public:
+ cSlotArea(int a_NumSlots, cWindow & a_ParentWindow);
+ virtual ~cSlotArea() {} // force a virtual destructor in all subclasses
+
+ int GetNumSlots(void) const { return m_NumSlots; }
+
+ /// Called to retrieve an item in the specified slot for the specified player. Must return a valid cItem.
+ virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const = 0;
+
+ /// Called to set an item in the specified slot for the specified player
+ virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) = 0;
+
+ /// Called when a player clicks in the window. Parameters taken from the click packet.
+ virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem);
+
+ /// Called from Clicked if it is a valid shiftclick
+ virtual void ShiftClicked(cPlayer & a_Player, int a_SlotNum, const cItem & a_ClickedItem);
+
+ /// Called when a new player opens the same parent window. The window already tracks the player. CS-locked.
+ virtual void OnPlayerAdded(cPlayer & a_Player) {} ;
+
+ /// Called when one of the players closes the parent window. The window already doesn't track the player. CS-locked.
+ virtual void OnPlayerRemoved(cPlayer & a_Player) {} ;
+
+ /** Called to store as much of a_ItemStack in the area as possible. a_ItemStack is modified to reflect the change.
+ The default implementation searches each slot for available space and distributes the stack there.
+ if a_ShouldApply is true, the changes are written into the slots;
+ if a_ShouldApply is false, only a_ItemStack is modified to reflect the number of fits (for fit-testing purposes)
+ If a_KeepEmptySlots is true, empty slots will be skipped and won't be filled
+ */
+ virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots);
+
+protected:
+ int m_NumSlots;
+ cWindow & m_ParentWindow;
+} ;
+
+
+
+
+
+/// Handles any part of the inventory, using parameters in constructor to distinguish between the parts
+class cSlotAreaInventoryBase :
+ public cSlotArea
+{
+ typedef cSlotArea super;
+
+public:
+ cSlotAreaInventoryBase(int a_NumSlots, int a_SlotOffset, cWindow & a_ParentWindow);
+
+ // Creative inventory's click handling is somewhat different from survival inventory's, handle that here:
+ virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override;
+
+ virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override;
+ virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
+
+protected:
+ int m_SlotOffset; // Index that this area's slot 0 has in the underlying cInventory
+} ;
+
+
+
+
+
+/// Handles the main inventory of each player, excluding the armor and hotbar
+class cSlotAreaInventory :
+ public cSlotAreaInventoryBase
+{
+ typedef cSlotAreaInventoryBase super;
+
+public:
+ cSlotAreaInventory(cWindow & a_ParentWindow) :
+ cSlotAreaInventoryBase(cInventory::invInventoryCount, cInventory::invInventoryOffset, a_ParentWindow)
+ {
+ }
+} ;
+
+
+
+
+
+/// Handles the hotbar of each player
+class cSlotAreaHotBar :
+ public cSlotAreaInventoryBase
+{
+ typedef cSlotAreaInventoryBase super;
+
+public:
+ cSlotAreaHotBar(cWindow & a_ParentWindow) :
+ cSlotAreaInventoryBase(cInventory::invHotbarCount, cInventory::invHotbarOffset, a_ParentWindow)
+ {
+ }
+} ;
+
+
+
+
+
+/// Handles the armor area of the player's inventory
+class cSlotAreaArmor :
+ public cSlotAreaInventoryBase
+{
+public:
+ cSlotAreaArmor(cWindow & a_ParentWindow) :
+ cSlotAreaInventoryBase(cInventory::invArmorCount, cInventory::invArmorOffset, a_ParentWindow)
+ {
+ }
+
+ // Distributing the stack is allowed only for compatible items (helmets into helmet slot etc.)
+ virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override;
+} ;
+
+
+
+
+
+/// Handles any slot area that is representing a cItemGrid; same items for all the players
+class cSlotAreaItemGrid :
+ public cSlotArea,
+ public cItemGrid::cListener
+{
+ typedef cSlotArea super;
+
+public:
+ cSlotAreaItemGrid(cItemGrid & a_ItemGrid, cWindow & a_ParentWindow);
+
+ virtual ~cSlotAreaItemGrid();
+
+ virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override;
+ virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
+
+protected:
+ cItemGrid & m_ItemGrid;
+
+ // cItemGrid::cListener overrides:
+ virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override;
+} ;
+
+
+
+
+
+/** A cSlotArea with items layout that is private to each player and is temporary, such as
+a crafting grid or an enchantment table.
+This common ancestor stores the items in a per-player map. It also implements tossing items from the map.
+*/
+class cSlotAreaTemporary :
+ public cSlotArea
+{
+ typedef cSlotArea super;
+
+public:
+ cSlotAreaTemporary(int a_NumSlots, cWindow & a_ParentWindow);
+
+ // cSlotArea overrides:
+ virtual const cItem * GetSlot (int a_SlotNum, cPlayer & a_Player) const override;
+ virtual void SetSlot (int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
+ virtual void OnPlayerAdded (cPlayer & a_Player) override;
+ virtual void OnPlayerRemoved(cPlayer & a_Player) override;
+
+ /// Tosses the player's items in slots [a_Begin, a_End) (ie. incl. a_Begin, but excl. a_End)
+ void TossItems(cPlayer & a_Player, int a_Begin, int a_End);
+
+protected:
+ typedef std::map<int, std::vector<cItem> > cItemMap; // Maps EntityID -> items
+
+ cItemMap m_Items;
+
+ /// Returns the pointer to the slot array for the player specified.
+ cItem * GetPlayerSlots(cPlayer & a_Player);
+} ;
+
+
+
+
+
+class cSlotAreaCrafting :
+ public cSlotAreaTemporary
+{
+ typedef cSlotAreaTemporary super;
+
+public:
+ /// a_GridSize is allowed to be only 2 or 3
+ cSlotAreaCrafting(int a_GridSize, cWindow & a_ParentWindow);
+
+ // cSlotAreaTemporary overrides:
+ virtual void Clicked (cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override;
+ virtual void OnPlayerRemoved(cPlayer & a_Player) override;
+
+ // Distributing items into this area is completely disabled
+ virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override {}
+
+protected:
+ /// Maps player's EntityID -> current recipe; not a std::map because cCraftingGrid needs proper constructor params
+ typedef std::list<std::pair<int, cCraftingRecipe> > cRecipeMap;
+
+ int m_GridSize;
+ cRecipeMap m_Recipes;
+
+ /// Handles a click in the result slot. Crafts using the current recipe, if possible
+ void ClickedResult(cPlayer & a_Player);
+
+ /// Handles a shift-click in the result slot. Crafts using the current recipe until it changes or no more space for result.
+ void ShiftClickedResult(cPlayer & a_Player);
+
+ /// Updates the current recipe and result slot based on the ingredients currently in the crafting grid of the specified player
+ void UpdateRecipe(cPlayer & a_Player);
+
+ /// Retrieves the recipe for the specified player from the map, or creates one if not found
+ cCraftingRecipe & GetRecipeForPlayer(cPlayer & a_Player);
+} ;
+
+
+
+
+
+class cSlotAreaChest :
+ public cSlotArea
+{
+public:
+ cSlotAreaChest(cChestEntity * a_Chest, cWindow & a_ParentWindow);
+
+ virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override;
+ virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
+
+protected:
+ cChestEntity * m_Chest;
+} ;
+
+
+
+
+
+class cSlotAreaDoubleChest :
+ public cSlotArea
+{
+public:
+ cSlotAreaDoubleChest(cChestEntity * a_TopChest, cChestEntity * a_BottomChest, cWindow & a_ParentWindow);
+
+ virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override;
+ virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
+
+protected:
+ cChestEntity * m_TopChest;
+ cChestEntity * m_BottomChest;
+} ;
+
+
+
+
+
+class cSlotAreaFurnace :
+ public cSlotArea,
+ public cItemGrid::cListener
+{
+ typedef cSlotArea super;
+
+public:
+ cSlotAreaFurnace(cFurnaceEntity * a_Furnace, cWindow & a_ParentWindow);
+
+ virtual ~cSlotAreaFurnace();
+
+ virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override;
+ virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override;
+ virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
+
+protected:
+ cFurnaceEntity * m_Furnace;
+
+ // cItemGrid::cListener overrides:
+ virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override;
+} ;
+
+
+
+