UI: Added shift-click support to most slot areas, except crafting.
Also fixed survival inventory's crafting grid not working. git-svn-id: http://mc-server.googlecode.com/svn/trunk@868 0a769ca7-a7f5-676a-18bf-c427514a06d6master
parent
bc466f07a4
commit
fdf8387092
|
@ -572,12 +572,13 @@ void cProtocol132::WriteItem(const cItem & a_Item)
|
|||
ItemType = -1;
|
||||
}
|
||||
|
||||
WriteShort(ItemType);
|
||||
if (a_Item.IsEmpty())
|
||||
{
|
||||
WriteShort(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
WriteShort(ItemType);
|
||||
WriteByte (a_Item.m_ItemCount);
|
||||
WriteShort(a_Item.m_ItemDamage);
|
||||
|
||||
|
|
|
@ -48,6 +48,17 @@ void cSlotArea::Clicked(cPlayer & a_Player, int a_SlotNum, bool a_IsRightClick,
|
|||
return;
|
||||
}
|
||||
|
||||
if (a_IsShiftPressed)
|
||||
{
|
||||
if (!a_Player.IsDraggingItem())
|
||||
{
|
||||
ShiftClicked(a_Player, a_SlotNum, a_ClickedItem);
|
||||
return;
|
||||
}
|
||||
LOGD("Shift clicked, but the player is draggint an item: %s", ItemToFullString(a_Player.GetDraggingItem()));
|
||||
return;
|
||||
}
|
||||
|
||||
cItem Slot(*GetSlot(a_SlotNum, a_Player));
|
||||
if (!Slot.IsEqual(a_ClickedItem))
|
||||
{
|
||||
|
@ -138,31 +149,57 @@ void cSlotArea::Clicked(cPlayer & a_Player, int a_SlotNum, bool a_IsRightClick,
|
|||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cSlotAreaArmor:
|
||||
|
||||
cSlotAreaArmor::cSlotAreaArmor(cWindow & a_ParentWindow) :
|
||||
cSlotArea(4, a_ParentWindow)
|
||||
void cSlotArea::ShiftClicked(cPlayer & a_Player, int a_SlotNum, const cItem & a_ClickedItem)
|
||||
{
|
||||
// Make a copy of the slot, distribute it among the other areas, then update the slot to contain the leftover:
|
||||
cItem Slot(*GetSlot(a_SlotNum, a_Player));
|
||||
m_ParentWindow.DistributeStack(Slot, a_Player, this, true);
|
||||
if (Slot.IsEmpty())
|
||||
{
|
||||
// Empty the slot completely, the cilent doesn't like left-over ItemType with zero count
|
||||
Slot.Empty();
|
||||
}
|
||||
SetSlot(a_SlotNum, a_Player, Slot);
|
||||
|
||||
// Some clients try to guess our actions and not always right (armor slots in 1.2.5), so we fix them:
|
||||
m_ParentWindow.BroadcastWholeWindow();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const cItem * cSlotAreaArmor::GetSlot(int a_SlotNum, cPlayer & a_Player)
|
||||
void cSlotArea::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_Apply, bool a_KeepEmptySlots)
|
||||
{
|
||||
// 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;
|
||||
for (int i = 0; i < m_NumSlots; i++)
|
||||
{
|
||||
const cItem * Slot = GetSlot(i, a_Player);
|
||||
if (!Slot->IsSameType(a_ItemStack) && (!Slot->IsEmpty() || a_KeepEmptySlots))
|
||||
{
|
||||
// Different items
|
||||
continue;
|
||||
}
|
||||
int NumFit = ItemHandler(Slot->m_ItemType)->GetMaxStackSize() - Slot->m_ItemCount;
|
||||
if (NumFit <= 0)
|
||||
{
|
||||
// Full stack already
|
||||
continue;
|
||||
}
|
||||
if (NumFit > a_ItemStack.m_ItemCount)
|
||||
{
|
||||
NumFit = a_ItemStack.m_ItemCount;
|
||||
}
|
||||
if (a_Apply)
|
||||
{
|
||||
cItem NewSlot(a_ItemStack.m_ItemType, Slot->m_ItemCount + NumFit, a_ItemStack.m_ItemDamage);
|
||||
SetSlot(i, a_Player, NewSlot);
|
||||
}
|
||||
a_ItemStack.m_ItemCount -= NumFit;
|
||||
if (a_ItemStack.IsEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
} // for i - Slots
|
||||
}
|
||||
|
||||
|
||||
|
@ -390,10 +427,11 @@ void cSlotAreaFurnace::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem &
|
|||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cSlotAreaInventory:
|
||||
// cSlotAreaInventoryBase:
|
||||
|
||||
cSlotAreaInventory::cSlotAreaInventory(cWindow & a_ParentWindow) :
|
||||
cSlotArea(27 + 9, a_ParentWindow) // 27 internal slots, 9 hotbar slots
|
||||
cSlotAreaInventoryBase::cSlotAreaInventoryBase(int a_NumSlots, int a_SlotOffset, cWindow & a_ParentWindow) :
|
||||
cSlotArea(a_NumSlots, a_ParentWindow),
|
||||
m_SlotOffset(a_SlotOffset)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -401,7 +439,7 @@ cSlotAreaInventory::cSlotAreaInventory(cWindow & a_ParentWindow) :
|
|||
|
||||
|
||||
|
||||
void cSlotAreaInventory::Clicked(cPlayer & a_Player, int a_SlotNum, bool a_IsRightClick, bool a_IsShiftPressed, const cItem & a_ClickedItem)
|
||||
void cSlotAreaInventoryBase::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))
|
||||
{
|
||||
|
@ -419,19 +457,62 @@ void cSlotAreaInventory::Clicked(cPlayer & a_Player, int a_SlotNum, bool a_IsRig
|
|||
|
||||
|
||||
|
||||
const cItem * cSlotAreaInventory::GetSlot(int a_SlotNum, cPlayer & a_Player)
|
||||
const cItem * cSlotAreaInventoryBase::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);
|
||||
return a_Player.GetInventory().GetSlot(a_SlotNum + m_SlotOffset);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cSlotAreaInventory::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
|
||||
void cSlotAreaInventoryBase::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
|
||||
{
|
||||
*(a_Player.GetInventory().GetSlot(a_SlotNum + 9)) = a_Item;
|
||||
*(a_Player.GetInventory().GetSlot(a_SlotNum + m_SlotOffset)) = a_Item;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cSlotAreaArmor:
|
||||
|
||||
void cSlotAreaArmor::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots)
|
||||
{
|
||||
if (ItemCategory::IsHelmet(a_ItemStack.m_ItemType) && GetSlot(0, a_Player)->IsEmpty())
|
||||
{
|
||||
if (a_ShouldApply)
|
||||
{
|
||||
SetSlot(0, a_Player, cItem(a_ItemStack.m_ItemType, 1, a_ItemStack.m_ItemDamage));
|
||||
}
|
||||
a_ItemStack.m_ItemCount -= 1;
|
||||
}
|
||||
else if (ItemCategory::IsChestPlate(a_ItemStack.m_ItemType) && GetSlot(1, a_Player)->IsEmpty())
|
||||
{
|
||||
if (a_ShouldApply)
|
||||
{
|
||||
SetSlot(1, a_Player, cItem(a_ItemStack.m_ItemType, 1, a_ItemStack.m_ItemDamage));
|
||||
}
|
||||
a_ItemStack.m_ItemCount -= 1;
|
||||
}
|
||||
else if (ItemCategory::IsLeggings(a_ItemStack.m_ItemType) && GetSlot(2, a_Player)->IsEmpty())
|
||||
{
|
||||
if (a_ShouldApply)
|
||||
{
|
||||
SetSlot(2, a_Player, cItem(a_ItemStack.m_ItemType, 1, a_ItemStack.m_ItemDamage));
|
||||
}
|
||||
a_ItemStack.m_ItemCount -= 1;
|
||||
}
|
||||
else if (ItemCategory::IsBoots(a_ItemStack.m_ItemType) && GetSlot(3, a_Player)->IsEmpty())
|
||||
{
|
||||
if (a_ShouldApply)
|
||||
{
|
||||
SetSlot(3, a_Player, cItem(a_ItemStack.m_ItemType, 1, a_ItemStack.m_ItemDamage));
|
||||
}
|
||||
a_ItemStack.m_ItemCount -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -38,12 +38,23 @@ public:
|
|||
/// 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 from Clicked if it is a valid shiftclick
|
||||
virtual void ShiftClicked(cPlayer & a_Player, int a_SlotNum, const cItem & a_ClickedItem);
|
||||
|
||||
/// Called when a new player opens the same parent window. The window already tracks the player. CS-locked.
|
||||
virtual void OnPlayerAdded(cPlayer & a_Player) {} ;
|
||||
|
||||
/// Called when one of the players closes the parent window. The window already doesn't track the player. CS-locked.
|
||||
virtual void OnPlayerRemoved(cPlayer & a_Player) {} ;
|
||||
|
||||
/** Called to store as much of a_ItemStack in the area as possible. a_ItemStack is modified to reflect the change.
|
||||
The default implementation searches each slot for available space and distributes the stack there.
|
||||
if a_ShouldApply is true, the changes are written into the slots;
|
||||
if a_ShouldApply is false, only a_ItemStack is modified to reflect the number of fits (for fit-testing purposes)
|
||||
If a_KeepEmptySlots is true, empty slots will be skipped and won't be filled
|
||||
*/
|
||||
virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots);
|
||||
|
||||
protected:
|
||||
int m_NumSlots;
|
||||
cWindow & m_ParentWindow;
|
||||
|
@ -53,19 +64,75 @@ protected:
|
|||
|
||||
|
||||
|
||||
class cSlotAreaInventory :
|
||||
/// Handles any part of the inventory, using parameters in constructor to distinguish between the parts
|
||||
class cSlotAreaInventoryBase :
|
||||
public cSlotArea
|
||||
{
|
||||
typedef cSlotArea super;
|
||||
|
||||
public:
|
||||
cSlotAreaInventory(cWindow & a_ParentWindow);
|
||||
cSlotAreaInventoryBase(int a_NumSlots, int a_SlotOffset, cWindow & a_ParentWindow);
|
||||
|
||||
// Creative inventory's click handling is somewhat different from survival inventory's, handle that here:
|
||||
virtual void Clicked(cPlayer & a_Player, int a_SlotNum, 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:
|
||||
int m_SlotOffset; // Index that this area's slot 0 has in the underlying cInventory
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Handles the "inner" inventory of each player, excluding the armor and hotbar
|
||||
class cSlotAreaInventory :
|
||||
public cSlotAreaInventoryBase
|
||||
{
|
||||
typedef cSlotAreaInventoryBase super;
|
||||
|
||||
public:
|
||||
cSlotAreaInventory(cWindow & a_ParentWindow) :
|
||||
cSlotAreaInventoryBase(27, 9, a_ParentWindow) // 27 slots, starting at inventory index 9
|
||||
{
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Handles the "outer" inevntory of each player - the hotbar
|
||||
class cSlotAreaHotBar :
|
||||
public cSlotAreaInventoryBase
|
||||
{
|
||||
typedef cSlotAreaInventoryBase super;
|
||||
|
||||
public:
|
||||
cSlotAreaHotBar(cWindow & a_ParentWindow) :
|
||||
cSlotAreaInventoryBase(9, 36, a_ParentWindow)
|
||||
{
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Handles the armor area of the inventory
|
||||
class cSlotAreaArmor :
|
||||
public cSlotAreaInventoryBase
|
||||
{
|
||||
public:
|
||||
cSlotAreaArmor(cWindow & a_ParentWindow) :
|
||||
cSlotAreaInventoryBase(4, 5, a_ParentWindow)
|
||||
{
|
||||
}
|
||||
|
||||
// Distributing the stack is allowed only for compatible items (helmets into helmet slot etc.)
|
||||
virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override;
|
||||
} ;
|
||||
|
||||
|
||||
|
@ -119,6 +186,9 @@ public:
|
|||
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;
|
||||
|
||||
// Distributing items into this area is completely disabled
|
||||
virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override {}
|
||||
|
||||
protected:
|
||||
/// Maps player's EntityID -> current recipe; not a std::map because cCraftingGrid needs proper constructor params
|
||||
typedef std::list<std::pair<int, cCraftingRecipe> > cRecipeMap;
|
||||
|
@ -176,17 +246,3 @@ protected:
|
|||
|
||||
|
||||
|
||||
|
||||
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;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -256,7 +256,45 @@ bool cWindow::ForEachClient(cItemCallback<cClientHandle> & a_Callback)
|
|||
|
||||
|
||||
|
||||
void cWindow::Destroy()
|
||||
void cWindow::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, cSlotArea * a_ExcludeArea, bool a_ShouldApply)
|
||||
{
|
||||
// Ask each slot area to take as much of the stack as it can.
|
||||
// First ask only slots that already have the same kind of item
|
||||
// Then ask any remaining slots
|
||||
for (int Pass = 0; Pass < 2; ++Pass)
|
||||
{
|
||||
// First distribute into the hotbar:
|
||||
if (a_ExcludeArea != m_SlotAreas.back())
|
||||
{
|
||||
m_SlotAreas.back()->DistributeStack(a_ItemStack, a_Player, a_ShouldApply, (Pass == 0));
|
||||
if (a_ItemStack.IsEmpty())
|
||||
{
|
||||
// Distributed it all
|
||||
return;
|
||||
}
|
||||
}
|
||||
// The distribute to all other areas:
|
||||
for (cSlotAreas::iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end() - 1; itr != end; ++itr)
|
||||
{
|
||||
if (*itr == a_ExcludeArea)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
(*itr)->DistributeStack(a_ItemStack, a_Player, a_ShouldApply, (Pass == 0));
|
||||
if (a_ItemStack.IsEmpty())
|
||||
{
|
||||
// Distributed it all
|
||||
return;
|
||||
}
|
||||
} // for itr - m_SlotAreas[]
|
||||
} // for Pass - repeat twice
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cWindow::Destroy(void)
|
||||
{
|
||||
LOGD("Destroying window %p (type %d)", this, m_WindowType);
|
||||
if (m_Owner != NULL)
|
||||
|
@ -316,6 +354,7 @@ cInventoryWindow::cInventoryWindow(cPlayer & 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));
|
||||
m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
|
||||
}
|
||||
|
||||
|
||||
|
@ -330,6 +369,7 @@ cCraftingWindow::cCraftingWindow(int a_BlockX, int a_BlockY, int a_BlockZ) :
|
|||
{
|
||||
m_SlotAreas.push_back(new cSlotAreaCrafting(3, *this));
|
||||
m_SlotAreas.push_back(new cSlotAreaInventory(*this));
|
||||
m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
|
||||
}
|
||||
|
||||
|
||||
|
@ -351,6 +391,7 @@ cChestWindow::cChestWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cChestEntit
|
|||
// TODO: Double chests
|
||||
|
||||
m_SlotAreas.push_back(new cSlotAreaInventory(*this));
|
||||
m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
|
||||
|
||||
// Send out the chest-open packet:
|
||||
m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 1, E_BLOCK_CHEST);
|
||||
|
@ -378,6 +419,7 @@ cFurnaceWindow::cFurnaceWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnac
|
|||
{
|
||||
m_SlotAreas.push_back(new cSlotAreaFurnace(a_Furnace, *this));
|
||||
m_SlotAreas.push_back(new cSlotAreaInventory(*this));
|
||||
m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -93,6 +93,12 @@ public:
|
|||
/// 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);
|
||||
|
||||
/** Called on shift-clicking to distribute the stack into other areas; Modifies a_ItemStack as it is distributed!
|
||||
if a_ShouldApply is true, the changes are written into the slots;
|
||||
if a_ShouldApply is false, only a_ItemStack is modified to reflect the number of fits (for fit-testing purposes)
|
||||
*/
|
||||
void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, cSlotArea * a_ExcludeArea, bool a_ShouldApply);
|
||||
|
||||
protected:
|
||||
cSlotAreas m_SlotAreas;
|
||||
|
||||
|
|
|
@ -64,6 +64,7 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
|
|||
|
||||
m_InventoryWindow = new cInventoryWindow(*this);
|
||||
m_CurrentWindow = m_InventoryWindow;
|
||||
m_InventoryWindow->OpenedByPlayer(*this);
|
||||
|
||||
SetMaxHealth(20);
|
||||
m_MaxFoodLevel = 20;
|
||||
|
@ -110,6 +111,8 @@ cPlayer::~cPlayer(void)
|
|||
|
||||
m_ClientHandle = NULL;
|
||||
|
||||
delete m_InventoryWindow;
|
||||
|
||||
LOG("Player %p deleted", this);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue