1
0
Fork 0

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:
madmaxoft@gmail.com 2012-09-20 13:25:54 +00:00
parent 2b35b9a724
commit bc466f07a4
35 changed files with 1574 additions and 1565 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,7 +5,6 @@
#include "cServer.h"
#include "cClientHandle.h"
#include "cSleep.h"
#include "cTimer.h"
#include "cMonster.h"
#include "cSocket.h"

View File

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

View File

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

View File

@ -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[]
}

View File

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