summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormadmaxoft@gmail.com <madmaxoft@gmail.com@0a769ca7-a7f5-676a-18bf-c427514a06d6>2012-09-20 15:25:54 +0200
committermadmaxoft@gmail.com <madmaxoft@gmail.com@0a769ca7-a7f5-676a-18bf-c427514a06d6>2012-09-20 15:25:54 +0200
commitbc466f07a454271d4845a7e8c7f0822541c5afbd (patch)
treec8455a2af322bbd847b6b4ea74256b78aa834c4c
parentProtoProxy: moar packets! (can now sustain parsing while connected to vanilla server, most of the times) (diff)
downloadcuberite-bc466f07a454271d4845a7e8c7f0822541c5afbd.tar
cuberite-bc466f07a454271d4845a7e8c7f0822541c5afbd.tar.gz
cuberite-bc466f07a454271d4845a7e8c7f0822541c5afbd.tar.bz2
cuberite-bc466f07a454271d4845a7e8c7f0822541c5afbd.tar.lz
cuberite-bc466f07a454271d4845a7e8c7f0822541c5afbd.tar.xz
cuberite-bc466f07a454271d4845a7e8c7f0822541c5afbd.tar.zst
cuberite-bc466f07a454271d4845a7e8c7f0822541c5afbd.zip
-rw-r--r--VC2008/MCServer.vcproj34
-rw-r--r--source/BlockID.cpp11
-rw-r--r--source/BlockID.h3
-rw-r--r--source/CraftingRecipes.cpp2
-rw-r--r--source/CraftingRecipes.h2
-rw-r--r--source/Protocol125.cpp27
-rw-r--r--source/Protocol125.h4
-rw-r--r--source/UI/SlotArea.cpp569
-rw-r--r--source/UI/SlotArea.h192
-rw-r--r--source/UI/cWindow.cpp385
-rw-r--r--source/UI/cWindow.h172
-rw-r--r--source/UI/cWindowOwner.h (renamed from source/cWindowOwner.h)17
-rw-r--r--source/WSSAnvil.cpp6
-rw-r--r--source/blocks/BlockWorkbench.h15
-rw-r--r--source/cChestEntity.cpp18
-rw-r--r--source/cChestEntity.h12
-rw-r--r--source/cClientHandle.cpp26
-rw-r--r--source/cCraftingWindow.cpp244
-rw-r--r--source/cCraftingWindow.h43
-rw-r--r--source/cCreativeInventory.cpp59
-rw-r--r--source/cCreativeInventory.h22
-rw-r--r--source/cFurnaceEntity.cpp79
-rw-r--r--source/cFurnaceEntity.h6
-rw-r--r--source/cFurnaceWindow.cpp61
-rw-r--r--source/cFurnaceWindow.h37
-rw-r--r--source/cInventory.cpp95
-rw-r--r--source/cInventory.h40
-rw-r--r--source/cPlayer.cpp117
-rw-r--r--source/cPlayer.h20
-rw-r--r--source/cRoot.cpp2
-rw-r--r--source/cServer.cpp1
-rw-r--r--source/cSurvivalInventory.cpp285
-rw-r--r--source/cSurvivalInventory.h48
-rw-r--r--source/cWindow.cpp374
-rw-r--r--source/cWindow.h109
35 files changed, 1573 insertions, 1564 deletions
diff --git a/VC2008/MCServer.vcproj b/VC2008/MCServer.vcproj
index 52ac9b5f3..f2222dd5e 100644
--- a/VC2008/MCServer.vcproj
+++ b/VC2008/MCServer.vcproj
@@ -1040,47 +1040,23 @@
Name="UI"
>
<File
- RelativePath="..\source\cCraftingWindow.cpp"
+ RelativePath="..\source\UI\cWindow.cpp"
>
</File>
<File
- RelativePath="..\source\cCraftingWindow.h"
+ RelativePath="..\source\UI\cWindow.h"
>
</File>
<File
- RelativePath="..\source\cCreativeInventory.cpp"
+ RelativePath="..\source\UI\cWindowOwner.h"
>
</File>
<File
- RelativePath="..\source\cCreativeInventory.h"
+ RelativePath="..\source\UI\SlotArea.cpp"
>
</File>
<File
- RelativePath="..\source\cFurnaceWindow.cpp"
- >
- </File>
- <File
- RelativePath="..\source\cFurnaceWindow.h"
- >
- </File>
- <File
- RelativePath="..\source\cSurvivalInventory.cpp"
- >
- </File>
- <File
- RelativePath="..\source\cSurvivalInventory.h"
- >
- </File>
- <File
- RelativePath="..\source\cWindow.cpp"
- >
- </File>
- <File
- RelativePath="..\source\cWindow.h"
- >
- </File>
- <File
- RelativePath="..\source\cWindowOwner.h"
+ RelativePath="..\source\UI\SlotArea.h"
>
</File>
</Filter>
diff --git a/source/BlockID.cpp b/source/BlockID.cpp
index 3652938c2..9cdc05c58 100644
--- a/source/BlockID.cpp
+++ b/source/BlockID.cpp
@@ -251,6 +251,17 @@ AString ItemTypeToString(short a_ItemType)
+AString ItemToFullString(const cItem & a_Item)
+{
+ AString res;
+ Printf(res, "%s:%d * %d", ItemToString(a_Item).c_str(), a_Item.m_ItemHealth, a_Item.m_ItemCount);
+ return res;
+}
+
+
+
+
+
EMCSBiome StringToBiome(const AString & a_BiomeString)
{
// If it is a number, return it:
diff --git a/source/BlockID.h b/source/BlockID.h
index 5833a6d1d..177f2f389 100644
--- a/source/BlockID.h
+++ b/source/BlockID.h
@@ -637,6 +637,9 @@ extern AString ItemToString(const cItem & a_Item); // tolua_export
/// Translates itemtype into a string. If the type is not recognized, the itemtype number is output into the string.
extern AString ItemTypeToString(short a_ItemType); // tolua_export
+/// Translates a full item into a fully-specified string (including meta and count). If the ItemType is not recognized, the ItemType number is output into the string.
+extern AString ItemToFullString(const cItem & a_Item); // tolua_export
+
/// Translates a biome string to biome enum. Takes either a number or a biome alias (built-in). Returns -1 on failure.
extern EMCSBiome StringToBiome(const AString & a_BiomeString);
diff --git a/source/CraftingRecipes.cpp b/source/CraftingRecipes.cpp
index 38daa1fe8..f75434e75 100644
--- a/source/CraftingRecipes.cpp
+++ b/source/CraftingRecipes.cpp
@@ -26,7 +26,7 @@ cCraftingGrid::cCraftingGrid(int a_Width, int a_Height) :
-cCraftingGrid::cCraftingGrid(cItem * a_Items, int a_Width, int a_Height) :
+cCraftingGrid::cCraftingGrid(const cItem * a_Items, int a_Width, int a_Height) :
m_Width(a_Width),
m_Height(a_Height),
m_Items(new cItem[a_Width * a_Height])
diff --git a/source/CraftingRecipes.h b/source/CraftingRecipes.h
index 25f8fdae2..74d103f4d 100644
--- a/source/CraftingRecipes.h
+++ b/source/CraftingRecipes.h
@@ -26,7 +26,7 @@ class cCraftingGrid // tolua_export
public:
cCraftingGrid(const cCraftingGrid & a_Original);
cCraftingGrid(int a_Width, int a_Height); // tolua_export
- cCraftingGrid(cItem * a_Items, int a_Width, int a_Height);
+ cCraftingGrid(const cItem * a_Items, int a_Width, int a_Height);
~cCraftingGrid();
// tolua_begin
diff --git a/source/Protocol125.cpp b/source/Protocol125.cpp
index adea37824..f9a13668b 100644
--- a/source/Protocol125.cpp
+++ b/source/Protocol125.cpp
@@ -2,6 +2,12 @@
// Protocol125.cpp
// Implements the cProtocol125 class representing the release 1.2.5 protocol (#29)
+/*
+Documentation:
+ - protocol: http://wiki.vg/wiki/index.php?title=Protocol&oldid=2513
+ - session handling: http://wiki.vg/wiki/index.php?title=Session&oldid=2262
+ - slot format: http://wiki.vg/wiki/index.php?title=Slot_Data&oldid=2152
+*/
#include "Globals.h"
@@ -14,7 +20,7 @@
#include "cPickup.h"
#include "cPlayer.h"
#include "cChatColor.h"
-#include "cWindow.h"
+#include "UI/cWindow.h"
#include "cRoot.h"
#include "cServer.h"
@@ -713,7 +719,7 @@ void cProtocol125::SendWeather(eWeather a_Weather)
void cProtocol125::SendWholeInventory(const cInventory & a_Inventory)
{
cCSLock Lock(m_CSPacket);
- SendWholeInventory(0, a_Inventory.c_NumSlots, a_Inventory.GetSlots());
+ SendWindowSlots(0, a_Inventory.c_NumSlots, a_Inventory.GetSlots());
}
@@ -723,11 +729,9 @@ void cProtocol125::SendWholeInventory(const cInventory & a_Inventory)
void cProtocol125::SendWholeInventory(const cWindow & a_Window)
{
cCSLock Lock(m_CSPacket);
- SendWholeInventory(
- (char)a_Window.GetWindowID(),
- a_Window.GetNumSlots(),
- a_Window.GetSlots()
- );
+ cItems Slots;
+ a_Window.GetSlots(*(m_Client->GetPlayer()), Slots);
+ SendWindowSlots(a_Window.GetWindowID(), Slots.size(), &(Slots[0]));
}
@@ -748,6 +752,10 @@ void cProtocol125::SendWindowClose(char a_WindowID)
void cProtocol125::SendWindowOpen(char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots)
{
+ LOGD("Sending a WindowOpen packet: ID = %d, Type = %d, Title = \"%s\", NumSlots = %d",
+ a_WindowID, a_WindowType, a_WindowTitle.c_str(), a_NumSlots
+ );
+
if (a_WindowType < 0)
{
// Do not send for inventory windows
@@ -1246,8 +1254,11 @@ void cProtocol125::SendPreChunk(int a_ChunkX, int a_ChunkZ, bool a_ShouldLoad)
-void cProtocol125::SendWholeInventory(char a_WindowID, int a_NumItems, const cItem * a_Items)
+void cProtocol125::SendWindowSlots(char a_WindowID, int a_NumItems, const cItem * a_Items)
{
+ LOGD("Sending a InventoryWhole packet: WindowID = %d, NumItems = %d",
+ a_WindowID, a_NumItems
+ );
WriteByte (PACKET_INVENTORY_WHOLE);
WriteByte (a_WindowID);
WriteShort((short)a_NumItems);
diff --git a/source/Protocol125.h b/source/Protocol125.h
index 41bb8deb5..daa2a8c55 100644
--- a/source/Protocol125.h
+++ b/source/Protocol125.h
@@ -119,8 +119,8 @@ protected:
/// Writes a "pre-chunk" packet
void SendPreChunk(int a_ChunkX, int a_ChunkZ, bool a_ShouldLoad);
- /// Writes a "whole inventory" packet with the specified params
- void SendWholeInventory(char a_WindowID, int a_NumItems, const cItem * a_Items);
+ /// Writes a "set window items" packet with the specified params
+ void SendWindowSlots(char a_WindowID, int a_NumItems, const cItem * a_Items);
/// Writes one item, "slot" as the protocol wiki calls it
virtual void WriteItem(const cItem & a_Item);
diff --git a/source/UI/SlotArea.cpp b/source/UI/SlotArea.cpp
new file mode 100644
index 000000000..744492bff
--- /dev/null
+++ b/source/UI/SlotArea.cpp
@@ -0,0 +1,569 @@
+
+// SlotArea.cpp
+
+// Implements the cSlotArea class and its descendants
+
+#include "Globals.h"
+#include "SlotArea.h"
+#include "../cPlayer.h"
+#include "../cChestEntity.h"
+#include "../cFurnaceEntity.h"
+#include "../Items/Item.h"
+#include "cWindow.h"
+#include "../CraftingRecipes.h"
+#include "../cRoot.h"
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSlotArea:
+
+cSlotArea::cSlotArea(int a_NumSlots, cWindow & a_ParentWindow) :
+ m_NumSlots(a_NumSlots),
+ m_ParentWindow(a_ParentWindow)
+{
+ LOGD("Created a new cSlotArea with %d slots", a_NumSlots);
+}
+
+
+
+
+
+void cSlotArea::Clicked(cPlayer & a_Player, int a_SlotNum, bool a_IsRightClick, bool a_IsShiftPressed, 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;
+ }
+
+ cItem Slot(*GetSlot(a_SlotNum, a_Player));
+ if (!Slot.IsEqual(a_ClickedItem))
+ {
+ LOGD("*** Window lost sync ***");
+ LOGD("My Type: %i Their Type: %i", Slot.m_ItemID, a_ClickedItem.m_ItemID);
+ LOGD("My Count: %i Their Count: %i", Slot.m_ItemCount, a_ClickedItem.m_ItemCount);
+ LOGD("My Dmg: %i Their Dmg: %i", Slot.m_ItemHealth, a_ClickedItem.m_ItemHealth);
+ bAsync = true;
+ }
+ cItem & DraggingItem = a_Player.GetDraggingItem();
+ if (a_IsRightClick)
+ {
+ // Right clicked
+ if (DraggingItem.m_ItemID <= 0) // Empty-handed?
+ {
+ DraggingItem.m_ItemCount = (char)(((float)Slot.m_ItemCount) / 2.f + 0.5f);
+ Slot.m_ItemCount -= DraggingItem.m_ItemCount;
+ DraggingItem.m_ItemID = Slot.m_ItemID;
+ DraggingItem.m_ItemHealth = Slot.m_ItemHealth;
+
+ if (Slot.m_ItemCount <= 0)
+ {
+ Slot.Empty();
+ }
+ }
+ else if ((Slot.m_ItemID <= 0) || DraggingItem.IsEqual(Slot))
+ {
+ // Drop one item in slot
+ cItemHandler * Handler = ItemHandler(Slot.m_ItemID);
+ if ((DraggingItem.m_ItemCount > 0) && (Slot.m_ItemCount < Handler->GetMaxStackSize()))
+ {
+ Slot.m_ItemID = DraggingItem.m_ItemID;
+ Slot.m_ItemCount++;
+ Slot.m_ItemHealth = DraggingItem.m_ItemHealth;
+ DraggingItem.m_ItemCount--;
+ }
+ if (DraggingItem.m_ItemCount <= 0)
+ {
+ DraggingItem.Empty();
+ }
+ }
+ else if (!DraggingItem.IsEqual(Slot))
+ {
+ // Swap contents
+ cItem tmp(DraggingItem);
+ DraggingItem = Slot;
+ Slot = tmp;
+ }
+ }
+ else
+ {
+ // 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_ItemID);
+ 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();
+ }
+ }
+ }
+
+ if (bAsync)
+ {
+ m_ParentWindow.BroadcastWholeWindow();
+ }
+ SetSlot(a_SlotNum, a_Player, Slot);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSlotAreaArmor:
+
+cSlotAreaArmor::cSlotAreaArmor(cWindow & a_ParentWindow) :
+ cSlotArea(4, a_ParentWindow)
+{
+}
+
+
+
+
+
+const cItem * cSlotAreaArmor::GetSlot(int a_SlotNum, cPlayer & a_Player)
+{
+ // a_SlotNum ranges from 0 to 3, map that to the armor slots in player's inventory, 5 to 8:
+ return a_Player.GetInventory().GetSlot(a_SlotNum + 5);
+}
+
+
+
+
+
+void cSlotAreaArmor::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
+{
+ *(a_Player.GetInventory().GetSlot(a_SlotNum + 5)) = a_Item;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 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)
+{
+ // 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);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 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, bool a_IsRightClick, bool a_IsShiftPressed, const cItem & a_ClickedItem)
+{
+ // Override for craft result slot
+ if (a_SlotNum == 0)
+ {
+ ClickedResult(a_Player, a_IsShiftPressed);
+ return;
+ }
+ super::Clicked(a_Player, a_SlotNum, a_IsRightClick, a_IsShiftPressed, 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, bool a_IsShiftPressed)
+{
+ const cItem * ResultSlot = GetSlot(0, a_Player);
+ LOGD("Clicked in craft result slot, item there: %d:%d (%d times)",
+ ResultSlot->m_ItemID, ResultSlot->m_ItemHealth, ResultSlot->m_ItemCount
+ );
+ 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_ItemID);
+ 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::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);
+}
+
+
+
+
+
+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)
+{
+}
+
+
+
+
+
+void cSlotAreaFurnace::Clicked(cPlayer & a_Player, int a_SlotNum, bool a_IsRightClick, bool a_IsShiftPressed, const cItem & a_ClickedItem)
+{
+ cItem Fuel = *GetSlot(0, a_Player);
+
+ super::Clicked(a_Player, a_SlotNum, a_IsRightClick, a_IsShiftPressed, a_ClickedItem);
+
+ if (m_Furnace == NULL)
+ {
+ LOGERROR("cSlotAreaFurnace::Clicked(): m_Furnace == NULL");
+ ASSERT(!"cSlotAreaFurnace::Clicked(): m_Furnace == NULL");
+ return;
+ }
+
+ if (Fuel.m_ItemID != GetSlot(0, a_Player)->m_ItemID)
+ {
+ m_Furnace->ResetCookTimer();
+ }
+
+ if (m_Furnace->StartCooking())
+ {
+ m_ParentWindow.SendWholeWindow(*(a_Player.GetClientHandle()));
+ }
+}
+
+
+
+
+
+const cItem * cSlotAreaFurnace::GetSlot(int a_SlotNum, cPlayer & a_Player)
+{
+ // 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);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSlotAreaInventory:
+
+cSlotAreaInventory::cSlotAreaInventory(cWindow & a_ParentWindow) :
+ cSlotArea(27 + 9, a_ParentWindow) // 27 internal slots, 9 hotbar slots
+{
+}
+
+
+
+
+
+void cSlotAreaInventory::Clicked(cPlayer & a_Player, int a_SlotNum, bool a_IsRightClick, bool a_IsShiftPressed, 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_IsRightClick, a_IsShiftPressed, a_ClickedItem);
+ return;
+}
+
+
+
+
+
+const cItem * cSlotAreaInventory::GetSlot(int a_SlotNum, cPlayer & a_Player)
+{
+ // a_SlotNum ranges from 0 to 35, map that to the player's inventory slots 9 to 44
+ return a_Player.GetInventory().GetSlot(a_SlotNum + 9);
+}
+
+
+
+
+
+void cSlotAreaInventory::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
+{
+ *(a_Player.GetInventory().GetSlot(a_SlotNum + 9)) = a_Item;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSlotAreaTemporary:
+
+cSlotAreaTemporary::cSlotAreaTemporary(int a_NumSlots, cWindow & a_ParentWindow) :
+ cSlotArea(a_NumSlots, a_ParentWindow)
+{
+}
+
+
+
+
+
+const cItem * cSlotAreaTemporary::GetSlot(int a_SlotNum, cPlayer & a_Player)
+{
+ cItemMap::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;
+ }
+
+ LOGD("cSlotAreaTemporary: getting slot %d as %s", a_SlotNum, ItemToFullString(itr->second[a_SlotNum]).c_str());
+
+ return &(itr->second[a_SlotNum]);
+}
+
+
+
+
+
+void cSlotAreaTemporary::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
+{
+ LOGD("cSlotAreaTemporary: setting slot %d to %s", a_SlotNum, ItemToFullString(a_Item).c_str());
+
+ 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[]
+
+ float 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
new file mode 100644
index 000000000..0e620cd54
--- /dev/null
+++ b/source/UI/SlotArea.h
@@ -0,0 +1,192 @@
+
+// SlotArea.h
+
+// Interfaces to the cSlotArea class representing a contiguous area of slots in a UI window
+
+
+
+
+#pragma once
+
+#include "../cItem.h"
+
+
+
+class cWindow;
+class cPlayer;
+class cChestEntity;
+class cFurnaceEntity;
+class cCraftingRecipe;
+
+
+
+
+
+class cSlotArea
+{
+public:
+ cSlotArea(int a_NumSlots, cWindow & a_ParentWindow);
+
+ 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) = 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, bool a_IsRightClick, bool a_IsShiftPressed, 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) {} ;
+
+protected:
+ int m_NumSlots;
+ cWindow & m_ParentWindow;
+} ;
+
+
+
+
+
+class cSlotAreaInventory :
+ public cSlotArea
+{
+ typedef cSlotArea super;
+
+public:
+ cSlotAreaInventory(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, bool a_IsRightClick, bool a_IsShiftPressed, const cItem & a_ClickedItem) override;
+
+ virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) override;
+ virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) 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) 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, bool a_IsRightClick, bool a_IsShiftPressed, const cItem & a_ClickedItem) override;
+ virtual void OnPlayerRemoved(cPlayer & a_Player) 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, bool a_IsShiftPressed);
+
+ /// 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) override;
+ virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
+
+protected:
+ cChestEntity * m_Chest;
+} ;
+
+
+
+
+
+class cSlotAreaFurnace :
+ public cSlotArea
+{
+ typedef cSlotArea super;
+
+public:
+ cSlotAreaFurnace(cFurnaceEntity * a_Furnace, cWindow & a_ParentWindow);
+
+ virtual void Clicked(cPlayer & a_Player, int a_SlotNum, bool a_IsRightClick, bool a_IsShiftPressed, const cItem & a_ClickedItem) override;
+ virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) override;
+ virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
+
+protected:
+ cFurnaceEntity * m_Furnace;
+} ;
+
+
+
+
+
+class cSlotAreaArmor :
+ public cSlotArea
+{
+public:
+ cSlotAreaArmor(cWindow & a_ParentWindow);
+
+ virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) override;
+ virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
+} ;
+
+
+
+
diff --git a/source/UI/cWindow.cpp b/source/UI/cWindow.cpp
new file mode 100644
index 000000000..475670425
--- /dev/null
+++ b/source/UI/cWindow.cpp
@@ -0,0 +1,385 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "cWindow.h"
+#include "../cItem.h"
+#include "../cClientHandle.h"
+#include "../cPlayer.h"
+#include "../cPickup.h"
+#include "../cInventory.h"
+#include "cWindowOwner.h"
+#include "../items/Item.h"
+#include "SlotArea.h"
+#include "../cChestEntity.h"
+
+
+
+
+
+char cWindow::m_WindowIDCounter = 1;
+
+
+
+
+
+cWindow::cWindow(cWindow::WindowType a_WindowType, const AString & a_WindowTitle)
+ : m_WindowID(1 + (m_WindowIDCounter++ % 127))
+ , m_WindowType(a_WindowType)
+ , m_WindowTitle(a_WindowTitle)
+ , m_Owner(NULL)
+ , m_IsDestroyed(false)
+{
+ if (a_WindowType == Inventory)
+ {
+ m_WindowID = 0;
+ }
+ LOGD("Created a window at %p, type = %d, ID = %i, title = \"%s\".",
+ this, m_WindowType, m_WindowID, m_WindowTitle.c_str()
+ );
+}
+
+
+
+
+
+cWindow::~cWindow()
+{
+ LOGD("Deleted a window at %p", this);
+}
+
+
+
+
+
+int cWindow::GetNumSlots(void) const
+{
+ int res = 0;
+ for (cSlotAreas::const_iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr)
+ {
+ res += (*itr)->GetNumSlots();
+ } // for itr - m_SlotAreas[]
+ return res;
+}
+
+
+
+
+
+void cWindow::GetSlots(cPlayer & a_Player, cItems & a_Slots) const
+{
+ a_Slots.clear();
+ a_Slots.reserve(GetNumSlots());
+ for (cSlotAreas::const_iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr)
+ {
+ int NumSlots = (*itr)->GetNumSlots();
+ for (int i = 0; i < NumSlots; i++)
+ {
+
+ const cItem * Item = (*itr)->GetSlot(i, a_Player);
+ if (Item == NULL)
+ {
+ a_Slots.push_back(cItem());
+ }
+ else
+ {
+ a_Slots.push_back(*Item);
+ }
+ }
+ } // for itr - m_SlotAreas[]
+}
+
+
+
+
+
+void cWindow::Clicked(
+ cPlayer & a_Player,
+ int a_WindowID, short a_SlotNum, bool a_IsRightClick, bool a_IsShiftPressed,
+ const cItem & a_ClickedItem
+)
+{
+ LOGD("cWindow::Clicked(): ID %d (exp %d), SlotNum %d", a_WindowID, m_WindowID, a_SlotNum);
+
+ if (a_WindowID != m_WindowID)
+ {
+ LOG("WRONG WINDOW ID! (exp %d, got %d) received from \"%s\"", m_WindowID, a_WindowID, a_Player.GetName().c_str());
+ return;
+ }
+
+ if (a_SlotNum == -999) // Outside window click
+ {
+ if (a_IsRightClick)
+ {
+ a_Player.TossItem(true);
+ }
+ else
+ {
+ a_Player.TossItem(true, a_Player.GetDraggingItem().m_ItemCount);
+ }
+ return;
+ }
+
+ int LocalSlotNum = a_SlotNum;
+ int idx = 0;
+ for (cSlotAreas::iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr)
+ {
+ if (LocalSlotNum < (*itr)->GetNumSlots())
+ {
+ LOGD("SlotArea #%d (%d slots) handling the click", idx, (*itr)->GetNumSlots());
+ (*itr)->Clicked(a_Player, LocalSlotNum, a_IsRightClick, a_IsShiftPressed, a_ClickedItem);
+ return;
+ }
+ LocalSlotNum -= (*itr)->GetNumSlots();
+ idx++;
+ }
+
+ LOGWARNING("Slot number higher than available window slots: %d, max %d received from \"%s\"; ignoring.",
+ a_SlotNum, GetNumSlots(), a_Player.GetName().c_str()
+ );
+}
+
+
+
+
+
+void cWindow::OpenedByPlayer(cPlayer & a_Player)
+{
+ {
+ cCSLock Lock(m_CS);
+ // If player is already in OpenedBy remove player first
+ m_OpenedBy.remove(&a_Player);
+ // Then add player
+ m_OpenedBy.push_back(&a_Player);
+
+ for (cSlotAreas::iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr)
+ {
+ (*itr)->OnPlayerAdded(a_Player);
+ } // for itr - m_SlotAreas[]
+ }
+
+ // TODO: Notify all areas that a new player has opened the window
+
+ a_Player.GetClientHandle()->SendWindowOpen(m_WindowID, m_WindowType, m_WindowTitle, GetNumSlots() - c_NumInventorySlots);
+}
+
+
+
+
+
+void cWindow::ClosedByPlayer(cPlayer & a_Player)
+{
+ ASSERT(m_WindowType != Inventory); // Inventory windows must not be closed (the client would repeat the close packet, looping forever)
+
+ // Checks whether the player is still holding an item
+ if (a_Player.IsDraggingItem())
+ {
+ LOGD("Player holds item! Dropping it...");
+ a_Player.TossItem(true, a_Player.GetDraggingItem().m_ItemCount);
+ }
+
+ cClientHandle * ClientHandle = a_Player.GetClientHandle();
+ if (ClientHandle != NULL)
+ {
+ ClientHandle->SendWindowClose(m_WindowID);
+ }
+
+ {
+ cCSLock Lock(m_CS);
+
+ for (cSlotAreas::iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr)
+ {
+ (*itr)->OnPlayerRemoved(a_Player);
+ } // for itr - m_SlotAreas[]
+
+ m_OpenedBy.remove(&a_Player);
+ if (m_OpenedBy.empty())
+ {
+ Destroy();
+ }
+ }
+ if (m_IsDestroyed)
+ {
+ delete this;
+ }
+}
+
+
+
+
+
+void cWindow::OwnerDestroyed()
+{
+ m_Owner = NULL;
+ // Close window for each player. Note that the last one needs special handling
+ while (m_OpenedBy.size() > 1)
+ {
+ (*m_OpenedBy.begin() )->CloseWindow((char)GetWindowType());
+ }
+ (*m_OpenedBy.begin() )->CloseWindow((char)GetWindowType());
+}
+
+
+
+
+
+bool cWindow::ForEachPlayer(cItemCallback<cPlayer> & a_Callback)
+{
+ cCSLock Lock(m_CS);
+ for (cPlayerList::iterator itr = m_OpenedBy.begin(), end = m_OpenedBy.end(); itr != end; ++itr)
+ {
+ if (a_Callback.Item(*itr))
+ {
+ return false;
+ }
+ } // for itr - m_OpenedBy[]
+ return true;
+}
+
+
+
+
+
+bool cWindow::ForEachClient(cItemCallback<cClientHandle> & a_Callback)
+{
+ cCSLock Lock(m_CS);
+ for (cPlayerList::iterator itr = m_OpenedBy.begin(), end = m_OpenedBy.end(); itr != end; ++itr)
+ {
+ if (a_Callback.Item((*itr)->GetClientHandle()))
+ {
+ return false;
+ }
+ } // for itr - m_OpenedBy[]
+ return true;
+}
+
+
+
+
+
+void cWindow::Destroy()
+{
+ LOGD("Destroying window %p (type %d)", this, m_WindowType);
+ if (m_Owner != NULL)
+ {
+ m_Owner->CloseWindow();
+ m_Owner = NULL;
+ }
+ m_IsDestroyed = true;
+}
+
+
+
+
+
+void cWindow::SendWholeWindow(cClientHandle & a_Client)
+{
+ a_Client.SendWholeInventory(*this);
+}
+
+
+
+
+
+void cWindow::BroadcastWholeWindow(void)
+{
+ cCSLock Lock(m_CS);
+ for (cPlayerList::iterator itr = m_OpenedBy.begin(); itr != m_OpenedBy.end(); ++itr)
+ {
+ SendWholeWindow(*(*itr)->GetClientHandle());
+ } // for itr - m_OpenedBy[]
+}
+
+
+
+
+
+void cWindow::BroadcastInventoryProgress(short a_Progressbar, short a_Value)
+{
+ cCSLock Lock(m_CS);
+ for (cPlayerList::iterator itr = m_OpenedBy.begin(); itr != m_OpenedBy.end(); ++itr)
+ {
+ (*itr)->GetClientHandle()->SendInventoryProgress(m_WindowID, a_Progressbar, a_Value);
+ } // for itr - m_OpenedBy[]
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cInventoryWindow:
+
+cInventoryWindow::cInventoryWindow(cPlayer & a_Player) :
+ cWindow(cWindow::Inventory, "MCS-Inventory"),
+ m_Player(a_Player)
+{
+ m_SlotAreas.push_back(new cSlotAreaCrafting(2, *this)); // The creative inventory doesn't display it, but it's still counted into slot numbers
+ m_SlotAreas.push_back(new cSlotAreaArmor(*this));
+ m_SlotAreas.push_back(new cSlotAreaInventory(*this));
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cCraftingWindow:
+
+cCraftingWindow::cCraftingWindow(int a_BlockX, int a_BlockY, int a_BlockZ) :
+ cWindow(cWindow::Workbench, "MCS-Workbench")
+{
+ m_SlotAreas.push_back(new cSlotAreaCrafting(3, *this));
+ m_SlotAreas.push_back(new cSlotAreaInventory(*this));
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cChestWindow:
+
+cChestWindow::cChestWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cChestEntity * a_Chest) :
+ cWindow(cWindow::Chest, "MCS-Chest"),
+ m_World(a_Chest->GetWorld()),
+ m_BlockX(a_BlockX),
+ m_BlockY(a_BlockY),
+ m_BlockZ(a_BlockZ)
+{
+ m_SlotAreas.push_back(new cSlotAreaChest(a_Chest, *this));
+
+ // TODO: Double chests
+
+ m_SlotAreas.push_back(new cSlotAreaInventory(*this));
+
+ // Send out the chest-open packet:
+ m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 1, E_BLOCK_CHEST);
+}
+
+
+
+
+
+cChestWindow::~cChestWindow()
+{
+ // Send out the chest-close packet:
+ m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 0, E_BLOCK_CHEST);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cFurnaceWindow:
+
+cFurnaceWindow::cFurnaceWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceEntity * a_Furnace) :
+ cWindow(cWindow::Furnace, "MCS-Furnace")
+{
+ m_SlotAreas.push_back(new cSlotAreaFurnace(a_Furnace, *this));
+ m_SlotAreas.push_back(new cSlotAreaInventory(*this));
+}
+
+
+
+
diff --git a/source/UI/cWindow.h b/source/UI/cWindow.h
new file mode 100644
index 000000000..231e4fdc0
--- /dev/null
+++ b/source/UI/cWindow.h
@@ -0,0 +1,172 @@
+
+// cWindow.h
+
+// Interfaces to the cWindow class representing a UI window for a specific block
+
+
+
+
+
+#pragma once
+
+#include "../cItem.h"
+
+
+
+
+
+class cPlayer;
+class cWindowOwner;
+class cClientHandle;
+class cChestEntity;
+class cFurnaceEntity;
+class cSlotArea;
+class cWorld;
+
+typedef std::list<cPlayer *> cPlayerList;
+typedef std::vector<cSlotArea *> cSlotAreas;
+
+
+
+
+
+/**
+Represents a UI window.
+
+Each window has a list of players that are currently using it
+When there's no player using a window, it is destroyed.
+A window consists of several areas of slots with similar functionality - for example the crafting grid area, or
+the inventory area. Each area knows what its slots are (GetSlot() function) and can handle mouse clicks.
+The window acts only as a top-level container for those areas, redirecting the click events to the correct areas.
+*/
+class cWindow
+{
+public:
+ enum WindowType
+ {
+ Inventory = -1, // This value is never actually sent to a client
+ Chest = 0,
+ Workbench = 1,
+ Furnace = 2,
+ Dispenser = 3,
+ Enchantment = 4,
+ Brewery = 5
+ };
+
+ static const int c_NumInventorySlots = 36;
+
+ cWindow(WindowType a_WindowType, const AString & a_WindowTitle);
+ virtual ~cWindow();
+
+ int GetWindowID(void) const { return m_WindowID; }
+ int GetWindowType(void) const { return m_WindowType; }
+
+ cWindowOwner * GetOwner() { return m_Owner; }
+ void SetOwner( cWindowOwner * a_Owner ) { m_Owner = a_Owner; }
+
+ int GetNumSlots(void) const;
+
+ /// Fills a_Slots with the slots read from m_SlotAreas[], for the specified player
+ void GetSlots(cPlayer & a_Player, cItems & a_Slots) const;
+
+ void Clicked(
+ cPlayer & a_Player, int a_WindowID,
+ short a_SlotNum, bool a_IsRightClick, bool a_IsShiftPressed,
+ const cItem & a_ClickedItem
+ );
+
+ void OpenedByPlayer(cPlayer & a_Player);
+ void ClosedByPlayer(cPlayer & a_Player);
+
+ void SendWholeWindow(cClientHandle & a_Client);
+ void BroadcastWholeWindow(void);
+ void BroadcastInventoryProgress(short a_Progressbar, short a_Value);
+
+ const AString & GetWindowTitle() const { return m_WindowTitle; }
+ void SetWindowTitle(const AString & a_WindowTitle ) { m_WindowTitle = a_WindowTitle; }
+
+ void OwnerDestroyed(void);
+
+ /// Calls the callback safely for each player that has this window open; returns true if all players have been enumerated
+ bool ForEachPlayer(cItemCallback<cPlayer> & a_Callback);
+
+ /// Calls the callback safely for each client that has this window open; returns true if all clients have been enumerated
+ bool ForEachClient(cItemCallback<cClientHandle> & a_Callback);
+
+protected:
+ cSlotAreas m_SlotAreas;
+
+private:
+ char m_WindowID;
+ int m_WindowType;
+ AString m_WindowTitle;
+
+ cCriticalSection m_CS;
+ cPlayerList m_OpenedBy;
+
+ bool m_IsDestroyed;
+
+ cWindowOwner * m_Owner;
+
+ static char m_WindowIDCounter;
+
+ void Destroy(void);
+} ;
+
+
+
+
+
+class cCraftingWindow :
+ public cWindow
+{
+public:
+ cCraftingWindow(int a_BlockX, int a_BlockY, int a_BlockZ);
+} ;
+
+
+
+
+
+class cFurnaceWindow :
+ public cWindow
+{
+public:
+ cFurnaceWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceEntity * a_Furnace);
+} ;
+
+
+
+
+
+class cChestWindow :
+ public cWindow
+{
+public:
+ cChestWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cChestEntity * a_Chest);
+ ~cChestWindow();
+
+protected:
+ cWorld * m_World;
+ int m_BlockX, m_BlockY, m_BlockZ; // Position of the chest, for the window-close packet
+} ;
+
+
+
+
+
+class cInventoryWindow :
+ public cWindow
+{
+public:
+ cInventoryWindow(cPlayer & a_Player);
+
+protected:
+ cPlayer & m_Player;
+
+} ;
+
+
+
+
+
diff --git a/source/cWindowOwner.h b/source/UI/cWindowOwner.h
index 6c9a15a45..1ce03ed30 100644
--- a/source/cWindowOwner.h
+++ b/source/UI/cWindowOwner.h
@@ -1,21 +1,29 @@
#pragma once
-#include "cBlockEntity.h"
-#include "cEntity.h"
+#include "../cBlockEntity.h"
+#include "../cEntity.h"
+#include "cWindow.h"
+
+/*
+Being a descendant of cWindowOwner means that the class can own one window. That window can be
+queried, opened by other players, closed by players and finally destroyed.
+Also, a cWindowOwner can be queried for the block coords where the window is displayed. That will be used
+for entities / players in motion to close their windows when they get too far away from the window "source".
+*/
-class cWindow;
+// class cWindow;
/**
-Base class for the behavior expected from a class that can handle UI windows for block entities.
+Base class for the window owning
*/
class cWindowOwner
{
@@ -33,6 +41,7 @@ public:
void OpenWindow(cWindow * a_Window)
{
m_Window = a_Window;
+ m_Window->SetOwner(this);
}
cWindow * GetWindow(void) const
diff --git a/source/WSSAnvil.cpp b/source/WSSAnvil.cpp
index 44b5f7faa..cacd81df9 100644
--- a/source/WSSAnvil.cpp
+++ b/source/WSSAnvil.cpp
@@ -127,9 +127,9 @@ protected:
m_Writer.BeginCompound("");
AddBasicTileEntity(a_Furnace, "Furnace");
m_Writer.BeginList("Items", TAG_Compound);
- AddItem(&a_Furnace->GetSlot(0), 0);
- AddItem(&a_Furnace->GetSlot(1), 1);
- AddItem(&a_Furnace->GetSlot(2), 2);
+ AddItem(a_Furnace->GetSlot(0), 0);
+ AddItem(a_Furnace->GetSlot(1), 1);
+ AddItem(a_Furnace->GetSlot(2), 2);
m_Writer.EndList();
m_Writer.AddShort("BurnTime", (Int16)(a_Furnace->GetTimeToBurn() / 50.0));
m_Writer.AddShort("CookTime", (Int16)(a_Furnace->GetTimeCooked() / 50.0));
diff --git a/source/blocks/BlockWorkbench.h b/source/blocks/BlockWorkbench.h
index 6c5f0d3e8..998e07d9b 100644
--- a/source/blocks/BlockWorkbench.h
+++ b/source/blocks/BlockWorkbench.h
@@ -1,9 +1,14 @@
#pragma once
#include "Block.h"
-#include "../cCraftingWindow.h"
+#include "../UI/cWindow.h"
#include "../cPlayer.h"
-class cBlockWorkbenchHandler : public cBlockHandler
+
+
+
+
+class cBlockWorkbenchHandler:
+ public cBlockHandler
{
public:
cBlockWorkbenchHandler(BLOCKTYPE a_BlockID)
@@ -11,9 +16,9 @@ public:
{
}
- virtual void OnUse(cWorld *a_World, cPlayer *a_Player, int a_X, int a_Y, int a_Z) override
+ virtual void OnUse(cWorld * a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) override
{
- cWindow* Window = new cCraftingWindow(0, true);
+ cWindow * Window = new cCraftingWindow(a_BlockX, a_BlockY, a_BlockZ);
a_Player->OpenWindow(Window);
}
@@ -28,4 +33,4 @@ public:
}
-};
+}; \ No newline at end of file
diff --git a/source/cChestEntity.cpp b/source/cChestEntity.cpp
index 2a69c7bdb..8914f5f41 100644
--- a/source/cChestEntity.cpp
+++ b/source/cChestEntity.cpp
@@ -5,7 +5,7 @@
#include "cItem.h"
#include "cClientHandle.h"
#include "cPlayer.h"
-#include "cWindow.h"
+#include "UI/cWindow.h"
#include "cWorld.h"
#include "cRoot.h"
#include "cPickup.h"
@@ -88,9 +88,9 @@ const cItem * cChestEntity::GetSlot( int a_Slot ) const
-void cChestEntity::SetSlot( int a_Slot, cItem & a_Item )
+void cChestEntity::SetSlot(int a_Slot, const cItem & a_Item)
{
- if( a_Slot > -1 && a_Slot < c_ChestHeight*c_ChestWidth )
+ if ((a_Slot > -1) && (a_Slot < c_ChestHeight * c_ChestWidth))
{
m_Content[a_Slot] = a_Item;
}
@@ -170,22 +170,18 @@ void cChestEntity::SendTo(cClientHandle & a_Client)
void cChestEntity::UsedBy(cPlayer * a_Player)
{
- if (!GetWindow())
+ if (GetWindow() == NULL)
{
- cWindow * Window = new cWindow(this, true, cWindow::Chest, 1);
- Window->SetSlots(GetContents(), GetChestHeight() * c_ChestWidth);
- Window->SetWindowTitle("UberChest");
- OpenWindow( Window );
+ OpenWindow(new cChestWindow(m_PosX, m_PosY, m_PosZ, this));
}
- if ( GetWindow() )
+ if (GetWindow())
{
if( a_Player->GetWindow() != GetWindow() )
{
a_Player->OpenWindow( GetWindow() );
- GetWindow()->SendWholeWindow( a_Player->GetClientHandle() );
+ GetWindow()->SendWholeWindow(*a_Player->GetClientHandle());
}
}
- m_World->BroadcastBlockAction(m_PosX, m_PosY, m_PosZ, 1, 1, E_BLOCK_CHEST);
// This is rather a hack
// Instead of marking the chunk as dirty upon chest contents change, we mark it dirty now
diff --git a/source/cChestEntity.h b/source/cChestEntity.h
index 1e186dc9c..962e9f86d 100644
--- a/source/cChestEntity.h
+++ b/source/cChestEntity.h
@@ -2,7 +2,7 @@
#pragma once
#include "cBlockEntity.h"
-#include "cWindowOwner.h"
+#include "UI/cWindowOwner.h"
@@ -22,10 +22,10 @@ class cNBTData;
-class cChestEntity : //tolua_export
- public cBlockEntity, //tolua_export
- public cBlockEntityWindowOwner //tolua_export
-{ //tolua_export
+class cChestEntity : // tolua_export
+ public cBlockEntity, // tolua_export
+ public cBlockEntityWindowOwner // tolua_export
+{ // tolua_export
public:
cChestEntity(int a_X, int a_Y, int a_Z, cWorld * a_World);
virtual ~cChestEntity();
@@ -34,7 +34,7 @@ public:
void HandleData( cNBTData* a_NBTData );
const cItem * GetSlot( int a_Slot ) const; //tolua_export
- void SetSlot( int a_Slot, cItem & a_Item ); //tolua_export
+ void SetSlot(int a_Slot, const cItem & a_Item ); //tolua_export
bool LoadFromJson( const Json::Value& a_Value );
virtual void SaveToJson(Json::Value& a_Value ) override;
diff --git a/source/cClientHandle.cpp b/source/cClientHandle.cpp
index 35e5a0ee4..7108c7711 100644
--- a/source/cClientHandle.cpp
+++ b/source/cClientHandle.cpp
@@ -10,8 +10,7 @@
#include "cInventory.h"
#include "cChestEntity.h"
#include "cSignEntity.h"
-#include "cWindow.h"
-#include "cCraftingWindow.h"
+#include "UI/cWindow.h"
#include "cItem.h"
#include "cTorch.h"
#include "cDoors.h"
@@ -256,7 +255,7 @@ void cClientHandle::Authenticate(void)
m_Protocol->SendTimeUpdate(World->GetWorldTime());
// Send inventory
- m_Player->GetInventory().SendWholeInventory(this);
+ m_Player->GetInventory().SendWholeInventory(*this);
// Send health
m_Player->SendHealth();
@@ -451,14 +450,18 @@ bool cClientHandle::HandleLogin(int a_ProtocolVersion, const AString & a_Usernam
void cClientHandle::HandleCreativeInventory(short a_SlotNum, const cItem & a_HeldItem)
{
// This is for creative Inventory changes
- if (m_Player->GetGameMode() == 1)
+ if (m_Player->GetGameMode() != eGameMode_Creative)
{
- m_Player->GetInventory().Clicked(a_SlotNum, false, false, a_HeldItem);
+ LOGWARNING("Got a CreativeInventoryAction packet from user \"%s\" while not in creative mode. Ignoring.", m_Username.c_str());
+ return;
}
- else
+ if (m_Player->GetWindow()->GetWindowType() != cWindow::Inventory)
{
- LOGWARNING("Got a CreativeInventoryAction packet from user \"%s\" while not in creative mode. Ignoring.", m_Username.c_str());
+ LOGWARNING("Got a CreativeInventoryAction packet from user \"%s\" while not in the inventory window. Ignoring.", m_Username.c_str());
+ return;
}
+
+ m_Player->GetWindow()->Clicked(*m_Player, 0, a_SlotNum, false, false, a_HeldItem);
}
@@ -821,11 +824,10 @@ void cClientHandle::HandleWindowClose(char a_WindowID)
void cClientHandle::HandleWindowClick(char a_WindowID, short a_SlotNum, bool a_IsRightClick, bool a_IsShiftPressed, const cItem & a_HeldItem)
{
- if (a_WindowID == 0)
- {
- m_Player->GetInventory().Clicked(a_SlotNum, a_IsRightClick, a_IsShiftPressed, a_HeldItem);
- return;
- }
+ LOGD("WindowClick: WinID %d, SlotNum %d, IsRclk %d, IsShift %d, Item %s x %d",
+ a_WindowID, a_SlotNum, a_IsRightClick, a_IsShiftPressed,
+ ItemToString(a_HeldItem).c_str(), a_HeldItem.m_ItemCount
+ );
cWindow * Window = m_Player->GetWindow();
if (Window == NULL)
diff --git a/source/cCraftingWindow.cpp b/source/cCraftingWindow.cpp
deleted file mode 100644
index fee000da3..000000000
--- a/source/cCraftingWindow.cpp
+++ /dev/null
@@ -1,244 +0,0 @@
-
-#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
-
-#include "cCraftingWindow.h"
-#include "cItem.h"
-#include "CraftingRecipes.h"
-#include "cPlayer.h"
-#include "cClientHandle.h"
-#include "cInventory.h"
-#include "cPickup.h"
-#include "cRoot.h"
-#include "cWorld.h"
-#include "items/Item.h"
-
-
-
-
-
-/* These numbers are valid for the underlying cInventory object, because that's where we're sending the
-MoveItem() and HowManyCanFit() function calls
-*/
-enum
-{
- SLOT_INVENTORY_MIN = 9,
- SLOT_INVENTORY_MAX = 35,
- SLOT_HOTBAR_MIN = 36,
- SLOT_HOTBAR_MAX = 44
-} ;
-
-
-
-
-
-cCraftingWindow::cCraftingWindow( cWindowOwner* a_Owner, bool a_bInventoryVisible )
- : cWindow(a_Owner, a_bInventoryVisible, cWindow::Workbench, 1)
-{
- cItem * Slots = new cItem[10];
- SetSlots( Slots, 10 );
-}
-
-
-
-
-
-void cCraftingWindow::Clicked(
- cPlayer & a_Player,
- int a_WindowID, short a_SlotNum, bool a_IsRightClick, bool a_IsShiftPressed,
- const cItem & a_HeldItem
-)
-{
- bool bDontCook = false;
-
- cItem * DraggingItem = GetDraggingItem(&a_Player);
- if (
- a_IsShiftPressed &&
- ((DraggingItem == NULL) || DraggingItem->IsEmpty())
- )
- {
- ShiftClicked(a_Player, a_SlotNum);
- return;
- }
-
- // Override for craft result slot
- if (a_SlotNum == 0)
- {
- LOGD("Clicked in craft result slot, item there: %d:%d (%d times) !!", GetSlot(0)->m_ItemID, GetSlot(0)->m_ItemHealth, GetSlot(0)->m_ItemCount);
- cItem * DraggingItem = GetDraggingItem(&a_Player);
- if (DraggingItem->IsEmpty())
- {
- *DraggingItem = *GetSlot(0);
- GetSlot(0)->Empty();
- }
- else if (DraggingItem->IsEqual(*GetSlot(0)))
- {
- cItemHandler * Handler = ItemHandler(GetSlot(0)->m_ItemID);
- if (DraggingItem->m_ItemCount + GetSlot(0)->m_ItemCount <= Handler->GetMaxStackSize())
- {
- DraggingItem->m_ItemCount += GetSlot(0)->m_ItemCount;
- GetSlot(0)->Empty();
- }
- else
- {
- bDontCook = true;
- }
- }
- else
- {
- bDontCook = true;
- }
- }
- else
- {
- cWindow::Clicked(a_Player, GetWindowID(), a_SlotNum, a_IsRightClick, a_IsShiftPressed, a_HeldItem);
- }
-
- if ((a_SlotNum >= 0) && (a_SlotNum < 10))
- {
- cCraftingGrid Grid(GetSlots() + 1, 3, 3);
- cCraftingRecipe Recipe(Grid);
-
- cRoot::Get()->GetCraftingRecipes()->GetRecipe(&a_Player, Grid, Recipe);
-
- if ((a_SlotNum == 0) && !bDontCook)
- {
- // Consume the items from the crafting grid:
- Recipe.ConsumeIngredients(Grid);
-
- // Propagate grid back to m_Slots:
- Grid.CopyToItems(GetSlots() + 1);
-
- // Get the recipe for the new grid contents:
- cRoot::Get()->GetCraftingRecipes()->GetRecipe(&a_Player, Grid, Recipe);
- }
- *GetSlot(0) = Recipe.GetResult();
- LOGD("%s cooked: %d:%d (%d times) !!", a_Player.GetName().c_str(), GetSlot(0)->m_ItemID, GetSlot(0)->m_ItemHealth, GetSlot(0)->m_ItemCount);
- }
- SendWholeWindow( a_Player.GetClientHandle() );
- a_Player.GetInventory().SendWholeInventory( a_Player.GetClientHandle() );
-
- // Separate packet for result =/ Don't know why
- a_Player.GetClientHandle()->SendInventorySlot((char)GetWindowID(), 0, *GetSlot(0));
-}
-
-
-
-
-
-void cCraftingWindow::Close(cPlayer & a_Player)
-{
- // Start from slot 1, don't drop what's in the result slot
- cItems Drops;
- for( int i = 1; i < GetNumSlots(); i++ )
- {
- cItem * Item = GetSlot(i);
- if (!Item->IsEmpty())
- {
- Drops.push_back(*Item);
- }
- Item->Empty();
- }
- float 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);
- cWindow::Close(a_Player);
-}
-
-
-
-
-
-void cCraftingWindow::ShiftClicked(cPlayer & a_Player, short a_SlotNum)
-{
- if (a_SlotNum == SLOT_CRAFTING_RESULT)
- {
- ShiftClickedCraftingResult(a_Player, a_SlotNum);
- }
- else if ((a_SlotNum >= SLOT_CRAFTING_MIN) && (a_SlotNum <= SLOT_CRAFTING_MAX))
- {
- ShiftClickedCraftingGrid(a_Player, a_SlotNum);
- }
- else
- {
- // No need to handle inventory shift-click, it is handled by the underlying cSurvivalInventory, surprise surprise ;)
- }
- SendWholeWindow(a_Player.GetClientHandle());
-}
-
-
-
-
-
-void cCraftingWindow::ShiftClickedCraftingResult(cPlayer & a_Player, short a_Slot)
-{
- // Craft until either the recipe changes (due to ingredients) or there's not enough storage for the result
- cInventory & Inventory = a_Player.GetInventory();
- cItem * CraftingResult = GetSlot(SLOT_CRAFTING_RESULT);
- if ((CraftingResult == NULL) || CraftingResult->IsEmpty())
- {
- return;
- }
- cItem ResultCopy = *CraftingResult;
- int HowManyItemsWillFit = Inventory.HowManyCanFit(CraftingResult->m_ItemID, CraftingResult->m_ItemHealth, SLOT_INVENTORY_MIN, SLOT_INVENTORY_MAX);
- HowManyItemsWillFit += Inventory.HowManyCanFit(CraftingResult->m_ItemID, CraftingResult->m_ItemHealth, SLOT_HOTBAR_MIN, SLOT_HOTBAR_MAX);
- int HowManyPassesWillFit = HowManyItemsWillFit / CraftingResult->m_ItemCount;
- for (int i = 0; i < HowManyPassesWillFit; i++)
- {
- // First try moving into the hotbar:
- int NumMoved = Inventory.MoveItem(CraftingResult->m_ItemID, CraftingResult->m_ItemHealth, CraftingResult->m_ItemCount, SLOT_HOTBAR_MIN, SLOT_HOTBAR_MAX);
-
- // If something didn't fit, move into main inventory:
- if (NumMoved < CraftingResult->m_ItemCount)
- {
- NumMoved += Inventory.MoveItem(CraftingResult->m_ItemID, CraftingResult->m_ItemHealth, CraftingResult->m_ItemCount - NumMoved, SLOT_INVENTORY_MIN, SLOT_INVENTORY_MAX);
- ASSERT(NumMoved == CraftingResult->m_ItemCount); // We checked earlier that we can fit this many items
- }
-
- // "Use" the crafting recipe once:
- cCraftingGrid Grid(GetSlots() + 1, 3, 3);
- cCraftingRecipe Recipe(Grid);
- cRoot::Get()->GetCraftingRecipes()->GetRecipe(&a_Player, Grid, Recipe);
- Recipe.ConsumeIngredients(Grid);
- Grid.CopyToItems(GetSlots() + 1);
- cRoot::Get()->GetCraftingRecipes()->GetRecipe(&a_Player, Grid, Recipe);
- GetSlots()[SLOT_CRAFTING_RESULT] = Recipe.GetResult();
-
- // If the recipe changed, abort:
- if (!Recipe.GetResult().IsEqual(ResultCopy))
- {
- break;
- }
- }
-}
-
-
-
-
-
-void cCraftingWindow::ShiftClickedCraftingGrid(cPlayer & a_Player, short a_Slot)
-{
- cInventory & Inventory = a_Player.GetInventory();
- cItem * Item = GetSlot(a_Slot);
- if ((Item == NULL) || Item->IsEmpty())
- {
- return;
- }
-
- // First try the main inventory:
- Item->m_ItemCount -= Inventory.MoveItem(Item->m_ItemID, Item->m_ItemHealth, Item->m_ItemCount, SLOT_INVENTORY_MIN, SLOT_INVENTORY_MAX);
-
- // If anything left, try the hotbar:
- if (Item->m_ItemCount > 0)
- {
- Item->m_ItemCount -= Inventory.MoveItem(Item->m_ItemID, Item->m_ItemHealth, Item->m_ItemCount, SLOT_HOTBAR_MIN, SLOT_HOTBAR_MAX);
- }
- if (Item->m_ItemCount == 0)
- {
- Item->Empty();
- }
-}
-
-
-
-
diff --git a/source/cCraftingWindow.h b/source/cCraftingWindow.h
deleted file mode 100644
index 638b1aa53..000000000
--- a/source/cCraftingWindow.h
+++ /dev/null
@@ -1,43 +0,0 @@
-
-#pragma once
-
-#include "cWindow.h"
-
-
-
-
-// fwd:
-class cWindowOwner;
-
-
-
-
-
-class cCraftingWindow : public cWindow
-{
-public:
- enum
- {
- SLOT_CRAFTING_RESULT = 0,
- SLOT_CRAFTING_MIN = 1,
- SLOT_CRAFTING_MAX = 9,
- } ;
-
- cCraftingWindow(cWindowOwner * a_Owner, bool a_bInventoryVisible);
-
- virtual void Clicked(
- cPlayer & a_Player,
- int a_WindowID, short a_SlotNum, bool a_IsRightClick, bool a_IsShiftPressed,
- const cItem & a_HeldItem
- ) override;
-
- virtual void Close(cPlayer & a_Player) override;
-
- void ShiftClicked(cPlayer & a_Player, short a_SlotNum);
- void ShiftClickedCraftingResult(cPlayer & a_Player, short a_SlotNum);
- void ShiftClickedCraftingGrid (cPlayer & a_Player, short a_SlotNum);
-};
-
-
-
-
diff --git a/source/cCreativeInventory.cpp b/source/cCreativeInventory.cpp
deleted file mode 100644
index e8b3a0d15..000000000
--- a/source/cCreativeInventory.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-
-#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
-
-#include "cCreativeInventory.h"
-#include "cPlayer.h"
-#include "cClientHandle.h"
-#include "cWindow.h"
-#include "cItem.h"
-#include "cRoot.h"
-
-#include <json/json.h>
-
-
-
-
-
-cCreativeInventory::cCreativeInventory(cPlayer * a_Owner)
- : cInventory(a_Owner)
-{
-
-}
-
-
-
-
-
-cCreativeInventory::~cCreativeInventory()
-{
-}
-
-
-
-
-
-void cCreativeInventory::Clicked(
- short a_SlotNum, bool a_IsRightClick, bool a_IsShiftPressed,
- const cItem & a_HeldItem
-)
-{
- if (a_SlotNum == -1)
- {
- // object thrown out
- m_Owner->TossItem(false, a_HeldItem.m_ItemCount, a_HeldItem.m_ItemType, a_HeldItem.m_ItemDamage);
- return;
- }
-
- if ((a_SlotNum < c_HotOffset) || (a_SlotNum >= c_NumSlots))
- {
- LOG("%s: Invalid slot (%d) in cCreativeInventory::Clicked(). Ignoring...", m_Owner->GetName().c_str(), a_SlotNum);
- return;
- }
-
- m_Slots[a_SlotNum] = a_HeldItem;
-}
-
-
-
-
-
diff --git a/source/cCreativeInventory.h b/source/cCreativeInventory.h
deleted file mode 100644
index 469f0ecd6..000000000
--- a/source/cCreativeInventory.h
+++ /dev/null
@@ -1,22 +0,0 @@
-
-#pragma once
-
-#include "cInventory.h"
-
-
-
-
-
-class cCreativeInventory
- : public cInventory
-{
-public:
- cCreativeInventory(cPlayer * a_Owner);
- ~cCreativeInventory();
-
- virtual void Clicked(short a_SlotNum, bool a_IsRightClick, bool a_IsShiftPressed, const cItem & a_HeldItem) override;
-} ;
-
-
-
-
diff --git a/source/cFurnaceEntity.cpp b/source/cFurnaceEntity.cpp
index 5f0840913..0f51f4ade 100644
--- a/source/cFurnaceEntity.cpp
+++ b/source/cFurnaceEntity.cpp
@@ -4,7 +4,7 @@
#include "cFurnaceEntity.h"
#include "BlockID.h"
#include "cItem.h"
-#include "cFurnaceWindow.h"
+#include "UI/cWindow.h"
#include "cPlayer.h"
#include "cWorld.h"
#include "cClientHandle.h"
@@ -18,6 +18,17 @@
+
+enum
+{
+ PROGRESSBAR_SMELTING = 0,
+ PROGRESSBAR_FUEL = 1,
+} ;
+
+
+
+
+
cFurnaceEntity::cFurnaceEntity(int a_X, int a_Y, int a_Z, cWorld * a_World)
: cBlockEntity( E_BLOCK_FURNACE, a_X, a_Y, a_Z, a_World )
, m_Items( new cItem[3] )
@@ -72,24 +83,20 @@ void cFurnaceEntity::Destroy()
-void cFurnaceEntity::UsedBy( cPlayer * a_Player )
+void cFurnaceEntity::UsedBy(cPlayer * a_Player)
{
LOG("Used a furnace");
- if( !GetWindow() )
+ if (GetWindow() == NULL)
{
- cWindow* Window = new cFurnaceWindow( this );
- Window->SetSlots( m_Items, 3 );
- Window->SetWindowTitle("UberFurnace");
- OpenWindow( Window );
+ OpenWindow(new cFurnaceWindow(m_PosX, m_PosY, m_PosZ, this));
}
- if( GetWindow() )
+ if (GetWindow() != NULL)
{
- if( a_Player->GetWindow() != GetWindow() )
+ if (a_Player->GetWindow() != GetWindow())
{
- a_Player->OpenWindow( GetWindow() );
-
- GetWindow()->SendWholeWindow( a_Player->GetClientHandle() );
+ a_Player->OpenWindow(GetWindow());
+ GetWindow()->SendWholeWindow(*a_Player->GetClientHandle());
}
}
}
@@ -109,12 +116,19 @@ bool cFurnaceEntity::Tick( float a_Dt )
if (m_BurnTime <= 0)
{
+ if (m_TimeCooked > 0)
+ {
+ // We have just finished smelting, reset the progress bar:
+ BroadcastProgress(PROGRESSBAR_SMELTING, 0);
+ m_TimeCooked = 0;
+ }
// There is no fuel and no flame, no need to tick at all
return false;
}
// DEBUG: LOGD("Furnace: Left: %0.1f Burned: %0.1f Burn time: %0.1f", m_CookTime - m_TimeCooked, m_TimeBurned, m_BurnTime );
+ short SmeltingProgress = 0;
if ((m_CookingItem != NULL) && ((m_TimeBurned < m_BurnTime) || (m_TimeCooked + a_Dt >= m_CookTime)))
{
if (m_CookingItem->IsEqual(m_Items[2]) || m_Items[2].IsEmpty())
@@ -140,16 +154,12 @@ bool cFurnaceEntity::Tick( float a_Dt )
m_TimeCooked -= m_CookTime;
StartCooking();
}
- cWindow * Window = GetWindow();
- if (Window != NULL)
- {
- short Value = (short)( m_TimeCooked * (180.f / m_CookTime));
- if (Value > 180) Value = 180;
- if (Value < 0) Value = 0;
- Window->BroadcastInventoryProgress(0, Value);
- }
+ SmeltingProgress = (short)( m_TimeCooked * (180.f / m_CookTime));
+ if (SmeltingProgress > 180) SmeltingProgress = 180;
+ if (SmeltingProgress < 0) SmeltingProgress = 0;
}
}
+ BroadcastProgress(PROGRESSBAR_SMELTING, SmeltingProgress);
m_TimeBurned += a_Dt;
@@ -163,17 +173,15 @@ bool cFurnaceEntity::Tick( float a_Dt )
Window->BroadcastWholeWindow();
}
}
- if (Window != NULL)
+ short Value = 0;
+ if (m_BurnTime > 0.f)
{
- short Value = 0;
- if (m_BurnTime > 0.f)
- {
- Value = 250 - (short)( m_TimeBurned * (250.f / m_BurnTime));
- if (Value > 250) Value = 250;
- if (Value < 0) Value = 0;
- }
- Window->BroadcastInventoryProgress(1, Value);
+ Value = 250 - (short)( m_TimeBurned * (250.f / m_BurnTime));
+ if (Value > 250) Value = 250;
+ if (Value < 0) Value = 0;
}
+ BroadcastProgress(PROGRESSBAR_FUEL, Value);
+
return ((m_CookingItem != NULL) || (m_TimeBurned < m_BurnTime)) && (m_BurnTime > 0.0); // Keep on ticking, if there's more to cook, or if it's cooking
}
@@ -396,3 +404,16 @@ void cFurnaceEntity::SendTo(cClientHandle & a_Client)
+
+void cFurnaceEntity::BroadcastProgress(int a_ProgressbarID, short a_Value)
+{
+ cWindow * Window = GetWindow();
+ if (Window != NULL)
+ {
+ Window->BroadcastInventoryProgress(a_ProgressbarID, a_Value);
+ }
+}
+
+
+
+
diff --git a/source/cFurnaceEntity.h b/source/cFurnaceEntity.h
index 953644097..43bf615a6 100644
--- a/source/cFurnaceEntity.h
+++ b/source/cFurnaceEntity.h
@@ -2,7 +2,7 @@
#pragma once
#include "cBlockEntity.h"
-#include "cWindowOwner.h"
+#include "UI/cWindowOwner.h"
#include "cItem.h"
@@ -49,7 +49,7 @@ public:
void ResetCookTimer();
- const cItem & GetSlot(int i) const { return m_Items[i]; }
+ const cItem * GetSlot(int i) const { return &(m_Items[i]); }
void SetSlot(int a_Slot, const cItem & a_Item);
@@ -69,6 +69,8 @@ private:
float m_TimeCooked; // Amount of time that the current item has been cooking
float m_BurnTime; // Amount of time that the current fuel can burn (in total); zero if no fuel burning
float m_TimeBurned; // Amount of time that the current fuel has been burning
+
+ void BroadcastProgress(int a_ProgressbarID, short a_Value);
};
diff --git a/source/cFurnaceWindow.cpp b/source/cFurnaceWindow.cpp
deleted file mode 100644
index 683bf7166..000000000
--- a/source/cFurnaceWindow.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-
-#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
-
-#include "cFurnaceWindow.h"
-#include "cItem.h"
-#include "cFurnaceEntity.h"
-#include "cPlayer.h"
-
-
-
-
-
-cFurnaceWindow::cFurnaceWindow( cFurnaceEntity* a_Owner )
- : cWindow(a_Owner, true, cWindow::Furnace, 1)
- , m_Furnace( a_Owner )
-{
-}
-
-
-
-
-
-void cFurnaceWindow::Clicked(
- cPlayer & a_Player,
- int a_WindowID, short a_SlotNum, bool a_IsRightClick, bool a_IsShiftPressed,
- const cItem & a_HeldItem
-)
-{
- cItem Fuel = *GetSlot( 0 );
-
- cWindow::Clicked(a_Player, a_WindowID, a_SlotNum, a_IsRightClick, a_IsShiftPressed, a_HeldItem);
- if (m_Furnace != NULL)
- {
- if ((a_SlotNum >= 0) && (a_SlotNum <= 2)) // them important slots
- {
- if (Fuel.m_ItemID != GetSlot( 0 )->m_ItemID)
- {
- m_Furnace->ResetCookTimer();
- }
-
- if (m_Furnace->StartCooking())
- {
- SendWholeWindow(a_Player.GetClientHandle());
- }
- }
- }
-}
-
-
-
-
-
-void cFurnaceWindow::Close( cPlayer & a_Player )
-{
- m_Furnace = NULL;
- cWindow::Close( a_Player );
-}
-
-
-
-
diff --git a/source/cFurnaceWindow.h b/source/cFurnaceWindow.h
deleted file mode 100644
index ccaa5a97c..000000000
--- a/source/cFurnaceWindow.h
+++ /dev/null
@@ -1,37 +0,0 @@
-
-#pragma once
-
-#include "cWindow.h"
-
-
-
-
-
-class cFurnaceEntity;
-class cWindowOwner;
-
-
-
-
-
-class cFurnaceWindow :
- public cWindow
-{
-public:
- cFurnaceWindow( cFurnaceEntity* a_Owner );
-
- virtual void Clicked(
- cPlayer & a_Player,
- int a_WindowID, short a_SlotNum, bool a_IsRightClick, bool a_IsShiftPressed,
- const cItem & a_HeldItem
- ) override;
-
- virtual void Close( cPlayer & a_Player ) override;
-
-private:
- cFurnaceEntity * m_Furnace;
-};
-
-
-
-
diff --git a/source/cInventory.cpp b/source/cInventory.cpp
index 58165ff09..1e0b4f6ae 100644
--- a/source/cInventory.cpp
+++ b/source/cInventory.cpp
@@ -4,7 +4,7 @@
#include "cInventory.h"
#include "cPlayer.h"
#include "cClientHandle.h"
-#include "cWindow.h"
+#include "UI/cWindow.h"
#include "cItem.h"
#include "cRoot.h"
@@ -18,39 +18,35 @@
cInventory::~cInventory()
{
- delete [] m_Slots;
- delete m_EquippedItem;
- if( GetWindow() ) GetWindow()->Close( *m_Owner );
+ /*
+ // TODO
+ cWindow wnd = GetWindow();
+ if (wnd != NULL)
+ {
+ wnd->Close(*m_Owner);
+ }
CloseWindow();
+ */
}
-cInventory::cInventory(cPlayer* a_Owner)
+cInventory::cInventory(cPlayer & a_Owner) :
+ m_Owner(a_Owner)
{
- m_Owner = a_Owner;
-
- m_Slots = new cItem[c_NumSlots];
- for(unsigned int i = 0; i < c_NumSlots; i++)
+ for (unsigned int i = 0; i < c_NumSlots; i++)
+ {
m_Slots[i].Empty();
+ }
m_CraftSlots = m_Slots + c_CraftOffset;
m_ArmorSlots = m_Slots + c_ArmorOffset;
m_MainSlots = m_Slots + c_MainOffset;
m_HotSlots = m_Slots + c_HotOffset;
- m_EquippedItem = new cItem();
- m_EquippedSlot = 0;
-
- if (GetWindow() == NULL)
- {
- cWindow * Window = new cWindow( this, false, cWindow::Inventory, 0);
- Window->SetSlots(m_Slots, c_NumSlots);
- Window->Open(*a_Owner);
- OpenWindow(Window);
- }
+ SetEquippedSlot(0);
}
@@ -97,9 +93,9 @@ bool cInventory::AddItem( cItem & a_Item )
bool cInventory::RemoveItem( cItem & a_Item )
{
// First check equipped slot
- if( m_EquippedSlot >= 0 && m_EquippedSlot < 9 )
+ if ((m_EquippedSlot >= 0) && (m_EquippedSlot < 9))
{
- if( m_HotSlots[m_EquippedSlot].m_ItemID == a_Item.m_ItemID )
+ if (m_HotSlots[m_EquippedSlot].m_ItemID == a_Item.m_ItemID)
{
cItem & Item = m_HotSlots[m_EquippedSlot];
if(Item.m_ItemCount > a_Item.m_ItemCount)
@@ -118,7 +114,7 @@ bool cInventory::RemoveItem( cItem & a_Item )
}
// Then check other slotz
- if( a_Item.m_ItemCount > 0 )
+ if (a_Item.m_ItemCount > 0)
{
for(int i = 0; i < 36; i++)
{
@@ -141,10 +137,7 @@ bool cInventory::RemoveItem( cItem & a_Item )
}
}
- if( a_Item.m_ItemCount == 0 )
- return true;
- else
- return false;
+ return (a_Item.m_ItemCount == 0);
}
@@ -221,10 +214,17 @@ cItem* cInventory::GetFromHotBar( int a_SlotNum )
-void cInventory::SetEquippedSlot( int a_SlotNum )
+void cInventory::SetEquippedSlot(int a_SlotNum)
{
- if( a_SlotNum < 0 || a_SlotNum >= 9 ) m_EquippedSlot = 0;
- else m_EquippedSlot = (short)a_SlotNum;
+ if ((a_SlotNum < 0) || (a_SlotNum >= 9))
+ {
+ m_EquippedSlot = 0;
+ }
+ else
+ {
+ m_EquippedSlot = (short)a_SlotNum;
+ }
+ m_EquippedItem = GetFromHotBar(m_EquippedSlot);
}
@@ -259,36 +259,9 @@ const cItem & cInventory::GetEquippedItem(void) const
-void cInventory::SendWholeInventory(cClientHandle * a_Client)
-{
- a_Client->SendWholeInventory(*this);
-}
-
-
-
-
-
-void cInventory::SendWholeInventoryToAll(void)
+void cInventory::SendWholeInventory(cClientHandle & a_Client)
{
- cWindow * Window = GetWindow();
- if (Window == NULL)
- {
- return;
- }
-
- class cSender :
- public cItemCallback<cClientHandle>
- {
- cInventory * m_Inventory;
- public:
- cSender(cInventory * a_Inventory) : m_Inventory(a_Inventory) {}
- virtual bool Item(cClientHandle * a_Client) override
- {
- m_Inventory->SendWholeInventory(a_Client);
- return false;
- }
- } Sender(this);
- Window->ForEachClient(Sender);
+ a_Client.SendWholeInventory(*this);
}
@@ -305,7 +278,7 @@ void cInventory::SendSlot(int a_SlotNum)
// Sanitize items that are not completely empty (ie. count == 0, but type != empty)
Item->Empty();
}
- m_Owner->GetClientHandle()->SendInventorySlot(0, a_SlotNum, *Item);
+ m_Owner.GetClientHandle()->SendInventorySlot(0, a_SlotNum, *Item);
}
}
@@ -437,6 +410,10 @@ void cInventory::SaveToJson(Json::Value & a_Value)
bool cInventory::LoadFromJson(Json::Value & a_Value)
{
int SlotIdx = 0;
+
+ // TODO: Limit the number of slots written to the actual number of slots,
+ // otherwise an invalid json will crash the server!
+
for( Json::Value::iterator itr = a_Value.begin(); itr != a_Value.end(); ++itr )
{
m_Slots[SlotIdx].FromJson( *itr );
diff --git a/source/cInventory.h b/source/cInventory.h
index 96d744baf..bce485852 100644
--- a/source/cInventory.h
+++ b/source/cInventory.h
@@ -1,7 +1,7 @@
#pragma once
-#include "cWindowOwner.h"
+#include "cItem.h"
@@ -12,7 +12,6 @@ namespace Json
class Value;
};
-class cItem;
class cClientHandle;
class cPlayer;
@@ -21,10 +20,9 @@ class cPlayer;
class cInventory //tolua_export
- : public cWindowOwner
{ //tolua_export
public:
- cInventory(cPlayer* a_Owner);
+ cInventory(cPlayer & a_Owner);
~cInventory();
void Clear(); //tolua_export
@@ -38,19 +36,17 @@ public:
void SaveToJson(Json::Value & a_Value);
bool LoadFromJson(Json::Value & a_Value);
- void SendWholeInventory( cClientHandle* a_Client );
- void SendWholeInventoryToAll(void);
+ void SendWholeInventory(cClientHandle & a_Client);
- cItem* GetSlot( int a_SlotNum ); //tolua_export
- cItem* GetSlots() const { return m_Slots; }
- cItem* GetFromHotBar( int a_SlotNum ); //tolua_export
+ cItem * GetSlot(int a_SlotNum ); //tolua_export
+ cItem * GetSlots(void) { return m_Slots; }
+ const cItem * GetSlots(void) const { return m_Slots; }
+ cItem * GetFromHotBar(int a_HotBarSlotNum); //tolua_export
cItem & GetEquippedItem(void); //tolua_export
const cItem & GetEquippedItem(void) const;
- void SetEquippedSlot( int a_SlotNum ); //tolua_export
- short GetEquippedSlot() { return m_EquippedSlot; } //tolua_export
-
- virtual void Clicked(short a_SlotNum, bool a_IsRightClick, bool a_IsShiftPressed, const cItem & a_HeldItem) = 0;
+ void SetEquippedSlot(int a_SlotNum); //tolua_export
+ short GetEquippedSlot(void) { return m_EquippedSlot; } //tolua_export
void SendSlot( int a_SlotNum ); //tolua_export
@@ -74,19 +70,17 @@ public:
protected:
bool AddToBar( cItem & a_Item, const int a_Offset, const int a_Size, bool* a_bChangedSlots, int a_Mode = 0 );
- cItem* m_Slots;
- cItem* m_MainSlots;
- cItem* m_CraftSlots;
- cItem* m_ArmorSlots;
- cItem* m_HotSlots;
+ cItem m_Slots[c_NumSlots];
+
+ cItem * m_MainSlots;
+ cItem * m_CraftSlots;
+ cItem * m_ArmorSlots;
+ cItem * m_HotSlots;
- cItem* m_EquippedItem;
+ cItem * m_EquippedItem;
short m_EquippedSlot;
- cPlayer* m_Owner;
-
- // cWindowOwner override:
- virtual void GetBlockPos(int & a_BlockX, int & a_BlockY, int & a_BlockZ) override {} // UNUSED for an inventory
+ cPlayer & m_Owner;
}; //tolua_export
diff --git a/source/cPlayer.cpp b/source/cPlayer.cpp
index 4095c633a..1d2774dbf 100644
--- a/source/cPlayer.cpp
+++ b/source/cPlayer.cpp
@@ -3,13 +3,12 @@
#include "cPlayer.h"
#include "cServer.h"
-#include "cCreativeInventory.h"
-#include "cSurvivalInventory.h"
#include "cClientHandle.h"
+#include "UI/cWindow.h"
+#include "UI/cWindowOwner.h"
#include "cWorld.h"
#include "cPickup.h"
#include "cPluginManager.h"
-#include "cWindow.h"
#include "cBlockEntity.h"
#include "cGroupManager.h"
#include "cGroup.h"
@@ -48,8 +47,9 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
, m_LastGroundHeight( 0 )
, m_bTouchGround( false )
, m_Stance( 0.0 )
- , m_Inventory( 0 )
- , m_CurrentWindow( 0 )
+ , m_Inventory(*this)
+ , m_CurrentWindow(NULL)
+ , m_InventoryWindow(NULL)
, m_TimeLastPickupCheck( 0.f )
, m_Color('-')
, m_ClientHandle( a_Client )
@@ -61,6 +61,9 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
this, GetUniqueID()
);
m_EntityType = eEntityType_Player;
+
+ m_InventoryWindow = new cInventoryWindow(*this);
+ m_CurrentWindow = m_InventoryWindow;
SetMaxHealth(20);
m_MaxFoodLevel = 20;
@@ -69,8 +72,6 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
m_FoodLevel = m_MaxFoodLevel;
m_FoodSaturationLevel = 5.f;
- m_Inventory = new cSurvivalInventory( this );
- m_CreativeInventory = new cCreativeInventory(this);
cTimer t1;
m_LastPlayerListTime = t1.GetNowTime();
@@ -80,10 +81,9 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
m_PlayerName = a_PlayerName;
m_bDirtyPosition = true; // So chunks are streamed to player at spawn
- if( !LoadFromDisk() )
+ if (!LoadFromDisk())
{
- m_Inventory->Clear();
- m_CreativeInventory->Clear();
+ m_Inventory.Clear();
m_Pos.x = cRoot::Get()->GetDefaultWorld()->GetSpawnX();
m_Pos.y = cRoot::Get()->GetDefaultWorld()->GetSpawnY();
m_Pos.z = cRoot::Get()->GetDefaultWorld()->GetSpawnZ();
@@ -110,11 +110,6 @@ cPlayer::~cPlayer(void)
m_ClientHandle = NULL;
- delete m_Inventory;
- m_Inventory = NULL;
-
- delete m_CreativeInventory;
-
LOG("Player %p deleted", this);
}
@@ -373,9 +368,9 @@ void cPlayer::KilledBy(cEntity * a_Killer)
m_bVisible = false; // So new clients don't see the player
// Puke out all the items
- cItem* Items = m_Inventory->GetSlots();
+ cItem * Items = m_Inventory.GetSlots();
cItems Pickups;
- for (unsigned int i = 1; i < m_Inventory->c_NumSlots; ++i)
+ for (unsigned int i = 1; i < m_Inventory.c_NumSlots; ++i)
{
if( !Items[i].IsEmpty() )
{
@@ -419,10 +414,14 @@ Vector3d cPlayer::GetEyePosition()
return Vector3d( m_Pos.x, m_Stance, m_Pos.z );
}
+
+
+
+
void cPlayer::OpenWindow( cWindow* a_Window )
{
CloseWindow(m_CurrentWindow ? (char)m_CurrentWindow->GetWindowType() : 0);
- a_Window->Open( *this );
+ a_Window->OpenedByPlayer(*this);
m_CurrentWindow = a_Window;
}
@@ -432,38 +431,15 @@ void cPlayer::OpenWindow( cWindow* a_Window )
void cPlayer::CloseWindow(char a_WindowType)
{
- if (a_WindowType == 0)
+ if (m_CurrentWindow == m_InventoryWindow)
{
- // Inventory
- if (
- (m_Inventory->GetWindow()->GetDraggingItem() != NULL) &&
- (m_Inventory->GetWindow()->GetDraggingItem()->m_ItemCount > 0)
- )
- {
- LOGD("Player holds item! Dropping it...");
- TossItem( true, m_Inventory->GetWindow()->GetDraggingItem()->m_ItemCount );
- }
-
- //Drop whats in the crafting slots (1, 2, 3, 4)
- cItems Drops;
- for (int i = 1; i <= 4; i++)
- {
- cItem* Item = m_Inventory->GetSlot( i );
- if (!Item->IsEmpty())
- {
- Drops.push_back(*Item);
- }
- Item->Empty();
- }
- float vX = 0, vY = 0, vZ = 0;
- EulerToVector(-GetRotation(), GetPitch(), vZ, vX, vY);
- vY = -vY*2 + 1.f;
- m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY() + 1.6f, GetPosZ(), vX * 2, vY * 2, vZ * 2);
+ // The inventory window must not be closed and must not be even sent a close packet
+ return;
}
if (m_CurrentWindow != NULL)
{
- // FIXME: If the player entity is destroyed while having a chest window open, the chest will not close
+ // TODO: This code should be in cChestWindow instead
if ((a_WindowType == 1) && (m_CurrentWindow->GetWindowType() == cWindow::Chest))
{
int x, y, z;
@@ -471,9 +447,9 @@ void cPlayer::CloseWindow(char a_WindowType)
m_World->BroadcastBlockAction(x, y, z, 1, 0, E_BLOCK_CHEST);
}
- m_CurrentWindow->Close( *this );
+ m_CurrentWindow->ClosedByPlayer(*this);
}
- m_CurrentWindow = NULL;
+ m_CurrentWindow = m_InventoryWindow;
}
@@ -515,19 +491,8 @@ void cPlayer::SetGameMode(eGameMode a_GameMode)
return;
}
- short OldSlotNum = 0;
- if (m_GameMode == eGameMode_Survival)
- {
- OldSlotNum = m_Inventory->GetEquippedSlot();
- }
- else
- {
- OldSlotNum = m_CreativeInventory->GetEquippedSlot();
- }
m_GameMode = a_GameMode;
m_ClientHandle->SendGameMode(a_GameMode);
- GetInventory().SendWholeInventory(m_ClientHandle);
- GetInventory().SetEquippedSlot(OldSlotNum);
}
@@ -785,14 +750,18 @@ void cPlayer::TossItem(
// Drop an item from the inventory:
if (a_bDraggingItem)
{
- cItem * Item = GetInventory().GetWindow()->GetDraggingItem();
- if (!Item->IsEmpty())
+ cItem & Item = GetDraggingItem();
+ if (!Item.IsEmpty())
{
- Drops.push_back(*Item);
- if( Item->m_ItemCount > a_Amount )
- Item->m_ItemCount -= (char)a_Amount;
+ Drops.push_back(Item);
+ if (Item.m_ItemCount > a_Amount)
+ {
+ Item.m_ItemCount -= (char)a_Amount;
+ }
else
- Item->Empty();
+ {
+ Item.Empty();
+ }
}
}
else
@@ -917,7 +886,7 @@ bool cPlayer::LoadFromDisk()
}
Json::Value & JSON_PlayerPosition = root["position"];
- if( JSON_PlayerPosition.size() == 3 )
+ if (JSON_PlayerPosition.size() == 3)
{
m_Pos.x = JSON_PlayerPosition[(unsigned int)0].asDouble();
m_Pos.y = JSON_PlayerPosition[(unsigned int)1].asDouble();
@@ -925,7 +894,7 @@ bool cPlayer::LoadFromDisk()
}
Json::Value & JSON_PlayerRotation = root["rotation"];
- if( JSON_PlayerRotation.size() == 3 )
+ if (JSON_PlayerRotation.size() == 3)
{
m_Rot.x = (float)JSON_PlayerRotation[(unsigned int)0].asDouble();
m_Rot.y = (float)JSON_PlayerRotation[(unsigned int)1].asDouble();
@@ -938,9 +907,7 @@ bool cPlayer::LoadFromDisk()
m_GameMode = (eGameMode) root.get("gamemode", eGameMode_NotSet).asInt();
-
- m_Inventory->LoadFromJson(root["inventory"]);
- m_CreativeInventory->LoadFromJson(root["creativeinventory"]);
+ m_Inventory.LoadFromJson(root["inventory"]);
m_LoadedWorldName = root.get("world", "world").asString();
@@ -971,25 +938,23 @@ bool cPlayer::SaveToDisk()
JSON_PlayerRotation.append( Json::Value( m_Rot.z ) );
Json::Value JSON_Inventory;
- m_Inventory->SaveToJson( JSON_Inventory );
-
- Json::Value JSON_CreativeInventory;
- m_CreativeInventory->SaveToJson( JSON_CreativeInventory );
+ m_Inventory.SaveToJson( JSON_Inventory );
Json::Value root;
root["position"] = JSON_PlayerPosition;
root["rotation"] = JSON_PlayerRotation;
root["inventory"] = JSON_Inventory;
- root["creativeinventory"] = JSON_CreativeInventory;
root["health"] = m_Health;
root["food"] = m_FoodLevel;
root["foodSaturation"] = m_FoodSaturationLevel;
root["world"] = GetWorld()->GetName();
- if(m_GameMode == GetWorld()->GetGameMode())
+ if (m_GameMode == GetWorld()->GetGameMode())
{
root["gamemode"] = (int) eGameMode_NotSet;
- }else{
+ }
+ else
+ {
root["gamemode"] = (int) m_GameMode;
}
diff --git a/source/cPlayer.h b/source/cPlayer.h
index 8d652120a..d5b63941c 100644
--- a/source/cPlayer.h
+++ b/source/cPlayer.h
@@ -2,8 +2,7 @@
#pragma once
#include "cPawn.h"
-#include "cSurvivalInventory.h"
-#include "cCreativeInventory.h"
+#include "cInventory.h"
#include "Defines.h"
@@ -12,7 +11,6 @@
class cGroup;
class cWindow;
-class cInventory;
class cClientHandle;
@@ -43,8 +41,8 @@ public:
inline bool GetFlying() { return m_bTouchGround; } //tolua_export
inline bool IsOnGround(void) const {return m_bTouchGround; } // tolua_export
inline const double GetStance(void) const { return m_Pos.y + 1.62; } //tolua_export // TODO: Proper stance when crouching etc.
- inline cInventory & GetInventory(void) { if (GetGameMode() == eGameMode_Survival) return *m_Inventory; else return *m_CreativeInventory; } //tolua_export
- inline const cInventory & GetInventory(void) const { if (GetGameMode() == eGameMode_Survival) return *m_Inventory; else return *m_CreativeInventory; }
+ inline cInventory & GetInventory(void) { return m_Inventory; } //tolua_export
+ inline const cInventory & GetInventory(void) const { return m_Inventory; }
inline const cItem & GetEquippedItem(void) const {return GetInventory().GetEquippedItem(); }
@@ -118,6 +116,10 @@ public:
void UseEquippedItem(void);
void SendHealth();
+
+ // In UI windows, the item that the player is dragging:
+ bool IsDraggingItem(void) const { return !m_DraggingItem.IsEmpty(); }
+ cItem & GetDraggingItem(void) {return m_DraggingItem; }
protected:
virtual void Destroyed();
@@ -144,9 +146,9 @@ protected:
float m_LastGroundHeight;
bool m_bTouchGround;
double m_Stance;
- cSurvivalInventory* m_Inventory;
- cCreativeInventory* m_CreativeInventory;
- cWindow* m_CurrentWindow;
+ cInventory m_Inventory;
+ cWindow * m_CurrentWindow;
+ cWindow * m_InventoryWindow;
float m_TimeLastPickupCheck;
@@ -159,6 +161,8 @@ protected:
int m_LastBlockActionCnt;
eGameMode m_GameMode;
std::string m_IP;
+
+ cItem m_DraggingItem;
long long m_LastPlayerListTime;
static const unsigned short PLAYER_LIST_TIME_MS = 1000; // 1000 = once per second
diff --git a/source/cRoot.cpp b/source/cRoot.cpp
index 4c87210c7..6cd696da8 100644
--- a/source/cRoot.cpp
+++ b/source/cRoot.cpp
@@ -10,8 +10,6 @@
#include "CraftingRecipes.h"
#include "cPluginManager.h"
#include "cMonsterConfig.h"
-#include "cSleep.h"
-#include "cThread.h"
#include "cFileFormatUpdater.h"
#include "cRedstone.h"
#include "cPlayer.h"
diff --git a/source/cServer.cpp b/source/cServer.cpp
index 9653a2542..a4e2607a5 100644
--- a/source/cServer.cpp
+++ b/source/cServer.cpp
@@ -5,7 +5,6 @@
#include "cServer.h"
#include "cClientHandle.h"
-#include "cSleep.h"
#include "cTimer.h"
#include "cMonster.h"
#include "cSocket.h"
diff --git a/source/cSurvivalInventory.cpp b/source/cSurvivalInventory.cpp
deleted file mode 100644
index 3786f86fa..000000000
--- a/source/cSurvivalInventory.cpp
+++ /dev/null
@@ -1,285 +0,0 @@
-
-#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
-
-#include "cSurvivalInventory.h"
-#include "cPlayer.h"
-#include "cClientHandle.h"
-#include "cWindow.h"
-#include "cItem.h"
-#include "CraftingRecipes.h"
-#include "cRoot.h"
-#include "items/Item.h"
-
-
-
-
-
-cSurvivalInventory::cSurvivalInventory(cPlayer* a_Owner)
- : cInventory(a_Owner)
-{
-}
-
-
-
-
-
-cSurvivalInventory::~cSurvivalInventory()
-{
-}
-
-
-
-
-
-void cSurvivalInventory::Clicked(
- short a_SlotNum, bool a_IsRightClick, bool a_IsShiftPressed,
- const cItem & a_HeldItem
-)
-{
- cWindow * Window = GetWindow();
- if (
- a_IsShiftPressed && // Shift pressed
- (Window != NULL) && // Window is valid
- (Window->GetDraggingItem()->IsEmpty()) // Not dragging anything
- )
- {
- ShiftClicked(a_SlotNum);
- return;
- }
-
- bool bDontCook = false;
- if (Window != NULL)
- {
- // Override for craft result slot
- if (a_SlotNum == (short)c_CraftOffset)
- {
- LOGD("Clicked in craft result slot, item there: %d:%d (%d times) !!", m_Slots[c_CraftOffset].m_ItemID, m_Slots[c_CraftOffset].m_ItemHealth, m_Slots[c_CraftOffset].m_ItemCount);
- cItem * DraggingItem = Window->GetDraggingItem();
- if (DraggingItem->IsEmpty())
- {
- *DraggingItem = m_Slots[c_CraftOffset];
- m_Slots[c_CraftOffset].Empty();
- }
- else if (DraggingItem->IsEqual(m_Slots[c_CraftOffset]))
- {
- cItemHandler * Handler = ItemHandler(m_Slots[c_CraftOffset].m_ItemID);
- if (DraggingItem->m_ItemCount + m_Slots[c_CraftOffset].m_ItemCount <= Handler->GetMaxStackSize())
- {
- DraggingItem->m_ItemCount += m_Slots[c_CraftOffset].m_ItemCount;
- m_Slots[c_CraftOffset].Empty();
- }
- else
- {
- bDontCook = true;
- }
- }
- else
- {
- bDontCook = true;
- }
- }
- else
- {
- Window->Clicked(*m_Owner, 0, a_SlotNum, a_IsRightClick, a_IsShiftPressed, a_HeldItem);
- }
- }
- else
- {
- ASSERT(!"No inventory window! WTF?");
- LOG("No Inventory window! WTF");
- }
-
- if ((a_SlotNum >= (short)c_CraftOffset) && (a_SlotNum < (short)(c_CraftOffset + c_CraftSlots + 1)))
- {
- cCraftingGrid Grid(m_Slots + c_CraftOffset + 1, 2, 2);
- cCraftingRecipe Recipe(Grid);
-
- cRoot::Get()->GetCraftingRecipes()->GetRecipe(m_Owner, Grid, Recipe);
-
- if ((a_SlotNum == 0) && !bDontCook)
- {
- // Consume the items from the crafting grid:
- Recipe.ConsumeIngredients(Grid);
-
- // Propagate grid back to m_Slots:
- Grid.CopyToItems(m_Slots + c_CraftOffset + 1);
-
- // Get the recipe for the new grid contents:
- cRoot::Get()->GetCraftingRecipes()->GetRecipe(m_Owner, Grid, Recipe);
- }
- m_Slots[c_CraftOffset] = Recipe.GetResult();
- LOGD("%s cooked: %d:%d (%d times) !!", m_Owner->GetName().c_str(), m_Slots[c_CraftOffset].m_ItemID, m_Slots[c_CraftOffset].m_ItemHealth, m_Slots[c_CraftOffset].m_ItemCount );
- SendWholeInventory(m_Owner->GetClientHandle());
- }
- SendSlot(0);
-}
-
-
-
-
-
-void cSurvivalInventory::ShiftClicked(short a_SlotNum)
-{
- ASSERT((GetWindow() == NULL) || (GetWindow()->GetDraggingItem()->IsEmpty())); // Cannot handle shift-click if dragging something
-
- if (a_SlotNum == SLOT_CRAFTING_RESULT)
- {
- ShiftClickedCraftingResult(a_SlotNum);
- }
- else if ((a_SlotNum >= SLOT_CRAFTING_MIN) && (a_SlotNum <= SLOT_CRAFTING_MAX))
- {
- ShiftClickedCraftingGrid(a_SlotNum);
- }
- else if ((a_SlotNum >= SLOT_ARMOR_MIN) && (a_SlotNum <= SLOT_ARMOR_MAX))
- {
- ShiftClickedArmor(a_SlotNum);
- }
- else if ((a_SlotNum >= SLOT_HOTBAR_MIN) && (a_SlotNum <= SLOT_HOTBAR_MAX))
- {
- ShiftClickedHotbar(a_SlotNum);
- }
- else
- {
- ShiftClickedInventory(a_SlotNum);
- }
- // Because the client tries to guess our actions and is not always right, send the whole inventory:
- SendWholeInventoryToAll();
-}
-
-
-
-
-
-void cSurvivalInventory::ShiftClickedCraftingResult(short a_Slot)
-{
- // Craft until either the recipe changes (due to ingredients) or there's not enough storage for the result
- cItem * CraftingResult = GetSlot(SLOT_CRAFTING_RESULT);
- if ((CraftingResult == NULL) || CraftingResult->IsEmpty())
- {
- return;
- }
- cItem ResultCopy = *CraftingResult;
- int HowManyItemsWillFit = HowManyCanFit(CraftingResult->m_ItemID, CraftingResult->m_ItemHealth, SLOT_INVENTORY_MIN, SLOT_INVENTORY_MAX);
- HowManyItemsWillFit += HowManyCanFit(CraftingResult->m_ItemID, CraftingResult->m_ItemHealth, SLOT_HOTBAR_MIN, SLOT_HOTBAR_MAX);
- int HowManyPassesWillFit = HowManyItemsWillFit / CraftingResult->m_ItemCount;
- for (int i = 0; i < HowManyPassesWillFit; i++)
- {
- // First try moving into the hotbar:
- int NumMoved = MoveItem(CraftingResult->m_ItemID, CraftingResult->m_ItemHealth, CraftingResult->m_ItemCount, SLOT_HOTBAR_MIN, SLOT_HOTBAR_MAX);
-
- // If something didn't fit, move into main inventory:
- if (NumMoved < CraftingResult->m_ItemCount)
- {
- MoveItem(CraftingResult->m_ItemID, CraftingResult->m_ItemHealth, CraftingResult->m_ItemCount - NumMoved, SLOT_INVENTORY_MIN, SLOT_INVENTORY_MAX);
- }
-
- // "Use" the crafting recipe once:
- cCraftingGrid Grid(m_Slots + SLOT_CRAFTING_MIN, 2, 2);
- cCraftingRecipe Recipe(Grid);
- cRoot::Get()->GetCraftingRecipes()->GetRecipe(m_Owner, Grid, Recipe);
- Recipe.ConsumeIngredients(Grid);
- Grid.CopyToItems(m_Slots + c_CraftOffset + 1);
- cRoot::Get()->GetCraftingRecipes()->GetRecipe(m_Owner, Grid, Recipe);
- m_Slots[SLOT_CRAFTING_RESULT] = Recipe.GetResult();
-
- // If the recipe changed, abort:
- if (!Recipe.GetResult().IsEqual(ResultCopy))
- {
- break;
- }
- }
-}
-
-
-
-
-
-void cSurvivalInventory::ShiftClickedCraftingGrid(short a_Slot)
-{
- // Move the item from the crafting grid into the main inventory:
- cItem * Item = GetSlot(a_Slot);
- if ((Item == NULL) || Item->IsEmpty())
- {
- return;
- }
- // First try the main inventory:
- Item->m_ItemCount -= MoveItem(Item->m_ItemID, Item->m_ItemHealth, Item->m_ItemCount, SLOT_INVENTORY_MIN, SLOT_INVENTORY_MAX);
-
- // If anything left, try the hotbar:
- if (Item->m_ItemCount > 0)
- {
- Item->m_ItemCount -= MoveItem(Item->m_ItemID, Item->m_ItemHealth, Item->m_ItemCount, SLOT_HOTBAR_MIN, SLOT_HOTBAR_MAX);
- }
- SendSlot(a_Slot);
-}
-
-
-
-
-
-void cSurvivalInventory::ShiftClickedArmor(short a_Slot)
-{
- // Move the item from the armor slot into the main inventory:
- cItem * Item = GetSlot(a_Slot);
- if ((Item == NULL) || Item->IsEmpty())
- {
- return;
- }
- Item->m_ItemCount -= MoveItem(Item->m_ItemID, Item->m_ItemHealth, Item->m_ItemCount, SLOT_INVENTORY_MIN, SLOT_INVENTORY_MAX);
- SendSlot(a_Slot);
-}
-
-
-
-
-
-void cSurvivalInventory::ShiftClickedHotbar(short a_Slot)
-{
- // Move the item from the hotbar into the main inventory:
- cItem * Item = GetSlot(a_Slot);
- if ((Item == NULL) || Item->IsEmpty())
- {
- return;
- }
- Item->m_ItemCount -= MoveItem(Item->m_ItemID, Item->m_ItemHealth, Item->m_ItemCount, SLOT_INVENTORY_MIN, SLOT_INVENTORY_MAX);
- SendSlot(a_Slot);
-}
-
-
-
-
-
-void cSurvivalInventory::ShiftClickedInventory(short a_Slot)
-{
- // Move the item from the main inventory into armor slot if it is armor, or the hotbar otherwise:
- cItem * Item = GetSlot(a_Slot);
- if ((Item == NULL) || Item->IsEmpty())
- {
- return;
- }
- if (ItemCategory::IsHelmet(Item->m_ItemID))
- {
- Item->m_ItemCount -= MoveItem(Item->m_ItemID, Item->m_ItemHealth, Item->m_ItemCount, SLOT_ARMOR_HELMET, SLOT_ARMOR_HELMET);
- }
- else if (ItemCategory::IsChestPlate(Item->m_ItemID))
- {
- Item->m_ItemCount -= MoveItem(Item->m_ItemID, Item->m_ItemHealth, Item->m_ItemCount, SLOT_ARMOR_CHESTPLATE, SLOT_ARMOR_CHESTPLATE);
- }
- else if (ItemCategory::IsLeggings(Item->m_ItemID))
- {
- Item->m_ItemCount -= MoveItem(Item->m_ItemID, Item->m_ItemHealth, Item->m_ItemCount, SLOT_ARMOR_LEGGINGS, SLOT_ARMOR_LEGGINGS);
- }
- else if (ItemCategory::IsBoots(Item->m_ItemID))
- {
- Item->m_ItemCount -= MoveItem(Item->m_ItemID, Item->m_ItemHealth, Item->m_ItemCount, SLOT_ARMOR_BOOTS, SLOT_ARMOR_BOOTS);
- }
- else
- {
- Item->m_ItemCount -= MoveItem(Item->m_ItemID, Item->m_ItemHealth, Item->m_ItemCount, SLOT_HOTBAR_MIN, SLOT_HOTBAR_MAX);
- }
- SendSlot(a_Slot);
-}
-
-
-
-
diff --git a/source/cSurvivalInventory.h b/source/cSurvivalInventory.h
deleted file mode 100644
index 3ad385fcf..000000000
--- a/source/cSurvivalInventory.h
+++ /dev/null
@@ -1,48 +0,0 @@
-
-#pragma once
-
-#include "cInventory.h"
-
-
-
-
-
-class cSurvivalInventory //tolua_export
- : public cInventory
-{ //tolua_export
-
- enum
- {
- SLOT_CRAFTING_RESULT = 0,
- SLOT_CRAFTING_MIN = 1,
- SLOT_CRAFTING_MAX = 4,
- SLOT_ARMOR_MIN = 5,
- SLOT_ARMOR_HELMET = 5,
- SLOT_ARMOR_CHESTPLATE = 6,
- SLOT_ARMOR_LEGGINGS = 7,
- SLOT_ARMOR_BOOTS = 8,
- SLOT_ARMOR_MAX = 8,
- SLOT_INVENTORY_MIN = 9,
- SLOT_INVENTORY_MAX = 35,
- SLOT_HOTBAR_MIN = 36,
- SLOT_HOTBAR_MAX = 44,
- } ;
-
- void ShiftClickedCraftingResult(short a_SlotNum);
- void ShiftClickedCraftingGrid (short a_SlotNum);
- void ShiftClickedArmor (short a_Slot);
- void ShiftClickedHotbar (short a_Slot);
- void ShiftClickedInventory (short a_Slot);
-
-public:
- cSurvivalInventory(cPlayer* a_Owner);
- ~cSurvivalInventory();
-
- virtual void Clicked(short a_SlotNum, bool a_IsRightClick, bool a_IsShiftPressed, const cItem & a_HeldItem) override;
-
- void ShiftClicked(short a_SlotNum);
-}; //tolua_export
-
-
-
-
diff --git a/source/cWindow.cpp b/source/cWindow.cpp
deleted file mode 100644
index 5278122d5..000000000
--- a/source/cWindow.cpp
+++ /dev/null
@@ -1,374 +0,0 @@
-
-#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
-
-#include "cWindow.h"
-#include "cItem.h"
-#include "cClientHandle.h"
-#include "cPlayer.h"
-#include "cPickup.h"
-#include "cInventory.h"
-#include "cWindowOwner.h"
-#include "items/Item.h"
-
-
-
-
-
-cWindow::cWindow( cWindowOwner* a_Owner, bool a_bInventoryVisible, cWindow::WindowType a_WindowType, int a_WindowID)
- : m_WindowID( a_WindowID )
- , m_WindowType( a_WindowType )
- , m_Owner( a_Owner )
- , m_bInventoryVisible( a_bInventoryVisible )
- , m_NumSlots( 0 )
- , m_Slots( 0 )
- , m_DraggingItem( 0 )
- , m_IsDestroyed(false)
-{
- LOGD("Created a window at %p, type = %d, ID = %i", this, a_WindowType, a_WindowID);
- if (!m_bInventoryVisible)
- {
- m_DraggingItem = new cItem();
- }
-}
-
-
-
-
-
-cWindow::~cWindow()
-{
- LOGD("Deleting a window at %p", this);
- if( !m_bInventoryVisible && m_DraggingItem )
- {
- delete m_DraggingItem;
- m_DraggingItem = 0;
- }
- LOGD("Deleted a window at %p", this);
-}
-
-
-
-
-
-cItem* cWindow::GetSlot( int a_Slot )
-{
- if(a_Slot > -1 && a_Slot < m_NumSlots)
- {
- return (m_Slots + a_Slot);
- }
- return 0;
-}
-
-
-
-
-
-cItem* cWindow::GetDraggingItem( cPlayer * a_Player /* = 0 */ )
-{
- if( m_bInventoryVisible && a_Player )
- {
- cWindow* Window = a_Player->GetInventory().GetWindow();
- if( Window )
- {
- return Window->GetDraggingItem();
- }
- }
- return m_DraggingItem;
-}
-
-
-
-
-
-void cWindow::Clicked(
- cPlayer & a_Player,
- int a_WindowID, short a_SlotNum, bool a_IsRightClick, bool a_IsShiftPressed,
- const cItem & a_HeldItem
-)
-{
- if (a_WindowID != m_WindowID)
- {
- LOG("WRONG WINDOW ID! (exp %d, got %d) received from \"%s\"", m_WindowID, a_WindowID, a_Player.GetName().c_str());
- return;
- }
-
- if (m_bInventoryVisible)
- {
- cWindow * Window = a_Player.GetInventory().GetWindow();
- if (Window != NULL)
- {
- m_DraggingItem = Window->GetDraggingItem();
- }
- }
- bool bAsync = false;
- if (a_SlotNum == -999) // Outside window click
- {
- if (a_IsRightClick)
- {
- a_Player.TossItem(true);
- }
- else
- {
- a_Player.TossItem(true, m_DraggingItem->m_ItemCount);
- }
- }
- else if (GetSlot(a_SlotNum) != NULL)
- {
- cItem * Item = GetSlot(a_SlotNum);
- if (!Item->IsEqual(a_HeldItem))
- {
- LOGD("*** Window lost sync ***");
- LOGD("My Type: %i Their Type: %i", Item->m_ItemID, a_HeldItem.m_ItemID);
- LOGD("My Count: %i Their Count: %i", Item->m_ItemCount, a_HeldItem.m_ItemCount);
- LOGD("My Dmg: %i Their Dmg: %i", Item->m_ItemHealth, a_HeldItem.m_ItemHealth);
- bAsync = true;
- }
- }
- if (m_DraggingItem && (a_SlotNum > -1) && (a_SlotNum < m_NumSlots))
- {
- if (!a_IsRightClick)
- {
- // Left-clicked
- if (!m_DraggingItem->IsEqual(m_Slots[a_SlotNum]))
- {
- // Switch contents
- cItem tmp(*m_DraggingItem);
- *m_DraggingItem = m_Slots[a_SlotNum];
- m_Slots[a_SlotNum] = tmp;
- }
- else
- {
- // Same type, add items:
- cItemHandler * Handler = ItemHandler(m_DraggingItem->m_ItemID);
- int FreeSlots = Handler->GetMaxStackSize() - m_Slots[a_SlotNum].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 > m_DraggingItem->m_ItemCount) ? m_DraggingItem->m_ItemCount : FreeSlots;
- m_Slots[a_SlotNum].m_ItemCount += (char)Filling;
- m_DraggingItem->m_ItemCount -= (char)Filling;
- if (m_DraggingItem->m_ItemCount <= 0)
- {
- m_DraggingItem->Empty();
- }
- }
- }
- else
- {
- // Right clicked
- if (m_DraggingItem->m_ItemID <= 0) // Empty-handed?
- {
- m_DraggingItem->m_ItemCount = (char)(((float)m_Slots[a_SlotNum].m_ItemCount) / 2.f + 0.5f);
- m_Slots[a_SlotNum].m_ItemCount -= m_DraggingItem->m_ItemCount;
- m_DraggingItem->m_ItemID = m_Slots[a_SlotNum].m_ItemID;
- m_DraggingItem->m_ItemHealth = m_Slots[a_SlotNum].m_ItemHealth;
-
- if (m_Slots[a_SlotNum].m_ItemCount <= 0)
- {
- m_Slots[a_SlotNum].Empty();
- }
- }
- else if ((m_Slots[a_SlotNum].m_ItemID <= 0) || m_DraggingItem->IsEqual(m_Slots[a_SlotNum]))
- {
- // Drop one item in slot
- cItemHandler * Handler = ItemHandler(m_Slots[a_SlotNum].m_ItemID);
- if ((m_DraggingItem->m_ItemCount > 0) && (m_Slots[a_SlotNum].m_ItemCount < Handler->GetMaxStackSize()))
- {
- m_Slots[a_SlotNum].m_ItemID = m_DraggingItem->m_ItemID;
- m_Slots[a_SlotNum].m_ItemCount++;
- m_Slots[a_SlotNum].m_ItemHealth = m_DraggingItem->m_ItemHealth;
- m_DraggingItem->m_ItemCount--;
- }
- if (m_DraggingItem->m_ItemCount <= 0)
- {
- m_DraggingItem->Empty();
- }
- }
- else if (!m_DraggingItem->IsEqual(m_Slots[a_SlotNum]))
- {
- // Swap contents
- cItem tmp( *m_DraggingItem );
- *m_DraggingItem = m_Slots[a_SlotNum];
- m_Slots[a_SlotNum] = tmp;
- }
- }
- if (bAsync)
- {
- // TODO: Handle this thread-safely (m_OpenedBy may change by another cSocketThread
- for (cPlayerList::iterator itr = m_OpenedBy.begin(); itr != m_OpenedBy.end(); ++itr)
- {
- SendWholeWindow((*itr)->GetClientHandle());
- }
- if (m_bInventoryVisible || m_OpenedBy.empty())
- {
- a_Player.GetInventory().SendWholeInventory( a_Player.GetClientHandle() );
- }
- }
- }
- else if (m_bInventoryVisible) // Click in player inventory
- {
- cWindow * Window = a_Player.GetInventory().GetWindow();
- if (Window)
- {
- Window->Clicked(a_Player, a_WindowID, a_SlotNum - 9, a_IsRightClick, a_IsShiftPressed, a_HeldItem);
- }
- }
- if (m_DraggingItem != NULL)
- {
- LOGD("Dragging: %i", m_DraggingItem->m_ItemCount );
- }
-}
-
-
-
-
-
-void cWindow::Open( cPlayer & a_Player )
-{
- {
- cCSLock Lock(m_CS);
- // If player is already in OpenedBy remove player first
- m_OpenedBy.remove( &a_Player );
- // Then add player
- m_OpenedBy.push_back( &a_Player );
- }
-
- a_Player.GetClientHandle()->SendWindowOpen(m_WindowID, m_WindowType, m_WindowTitle, m_NumSlots);
-}
-
-
-
-
-
-void cWindow::Close( cPlayer & a_Player )
-{
- //Checks wheather the player is still holding an item
- if (m_DraggingItem && m_DraggingItem->m_ItemCount > 0)
- {
- LOGD("Player holds item! Dropping it...");
- a_Player.TossItem(true, m_DraggingItem->m_ItemCount);
- }
-
- cClientHandle * ClientHandle = a_Player.GetClientHandle();
- if (ClientHandle != NULL)
- {
- ClientHandle->SendWindowClose(m_WindowID);
- }
-
- {
- cCSLock Lock(m_CS);
- m_OpenedBy.remove( &a_Player );
- if( m_OpenedBy.size() == 0 )
- {
- Destroy();
- }
- }
- if (m_IsDestroyed)
- {
- delete this;
- }
-}
-
-
-
-
-
-void cWindow::OwnerDestroyed()
-{
- m_Owner = 0;
- while( m_OpenedBy.size() > 1 )
- {
- (*m_OpenedBy.begin() )->CloseWindow((char)GetWindowType());
- }
- (*m_OpenedBy.begin() )->CloseWindow((char)GetWindowType());
-}
-
-
-
-
-
-bool cWindow::ForEachPlayer(cItemCallback<cPlayer> & a_Callback)
-{
- cCSLock Lock(m_CS);
- for (cPlayerList::iterator itr = m_OpenedBy.begin(), end = m_OpenedBy.end(); itr != end; ++itr)
- {
- if (a_Callback.Item(*itr))
- {
- return false;
- }
- } // for itr - m_OpenedBy[]
- return true;
-}
-
-
-
-
-
-bool cWindow::ForEachClient(cItemCallback<cClientHandle> & a_Callback)
-{
- cCSLock Lock(m_CS);
- for (cPlayerList::iterator itr = m_OpenedBy.begin(), end = m_OpenedBy.end(); itr != end; ++itr)
- {
- if (a_Callback.Item((*itr)->GetClientHandle()))
- {
- return false;
- }
- } // for itr - m_OpenedBy[]
- return true;
-}
-
-
-
-
-
-void cWindow::Destroy()
-{
- LOGD("Destroying window %p (type %d)", this, m_WindowType);
- if (m_Owner != NULL)
- {
- m_Owner->CloseWindow();
- m_Owner = NULL;
- }
- m_IsDestroyed = true;
-}
-
-
-
-
-
-void cWindow::SendWholeWindow(cClientHandle * a_Client )
-{
- a_Client->SendWholeInventory(*this);
-}
-
-
-
-
-
-void cWindow::BroadcastWholeWindow(void)
-{
- cCSLock Lock(m_CS);
- for (cPlayerList::iterator itr = m_OpenedBy.begin(); itr != m_OpenedBy.end(); ++itr)
- {
- SendWholeWindow((*itr)->GetClientHandle());
- } // for itr - m_OpenedBy[]
-}
-
-
-
-
-
-void cWindow::BroadcastInventoryProgress(short a_Progressbar, short a_Value)
-{
- cCSLock Lock(m_CS);
- for (cPlayerList::iterator itr = m_OpenedBy.begin(); itr != m_OpenedBy.end(); ++itr)
- {
- (*itr)->GetClientHandle()->SendInventoryProgress(m_WindowID, a_Progressbar, a_Value);
- } // for itr - m_OpenedBy[]
-}
-
-
-
-
diff --git a/source/cWindow.h b/source/cWindow.h
deleted file mode 100644
index 77dc608a4..000000000
--- a/source/cWindow.h
+++ /dev/null
@@ -1,109 +0,0 @@
-
-// cWindow.h
-
-// Interfaces to the cWindow class representing a UI window for a specific block
-
-
-
-
-
-#pragma once
-
-
-class cPlayer;
-class cItem;
-class cWindowOwner;
-class cClientHandle;
-
-typedef std::list<cPlayer *> cPlayerList;
-
-
-
-
-
-/**
-Represents a UI window (base class) for a specific block entity.
-
-There is up to one instance of the class for each block entity
-Each window has a list of players that are currently using it
-When there's no player using a window, it is destroyed
-*/
-class cWindow
-{
-public:
- enum WindowType
- {
- Inventory = -1, // This value is never actually sent to a client
- Chest = 0,
- Workbench = 1,
- Furnace = 2,
- Dispenser = 3,
- Enchantment = 4,
- Brewery = 5
- };
-
- cWindow(cWindowOwner * a_Owner, bool a_bInventoryVisible, WindowType a_WindowType, int a_WindowID);
- ~cWindow();
-
- int GetWindowID(void) const { return m_WindowID; }
- int GetWindowType(void) const { return m_WindowType; }
-
- cItem* GetSlots(void) const { return m_Slots; }
- int GetNumSlots(void) const { return m_NumSlots; }
-
- cItem* GetSlot( int a_Slot );
-
- cItem* GetDraggingItem( cPlayer * a_Player = 0 );
-
- // a_Slots is an array of slots of size a_NumSlots
- void SetSlots(cItem* a_Slots, int a_NumSlots) { m_Slots = a_Slots; m_NumSlots = a_NumSlots; }
-
- bool IsInventoryVisible() { return m_bInventoryVisible; }
- void SetInventoryVisible( bool a_bVisible ) { m_bInventoryVisible = a_bVisible; }
-
- virtual void Clicked(
- cPlayer & a_Player, int a_WindowID,
- short a_SlotNum, bool a_IsRightClick, bool a_IsShiftPressed,
- const cItem & a_HeldItem
- );
-
- virtual void Open( cPlayer & a_Player );
- virtual void Close( cPlayer & a_Player );
-
- cWindowOwner* GetOwner() { return m_Owner; }
- void SetOwner( cWindowOwner* a_Owner ) { m_Owner = a_Owner; }
-
- void SendWholeWindow(cClientHandle * a_Client);
- void BroadcastWholeWindow(void);
- void BroadcastInventoryProgress(short a_Progressbar, short a_Value);
-
- const AString & GetWindowTitle() const { return m_WindowTitle; }
- void SetWindowTitle( const std::string & a_WindowTitle ) { m_WindowTitle = a_WindowTitle; }
-
- void OwnerDestroyed(void);
-
- /// Calls the callback safely for each player that has this window open; returns true if all players have been enumerated
- bool ForEachPlayer(cItemCallback<cPlayer> & a_Callback);
-
- /// Calls the callback safely for each client that has this window open; returns true if all clients have been enumerated
- bool ForEachClient(cItemCallback<cClientHandle> & a_Callback);
-
-private:
-
- void Destroy();
-
- char m_WindowID;
- int m_WindowType;
- AString m_WindowTitle;
-
- cWindowOwner * m_Owner;
-
- cCriticalSection m_CS;
- cPlayerList m_OpenedBy;
-
- bool m_bInventoryVisible;
- int m_NumSlots;
- cItem * m_Slots;
- cItem * m_DraggingItem;
- bool m_IsDestroyed;
-}; \ No newline at end of file