Refactored windows.
As described on the forum: http://forum.mc-server.org/showthread.php?tid=561 For now, only basic clicking works; shift-click not implemented yet. git-svn-id: http://mc-server.googlecode.com/svn/trunk@867 0a769ca7-a7f5-676a-18bf-c427514a06d6
This commit is contained in:
parent
2b35b9a724
commit
bc466f07a4
@ -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>
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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])
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
569
source/UI/SlotArea.cpp
Normal file
569
source/UI/SlotArea.cpp
Normal file
@ -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]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
192
source/UI/SlotArea.h
Normal file
192
source/UI/SlotArea.h
Normal file
@ -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;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
385
source/UI/cWindow.cpp
Normal file
385
source/UI/cWindow.cpp
Normal file
@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
172
source/UI/cWindow.h
Normal file
172
source/UI/cWindow.h
Normal file
@ -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;
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
@ -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));
|
||||
|
@ -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:
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
};
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
m_Player->GetInventory().Clicked(a_SlotNum, false, false, a_HeldItem);
|
||||
}
|
||||
else
|
||||
if (m_Player->GetGameMode() != eGameMode_Creative)
|
||||
{
|
||||
LOGWARNING("Got a CreativeInventoryAction packet from user \"%s\" while not in creative mode. Ignoring.", m_Username.c_str());
|
||||
return;
|
||||
}
|
||||
if (m_Player->GetWindow()->GetWindowType() != cWindow::Inventory)
|
||||
{
|
||||
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)
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
@ -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 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
@ -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)
|
||||
void cInventory::SendWholeInventory(cClientHandle & a_Client)
|
||||
{
|
||||
a_Client->SendWholeInventory(*this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cInventory::SendWholeInventoryToAll(void)
|
||||
{
|
||||
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 );
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -5,7 +5,6 @@
|
||||
|
||||
#include "cServer.h"
|
||||
#include "cClientHandle.h"
|
||||
#include "cSleep.h"
|
||||
#include "cTimer.h"
|
||||
#include "cMonster.h"
|
||||
#include "cSocket.h"
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
@ -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[]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
109
source/cWindow.h
109
source/cWindow.h
@ -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;
|
||||
};
|
Loading…
Reference in New Issue
Block a user