2013-07-29 07:13:03 -04:00
// HopperEntity.cpp
// Implements the cHopperEntity representing a hopper block entity
# include "Globals.h"
# include "HopperEntity.h"
# include "../Chunk.h"
2013-08-19 05:39:13 -04:00
# include "../Entities/Player.h"
2014-02-11 17:54:01 -05:00
# include "../Entities/Pickup.h"
2013-12-08 06:17:54 -05:00
# include "../Bindings/PluginManager.h"
2014-12-13 09:06:55 -05:00
# include "../UI/HopperWindow.h"
2013-07-29 07:13:03 -04:00
# include "ChestEntity.h"
# include "FurnaceEntity.h"
2017-06-15 09:32:33 -04:00
cHopperEntity : : cHopperEntity ( BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta , int a_BlockX , int a_BlockY , int a_BlockZ , cWorld * a_World ) :
Super ( a_BlockType , a_BlockMeta , a_BlockX , a_BlockY , a_BlockZ , ContentsWidth , ContentsHeight , a_World ) ,
2013-07-29 07:13:03 -04:00
m_LastMoveItemsInTick ( 0 ) ,
m_LastMoveItemsOutTick ( 0 )
{
2017-06-15 09:32:33 -04:00
ASSERT ( a_BlockType = = E_BLOCK_HOPPER ) ;
2013-07-29 07:13:03 -04:00
}
bool cHopperEntity : : GetOutputBlockPos ( NIBBLETYPE a_BlockMeta , int & a_OutputX , int & a_OutputY , int & a_OutputZ )
{
a_OutputX = m_PosX ;
a_OutputY = m_PosY ;
a_OutputZ = m_PosZ ;
switch ( a_BlockMeta )
{
case E_META_HOPPER_FACING_XM : a_OutputX - - ; return true ;
case E_META_HOPPER_FACING_XP : a_OutputX + + ; return true ;
case E_META_HOPPER_FACING_YM : a_OutputY - - ; return true ;
case E_META_HOPPER_FACING_ZM : a_OutputZ - - ; return true ;
case E_META_HOPPER_FACING_ZP : a_OutputZ + + ; return true ;
default :
{
// Not attached
return false ;
}
}
}
2017-06-15 09:32:33 -04:00
void cHopperEntity : : CopyFrom ( const cBlockEntity & a_Src )
{
Super : : CopyFrom ( a_Src ) ;
auto & src = reinterpret_cast < const cHopperEntity & > ( a_Src ) ;
m_LastMoveItemsInTick = src . m_LastMoveItemsInTick ;
m_LastMoveItemsOutTick = src . m_LastMoveItemsOutTick ;
}
2015-01-11 16:12:26 -05:00
bool cHopperEntity : : Tick ( std : : chrono : : milliseconds a_Dt , cChunk & a_Chunk )
2013-07-29 07:13:03 -04:00
{
2014-02-24 14:29:59 -05:00
UNUSED ( a_Dt ) ;
2013-07-29 07:13:03 -04:00
Int64 CurrentTick = a_Chunk . GetWorld ( ) - > GetWorldAge ( ) ;
2014-10-11 22:39:55 -04:00
2017-06-15 09:32:33 -04:00
bool isDirty = false ;
isDirty = MoveItemsIn ( a_Chunk , CurrentTick ) | | isDirty ;
isDirty = MovePickupsIn ( a_Chunk , CurrentTick ) | | isDirty ;
isDirty = MoveItemsOut ( a_Chunk , CurrentTick ) | | isDirty ;
return isDirty ;
2013-07-29 07:13:03 -04:00
}
void cHopperEntity : : SendTo ( cClientHandle & a_Client )
{
// The hopper entity doesn't need anything sent to the client when it's created / gets in the viewdistance
// All the actual handling is in the cWindow UI code that gets called when the hopper is rclked
2014-10-11 22:39:55 -04:00
2013-07-29 07:13:03 -04:00
UNUSED ( a_Client ) ;
}
2015-12-01 17:12:44 -05:00
bool cHopperEntity : : UsedBy ( cPlayer * a_Player )
2013-07-29 07:13:03 -04:00
{
// If the window is not created, open it anew:
cWindow * Window = GetWindow ( ) ;
2014-10-20 16:55:07 -04:00
if ( Window = = nullptr )
2013-07-29 07:13:03 -04:00
{
OpenNewWindow ( ) ;
Window = GetWindow ( ) ;
}
2014-10-11 22:39:55 -04:00
2013-07-29 07:13:03 -04:00
// Open the window for the player:
2014-10-20 16:55:07 -04:00
if ( Window ! = nullptr )
2013-07-29 07:13:03 -04:00
{
if ( a_Player - > GetWindow ( ) ! = Window )
{
2017-05-29 15:33:30 -04:00
a_Player - > OpenWindow ( * Window ) ;
2013-07-29 07:13:03 -04:00
}
}
// This is rather a hack
// Instead of marking the chunk as dirty upon chest contents change, we mark it dirty now
// We cannot properly detect contents change, but such a change doesn't happen without a player opening the chest first.
// The few false positives aren't much to worry about
int ChunkX , ChunkZ ;
2013-08-03 14:05:07 -04:00
cChunkDef : : BlockToChunk ( m_PosX , m_PosZ , ChunkX , ChunkZ ) ;
2013-07-29 07:13:03 -04:00
m_World - > MarkChunkDirty ( ChunkX , ChunkZ ) ;
2015-12-01 17:12:44 -05:00
return true ;
2013-07-29 07:13:03 -04:00
}
void cHopperEntity : : OpenNewWindow ( void )
{
OpenWindow ( new cHopperWindow ( m_PosX , m_PosY , m_PosZ , this ) ) ;
}
bool cHopperEntity : : MoveItemsIn ( cChunk & a_Chunk , Int64 a_CurrentTick )
{
if ( m_PosY > = cChunkDef : : Height )
{
// This hopper is at the top of the world, no more blocks above
return false ;
}
if ( a_CurrentTick - m_LastMoveItemsInTick < TICKS_PER_TRANSFER )
{
// Too early after the previous transfer
return false ;
}
2014-10-11 22:39:55 -04:00
2013-07-29 07:13:03 -04:00
// Try moving an item in:
bool res = false ;
switch ( a_Chunk . GetBlock ( m_RelX , m_PosY + 1 , m_RelZ ) )
{
2014-07-06 18:50:22 -04:00
case E_BLOCK_TRAPPED_CHEST :
2013-08-11 08:57:07 -04:00
case E_BLOCK_CHEST :
{
// Chests have special handling because of double-chests
res = MoveItemsFromChest ( a_Chunk ) ;
break ;
}
case E_BLOCK_LIT_FURNACE :
case E_BLOCK_FURNACE :
{
// Furnaces have special handling because only the output and leftover fuel buckets shall be moved
res = MoveItemsFromFurnace ( a_Chunk ) ;
break ;
}
2013-07-29 07:13:03 -04:00
case E_BLOCK_DISPENSER :
2013-08-11 08:57:07 -04:00
case E_BLOCK_DROPPER :
case E_BLOCK_HOPPER :
{
2014-10-11 22:39:55 -04:00
res = MoveItemsFromGrid ( * static_cast < cBlockEntityWithItems * > ( a_Chunk . GetBlockEntity ( m_PosX , m_PosY + 1 , m_PosZ ) ) ) ;
2013-08-11 08:57:07 -04:00
break ;
}
2013-07-29 07:13:03 -04:00
}
2014-10-11 22:39:55 -04:00
2013-07-29 07:13:03 -04:00
// If the item has been moved, reset the last tick:
if ( res )
{
m_LastMoveItemsInTick = a_CurrentTick ;
}
2014-10-11 22:39:55 -04:00
2013-07-29 07:13:03 -04:00
return res ;
}
bool cHopperEntity : : MovePickupsIn ( cChunk & a_Chunk , Int64 a_CurrentTick )
{
2014-02-11 17:54:01 -05:00
UNUSED ( a_CurrentTick ) ;
class cHopperPickupSearchCallback :
public cEntityCallback
{
public :
2014-02-13 14:57:23 -05:00
cHopperPickupSearchCallback ( const Vector3i & a_Pos , cItemGrid & a_Contents ) :
2014-02-11 17:54:01 -05:00
m_Pos ( a_Pos ) ,
2014-02-12 17:01:22 -05:00
m_bFoundPickupsAbove ( false ) ,
m_Contents ( a_Contents )
2014-02-11 17:54:01 -05:00
{
}
virtual bool Item ( cEntity * a_Entity ) override
{
2014-10-20 16:55:07 -04:00
ASSERT ( a_Entity ! = nullptr ) ;
2014-02-11 17:54:01 -05:00
2016-02-07 12:07:14 -05:00
if ( ! a_Entity - > IsPickup ( ) )
2014-02-11 17:54:01 -05:00
{
return false ;
}
Vector3f EntityPos = a_Entity - > GetPosition ( ) ;
2014-10-11 22:39:55 -04:00
Vector3f BlockPos ( m_Pos . x + 0.5f , static_cast < float > ( m_Pos . y ) + 1 , m_Pos . z + 0.5f ) ; // One block above hopper, and search from center outwards
2014-03-16 17:00:28 -04:00
double Distance = ( EntityPos - BlockPos ) . Length ( ) ;
2014-02-11 17:54:01 -05:00
if ( Distance < 0.5 )
{
2014-10-11 22:39:55 -04:00
if ( TrySuckPickupIn ( static_cast < cPickup * > ( a_Entity ) ) )
2014-02-11 17:54:01 -05:00
{
2014-02-12 17:01:22 -05:00
return false ;
}
}
return false ;
}
bool TrySuckPickupIn ( cPickup * a_Pickup )
{
2014-04-18 15:09:44 -04:00
cItem & Item = a_Pickup - > GetItem ( ) ;
2014-02-12 17:01:22 -05:00
for ( int i = 0 ; i < ContentsWidth * ContentsHeight ; i + + )
{
if ( m_Contents . IsSlotEmpty ( i ) )
{
m_bFoundPickupsAbove = true ;
2014-04-18 15:09:44 -04:00
m_Contents . SetSlot ( i , Item ) ;
2014-07-17 16:15:34 -04:00
a_Pickup - > Destroy ( ) ; // Kill pickup
2014-02-13 15:20:37 -05:00
2014-02-12 17:01:22 -05:00
return true ;
}
2014-04-18 15:09:44 -04:00
else if ( m_Contents . GetSlot ( i ) . IsEqual ( Item ) & & ! m_Contents . GetSlot ( i ) . IsFullStack ( ) )
2014-02-12 17:01:22 -05:00
{
m_bFoundPickupsAbove = true ;
2014-02-13 15:20:37 -05:00
2014-02-12 17:01:22 -05:00
int PreviousCount = m_Contents . GetSlot ( i ) . m_ItemCount ;
2014-10-11 22:39:55 -04:00
2014-07-17 16:15:34 -04:00
Item . m_ItemCount - = m_Contents . ChangeSlotCount ( i , Item . m_ItemCount ) - PreviousCount ; // Set count to however many items were added
2014-10-11 22:39:55 -04:00
2014-04-18 15:09:44 -04:00
if ( Item . IsEmpty ( ) )
2014-02-11 17:54:01 -05:00
{
2014-07-17 16:15:34 -04:00
a_Pickup - > Destroy ( ) ; // Kill pickup if all items were added
2014-02-11 17:54:01 -05:00
}
2014-02-12 17:01:22 -05:00
return true ;
2014-02-11 17:54:01 -05:00
}
}
return false ;
}
bool FoundPickupsAbove ( void ) const
{
return m_bFoundPickupsAbove ;
}
protected :
2014-02-13 14:57:23 -05:00
Vector3i m_Pos ;
2014-02-11 17:54:01 -05:00
bool m_bFoundPickupsAbove ;
cItemGrid & m_Contents ;
} ;
cHopperPickupSearchCallback HopperPickupSearchCallback ( Vector3i ( GetPosX ( ) , GetPosY ( ) , GetPosZ ( ) ) , m_Contents ) ;
a_Chunk . ForEachEntity ( HopperPickupSearchCallback ) ;
return HopperPickupSearchCallback . FoundPickupsAbove ( ) ;
2013-07-29 07:13:03 -04:00
}
bool cHopperEntity : : MoveItemsOut ( cChunk & a_Chunk , Int64 a_CurrentTick )
{
if ( a_CurrentTick - m_LastMoveItemsOutTick < TICKS_PER_TRANSFER )
{
// Too early after the previous transfer
return false ;
}
2014-10-11 22:39:55 -04:00
2014-06-24 03:46:04 -04:00
// Get the coords of the block where to output items:
int OutX , OutY , OutZ ;
2013-07-29 07:13:03 -04:00
NIBBLETYPE Meta = a_Chunk . GetMeta ( m_RelX , m_PosY , m_RelZ ) ;
2014-06-24 03:46:04 -04:00
if ( ! GetOutputBlockPos ( Meta , OutX , OutY , OutZ ) )
2013-07-29 07:13:03 -04:00
{
// Not attached to another container
return false ;
}
2014-06-24 03:46:04 -04:00
if ( OutY < 0 )
2013-07-29 07:13:03 -04:00
{
// Cannot output below the zero-th block level
return false ;
}
2014-10-11 22:39:55 -04:00
2013-07-29 07:13:03 -04:00
// Convert coords to relative:
2014-06-24 03:46:04 -04:00
int OutRelX = OutX - a_Chunk . GetPosX ( ) * cChunkDef : : Width ;
int OutRelZ = OutZ - a_Chunk . GetPosZ ( ) * cChunkDef : : Width ;
cChunk * DestChunk = a_Chunk . GetRelNeighborChunkAdjustCoords ( OutRelX , OutRelZ ) ;
2014-10-20 16:55:07 -04:00
if ( DestChunk = = nullptr )
2013-07-29 07:13:03 -04:00
{
// The destination chunk has been unloaded, don't tick
return false ;
}
2014-10-11 22:39:55 -04:00
2013-07-29 07:13:03 -04:00
// Call proper moving function, based on the blocktype present at the coords:
bool res = false ;
2014-06-24 03:46:04 -04:00
switch ( DestChunk - > GetBlock ( OutRelX , OutY , OutRelZ ) )
2013-07-29 07:13:03 -04:00
{
2014-07-06 18:50:22 -04:00
case E_BLOCK_TRAPPED_CHEST :
2013-08-11 08:57:07 -04:00
case E_BLOCK_CHEST :
{
// Chests have special handling because of double-chests
2014-06-24 03:46:04 -04:00
res = MoveItemsToChest ( * DestChunk , OutX , OutY , OutZ ) ;
2013-08-11 08:57:07 -04:00
break ;
}
case E_BLOCK_LIT_FURNACE :
case E_BLOCK_FURNACE :
{
// Furnaces have special handling because of the direction-to-slot relation
2014-06-24 03:46:04 -04:00
res = MoveItemsToFurnace ( * DestChunk , OutX , OutY , OutZ , Meta ) ;
2013-08-11 08:57:07 -04:00
break ;
}
2013-07-29 07:13:03 -04:00
case E_BLOCK_DISPENSER :
2013-08-11 08:57:07 -04:00
case E_BLOCK_DROPPER :
case E_BLOCK_HOPPER :
{
2014-10-11 22:39:55 -04:00
cBlockEntityWithItems * BlockEntity = static_cast < cBlockEntityWithItems * > ( DestChunk - > GetBlockEntity ( OutX , OutY , OutZ ) ) ;
2014-10-20 16:55:07 -04:00
if ( BlockEntity = = nullptr )
2014-06-24 03:46:04 -04:00
{
LOGWARNING ( " %s: A block entity was not found where expected at {%d, %d, %d} " , __FUNCTION__ , OutX , OutY , OutZ ) ;
return false ;
}
res = MoveItemsToGrid ( * BlockEntity ) ;
2013-08-11 08:57:07 -04:00
break ;
}
2013-07-29 07:13:03 -04:00
}
2014-10-11 22:39:55 -04:00
2013-07-29 07:13:03 -04:00
// If the item has been moved, reset the last tick:
if ( res )
{
m_LastMoveItemsOutTick = a_CurrentTick ;
}
2014-10-11 22:39:55 -04:00
2013-07-29 07:13:03 -04:00
return res ;
}
bool cHopperEntity : : MoveItemsFromChest ( cChunk & a_Chunk )
{
2014-10-11 22:39:55 -04:00
cChestEntity * MainChest = static_cast < cChestEntity * > ( a_Chunk . GetBlockEntity ( m_PosX , m_PosY + 1 , m_PosZ ) ) ;
2014-10-20 16:55:07 -04:00
if ( MainChest = = nullptr )
2014-06-24 03:46:04 -04:00
{
LOGWARNING ( " %s: A chest entity was not found where expected, at {%d, %d, %d} " , __FUNCTION__ , m_PosX , m_PosY + 1 , m_PosZ ) ;
return false ;
}
2014-07-13 10:01:49 -04:00
if ( MoveItemsFromGrid ( * MainChest ) )
2013-07-29 07:13:03 -04:00
{
// Moved the item from the chest directly above the hopper
return true ;
}
2014-10-11 22:39:55 -04:00
2014-07-12 17:06:25 -04:00
// Check if the chest is a double-chest (chest directly above was empty), if so, try to move from there:
2013-07-29 07:13:03 -04:00
static const struct
{
int x , z ;
}
Coords [ ] =
{
{ 1 , 0 } ,
{ - 1 , 0 } ,
{ 0 , 1 } ,
{ 0 , - 1 } ,
} ;
2013-12-20 10:01:34 -05:00
for ( size_t i = 0 ; i < ARRAYCOUNT ( Coords ) ; i + + )
2013-07-29 07:13:03 -04:00
{
int x = m_RelX + Coords [ i ] . x ;
int z = m_RelZ + Coords [ i ] . z ;
cChunk * Neighbor = a_Chunk . GetRelNeighborChunkAdjustCoords ( x , z ) ;
2014-10-20 16:55:07 -04:00
if ( Neighbor = = nullptr )
2013-07-29 07:13:03 -04:00
{
continue ;
}
2014-07-06 18:50:22 -04:00
BLOCKTYPE Block = Neighbor - > GetBlock ( x , m_PosY + 1 , z ) ;
2014-07-13 10:01:49 -04:00
if ( Block ! = MainChest - > GetBlockType ( ) )
2014-07-06 18:50:22 -04:00
{
2014-07-12 17:34:32 -04:00
// Not the same kind of chest
2014-07-06 18:50:22 -04:00
continue ;
}
2014-10-11 22:39:55 -04:00
cChestEntity * SideChest = static_cast < cChestEntity * > ( Neighbor - > GetBlockEntity ( m_PosX + Coords [ i ] . x , m_PosY + 1 , m_PosZ + Coords [ i ] . z ) ) ;
2014-10-20 16:55:07 -04:00
if ( SideChest = = nullptr )
2013-07-29 07:13:03 -04:00
{
2014-06-24 03:46:04 -04:00
LOGWARNING ( " %s: A chest entity was not found where expected, at {%d, %d, %d} " , __FUNCTION__ , m_PosX + Coords [ i ] . x , m_PosY + 1 , m_PosZ + Coords [ i ] . z ) ;
}
else
{
2014-07-13 10:01:49 -04:00
if ( MoveItemsFromGrid ( * SideChest ) )
2014-06-24 03:46:04 -04:00
{
return true ;
}
2013-07-29 07:13:03 -04:00
}
return false ;
}
2014-10-11 22:39:55 -04:00
2013-07-29 07:13:03 -04:00
// The chest was single and nothing could be moved
return false ;
}
bool cHopperEntity : : MoveItemsFromFurnace ( cChunk & a_Chunk )
{
2014-10-11 22:39:55 -04:00
cFurnaceEntity * Furnace = static_cast < cFurnaceEntity * > ( a_Chunk . GetBlockEntity ( m_PosX , m_PosY + 1 , m_PosZ ) ) ;
2014-10-20 16:55:07 -04:00
if ( Furnace = = nullptr )
2014-06-24 03:46:04 -04:00
{
LOGWARNING ( " %s: A furnace entity was not found where expected, at {%d, %d, %d} " , __FUNCTION__ , m_PosX , m_PosY + 1 , m_PosZ ) ;
return false ;
}
2014-10-11 22:39:55 -04:00
2013-07-29 07:13:03 -04:00
// Try move from the output slot:
2015-05-11 17:58:27 -04:00
if ( MoveItemsFromSlot ( * Furnace , cFurnaceEntity : : fsOutput ) )
2013-07-29 07:13:03 -04:00
{
cItem NewOutput ( Furnace - > GetOutputSlot ( ) ) ;
Furnace - > SetOutputSlot ( NewOutput . AddCount ( - 1 ) ) ;
return true ;
}
2014-10-11 22:39:55 -04:00
2013-07-29 07:13:03 -04:00
// No output moved, check if we can move an empty bucket out of the fuel slot:
if ( Furnace - > GetFuelSlot ( ) . m_ItemType = = E_ITEM_BUCKET )
{
2015-05-11 17:58:27 -04:00
if ( MoveItemsFromSlot ( * Furnace , cFurnaceEntity : : fsFuel ) )
2013-07-29 07:13:03 -04:00
{
Furnace - > SetFuelSlot ( cItem ( ) ) ;
return true ;
}
}
2014-10-11 22:39:55 -04:00
2013-07-29 07:13:03 -04:00
// Nothing can be moved
return false ;
}
2013-08-11 08:57:07 -04:00
bool cHopperEntity : : MoveItemsFromGrid ( cBlockEntityWithItems & a_Entity )
2013-07-29 07:13:03 -04:00
{
2013-08-11 08:57:07 -04:00
cItemGrid & Grid = a_Entity . GetContents ( ) ;
int NumSlots = Grid . GetNumSlots ( ) ;
2014-10-11 22:39:55 -04:00
2013-07-29 07:13:03 -04:00
for ( int i = 0 ; i < NumSlots ; i + + )
{
2013-08-11 08:57:07 -04:00
if ( Grid . IsSlotEmpty ( i ) )
2013-07-29 07:13:03 -04:00
{
continue ;
}
2015-05-11 17:58:27 -04:00
if ( MoveItemsFromSlot ( a_Entity , i ) )
2013-07-29 07:13:03 -04:00
{
2013-08-11 08:57:07 -04:00
Grid . ChangeSlotCount ( i , - 1 ) ;
2013-07-29 07:13:03 -04:00
return true ;
}
}
return false ;
}
2015-05-11 17:58:27 -04:00
bool cHopperEntity : : MoveItemsFromSlot ( cBlockEntityWithItems & a_Entity , int a_SlotNum )
2013-07-29 07:13:03 -04:00
{
2013-08-11 08:57:07 -04:00
cItem One ( a_Entity . GetSlot ( a_SlotNum ) . CopyOne ( ) ) ;
for ( int i = 0 ; i < ContentsWidth * ContentsHeight ; i + + )
2013-07-29 07:13:03 -04:00
{
2013-08-11 08:57:07 -04:00
if ( m_Contents . IsSlotEmpty ( i ) )
{
2015-05-11 17:58:27 -04:00
if ( cPluginManager : : Get ( ) - > CallHookHopperPullingItem ( * m_World , * this , i , a_Entity , a_SlotNum ) )
2013-08-11 08:57:07 -04:00
{
2015-05-11 17:58:27 -04:00
// Plugin disagrees with the move
continue ;
2013-08-11 08:57:07 -04:00
}
2015-05-11 17:58:27 -04:00
2013-08-11 08:57:07 -04:00
m_Contents . SetSlot ( i , One ) ;
return true ;
}
2014-01-16 14:00:49 -05:00
else if ( m_Contents . GetSlot ( i ) . IsEqual ( One ) )
2013-08-11 08:57:07 -04:00
{
if ( cPluginManager : : Get ( ) - > CallHookHopperPullingItem ( * m_World , * this , i , a_Entity , a_SlotNum ) )
{
// Plugin disagrees with the move
continue ;
}
2014-10-11 22:39:55 -04:00
2015-05-11 17:58:27 -04:00
auto PreviousCount = m_Contents . GetSlot ( i ) . m_ItemCount ;
2013-08-11 08:57:07 -04:00
m_Contents . ChangeSlotCount ( i , 1 ) ;
2015-05-11 17:58:27 -04:00
2015-06-16 19:27:50 -04:00
if ( PreviousCount + 1 = = m_Contents . GetSlot ( i ) . m_ItemCount )
2015-05-11 17:58:27 -04:00
{
// Successfully added a new item. (Failure condition consistutes: stack full)
return true ;
}
2013-08-11 08:57:07 -04:00
}
2013-07-29 07:13:03 -04:00
}
return false ;
}
bool cHopperEntity : : MoveItemsToChest ( cChunk & a_Chunk , int a_BlockX , int a_BlockY , int a_BlockZ )
{
// Try the chest directly connected to the hopper:
2014-10-11 22:39:55 -04:00
cChestEntity * ConnectedChest = static_cast < cChestEntity * > ( a_Chunk . GetBlockEntity ( a_BlockX , a_BlockY , a_BlockZ ) ) ;
2014-10-20 16:55:07 -04:00
if ( ConnectedChest = = nullptr )
2014-06-24 03:46:04 -04:00
{
LOGWARNING ( " %s: A chest entity was not found where expected, at {%d, %d, %d} " , __FUNCTION__ , a_BlockX , a_BlockY , a_BlockZ ) ;
return false ;
}
2014-08-10 17:06:56 -04:00
if ( MoveItemsToGrid ( * ConnectedChest ) )
2013-07-29 07:13:03 -04:00
{
2014-07-12 17:06:25 -04:00
// Chest block directly connected was not full
2013-07-29 07:13:03 -04:00
return true ;
}
2014-07-12 17:06:25 -04:00
// Check if the chest is a double-chest (chest block directly connected was full), if so, try to move into the other half:
2013-07-29 07:13:03 -04:00
static const struct
{
int x , z ;
}
Coords [ ] =
{
{ 1 , 0 } ,
{ - 1 , 0 } ,
{ 0 , 1 } ,
{ 0 , - 1 } ,
} ;
2014-06-24 03:46:04 -04:00
int RelX = a_BlockX - a_Chunk . GetPosX ( ) * cChunkDef : : Width ;
int RelZ = a_BlockZ - a_Chunk . GetPosZ ( ) * cChunkDef : : Width ;
2013-12-20 10:01:34 -05:00
for ( size_t i = 0 ; i < ARRAYCOUNT ( Coords ) ; i + + )
2013-07-29 07:13:03 -04:00
{
2014-06-24 03:46:04 -04:00
int x = RelX + Coords [ i ] . x ;
int z = RelZ + Coords [ i ] . z ;
2013-07-29 07:13:03 -04:00
cChunk * Neighbor = a_Chunk . GetRelNeighborChunkAdjustCoords ( x , z ) ;
2014-10-20 16:55:07 -04:00
if ( Neighbor = = nullptr )
2013-07-29 07:13:03 -04:00
{
continue ;
}
2014-07-06 18:50:22 -04:00
BLOCKTYPE Block = Neighbor - > GetBlock ( x , a_BlockY , z ) ;
2014-08-10 17:06:56 -04:00
if ( Block ! = ConnectedChest - > GetBlockType ( ) )
2014-07-06 18:50:22 -04:00
{
2014-07-12 17:34:32 -04:00
// Not the same kind of chest
2014-07-06 18:50:22 -04:00
continue ;
}
2014-10-11 22:39:55 -04:00
cChestEntity * Chest = static_cast < cChestEntity * > ( Neighbor - > GetBlockEntity ( a_BlockX + Coords [ i ] . x , a_BlockY , a_BlockZ + Coords [ i ] . z ) ) ;
2014-10-20 16:55:07 -04:00
if ( Chest = = nullptr )
2014-06-24 03:46:04 -04:00
{
LOGWARNING ( " %s: A chest entity was not found where expected, at {%d, %d, %d} (%d, %d) " , __FUNCTION__ , a_BlockX + Coords [ i ] . x , a_BlockY , a_BlockZ + Coords [ i ] . z , x , z ) ;
continue ;
}
if ( MoveItemsToGrid ( * Chest ) )
2013-07-29 07:13:03 -04:00
{
return true ;
}
return false ;
}
2014-10-11 22:39:55 -04:00
2013-07-29 07:13:03 -04:00
// The chest was single and nothing could be moved
return false ;
}
bool cHopperEntity : : MoveItemsToFurnace ( cChunk & a_Chunk , int a_BlockX , int a_BlockY , int a_BlockZ , NIBBLETYPE a_HopperMeta )
{
2014-10-11 22:39:55 -04:00
cFurnaceEntity * Furnace = static_cast < cFurnaceEntity * > ( a_Chunk . GetBlockEntity ( a_BlockX , a_BlockY , a_BlockZ ) ) ;
2013-07-29 07:13:03 -04:00
if ( a_HopperMeta = = E_META_HOPPER_FACING_YM )
{
// Feed the input slot of the furnace
2013-08-11 08:57:07 -04:00
return MoveItemsToSlot ( * Furnace , cFurnaceEntity : : fsInput ) ;
2013-07-29 07:13:03 -04:00
}
else
{
// Feed the fuel slot of the furnace
2013-08-11 08:57:07 -04:00
return MoveItemsToSlot ( * Furnace , cFurnaceEntity : : fsFuel ) ;
2013-07-29 07:13:03 -04:00
}
}
2013-08-11 08:57:07 -04:00
bool cHopperEntity : : MoveItemsToGrid ( cBlockEntityWithItems & a_Entity )
2013-07-29 07:13:03 -04:00
{
// Iterate through our slots, try to move from each one:
2013-08-11 08:57:07 -04:00
int NumSlots = a_Entity . GetContents ( ) . GetNumSlots ( ) ;
for ( int i = 0 ; i < NumSlots ; i + + )
2013-07-29 07:13:03 -04:00
{
2013-08-11 08:57:07 -04:00
if ( MoveItemsToSlot ( a_Entity , i ) )
2013-07-29 07:13:03 -04:00
{
return true ;
}
}
return false ;
}
2013-08-11 08:57:07 -04:00
bool cHopperEntity : : MoveItemsToSlot ( cBlockEntityWithItems & a_Entity , int a_DstSlotNum )
2013-07-29 07:13:03 -04:00
{
2013-08-11 08:57:07 -04:00
cItemGrid & Grid = a_Entity . GetContents ( ) ;
if ( Grid . IsSlotEmpty ( a_DstSlotNum ) )
2013-07-29 07:13:03 -04:00
{
// The slot is empty, move the first non-empty slot from our contents:
for ( int i = 0 ; i < ContentsWidth * ContentsHeight ; i + + )
{
if ( ! m_Contents . IsSlotEmpty ( i ) )
{
2013-08-11 08:57:07 -04:00
if ( cPluginManager : : Get ( ) - > CallHookHopperPushingItem ( * m_World , * this , i , a_Entity , a_DstSlotNum ) )
{
// A plugin disagrees with the move
continue ;
}
Grid . SetSlot ( a_DstSlotNum , m_Contents . GetSlot ( i ) . CopyOne ( ) ) ;
2013-07-29 07:13:03 -04:00
m_Contents . ChangeSlotCount ( i , - 1 ) ;
return true ;
}
}
return false ;
}
else
{
// The slot is taken, try to top it up:
2013-08-11 08:57:07 -04:00
const cItem & DestSlot = Grid . GetSlot ( a_DstSlotNum ) ;
2013-07-29 07:13:03 -04:00
if ( DestSlot . IsFullStack ( ) )
{
return false ;
}
for ( int i = 0 ; i < ContentsWidth * ContentsHeight ; i + + )
{
2014-01-16 14:00:49 -05:00
if ( m_Contents . GetSlot ( i ) . IsEqual ( DestSlot ) )
2013-07-29 07:13:03 -04:00
{
2013-08-11 08:57:07 -04:00
if ( cPluginManager : : Get ( ) - > CallHookHopperPushingItem ( * m_World , * this , i , a_Entity , a_DstSlotNum ) )
{
// A plugin disagrees with the move
continue ;
}
Grid . ChangeSlotCount ( a_DstSlotNum , 1 ) ;
2013-07-29 07:13:03 -04:00
m_Contents . ChangeSlotCount ( i , - 1 ) ;
return true ;
}
}
return false ;
}
}
2015-07-31 10:49:10 -04:00