2012-09-20 13:25:54 +00:00
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
2012-09-23 17:19:34 +00:00
#include "Window.h"
2013-05-28 19:12:47 +00:00
#include "WindowOwner.h"
#include "SlotArea.h"
2012-09-23 22:09:57 +00:00
#include "../Item.h"
#include "../ClientHandle.h"
2013-08-19 11:39:13 +02:00
#include "../Entities/Player.h"
#include "../Entities/Pickup.h"
2012-09-23 22:09:57 +00:00
#include "../Inventory.h"
2012-09-29 13:59:32 +00:00
#include "../Items/ItemHandler.h"
2014-07-30 21:59:35 +02:00
#include "../BlockEntities/BeaconEntity.h"
2013-05-28 19:12:47 +00:00
#include "../BlockEntities/ChestEntity.h"
2013-06-16 20:24:07 +00:00
#include "../BlockEntities/DropSpenserEntity.h"
2013-12-07 00:18:58 +00:00
#include "../BlockEntities/EnderChestEntity.h"
2013-06-13 07:36:43 +00:00
#include "../BlockEntities/HopperEntity.h"
2014-01-23 00:27:39 -07:00
#include "../Root.h"
#include "../Bindings/PluginManager.h"
2012-09-20 13:25:54 +00:00
char cWindow :: m_WindowIDCounter = 1 ;
2013-10-28 13:30:24 +01:00
cWindow :: cWindow ( WindowType a_WindowType , const AString & a_WindowTitle ) :
2013-05-30 08:40:13 +00:00
m_WindowID (( ++ m_WindowIDCounter ) % 127 ),
m_WindowType ( a_WindowType ),
m_WindowTitle ( a_WindowTitle ),
m_IsDestroyed ( false ),
2013-12-07 23:36:50 +00:00
m_ShouldDistributeToHotbarFirst ( true ),
m_Owner ( NULL )
2012-09-20 13:25:54 +00:00
{
2013-10-28 13:30:24 +01:00
if ( a_WindowType == wtInventory )
2012-09-20 13:25:54 +00:00
{
m_WindowID = 0 ;
}
}
cWindow ::~ cWindow ()
{
2012-09-29 19:55:18 +00:00
for ( cSlotAreas :: iterator itr = m_SlotAreas . begin (), end = m_SlotAreas . end (); itr != end ; ++ itr )
{
delete * itr ;
}
m_SlotAreas . clear ();
2012-09-20 13:25:54 +00:00
}
2014-09-11 23:17:27 +02:00
const AString cWindow :: GetWindowTypeName ( void ) const
{
switch ( m_WindowType )
{
case wtChest : return "minecraft:chest" ;
case wtWorkbench : return "minecraft:crafting_table" ;
case wtFurnace : return "minecraft:furnace" ;
case wtDropSpenser : return "minecraft:dispenser" ;
case wtEnchantment : return "minecraft:enchanting_table" ;
case wtBrewery : return "minecraft:brewing_stand" ;
case wtNPCTrade : return "minecraft:villager" ;
case wtBeacon : return "minecraft:beacon" ;
case wtAnvil : return "minecraft:anvil" ;
case wtHopper : return "minecraft:hopper" ;
case wtDropper : return "minecraft:dropper" ;
case wtAnimalChest : return "EntityHorse" ;
default :
{
ASSERT ( ! "Unknown inventory type!" );
return "" ;
}
}
}
2012-09-20 13:25:54 +00:00
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 ;
}
2013-05-30 08:40:13 +00:00
const cItem * cWindow :: GetSlot ( cPlayer & a_Player , int a_SlotNum ) const
{
// Return the item at the specified slot for the specified player
int LocalSlotNum = 0 ;
const cSlotArea * Area = GetSlotArea ( a_SlotNum , LocalSlotNum );
if ( Area == NULL )
{
LOGWARNING ( "%s: requesting item from an invalid SlotArea (SlotNum %d), returning NULL." , __FUNCTION__ , a_SlotNum );
return NULL ;
}
return Area -> GetSlot ( LocalSlotNum , a_Player );
}
void cWindow :: SetSlot ( cPlayer & a_Player , int a_SlotNum , const cItem & a_Item )
{
// Set the item to the specified slot for the specified player
int LocalSlotNum = 0 ;
cSlotArea * Area = GetSlotArea ( a_SlotNum , LocalSlotNum );
if ( Area == NULL )
{
LOGWARNING ( "%s: requesting write to an invalid SlotArea (SlotNum %d), ignoring." , __FUNCTION__ , a_SlotNum );
return ;
}
Area -> SetSlot ( LocalSlotNum , a_Player , a_Item );
}
2013-05-30 19:34:09 +00:00
bool cWindow :: IsSlotInPlayerMainInventory ( int a_SlotNum ) const
{
// Returns true if the specified slot is in the Player Main Inventory slotarea
// The player main inventory is always 27 slots, 9 slots from the end of the inventory
return (( a_SlotNum >= GetNumSlots () - 36 ) && ( a_SlotNum < GetNumSlots () - 9 ));
}
bool cWindow :: IsSlotInPlayerHotbar ( int a_SlotNum ) const
{
// Returns true if the specified slot is in the Player Hotbar slotarea
// The hotbar is always the last 9 slots
return (( a_SlotNum >= GetNumSlots () - 9 ) && ( a_SlotNum < GetNumSlots ()));
}
bool cWindow :: IsSlotInPlayerInventory ( int a_SlotNum ) const
{
// Returns true if the specified slot is in the Player Main Inventory or Hotbar slotareas. Note that returns false for Armor.
// The player combined inventory is always the last 36 slots
return (( a_SlotNum >= GetNumSlots () - 36 ) && ( a_SlotNum < GetNumSlots ()));
}
2012-09-20 13:25:54 +00:00
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 (
2014-07-17 22:50:58 +02:00
cPlayer & a_Player ,
2013-05-08 09:45:07 +00:00
int a_WindowID , short a_SlotNum , eClickAction a_ClickAction ,
2012-09-20 13:25:54 +00:00
const cItem & a_ClickedItem
)
{
2014-07-01 14:47:49 +02:00
cPluginManager * PlgMgr = cRoot :: Get () -> GetPluginManager ();
2012-09-20 13:25:54 +00:00
if ( a_WindowID != m_WindowID )
{
2013-05-30 08:40:13 +00:00
LOGWARNING ( "%s: Wrong window ID (exp %d, got %d) received from \" %s \" ; ignoring click." , __FUNCTION__ , m_WindowID , a_WindowID , a_Player . GetName (). c_str ());
2012-09-20 13:25:54 +00:00
return ;
}
2013-05-08 09:45:07 +00:00
switch ( a_ClickAction )
2012-09-20 13:25:54 +00:00
{
2014-07-06 00:40:59 +02:00
case caLeftClickOutside :
2013-05-08 09:45:07 +00:00
case caRightClickOutside :
2012-09-20 13:25:54 +00:00
{
2014-01-23 20:11:10 -07:00
if ( PlgMgr -> CallHookPlayerTossingItem ( a_Player ))
{
// A plugin doesn't agree with the tossing. The plugin itself is responsible for handling the consequences (possible inventory mismatch)
return ;
}
if ( a_Player . IsGameModeCreative ())
2014-01-23 00:27:39 -07:00
{
2014-01-23 20:11:10 -07:00
a_Player . TossPickup ( a_ClickedItem );
2014-01-23 00:27:39 -07:00
}
2014-07-06 00:40:59 +02:00
if ( a_ClickAction == caLeftClickOutside )
2014-01-23 00:27:39 -07:00
{
2014-07-06 00:40:59 +02:00
// Toss all dragged items:
a_Player . TossHeldItem ( a_Player . GetDraggingItem (). m_ItemCount );
2014-01-23 00:27:39 -07:00
}
2014-07-06 00:40:59 +02:00
else
2014-01-23 20:11:10 -07:00
{
2014-07-06 00:40:59 +02:00
// Toss one of the dragged items:
a_Player . TossHeldItem ();
2014-01-23 20:11:10 -07:00
}
2013-05-08 09:45:07 +00:00
return ;
}
case caLeftClickOutsideHoldNothing :
case caRightClickOutsideHoldNothing :
{
// Nothing needed
return ;
2012-09-20 13:25:54 +00:00
}
2013-05-30 08:40:13 +00:00
case caLeftPaintBegin : OnPaintBegin ( a_Player ); return ;
case caRightPaintBegin : OnPaintBegin ( a_Player ); return ;
case caLeftPaintProgress : OnPaintProgress ( a_Player , a_SlotNum ); return ;
case caRightPaintProgress : OnPaintProgress ( a_Player , a_SlotNum ); return ;
case caLeftPaintEnd : OnLeftPaintEnd ( a_Player ); return ;
case caRightPaintEnd : OnRightPaintEnd ( a_Player ); return ;
2013-12-07 23:42:43 +00:00
default :
{
break ;
}
2013-05-08 09:45:07 +00:00
}
if ( a_SlotNum < 0 )
{
// TODO: Other click actions with irrelevant slot number (FS #371)
2012-09-20 13:25:54 +00:00
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 ())
{
2013-05-08 09:45:07 +00:00
( * itr ) -> Clicked ( a_Player , LocalSlotNum , a_ClickAction , a_ClickedItem );
2012-09-20 13:25:54 +00:00
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[]
}
2013-11-08 21:32:14 +01:00
a_Player . GetClientHandle () -> SendWindowOpen ( * this );
2012-09-20 13:25:54 +00:00
}
2013-06-02 21:59:25 +00:00
bool cWindow :: ClosedByPlayer ( cPlayer & a_Player , bool a_CanRefuse )
2012-09-20 13:25:54 +00:00
{
// Checks whether the player is still holding an item
2014-06-04 18:59:56 +01:00
if ( ! a_Player . GetDraggingItem (). IsEmpty ())
2012-09-20 13:25:54 +00:00
{
LOGD ( "Player holds item! Dropping it..." );
2014-01-23 20:11:10 -07:00
a_Player . TossHeldItem ( a_Player . GetDraggingItem (). m_ItemCount );
2012-09-20 13:25:54 +00:00
}
cClientHandle * ClientHandle = a_Player . GetClientHandle ();
if ( ClientHandle != NULL )
{
2013-04-04 11:47:31 +00:00
ClientHandle -> SendWindowClose ( * this );
2012-09-20 13:25:54 +00:00
}
{
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 );
2013-04-04 11:47:31 +00:00
2013-10-28 13:30:24 +01:00
if (( m_WindowType != wtInventory ) && m_OpenedBy . empty ())
2012-09-20 13:25:54 +00:00
{
Destroy ();
}
}
if ( m_IsDestroyed )
{
delete this ;
}
2013-05-30 20:40:43 +00:00
return true ;
2012-09-20 13:25:54 +00:00
}
void cWindow :: OwnerDestroyed ()
{
m_Owner = NULL ;
// Close window for each player. Note that the last one needs special handling
while ( m_OpenedBy . size () > 1 )
{
2014-07-21 15:19:48 +02:00
( * m_OpenedBy . begin ()) -> CloseWindow ();
2012-09-20 13:25:54 +00:00
}
2014-07-21 15:19:48 +02:00
( * m_OpenedBy . begin ()) -> CloseWindow ();
2012-09-20 13:25:54 +00:00
}
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 ;
}
2012-09-20 20:10:46 +00:00
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 )
{
2013-01-08 05:01:06 +00:00
if ( m_ShouldDistributeToHotbarFirst )
2012-09-20 20:10:46 +00:00
{
2013-01-08 05:01:06 +00:00
// First distribute into the hotbar:
if ( a_ExcludeArea != m_SlotAreas . back ())
2012-09-20 20:10:46 +00:00
{
2013-01-08 05:01:06 +00:00
m_SlotAreas . back () -> DistributeStack ( a_ItemStack , a_Player , a_ShouldApply , ( Pass == 0 ));
if ( a_ItemStack . IsEmpty ())
{
// Distributed it all
return ;
}
2012-09-20 20:10:46 +00:00
}
}
2013-01-08 05:01:06 +00:00
2012-09-20 20:10:46 +00:00
// The distribute to all other areas:
2013-01-08 05:01:06 +00:00
cSlotAreas :: iterator end = m_ShouldDistributeToHotbarFirst ? ( m_SlotAreas . end () - 1 ) : m_SlotAreas . end ();
for ( cSlotAreas :: iterator itr = m_SlotAreas . begin (); itr != end ; ++ itr )
2012-09-20 20:10:46 +00:00
{
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
}
2013-11-10 18:42:46 +01:00
bool cWindow :: CollectItemsToHand ( cItem & a_Dragging , cSlotArea & a_Area , cPlayer & a_Player , bool a_CollectFullStacks )
{
// First ask the slot areas from a_Area till the end of list:
bool ShouldCollect = false ;
for ( cSlotAreas :: iterator itr = m_SlotAreas . begin (), end = m_SlotAreas . end (); itr != end ; ++ itr )
{
if ( & a_Area == * itr )
{
ShouldCollect = true ;
}
if ( ! ShouldCollect )
{
continue ;
}
if (( * itr ) -> CollectItemsToHand ( a_Dragging , a_Player , a_CollectFullStacks ))
{
// a_Dragging is full
return true ;
}
}
// a_Dragging still not full, ask slot areas before a_Area in the list:
for ( cSlotAreas :: iterator itr = m_SlotAreas . begin (), end = m_SlotAreas . end (); itr != end ; ++ itr )
{
if ( * itr == & a_Area )
{
// All areas processed
return false ;
}
if (( * itr ) -> CollectItemsToHand ( a_Dragging , a_Player , a_CollectFullStacks ))
{
// a_Dragging is full
return true ;
}
}
// Shouldn't reach here
// a_Area is expected to be part of m_SlotAreas[], so the "return false" in the loop above should have returned already
ASSERT ( ! "This branch should not be reached" );
return false ;
}
2012-09-21 20:50:34 +00:00
void cWindow :: SendSlot ( cPlayer & a_Player , cSlotArea * a_SlotArea , int a_RelativeSlotNum )
{
int SlotBase = 0 ;
bool Found = false ;
for ( cSlotAreas :: iterator itr = m_SlotAreas . begin (), end = m_SlotAreas . end (); itr != end ; ++ itr )
{
if ( * itr == a_SlotArea )
{
Found = true ;
break ;
}
SlotBase += ( * itr ) -> GetNumSlots ();
} // for itr - m_SlotAreas[]
if ( ! Found )
{
LOGERROR ( "cWindow::SendSlot(): unknown a_SlotArea" );
ASSERT ( ! "cWindow::SendSlot(): unknown a_SlotArea" );
return ;
}
a_Player . GetClientHandle () -> SendInventorySlot (
m_WindowID , a_RelativeSlotNum + SlotBase , * ( a_SlotArea -> GetSlot ( a_RelativeSlotNum , a_Player ))
);
}
2012-09-20 20:10:46 +00:00
void cWindow :: Destroy ( void )
2012-09-20 13:25:54 +00:00
{
if ( m_Owner != NULL )
{
m_Owner -> CloseWindow ();
m_Owner = NULL ;
}
m_IsDestroyed = true ;
}
2013-05-30 08:40:13 +00:00
cSlotArea * cWindow :: GetSlotArea ( int a_GlobalSlotNum , int & a_LocalSlotNum )
{
if (( a_GlobalSlotNum < 0 ) || ( a_GlobalSlotNum >= GetNumSlots ()))
{
LOGWARNING ( "%s: requesting an invalid SlotNum: %d out of %d slots" , __FUNCTION__ , a_GlobalSlotNum , GetNumSlots () - 1 );
ASSERT ( ! "Invalid SlotNum" );
return NULL ;
}
// Iterate through all the SlotAreas, find the correct one
int LocalSlotNum = a_GlobalSlotNum ;
for ( cSlotAreas :: iterator itr = m_SlotAreas . begin (), end = m_SlotAreas . end (); itr != end ; ++ itr )
{
if ( LocalSlotNum < ( * itr ) -> GetNumSlots ())
{
a_LocalSlotNum = LocalSlotNum ;
return * itr ;
}
LocalSlotNum -= ( * itr ) -> GetNumSlots ();
} // for itr - m_SlotAreas[]
// We shouldn't be here - the check at the beginnning should prevent this. Log and assert
LOGWARNING ( "%s: GetNumSlots() is out of sync: %d; LocalSlotNum = %d" , __FUNCTION__ , GetNumSlots (), LocalSlotNum );
ASSERT ( ! "Invalid GetNumSlots" );
return NULL ;
}
const cSlotArea * cWindow :: GetSlotArea ( int a_GlobalSlotNum , int & a_LocalSlotNum ) const
{
if (( a_GlobalSlotNum < 0 ) || ( a_GlobalSlotNum >= GetNumSlots ()))
{
LOGWARNING ( "%s: requesting an invalid SlotNum: %d out of %d slots" , __FUNCTION__ , a_GlobalSlotNum , GetNumSlots () - 1 );
ASSERT ( ! "Invalid SlotNum" );
return NULL ;
}
// Iterate through all the SlotAreas, find the correct one
int LocalSlotNum = a_GlobalSlotNum ;
for ( cSlotAreas :: const_iterator itr = m_SlotAreas . begin (), end = m_SlotAreas . end (); itr != end ; ++ itr )
{
if ( LocalSlotNum < ( * itr ) -> GetNumSlots ())
{
a_LocalSlotNum = LocalSlotNum ;
return * itr ;
}
LocalSlotNum -= ( * itr ) -> GetNumSlots ();
} // for itr - m_SlotAreas[]
// We shouldn't be here - the check at the beginnning should prevent this. Log and assert
LOGWARNING ( "%s: GetNumSlots() is out of sync: %d; LocalSlotNum = %d" , __FUNCTION__ , GetNumSlots (), LocalSlotNum );
ASSERT ( ! "Invalid GetNumSlots" );
return NULL ;
}
void cWindow :: OnPaintBegin ( cPlayer & a_Player )
{
// Prepares the internal structures for inventory painting from the specified player
a_Player . ClearInventoryPaintSlots ();
}
void cWindow :: OnPaintProgress ( cPlayer & a_Player , int a_SlotNum )
{
// Add the slot to the internal structures for inventory painting by the specified player
a_Player . AddInventoryPaintSlot ( a_SlotNum );
}
void cWindow :: OnLeftPaintEnd ( cPlayer & a_Player )
{
// Process the entire action stored in the internal structures for inventory painting
// distribute as many items as possible
const cSlotNums & SlotNums = a_Player . GetInventoryPaintSlots ();
cItem ToDistribute ( a_Player . GetDraggingItem ());
2014-05-08 20:16:35 +02:00
int ToEachSlot = ( int ) ToDistribute . m_ItemCount / ( int ) SlotNums . size ();
2013-05-30 10:10:58 +00:00
int NumDistributed = DistributeItemToSlots ( a_Player , ToDistribute , ToEachSlot , SlotNums );
// Remove the items distributed from the dragging item:
a_Player . GetDraggingItem (). m_ItemCount -= NumDistributed ;
if ( a_Player . GetDraggingItem (). m_ItemCount == 0 )
{
a_Player . GetDraggingItem (). Empty ();
}
SendWholeWindow ( * a_Player . GetClientHandle ());
}
void cWindow :: OnRightPaintEnd ( cPlayer & a_Player )
{
// Process the entire action stored in the internal structures for inventory painting
// distribute one item into each slot
const cSlotNums & SlotNums = a_Player . GetInventoryPaintSlots ();
cItem ToDistribute ( a_Player . GetDraggingItem ());
int NumDistributed = DistributeItemToSlots ( a_Player , ToDistribute , 1 , SlotNums );
// Remove the items distributed from the dragging item:
a_Player . GetDraggingItem (). m_ItemCount -= NumDistributed ;
if ( a_Player . GetDraggingItem (). m_ItemCount == 0 )
{
a_Player . GetDraggingItem (). Empty ();
}
2013-05-30 08:40:13 +00:00
2013-05-30 10:10:58 +00:00
SendWholeWindow ( * a_Player . GetClientHandle ());
}
int cWindow :: DistributeItemToSlots ( cPlayer & a_Player , const cItem & a_Item , int a_NumToEachSlot , const cSlotNums & a_SlotNums )
{
if (( size_t )( a_Item . m_ItemCount ) < a_SlotNums . size ())
2013-05-30 08:40:13 +00:00
{
2014-03-12 10:34:50 -07:00
LOGWARNING ( "%s: Distributing less items (%d) than slots (" SIZE_T_FMT ")" , __FUNCTION__ , ( int ) a_Item . m_ItemCount , a_SlotNums . size ());
2013-05-30 08:40:13 +00:00
// This doesn't seem to happen with the 1.5.1 client, so we don't worry about it for now
2013-05-30 10:10:58 +00:00
return 0 ;
2013-05-30 08:40:13 +00:00
}
// Distribute to individual slots, keep track of how many items were actually distributed (full stacks etc.)
int NumDistributed = 0 ;
2013-05-30 10:10:58 +00:00
for ( cSlotNums :: const_iterator itr = a_SlotNums . begin (), end = a_SlotNums . end (); itr != end ; ++ itr )
2013-05-30 08:40:13 +00:00
{
int LocalSlotNum = 0 ;
cSlotArea * Area = GetSlotArea ( * itr , LocalSlotNum );
if ( Area == NULL )
{
LOGWARNING ( "%s: Bad SlotArea for slot %d" , __FUNCTION__ , * itr );
continue ;
}
// Modify the item at the slot
cItem AtSlot ( * Area -> GetSlot ( LocalSlotNum , a_Player ));
2013-11-12 13:15:09 +01:00
int MaxStack = AtSlot . GetMaxStackSize ();
2013-05-30 08:40:13 +00:00
if ( AtSlot . IsEmpty ())
{
// Empty, just move all of it there:
2013-05-30 10:10:58 +00:00
cItem ToStore ( a_Item );
ToStore . m_ItemCount = std :: min ( a_NumToEachSlot , ( int ) MaxStack );
2013-05-30 08:40:13 +00:00
Area -> SetSlot ( LocalSlotNum , a_Player , ToStore );
NumDistributed += ToStore . m_ItemCount ;
}
2014-01-16 19:00:49 +00:00
else if ( AtSlot . IsEqual ( a_Item ))
2013-05-30 08:40:13 +00:00
{
2013-05-30 10:10:58 +00:00
// Occupied, add and cap at MaxStack:
int CanStore = std :: min ( a_NumToEachSlot , ( int ) MaxStack - AtSlot . m_ItemCount );
2013-05-30 08:40:13 +00:00
AtSlot . m_ItemCount += CanStore ;
Area -> SetSlot ( LocalSlotNum , a_Player , AtSlot );
NumDistributed += CanStore ;
}
} // for itr - SlotNums[]
2013-05-30 10:10:58 +00:00
return NumDistributed ;
2013-05-30 08:40:13 +00:00
}
2013-06-13 07:36:43 +00:00
void cWindow :: BroadcastSlot ( cSlotArea * a_Area , int a_LocalSlotNum )
{
// Translate local slot num into global slot num:
int SlotNum = 0 ;
bool HasFound = false ;
for ( cSlotAreas :: const_iterator itr = m_SlotAreas . begin (), end = m_SlotAreas . end (); itr != end ; ++ itr )
{
if ( a_Area == * itr )
{
SlotNum += a_LocalSlotNum ;
HasFound = true ;
break ;
}
SlotNum += ( * itr ) -> GetNumSlots ();
} // for itr - m_SlotAreas[]
if ( ! HasFound )
{
LOGWARNING ( "%s: Invalid slot area parameter" , __FUNCTION__ );
ASSERT ( ! "Invalid slot area" );
return ;
}
// Broadcast the update packet:
cCSLock Lock ( m_CS );
for ( cPlayerList :: iterator itr = m_OpenedBy . begin (); itr != m_OpenedBy . end (); ++ itr )
{
( * itr ) -> GetClientHandle () -> SendInventorySlot ( m_WindowID , SlotNum , * a_Area -> GetSlot ( a_LocalSlotNum , ** itr ));
} // for itr - m_OpenedBy[]
}
2012-09-20 13:25:54 +00:00
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[]
}
2013-08-18 13:26:37 +02:00
void cWindow :: BroadcastProgress ( int a_Progressbar , int a_Value )
2012-09-20 13:25:54 +00:00
{
cCSLock Lock ( m_CS );
for ( cPlayerList :: iterator itr = m_OpenedBy . begin (); itr != m_OpenedBy . end (); ++ itr )
{
2013-08-18 13:26:37 +02:00
( * itr ) -> GetClientHandle () -> SendWindowProperty ( * this , a_Progressbar , a_Value );
2012-09-20 13:25:54 +00:00
} // for itr - m_OpenedBy[]
}
2013-08-18 13:26:37 +02:00
void cWindow :: SetProperty ( int a_Property , int a_Value )
{
cCSLock Lock ( m_CS );
for ( cPlayerList :: iterator itr = m_OpenedBy . begin (), end = m_OpenedBy . end (); itr != end ; ++ itr )
{
( * itr ) -> GetClientHandle () -> SendWindowProperty ( * this , a_Property , a_Value );
} // for itr - m_OpenedBy[]
}
void cWindow :: SetProperty ( int a_Property , int a_Value , cPlayer & a_Player )
{
a_Player . GetClientHandle () -> SendWindowProperty ( * this , a_Property , a_Value );
}
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2012-09-20 13:25:54 +00:00
// cInventoryWindow:
cInventoryWindow :: cInventoryWindow ( cPlayer & a_Player ) :
2013-10-28 13:30:24 +01:00
cWindow ( wtInventory , "Inventory" ),
2012-09-20 13:25:54 +00:00
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 ));
2012-09-20 20:10:46 +00:00
m_SlotAreas . push_back ( new cSlotAreaHotBar ( * this ));
2012-09-20 13:25:54 +00:00
}
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2012-09-20 13:25:54 +00:00
// cCraftingWindow:
cCraftingWindow :: cCraftingWindow ( int a_BlockX , int a_BlockY , int a_BlockZ ) :
2013-10-28 13:30:24 +01:00
cWindow ( wtWorkbench , "Crafting Table" )
2012-09-20 13:25:54 +00:00
{
m_SlotAreas . push_back ( new cSlotAreaCrafting ( 3 , * this ));
m_SlotAreas . push_back ( new cSlotAreaInventory ( * this ));
2012-09-20 20:10:46 +00:00
m_SlotAreas . push_back ( new cSlotAreaHotBar ( * this ));
2012-09-20 13:25:54 +00:00
}
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2014-05-01 00:47:57 +02:00
// cAnvilWindow:
2014-05-03 23:42:26 +02:00
cAnvilWindow :: cAnvilWindow ( int a_BlockX , int a_BlockY , int a_BlockZ ) :
2014-05-01 01:25:04 +02:00
cWindow ( wtAnvil , "Repair" ),
2014-05-03 23:42:26 +02:00
m_RepairedItemName ( "" ),
m_BlockX ( a_BlockX ),
m_BlockY ( a_BlockY ),
m_BlockZ ( a_BlockZ )
2014-05-01 00:47:57 +02:00
{
2014-05-03 23:42:26 +02:00
m_AnvilSlotArea = new cSlotAreaAnvil ( * this );
m_SlotAreas . push_back ( m_AnvilSlotArea );
2014-05-01 00:47:57 +02:00
m_SlotAreas . push_back ( new cSlotAreaInventory ( * this ));
m_SlotAreas . push_back ( new cSlotAreaHotBar ( * this ));
}
2014-05-03 23:42:26 +02:00
void cAnvilWindow :: SetRepairedItemName ( const AString & a_Name , cPlayer * a_Player )
{
m_RepairedItemName = a_Name ;
if ( a_Player != NULL )
{
m_AnvilSlotArea -> UpdateResult ( * a_Player );
}
}
void cAnvilWindow :: GetBlockPos ( int & a_PosX , int & a_PosY , int & a_PosZ )
{
a_PosX = m_BlockX ;
a_PosY = m_BlockY ;
a_PosZ = m_BlockZ ;
}
2014-07-30 21:59:35 +02:00
////////////////////////////////////////////////////////////////////////////////
// cBeaconWindow:
cBeaconWindow :: cBeaconWindow ( int a_BlockX , int a_BlockY , int a_BlockZ , cBeaconEntity * a_Beacon ) :
cWindow ( wtBeacon , "Beacon" ),
m_Beacon ( a_Beacon )
{
m_ShouldDistributeToHotbarFirst = true ;
m_SlotAreas . push_back ( new cSlotAreaBeacon ( m_Beacon , * this ));
m_SlotAreas . push_back ( new cSlotAreaInventory ( * this ));
m_SlotAreas . push_back ( new cSlotAreaHotBar ( * this ));
}
void cBeaconWindow :: OpenedByPlayer ( cPlayer & a_Player )
{
super :: OpenedByPlayer ( a_Player );
a_Player . GetClientHandle () -> SendWindowProperty ( * this , 0 , m_Beacon -> GetBeaconLevel ());
2014-07-31 12:13:11 +02:00
a_Player . GetClientHandle () -> SendWindowProperty ( * this , 1 , m_Beacon -> GetPrimaryEffect ());
a_Player . GetClientHandle () -> SendWindowProperty ( * this , 2 , m_Beacon -> GetSecondaryEffect ());
2014-07-30 21:59:35 +02:00
}
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2014-01-20 18:22:08 +01:00
// cEnchantingWindow:
cEnchantingWindow :: cEnchantingWindow ( int a_BlockX , int a_BlockY , int a_BlockZ ) :
2014-04-12 14:58:46 +02:00
cWindow ( wtEnchantment , "Enchant" ),
2014-05-19 15:46:36 +02:00
m_SlotArea (),
2014-04-12 14:58:46 +02:00
m_BlockX ( a_BlockX ),
m_BlockY ( a_BlockY ),
m_BlockZ ( a_BlockZ )
2014-01-20 18:22:08 +01:00
{
2014-08-28 01:21:54 +02:00
m_SlotAreas . push_back ( new cSlotAreaEnchanting ( * this , m_BlockX , m_BlockY , m_BlockZ ));
2014-01-20 18:22:08 +01:00
m_SlotAreas . push_back ( new cSlotAreaInventory ( * this ));
m_SlotAreas . push_back ( new cSlotAreaHotBar ( * this ));
}
2014-04-12 14:58:46 +02:00
void cEnchantingWindow :: SetProperty ( int a_Property , int a_Value )
{
2014-08-28 01:21:54 +02:00
if (( a_Property < 0 ) || (( size_t ) a_Property >= ARRAYCOUNT ( m_PropertyValue )))
{
ASSERT ( ! "a_Property is invalid" );
return ;
}
2014-04-12 14:58:46 +02:00
2014-08-28 01:21:54 +02:00
m_PropertyValue [ a_Property ] = a_Value ;
2014-04-19 15:10:50 +02:00
super :: SetProperty ( a_Property , a_Value );
2014-04-12 14:58:46 +02:00
}
void cEnchantingWindow :: SetProperty ( int a_Property , int a_Value , cPlayer & a_Player )
{
2014-08-28 01:21:54 +02:00
if (( a_Property < 0 ) || (( size_t ) a_Property >= ARRAYCOUNT ( m_PropertyValue )))
{
ASSERT ( ! "a_Property is invalid" );
return ;
}
2014-04-12 14:58:46 +02:00
2014-08-28 01:21:54 +02:00
m_PropertyValue [ a_Property ] = a_Value ;
2014-04-19 15:10:50 +02:00
super :: SetProperty ( a_Property , a_Value , a_Player );
2014-04-12 14:58:46 +02:00
}
int cEnchantingWindow :: GetPropertyValue ( int a_Property )
{
2014-08-28 01:21:54 +02:00
if (( a_Property < 0 ) || (( size_t ) a_Property >= ARRAYCOUNT ( m_PropertyValue )))
{
ASSERT ( ! "a_Property is invalid" );
return 0 ;
}
2014-04-12 14:58:46 +02:00
2014-08-28 01:21:54 +02:00
return m_PropertyValue [ a_Property ];
2014-04-12 14:58:46 +02:00
}
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2012-09-20 13:25:54 +00:00
// cChestWindow:
2012-10-20 21:53:09 +00:00
cChestWindow :: cChestWindow ( cChestEntity * a_Chest ) :
2014-07-06 23:50:22 +01:00
cWindow ( wtChest , ( a_Chest -> GetBlockType () == E_BLOCK_CHEST ) ? "Chest" : "Trapped Chest" ),
2012-09-20 13:25:54 +00:00
m_World ( a_Chest -> GetWorld ()),
2012-10-20 21:53:09 +00:00
m_BlockX ( a_Chest -> GetPosX ()),
m_BlockY ( a_Chest -> GetPosY ()),
2014-07-06 23:50:22 +01:00
m_BlockZ ( a_Chest -> GetPosZ ()),
m_PrimaryChest ( a_Chest ),
m_SecondaryChest ( NULL )
2012-09-20 13:25:54 +00:00
{
m_SlotAreas . push_back ( new cSlotAreaChest ( a_Chest , * this ));
2012-10-20 21:53:09 +00:00
m_SlotAreas . push_back ( new cSlotAreaInventory ( * this ));
m_SlotAreas . push_back ( new cSlotAreaHotBar ( * this ));
2012-09-20 13:25:54 +00:00
2012-10-20 21:53:09 +00:00
// Play the opening sound:
2014-07-13 02:08:02 +02:00
m_World -> BroadcastSoundEffect ( "random.chestopen" , ( double ) m_BlockX , ( double ) m_BlockY , ( double ) m_BlockZ , 1 , 1 );
2012-10-20 21:53:09 +00:00
// Send out the chest-open packet:
2014-07-06 23:50:22 +01:00
m_World -> BroadcastBlockAction ( m_BlockX , m_BlockY , m_BlockZ , 1 , 1 , a_Chest -> GetBlockType ());
2012-10-20 21:53:09 +00:00
}
cChestWindow :: cChestWindow ( cChestEntity * a_PrimaryChest , cChestEntity * a_SecondaryChest ) :
2014-07-06 23:50:22 +01:00
cWindow ( wtChest , ( a_PrimaryChest -> GetBlockType () == E_BLOCK_CHEST ) ? "Double Chest" : "Double Trapped Chest" ),
2012-10-20 21:53:09 +00:00
m_World ( a_PrimaryChest -> GetWorld ()),
m_BlockX ( a_PrimaryChest -> GetPosX ()),
m_BlockY ( a_PrimaryChest -> GetPosY ()),
2014-07-06 23:50:22 +01:00
m_BlockZ ( a_PrimaryChest -> GetPosZ ()),
m_PrimaryChest ( a_PrimaryChest ),
m_SecondaryChest ( a_SecondaryChest )
2012-10-20 21:53:09 +00:00
{
2013-01-08 05:01:06 +00:00
m_SlotAreas . push_back ( new cSlotAreaDoubleChest ( a_PrimaryChest , a_SecondaryChest , * this ));
2012-09-20 13:25:54 +00:00
m_SlotAreas . push_back ( new cSlotAreaInventory ( * this ));
2012-09-20 20:10:46 +00:00
m_SlotAreas . push_back ( new cSlotAreaHotBar ( * this ));
2012-09-20 13:25:54 +00:00
2013-01-08 05:01:06 +00:00
m_ShouldDistributeToHotbarFirst = false ;
2012-10-20 21:53:09 +00:00
// Play the opening sound:
2014-07-13 02:08:02 +02:00
m_World -> BroadcastSoundEffect ( "random.chestopen" , ( double ) m_BlockX , ( double ) m_BlockY , ( double ) m_BlockZ , 1 , 1 );
2012-10-20 21:53:09 +00:00
2012-09-20 13:25:54 +00:00
// Send out the chest-open packet:
2014-07-06 23:50:22 +01:00
m_World -> BroadcastBlockAction ( m_BlockX , m_BlockY , m_BlockZ , 1 , 1 , a_PrimaryChest -> GetBlockType ());
}
void cChestWindow :: OpenedByPlayer ( cPlayer & a_Player )
{
int ChunkX , ChunkZ ;
m_PrimaryChest -> SetNumberOfPlayers ( m_PrimaryChest -> GetNumberOfPlayers () + 1 );
cChunkDef :: BlockToChunk ( m_PrimaryChest -> GetPosX (), m_PrimaryChest -> GetPosZ (), ChunkX , ChunkZ );
m_PrimaryChest -> GetWorld () -> MarkRedstoneDirty ( ChunkX , ChunkZ );
if ( m_SecondaryChest != NULL )
{
m_SecondaryChest -> SetNumberOfPlayers ( m_SecondaryChest -> GetNumberOfPlayers () + 1 );
cChunkDef :: BlockToChunk ( m_SecondaryChest -> GetPosX (), m_SecondaryChest -> GetPosZ (), ChunkX , ChunkZ );
m_SecondaryChest -> GetWorld () -> MarkRedstoneDirty ( ChunkX , ChunkZ );
}
cWindow :: OpenedByPlayer ( a_Player );
}
bool cChestWindow :: ClosedByPlayer ( cPlayer & a_Player , bool a_CanRefuse )
{
int ChunkX , ChunkZ ;
m_PrimaryChest -> SetNumberOfPlayers ( m_PrimaryChest -> GetNumberOfPlayers () - 1 );
cChunkDef :: BlockToChunk ( m_PrimaryChest -> GetPosX (), m_PrimaryChest -> GetPosZ (), ChunkX , ChunkZ );
m_PrimaryChest -> GetWorld () -> MarkRedstoneDirty ( ChunkX , ChunkZ );
if ( m_SecondaryChest != NULL )
{
m_SecondaryChest -> SetNumberOfPlayers ( m_SecondaryChest -> GetNumberOfPlayers () - 1 );
cChunkDef :: BlockToChunk ( m_SecondaryChest -> GetPosX (), m_SecondaryChest -> GetPosZ (), ChunkX , ChunkZ );
m_SecondaryChest -> GetWorld () -> MarkRedstoneDirty ( ChunkX , ChunkZ );
}
cWindow :: ClosedByPlayer ( a_Player , a_CanRefuse );
return true ;
2012-09-20 13:25:54 +00:00
}
cChestWindow ::~ cChestWindow ()
{
// Send out the chest-close packet:
2014-07-12 22:06:25 +01:00
m_World -> BroadcastBlockAction ( m_BlockX , m_BlockY , m_BlockZ , 1 , 0 , m_PrimaryChest -> GetBlockType ());
2012-10-20 21:53:09 +00:00
2014-07-13 02:08:02 +02:00
m_World -> BroadcastSoundEffect ( "random.chestclosed" , ( double ) m_BlockX , ( double ) m_BlockY , ( double ) m_BlockZ , 1 , 1 );
2012-09-20 13:25:54 +00:00
}
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2013-05-26 14:39:04 +00:00
// cDropSpenserWindow:
2012-12-19 21:19:36 +00:00
2013-05-26 14:39:04 +00:00
cDropSpenserWindow :: cDropSpenserWindow ( int a_BlockX , int a_BlockY , int a_BlockZ , cDropSpenserEntity * a_DropSpenser ) :
2014-07-06 23:50:22 +01:00
cWindow ( wtDropSpenser , ( a_DropSpenser -> GetBlockType () == E_BLOCK_DISPENSER ) ? "Dispenser" : "Dropper" )
2012-12-19 21:19:36 +00:00
{
2013-06-13 07:36:43 +00:00
m_ShouldDistributeToHotbarFirst = false ;
2013-06-16 20:24:07 +00:00
m_SlotAreas . push_back ( new cSlotAreaItemGrid ( a_DropSpenser -> GetContents (), * this ));
2012-12-19 21:19:36 +00:00
m_SlotAreas . push_back ( new cSlotAreaInventory ( * this ));
m_SlotAreas . push_back ( new cSlotAreaHotBar ( * this ));
}
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2013-12-07 00:18:58 +00:00
// cEnderChestWindow:
cEnderChestWindow :: cEnderChestWindow ( cEnderChestEntity * a_EnderChest ) :
cWindow ( wtChest , "Ender Chest" ),
m_World ( a_EnderChest -> GetWorld ()),
m_BlockX ( a_EnderChest -> GetPosX ()),
m_BlockY ( a_EnderChest -> GetPosY ()),
m_BlockZ ( a_EnderChest -> GetPosZ ())
{
m_SlotAreas . push_back ( new cSlotAreaEnderChest ( a_EnderChest , * this ));
m_SlotAreas . push_back ( new cSlotAreaInventory ( * this ));
m_SlotAreas . push_back ( new cSlotAreaHotBar ( * this ));
// Play the opening sound:
2014-07-13 02:08:02 +02:00
m_World -> BroadcastSoundEffect ( "random.chestopen" , ( double ) m_BlockX , ( double ) m_BlockY , ( double ) m_BlockZ , 1 , 1 );
2013-12-07 00:18:58 +00:00
// Send out the chest-open packet:
m_World -> BroadcastBlockAction ( m_BlockX , m_BlockY , m_BlockZ , 1 , 1 , E_BLOCK_ENDER_CHEST );
}
cEnderChestWindow ::~ cEnderChestWindow ()
{
// Send out the chest-close packet:
m_World -> BroadcastBlockAction ( m_BlockX , m_BlockY , m_BlockZ , 1 , 0 , E_BLOCK_ENDER_CHEST );
2014-06-29 11:36:38 +01:00
// Play the closing sound
2014-07-13 02:08:02 +02:00
m_World -> BroadcastSoundEffect ( "random.chestclosed" , ( double ) m_BlockX , ( double ) m_BlockY , ( double ) m_BlockZ , 1 , 1 );
2013-12-07 00:18:58 +00:00
}
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2013-06-13 07:36:43 +00:00
// cHopperWindow:
cHopperWindow :: cHopperWindow ( int a_BlockX , int a_BlockY , int a_BlockZ , cHopperEntity * a_Hopper ) :
2013-10-28 13:30:24 +01:00
super ( wtHopper , "Hopper" )
2013-06-13 07:36:43 +00:00
{
m_ShouldDistributeToHotbarFirst = false ;
m_SlotAreas . push_back ( new cSlotAreaItemGrid ( a_Hopper -> GetContents (), * this ));
m_SlotAreas . push_back ( new cSlotAreaInventory ( * this ));
m_SlotAreas . push_back ( new cSlotAreaHotBar ( * this ));
}
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2012-09-20 13:25:54 +00:00
// cFurnaceWindow:
cFurnaceWindow :: cFurnaceWindow ( int a_BlockX , int a_BlockY , int a_BlockZ , cFurnaceEntity * a_Furnace ) :
2013-10-28 13:30:24 +01:00
cWindow ( wtFurnace , "Furnace" )
2012-09-20 13:25:54 +00:00
{
2013-06-13 07:36:43 +00:00
m_ShouldDistributeToHotbarFirst = false ;
2012-09-20 13:25:54 +00:00
m_SlotAreas . push_back ( new cSlotAreaFurnace ( a_Furnace , * this ));
m_SlotAreas . push_back ( new cSlotAreaInventory ( * this ));
2012-09-20 20:10:46 +00:00
m_SlotAreas . push_back ( new cSlotAreaHotBar ( * this ));
2012-09-20 13:25:54 +00:00
}