2013-07-29 12:13:03 +01:00
// SlotArea.cpp
// Implements the cSlotArea class and its descendants
#include "Globals.h"
#include "SlotArea.h"
2013-08-19 11:39:13 +02:00
#include "../Entities/Player.h"
2014-07-30 21:59:35 +02:00
#include "../BlockEntities/BeaconEntity.h"
2013-07-29 12:13:03 +01:00
#include "../BlockEntities/ChestEntity.h"
#include "../BlockEntities/DropSpenserEntity.h"
2013-12-07 00:18:58 +00:00
#include "../BlockEntities/EnderChestEntity.h"
2013-07-29 12:13:03 +01:00
#include "../BlockEntities/FurnaceEntity.h"
2014-09-12 23:18:02 +01:00
#include "../Entities/Minecart.h"
2013-07-29 12:13:03 +01:00
#include "../Items/ItemHandler.h"
#include "Window.h"
#include "../CraftingRecipes.h"
#include "../Root.h"
2014-04-07 19:52:35 +02:00
#include "../FastRandom.h"
#include "../BlockArea.h"
2013-07-29 12:13:03 +01:00
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2013-07-29 12:13:03 +01:00
// cSlotArea:
cSlotArea :: cSlotArea ( int a_NumSlots , cWindow & a_ParentWindow ) :
m_NumSlots ( a_NumSlots ),
m_ParentWindow ( a_ParentWindow )
{
}
void cSlotArea :: Clicked ( cPlayer & a_Player , int a_SlotNum , eClickAction a_ClickAction , const cItem & a_ClickedItem )
{
/*
2014-07-17 22:50:58 +02:00
LOGD("Slot area with %d slots clicked at slot number %d, clicked item %s, slot item %s",
GetNumSlots(), a_SlotNum,
2013-07-29 12:13:03 +01:00
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 ;
}
2013-11-10 18:42:46 +01:00
switch ( a_ClickAction )
2013-07-29 12:13:03 +01:00
{
2013-11-10 18:42:46 +01:00
case caShiftLeftClick :
case caShiftRightClick :
2013-07-29 12:13:03 +01:00
{
ShiftClicked ( a_Player , a_SlotNum , a_ClickedItem );
return ;
}
2013-11-10 18:42:46 +01:00
case caDblClick :
{
DblClicked ( a_Player , a_SlotNum );
return ;
}
2014-07-05 14:00:04 +02:00
case caMiddleClick :
{
MiddleClicked ( a_Player , a_SlotNum );
return ;
}
2014-07-06 00:40:59 +02:00
case caDropKey :
case caCtrlDropKey :
{
DropClicked ( a_Player , a_SlotNum , ( a_ClickAction == caCtrlDropKey ));
return ;
}
2014-07-09 14:30:06 +02:00
case caNumber1 :
case caNumber2 :
case caNumber3 :
case caNumber4 :
case caNumber5 :
case caNumber6 :
case caNumber7 :
case caNumber8 :
case caNumber9 :
{
NumberClicked ( a_Player , a_SlotNum , a_ClickAction );
return ;
}
2013-12-09 13:57:09 +00:00
default :
{
break ;
}
2013-07-29 12:13:03 +01:00
}
cItem Slot ( * GetSlot ( a_SlotNum , a_Player ));
if ( ! Slot . IsSameType ( a_ClickedItem ))
{
LOGWARNING ( "*** Window lost sync at item %d in SlotArea with %d items ***" , a_SlotNum , m_NumSlots );
LOGWARNING ( "My item: %s" , ItemToFullString ( Slot ). c_str ());
LOGWARNING ( "Their item: %s" , ItemToFullString ( a_ClickedItem ). c_str ());
bAsync = true ;
}
cItem & DraggingItem = a_Player . GetDraggingItem ();
switch ( a_ClickAction )
{
case caRightClick :
{
2014-07-17 22:15:34 +02:00
if ( DraggingItem . m_ItemType <= 0 ) // Empty-handed?
2013-07-29 12:13:03 +01:00
{
2014-07-17 22:15:34 +02:00
DraggingItem = Slot . CopyOne (); // Obtain copy of slot to preserve lore, enchantments, etc.
2014-01-16 21:41:08 +00:00
2013-07-29 12:13:03 +01:00
DraggingItem . m_ItemCount = ( char )((( float ) Slot . m_ItemCount ) / 2.f + 0.5f );
Slot . m_ItemCount -= DraggingItem . m_ItemCount ;
2014-01-16 18:44:11 +00:00
2013-07-29 12:13:03 +01:00
if ( Slot . m_ItemCount <= 0 )
{
Slot . Empty ();
}
}
else if (( Slot . m_ItemType <= 0 ) || DraggingItem . IsEqual ( Slot ))
{
// Drop one item in slot
cItemHandler * Handler = ItemHandler ( Slot . m_ItemType );
if (( DraggingItem . m_ItemCount > 0 ) && ( Slot . m_ItemCount < Handler -> GetMaxStackSize ()))
{
2014-01-16 21:41:08 +00:00
char OldSlotCount = Slot . m_ItemCount ;
2014-01-15 22:38:03 +00:00
2014-07-17 22:15:34 +02:00
Slot = DraggingItem . CopyOne (); // See above
2014-01-16 21:41:08 +00:00
OldSlotCount ++ ;
Slot . m_ItemCount = OldSlotCount ;
DraggingItem . m_ItemCount -- ;
2013-07-29 12:13:03 +01:00
}
if ( DraggingItem . m_ItemCount <= 0 )
{
DraggingItem . Empty ();
}
}
else if ( ! DraggingItem . IsEqual ( Slot ))
{
// Swap contents
cItem tmp ( DraggingItem );
DraggingItem = Slot ;
Slot = tmp ;
}
break ;
}
case caLeftClick :
{
// 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_ItemType );
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 ;
2014-04-14 22:05:04 +02:00
2013-07-29 12:13:03 +01:00
Slot . m_ItemCount += ( char ) Filling ;
DraggingItem . m_ItemCount -= ( char ) Filling ;
if ( DraggingItem . m_ItemCount <= 0 )
{
DraggingItem . Empty ();
}
}
break ;
}
default :
{
LOGWARNING ( "SlotArea: Unhandled click action: %d (%s)" , a_ClickAction , ClickActionToString ( a_ClickAction ));
m_ParentWindow . BroadcastWholeWindow ();
return ;
}
} // switch (a_ClickAction
SetSlot ( a_SlotNum , a_Player , Slot );
if ( bAsync )
{
m_ParentWindow . BroadcastWholeWindow ();
}
}
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 ())
{
2014-08-28 01:21:54 +02:00
// Empty the slot completely, the client doesn't like left-over ItemType with zero count
2013-07-29 12:13:03 +01:00
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 ();
}
2013-11-10 18:42:46 +01:00
void cSlotArea :: DblClicked ( cPlayer & a_Player , int a_SlotNum )
{
cItem & Dragging = a_Player . GetDraggingItem ();
if ( Dragging . IsEmpty ())
{
// Move the item in the dblclicked slot into hand:
Dragging = * GetSlot ( a_SlotNum , a_Player );
cItem EmptyItem ;
SetSlot ( a_SlotNum , a_Player , EmptyItem );
}
if ( Dragging . IsEmpty ())
{
LOGD ( "%s DblClicked with an empty hand over empty slot, ignoring" , a_Player . GetName (). c_str ());
return ;
}
// Add as many items from the surrounding area into hand as possible:
// First skip full stacks, then if there's still space, process full stacks as well:
if ( ! m_ParentWindow . CollectItemsToHand ( Dragging , * this , a_Player , false ))
{
m_ParentWindow . CollectItemsToHand ( Dragging , * this , a_Player , true );
}
m_ParentWindow . BroadcastWholeWindow (); // We need to broadcast, in case the window was a chest opened by multiple players
}
2014-07-05 14:00:04 +02:00
void cSlotArea :: MiddleClicked ( cPlayer & a_Player , int a_SlotNum )
{
cItem Slot ( * GetSlot ( a_SlotNum , a_Player ));
cItem & DraggingItem = a_Player . GetDraggingItem ();
if ( ! a_Player . IsGameModeCreative () || Slot . IsEmpty () || ! DraggingItem . IsEmpty ())
{
return ;
}
DraggingItem = Slot ;
DraggingItem . m_ItemCount = DraggingItem . GetMaxStackSize ();
}
2014-07-06 00:40:59 +02:00
void cSlotArea :: DropClicked ( cPlayer & a_Player , int a_SlotNum , bool a_DropStack )
{
cItem Slot ( * GetSlot ( a_SlotNum , a_Player ));
if ( Slot . IsEmpty ())
{
return ;
}
cItem ItemToDrop = Slot . CopyOne ();
if ( a_DropStack )
{
ItemToDrop . m_ItemCount = Slot . m_ItemCount ;
}
Slot . m_ItemCount -= ItemToDrop . m_ItemCount ;
if ( Slot . m_ItemCount <= 0 )
{
Slot . Empty ();
}
SetSlot ( a_SlotNum , a_Player , Slot );
a_Player . TossPickup ( ItemToDrop );
}
2014-07-09 14:30:06 +02:00
void cSlotArea :: NumberClicked ( cPlayer & a_Player , int a_SlotNum , eClickAction a_ClickAction )
{
if (( a_ClickAction < caNumber1 ) || ( a_ClickAction > caNumber9 ))
{
return ;
}
int HotbarSlot = ( int ) a_ClickAction - ( int ) caNumber1 ;
cItem ItemInHotbar ( a_Player . GetInventory (). GetHotbarSlot ( HotbarSlot ));
cItem ItemInSlot ( * GetSlot ( a_SlotNum , a_Player ));
// The items are equal. Do nothing.
if ( ItemInHotbar . IsEqual ( ItemInSlot ))
{
return ;
}
a_Player . GetInventory (). SetHotbarSlot ( HotbarSlot , ItemInSlot );
SetSlot ( a_SlotNum , a_Player , ItemInHotbar );
}
2014-02-28 16:26:23 +01:00
void cSlotArea :: OnPlayerAdded ( cPlayer & a_Player )
{
UNUSED ( a_Player );
}
void cSlotArea :: OnPlayerRemoved ( cPlayer & a_Player )
{
UNUSED ( a_Player );
}
2014-05-04 11:11:07 +02:00
void cSlotArea :: DistributeStack ( cItem & a_ItemStack , cPlayer & a_Player , bool a_ShouldApply , bool a_KeepEmptySlots )
2013-07-29 12:13:03 +01:00
{
for ( int i = 0 ; i < m_NumSlots ; i ++ )
{
const cItem * Slot = GetSlot ( i , a_Player );
2014-01-16 19:00:49 +00:00
if ( ! Slot -> IsEqual ( a_ItemStack ) && ( ! Slot -> IsEmpty () || a_KeepEmptySlots ))
2013-07-29 12:13:03 +01:00
{
// 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 ;
}
2014-05-04 11:11:07 +02:00
if ( a_ShouldApply )
2013-07-29 12:13:03 +01:00
{
cItem NewSlot ( a_ItemStack );
NewSlot . m_ItemCount = Slot -> m_ItemCount + NumFit ;
SetSlot ( i , a_Player , NewSlot );
}
a_ItemStack . m_ItemCount -= NumFit ;
if ( a_ItemStack . IsEmpty ())
{
return ;
}
} // for i - Slots
}
2013-11-10 18:42:46 +01:00
bool cSlotArea :: CollectItemsToHand ( cItem & a_Dragging , cPlayer & a_Player , bool a_CollectFullStacks )
{
int NumSlots = GetNumSlots ();
for ( int i = 0 ; i < NumSlots ; i ++ )
{
const cItem & SlotItem = * GetSlot ( i , a_Player );
2014-01-16 19:00:49 +00:00
if ( ! SlotItem . IsEqual ( a_Dragging ))
2013-11-10 18:42:46 +01:00
{
continue ;
}
int ToMove = a_Dragging . GetMaxStackSize () - a_Dragging . m_ItemCount ;
if ( ToMove > SlotItem . m_ItemCount )
{
ToMove = SlotItem . m_ItemCount ;
}
a_Dragging . m_ItemCount += ToMove ;
cItem NewSlot ( SlotItem );
NewSlot . m_ItemCount -= ToMove ;
SetSlot ( i , a_Player , NewSlot );
if ( ! NewSlot . IsEmpty ())
{
// There are leftovers in the slot, so a_Dragging must be full
return true ;
}
} // for i - Slots[]
// a_Dragging may be full if there were exactly the number of items needed to fill it
return a_Dragging . IsFullStack ();
}
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2013-07-29 12:13:03 +01:00
// 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 ) const
{
// 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 );
}
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2013-07-29 12:13:03 +01:00
// cSlotAreaDoubleChest:
cSlotAreaDoubleChest :: cSlotAreaDoubleChest ( cChestEntity * a_TopChest , cChestEntity * a_BottomChest , cWindow & a_ParentWindow ) :
cSlotArea ( 54 , a_ParentWindow ),
m_TopChest ( a_TopChest ),
m_BottomChest ( a_BottomChest )
{
}
const cItem * cSlotAreaDoubleChest :: GetSlot ( int a_SlotNum , cPlayer & a_Player ) const
{
// a_SlotNum ranges from 0 to 53, use that to index the correct chest's inventory:
if ( a_SlotNum < 27 )
{
return & ( m_TopChest -> GetSlot ( a_SlotNum ));
}
else
{
return & ( m_BottomChest -> GetSlot ( a_SlotNum - 27 ));
}
}
void cSlotAreaDoubleChest :: SetSlot ( int a_SlotNum , cPlayer & a_Player , const cItem & a_Item )
{
if ( a_SlotNum < 27 )
{
m_TopChest -> SetSlot ( a_SlotNum , a_Item );
}
else
{
m_BottomChest -> SetSlot ( a_SlotNum - 27 , a_Item );
}
}
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2013-07-29 12:13:03 +01:00
// 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 , eClickAction a_ClickAction , const cItem & a_ClickedItem )
{
2014-07-05 14:00:04 +02:00
if ( a_ClickAction == caMiddleClick )
{
MiddleClicked ( a_Player , a_SlotNum );
return ;
}
2013-07-29 12:13:03 +01:00
// Override for craft result slot
if ( a_SlotNum == 0 )
{
if (( a_ClickAction == caShiftLeftClick ) || ( a_ClickAction == caShiftRightClick ))
{
ShiftClickedResult ( a_Player );
}
2014-07-06 00:40:59 +02:00
else if (( a_ClickAction == caDropKey ) || ( a_ClickAction == caCtrlDropKey ))
{
DropClickedResult ( a_Player );
}
2013-07-29 12:13:03 +01:00
else
{
ClickedResult ( a_Player );
}
return ;
}
2014-07-05 14:00:04 +02:00
2013-07-29 12:13:03 +01:00
super :: Clicked ( a_Player , a_SlotNum , a_ClickAction , a_ClickedItem );
UpdateRecipe ( a_Player );
}
2013-11-11 21:33:14 +01:00
void cSlotAreaCrafting :: DblClicked ( cPlayer & a_Player , int a_SlotNum )
{
if ( a_SlotNum == 0 )
{
// Dbl-clicking the crafting result slot shouldn't collect items to hand
return ;
}
super :: DblClicked ( a_Player , a_SlotNum );
}
2013-07-29 12:13:03 +01:00
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
}
2014-02-28 16:26:23 +01:00
2014-07-06 14:42:28 +02:00
void cSlotAreaCrafting :: SetSlot ( int a_SlotNum , cPlayer & a_Player , const cItem & a_Item )
{
// Update the recipe after setting the slot, if the slot is not the result slot:
super :: SetSlot ( a_SlotNum , a_Player , a_Item );
if ( a_SlotNum != 0 )
{
UpdateRecipe ( a_Player );
}
}
2014-02-28 16:26:23 +01:00
void cSlotAreaCrafting :: DistributeStack ( cItem & a_ItemStack , cPlayer & a_Player , bool a_ShouldApply , bool a_KeepEmptySlots )
{
UNUSED ( a_ItemStack );
UNUSED ( a_Player );
UNUSED ( a_ShouldApply );
UNUSED ( a_KeepEmptySlots );
}
2013-07-29 12:13:03 +01:00
void cSlotAreaCrafting :: ClickedResult ( cPlayer & a_Player )
{
cItem & DraggingItem = a_Player . GetDraggingItem ();
// Get the current recipe:
cCraftingRecipe & Recipe = GetRecipeForPlayer ( a_Player );
2014-04-18 21:09:44 +02:00
const cItem & Result = Recipe . GetResult ();
2013-07-29 12:13:03 +01:00
cItem * PlayerSlots = GetPlayerSlots ( a_Player ) + 1 ;
cCraftingGrid Grid ( PlayerSlots , m_GridSize , m_GridSize );
// If possible, craft:
if ( DraggingItem . IsEmpty ())
{
2014-04-18 21:09:44 +02:00
DraggingItem = Result ;
2013-07-29 12:13:03 +01:00
Recipe . ConsumeIngredients ( Grid );
Grid . CopyToItems ( PlayerSlots );
2014-05-12 17:05:09 +03:00
HandleCraftItem ( Result , a_Player );
2013-07-29 12:13:03 +01:00
}
2014-04-18 21:09:44 +02:00
else if ( DraggingItem . IsEqual ( Result ))
2013-07-29 12:13:03 +01:00
{
2014-04-18 21:09:44 +02:00
cItemHandler * Handler = ItemHandler ( Result . m_ItemType );
if ( DraggingItem . m_ItemCount + Result . m_ItemCount <= Handler -> GetMaxStackSize ())
2013-07-29 12:13:03 +01:00
{
2014-04-18 21:09:44 +02:00
DraggingItem . m_ItemCount += Result . m_ItemCount ;
2013-07-29 12:13:03 +01:00
Recipe . ConsumeIngredients ( Grid );
Grid . CopyToItems ( PlayerSlots );
2014-05-12 17:05:09 +03:00
HandleCraftItem ( Result , a_Player );
2013-07-29 12:13:03 +01:00
}
}
// 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 :: ShiftClickedResult ( cPlayer & a_Player )
{
cItem Result ( * GetSlot ( 0 , a_Player ));
if ( Result . IsEmpty ())
{
return ;
}
cItem * PlayerSlots = GetPlayerSlots ( a_Player ) + 1 ;
2014-01-07 16:00:19 +01:00
for (;;)
2013-07-29 12:13:03 +01:00
{
// Try distributing the result. If it fails, bail out:
cItem ResultCopy ( Result );
m_ParentWindow . DistributeStack ( ResultCopy , a_Player , this , false );
if ( ! ResultCopy . IsEmpty ())
{
// Couldn't distribute all of it. Bail out
return ;
}
// Distribute the result, this time for real:
ResultCopy = Result ;
m_ParentWindow . DistributeStack ( ResultCopy , a_Player , this , true );
2014-07-06 14:42:28 +02:00
2013-07-29 12:13:03 +01:00
// Remove the ingredients from the crafting grid and update the recipe:
cCraftingRecipe & Recipe = GetRecipeForPlayer ( a_Player );
cCraftingGrid Grid ( PlayerSlots , m_GridSize , m_GridSize );
Recipe . ConsumeIngredients ( Grid );
Grid . CopyToItems ( PlayerSlots );
UpdateRecipe ( a_Player );
2014-07-06 14:42:28 +02:00
// Broadcast the window, we sometimes move items to different locations than Vanilla, causing needless desyncs:
m_ParentWindow . BroadcastWholeWindow ();
// If the recipe has changed, bail out:
2013-07-29 12:13:03 +01:00
if ( ! Recipe . GetResult (). IsEqual ( Result ))
{
return ;
}
2014-01-07 16:00:19 +01:00
}
2013-07-29 12:13:03 +01:00
}
2014-07-06 00:40:59 +02:00
void cSlotAreaCrafting :: DropClickedResult ( cPlayer & a_Player )
{
// Get the current recipe:
cCraftingRecipe & Recipe = GetRecipeForPlayer ( a_Player );
const cItem & Result = Recipe . GetResult ();
cItem * PlayerSlots = GetPlayerSlots ( a_Player ) + 1 ;
cCraftingGrid Grid ( PlayerSlots , m_GridSize , m_GridSize );
a_Player . TossPickup ( Result );
Recipe . ConsumeIngredients ( Grid );
Grid . CopyToItems ( PlayerSlots );
HandleCraftItem ( Result , a_Player );
UpdateRecipe ( a_Player );
}
2013-07-29 12:13:03 +01:00
void cSlotAreaCrafting :: UpdateRecipe ( cPlayer & a_Player )
{
cCraftingGrid Grid ( GetPlayerSlots ( a_Player ) + 1 , m_GridSize , m_GridSize );
cCraftingRecipe & Recipe = GetRecipeForPlayer ( a_Player );
2014-10-15 19:01:55 +02:00
cRoot :: Get () -> GetCraftingRecipes () -> GetRecipe ( a_Player , Grid , Recipe );
2013-07-29 12:13:03 +01:00
SetSlot ( 0 , a_Player , Recipe . GetResult ());
m_ParentWindow . SendSlot ( a_Player , this , 0 );
}
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 );
2014-10-15 19:01:55 +02:00
cRoot :: Get () -> GetCraftingRecipes () -> GetRecipe ( a_Player , Grid , Recipe );
2013-07-29 12:13:03 +01:00
m_Recipes . push_back ( std :: make_pair ( a_Player . GetUniqueID (), Recipe ));
return m_Recipes . back (). second ;
}
2014-05-12 17:05:09 +03:00
void cSlotAreaCrafting :: HandleCraftItem ( const cItem & a_Result , cPlayer & a_Player )
{
switch ( a_Result . m_ItemType )
{
case E_BLOCK_WORKBENCH : a_Player . AwardAchievement ( achCraftWorkbench ); break ;
case E_BLOCK_FURNACE : a_Player . AwardAchievement ( achCraftFurnace ); break ;
case E_BLOCK_CAKE : a_Player . AwardAchievement ( achBakeCake ); break ;
case E_BLOCK_ENCHANTMENT_TABLE : a_Player . AwardAchievement ( achCraftEnchantTable ); break ;
case E_BLOCK_BOOKCASE : a_Player . AwardAchievement ( achBookshelf ); break ;
case E_ITEM_WOODEN_PICKAXE : a_Player . AwardAchievement ( achCraftPickaxe ); break ;
case E_ITEM_WOODEN_SWORD : a_Player . AwardAchievement ( achCraftSword ); break ;
case E_ITEM_STONE_PICKAXE : a_Player . AwardAchievement ( achCraftBetterPick ); break ;
case E_ITEM_WOODEN_HOE : a_Player . AwardAchievement ( achCraftHoe ); break ;
case E_ITEM_BREAD : a_Player . AwardAchievement ( achMakeBread ); break ;
default : break ;
}
}
2013-07-29 12:13:03 +01:00
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2014-05-01 00:47:57 +02:00
// cSlotAreaAnvil:
cSlotAreaAnvil :: cSlotAreaAnvil ( cAnvilWindow & a_ParentWindow ) :
cSlotAreaTemporary ( 3 , a_ParentWindow ),
2014-06-04 11:12:06 +01:00
m_MaximumCost ( 0 ),
2014-06-04 11:39:21 +01:00
m_StackSizeToBeUsedInRepair ( 0 )
2014-05-01 00:47:57 +02:00
{
}
void cSlotAreaAnvil :: Clicked ( cPlayer & a_Player , int a_SlotNum , eClickAction a_ClickAction , const cItem & a_ClickedItem )
{
2014-05-03 23:42:26 +02:00
ASSERT (( a_SlotNum >= 0 ) && ( a_SlotNum < GetNumSlots ()));
if ( a_SlotNum != 2 )
{
super :: Clicked ( a_Player , a_SlotNum , a_ClickAction , a_ClickedItem );
UpdateResult ( a_Player );
return ;
}
bool bAsync = false ;
if ( GetSlot ( a_SlotNum , a_Player ) == NULL )
{
LOGWARNING ( "GetSlot(%d) returned NULL! Ignoring click" , a_SlotNum );
return ;
}
2014-07-05 14:00:04 +02:00
switch ( a_ClickAction )
2014-05-04 11:11:07 +02:00
{
2014-07-05 14:00:04 +02:00
case caDblClick :
{
return ;
}
case caShiftLeftClick :
case caShiftRightClick :
{
ShiftClicked ( a_Player , a_SlotNum , a_ClickedItem );
return ;
}
case caMiddleClick :
{
MiddleClicked ( a_Player , a_SlotNum );
return ;
}
2014-07-06 00:40:59 +02:00
case caDropKey :
case caCtrlDropKey :
{
if ( CanTakeResultItem ( a_Player ))
{
DropClicked ( a_Player , a_SlotNum , true );
OnTakeResult ( a_Player );
}
return ;
}
2014-07-05 14:00:04 +02:00
default :
{
break ;
}
2014-05-04 11:11:07 +02:00
}
2014-05-03 23:42:26 +02:00
cItem Slot ( * GetSlot ( a_SlotNum , a_Player ));
if ( ! Slot . IsSameType ( a_ClickedItem ))
{
LOGWARNING ( "*** Window lost sync at item %d in SlotArea with %d items ***" , a_SlotNum , m_NumSlots );
LOGWARNING ( "My item: %s" , ItemToFullString ( Slot ). c_str ());
LOGWARNING ( "Their item: %s" , ItemToFullString ( a_ClickedItem ). c_str ());
bAsync = true ;
}
cItem & DraggingItem = a_Player . GetDraggingItem ();
if ( Slot . IsEmpty ())
{
return ;
}
if ( ! DraggingItem . IsEmpty ())
{
if ( ! ( DraggingItem . IsEqual ( Slot ) && (( DraggingItem . m_ItemCount + Slot . m_ItemCount ) <= cItemHandler :: GetItemHandler ( Slot ) -> GetMaxStackSize ())))
{
return ;
}
}
if ( ! CanTakeResultItem ( a_Player ))
{
return ;
}
cItem NewItem = cItem ( Slot );
NewItem . m_ItemCount += DraggingItem . m_ItemCount ;
Slot . Empty ();
DraggingItem . Empty ();
SetSlot ( a_SlotNum , a_Player , Slot );
DraggingItem = NewItem ;
2014-05-04 11:11:07 +02:00
OnTakeResult ( a_Player );
2014-05-03 23:42:26 +02:00
if ( bAsync )
{
m_ParentWindow . BroadcastWholeWindow ();
}
}
2014-05-04 11:11:07 +02:00
void cSlotAreaAnvil :: ShiftClicked ( cPlayer & a_Player , int a_SlotNum , const cItem & a_ClickedItem )
{
if ( a_SlotNum != 2 )
{
super :: ShiftClicked ( a_Player , a_SlotNum , a_ClickedItem );
UpdateResult ( a_Player );
return ;
}
// 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 ));
if ( Slot . IsEmpty () || ! CanTakeResultItem ( a_Player ))
{
return ;
}
m_ParentWindow . DistributeStack ( Slot , a_Player , this , true );
if ( Slot . IsEmpty ())
{
Slot . Empty ();
OnTakeResult ( a_Player );
}
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 ();
}
void cSlotAreaAnvil :: DistributeStack ( cItem & a_ItemStack , cPlayer & a_Player , bool a_ShouldApply , bool a_KeepEmptySlots )
{
for ( int i = 0 ; i < 2 ; i ++ )
{
const cItem * Slot = GetSlot ( i , a_Player );
if ( ! Slot -> IsEqual ( 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_ShouldApply )
{
cItem NewSlot ( a_ItemStack );
NewSlot . m_ItemCount = Slot -> m_ItemCount + NumFit ;
SetSlot ( i , a_Player , NewSlot );
}
a_ItemStack . m_ItemCount -= NumFit ;
if ( a_ItemStack . IsEmpty ())
{
UpdateResult ( a_Player );
return ;
}
} // for i - Slots
UpdateResult ( a_Player );
}
void cSlotAreaAnvil :: OnTakeResult ( cPlayer & a_Player )
2014-05-03 23:42:26 +02:00
{
if ( ! a_Player . IsGameModeCreative ())
{
2014-05-16 19:33:57 +02:00
a_Player . DeltaExperience ( - cPlayer :: XpForLevel ( m_MaximumCost ));
2014-05-03 23:42:26 +02:00
}
SetSlot ( 0 , a_Player , cItem ());
if ( m_StackSizeToBeUsedInRepair > 0 )
{
const cItem * Item = GetSlot ( 1 , a_Player );
if ( ! Item -> IsEmpty () && ( Item -> m_ItemCount > m_StackSizeToBeUsedInRepair ))
{
cItem NewSecondItem ( * Item );
NewSecondItem . m_ItemCount -= m_StackSizeToBeUsedInRepair ;
2014-06-04 11:12:06 +01:00
m_StackSizeToBeUsedInRepair = 0 ;
2014-05-03 23:42:26 +02:00
SetSlot ( 1 , a_Player , NewSecondItem );
}
else
{
SetSlot ( 1 , a_Player , cItem ());
}
}
else
{
SetSlot ( 1 , a_Player , cItem ());
}
m_ParentWindow . SetProperty ( 0 , m_MaximumCost , a_Player );
m_MaximumCost = 0 ;
2014-05-05 17:36:22 +02:00
(( cAnvilWindow * ) & m_ParentWindow ) -> SetRepairedItemName ( "" , NULL );
2014-05-03 23:42:26 +02:00
int PosX , PosY , PosZ ;
(( cAnvilWindow * ) & m_ParentWindow ) -> GetBlockPos ( PosX , PosY , PosZ );
BLOCKTYPE Block ;
NIBBLETYPE BlockMeta ;
a_Player . GetWorld () -> GetBlockTypeMeta ( PosX , PosY , PosZ , Block , BlockMeta );
cFastRandom Random ;
if ( ! a_Player . IsGameModeCreative () && ( Block == E_BLOCK_ANVIL ) && ( Random . NextFloat ( 1.0F ) < 0.12F ))
{
2014-05-06 19:38:09 +02:00
NIBBLETYPE Orientation = BlockMeta & 0x3 ;
2014-05-03 23:42:26 +02:00
NIBBLETYPE AnvilDamage = BlockMeta >> 2 ;
++ AnvilDamage ;
if ( AnvilDamage > 2 )
{
// Anvil will break
a_Player . GetWorld () -> SetBlock ( PosX , PosY , PosZ , E_BLOCK_AIR , ( NIBBLETYPE ) 0 );
a_Player . GetWorld () -> BroadcastSoundParticleEffect ( 1020 , PosX , PosY , PosZ , 0 );
a_Player . CloseWindow ( false );
}
else
{
2014-05-06 19:38:09 +02:00
a_Player . GetWorld () -> SetBlockMeta ( PosX , PosY , PosZ , Orientation | ( AnvilDamage << 2 ));
2014-05-03 23:42:26 +02:00
a_Player . GetWorld () -> BroadcastSoundParticleEffect ( 1021 , PosX , PosY , PosZ , 0 );
}
}
else
{
a_Player . GetWorld () -> BroadcastSoundParticleEffect ( 1021 , PosX , PosY , PosZ , 0 );
}
}
bool cSlotAreaAnvil :: CanTakeResultItem ( cPlayer & a_Player )
{
return (
(
2014-05-05 15:49:56 +02:00
a_Player . IsGameModeCreative () || // Is the player in gamemode?
( a_Player . GetXpLevel () >= m_MaximumCost ) // or the player have enough exp?
) &&
( ! GetSlot ( 2 , a_Player ) -> IsEmpty ()) && // Is a item in the result slot?
2014-05-06 19:38:09 +02:00
( m_MaximumCost > 0 ) // When no maximum cost is set, the item isn't set from the UpdateResult() method and can't be a valid enchanting result.
2014-05-03 23:42:26 +02:00
);
2014-05-01 00:47:57 +02:00
}
void cSlotAreaAnvil :: OnPlayerRemoved ( cPlayer & a_Player )
{
2014-05-03 23:42:26 +02:00
TossItems ( a_Player , 0 , 2 );
2014-05-01 00:47:57 +02:00
super :: OnPlayerRemoved ( a_Player );
}
void cSlotAreaAnvil :: UpdateResult ( cPlayer & a_Player )
{
cItem Input ( * GetSlot ( 0 , a_Player ));
cItem SecondInput ( * GetSlot ( 1 , a_Player ));
cItem Output ( * GetSlot ( 2 , a_Player ));
if ( Input . IsEmpty () && ! Output . IsEmpty ())
{
Output . Empty ();
SetSlot ( 2 , a_Player , Output );
m_ParentWindow . SetProperty ( 0 , 0 , a_Player );
return ;
}
2014-05-03 23:42:26 +02:00
m_MaximumCost = 0 ;
m_StackSizeToBeUsedInRepair = 0 ;
2014-05-07 20:43:37 +02:00
int RepairCost = Input . m_RepairCost ;
2014-05-01 00:47:57 +02:00
int NeedExp = 0 ;
2014-05-07 12:30:30 +02:00
bool IsEnchantBook = false ;
2014-05-01 00:47:57 +02:00
if ( ! SecondInput . IsEmpty ())
{
2014-05-07 12:30:30 +02:00
IsEnchantBook = ( SecondInput . m_ItemType == E_ITEM_ENCHANTED_BOOK );
RepairCost += SecondInput . m_RepairCost ;
2014-05-06 19:38:09 +02:00
if ( Input . IsDamageable () && cItemHandler :: GetItemHandler ( Input ) -> CanRepairWithRawMaterial ( SecondInput . m_ItemType ))
2014-05-01 00:47:57 +02:00
{
// Tool and armor repair with special item (iron / gold / diamond / ...)
int DamageDiff = std :: min (( int ) Input . m_ItemDamage , ( int ) Input . GetMaxDamage () / 4 );
2014-05-07 12:30:30 +02:00
if ( DamageDiff <= 0 )
2014-05-01 00:47:57 +02:00
{
// No enchantment
Output . Empty ();
SetSlot ( 2 , a_Player , Output );
m_ParentWindow . SetProperty ( 0 , 0 , a_Player );
return ;
}
int x = 0 ;
while (( DamageDiff > 0 ) && ( x < SecondInput . m_ItemCount ))
{
Input . m_ItemDamage -= DamageDiff ;
2014-05-08 20:16:35 +02:00
NeedExp += std :: max ( 1 , DamageDiff / 100 ) + ( int ) Input . m_Enchantments . Count ();
2014-05-01 00:47:57 +02:00
DamageDiff = std :: min (( int ) Input . m_ItemDamage , ( int ) Input . GetMaxDamage () / 4 );
++ x ;
}
2014-05-03 23:42:26 +02:00
m_StackSizeToBeUsedInRepair = x ;
2014-05-01 00:47:57 +02:00
}
else
{
// Tool and armor repair with two tools / armors
2014-05-07 12:30:30 +02:00
if ( ! IsEnchantBook && ( ! Input . IsSameType ( SecondInput ) || ! Input . IsDamageable ()))
2014-05-01 00:47:57 +02:00
{
// No enchantment
Output . Empty ();
SetSlot ( 2 , a_Player , Output );
m_ParentWindow . SetProperty ( 0 , 0 , a_Player );
return ;
}
2014-05-07 12:30:30 +02:00
if (( Input . GetMaxDamage () > 0 ) && ! IsEnchantBook )
2014-05-01 00:47:57 +02:00
{
2014-05-07 12:30:30 +02:00
int FirstDamageDiff = Input . GetMaxDamage () - Input . m_ItemDamage ;
int SecondDamageDiff = SecondInput . GetMaxDamage () - SecondInput . m_ItemDamage ;
int Damage = SecondDamageDiff + Input . GetMaxDamage () * 12 / 100 ;
2014-05-01 00:47:57 +02:00
2014-05-07 12:30:30 +02:00
int NewItemDamage = Input . GetMaxDamage () - ( FirstDamageDiff + Damage );
if ( NewItemDamage > 0 )
{
NewItemDamage = 0 ;
}
if ( NewItemDamage < Input . m_ItemDamage )
{
Input . m_ItemDamage = NewItemDamage ;
NeedExp += std :: max ( 1 , Damage / 100 );
}
2014-05-01 00:47:57 +02:00
}
// TODO: Add enchantments.
}
}
int NameChangeExp = 0 ;
2014-05-01 01:25:04 +02:00
const AString & RepairedItemName = (( cAnvilWindow * ) & m_ParentWindow ) -> GetRepairedItemName ();
if ( RepairedItemName . empty ())
2014-05-01 00:47:57 +02:00
{
// Remove custom name
if ( ! Input . m_CustomName . empty ())
{
2014-05-07 12:30:30 +02:00
NameChangeExp = ( Input . IsDamageable ()) ? 7 : ( Input . m_ItemCount * 5 );
2014-05-01 00:47:57 +02:00
NeedExp += NameChangeExp ;
Input . m_CustomName = "" ;
}
}
2014-05-01 01:25:04 +02:00
else if ( RepairedItemName != Input . m_CustomName )
2014-05-01 00:47:57 +02:00
{
// Change custom name
2014-05-07 12:30:30 +02:00
NameChangeExp = ( Input . IsDamageable ()) ? 7 : ( Input . m_ItemCount * 5 );
2014-05-01 00:47:57 +02:00
NeedExp += NameChangeExp ;
if ( ! Input . m_CustomName . empty ())
{
RepairCost += NameChangeExp / 2 ;
}
2014-05-01 01:25:04 +02:00
Input . m_CustomName = RepairedItemName ;
2014-05-01 00:47:57 +02:00
}
// TODO: Add enchantment exp cost.
2014-05-03 23:42:26 +02:00
m_MaximumCost = RepairCost + NeedExp ;
2014-05-01 00:47:57 +02:00
if ( NeedExp < 0 )
{
Input . Empty ();
}
2014-05-07 12:30:30 +02:00
if (( NameChangeExp == NeedExp ) && ( NameChangeExp > 0 ) && ( m_MaximumCost >= 40 ))
2014-05-01 00:47:57 +02:00
{
2014-05-03 23:42:26 +02:00
m_MaximumCost = 39 ;
2014-05-01 00:47:57 +02:00
}
2014-05-03 23:42:26 +02:00
if ( m_MaximumCost >= 40 && ! a_Player . IsGameModeCreative ())
2014-05-01 00:47:57 +02:00
{
Input . Empty ();
}
if ( ! Input . IsEmpty ())
{
2014-05-07 12:30:30 +02:00
RepairCost = std :: max ( Input . m_RepairCost , SecondInput . m_RepairCost );
2014-05-01 00:47:57 +02:00
if ( ! Input . m_CustomName . empty ())
{
2014-05-07 20:43:37 +02:00
RepairCost -= 9 ;
2014-05-01 00:47:57 +02:00
}
2014-05-07 20:43:37 +02:00
RepairCost = std :: max ( RepairCost , 0 );
2014-05-01 00:47:57 +02:00
RepairCost += 2 ;
2014-05-07 12:30:30 +02:00
Input . m_RepairCost = RepairCost ;
}
2014-05-01 00:47:57 +02:00
SetSlot ( 2 , a_Player , Input );
2014-05-03 23:42:26 +02:00
m_ParentWindow . SetProperty ( 0 , m_MaximumCost , a_Player );
2014-05-01 00:47:57 +02:00
}
2014-07-30 21:59:35 +02:00
////////////////////////////////////////////////////////////////////////////////
// cSlotAreaBeacon:
cSlotAreaBeacon :: cSlotAreaBeacon ( cBeaconEntity * a_Beacon , cWindow & a_ParentWindow ) :
cSlotArea ( 1 , a_ParentWindow ),
m_Beacon ( a_Beacon )
{
m_Beacon -> GetContents (). AddListener ( * this );
}
cSlotAreaBeacon ::~ cSlotAreaBeacon ()
{
m_Beacon -> GetContents (). RemoveListener ( * this );
}
bool cSlotAreaBeacon :: IsPlaceableItem ( short a_ItemType )
{
switch ( a_ItemType )
{
case E_ITEM_EMERALD :
case E_ITEM_DIAMOND :
case E_ITEM_GOLD :
case E_ITEM_IRON :
{
return true ;
}
}
return false ;
}
void cSlotAreaBeacon :: Clicked ( cPlayer & a_Player , int a_SlotNum , eClickAction a_ClickAction , const cItem & a_ClickedItem )
{
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 ;
}
switch ( a_ClickAction )
{
case caShiftLeftClick :
case caShiftRightClick :
{
ShiftClicked ( a_Player , a_SlotNum , a_ClickedItem );
return ;
}
case caMiddleClick :
{
MiddleClicked ( a_Player , a_SlotNum );
return ;
}
case caDropKey :
case caCtrlDropKey :
{
DropClicked ( a_Player , a_SlotNum , false );
return ;
}
case caNumber1 :
case caNumber2 :
case caNumber3 :
case caNumber4 :
case caNumber5 :
case caNumber6 :
case caNumber7 :
case caNumber8 :
case caNumber9 :
{
NumberClicked ( a_Player , a_SlotNum , a_ClickAction );
return ;
}
default :
{
break ;
}
}
cItem Slot ( * GetSlot ( a_SlotNum , a_Player ));
if ( ! Slot . IsSameType ( a_ClickedItem ))
{
LOGWARNING ( "*** Window lost sync at item %d in SlotArea with %d items ***" , a_SlotNum , m_NumSlots );
LOGWARNING ( "My item: %s" , ItemToFullString ( Slot ). c_str ());
LOGWARNING ( "Their item: %s" , ItemToFullString ( a_ClickedItem ). c_str ());
bAsync = true ;
}
cItem & DraggingItem = a_Player . GetDraggingItem ();
if ( DraggingItem . IsEmpty ())
{
DraggingItem = Slot ;
Slot . Empty ();
}
else if ( Slot . IsEmpty ())
{
if ( ! IsPlaceableItem ( DraggingItem . m_ItemType ))
{
return ;
}
Slot = DraggingItem . CopyOne ();
DraggingItem . m_ItemCount -= 1 ;
if ( DraggingItem . m_ItemCount <= 0 )
{
DraggingItem . Empty ();
}
}
else if ( DraggingItem . m_ItemCount == 1 )
{
if ( ! IsPlaceableItem ( DraggingItem . m_ItemCount ))
{
return ;
}
// Switch contents
cItem tmp ( DraggingItem );
DraggingItem = Slot ;
Slot = tmp ;
}
SetSlot ( a_SlotNum , a_Player , Slot );
if ( bAsync )
{
m_ParentWindow . BroadcastWholeWindow ();
}
}
void cSlotAreaBeacon :: DistributeStack ( cItem & a_ItemStack , cPlayer & a_Player , bool a_ShouldApply , bool a_KeepEmptySlots )
{
const cItem * Slot = GetSlot ( 0 , a_Player );
if ( ! Slot -> IsEmpty () || ! IsPlaceableItem ( a_ItemStack . m_ItemType ) || ( a_ItemStack . m_ItemCount != 1 ))
{
return ;
}
if ( a_ShouldApply )
{
SetSlot ( 0 , a_Player , a_ItemStack . CopyOne ());
}
a_ItemStack . Empty ();
}
const cItem * cSlotAreaBeacon :: GetSlot ( int a_SlotNum , cPlayer & a_Player ) const
{
UNUSED ( a_Player );
return & ( m_Beacon -> GetSlot ( a_SlotNum ));
}
void cSlotAreaBeacon :: SetSlot ( int a_SlotNum , cPlayer & a_Player , const cItem & a_Item )
{
UNUSED ( a_Player );
m_Beacon -> SetSlot ( a_SlotNum , a_Item );
}
void cSlotAreaBeacon :: OnSlotChanged ( cItemGrid * a_ItemGrid , int a_SlotNum )
{
UNUSED ( a_SlotNum );
// Something has changed in the window, broadcast the entire window to all clients
ASSERT ( a_ItemGrid == & ( m_Beacon -> GetContents ()));
m_ParentWindow . BroadcastWholeWindow ();
}
2014-05-01 00:47:57 +02:00
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2014-01-20 18:22:08 +01:00
// cSlotAreaEnchanting:
2014-08-28 01:21:54 +02:00
cSlotAreaEnchanting :: cSlotAreaEnchanting ( cEnchantingWindow & a_ParentWindow , int a_BlockX , int a_BlockY , int a_BlockZ ) :
cSlotAreaTemporary ( 1 , a_ParentWindow ),
m_BlockX ( a_BlockX ),
m_BlockY ( a_BlockY ),
m_BlockZ ( a_BlockZ )
2014-01-20 18:22:08 +01:00
{
2014-04-15 21:10:44 +02:00
a_ParentWindow . m_SlotArea = this ;
2014-01-20 18:22:08 +01:00
}
2014-04-15 21:10:44 +02:00
void cSlotAreaEnchanting :: Clicked ( cPlayer & a_Player , int a_SlotNum , eClickAction a_ClickAction , const cItem & a_ClickedItem )
2014-04-14 11:48:12 +02:00
{
2014-04-15 21:10:44 +02:00
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 ;
}
2014-08-28 01:21:54 +02:00
2014-04-15 21:10:44 +02:00
switch ( a_ClickAction )
{
case caShiftLeftClick :
case caShiftRightClick :
{
ShiftClicked ( a_Player , a_SlotNum , a_ClickedItem );
return ;
}
case caDblClick :
{
2014-08-28 14:58:03 +02:00
DblClicked ( a_Player , a_SlotNum );
2014-04-15 21:10:44 +02:00
return ;
}
2014-07-05 14:00:04 +02:00
case caMiddleClick :
{
MiddleClicked ( a_Player , a_SlotNum );
return ;
}
2014-08-28 01:21:54 +02:00
case caDropKey :
case caCtrlDropKey :
{
DropClicked ( a_Player , a_SlotNum , false );
return ;
}
case caNumber1 :
case caNumber2 :
case caNumber3 :
case caNumber4 :
case caNumber5 :
case caNumber6 :
case caNumber7 :
case caNumber8 :
case caNumber9 :
{
NumberClicked ( a_Player , a_SlotNum , a_ClickAction );
return ;
}
2014-04-15 21:10:44 +02:00
default :
{
break ;
}
}
cItem Slot ( * GetSlot ( a_SlotNum , a_Player ));
if ( ! Slot . IsSameType ( a_ClickedItem ))
{
LOGWARNING ( "*** Window lost sync at item %d in SlotArea with %d items ***" , a_SlotNum , m_NumSlots );
LOGWARNING ( "My item: %s" , ItemToFullString ( Slot ). c_str ());
LOGWARNING ( "Their item: %s" , ItemToFullString ( a_ClickedItem ). c_str ());
bAsync = true ;
}
cItem & DraggingItem = a_Player . GetDraggingItem ();
2014-08-28 01:21:54 +02:00
if ( DraggingItem . IsEmpty ())
2014-04-15 21:10:44 +02:00
{
2014-08-28 15:21:50 +02:00
// DraggingItem is empty -> Switch draggingitem and slot
2014-08-28 01:21:54 +02:00
if ( ! Slot . IsEmpty ())
2014-04-15 21:10:44 +02:00
{
2014-08-28 14:58:03 +02:00
std :: swap ( DraggingItem , Slot );
2014-04-15 21:10:44 +02:00
}
2014-08-28 01:21:54 +02:00
}
else if ( Slot . IsEmpty ())
{
2014-08-28 15:21:50 +02:00
// DraggingItem isn't empty and slot is empty -> Set one dragging item in the slot
2014-08-28 01:21:54 +02:00
Slot = DraggingItem . CopyOne ();
DraggingItem . m_ItemCount -= 1 ;
if ( DraggingItem . m_ItemCount <= 0 )
2014-04-15 21:10:44 +02:00
{
2014-08-28 01:21:54 +02:00
DraggingItem . Empty ();
2014-04-15 21:10:44 +02:00
}
}
2014-08-28 01:21:54 +02:00
else if (( DraggingItem . m_ItemCount == 1 ) && ! DraggingItem . IsEqual ( Slot ))
2014-04-15 21:10:44 +02:00
{
2014-08-28 15:21:50 +02:00
// DraggingItem and slot aren't empty -> Switch items
2014-08-28 14:58:03 +02:00
std :: swap ( DraggingItem , Slot );
2014-04-15 21:10:44 +02:00
}
2014-08-28 01:21:54 +02:00
SetSlot ( a_SlotNum , a_Player , Slot );
if ( bAsync )
2014-04-15 21:10:44 +02:00
{
2014-08-28 01:21:54 +02:00
m_ParentWindow . BroadcastWholeWindow ();
2014-04-15 21:10:44 +02:00
}
}
2014-04-14 11:48:12 +02:00
2014-04-15 21:10:44 +02:00
void cSlotAreaEnchanting :: DistributeStack ( cItem & a_ItemStack , cPlayer & a_Player , bool a_Apply , bool a_KeepEmptySlots )
{
const cItem * Slot = GetSlot ( 0 , a_Player );
if ( ! Slot -> IsEmpty ())
2014-01-20 18:22:08 +01:00
{
2014-04-15 21:10:44 +02:00
return ;
2014-01-20 18:22:08 +01:00
}
2014-04-15 21:10:44 +02:00
if ( a_Apply )
{
SetSlot ( 0 , a_Player , a_ItemStack . CopyOne ());
}
a_ItemStack . m_ItemCount -= 1 ;
if ( a_ItemStack . m_ItemCount <= 0 )
{
a_ItemStack . Empty ();
}
2014-08-28 01:21:54 +02:00
}
2014-04-15 21:10:44 +02:00
2014-08-28 01:21:54 +02:00
void cSlotAreaEnchanting :: OnPlayerAdded ( cPlayer & a_Player )
{
super :: OnPlayerAdded ( a_Player );
2014-04-15 21:10:44 +02:00
UpdateResult ( a_Player );
}
void cSlotAreaEnchanting :: OnPlayerRemoved ( cPlayer & a_Player )
{
// Toss the item in the enchanting slot
TossItems ( a_Player , 0 , 1 );
2014-04-19 15:10:50 +02:00
super :: OnPlayerRemoved ( a_Player );
2014-01-20 18:22:08 +01:00
}
2014-08-28 01:21:54 +02:00
void cSlotAreaEnchanting :: SetSlot ( int a_SlotNum , cPlayer & a_Player , const cItem & a_Item )
{
super :: SetSlot ( a_SlotNum , a_Player , a_Item );
UpdateResult ( a_Player );
}
2014-04-15 21:10:44 +02:00
void cSlotAreaEnchanting :: UpdateResult ( cPlayer & a_Player )
2014-01-20 18:22:08 +01:00
{
2014-04-14 11:48:12 +02:00
cItem Item = * GetSlot ( 0 , a_Player );
2014-08-28 01:21:54 +02:00
if ( cItem :: IsEnchantable ( Item . m_ItemType ) && Item . m_Enchantments . IsEmpty ())
2014-01-20 18:22:08 +01:00
{
2014-04-17 13:15:35 +02:00
int Bookshelves = std :: min ( GetBookshelvesCount ( a_Player . GetWorld ()), 15 );
2014-04-07 19:52:35 +02:00
cFastRandom Random ;
2014-08-28 01:21:54 +02:00
int Base = ( Random . GenerateRandomInteger ( 1 , 8 ) + ( int ) floor (( float ) Bookshelves / 2 ) + Random . GenerateRandomInteger ( 0 , Bookshelves ));
int TopSlot = std :: max ( Base / 3 , 1 );
int MiddleSlot = ( Base * 2 ) / 3 + 1 ;
int BottomSlot = std :: max ( Base , Bookshelves * 2 );
2014-04-07 19:52:35 +02:00
2014-08-28 01:21:54 +02:00
m_ParentWindow . SetProperty ( 0 , TopSlot , a_Player );
m_ParentWindow . SetProperty ( 1 , MiddleSlot , a_Player );
m_ParentWindow . SetProperty ( 2 , BottomSlot , a_Player );
2014-01-20 18:22:08 +01:00
}
else
{
2014-04-12 14:58:46 +02:00
m_ParentWindow . SetProperty ( 0 , 0 , a_Player );
m_ParentWindow . SetProperty ( 1 , 0 , a_Player );
m_ParentWindow . SetProperty ( 2 , 0 , a_Player );
2014-01-20 18:22:08 +01:00
}
}
2014-04-15 21:10:44 +02:00
int cSlotAreaEnchanting :: GetBookshelvesCount ( cWorld * a_World )
2014-01-20 18:22:08 +01:00
{
2014-04-15 21:10:44 +02:00
int Bookshelves = 0 ;
cBlockArea Area ;
2014-08-28 01:21:54 +02:00
Area . Read ( a_World , m_BlockX - 2 , m_BlockX + 2 , m_BlockY , m_BlockY + 1 , m_BlockZ - 2 , m_BlockZ + 2 );
2014-01-20 18:22:08 +01:00
2014-04-17 13:15:35 +02:00
static const struct
{
int m_BookX , m_BookY , m_BookZ ; // Coords to check for bookcases
2014-04-19 15:10:50 +02:00
int m_AirX , m_AirY , m_AirZ ; // Coords to check for air; if not air, the bookcase won't be counted
2014-04-17 13:15:35 +02:00
} CheckCoords [] =
{
{ 0 , 0 , 0 , 1 , 0 , 1 }, // Bookcase at {0, 0, 0}, air at {1, 0, 1}
{ 0 , 0 , 1 , 1 , 0 , 1 }, // Bookcase at {0, 0, 1}, air at {1, 0, 1}
{ 0 , 0 , 2 , 1 , 0 , 2 }, // Bookcase at {0, 0, 2}, air at {1, 0, 2}
{ 0 , 0 , 3 , 1 , 0 , 3 }, // Bookcase at {0, 0, 3}, air at {1, 0, 3}
{ 0 , 0 , 4 , 1 , 0 , 3 }, // Bookcase at {0, 0, 4}, air at {1, 0, 3}
{ 1 , 0 , 4 , 1 , 0 , 3 }, // Bookcase at {1, 0, 4}, air at {1, 0, 3}
{ 2 , 0 , 4 , 2 , 0 , 3 }, // Bookcase at {2, 0, 4}, air at {2, 0, 3}
{ 3 , 0 , 4 , 3 , 0 , 3 }, // Bookcase at {3, 0, 4}, air at {3, 0, 3}
{ 4 , 0 , 4 , 3 , 0 , 3 }, // Bookcase at {4, 0, 4}, air at {3, 0, 3}
{ 4 , 0 , 3 , 3 , 0 , 3 }, // Bookcase at {4, 0, 3}, air at {3, 0, 3}
{ 4 , 0 , 2 , 3 , 0 , 2 }, // Bookcase at {4, 0, 2}, air at {3, 0, 2}
{ 4 , 0 , 1 , 3 , 0 , 1 }, // Bookcase at {4, 0, 1}, air at {3, 0, 1}
{ 4 , 0 , 0 , 3 , 0 , 1 }, // Bookcase at {4, 0, 0}, air at {3, 0, 1}
{ 3 , 0 , 0 , 3 , 0 , 1 }, // Bookcase at {3, 0, 0}, air at {3, 0, 1}
{ 2 , 0 , 0 , 2 , 0 , 1 }, // Bookcase at {2, 0, 0}, air at {2, 0, 1}
{ 1 , 0 , 0 , 1 , 0 , 1 }, // Bookcase at {1, 0, 0}, air at {1, 0, 1}
{ 0 , 1 , 0 , 1 , 1 , 1 }, // Bookcase at {0, 1, 0}, air at {1, 1, 1}
{ 0 , 1 , 1 , 1 , 1 , 1 }, // Bookcase at {0, 1, 1}, air at {1, 1, 1}
{ 0 , 1 , 2 , 1 , 1 , 2 }, // Bookcase at {0, 1, 2}, air at {1, 1, 2}
{ 0 , 1 , 3 , 1 , 1 , 3 }, // Bookcase at {0, 1, 3}, air at {1, 1, 3}
{ 0 , 1 , 4 , 1 , 1 , 3 }, // Bookcase at {0, 1, 4}, air at {1, 1, 3}
{ 1 , 1 , 4 , 1 , 1 , 3 }, // Bookcase at {1, 1, 4}, air at {1, 1, 3}
{ 2 , 1 , 4 , 2 , 1 , 3 }, // Bookcase at {2, 1, 4}, air at {2, 1, 3}
{ 3 , 1 , 4 , 3 , 1 , 3 }, // Bookcase at {3, 1, 4}, air at {3, 1, 3}
{ 4 , 1 , 4 , 3 , 1 , 3 }, // Bookcase at {4, 1, 4}, air at {3, 1, 3}
{ 4 , 1 , 3 , 3 , 1 , 3 }, // Bookcase at {4, 1, 3}, air at {3, 1, 3}
{ 4 , 1 , 2 , 3 , 1 , 2 }, // Bookcase at {4, 1, 2}, air at {3, 1, 2}
{ 4 , 1 , 1 , 3 , 1 , 1 }, // Bookcase at {4, 1, 1}, air at {3, 1, 1}
{ 4 , 1 , 0 , 3 , 1 , 1 }, // Bookcase at {4, 1, 0}, air at {3, 1, 1}
{ 3 , 1 , 0 , 3 , 1 , 1 }, // Bookcase at {3, 1, 0}, air at {3, 1, 1}
{ 2 , 1 , 0 , 2 , 1 , 1 }, // Bookcase at {2, 1, 0}, air at {2, 1, 1}
{ 1 , 1 , 0 , 1 , 1 , 1 }, // Bookcase at {1, 1, 0}, air at {1, 1, 1}
};
for ( size_t i = 0 ; i < ARRAYCOUNT ( CheckCoords ); i ++ )
{
if (
( Area . GetRelBlockType ( CheckCoords [ i ]. m_AirX , CheckCoords [ i ]. m_AirY , CheckCoords [ i ]. m_AirZ ) == E_BLOCK_AIR ) && // There's air in the checkspot
( Area . GetRelBlockType ( CheckCoords [ i ]. m_BookX , CheckCoords [ i ]. m_BookY , CheckCoords [ i ]. m_BookZ ) == E_BLOCK_BOOKCASE ) // There's bookcase in the wanted place
2014-08-28 01:21:54 +02:00
)
2014-04-15 21:10:44 +02:00
{
2014-04-17 13:15:35 +02:00
Bookshelves ++ ;
2014-04-15 21:10:44 +02:00
}
2014-04-17 13:15:35 +02:00
} // for i - CheckCoords
2014-04-15 21:10:44 +02:00
return Bookshelves ;
2014-04-14 22:05:04 +02:00
}
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2013-12-07 00:18:58 +00:00
// cSlotAreaEnderChest:
cSlotAreaEnderChest :: cSlotAreaEnderChest ( cEnderChestEntity * a_EnderChest , cWindow & a_ParentWindow ) :
cSlotArea ( 27 , a_ParentWindow ),
m_EnderChest ( a_EnderChest )
{
}
const cItem * cSlotAreaEnderChest :: GetSlot ( int a_SlotNum , cPlayer & a_Player ) const
{
2014-06-29 11:36:38 +01:00
return & ( a_Player . GetEnderChestContents (). GetSlot ( a_SlotNum ));
2013-12-07 00:18:58 +00:00
}
void cSlotAreaEnderChest :: SetSlot ( int a_SlotNum , cPlayer & a_Player , const cItem & a_Item )
{
2014-06-29 11:36:38 +01:00
a_Player . GetEnderChestContents (). SetSlot ( a_SlotNum , a_Item );
2013-12-07 00:18:58 +00:00
}
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2013-07-29 12:13:03 +01:00
// cSlotAreaFurnace:
cSlotAreaFurnace :: cSlotAreaFurnace ( cFurnaceEntity * a_Furnace , cWindow & a_ParentWindow ) :
cSlotArea ( 3 , a_ParentWindow ),
m_Furnace ( a_Furnace )
{
m_Furnace -> GetContents (). AddListener ( * this );
}
cSlotAreaFurnace ::~ cSlotAreaFurnace ()
{
m_Furnace -> GetContents (). RemoveListener ( * this );
}
void cSlotAreaFurnace :: Clicked ( cPlayer & a_Player , int a_SlotNum , eClickAction a_ClickAction , const cItem & a_ClickedItem )
{
if ( m_Furnace == NULL )
{
LOGERROR ( "cSlotAreaFurnace::Clicked(): m_Furnace == NULL" );
ASSERT ( ! "cSlotAreaFurnace::Clicked(): m_Furnace == NULL" );
return ;
}
2014-05-30 23:41:17 +02:00
if ( a_SlotNum == 2 )
{
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 . IsSameType ( a_ClickedItem ))
{
LOGWARNING ( "*** Window lost sync at item %d in SlotArea with %d items ***" , a_SlotNum , m_NumSlots );
LOGWARNING ( "My item: %s" , ItemToFullString ( Slot ). c_str ());
LOGWARNING ( "Their item: %s" , ItemToFullString ( a_ClickedItem ). c_str ());
bAsync = true ;
}
2014-07-06 00:40:59 +02:00
switch ( a_ClickAction )
2014-06-01 00:43:09 +02:00
{
2014-07-06 00:40:59 +02:00
case caShiftLeftClick :
case caShiftRightClick :
{
HandleSmeltItem ( Slot , a_Player );
ShiftClicked ( a_Player , a_SlotNum , Slot );
return ;
}
case caMiddleClick :
{
MiddleClicked ( a_Player , a_SlotNum );
return ;
}
case caDropKey :
case caCtrlDropKey :
{
DropClicked ( a_Player , a_SlotNum , ( a_SlotNum == caCtrlDropKey ));
Slot . m_ItemCount = Slot . m_ItemCount - GetSlot ( a_SlotNum , a_Player ) -> m_ItemCount ;
HandleSmeltItem ( Slot , a_Player );
return ;
}
default :
{
break ;
}
2014-06-01 00:43:09 +02:00
}
cItem & DraggingItem = a_Player . GetDraggingItem ();
2014-05-30 23:41:17 +02:00
if ( ! DraggingItem . IsEmpty ())
{
2014-05-31 00:25:20 +02:00
if ( a_ClickAction == caDblClick )
{
return ;
}
2014-05-30 23:41:17 +02:00
if ( ! DraggingItem . IsEqual ( Slot ))
{
return ;
}
if (( DraggingItem . m_ItemCount + Slot . m_ItemCount ) > Slot . GetMaxStackSize ())
{
return ;
}
DraggingItem . m_ItemCount += Slot . m_ItemCount ;
2014-06-01 00:43:09 +02:00
HandleSmeltItem ( Slot , a_Player );
2014-05-30 23:41:17 +02:00
Slot . Empty ();
}
else
{
switch ( a_ClickAction )
{
case caDblClick :
{
DblClicked ( a_Player , a_SlotNum );
return ;
}
case caLeftClick :
{
2014-05-31 00:27:24 +02:00
DraggingItem = Slot ;
2014-06-01 00:43:09 +02:00
HandleSmeltItem ( Slot , a_Player );
2014-05-30 23:41:17 +02:00
Slot . Empty ();
break ;
}
case caRightClick :
{
DraggingItem = Slot . CopyOne ();
DraggingItem . m_ItemCount = ( char )((( float ) Slot . m_ItemCount ) / 2.f + 0.5f );
Slot . m_ItemCount -= DraggingItem . m_ItemCount ;
2014-05-31 00:22:24 +02:00
if ( Slot . m_ItemCount <= 0 )
2014-05-30 23:41:17 +02:00
{
Slot . Empty ();
}
2014-06-01 00:43:09 +02:00
HandleSmeltItem ( DraggingItem , a_Player );
2014-05-30 23:41:17 +02:00
break ;
}
default :
{
ASSERT ( ! "Unhandled click type!" );
}
}
}
SetSlot ( a_SlotNum , a_Player , Slot );
if ( bAsync )
{
m_ParentWindow . BroadcastWholeWindow ();
}
return ;
}
super :: Clicked ( a_Player , a_SlotNum , a_ClickAction , a_ClickedItem );
2013-07-29 12:13:03 +01:00
}
2014-06-01 00:50:35 +02:00
void cSlotAreaFurnace :: DistributeStack ( cItem & a_ItemStack , cPlayer & a_Player , bool a_ShouldApply , bool a_KeepEmptySlots )
{
for ( int i = 0 ; i < 2 ; i ++ )
{
const cItem * Slot = GetSlot ( i , a_Player );
if ( ! Slot -> IsEqual ( 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_ShouldApply )
{
cItem NewSlot ( a_ItemStack );
NewSlot . m_ItemCount = Slot -> m_ItemCount + NumFit ;
SetSlot ( i , a_Player , NewSlot );
}
a_ItemStack . m_ItemCount -= NumFit ;
if ( a_ItemStack . IsEmpty ())
{
return ;
}
} // for i - Slots
}
2013-07-29 12:13:03 +01:00
const cItem * cSlotAreaFurnace :: GetSlot ( int a_SlotNum , cPlayer & a_Player ) const
{
2014-05-30 23:41:17 +02:00
UNUSED ( a_Player );
2013-07-29 12:13:03 +01:00
// 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 )
{
2014-05-30 23:41:17 +02:00
UNUSED ( a_Player );
2013-07-29 12:13:03 +01:00
m_Furnace -> SetSlot ( a_SlotNum , a_Item );
}
void cSlotAreaFurnace :: OnSlotChanged ( cItemGrid * a_ItemGrid , int a_SlotNum )
{
2014-05-30 23:41:17 +02:00
UNUSED ( a_SlotNum );
2013-07-29 12:13:03 +01:00
// Something has changed in the window, broadcast the entire window to all clients
ASSERT ( a_ItemGrid == & ( m_Furnace -> GetContents ()));
2014-05-12 17:05:09 +03:00
2013-07-29 12:13:03 +01:00
m_ParentWindow . BroadcastWholeWindow ();
}
2014-05-12 17:05:09 +03:00
void cSlotAreaFurnace :: HandleSmeltItem ( const cItem & a_Result , cPlayer & a_Player )
{
/** TODO 2014-05-12 xdot: Figure out when to call this method. */
switch ( a_Result . m_ItemType )
{
case E_ITEM_IRON : a_Player . AwardAchievement ( achAcquireIron ); break ;
case E_ITEM_COOKED_FISH : a_Player . AwardAchievement ( achCookFish ); break ;
default : break ;
}
}
2014-09-12 23:18:02 +01:00
////////////////////////////////////////////////////////////////////////////////
// cSlotAreaMinecartWithChest:
cSlotAreaMinecartWithChest :: cSlotAreaMinecartWithChest ( cMinecartWithChest * a_Chest , cWindow & a_ParentWindow ) :
cSlotArea ( 27 , a_ParentWindow ),
m_Chest ( a_Chest )
{
}
const cItem * cSlotAreaMinecartWithChest :: GetSlot ( int a_SlotNum , cPlayer & a_Player ) const
{
// a_SlotNum ranges from 0 to 26, use that to index the minecart chest entity's inventory directly:
UNUSED ( a_Player );
return & ( m_Chest -> GetSlot ( a_SlotNum ));
}
void cSlotAreaMinecartWithChest :: SetSlot ( int a_SlotNum , cPlayer & a_Player , const cItem & a_Item )
{
UNUSED ( a_Player );
m_Chest -> SetSlot ( a_SlotNum , a_Item );
}
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2013-07-29 12:13:03 +01:00
// cSlotAreaInventoryBase:
cSlotAreaInventoryBase :: cSlotAreaInventoryBase ( int a_NumSlots , int a_SlotOffset , cWindow & a_ParentWindow ) :
cSlotArea ( a_NumSlots , a_ParentWindow ),
m_SlotOffset ( a_SlotOffset )
{
}
void cSlotAreaInventoryBase :: Clicked ( cPlayer & a_Player , int a_SlotNum , eClickAction a_ClickAction , const cItem & a_ClickedItem )
{
2013-10-28 13:30:24 +01:00
if ( a_Player . IsGameModeCreative () && ( m_ParentWindow . GetWindowType () == cWindow :: wtInventory ))
2013-07-29 12:13:03 +01:00
{
2014-07-06 00:40:59 +02:00
if (( a_ClickAction == caDropKey ) || ( a_ClickAction == caCtrlDropKey ))
{
DropClicked ( a_Player , a_SlotNum , ( a_ClickAction == caCtrlDropKey ));
return ;
}
2013-07-29 12:13:03 +01:00
// 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_ClickAction , a_ClickedItem );
return ;
}
const cItem * cSlotAreaInventoryBase :: GetSlot ( int a_SlotNum , cPlayer & a_Player ) const
{
// a_SlotNum ranges from 0 to 35, map that to the player's inventory slots according to the internal offset
return & a_Player . GetInventory (). GetSlot ( a_SlotNum + m_SlotOffset );
}
void cSlotAreaInventoryBase :: SetSlot ( int a_SlotNum , cPlayer & a_Player , const cItem & a_Item )
{
a_Player . GetInventory (). SetSlot ( a_SlotNum + m_SlotOffset , a_Item );
}
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2013-07-29 12:13:03 +01:00
// 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 , a_ItemStack . CopyOne ());
}
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 , a_ItemStack . CopyOne ());
}
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 , a_ItemStack . CopyOne ());
}
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 , a_ItemStack . CopyOne ());
}
a_ItemStack . m_ItemCount -= 1 ;
}
}
2014-04-24 20:41:25 +02:00
void cSlotAreaArmor :: Clicked ( cPlayer & a_Player , int a_SlotNum , eClickAction a_ClickAction , const cItem & a_ClickedItem )
{
ASSERT (( a_SlotNum >= 0 ) && ( a_SlotNum < GetNumSlots ()));
2014-07-26 13:29:25 +02:00
// When the player is in creative mode, the client sends the new item as a_ClickedItem, not the current item in the slot.
2014-07-18 01:16:49 +02:00
if ( a_Player . IsGameModeCreative () && ( m_ParentWindow . GetWindowType () == cWindow :: wtInventory ))
{
if (( a_ClickAction == caDropKey ) || ( a_ClickAction == caCtrlDropKey ))
{
DropClicked ( a_Player , a_SlotNum , ( a_ClickAction == caCtrlDropKey ));
return ;
}
SetSlot ( a_SlotNum , a_Player , a_ClickedItem );
return ;
}
2014-04-24 20:41:25 +02:00
bool bAsync = false ;
if ( GetSlot ( a_SlotNum , a_Player ) == NULL )
{
LOGWARNING ( "GetSlot(%d) returned NULL! Ignoring click" , a_SlotNum );
return ;
}
2014-07-05 14:00:04 +02:00
switch ( a_ClickAction )
2014-04-24 20:41:25 +02:00
{
2014-07-05 14:00:04 +02:00
case caDblClick :
{
// Armors haven't a dbl click
return ;
}
case caShiftLeftClick :
case caShiftRightClick :
{
ShiftClicked ( a_Player , a_SlotNum , a_ClickedItem );
return ;
}
case caMiddleClick :
{
MiddleClicked ( a_Player , a_SlotNum );
return ;
}
default :
{
break ;
}
2014-04-24 20:41:25 +02:00
}
cItem Slot ( * GetSlot ( a_SlotNum , a_Player ));
if ( ! Slot . IsSameType ( a_ClickedItem ))
{
LOGWARNING ( "*** Window lost sync at item %d in SlotArea with %d items ***" , a_SlotNum , m_NumSlots );
LOGWARNING ( "My item: %s" , ItemToFullString ( Slot ). c_str ());
LOGWARNING ( "Their item: %s" , ItemToFullString ( a_ClickedItem ). c_str ());
bAsync = true ;
}
cItem & DraggingItem = a_Player . GetDraggingItem ();
if (( a_ClickAction != caRightClick ) && ( a_ClickAction != caLeftClick ))
{
LOGWARNING ( "SlotArea: Unhandled click action: %d (%s)" , a_ClickAction , ClickActionToString ( a_ClickAction ));
m_ParentWindow . BroadcastWholeWindow ();
return ;
}
2014-07-20 01:22:58 +02:00
if ( DraggingItem . IsEmpty () || CanPlaceArmorInSlot ( a_SlotNum , DraggingItem ))
2014-04-24 20:41:25 +02:00
{
// Swap contents
cItem tmp ( DraggingItem );
DraggingItem = Slot ;
Slot = tmp ;
}
SetSlot ( a_SlotNum , a_Player , Slot );
if ( bAsync )
{
m_ParentWindow . BroadcastWholeWindow ();
}
}
2014-07-20 01:22:58 +02:00
bool cSlotAreaArmor :: CanPlaceArmorInSlot ( int a_SlotNum , const cItem & a_Item )
2014-04-24 20:41:25 +02:00
{
switch ( a_SlotNum )
{
case 0 : return ItemCategory :: IsHelmet ( a_Item . m_ItemType );
case 1 : return ItemCategory :: IsChestPlate ( a_Item . m_ItemType );
case 2 : return ItemCategory :: IsLeggings ( a_Item . m_ItemType );
case 3 : return ItemCategory :: IsBoots ( a_Item . m_ItemType );
}
return false ;
}
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2013-07-29 12:13:03 +01:00
// cSlotAreaItemGrid:
cSlotAreaItemGrid :: cSlotAreaItemGrid ( cItemGrid & a_ItemGrid , cWindow & a_ParentWindow ) :
super ( a_ItemGrid . GetNumSlots (), a_ParentWindow ),
m_ItemGrid ( a_ItemGrid )
{
m_ItemGrid . AddListener ( * this );
}
cSlotAreaItemGrid ::~ cSlotAreaItemGrid ()
{
m_ItemGrid . RemoveListener ( * this );
}
const cItem * cSlotAreaItemGrid :: GetSlot ( int a_SlotNum , cPlayer & a_Player ) const
{
return & m_ItemGrid . GetSlot ( a_SlotNum );
}
void cSlotAreaItemGrid :: SetSlot ( int a_SlotNum , cPlayer & a_Player , const cItem & a_Item )
{
m_ItemGrid . SetSlot ( a_SlotNum , a_Item );
}
void cSlotAreaItemGrid :: OnSlotChanged ( cItemGrid * a_ItemGrid , int a_SlotNum )
{
ASSERT ( a_ItemGrid == & m_ItemGrid );
m_ParentWindow . BroadcastSlot ( this , a_SlotNum );
}
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2013-07-29 12:13:03 +01:00
// cSlotAreaTemporary:
cSlotAreaTemporary :: cSlotAreaTemporary ( int a_NumSlots , cWindow & a_ParentWindow ) :
cSlotArea ( a_NumSlots , a_ParentWindow )
{
}
const cItem * cSlotAreaTemporary :: GetSlot ( int a_SlotNum , cPlayer & a_Player ) const
{
cItemMap :: const_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 ;
}
return & ( itr -> second [ a_SlotNum ]);
}
void cSlotAreaTemporary :: SetSlot ( int a_SlotNum , cPlayer & a_Player , const cItem & a_Item )
{
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[]
double vX = 0 , vY = 0 , vZ = 0 ;
2014-01-17 11:11:17 +01:00
EulerToVector ( - a_Player . GetYaw (), a_Player . GetPitch (), vZ , vX , vY );
2013-07-29 12:13:03 +01:00
vY = - vY * 2 + 1.f ;
2014-07-17 22:15:34 +02:00
a_Player . GetWorld () -> SpawnItemPickups ( Drops , a_Player . GetPosX (), a_Player . GetPosY () + 1.6f , a_Player . GetPosZ (), vX * 3 , vY * 3 , vZ * 3 , true ); // 'true' because player created
2013-07-29 12:13:03 +01:00
}
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 ]);
}