From 97eda34a9437abe732cf6b60711828bbe4f0cb2e Mon Sep 17 00:00:00 2001 From: "madmaxoft@gmail.com" Date: Sun, 16 Jun 2013 20:24:07 +0000 Subject: Rewritten furnaces Furnaces now smelt the correct number of items. Furnaces store their contents in a cItemGrid. Furnace window is updated with correct items and progressbars. Furnace recipes now use ticks instead of milliseconds. Furnaces save and load their state completely, not missing a smelt operation. Hoppers take items out of furnaces. Dropped the cSlotAreaDropSpenser class, replaced it with generic cSlotAreaItemGrid git-svn-id: http://mc-server.googlecode.com/svn/trunk@1601 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- source/BlockEntities/ChestEntity.cpp | 4 +- source/BlockEntities/FurnaceEntity.cpp | 514 ++++++++++++++++++++------------- source/BlockEntities/FurnaceEntity.h | 128 ++++++-- source/BlockEntities/HopperEntity.cpp | 24 +- 4 files changed, 439 insertions(+), 231 deletions(-) (limited to 'source/BlockEntities') diff --git a/source/BlockEntities/ChestEntity.cpp b/source/BlockEntities/ChestEntity.cpp index 3e75d5100..24d2aef11 100644 --- a/source/BlockEntities/ChestEntity.cpp +++ b/source/BlockEntities/ChestEntity.cpp @@ -56,8 +56,8 @@ bool cChestEntity::LoadFromJson(const Json::Value & a_Value) for (Json::Value::iterator itr = AllSlots.begin(); itr != AllSlots.end(); ++itr) { cItem Item; - Item.FromJson( *itr ); - SetSlot( SlotIdx, Item ); + Item.FromJson(*itr); + SetSlot(SlotIdx, Item); SlotIdx++; } return true; diff --git a/source/BlockEntities/FurnaceEntity.cpp b/source/BlockEntities/FurnaceEntity.cpp index d4cdf3f89..ecbf715f2 100644 --- a/source/BlockEntities/FurnaceEntity.cpp +++ b/source/BlockEntities/FurnaceEntity.cpp @@ -4,7 +4,6 @@ #include "FurnaceEntity.h" #include "../UI/Window.h" #include "../Player.h" -#include "../FurnaceRecipe.h" #include "../Root.h" #include @@ -23,54 +22,50 @@ enum -cFurnaceEntity::cFurnaceEntity(int a_X, int a_Y, int a_Z, cWorld * a_World) - : cBlockEntity( E_BLOCK_FURNACE, a_X, a_Y, a_Z, a_World ) - , m_Items( new cItem[3] ) - , m_CookingItem( 0 ) - , m_CookTime( 0 ) - , m_TimeCooked( 0 ) - , m_BurnTime( 0 ) - , m_TimeBurned( 0 ) +cFurnaceEntity::cFurnaceEntity(int a_BlockX, int a_BlockY, int a_BlockZ) : + super(E_BLOCK_FURNACE, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, NULL), + m_IsCooking(false), + m_NeedCookTime(0), + m_TimeCooked(0), + m_FuelBurnTime(0), + m_TimeBurned(0), + m_LastProgressFuel(0), + m_LastProgressCook(0) { SetBlockEntity(this); // cBlockEntityWindowOwner + m_Contents.AddListener(*this); } -cFurnaceEntity::~cFurnaceEntity() +cFurnaceEntity::cFurnaceEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : + super(E_BLOCK_FURNACE, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World), + m_IsCooking(false), + m_NeedCookTime(0), + m_TimeCooked(0), + m_FuelBurnTime(0), + m_TimeBurned(0), + m_LastProgressFuel(0), + m_LastProgressCook(0) { - // Tell window its owner is destroyed - if( GetWindow() ) - { - GetWindow()->OwnerDestroyed(); - } - - // Clean up items - if( m_Items ) - { - delete [] m_Items; - } + SetBlockEntity(this); // cBlockEntityWindowOwner + m_Contents.AddListener(*this); } -void cFurnaceEntity::Destroy() +cFurnaceEntity::~cFurnaceEntity() { - // Drop items - cItems Pickups; - for( int i = 0; i < 3; i++) + // Tell window its owner is destroyed + cWindow * Window = GetWindow(); + if (Window != NULL) { - if( !m_Items[i].IsEmpty() ) - { - Pickups.push_back(m_Items[i]); - m_Items[i].Empty(); - } + Window->OwnerDestroyed(); } - m_World->SpawnItemPickups(Pickups, m_PosX, m_PosY, m_PosZ); } @@ -83,11 +78,12 @@ void cFurnaceEntity::UsedBy(cPlayer * a_Player) { OpenWindow(new cFurnaceWindow(m_PosX, m_PosY, m_PosZ, this)); } - if (GetWindow() != NULL) + cWindow * Window = GetWindow(); + if (Window != NULL) { - if (a_Player->GetWindow() != GetWindow()) + if (a_Player->GetWindow() != Window) { - a_Player->OpenWindow(GetWindow()); + a_Player->OpenWindow(Window); } } } @@ -96,270 +92,380 @@ void cFurnaceEntity::UsedBy(cPlayer * a_Player) +/// 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; +} + + + + + bool cFurnaceEntity::Tick(float a_Dt, cChunk & a_Chunk) { - /* - // DEBUG: - Int16 BurnTime = (Int16)(GetTimeToBurn() / 50.0); - Int16 CookTime = (Int16)(GetTimeCooked() / 50.0); - LOGD("Furnace: BurnTime %d, CookTime %d", BurnTime, CookTime); - */ - - if (m_BurnTime <= 0) + if (m_FuelBurnTime <= 0) { - if (m_TimeCooked > 0) + // No fuel is burning, reset progressbars and bail out + if ((m_LastProgressCook > 0) || (m_LastProgressFuel > 0)) { - // We have just finished smelting, reset the progress bar: - BroadcastProgress(PROGRESSBAR_SMELTING, 0); - m_TimeCooked = 0; - m_World->FastSetBlock(m_PosX, m_PosY, m_PosZ, E_BLOCK_FURNACE, m_World->GetBlockMeta(m_PosX, m_PosY, m_PosZ)); + UpdateProgressBars(); } - // There is no fuel and no flame, no need to tick at all return false; } - - // DEBUG: LOGD("Furnace: Left: %0.1f Burned: %0.1f Burn time: %0.1f", m_CookTime - m_TimeCooked, m_TimeBurned, m_BurnTime ); - - short SmeltingProgress = 0; - if ((m_CookingItem != NULL) && ((m_TimeBurned < m_BurnTime) || (m_TimeCooked + a_Dt >= m_CookTime))) - { - if (m_CookingItem->IsEqual(m_Items[2]) || m_Items[2].IsEmpty()) - { - m_TimeCooked += a_Dt; - if ( m_TimeCooked >= m_CookTime ) - { - m_Items[0].m_ItemCount--; - if( m_Items[0].IsEmpty() ) m_Items[0].Empty(); - - m_Items[2].m_ItemDamage = m_CookingItem->m_ItemDamage; - m_Items[2].m_ItemType = m_CookingItem->m_ItemType; - m_Items[2].m_ItemCount += m_CookingItem->m_ItemCount; - delete m_CookingItem; - m_CookingItem = NULL; - - cWindow * Window = GetWindow(); - if (Window != NULL) - { - Window->BroadcastWholeWindow(); - } - - m_TimeCooked -= m_CookTime; - StartCooking(); - } - SmeltingProgress = (short)( m_TimeCooked * (180.f / m_CookTime)); - if (SmeltingProgress > 180) SmeltingProgress = 180; - if (SmeltingProgress < 0) SmeltingProgress = 0; - } - } - BroadcastProgress(PROGRESSBAR_SMELTING, SmeltingProgress); - m_TimeBurned += a_Dt; - - cWindow * Window = GetWindow(); - if (m_TimeBurned >= m_BurnTime) + if (m_IsCooking) { - m_TimeBurned -= m_BurnTime; - m_BurnTime = 0; - if (StartCooking() && (Window != NULL)) + m_TimeCooked++; + if (m_TimeCooked >= m_NeedCookTime) { - Window->BroadcastWholeWindow(); + // Finished smelting one item + FinishOne(a_Chunk); } } - short Value = 0; - if (m_BurnTime > 0.f) + + m_TimeBurned++; + if (m_TimeBurned >= m_FuelBurnTime) { - Value = 250 - (short)( m_TimeBurned * (250.f / m_BurnTime)); - if (Value > 250) Value = 250; - if (Value < 0) Value = 0; + // The current fuel has been exhausted, use another one, if possible + BurnNewFuel(); } - BroadcastProgress(PROGRESSBAR_FUEL, Value); - return ((m_CookingItem != NULL) || (m_TimeBurned < m_BurnTime)) && (m_BurnTime > 0.0); // Keep on ticking, if there's more to cook, or if it's cooking + UpdateProgressBars(); + + return true; } -bool cFurnaceEntity::StartCooking(void) +bool cFurnaceEntity::LoadFromJson(const Json::Value & a_Value) { - cFurnaceRecipe* FR = cRoot::Get()->GetFurnaceRecipe(); - float BurnTime = FR->GetBurnTime( m_Items[1] ); - if( (m_TimeBurned < m_BurnTime) || BurnTime > 0.f ) // burnable material + 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) { - const cFurnaceRecipe::Recipe* R = FR->GetRecipeFrom( m_Items[0] ); - if (R != NULL) // cook able ingredient - { - if (m_Items[2].IsEqual(*R->Out) || m_Items[2].IsEmpty()) - { - // good to go - m_World->FastSetBlock(m_PosX, m_PosY, m_PosZ, E_BLOCK_LIT_FURNACE, m_World->GetBlockMeta(m_PosX, m_PosY, m_PosZ)); - - if( m_TimeBurned >= m_BurnTime ) // burn new material - { - m_Items[1].m_ItemCount--; - if( m_Items[1].m_ItemCount <= 0 ) m_Items[1].Empty(); - m_TimeBurned = 0; - m_BurnTime = BurnTime; - } - - if( !m_CookingItem ) // Only cook new item if not already cooking - { - m_CookingItem = new cItem( *R->Out ); // Resulting item - m_TimeCooked = 0.f; - m_CookTime = R->CookTime; - } - return true; - } - } + cItem Item; + Item.FromJson(*itr); + SetSlot(SlotIdx, Item); + SlotIdx++; } - return false; + + 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; } -bool cFurnaceEntity::ContinueCooking(void) +void cFurnaceEntity::SaveToJson( Json::Value& a_Value ) { - cFurnaceRecipe * FR = cRoot::Get()->GetFurnaceRecipe(); - float BurnTime = FR->GetBurnTime( m_Items[1] ); - if( (m_TimeBurned < m_BurnTime) || BurnTime > 0.f ) // burnable material + 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++) { - const cFurnaceRecipe::Recipe * R = FR->GetRecipeFrom( m_Items[0] ); - if (R != NULL) // cook able ingredient - { - if (m_Items[2].IsEqual(*R->Out) || m_Items[2].IsEmpty()) - { - // good to go - if (m_CookingItem == NULL) // Only cook new item if not already cooking - { - m_CookingItem = new cItem( *R->Out ); // Resulting item - } - return true; - } - } + Json::Value Slot; + m_Contents.GetSlot(i).GetJson(Slot); + AllSlots.append(Slot); } - return false; + 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; } -void cFurnaceEntity::ResetCookTimer() +void cFurnaceEntity::SendTo(cClientHandle & a_Client) { - delete m_CookingItem; - m_CookingItem = NULL; - m_TimeCooked = 0.f; - m_CookTime = 0.f; + // Nothing needs to be sent + UNUSED(a_Client); } -void cFurnaceEntity::SetSlot(int a_Slot, const cItem & a_Item) +void cFurnaceEntity::BroadcastProgress(int a_ProgressbarID, short a_Value) { - if ((a_Slot < 0) || (a_Slot >= 3)) + cWindow * Window = GetWindow(); + if (Window != NULL) { - ASSERT(!"Furnace: slot number out of range"); - return; + Window->BroadcastInventoryProgress(a_ProgressbarID, a_Value); } - m_Items[a_Slot] = a_Item; } -bool cFurnaceEntity::LoadFromJson(const Json::Value & a_Value) +/// One item finished cooking +void cFurnaceEntity::FinishOne(cChunk & a_Chunk) { - m_PosX = a_Value.get("x", 0).asInt(); - m_PosY = a_Value.get("y", 0).asInt(); - m_PosZ = a_Value.get("z", 0).asInt(); + m_TimeCooked = 0; - Json::Value AllSlots = a_Value.get("Slots", 0); - int SlotIdx = 0; - for( Json::Value::iterator itr = AllSlots.begin(); itr != AllSlots.end(); ++itr ) + if (m_Contents.GetSlot(fsOutput).IsEmpty()) { - m_Items[ SlotIdx ].FromJson( *itr ); - SlotIdx++; + m_Contents.SetSlot(fsOutput, *m_CurrentRecipe->Out); + } + else + { + m_Contents.ChangeSlotCount(fsOutput, m_CurrentRecipe->Out->m_ItemCount); } + m_Contents.ChangeSlotCount(fsInput, -m_CurrentRecipe->In->m_ItemCount); + + UpdateIsCooking(); +} + + + - // Get currently cooking item - Json::Value JsonItem = a_Value.get("Cooking", Json::nullValue ); - if( !JsonItem.empty() ) + +void cFurnaceEntity::BurnNewFuel(void) +{ + cFurnaceRecipe * FR = cRoot::Get()->GetFurnaceRecipe(); + int NewTime = FR->GetBurnTime(m_Contents.GetSlot(fsFuel)); + if (NewTime == 0) { - cItem Item; - Item.FromJson( JsonItem ); - if( !Item.IsEmpty() ) + // The item in the fuel slot is not suitable + m_FuelBurnTime = 0; + m_TimeBurned = 0; + m_IsCooking = false; + return; + } + + // Is the input and output ready for cooking? + if (!CanCookInputToOutput()) + { + return; + } + + // Burn one new fuel: + m_FuelBurnTime = NewTime; + m_TimeBurned = 0; + m_IsCooking = true; + 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: { - m_CookingItem = new cItem( Item ); + UpdateOutput(); + break; } } +} - m_CookTime = (float)a_Value.get("CookTime", 0).asDouble(); - m_TimeCooked = (float)a_Value.get("TimeCooked", 0).asDouble(); - m_BurnTime = (float)a_Value.get("BurnTime", 0).asDouble(); - m_TimeBurned = (float)a_Value.get("TimeBurned", 0).asDouble(); - return true; + + + + +/// 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; + m_IsCooking = false; + } + else + { + m_NeedCookTime = m_CurrentRecipe->CookTime; + m_IsCooking = true; + + // Start burning new fuel if there's no flame now: + if (GetFuelBurnTimeLeft() <= 0) + { + BurnNewFuel(); + } + } } -void cFurnaceEntity::SaveToJson( Json::Value& a_Value ) +/// Called when the fuel slot changes or when the fuel is spent, burns another piece of fuel if appropriate +void cFurnaceEntity::UpdateFuel(void) { - a_Value["x"] = m_PosX; - a_Value["y"] = m_PosY; - a_Value["z"] = m_PosZ; - - Json::Value AllSlots; - for(unsigned int i = 0; i < 3; i++) + if (m_FuelBurnTime > m_TimeBurned) { - Json::Value Slot; - m_Items[ i ].GetJson( Slot ); - AllSlots.append( Slot ); + // The current fuel is still burning, don't modify anything: + return; } - a_Value["Slots"] = AllSlots; + + // The current fuel is spent, try to burn some more: + BurnNewFuel(); +} + + + + - // Currently cooking item - if( m_CookingItem ) +/// Called when the output slot changes; starts burning if space became available +void cFurnaceEntity::UpdateOutput(void) +{ + if (!CanCookInputToOutput()) { - Json::Value JsonItem; - m_CookingItem->GetJson( JsonItem ); - a_Value["Cooking"] = JsonItem; + // Cannot cook anymore: + m_TimeCooked = 0; + m_NeedCookTime = 0; + m_IsCooking = false; + return; } + + // No need to burn new fuel, the Tick() function will take care of that - a_Value["CookTime"] = m_CookTime; - a_Value["TimeCooked"] = m_TimeCooked; - a_Value["BurnTime"] = m_BurnTime; - a_Value["TimeBurned"] = m_TimeBurned; + // Can cook, start cooking if not already underway: + m_NeedCookTime = m_CurrentRecipe->CookTime; + m_IsCooking = true; } -void cFurnaceEntity::SendTo(cClientHandle & a_Client) +/// Updates the m_IsCooking, based on the input slot, output slot and m_FuelBurnTime / m_TimeBurned +void cFurnaceEntity::UpdateIsCooking(void) { - // Nothing needs to be sent - UNUSED(a_Client); + if ( + !CanCookInputToOutput() || // Cannot cook this + (m_FuelBurnTime <= 0) || // No fuel + (m_TimeBurned >= m_FuelBurnTime) // Fuel burnt out + ) + { + // Reset everything + m_IsCooking = false; + m_TimeCooked = 0; + m_NeedCookTime = 0; + return; + } + + m_IsCooking = true; } -void cFurnaceEntity::BroadcastProgress(int a_ProgressbarID, short a_Value) +/// Returns true if the input can be cooked into output and the item counts allow for another cooking operation +bool cFurnaceEntity::CanCookInputToOutput(void) const { - cWindow * Window = GetWindow(); - if (Window != NULL) + if (m_CurrentRecipe == NULL) { - Window->BroadcastInventoryProgress(a_ProgressbarID, a_Value); + // 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; } } diff --git a/source/BlockEntities/FurnaceEntity.h b/source/BlockEntities/FurnaceEntity.h index 0606497b2..037ed75db 100644 --- a/source/BlockEntities/FurnaceEntity.h +++ b/source/BlockEntities/FurnaceEntity.h @@ -1,9 +1,9 @@ #pragma once -#include "BlockEntity.h" +#include "BlockEntityWithItems.h" #include "../UI/WindowOwner.h" -#include "../Item.h" +#include "../FurnaceRecipe.h" @@ -21,14 +21,33 @@ class cServer; -class cFurnaceEntity : - public cBlockEntity, - public cBlockEntityWindowOwner +class cFurnaceEntity : // tolua_export + public cBlockEntityWindowOwner, + // tolua_begin + public cBlockEntityWithItems { + typedef cBlockEntityWithItems super; + public: - cFurnaceEntity(int a_X, int a_Y, int a_Z, cWorld * a_World); + enum + { + fsInput = 0, // Input slot number + fsFuel = 1, // Fuel slot number + fsOutput = 2, // Output slot number + + ContentsWidth = 3, + ContentsHeight = 1, + }; + + /// Constructor used while generating a chunk; sets m_World to NULL + cFurnaceEntity(int a_BlockX, int a_BlockY, int a_BlockZ); + + // tolua_end + + /// Constructor used for normal operation + cFurnaceEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); + virtual ~cFurnaceEntity(); - virtual void Destroy(); static const char * GetClassStatic() { return "cFurnaceEntity"; } @@ -40,36 +59,97 @@ public: virtual bool Tick(float a_Dt, cChunk & a_Chunk) override; virtual void UsedBy(cPlayer * a_Player) override; - bool StartCooking(void); - /// 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 ContinueCooking(void); void ResetCookTimer(); - const cItem * GetSlot(int i) const { return &(m_Items[i]); } + // tolua_begin + + /// Returns the item in the input slot + const cItem & GetInputSlot(void) const { return GetSlot(fsInput); } + + /// Returns the item in the fuel slot + const cItem & GetFuelSlot(void) const { return GetSlot(fsFuel); } + + /// Returns the item in the output slot + const cItem & GetOutputSlot(void) const { return GetSlot(fsOutput); } + + /// Sets the item in the input slot + void SetInputSlot(const cItem & a_Item) { SetSlot(fsInput, a_Item); } + + /// Sets the item in the fuel slot + void SetFuelSlot(const cItem & a_Item) { SetSlot(fsFuel, a_Item); } + + /// Sets the item in the output slot + void SetOutputSlot(const cItem & a_Item) { SetSlot(fsOutput, a_Item); } + + /// Returns the time that the current item has been cooking, in ticks + int GetTimeCooked(void) const {return m_TimeCooked; } + + /// Returns the time until the current item finishes cooking, in ticks + int GetCookTimeLeft(void) const { return m_NeedCookTime - m_TimeCooked; } - void SetSlot(int a_Slot, const cItem & a_Item); + /// Returns the time until the current fuel is depleted, in ticks + int GetFuelBurnTimeLeft(void) const {return m_FuelBurnTime - m_TimeBurned; } - float GetTimeCooked(void) const {return m_TimeCooked; } - float GetTimeToBurn(void) const {return m_BurnTime - m_TimeBurned; } + /// Returns true if there's time left before the current fuel is depleted + bool HasFuelTimeLeft(void) const { return (GetFuelBurnTimeLeft() > 0); } - void SetBurnTimes(float a_BurnTime, float a_TimeBurned) {m_BurnTime = a_BurnTime; m_TimeBurned = 0; } - void SetCookTimes(float a_CookTime, float a_TimeCooked) {m_CookTime = a_CookTime; m_TimeCooked = a_TimeCooked; } + // tolua_end -private: + void SetBurnTimes(int a_FuelBurnTime, int a_TimeBurned) {m_FuelBurnTime = a_FuelBurnTime; m_TimeBurned = 0; } + void SetCookTimes(int a_NeedCookTime, int a_TimeCooked) {m_NeedCookTime = a_NeedCookTime; m_TimeCooked = a_TimeCooked; } + +protected: - cItem * m_Items; - cItem * m_CookingItem; + /// The recipe for the current input slot + const cFurnaceRecipe::Recipe * m_CurrentRecipe; + + /// The item that is being smelted + cItem m_LastInput; + + bool m_IsCooking; ///< Set to true if the furnace is cooking an item + + // All timers are in ticks + int m_NeedCookTime; ///< Amount of time needed to fully cook current item + int m_TimeCooked; ///< Amount of time that the current item has been cooking + int m_FuelBurnTime; ///< Amount of time that the current fuel can burn (in total); zero if no fuel burning + int m_TimeBurned; ///< Amount of time that the current fuel has been burning - // All timers are in 1 ms - float m_CookTime; // Amount of time needed to fully cook current item - float m_TimeCooked; // Amount of time that the current item has been cooking - float m_BurnTime; // Amount of time that the current fuel can burn (in total); zero if no fuel burning - float m_TimeBurned; // Amount of time that the current fuel has been burning + int m_LastProgressFuel; ///< Last value sent as the progress for the fuel + int m_LastProgressCook; ///< Last value sent as the progress for the cooking void BroadcastProgress(int a_ProgressbarID, short a_Value); -}; + + /// One item finished cooking + void FinishOne(cChunk & a_Chunk); + + /// Starts burning a new fuel, if possible + void BurnNewFuel(void); + + /// Updates the recipe, based on the current input + void UpdateInput(void); + + /// Called when the fuel slot changes or when the fuel is spent, burns another piece of fuel if appropriate + void UpdateFuel(void); + + /// Called when the output slot changes + void UpdateOutput(void); + + /// Updates the m_IsCooking, based on the input slot, output slot and m_FuelBurnTime / m_TimeBurned + void UpdateIsCooking(void); + + /// Returns true if the input can be cooked into output and the item counts allow for another cooking operation + bool CanCookInputToOutput(void) const; + + /// Broadcasts progressbar updates, if needed + void UpdateProgressBars(void); + + // cItemGrid::cListener overrides: + virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override; + +} ; // tolua_export diff --git a/source/BlockEntities/HopperEntity.cpp b/source/BlockEntities/HopperEntity.cpp index fb2e36aa3..1ed0dbbb8 100644 --- a/source/BlockEntities/HopperEntity.cpp +++ b/source/BlockEntities/HopperEntity.cpp @@ -9,6 +9,7 @@ #include "../Player.h" #include "ChestEntity.h" #include "DropSpenserEntity.h" +#include "FurnaceEntity.h" @@ -229,7 +230,28 @@ bool cHopperEntity::MoveItemsFromChest(cChunk & a_Chunk) /// Moves items from a furnace above the hopper into this hopper. Returns true if contents have changed. bool cHopperEntity::MoveItemsFromFurnace(cChunk & a_Chunk) { - // TODO + cFurnaceEntity * Furnace = (cFurnaceEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ); + ASSERT(Furnace != NULL); + + // Try move from the output slot: + if (MoveItemsFromSlot(Furnace->GetOutputSlot(), true)) + { + cItem NewOutput(Furnace->GetOutputSlot()); + Furnace->SetOutputSlot(NewOutput.AddCount(-1)); + return true; + } + + // No output moved, check if we can move an empty bucket out of the fuel slot: + if (Furnace->GetFuelSlot().m_ItemType == E_ITEM_BUCKET) + { + if (MoveItemsFromSlot(Furnace->GetFuelSlot(), true)) + { + Furnace->SetFuelSlot(cItem()); + return true; + } + } + + // Nothing can be moved return false; } -- cgit v1.2.3