2012-06-14 13:06:06 +00:00
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
2012-09-23 22:09:57 +00:00
#include "FurnaceEntity.h"
2013-05-28 19:12:47 +00:00
#include "../UI/Window.h"
#include "../Player.h"
#include "../Root.h"
2013-06-20 11:41:44 +00:00
#include "../Chunk.h"
2012-06-14 13:06:06 +00:00
#include <json/json.h>
2012-09-20 13:25:54 +00:00
enum
{
PROGRESSBAR_SMELTING = 0 ,
PROGRESSBAR_FUEL = 1 ,
} ;
2013-06-20 11:41:44 +00:00
cFurnaceEntity :: cFurnaceEntity ( int a_BlockX , int a_BlockY , int a_BlockZ , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta ) :
2013-06-16 20:24:07 +00:00
super ( E_BLOCK_FURNACE , a_BlockX , a_BlockY , a_BlockZ , ContentsWidth , ContentsHeight , NULL ),
2013-06-20 11:41:44 +00:00
m_BlockType ( a_BlockType ),
m_BlockMeta ( a_BlockMeta ),
2013-06-20 11:01:13 +00:00
m_CurrentRecipe ( NULL ),
2013-06-16 20:24:07 +00:00
m_IsCooking ( false ),
m_NeedCookTime ( 0 ),
m_TimeCooked ( 0 ),
m_FuelBurnTime ( 0 ),
m_TimeBurned ( 0 ),
m_LastProgressFuel ( 0 ),
m_LastProgressCook ( 0 )
2012-06-14 13:06:06 +00:00
{
2012-08-07 12:05:35 +00:00
SetBlockEntity ( this ); // cBlockEntityWindowOwner
2013-06-16 20:24:07 +00:00
m_Contents . AddListener ( * this );
2012-06-14 13:06:06 +00:00
}
2013-06-20 11:41:44 +00:00
cFurnaceEntity :: cFurnaceEntity ( int a_BlockX , int a_BlockY , int a_BlockZ , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta , cWorld * a_World ) :
2013-06-16 20:24:07 +00:00
super ( E_BLOCK_FURNACE , a_BlockX , a_BlockY , a_BlockZ , ContentsWidth , ContentsHeight , a_World ),
2013-06-20 11:41:44 +00:00
m_BlockType ( a_BlockType ),
m_BlockMeta ( a_BlockMeta ),
2013-06-20 11:01:13 +00:00
m_CurrentRecipe ( NULL ),
2013-06-20 11:41:44 +00:00
m_IsCooking (( a_World -> GetBlock ( a_BlockX , a_BlockY , a_BlockZ ) == E_BLOCK_LIT_FURNACE )),
2013-06-16 20:24:07 +00:00
m_NeedCookTime ( 0 ),
m_TimeCooked ( 0 ),
m_FuelBurnTime ( 0 ),
m_TimeBurned ( 0 ),
m_LastProgressFuel ( 0 ),
m_LastProgressCook ( 0 )
2012-06-14 13:06:06 +00:00
{
2013-06-16 20:24:07 +00:00
SetBlockEntity ( this ); // cBlockEntityWindowOwner
m_Contents . AddListener ( * this );
2012-06-14 13:06:06 +00:00
}
2013-06-16 20:24:07 +00:00
cFurnaceEntity ::~ cFurnaceEntity ()
2012-06-14 13:06:06 +00:00
{
2013-06-16 20:24:07 +00:00
// Tell window its owner is destroyed
cWindow * Window = GetWindow ();
if ( Window != NULL )
2012-06-14 13:06:06 +00:00
{
2013-06-16 20:24:07 +00:00
Window -> OwnerDestroyed ();
2012-06-14 13:06:06 +00:00
}
}
2012-09-20 13:25:54 +00:00
void cFurnaceEntity :: UsedBy ( cPlayer * a_Player )
2012-06-14 13:06:06 +00:00
{
2012-09-20 13:25:54 +00:00
if ( GetWindow () == NULL )
2012-06-14 13:06:06 +00:00
{
2012-09-20 13:25:54 +00:00
OpenWindow ( new cFurnaceWindow ( m_PosX , m_PosY , m_PosZ , this ));
2012-06-14 13:06:06 +00:00
}
2013-06-16 20:24:07 +00:00
cWindow * Window = GetWindow ();
if ( Window != NULL )
2012-06-14 13:06:06 +00:00
{
2013-06-16 20:24:07 +00:00
if ( a_Player -> GetWindow () != Window )
2012-06-14 13:06:06 +00:00
{
2013-06-16 20:24:07 +00:00
a_Player -> OpenWindow ( Window );
2013-06-20 11:46:08 +00:00
BroadcastProgress ( PROGRESSBAR_FUEL , m_LastProgressFuel );
BroadcastProgress ( PROGRESSBAR_SMELTING , m_LastProgressCook );
2012-06-14 13:06:06 +00:00
}
}
}
2013-06-16 20:24:07 +00:00
/// Restarts cooking. Used after the furnace is loaded from storage to set up the internal variables so that cooking continues, if it was active. Returns true if cooking.
bool cFurnaceEntity :: ContinueCooking ( void )
{
UpdateInput ();
UpdateFuel ();
return m_IsCooking ;
}
2013-05-28 18:50:44 +00:00
bool cFurnaceEntity :: Tick ( float a_Dt , cChunk & a_Chunk )
2012-06-14 13:06:06 +00:00
{
2013-06-16 20:24:07 +00:00
if ( m_FuelBurnTime <= 0 )
2012-06-14 13:06:06 +00:00
{
2013-06-16 20:24:07 +00:00
// No fuel is burning, reset progressbars and bail out
if (( m_LastProgressCook > 0 ) || ( m_LastProgressFuel > 0 ))
2012-09-20 13:25:54 +00:00
{
2013-06-16 20:24:07 +00:00
UpdateProgressBars ();
2012-09-20 13:25:54 +00:00
}
2012-06-14 13:06:06 +00:00
return false ;
}
2013-06-16 20:24:07 +00:00
if ( m_IsCooking )
2012-06-14 13:06:06 +00:00
{
2013-06-16 20:24:07 +00:00
m_TimeCooked ++ ;
if ( m_TimeCooked >= m_NeedCookTime )
2012-06-14 13:06:06 +00:00
{
2013-06-16 20:24:07 +00:00
// Finished smelting one item
FinishOne ( a_Chunk );
2012-06-14 13:06:06 +00:00
}
}
2013-06-16 20:24:07 +00:00
m_TimeBurned ++ ;
if ( m_TimeBurned >= m_FuelBurnTime )
2012-06-14 13:06:06 +00:00
{
2013-06-16 20:24:07 +00:00
// The current fuel has been exhausted, use another one, if possible
BurnNewFuel ();
2012-06-14 13:06:06 +00:00
}
2012-09-20 13:25:54 +00:00
2013-06-16 20:24:07 +00:00
UpdateProgressBars ();
return true ;
2012-06-14 13:06:06 +00:00
}
2013-06-16 20:24:07 +00:00
bool cFurnaceEntity :: LoadFromJson ( const Json :: Value & a_Value )
2012-06-14 13:06:06 +00:00
{
2013-06-16 20:24:07 +00:00
m_PosX = a_Value . get ( "x" , 0 ). asInt ();
m_PosY = a_Value . get ( "y" , 0 ). asInt ();
m_PosZ = a_Value . get ( "z" , 0 ). asInt ();
Json :: Value AllSlots = a_Value . get ( "Slots" , 0 );
int SlotIdx = 0 ;
for ( Json :: Value :: iterator itr = AllSlots . begin (); itr != AllSlots . end (); ++ itr )
2012-06-14 13:06:06 +00:00
{
2013-06-16 20:24:07 +00:00
cItem Item ;
Item . FromJson ( * itr );
SetSlot ( SlotIdx , Item );
SlotIdx ++ ;
2012-06-14 13:06:06 +00:00
}
2013-06-16 20:24:07 +00:00
m_NeedCookTime = ( int )( a_Value . get ( "CookTime" , 0 ). asDouble () / 50 );
m_TimeCooked = ( int )( a_Value . get ( "TimeCooked" , 0 ). asDouble () / 50 );
m_FuelBurnTime = ( int )( a_Value . get ( "BurnTime" , 0 ). asDouble () / 50 );
m_TimeBurned = ( int )( a_Value . get ( "TimeBurned" , 0 ). asDouble () / 50 );
return true ;
2012-06-14 13:06:06 +00:00
}
2013-06-16 20:24:07 +00:00
void cFurnaceEntity :: SaveToJson ( Json :: Value & a_Value )
2012-06-14 13:06:06 +00:00
{
2013-06-16 20:24:07 +00:00
a_Value [ "x" ] = m_PosX ;
a_Value [ "y" ] = m_PosY ;
a_Value [ "z" ] = m_PosZ ;
Json :: Value AllSlots ;
int NumSlots = m_Contents . GetNumSlots ();
for ( int i = 0 ; i < NumSlots ; i ++ )
2012-06-14 13:06:06 +00:00
{
2013-06-16 20:24:07 +00:00
Json :: Value Slot ;
m_Contents . GetSlot ( i ). GetJson ( Slot );
AllSlots . append ( Slot );
2012-06-14 13:06:06 +00:00
}
2013-06-16 20:24:07 +00:00
a_Value [ "Slots" ] = AllSlots ;
a_Value [ "CookTime" ] = m_NeedCookTime * 50 ;
a_Value [ "TimeCooked" ] = m_TimeCooked * 50 ;
a_Value [ "BurnTime" ] = m_FuelBurnTime * 50 ;
a_Value [ "TimeBurned" ] = m_TimeBurned * 50 ;
2012-06-14 13:06:06 +00:00
}
2013-06-16 20:24:07 +00:00
void cFurnaceEntity :: SendTo ( cClientHandle & a_Client )
2012-06-14 13:06:06 +00:00
{
2013-06-16 20:24:07 +00:00
// Nothing needs to be sent
UNUSED ( a_Client );
2012-06-14 13:06:06 +00:00
}
2013-06-16 20:24:07 +00:00
void cFurnaceEntity :: BroadcastProgress ( int a_ProgressbarID , short a_Value )
2012-06-14 13:06:06 +00:00
{
2013-06-16 20:24:07 +00:00
cWindow * Window = GetWindow ();
if ( Window != NULL )
2012-06-14 13:06:06 +00:00
{
2013-08-18 13:26:37 +02:00
Window -> BroadcastProgress ( a_ProgressbarID , a_Value );
2012-06-14 13:06:06 +00:00
}
}
2013-06-16 20:24:07 +00:00
/// One item finished cooking
void cFurnaceEntity :: FinishOne ( cChunk & a_Chunk )
2012-06-14 13:06:06 +00:00
{
2013-06-16 20:24:07 +00:00
m_TimeCooked = 0 ;
2012-06-14 13:06:06 +00:00
2013-06-16 20:24:07 +00:00
if ( m_Contents . GetSlot ( fsOutput ). IsEmpty ())
2012-06-14 13:06:06 +00:00
{
2013-06-16 20:24:07 +00:00
m_Contents . SetSlot ( fsOutput , * m_CurrentRecipe -> Out );
}
else
{
m_Contents . ChangeSlotCount ( fsOutput , m_CurrentRecipe -> Out -> m_ItemCount );
2012-06-14 13:06:06 +00:00
}
2013-06-16 20:24:07 +00:00
m_Contents . ChangeSlotCount ( fsInput , - m_CurrentRecipe -> In -> m_ItemCount );
UpdateIsCooking ();
}
2012-06-14 13:06:06 +00:00
2013-06-16 20:24:07 +00:00
void cFurnaceEntity :: BurnNewFuel ( void )
{
cFurnaceRecipe * FR = cRoot :: Get () -> GetFurnaceRecipe ();
int NewTime = FR -> GetBurnTime ( m_Contents . GetSlot ( fsFuel ));
if ( NewTime == 0 )
2012-06-14 13:06:06 +00:00
{
2013-06-16 20:24:07 +00:00
// The item in the fuel slot is not suitable
m_FuelBurnTime = 0 ;
m_TimeBurned = 0 ;
2013-06-20 11:41:44 +00:00
SetIsCooking ( false );
2013-06-16 20:24:07 +00:00
return ;
}
// Is the input and output ready for cooking?
if ( ! CanCookInputToOutput ())
{
return ;
}
// Burn one new fuel:
m_FuelBurnTime = NewTime ;
m_TimeBurned = 0 ;
2013-06-20 11:41:44 +00:00
SetIsCooking ( true );
2013-06-16 20:24:07 +00:00
if ( m_Contents . GetSlot ( fsFuel ). m_ItemType == E_ITEM_LAVA_BUCKET )
{
m_Contents . SetSlot ( fsFuel , cItem ( E_ITEM_BUCKET ));
}
else
{
m_Contents . ChangeSlotCount ( fsFuel , - 1 );
}
}
void cFurnaceEntity :: OnSlotChanged ( cItemGrid * a_ItemGrid , int a_SlotNum )
{
super :: OnSlotChanged ( a_ItemGrid , a_SlotNum );
if ( m_World == NULL )
{
// The furnace isn't initialized yet, do no processing
return ;
}
ASSERT ( a_ItemGrid == & m_Contents );
switch ( a_SlotNum )
{
case fsInput :
{
UpdateInput ();
break ;
}
case fsFuel :
{
UpdateFuel ();
break ;
}
case fsOutput :
2012-06-14 13:06:06 +00:00
{
2013-06-16 20:24:07 +00:00
UpdateOutput ();
break ;
2012-06-14 13:06:06 +00:00
}
}
2013-06-16 20:24:07 +00:00
}
2012-06-14 13:06:06 +00:00
2013-06-16 20:24:07 +00:00
/// Updates the current recipe, based on the current input
void cFurnaceEntity :: UpdateInput ( void )
{
if ( ! m_Contents . GetSlot ( fsInput ). IsStackableWith ( m_LastInput ))
{
// The input is different from what we had before, reset the cooking time
m_TimeCooked = 0 ;
}
m_LastInput = m_Contents . GetSlot ( fsInput );
cFurnaceRecipe * FR = cRoot :: Get () -> GetFurnaceRecipe ();
m_CurrentRecipe = FR -> GetRecipeFrom ( m_Contents . GetSlot ( fsInput ));
if ( ! CanCookInputToOutput ())
{
// This input cannot be cooked
m_NeedCookTime = 0 ;
2013-06-20 11:41:44 +00:00
SetIsCooking ( false );
2013-06-16 20:24:07 +00:00
}
else
{
m_NeedCookTime = m_CurrentRecipe -> CookTime ;
2013-06-20 11:41:44 +00:00
SetIsCooking ( true );
2013-06-16 20:24:07 +00:00
// Start burning new fuel if there's no flame now:
if ( GetFuelBurnTimeLeft () <= 0 )
{
BurnNewFuel ();
}
}
2012-06-14 13:06:06 +00:00
}
2013-06-16 20:24:07 +00:00
/// Called when the fuel slot changes or when the fuel is spent, burns another piece of fuel if appropriate
void cFurnaceEntity :: UpdateFuel ( void )
2012-06-14 13:06:06 +00:00
{
2013-06-16 20:24:07 +00:00
if ( m_FuelBurnTime > m_TimeBurned )
2012-06-14 13:06:06 +00:00
{
2013-06-16 20:24:07 +00:00
// The current fuel is still burning, don't modify anything:
return ;
2012-06-14 13:06:06 +00:00
}
2013-06-16 20:24:07 +00:00
// The current fuel is spent, try to burn some more:
BurnNewFuel ();
}
2012-06-14 13:06:06 +00:00
2013-06-16 20:24:07 +00:00
/// Called when the output slot changes; starts burning if space became available
void cFurnaceEntity :: UpdateOutput ( void )
{
if ( ! CanCookInputToOutput ())
2012-06-14 13:06:06 +00:00
{
2013-06-16 20:24:07 +00:00
// Cannot cook anymore:
m_TimeCooked = 0 ;
m_NeedCookTime = 0 ;
2013-06-20 11:41:44 +00:00
SetIsCooking ( false );
2013-06-16 20:24:07 +00:00
return ;
2012-06-14 13:06:06 +00:00
}
2013-06-16 20:24:07 +00:00
// No need to burn new fuel, the Tick() function will take care of that
2012-06-14 13:06:06 +00:00
2013-06-16 20:24:07 +00:00
// Can cook, start cooking if not already underway:
m_NeedCookTime = m_CurrentRecipe -> CookTime ;
2013-06-20 11:41:44 +00:00
SetIsCooking ( m_FuelBurnTime > 0 );
2012-06-14 13:06:06 +00:00
}
2012-08-24 07:58:26 +00:00
2013-06-16 20:24:07 +00:00
/// Updates the m_IsCooking, based on the input slot, output slot and m_FuelBurnTime / m_TimeBurned
void cFurnaceEntity :: UpdateIsCooking ( void )
2012-08-24 07:58:26 +00:00
{
2013-06-16 20:24:07 +00:00
if (
! CanCookInputToOutput () || // Cannot cook this
( m_FuelBurnTime <= 0 ) || // No fuel
( m_TimeBurned >= m_FuelBurnTime ) // Fuel burnt out
)
{
// Reset everything
2013-06-20 11:41:44 +00:00
SetIsCooking ( false );
2013-06-16 20:24:07 +00:00
m_TimeCooked = 0 ;
m_NeedCookTime = 0 ;
return ;
}
2013-06-20 11:41:44 +00:00
SetIsCooking ( true );
2012-08-24 07:58:26 +00:00
}
2012-09-20 13:25:54 +00:00
2013-06-16 20:24:07 +00:00
/// Returns true if the input can be cooked into output and the item counts allow for another cooking operation
bool cFurnaceEntity :: CanCookInputToOutput ( void ) const
2012-09-20 13:25:54 +00:00
{
2013-06-16 20:24:07 +00:00
if ( m_CurrentRecipe == NULL )
2012-09-20 13:25:54 +00:00
{
2013-06-16 20:24:07 +00:00
// This input cannot be cooked
return false ;
}
if ( m_Contents . GetSlot ( fsOutput ). IsEmpty ())
{
// The output is empty, can cook
return true ;
}
if ( ! m_Contents . GetSlot ( fsOutput ). IsStackableWith ( * m_CurrentRecipe -> Out ))
{
// The output slot is blocked with something that cannot be stacked with the recipe's output
return false ;
}
if ( m_Contents . GetSlot ( fsOutput ). IsFullStack ())
{
// Cannot add any more items to the output slot
return false ;
}
return true ;
}
/// Broadcasts progressbar updates, if needed
void cFurnaceEntity :: UpdateProgressBars ( void )
{
// In order to preserve bandwidth, an update is sent only every 10th tick
// That's why the comparisons use the division by eight
int CurFuel = ( m_FuelBurnTime > 0 ) ? ( 200 - 200 * m_TimeBurned / m_FuelBurnTime ) : 0 ;
if (( CurFuel / 8 ) != ( m_LastProgressFuel / 8 ))
{
BroadcastProgress ( PROGRESSBAR_FUEL , CurFuel );
m_LastProgressFuel = CurFuel ;
}
int CurCook = ( m_NeedCookTime > 0 ) ? ( 200 * m_TimeCooked / m_NeedCookTime ) : 0 ;
if (( CurCook / 8 ) != ( m_LastProgressCook / 8 ))
{
BroadcastProgress ( PROGRESSBAR_SMELTING , CurCook );
m_LastProgressCook = CurCook ;
2012-09-20 13:25:54 +00:00
}
}
2013-06-20 11:41:44 +00:00
void cFurnaceEntity :: SetIsCooking ( bool a_IsCooking )
{
if ( a_IsCooking == m_IsCooking )
{
return ;
}
m_IsCooking = a_IsCooking ;
// Light or extinguish the furnace:
m_World -> FastSetBlock ( m_PosX , m_PosY , m_PosZ , m_IsCooking ? E_BLOCK_LIT_FURNACE : E_BLOCK_FURNACE , m_BlockMeta );
}