2013-07-29 07:13:03 -04:00
// DropSpenserEntity.cpp
// Declares the cDropSpenserEntity class representing a common ancestor to the cDispenserEntity and cDropperEntity
// The dropper and dispenser only needs to override the DropSpenseFromSlot() function to provide the specific item behavior
# include "Globals.h"
# include "DropSpenserEntity.h"
2020-09-21 11:31:45 -04:00
# include "../Bindings/PluginManager.h"
2015-11-23 18:39:19 -05:00
# include "../EffectID.h"
2013-08-19 05:39:13 -04:00
# include "../Entities/Player.h"
2013-07-29 07:13:03 -04:00
# include "../Chunk.h"
2014-12-13 09:06:55 -05:00
# include "../UI/DropSpenserWindow.h"
2013-07-29 07:13:03 -04:00
2019-09-29 08:59:24 -04:00
cDropSpenserEntity : : cDropSpenserEntity ( BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta , Vector3i a_Pos , cWorld * a_World ) :
2020-04-13 12:38:06 -04:00
Super ( a_BlockType , a_BlockMeta , a_Pos , ContentsWidth , ContentsHeight , a_World ) ,
2015-06-26 18:24:51 -04:00
m_ShouldDropSpense ( false )
2013-07-29 07:13:03 -04:00
{
}
2019-09-29 08:59:24 -04:00
void cDropSpenserEntity : : AddDropSpenserDir ( Vector3i & a_RelCoord , NIBBLETYPE a_Direction )
2013-07-29 07:13:03 -04:00
{
2016-06-04 08:16:35 -04:00
switch ( a_Direction & E_META_DROPSPENSER_FACING_MASK )
2013-07-29 07:13:03 -04:00
{
2019-09-29 08:59:24 -04:00
case E_META_DROPSPENSER_FACING_YM : a_RelCoord . y - - ; return ;
case E_META_DROPSPENSER_FACING_YP : a_RelCoord . y + + ; return ;
case E_META_DROPSPENSER_FACING_ZM : a_RelCoord . z - - ; return ;
case E_META_DROPSPENSER_FACING_ZP : a_RelCoord . z + + ; return ;
case E_META_DROPSPENSER_FACING_XM : a_RelCoord . x - - ; return ;
case E_META_DROPSPENSER_FACING_XP : a_RelCoord . x + + ; return ;
2013-07-29 07:13:03 -04:00
}
LOGWARNING ( " %s: Unhandled direction: %d " , __FUNCTION__ , a_Direction ) ;
}
void cDropSpenserEntity : : DropSpense ( cChunk & a_Chunk )
{
// Pick one of the occupied slots:
int OccupiedSlots [ 9 ] ;
int SlotsCnt = 0 ;
for ( int i = m_Contents . GetNumSlots ( ) - 1 ; i > = 0 ; i - - )
{
if ( ! m_Contents . GetSlot ( i ) . IsEmpty ( ) )
{
OccupiedSlots [ SlotsCnt ] = i ;
SlotsCnt + + ;
}
} // for i - m_Contents[]
2016-02-05 16:45:45 -05:00
2013-07-29 07:13:03 -04:00
if ( SlotsCnt = = 0 )
{
// Nothing in the dropspenser, play the click sound
2019-09-29 08:59:24 -04:00
m_World - > BroadcastSoundEffect ( " block.dispenser.fail " , m_Pos , 1.0f , 1.2f ) ;
2013-07-29 07:13:03 -04:00
return ;
}
2016-02-05 16:45:45 -05:00
2020-09-22 06:08:12 -04:00
const int RandomSlot = m_World - > GetTickRandomNumber ( SlotsCnt - 1 ) ;
const int SpenseSlot = OccupiedSlots [ RandomSlot ] ;
2020-09-21 11:31:45 -04:00
if ( cPluginManager : : Get ( ) - > CallHookDropSpense ( * m_World , * this , SpenseSlot ) )
{
// Plugin disagrees with the move
return ;
}
2013-07-29 07:13:03 -04:00
// DropSpense the item, using the specialized behavior in the subclasses:
2020-09-21 11:31:45 -04:00
DropSpenseFromSlot ( a_Chunk , SpenseSlot ) ;
2016-02-05 16:45:45 -05:00
2013-07-29 07:13:03 -04:00
// Broadcast a smoke and click effects:
2019-09-29 08:59:24 -04:00
NIBBLETYPE Meta = a_Chunk . GetMeta ( GetRelPos ( ) ) ;
2013-07-29 07:13:03 -04:00
int SmokeDir = 0 ;
2016-06-04 08:16:35 -04:00
switch ( Meta & E_META_DROPSPENSER_FACING_MASK )
2013-07-29 07:13:03 -04:00
{
2015-11-23 18:39:19 -05:00
case E_META_DROPSPENSER_FACING_YP : SmokeDir = static_cast < int > ( SmokeDirection : : CENTRE ) ; break ; // YP & YM don't have associated smoke dirs, just do 4 (centre of block)
case E_META_DROPSPENSER_FACING_YM : SmokeDir = static_cast < int > ( SmokeDirection : : CENTRE ) ; break ;
case E_META_DROPSPENSER_FACING_XM : SmokeDir = static_cast < int > ( SmokeDirection : : EAST ) ; break ;
case E_META_DROPSPENSER_FACING_XP : SmokeDir = static_cast < int > ( SmokeDirection : : WEST ) ; break ;
case E_META_DROPSPENSER_FACING_ZM : SmokeDir = static_cast < int > ( SmokeDirection : : SOUTH ) ; break ;
case E_META_DROPSPENSER_FACING_ZP : SmokeDir = static_cast < int > ( SmokeDirection : : NORTH ) ; break ;
2013-07-29 07:13:03 -04:00
}
2018-07-26 19:12:41 -04:00
m_World - > BroadcastSoundParticleEffect ( EffectID : : PARTICLE_SMOKE , GetPos ( ) , SmokeDir ) ;
2019-09-29 08:59:24 -04:00
m_World - > BroadcastSoundEffect ( " block.dispenser.dispense " , m_Pos , 1.0f , 1.0f ) ;
2014-07-17 16:50:58 -04:00
}
2013-07-29 07:13:03 -04:00
void cDropSpenserEntity : : Activate ( void )
{
m_ShouldDropSpense = true ;
}
2017-06-15 09:32:33 -04:00
void cDropSpenserEntity : : CopyFrom ( const cBlockEntity & a_Src )
{
2020-04-13 12:38:06 -04:00
Super : : CopyFrom ( a_Src ) ;
2018-05-02 03:50:36 -04:00
auto & src = static_cast < const cDropSpenserEntity & > ( a_Src ) ;
2017-06-15 09:32:33 -04:00
m_Contents . CopyFrom ( src . m_Contents ) ;
m_ShouldDropSpense = src . m_ShouldDropSpense ;
}
2021-01-02 08:50:34 -05:00
void cDropSpenserEntity : : OnRemoveFromWorld ( )
{
const auto Window = GetWindow ( ) ;
if ( Window ! = nullptr )
{
// Tell window its owner is destroyed:
Window - > OwnerDestroyed ( ) ;
}
}
2015-01-11 16:12:26 -05:00
bool cDropSpenserEntity : : 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
if ( ! m_ShouldDropSpense )
{
return false ;
}
2016-02-05 16:45:45 -05:00
2013-07-29 07:13:03 -04:00
m_ShouldDropSpense = false ;
DropSpense ( a_Chunk ) ;
return true ;
}
void cDropSpenserEntity : : SendTo ( cClientHandle & a_Client )
{
// Nothing needs to be sent
UNUSED ( a_Client ) ;
}
2015-12-01 17:12:44 -05:00
bool cDropSpenserEntity : : UsedBy ( cPlayer * a_Player )
2013-07-29 07:13:03 -04:00
{
2020-09-05 11:13:44 -04:00
if ( m_BlockType = = E_BLOCK_DISPENSER )
{
2021-05-03 16:07:09 -04:00
a_Player - > GetStatistics ( ) . Custom [ CustomStatistic : : InspectDispenser ] + + ;
2020-09-05 11:13:44 -04:00
}
else // E_BLOCK_DROPPER
{
2021-05-03 16:07:09 -04:00
a_Player - > GetStatistics ( ) . Custom [ CustomStatistic : : InspectDropper ] + + ;
2020-09-05 11:13:44 -04:00
}
2013-07-29 07:13:03 -04:00
cWindow * Window = GetWindow ( ) ;
2014-10-20 16:55:07 -04:00
if ( Window = = nullptr )
2013-07-29 07:13:03 -04:00
{
2019-09-29 08:59:24 -04:00
OpenWindow ( new cDropSpenserWindow ( this ) ) ;
2013-07-29 07:13:03 -04:00
Window = GetWindow ( ) ;
}
2016-02-05 16:45:45 -05:00
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
}
}
2015-12-01 17:12:44 -05:00
return true ;
2013-07-29 07:13:03 -04:00
}
void cDropSpenserEntity : : DropFromSlot ( cChunk & a_Chunk , int a_SlotNum )
{
2019-09-29 08:59:24 -04:00
Vector3i dispCoord ( m_Pos ) ;
auto Meta = a_Chunk . GetMeta ( GetRelPos ( ) ) ;
AddDropSpenserDir ( dispCoord , Meta ) ;
2013-07-29 07:13:03 -04:00
cItems Pickups ;
Pickups . push_back ( m_Contents . RemoveOneItem ( a_SlotNum ) ) ;
2013-11-02 10:09:07 -04:00
2017-06-13 15:35:30 -04:00
const int PickupSpeed = GetRandomProvider ( ) . RandInt ( 2 , 6 ) ; // At least 2, at most 6
2013-11-02 10:09:07 -04:00
int PickupSpeedX = 0 , PickupSpeedY = 0 , PickupSpeedZ = 0 ;
2016-06-04 08:16:35 -04:00
switch ( Meta & E_META_DROPSPENSER_FACING_MASK )
2013-11-02 10:09:07 -04:00
{
case E_META_DROPSPENSER_FACING_YP : PickupSpeedY = PickupSpeed ; break ;
case E_META_DROPSPENSER_FACING_YM : PickupSpeedY = - PickupSpeed ; break ;
case E_META_DROPSPENSER_FACING_XM : PickupSpeedX = - PickupSpeed ; break ;
case E_META_DROPSPENSER_FACING_XP : PickupSpeedX = PickupSpeed ; break ;
case E_META_DROPSPENSER_FACING_ZM : PickupSpeedZ = - PickupSpeed ; break ;
case E_META_DROPSPENSER_FACING_ZP : PickupSpeedZ = PickupSpeed ; break ;
}
double MicroX , MicroY , MicroZ ;
2019-09-29 08:59:24 -04:00
MicroX = dispCoord . x + 0.5 ;
MicroY = dispCoord . y + 0.4 ; // Slightly less than half, to accomodate actual texture hole on DropSpenser
MicroZ = dispCoord . z + 0.5 ;
2013-11-02 10:09:07 -04:00
m_World - > SpawnItemPickups ( Pickups , MicroX , MicroY , MicroZ , PickupSpeedX , PickupSpeedY , PickupSpeedZ ) ;
2013-07-29 07:13:03 -04:00
}
2014-10-20 16:55:07 -04:00