2012-06-14 09:06:06 -04:00
# include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
2012-09-23 18:09:57 -04:00
# include "FurnaceEntity.h"
2013-05-28 15:12:47 -04:00
# include "../UI/Window.h"
# include "../Player.h"
# include "../Root.h"
2013-06-20 07:41:44 -04:00
# include "../Chunk.h"
2012-06-14 09:06:06 -04:00
# include <json/json.h>
2012-09-20 09:25:54 -04:00
enum
{
PROGRESSBAR_SMELTING = 0 ,
PROGRESSBAR_FUEL = 1 ,
} ;
2013-06-20 07:41:44 -04:00
cFurnaceEntity : : cFurnaceEntity ( int a_BlockX , int a_BlockY , int a_BlockZ , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta ) :
2013-06-16 16:24:07 -04:00
super ( E_BLOCK_FURNACE , a_BlockX , a_BlockY , a_BlockZ , ContentsWidth , ContentsHeight , NULL ) ,
2013-06-20 07:41:44 -04:00
m_BlockType ( a_BlockType ) ,
m_BlockMeta ( a_BlockMeta ) ,
2013-06-20 07:01:13 -04:00
m_CurrentRecipe ( NULL ) ,
2013-06-16 16:24:07 -04: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 09:06:06 -04:00
{
2012-08-07 08:05:35 -04:00
SetBlockEntity ( this ) ; // cBlockEntityWindowOwner
2013-06-16 16:24:07 -04:00
m_Contents . AddListener ( * this ) ;
2012-06-14 09:06:06 -04:00
}
2013-06-20 07:41:44 -04:00
cFurnaceEntity : : cFurnaceEntity ( int a_BlockX , int a_BlockY , int a_BlockZ , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta , cWorld * a_World ) :
2013-06-16 16:24:07 -04:00
super ( E_BLOCK_FURNACE , a_BlockX , a_BlockY , a_BlockZ , ContentsWidth , ContentsHeight , a_World ) ,
2013-06-20 07:41:44 -04:00
m_BlockType ( a_BlockType ) ,
m_BlockMeta ( a_BlockMeta ) ,
2013-06-20 07:01:13 -04:00
m_CurrentRecipe ( NULL ) ,
2013-06-20 07:41:44 -04:00
m_IsCooking ( ( a_World - > GetBlock ( a_BlockX , a_BlockY , a_BlockZ ) = = E_BLOCK_LIT_FURNACE ) ) ,
2013-06-16 16:24:07 -04:00
m_NeedCookTime ( 0 ) ,
m_TimeCooked ( 0 ) ,
m_FuelBurnTime ( 0 ) ,
m_TimeBurned ( 0 ) ,
m_LastProgressFuel ( 0 ) ,
m_LastProgressCook ( 0 )
2012-06-14 09:06:06 -04:00
{
2013-06-16 16:24:07 -04:00
SetBlockEntity ( this ) ; // cBlockEntityWindowOwner
m_Contents . AddListener ( * this ) ;
2012-06-14 09:06:06 -04:00
}
2013-06-16 16:24:07 -04:00
cFurnaceEntity : : ~ cFurnaceEntity ( )
2012-06-14 09:06:06 -04:00
{
2013-06-16 16:24:07 -04:00
// Tell window its owner is destroyed
cWindow * Window = GetWindow ( ) ;
if ( Window ! = NULL )
2012-06-14 09:06:06 -04:00
{
2013-06-16 16:24:07 -04:00
Window - > OwnerDestroyed ( ) ;
2012-06-14 09:06:06 -04:00
}
}
2012-09-20 09:25:54 -04:00
void cFurnaceEntity : : UsedBy ( cPlayer * a_Player )
2012-06-14 09:06:06 -04:00
{
2012-09-20 09:25:54 -04:00
if ( GetWindow ( ) = = NULL )
2012-06-14 09:06:06 -04:00
{
2012-09-20 09:25:54 -04:00
OpenWindow ( new cFurnaceWindow ( m_PosX , m_PosY , m_PosZ , this ) ) ;
2012-06-14 09:06:06 -04:00
}
2013-06-16 16:24:07 -04:00
cWindow * Window = GetWindow ( ) ;
if ( Window ! = NULL )
2012-06-14 09:06:06 -04:00
{
2013-06-16 16:24:07 -04:00
if ( a_Player - > GetWindow ( ) ! = Window )
2012-06-14 09:06:06 -04:00
{
2013-06-16 16:24:07 -04:00
a_Player - > OpenWindow ( Window ) ;
2013-06-20 07:46:08 -04:00
BroadcastProgress ( PROGRESSBAR_FUEL , m_LastProgressFuel ) ;
BroadcastProgress ( PROGRESSBAR_SMELTING , m_LastProgressCook ) ;
2012-06-14 09:06:06 -04:00
}
}
}
2013-06-16 16:24:07 -04: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 14:50:44 -04:00
bool cFurnaceEntity : : Tick ( float a_Dt , cChunk & a_Chunk )
2012-06-14 09:06:06 -04:00
{
2013-06-16 16:24:07 -04:00
if ( m_FuelBurnTime < = 0 )
2012-06-14 09:06:06 -04:00
{
2013-06-16 16:24:07 -04:00
// No fuel is burning, reset progressbars and bail out
if ( ( m_LastProgressCook > 0 ) | | ( m_LastProgressFuel > 0 ) )
2012-09-20 09:25:54 -04:00
{
2013-06-16 16:24:07 -04:00
UpdateProgressBars ( ) ;
2012-09-20 09:25:54 -04:00
}
2012-06-14 09:06:06 -04:00
return false ;
}
2013-06-16 16:24:07 -04:00
if ( m_IsCooking )
2012-06-14 09:06:06 -04:00
{
2013-06-16 16:24:07 -04:00
m_TimeCooked + + ;
if ( m_TimeCooked > = m_NeedCookTime )
2012-06-14 09:06:06 -04:00
{
2013-06-16 16:24:07 -04:00
// Finished smelting one item
FinishOne ( a_Chunk ) ;
2012-06-14 09:06:06 -04:00
}
}
2013-06-16 16:24:07 -04:00
m_TimeBurned + + ;
if ( m_TimeBurned > = m_FuelBurnTime )
2012-06-14 09:06:06 -04:00
{
2013-06-16 16:24:07 -04:00
// The current fuel has been exhausted, use another one, if possible
BurnNewFuel ( ) ;
2012-06-14 09:06:06 -04:00
}
2012-09-20 09:25:54 -04:00
2013-06-16 16:24:07 -04:00
UpdateProgressBars ( ) ;
return true ;
2012-06-14 09:06:06 -04:00
}
2013-06-16 16:24:07 -04:00
bool cFurnaceEntity : : LoadFromJson ( const Json : : Value & a_Value )
2012-06-14 09:06:06 -04:00
{
2013-06-16 16:24:07 -04: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 09:06:06 -04:00
{
2013-06-16 16:24:07 -04:00
cItem Item ;
Item . FromJson ( * itr ) ;
SetSlot ( SlotIdx , Item ) ;
SlotIdx + + ;
2012-06-14 09:06:06 -04:00
}
2013-06-16 16:24:07 -04: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 09:06:06 -04:00
}
2013-06-16 16:24:07 -04:00
void cFurnaceEntity : : SaveToJson ( Json : : Value & a_Value )
2012-06-14 09:06:06 -04:00
{
2013-06-16 16:24:07 -04: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 09:06:06 -04:00
{
2013-06-16 16:24:07 -04:00
Json : : Value Slot ;
m_Contents . GetSlot ( i ) . GetJson ( Slot ) ;
AllSlots . append ( Slot ) ;
2012-06-14 09:06:06 -04:00
}
2013-06-16 16:24:07 -04: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 09:06:06 -04:00
}
2013-06-16 16:24:07 -04:00
void cFurnaceEntity : : SendTo ( cClientHandle & a_Client )
2012-06-14 09:06:06 -04:00
{
2013-06-16 16:24:07 -04:00
// Nothing needs to be sent
UNUSED ( a_Client ) ;
2012-06-14 09:06:06 -04:00
}
2013-06-16 16:24:07 -04:00
void cFurnaceEntity : : BroadcastProgress ( int a_ProgressbarID , short a_Value )
2012-06-14 09:06:06 -04:00
{
2013-06-16 16:24:07 -04:00
cWindow * Window = GetWindow ( ) ;
if ( Window ! = NULL )
2012-06-14 09:06:06 -04:00
{
2013-06-16 16:24:07 -04:00
Window - > BroadcastInventoryProgress ( a_ProgressbarID , a_Value ) ;
2012-06-14 09:06:06 -04:00
}
}
2013-06-16 16:24:07 -04:00
/// One item finished cooking
void cFurnaceEntity : : FinishOne ( cChunk & a_Chunk )
2012-06-14 09:06:06 -04:00
{
2013-06-16 16:24:07 -04:00
m_TimeCooked = 0 ;
2012-06-14 09:06:06 -04:00
2013-06-16 16:24:07 -04:00
if ( m_Contents . GetSlot ( fsOutput ) . IsEmpty ( ) )
2012-06-14 09:06:06 -04:00
{
2013-06-16 16:24:07 -04:00
m_Contents . SetSlot ( fsOutput , * m_CurrentRecipe - > Out ) ;
}
else
{
m_Contents . ChangeSlotCount ( fsOutput , m_CurrentRecipe - > Out - > m_ItemCount ) ;
2012-06-14 09:06:06 -04:00
}
2013-06-16 16:24:07 -04:00
m_Contents . ChangeSlotCount ( fsInput , - m_CurrentRecipe - > In - > m_ItemCount ) ;
UpdateIsCooking ( ) ;
}
2012-06-14 09:06:06 -04:00
2013-06-16 16:24:07 -04:00
void cFurnaceEntity : : BurnNewFuel ( void )
{
cFurnaceRecipe * FR = cRoot : : Get ( ) - > GetFurnaceRecipe ( ) ;
int NewTime = FR - > GetBurnTime ( m_Contents . GetSlot ( fsFuel ) ) ;
if ( NewTime = = 0 )
2012-06-14 09:06:06 -04:00
{
2013-06-16 16:24:07 -04:00
// The item in the fuel slot is not suitable
m_FuelBurnTime = 0 ;
m_TimeBurned = 0 ;
2013-06-20 07:41:44 -04:00
SetIsCooking ( false ) ;
2013-06-16 16:24:07 -04: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 07:41:44 -04:00
SetIsCooking ( true ) ;
2013-06-16 16:24:07 -04: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 09:06:06 -04:00
{
2013-06-16 16:24:07 -04:00
UpdateOutput ( ) ;
break ;
2012-06-14 09:06:06 -04:00
}
}
2013-06-16 16:24:07 -04:00
}
2012-06-14 09:06:06 -04:00
2013-06-16 16:24:07 -04: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 07:41:44 -04:00
SetIsCooking ( false ) ;
2013-06-16 16:24:07 -04:00
}
else
{
m_NeedCookTime = m_CurrentRecipe - > CookTime ;
2013-06-20 07:41:44 -04:00
SetIsCooking ( true ) ;
2013-06-16 16:24:07 -04:00
// Start burning new fuel if there's no flame now:
if ( GetFuelBurnTimeLeft ( ) < = 0 )
{
BurnNewFuel ( ) ;
}
}
2012-06-14 09:06:06 -04:00
}
2013-06-16 16:24:07 -04: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 09:06:06 -04:00
{
2013-06-16 16:24:07 -04:00
if ( m_FuelBurnTime > m_TimeBurned )
2012-06-14 09:06:06 -04:00
{
2013-06-16 16:24:07 -04:00
// The current fuel is still burning, don't modify anything:
return ;
2012-06-14 09:06:06 -04:00
}
2013-06-16 16:24:07 -04:00
// The current fuel is spent, try to burn some more:
BurnNewFuel ( ) ;
}
2012-06-14 09:06:06 -04:00
2013-06-16 16:24:07 -04:00
/// Called when the output slot changes; starts burning if space became available
void cFurnaceEntity : : UpdateOutput ( void )
{
if ( ! CanCookInputToOutput ( ) )
2012-06-14 09:06:06 -04:00
{
2013-06-16 16:24:07 -04:00
// Cannot cook anymore:
m_TimeCooked = 0 ;
m_NeedCookTime = 0 ;
2013-06-20 07:41:44 -04:00
SetIsCooking ( false ) ;
2013-06-16 16:24:07 -04:00
return ;
2012-06-14 09:06:06 -04:00
}
2013-06-16 16:24:07 -04:00
// No need to burn new fuel, the Tick() function will take care of that
2012-06-14 09:06:06 -04:00
2013-06-16 16:24:07 -04:00
// Can cook, start cooking if not already underway:
m_NeedCookTime = m_CurrentRecipe - > CookTime ;
2013-06-20 07:41:44 -04:00
SetIsCooking ( m_FuelBurnTime > 0 ) ;
2012-06-14 09:06:06 -04:00
}
2012-08-24 03:58:26 -04:00
2013-06-16 16:24:07 -04:00
/// Updates the m_IsCooking, based on the input slot, output slot and m_FuelBurnTime / m_TimeBurned
void cFurnaceEntity : : UpdateIsCooking ( void )
2012-08-24 03:58:26 -04:00
{
2013-06-16 16:24:07 -04:00
if (
! CanCookInputToOutput ( ) | | // Cannot cook this
( m_FuelBurnTime < = 0 ) | | // No fuel
( m_TimeBurned > = m_FuelBurnTime ) // Fuel burnt out
)
{
// Reset everything
2013-06-20 07:41:44 -04:00
SetIsCooking ( false ) ;
2013-06-16 16:24:07 -04:00
m_TimeCooked = 0 ;
m_NeedCookTime = 0 ;
return ;
}
2013-06-20 07:41:44 -04:00
SetIsCooking ( true ) ;
2012-08-24 03:58:26 -04:00
}
2012-09-20 09:25:54 -04:00
2013-06-16 16:24:07 -04: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 09:25:54 -04:00
{
2013-06-16 16:24:07 -04:00
if ( m_CurrentRecipe = = NULL )
2012-09-20 09:25:54 -04:00
{
2013-06-16 16:24:07 -04: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 09:25:54 -04:00
}
}
2013-06-20 07:41:44 -04: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 ) ;
}