From 9749c3aac9dbfbc46a919193c97bb9c9e5339e03 Mon Sep 17 00:00:00 2001 From: Lukas Pioch Date: Thu, 24 Sep 2015 10:48:33 +0200 Subject: Implemented brewing --- src/Bindings/AllToLua.pkg | 1 + src/Bindings/ManualBindings.cpp | 50 +++++ src/Bindings/ManualBindings_World.cpp | 2 + src/Bindings/Plugin.h | 2 + src/Bindings/PluginLua.cpp | 48 +++++ src/Bindings/PluginLua.h | 2 + src/Bindings/PluginManager.cpp | 38 ++++ src/Bindings/PluginManager.h | 5 + src/BlockEntities/BlockEntity.cpp | 2 + src/BlockEntities/BrewingstandEntity.cpp | 309 +++++++++++++++++++++++++++++++ src/BlockEntities/BrewingstandEntity.h | 136 ++++++++++++++ src/BlockEntities/CMakeLists.txt | 2 + src/Blocks/BlockBrewingStand.h | 8 +- src/BrewingRecipes.cpp | 287 ++++++++++++++++++++++++++++ src/BrewingRecipes.h | 55 ++++++ src/CMakeLists.txt | 2 + src/Chunk.cpp | 53 ++++++ src/Chunk.h | 8 + src/ChunkMap.cpp | 33 ++++ src/ChunkMap.h | 9 + src/Root.cpp | 8 +- src/Root.h | 3 + src/UI/BrewingstandWindow.cpp | 70 +++++++ src/UI/BrewingstandWindow.h | 31 ++++ src/UI/CMakeLists.txt | 2 + src/UI/SlotArea.cpp | 238 ++++++++++++++++++++++++ src/UI/SlotArea.h | 30 +++ src/UI/Window.h | 1 + src/World.cpp | 18 ++ src/World.h | 8 + src/WorldStorage/NBTChunkSerializer.cpp | 17 ++ src/WorldStorage/NBTChunkSerializer.h | 2 + src/WorldStorage/WSSAnvil.cpp | 51 +++++ src/WorldStorage/WSSAnvil.h | 1 + 34 files changed, 1523 insertions(+), 9 deletions(-) create mode 100644 src/BlockEntities/BrewingstandEntity.cpp create mode 100644 src/BlockEntities/BrewingstandEntity.h create mode 100644 src/BrewingRecipes.cpp create mode 100644 src/BrewingRecipes.h create mode 100644 src/UI/BrewingstandWindow.cpp create mode 100644 src/UI/BrewingstandWindow.h (limited to 'src') diff --git a/src/Bindings/AllToLua.pkg b/src/Bindings/AllToLua.pkg index 8f55eba07..991ed0ddd 100644 --- a/src/Bindings/AllToLua.pkg +++ b/src/Bindings/AllToLua.pkg @@ -98,6 +98,7 @@ $cfile "../Mobs/Monster.h" $cfile "../BlockEntities/BlockEntity.h" $cfile "../BlockEntities/BeaconEntity.h" $cfile "../BlockEntities/BlockEntityWithItems.h" +$cfile "../BlockEntities/BrewingstandEntity.h" $cfile "../BlockEntities/ChestEntity.h" $cfile "../BlockEntities/CommandBlockEntity.h" $cfile "../BlockEntities/DropSpenserEntity.h" diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index 7e6839fdf..3a595c1d2 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -20,6 +20,7 @@ #include "../ClientHandle.h" #include "../BlockArea.h" #include "../BlockEntities/BeaconEntity.h" +#include "../BlockEntities/BrewingstandEntity.h" #include "../BlockEntities/ChestEntity.h" #include "../BlockEntities/CommandBlockEntity.h" #include "../BlockEntities/DispenserEntity.h" @@ -2516,6 +2517,54 @@ static int tolua_cRoot_GetBuildSeriesName(lua_State * tolua_S) +static int tolua_cRoot_GetBrewingRecipe(lua_State * tolua_S) +{ + cLuaState L(tolua_S); + if ( + !L.CheckParamUserTable(1, "cRoot") || + !L.CheckParamUserType (2, "const cItem") || + !L.CheckParamUserType (3, "const cItem") || + !L.CheckParamEnd (4) + ) + { + return 0; + } + + // Check the bottle param: + cItem * Bottle = nullptr; + L.GetStackValue(2, Bottle); + if (Bottle == nullptr) + { + LOGWARNING("cRoot:GetBrewingRecipe: the Bottle parameter is nil or missing."); + return 0; + } + + cItem * Ingredient = nullptr; + L.GetStackValue(3, Ingredient); + if (Ingredient == nullptr) + { + LOGWARNING("cRoot:GetBrewingRecipe: the Ingredient parameter is nil or missing."); + return 0; + } + + // Get the recipe for the input + cBrewingRecipes * BR = cRoot::Get()->GetBrewingRecipes(); + const cBrewingRecipes::cRecipe * Recipe = BR->GetRecipeFrom(*Bottle, *Ingredient); + if (Recipe == nullptr) + { + // There is no such brewing recipe for this bottle and ingredient, return no value + return 0; + } + + // Push the output item + L.Push(Recipe->Output.get()); + return 1; +} + + + + + static int tolua_cRoot_GetFurnaceRecipe(lua_State * tolua_S) { cLuaState L(tolua_S); @@ -3320,6 +3369,7 @@ void cManualBindings::Bind(lua_State * tolua_S) tolua_function(tolua_S, "DoWithPlayerByUUID", DoWith ); tolua_function(tolua_S, "ForEachPlayer", ForEach); tolua_function(tolua_S, "ForEachWorld", ForEach); + tolua_function(tolua_S, "GetBrewingRecipe", tolua_cRoot_GetBrewingRecipe); tolua_function(tolua_S, "GetBuildCommitID", tolua_cRoot_GetBuildCommitID); tolua_function(tolua_S, "GetBuildDateTime", tolua_cRoot_GetBuildDateTime); tolua_function(tolua_S, "GetBuildID", tolua_cRoot_GetBuildID); diff --git a/src/Bindings/ManualBindings_World.cpp b/src/Bindings/ManualBindings_World.cpp index c664329f9..20fe880c1 100644 --- a/src/Bindings/ManualBindings_World.cpp +++ b/src/Bindings/ManualBindings_World.cpp @@ -580,6 +580,7 @@ void cManualBindings::BindWorld(lua_State * tolua_S) tolua_function(tolua_S, "ChunkStay", tolua_cWorld_ChunkStay); tolua_function(tolua_S, "DoWithBlockEntityAt", DoWithXYZ); tolua_function(tolua_S, "DoWithBeaconAt", DoWithXYZ); + tolua_function(tolua_S, "DoWithBrewingstandAt", DoWithXYZ); tolua_function(tolua_S, "DoWithChestAt", DoWithXYZ); tolua_function(tolua_S, "DoWithDispenserAt", DoWithXYZ); tolua_function(tolua_S, "DoWithDropSpenserAt", DoWithXYZ); @@ -594,6 +595,7 @@ void cManualBindings::BindWorld(lua_State * tolua_S) tolua_function(tolua_S, "FindAndDoWithPlayer", DoWith< cWorld, cPlayer, &cWorld::FindAndDoWithPlayer>); tolua_function(tolua_S, "DoWithPlayerByUUID", DoWith< cWorld, cPlayer, &cWorld::DoWithPlayerByUUID>); tolua_function(tolua_S, "ForEachBlockEntityInChunk", ForEachInChunk); + tolua_function(tolua_S, "ForEachBrewingstandInChunk", ForEachInChunk); tolua_function(tolua_S, "ForEachChestInChunk", ForEachInChunk); tolua_function(tolua_S, "ForEachEntity", ForEach< cWorld, cEntity, &cWorld::ForEachEntity>); tolua_function(tolua_S, "ForEachEntityInBox", ForEachInBox< cWorld, cEntity, &cWorld::ForEachEntityInBox>); diff --git a/src/Bindings/Plugin.h b/src/Bindings/Plugin.h index 956e99dd9..b8fc97a06 100644 --- a/src/Bindings/Plugin.h +++ b/src/Bindings/Plugin.h @@ -45,6 +45,8 @@ public: /** Calls the specified hook with the params given. Returns the bool that the hook callback returns. */ virtual bool OnBlockSpread (cWorld & a_World, int a_BlockX, int a_BlockY, int a_BlockZ, eSpreadSource a_Source) = 0; virtual bool OnBlockToPickups (cWorld & a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cItems & a_Pickups) = 0; + virtual bool OnBrewingCompleting (cWorld & a_World, cBrewingstandEntity & a_BrewingstandEntity) = 0; + virtual bool OnBrewingCompleted (cWorld & a_World, cBrewingstandEntity & a_BrewingstandEntity) = 0; virtual bool OnChat (cPlayer & a_Player, AString & a_Message) = 0; virtual bool OnChunkAvailable (cWorld & a_World, int a_ChunkX, int a_ChunkZ) = 0; virtual bool OnChunkGenerated (cWorld & a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) = 0; diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp index 50a0a387b..3038264d2 100644 --- a/src/Bindings/PluginLua.cpp +++ b/src/Bindings/PluginLua.cpp @@ -294,6 +294,54 @@ bool cPluginLua::OnBlockToPickups(cWorld & a_World, cEntity * a_Digger, int a_Bl +bool cPluginLua::OnBrewingCompleted(cWorld & a_World, cBrewingstandEntity & a_Brewingstand) +{ + cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_BREWING_COMPLETED]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call(static_cast(**itr), &a_World, &a_Brewingstand, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnBrewingCompleting(cWorld & a_World, cBrewingstandEntity & a_Brewingstand) +{ + cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_BREWING_COMPLETING]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call(static_cast(**itr), &a_World, &a_Brewingstand, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + bool cPluginLua::OnChat(cPlayer & a_Player, AString & a_Message) { cCSLock Lock(m_CriticalSection); diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h index 1312103b8..59e56c0e7 100644 --- a/src/Bindings/PluginLua.h +++ b/src/Bindings/PluginLua.h @@ -105,6 +105,8 @@ public: virtual bool OnBlockSpread (cWorld & a_World, int a_BlockX, int a_BlockY, int a_BlockZ, eSpreadSource a_Source) override; virtual bool OnBlockToPickups (cWorld & a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cItems & a_Pickups) override; + virtual bool OnBrewingCompleting (cWorld & a_World, cBrewingstandEntity & a_BrewingstandEntity) override; + virtual bool OnBrewingCompleted (cWorld & a_World, cBrewingstandEntity & a_BrewingstandEntity) override; virtual bool OnChat (cPlayer & a_Player, AString & a_Message) override; virtual bool OnChunkAvailable (cWorld & a_World, int a_ChunkX, int a_ChunkZ) override; virtual bool OnChunkGenerated (cWorld & a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) override; diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp index 72e031b33..4d291f164 100644 --- a/src/Bindings/PluginManager.cpp +++ b/src/Bindings/PluginManager.cpp @@ -269,6 +269,44 @@ bool cPluginManager::CallHookBlockToPickups( +bool cPluginManager::CallHookBrewingCompleted(cWorld & a_World, cBrewingstandEntity & a_Brewingstand) +{ + FIND_HOOK(HOOK_BREWING_COMPLETED); + VERIFY_HOOK; + + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnBrewingCompleted(a_World, a_Brewingstand)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookBrewingCompleting(cWorld & a_World, cBrewingstandEntity & a_Brewingstand) +{ + FIND_HOOK(HOOK_BREWING_COMPLETING); + VERIFY_HOOK; + + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnBrewingCompleting(a_World, a_Brewingstand)) + { + return true; + } + } + return false; +} + + + + + bool cPluginManager::CallHookChat(cPlayer & a_Player, AString & a_Message) { // Check if the message contains a command, execute it: diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h index d7e3b8dfe..58ec74f53 100644 --- a/src/Bindings/PluginManager.h +++ b/src/Bindings/PluginManager.h @@ -10,6 +10,7 @@ // fwd: class cBlockEntityWithItems; +class cBrewingstandEntity; class cChunkDesc; class cClientHandle; class cCommandOutputCallback; @@ -75,6 +76,8 @@ public: { HOOK_BLOCK_SPREAD, HOOK_BLOCK_TO_PICKUPS, + HOOK_BREWING_COMPLETING, + HOOK_BREWING_COMPLETED, HOOK_CHAT, HOOK_CHUNK_AVAILABLE, HOOK_CHUNK_GENERATED, @@ -193,6 +196,8 @@ public: // Calls for individual hooks. Each returns false if the action is to continue or true if the plugin wants to abort bool CallHookBlockSpread (cWorld & a_World, int a_BlockX, int a_BlockY, int a_BlockZ, eSpreadSource a_Source); bool CallHookBlockToPickups (cWorld & a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cItems & a_Pickups); + bool CallHookBrewingCompleting (cWorld & a_World, cBrewingstandEntity & a_Brewingstand); + bool CallHookBrewingCompleted (cWorld & a_World, cBrewingstandEntity & a_Brewingstand); bool CallHookChat (cPlayer & a_Player, AString & a_Message); bool CallHookChunkAvailable (cWorld & a_World, int a_ChunkX, int a_ChunkZ); bool CallHookChunkGenerated (cWorld & a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc); diff --git a/src/BlockEntities/BlockEntity.cpp b/src/BlockEntities/BlockEntity.cpp index c59198e79..0b69830f6 100644 --- a/src/BlockEntities/BlockEntity.cpp +++ b/src/BlockEntities/BlockEntity.cpp @@ -6,6 +6,7 @@ #include "Globals.h" #include "BeaconEntity.h" #include "BlockEntity.h" +#include "BrewingstandEntity.h" #include "ChestEntity.h" #include "CommandBlockEntity.h" #include "DispenserEntity.h" @@ -36,6 +37,7 @@ cBlockEntity * cBlockEntity::CreateByBlockType(BLOCKTYPE a_BlockType, NIBBLETYPE case E_BLOCK_ENDER_CHEST: return new cEnderChestEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); case E_BLOCK_FLOWER_POT: return new cFlowerPotEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); case E_BLOCK_FURNACE: return new cFurnaceEntity (a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_World); + case E_BLOCK_BREWING_STAND: return new cBrewingstandEntity(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_World); case E_BLOCK_HEAD: return new cMobHeadEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); case E_BLOCK_HOPPER: return new cHopperEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); case E_BLOCK_MOB_SPAWNER: return new cMobSpawnerEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); diff --git a/src/BlockEntities/BrewingstandEntity.cpp b/src/BlockEntities/BrewingstandEntity.cpp new file mode 100644 index 000000000..e34297393 --- /dev/null +++ b/src/BlockEntities/BrewingstandEntity.cpp @@ -0,0 +1,309 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "BrewingstandEntity.h" +#include "../Bindings/PluginManager.h" +#include "../UI/BrewingstandWindow.h" +#include "../Entities/Player.h" +#include "../Root.h" +#include "../Chunk.h" + + + + + + + + + + + + +cBrewingstandEntity::cBrewingstandEntity(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cWorld * a_World) : + super(a_BlockType, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World), + m_BlockMeta(a_BlockMeta), + m_IsDestroyed(false), + m_IsBrewing(false), + m_TimeBrewed(0) +{ + m_Contents.AddListener(*this); + for (int i = 0; i < 3; i++) + { + m_Results[i] = *(new cItem()); + } +} + + + + + +cBrewingstandEntity::~cBrewingstandEntity() +{ + // Tell window its owner is destroyed + cWindow * Window = GetWindow(); + if (Window != nullptr) + { + Window->OwnerDestroyed(); + } +} + + + + + +void cBrewingstandEntity::UsedBy(cPlayer * a_Player) +{ + cWindow * Window = GetWindow(); + if (Window == nullptr) + { + OpenWindow(new cBrewingstandWindow(m_PosX, m_PosY, m_PosZ, this)); + Window = GetWindow(); + } + + if (Window != nullptr) + { + if (a_Player->GetWindow() != Window) + { + a_Player->OpenWindow(Window); + } + } + + if (m_IsBrewing) + { + BroadcastProgress(0, m_NeedBrewingTime - m_TimeBrewed); + } + else + { + BroadcastProgress(0, 0); + } +} + + + + + +bool cBrewingstandEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) +{ + UNUSED(a_Dt); + + if (!m_IsBrewing) + { + return false; + } + + const cBrewingRecipes::cRecipe * Recipe = nullptr; + // The necessary brewing time, has been reached + if (m_TimeBrewed >= m_NeedBrewingTime) + { + BroadcastProgress(0, 0); + m_IsBrewing = false; + m_TimeBrewed = 0; + + // Return if the hook has been canceled + if (cPluginManager::Get()->CallHookBrewingCompleting(*m_World, *this)) + { + return false; + } + + // Decrease item count, full stacks are allowed in the ingredient slot + cItem Ingredient = m_Contents.GetSlot(bsIngredient); + Ingredient.m_ItemCount -= 1; + m_Contents.SetSlot(bsIngredient, Ingredient); + + // Loop over all bottle slots and update available bottles + for (int i = 0; i < 3; i++) + { + if (m_Contents.GetSlot(i).IsEmpty() || (m_CurrentBrewingRecipes[i] == nullptr)) + { + continue; + } + + Recipe = m_CurrentBrewingRecipes[i]; + m_Contents.SetSlot(i, Recipe->Output->CopyOne()); + } + + // Brewing process completed + cPluginManager::Get()->CallHookBrewingCompleted(*m_World, *this); + + return true; + } + + m_TimeBrewed++; + UpdateProgressBars(false); + return false; +} + + + + + + +void cBrewingstandEntity::SendTo(cClientHandle & a_Client) +{ + // Nothing needs to be sent + UNUSED(a_Client); +} + + + + + +void cBrewingstandEntity::BroadcastProgress(short a_ProgressbarID, short a_Value) +{ + cWindow * Window = GetWindow(); + if (Window != nullptr) + { + Window->SetProperty(a_ProgressbarID, a_Value); + } +} + + + + + + +void cBrewingstandEntity::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) +{ + super::OnSlotChanged(a_ItemGrid, a_SlotNum); + + if (m_IsDestroyed) + { + return; + } + + ASSERT(a_ItemGrid == &m_Contents); + + // Check if still a item is in the ingredient slot + if (GetSlot(bsIngredient).IsEmpty()) + { + if (m_IsBrewing) + { + // Cancel brewing + BroadcastProgress(0, 0); + m_IsBrewing = false; + m_TimeBrewed = 0; + } + return; + } + + // Recheck the bottles + cBrewingRecipes * BR = cRoot::Get()->GetBrewingRecipes(); + const cBrewingRecipes::cRecipe * Recipe = nullptr; + bool Stop = true; + for (int i = 0; i < 3; i++) + { + if (GetSlot(i).IsEmpty()) + { + m_CurrentBrewingRecipes[i] = nullptr; + m_Results[i].Clear(); + continue; + } + + if (m_CurrentBrewingRecipes[i] != nullptr) + { + Recipe = m_CurrentBrewingRecipes[i]; + if (Recipe->Ingredient->IsEqual(GetSlot(bsIngredient)) && Recipe->Input->IsEqual(GetSlot(i))) + { + Stop = false; + continue; + } + } + + Recipe = BR->GetRecipeFrom(m_Contents.GetSlot(i), m_Contents.GetSlot(bsIngredient)); + if (Recipe != nullptr) + { + // Found a brewing recipe for the items + m_CurrentBrewingRecipes[i] = Recipe; + m_Results[i] = Recipe->Output->CopyOne(); + Stop = false; + } + } + + if (Stop) + { + if (m_IsBrewing) + { + // Cancel brewing + BroadcastProgress(0, 0); + m_IsBrewing = false; + m_TimeBrewed = 0; + } + return; + } + + // Start brewing process, if not running + if (!m_IsBrewing) + { + m_IsBrewing = true; + } +} + + + + + +void cBrewingstandEntity::UpdateProgressBars(bool a_ForceUpdate) +{ + /** Sending an update every 3th tick, using a higher value lets look the progressbar ugly */ + if (!a_ForceUpdate && (m_World->GetWorldAge() % 3 != 0)) + { + return; + } + + BroadcastProgress(0, m_NeedBrewingTime - m_TimeBrewed); +} + + + + + +void cBrewingstandEntity::setTimeBrewed(short a_TimeBrewed) +{ + m_TimeBrewed = a_TimeBrewed; +} + + + + + +void cBrewingstandEntity::ContinueBrewing(void) +{ + // Continue brewing if number is greater than 0 + if (m_TimeBrewed > 0) + { + m_IsBrewing = true; + } +} + + + + + +void cBrewingstandEntity::GetRecipes(void) +{ + if (GetSlot(3).IsEmpty()) + { + return; + } + + cBrewingRecipes * BR = cRoot::Get()->GetBrewingRecipes(); + const cBrewingRecipes::cRecipe * Recipe = nullptr; + for (int i = 0; i < 3; i++) + { + if (GetSlot(i).IsEmpty()) + { + continue; + } + Recipe = BR->GetRecipeFrom(GetSlot(i), GetSlot(bsIngredient)); + if (Recipe != nullptr) + { + m_CurrentBrewingRecipes[i] = Recipe; + m_Results[i] = Recipe->Output->CopyOne(); + } + } +} + + + + + diff --git a/src/BlockEntities/BrewingstandEntity.h b/src/BlockEntities/BrewingstandEntity.h new file mode 100644 index 000000000..a895c4bde --- /dev/null +++ b/src/BlockEntities/BrewingstandEntity.h @@ -0,0 +1,136 @@ + +#pragma once + +#include "BlockEntityWithItems.h" +#include "../BrewingRecipes.h" +#include "../Root.h" + + + + +class cClientHandle; + + + + + +// tolua_begin +class cBrewingstandEntity : + public cBlockEntityWithItems +{ + typedef cBlockEntityWithItems super; + +public: + enum + { + bsLeftBottle = 0, // Left bottle slot number + bsMiddleBottle = 1, // Middle bottle slot number + bsRightBottle = 2, // Right bottle slot number + bsIngredient = 3, // Top ingredient slot number + + ContentsWidth = 4, + ContentsHeight = 1, + }; + + // tolua_end + + BLOCKENTITY_PROTODEF(cBrewingstandEntity) + + /** Constructor used for normal operation */ + cBrewingstandEntity(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cWorld * a_World); + + virtual ~cBrewingstandEntity(); + + // cBlockEntity overrides: + virtual void SendTo(cClientHandle & a_Client) override; + virtual bool Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + virtual void UsedBy(cPlayer * a_Player) override; + virtual void Destroy() override + { + m_IsDestroyed = true; + super::Destroy(); + } + + // tolua_begin + + /** Returns the time until the current items finishes brewing, in ticks */ + short GetBrewingTimeLeft(void) const { return m_NeedBrewingTime - m_TimeBrewed; } + + /** Returns the time that the current items has been brewing, in ticks */ + short GetTimeBrewed(void) { return m_TimeBrewed; } + + /** Returns the item in the left bottle slot */ + const cItem & GetLeftBottleSlot(void) const { return GetSlot(bsLeftBottle); } + + /** Returns the item in the middle bottle slot */ + const cItem & GetMiddleBottleSlot(void) const { return GetSlot(bsMiddleBottle); } + + /** Returns the item in the right bottle slot */ + const cItem & GetRightBottleSlot(void) const { return GetSlot(bsRightBottle); } + + /** Returns the item in the ingredient slot */ + const cItem & GetIndgredientSlot(void) const { return GetSlot(bsIngredient); } + + /** Get the expected result item for the given slot number */ + const cItem & GetResultItem(int a_SlotNumber) { return m_Results[a_SlotNumber]; } + + /** Sets the item in the left bottle slot */ + void SetLeftBottleSlot(const cItem & a_Item) { SetSlot(bsLeftBottle, a_Item); } + + /** Sets the item in the middle bottle slot */ + void SetMiddleBottleSlot(const cItem & a_Item) { SetSlot(bsMiddleBottle, a_Item); } + + /** Sets the item in the right bottle slot */ + void SetRightBottleSlot(const cItem & a_Item) { SetSlot(bsRightBottle, a_Item); } + + /** Sets the item in the ingredient slot */ + void SetIngredientSlot(const cItem & a_Item) { SetSlot(bsIngredient, a_Item); } + + // tolua_end + + /** Sets the current brewing time. Will be called if the brewing stand gets loaded from the world. */ + void setTimeBrewed(short a_TimeBrewed); + + /** Starts the brewing proccess. Will be called if the brewing stand gets loaded from the world. */ + void ContinueBrewing(void); + + /** Gets the recipes. Will be called if the brewing stand gets loaded from the world. */ + void GetRecipes(void); +protected: + + /** Block meta of the block currently represented by this entity */ + NIBBLETYPE m_BlockMeta; + + /** Set to true when the brewing stand entity has been destroyed to prevent the block being set again */ + bool m_IsDestroyed; + + /** Set to true if the brewing stand is brewing an item */ + bool m_IsBrewing; + + /** Brewing time is 400 ticks */ + const short m_NeedBrewingTime = 400; + + /** Store the current brewing recipes */ + const cBrewingRecipes::cRecipe * m_CurrentBrewingRecipes[3] = {}; + + /** Result items for the bottle inputs */ + cItem m_Results[3] = {}; + + /** Amount of ticks that the current item has been brewed */ + short m_TimeBrewed; + + /** Sends the specified progressbar value to all clients of the window */ + void BroadcastProgress(short a_ProgressbarID, short a_Value); + + // /** Broadcasts progressbar updates, if needed */ + void UpdateProgressBars(bool a_ForceUpdate = false); + + // cItemGrid::cListener overrides: + virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override; + +} ; // tolua_export + + + + + diff --git a/src/BlockEntities/CMakeLists.txt b/src/BlockEntities/CMakeLists.txt index 0d1776eb5..931484be0 100644 --- a/src/BlockEntities/CMakeLists.txt +++ b/src/BlockEntities/CMakeLists.txt @@ -7,6 +7,7 @@ include_directories ("${PROJECT_SOURCE_DIR}/../") SET (SRCS BeaconEntity.cpp BlockEntity.cpp + BrewingstandEntity.cpp ChestEntity.cpp CommandBlockEntity.cpp DispenserEntity.cpp @@ -27,6 +28,7 @@ SET (HDRS BeaconEntity.h BlockEntity.h BlockEntityWithItems.h + BrewingstandEntity.h ChestEntity.h CommandBlockEntity.h DispenserEntity.h diff --git a/src/Blocks/BlockBrewingStand.h b/src/Blocks/BlockBrewingStand.h index f68f9d8af..0ab5c8eba 100644 --- a/src/Blocks/BlockBrewingStand.h +++ b/src/Blocks/BlockBrewingStand.h @@ -1,18 +1,18 @@ #pragma once +#include "BlockEntity.h" #include "BlockHandler.h" - - +#include "MetaRotator.h" class cBlockBrewingStandHandler : - public cBlockHandler + public cMetaRotator { public: cBlockBrewingStandHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) + : cMetaRotator(a_BlockType) { } diff --git a/src/BrewingRecipes.cpp b/src/BrewingRecipes.cpp new file mode 100644 index 000000000..3ee39c8af --- /dev/null +++ b/src/BrewingRecipes.cpp @@ -0,0 +1,287 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "BrewingRecipes.h" +#include "Item.h" + +#include + +#define BREWING_RECIPE_FILE "brewing.txt" + + + + + +typedef std::vector> RecipeList; + + + + + +struct cBrewingRecipes::sBrewingRecipeState +{ + RecipeList Recipes; +}; + + + + + +cBrewingRecipes::cBrewingRecipes() + : m_pState(new sBrewingRecipeState) +{ + ReloadRecipes(); +} + + + + + +cBrewingRecipes::~cBrewingRecipes() +{ + ClearRecipes(); +} + + + + + +void cBrewingRecipes::ReloadRecipes(void) +{ + ClearRecipes(); + LOGD("Loading brewing recipes..."); + + std::ifstream f(BREWING_RECIPE_FILE, std::ios::in); + if (!f.good()) + { + LOG("Could not open the brewing recipes file \"%s\". No brewing recipes are available.", BREWING_RECIPE_FILE); + return; + } + + unsigned int LineNum = 0; + AString ParsingLine; + + while (std::getline(f, ParsingLine)) + { + LineNum++; + // Remove comments from the line: + size_t FirstCommentSymbol = ParsingLine.find('#'); + if (FirstCommentSymbol != AString::npos) + { + ParsingLine.erase(ParsingLine.begin() += static_cast(FirstCommentSymbol), ParsingLine.end()); + } + + if (ParsingLine.empty()) + { + continue; + } + AddRecipeFromLine(ParsingLine, LineNum); + } // while (getline(ParsingLine)) + + LOG("Loaded " SIZE_T_FMT " brewing recipes", m_pState->Recipes.size()); +} + + + + + +void cBrewingRecipes::AddRecipeFromLine(const AString & a_Line, unsigned int a_LineNum) +{ + AString Line(a_Line); + Line.erase(std::remove_if(Line.begin(), Line.end(), isspace), Line.end()); + + short InputDamage; + short OutputDamage; + + std::unique_ptr InputItem = cpp14::make_unique(); + std::unique_ptr IngredientItem = cpp14::make_unique(); + std::unique_ptr OutputItem = cpp14::make_unique(); + + const AStringVector & InputAndIngredient = StringSplit(Line, "+"); + + if (InputAndIngredient.size() != 2) + { + LOGWARNING("brewing.txt: line %d: A line with '+' was expected", a_LineNum); + LOGINFO("Offending line: \"%s\"", a_Line.c_str()); + return; + } + + const AStringVector & IngredientAndOutput = StringSplit(InputAndIngredient[1].c_str(), "="); + if (IngredientAndOutput.size() != 2) + { + LOGWARNING("brewing.txt: line %d: A line with '=' was expected", a_LineNum); + LOGINFO("Offending line: \"%s\"", a_Line.c_str()); + return; + } + + if (!ParseItem(IngredientAndOutput[0], *IngredientItem)) + { + LOGWARNING("brewing.txt: Parsing of the item didn't worked."); + LOGINFO("Offending line: \"%s\"", a_Line.c_str()); + return; + } + + if (!StringToInteger(InputAndIngredient[0], InputDamage)) + { + LOGWARNING("brewing.txt: line %d: Cannot parse the damage value for the input item\"%s\".", a_LineNum, InputAndIngredient[0].c_str()); + LOGINFO("Offending line: \"%s\"", a_Line.c_str()); + return; + } + + if (!StringToInteger(IngredientAndOutput[1], OutputDamage)) + { + LOGWARNING("brewing.txt: line %d: Cannot parse the damage value for the output item\"%s\".", a_LineNum, IngredientAndOutput[1].c_str()); + LOGINFO("Offending line: \"%s\"", a_Line.c_str()); + return; + } + + // The items has always the same type + InputItem->m_ItemType = E_ITEM_POTION; + InputItem->m_ItemDamage = InputDamage; + + OutputItem->m_ItemType = E_ITEM_POTION; + OutputItem->m_ItemDamage = OutputDamage; + + std::unique_ptr Recipe = cpp14::make_unique(); + Recipe->Input = std::move(InputItem); + Recipe->Output = std::move(OutputItem); + Recipe->Ingredient = std::move(IngredientItem); + m_pState->Recipes.push_back(std::move(Recipe)); +} + + + + + +bool cBrewingRecipes::ParseItem(const AString & a_String, cItem & a_Item) +{ + return StringToItem(a_String, a_Item); +} + + + + + +void cBrewingRecipes::ClearRecipes(void) +{ + m_pState->Recipes.clear(); +} + + + + + +const cBrewingRecipes::cRecipe * cBrewingRecipes::GetRecipeFrom(const cItem & a_Input, const cItem & a_Ingredient) const +{ + for (auto & Recipe : m_pState->Recipes) + { + if ((Recipe->Input->IsEqual(a_Input)) && (Recipe->Ingredient->IsEqual(a_Ingredient))) + { + return Recipe.get(); + } + } + + // Check for gunpowder + if (a_Ingredient.m_ItemType == E_ITEM_GUNPOWDER) + { + if (a_Input.m_ItemDamage & 0x2000) + { + // Create new recipe and add it to list + std::unique_ptr InputItem = cpp14::make_unique(); + std::unique_ptr IngredientItem = cpp14::make_unique(); + std::unique_ptr OutputItem = cpp14::make_unique(); + + InputItem->m_ItemType = E_ITEM_POTION; + InputItem->m_ItemDamage = a_Input.m_ItemDamage; + OutputItem->m_ItemType = E_ITEM_POTION; + OutputItem->m_ItemDamage = a_Input.m_ItemDamage + 8192; + IngredientItem->m_ItemType = E_ITEM_GUNPOWDER; + + std::unique_ptr Recipe = cpp14::make_unique(); + Recipe->Input = std::move(InputItem); + Recipe->Output = std::move(OutputItem); + Recipe->Ingredient = std::move(IngredientItem); + m_pState->Recipes.push_back(std::move(Recipe)); + return Recipe.get(); + } + return nullptr; + } + + // Check for splash potion + if (a_Input.m_ItemDamage & 0x4000) + { + const std::unique_ptr * FoundRecipe = nullptr; + // Search for the drinkable potion, the ingredients are the same + short SplashItemDamage = a_Input.m_ItemDamage - 8192; + + for (auto & Recipe : m_pState->Recipes) + { + if ((Recipe->Input->m_ItemDamage == SplashItemDamage) && (Recipe->Ingredient->IsEqual(a_Ingredient))) + { + FoundRecipe = &Recipe; + break; + } + } + + if (FoundRecipe == nullptr) + { + return nullptr; + } + + // Create new recipe and add it to list + std::unique_ptr InputItem = cpp14::make_unique(); + std::unique_ptr IngredientItem = cpp14::make_unique(); + std::unique_ptr OutputItem = cpp14::make_unique(); + + InputItem->m_ItemType = E_ITEM_POTION; + InputItem->m_ItemDamage = a_Input.m_ItemDamage; + OutputItem->m_ItemType = E_ITEM_POTION; + OutputItem->m_ItemDamage = (*FoundRecipe)->Output->m_ItemDamage + 8192; + IngredientItem->m_ItemType = (*FoundRecipe)->Ingredient->m_ItemType; + + std::unique_ptr Recipe = cpp14::make_unique(); + Recipe->Input = std::move(InputItem); + Recipe->Output = std::move(OutputItem); + Recipe->Ingredient = std::move(IngredientItem); + m_pState->Recipes.push_back(std::move(Recipe)); + return Recipe.get(); + } + return nullptr; +} + + + + + + +bool cBrewingRecipes::IsIngredient(const cItem & a_Ingredient) const +{ + // Check for gunpowder + if (a_Ingredient.m_ItemType == E_ITEM_GUNPOWDER) + { + return true; + } + + for (auto & Recipe : m_pState->Recipes) + { + if (Recipe->Ingredient->IsEqual(a_Ingredient)) + { + return true; + } + } + return false; +} + + + + + +bool cBrewingRecipes::IsBottle(const cItem & a_Item) const +{ + return (a_Item.m_ItemType == E_ITEM_POTION); +} + + + + + diff --git a/src/BrewingRecipes.h b/src/BrewingRecipes.h new file mode 100644 index 000000000..82fb0b822 --- /dev/null +++ b/src/BrewingRecipes.h @@ -0,0 +1,55 @@ + +#pragma once + + + + + +class cItem; + + + + + +class cBrewingRecipes +{ +public: + cBrewingRecipes(void); + ~cBrewingRecipes(); + + void ReloadRecipes(void); + + struct cRecipe + { + cRecipe() {} + cRecipe(cRecipe &&) {} + + cRecipe(const cRecipe&) = delete; + cRecipe & operator=(const cRecipe&) = delete; + + std::unique_ptr Input; + std::unique_ptr Output; + std::unique_ptr Ingredient; + }; + + /** Returns a recipe for the specified input, nullptr if no recipe found */ + const cRecipe * GetRecipeFrom(const cItem & a_Input, const cItem & a_Ingredient) const; + + /** Returns true if the item is a ingredient, false if not. */ + bool IsIngredient(const cItem & a_Ingredient) const; + + /** Returns true if the item is a bottle / potion, false if not. */ + bool IsBottle(const cItem & a_Bottle) const; +private: + void ClearRecipes(void); + + /** Parses the recipe contained in the line, adds it to m_pState's recipes. + Logs a warning to the console on input error. */ + void AddRecipeFromLine(const AString & a_Line, unsigned int a_LineNum); + + /** Parses an item string, returns true if successful. */ + bool ParseItem(const AString & a_String, cItem & a_Item); + + struct sBrewingRecipeState; + std::unique_ptr m_pState; +}; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d37ba5419..9bc756eef 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -18,6 +18,7 @@ SET (SRCS BlockArea.cpp BlockID.cpp BlockInfo.cpp + BrewingRecipes.cpp Broadcaster.cpp BoundingBox.cpp ByteBuffer.cpp @@ -84,6 +85,7 @@ SET (HDRS BlockInServerPluginInterface.h BlockInfo.h BlockTracer.h + BrewingRecipes.h Broadcaster.h BoundingBox.h BuildInfo.h diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 39a5a6053..3e4a497be 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -13,6 +13,7 @@ #include "zlib/zlib.h" #include "Defines.h" #include "BlockEntities/BeaconEntity.h" +#include "BlockEntities/BrewingstandEntity.h" #include "BlockEntities/ChestEntity.h" #include "BlockEntities/DispenserEntity.h" #include "BlockEntities/DropperEntity.h" @@ -1359,6 +1360,7 @@ void cChunk::CreateBlockEntities(void) case E_BLOCK_JUKEBOX: case E_BLOCK_FLOWER_POT: case E_BLOCK_MOB_SPAWNER: + case E_BLOCK_BREWING_STAND: { if (!HasBlockEntityAt(x + m_PosX * Width, y, z + m_PosZ * Width)) { @@ -1491,6 +1493,7 @@ void cChunk::SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, case E_BLOCK_JUKEBOX: case E_BLOCK_FLOWER_POT: case E_BLOCK_MOB_SPAWNER: + case E_BLOCK_BREWING_STAND: { AddBlockEntity(cBlockEntity::CreateByBlockType(a_BlockType, a_BlockMeta, WorldPos.x, WorldPos.y, WorldPos.z, m_World)); break; @@ -2056,6 +2059,24 @@ bool cChunk::ForEachBlockEntity(cBlockEntityCallback & a_Callback) +bool cChunk::ForEachBrewingstand(cBrewingstandCallback & a_Callback) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) + { + ++itr2; + if (a_Callback.Item(reinterpret_cast(*itr))) + { + return false; + } + } // for itr - m_BlockEntitites[] + return true; +} + + + + + bool cChunk::ForEachChest(cChestCallback & a_Callback) { // The blockentity list is locked by the parent chunkmap's CS @@ -2271,6 +2292,38 @@ bool cChunk::DoWithBeaconAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBeaconCal +bool cChunk::DoWithBrewingstandAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBrewingstandCallback & a_Callback) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) + { + ++itr2; + if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ)) + { + continue; + } + if ((*itr)->GetBlockType() != E_BLOCK_BREWING_STAND) + { + // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out + return false; + } + + // The correct block entity is here + if (a_Callback.Item(reinterpret_cast(*itr))) + { + return false; + } + return true; + } // for itr - m_BlockEntitites[] + + // Not found: + return false; +} + + + + + bool cChunk::DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback) { // The blockentity list is locked by the parent chunkmap's CS diff --git a/src/Chunk.h b/src/Chunk.h index 6316f6910..60fbafa94 100644 --- a/src/Chunk.h +++ b/src/Chunk.h @@ -31,6 +31,7 @@ class MTRand; class cPlayer; class cChunkMap; class cBeaconEntity; +class cBrewingstandEntity; class cBoundingBox; class cChestEntity; class cChunkDataCallback; @@ -54,6 +55,7 @@ class cSetChunkData; typedef std::list cClientHandleList; typedef cItemCallback cEntityCallback; typedef cItemCallback cBeaconCallback; +typedef cItemCallback cBrewingstandCallback; typedef cItemCallback cChestCallback; typedef cItemCallback cDispenserCallback; typedef cItemCallback cFurnaceCallback; @@ -256,6 +258,9 @@ public: /** Calls the callback for each block entity; returns true if all block entities processed, false if the callback aborted by returning true */ bool ForEachBlockEntity(cBlockEntityCallback & a_Callback); // Lua-accessible + /** Calls the callback for each brewingstand; returns true if all brewingstands processed, false if the callback aborted by returning true */ + bool ForEachBrewingstand(cBrewingstandCallback & a_Callback); // Lua-accessible + /** Calls the callback for each chest; returns true if all chests processed, false if the callback aborted by returning true */ bool ForEachChest(cChestCallback & a_Callback); // Lua-accessible @@ -279,6 +284,9 @@ public: /** Calls the callback for the beacon at the specified coords; returns false if there's no beacon at those coords, true if found */ bool DoWithBeaconAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBeaconCallback & a_Callback); // Lua-acessible + /** Calls the callback for the brewingstand at the specified coords; returns false if there's no brewingstand at those coords, true if found */ + bool DoWithBrewingstandAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBrewingstandCallback & a_Callback); // Lua-acessible + /** Calls the callback for the chest at the specified coords; returns false if there's no chest at those coords, true if found */ bool DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback); // Lua-acessible diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp index 59a743746..d95ffd6ef 100644 --- a/src/ChunkMap.cpp +++ b/src/ChunkMap.cpp @@ -2085,6 +2085,21 @@ bool cChunkMap::ForEachBlockEntityInChunk(int a_ChunkX, int a_ChunkZ, cBlockEnti +bool cChunkMap::ForEachBrewingstandInChunk(int a_ChunkX, int a_ChunkZ, cBrewingstandCallback & a_Callback) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ); + if ((Chunk == nullptr) || !Chunk->IsValid()) + { + return false; + } + return Chunk->ForEachBrewingstand(a_Callback); +} + + + + + bool cChunkMap::ForEachChestInChunk(int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback) { cCSLock Lock(m_CSLayers); @@ -2196,6 +2211,24 @@ bool cChunkMap::DoWithBeaconAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBeacon +bool cChunkMap::DoWithBrewingstandAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBrewingstandCallback & a_Callback) +{ + int ChunkX, ChunkZ; + int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; + cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ); + if ((Chunk == nullptr) || !Chunk->IsValid()) + { + return false; + } + return Chunk->DoWithBrewingstandAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + bool cChunkMap::DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback) { int ChunkX, ChunkZ; diff --git a/src/ChunkMap.h b/src/ChunkMap.h index e9309dbd8..e229d108c 100644 --- a/src/ChunkMap.h +++ b/src/ChunkMap.h @@ -20,6 +20,7 @@ class cChunkStay; class cChunk; class cPlayer; class cBeaconEntity; +class cBrewingstandEntity; class cChestEntity; class cDispenserEntity; class cDropperEntity; @@ -43,6 +44,7 @@ typedef cChunk * cChunkPtr; typedef cItemCallback cEntityCallback; typedef cItemCallback cBlockEntityCallback; typedef cItemCallback cBeaconCallback; +typedef cItemCallback cBrewingstandCallback; typedef cItemCallback cChestCallback; typedef cItemCallback cDispenserCallback; typedef cItemCallback cDropperCallback; @@ -246,6 +248,10 @@ public: Returns true if all block entities processed, false if the callback aborted by returning true. */ bool ForEachBlockEntityInChunk(int a_ChunkX, int a_ChunkZ, cBlockEntityCallback & a_Callback); // Lua-accessible + /** Calls the callback for brewingstand in the specified chunk. + Returns true if all brewingstands processed, false if the callback aborted by returning true. */ + bool ForEachBrewingstandInChunk(int a_ChunkX, int a_ChunkZ, cBrewingstandCallback & a_Callback); // Lua-accessible + /** Calls the callback for each chest in the specified chunk. Returns true if all chests processed, false if the callback aborted by returning true. */ bool ForEachChestInChunk(int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback); // Lua-accessible @@ -274,6 +280,9 @@ public: Returns false if there's no beacon at those coords, true if found. */ bool DoWithBeaconAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBeaconCallback & a_Callback); // Lua-acessible + /** Calls the callback for the brewingstand at the specified coords; returns false if there's no brewingstand at those coords, true if found */ + bool DoWithBrewingstandAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBrewingstandCallback & a_Callback); // Lua-acessible + /** Calls the callback for the chest at the specified coords. Returns false if there's no chest at those coords, true if found. */ bool DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback); // Lua-acessible diff --git a/src/Root.cpp b/src/Root.cpp index 57056cc1b..ed9076268 100644 --- a/src/Root.cpp +++ b/src/Root.cpp @@ -5,6 +5,7 @@ #include "Server.h" #include "World.h" #include "WebAdmin.h" +#include "BrewingRecipes.h" #include "FurnaceRecipe.h" #include "CraftingRecipes.h" #include "Bindings/PluginManager.h" @@ -53,6 +54,7 @@ cRoot::cRoot(void) : m_MonsterConfig(nullptr), m_CraftingRecipes(nullptr), m_FurnaceRecipe(nullptr), + m_BrewingRecipes(nullptr), m_WebAdmin(nullptr), m_PluginManager(nullptr), m_MojangAPI(nullptr) @@ -169,6 +171,7 @@ void cRoot::Start(std::unique_ptr a_OverridesRepo) m_RankManager->Initialize(*m_MojangAPI); m_CraftingRecipes = new cCraftingRecipes(); m_FurnaceRecipe = new cFurnaceRecipe(); + m_BrewingRecipes.reset(new cBrewingRecipes()); LOGD("Loading worlds..."); LoadWorlds(*settingsRepo); @@ -913,8 +916,3 @@ int cRoot::GetFurnaceFuelBurnTime(const cItem & a_Fuel) cFurnaceRecipe * FR = Get()->GetFurnaceRecipe(); return FR->GetBurnTime(a_Fuel); } - - - - - diff --git a/src/Root.h b/src/Root.h index 772d858d9..142b323a5 100644 --- a/src/Root.h +++ b/src/Root.h @@ -16,6 +16,7 @@ // fwd: class cThread; class cMonsterConfig; +class cBrewingRecipes; class cCraftingRecipes; class cFurnaceRecipe; class cWebAdmin; @@ -88,6 +89,7 @@ public: cCraftingRecipes * GetCraftingRecipes(void) { return m_CraftingRecipes; } // tolua_export cFurnaceRecipe * GetFurnaceRecipe (void) { return m_FurnaceRecipe; } // Exported in ManualBindings.cpp with quite a different signature + cBrewingRecipes * GetBrewingRecipes (void) { return m_BrewingRecipes.get(); } // Exported in ManualBindings.cpp /** Returns the number of ticks for how long the item would fuel a furnace. Returns zero if not a fuel */ static int GetFurnaceFuelBurnTime(const cItem & a_Fuel); // tolua_export @@ -208,6 +210,7 @@ private: cCraftingRecipes * m_CraftingRecipes; cFurnaceRecipe * m_FurnaceRecipe; + std::unique_ptr m_BrewingRecipes; cWebAdmin * m_WebAdmin; cPluginManager * m_PluginManager; cAuthenticator m_Authenticator; diff --git a/src/UI/BrewingstandWindow.cpp b/src/UI/BrewingstandWindow.cpp new file mode 100644 index 000000000..2f1f3c97d --- /dev/null +++ b/src/UI/BrewingstandWindow.cpp @@ -0,0 +1,70 @@ + +// BrewingstandWindow.cpp + +// Representing the UI window for the brewing stand block + +#include "Globals.h" +#include "BrewingstandWindow.h" +#include "SlotArea.h" +#include "../BrewingRecipes.h" +#include "../Root.h" + + + + + +cBrewingstandWindow::cBrewingstandWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cBrewingstandEntity * a_Brewingstand) : + cWindow(wtBrewery, "Brewingstand") +{ + m_SlotAreas.push_back(new cSlotAreaBrewingstand(a_Brewingstand, *this)); + m_SlotAreas.push_back(new cSlotAreaInventory(*this)); + m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); +} + + + + + +void cBrewingstandWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) +{ + cSlotAreas AreasInOrder; + + if (a_ClickedArea == m_SlotAreas[0]) + { + // Brewing stand Area + if ((a_Slot >= 0) && (a_Slot < 3)) + { + // Bottle slots + AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */ + AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */ + super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, true); + } + else + { + // Ingredient slot + AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */ + AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */ + super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false); + } + } + else + { + cBrewingRecipes * BR = cRoot::Get()->GetBrewingRecipes(); + if ((BR->IsBottle(a_ItemStack)) || (BR->IsIngredient(a_ItemStack))) + { + AreasInOrder.push_back(m_SlotAreas[0]); /* brewing stand Area */ + } + else if (a_ClickedArea == m_SlotAreas[1]) + { + // Inventory Area + AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */ + } + else + { + // Hotbar Area + AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */ + } + + super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false); + } +} diff --git a/src/UI/BrewingstandWindow.h b/src/UI/BrewingstandWindow.h new file mode 100644 index 000000000..e55752187 --- /dev/null +++ b/src/UI/BrewingstandWindow.h @@ -0,0 +1,31 @@ + +// BrewingstandWindow.h + +// Representing the UI window for the brewing stand block + + + + + +#pragma once + +#include "Window.h" + + + + + +class cBrewingstandWindow : + public cWindow +{ + typedef cWindow super; + +public: + cBrewingstandWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cBrewingstandEntity * a_Brewingstand); + + virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) override; +}; + + + + diff --git a/src/UI/CMakeLists.txt b/src/UI/CMakeLists.txt index b907919c8..ad3c00f5f 100644 --- a/src/UI/CMakeLists.txt +++ b/src/UI/CMakeLists.txt @@ -9,6 +9,7 @@ SET (SRCS Window.cpp AnvilWindow.cpp BeaconWindow.cpp + BrewingstandWindow.cpp ChestWindow.cpp CraftingWindow.cpp DropSpenserWindow.cpp @@ -23,6 +24,7 @@ SET (HDRS Window.h AnvilWindow.h BeaconWindow.h + BrewingstandWindow.h ChestWindow.h CraftingWindow.h DropSpenserWindow.h diff --git a/src/UI/SlotArea.cpp b/src/UI/SlotArea.cpp index 478c004fb..2c0dfbe40 100644 --- a/src/UI/SlotArea.cpp +++ b/src/UI/SlotArea.cpp @@ -7,6 +7,7 @@ #include "SlotArea.h" #include "../Entities/Player.h" #include "../BlockEntities/BeaconEntity.h" +#include "../BlockEntities/BrewingstandEntity.h" #include "../BlockEntities/ChestEntity.h" #include "../BlockEntities/DropSpenserEntity.h" #include "../BlockEntities/EnderChestEntity.h" @@ -1934,6 +1935,242 @@ void cSlotAreaFurnace::HandleSmeltItem(const cItem & a_Result, cPlayer & a_Playe +//////////////////////////////////////////////////////////////////////////////// +// cSlotAreaBrewingstand: +cSlotAreaBrewingstand::cSlotAreaBrewingstand(cBrewingstandEntity * a_Brewingstand, cWindow & a_ParentWindow) : + cSlotArea(4, a_ParentWindow), + m_Brewingstand(a_Brewingstand) +{ + m_Brewingstand->GetContents().AddListener(*this); +} + + + + + +cSlotAreaBrewingstand::~cSlotAreaBrewingstand() +{ + m_Brewingstand->GetContents().RemoveListener(*this); +} + + + + + +void cSlotAreaBrewingstand::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) +{ + if (m_Brewingstand == nullptr) + { + LOGERROR("cSlotAreaBrewingstand::Clicked(): m_Brewingstand == nullptr"); + ASSERT(!"cSlotAreaBrewingstand::Clicked(): m_Brewingstand == nullptr"); + return; + } + + if ((a_SlotNum >= 0) && (a_SlotNum < 3)) + { + bool bAsync = false; + if (GetSlot(a_SlotNum, a_Player) == nullptr) + { + LOGWARNING("GetSlot(%d) returned nullptr! Ignoring click", a_SlotNum); + return; + } + + cItem Slot(*GetSlot(a_SlotNum, a_Player)); + if (!Slot.IsSameType(a_ClickedItem)) + { + LOGWARNING("*** Window lost sync at item %d in SlotArea with %d items ***", a_SlotNum, m_NumSlots); + LOGWARNING("My item: %s", ItemToFullString(Slot).c_str()); + LOGWARNING("Their item: %s", ItemToFullString(a_ClickedItem).c_str()); + bAsync = true; + } + + switch (a_ClickAction) + { + case caShiftLeftClick: + case caShiftRightClick: + { + HandleBrewedItem(a_Player); + ShiftClicked(a_Player, a_SlotNum, Slot); + return; + } + case caMiddleClick: + { + MiddleClicked(a_Player, a_SlotNum); + return; + } + case caDropKey: + case caCtrlDropKey: + { + DropClicked(a_Player, a_SlotNum, (a_SlotNum == caCtrlDropKey)); + Slot.m_ItemCount = Slot.m_ItemCount - GetSlot(a_SlotNum, a_Player)->m_ItemCount; + HandleBrewedItem(a_Player); + return; + } + default: + { + break; + } + } + + cItem & DraggingItem = a_Player.GetDraggingItem(); + if (!DraggingItem.IsEmpty()) + { + super::Clicked(a_Player, a_SlotNum, a_ClickAction, a_ClickedItem); + return; + } + else + { + switch (a_ClickAction) + { + case caDblClick: + { + DblClicked(a_Player, a_SlotNum); + return; + } + case caLeftClick: + { + DraggingItem = Slot; + HandleBrewedItem(a_Player); + Slot.Empty(); + break; + } + case caRightClick: + { + DraggingItem = Slot.CopyOne(); + DraggingItem.m_ItemCount = static_cast(static_cast(Slot.m_ItemCount) / 2.f + 0.5f); + Slot.m_ItemCount -= DraggingItem.m_ItemCount; + + if (Slot.m_ItemCount <= 0) + { + Slot.Empty(); + } + HandleBrewedItem(a_Player); + break; + } + default: + { + ASSERT(!"Unhandled click type!"); + } + } + } + + SetSlot(a_SlotNum, a_Player, Slot); + if (bAsync) + { + m_ParentWindow.BroadcastWholeWindow(); + } + return; + } + + super::Clicked(a_Player, a_SlotNum, a_ClickAction, a_ClickedItem); +} + + + + + +void cSlotAreaBrewingstand::HandleBrewedItem(cPlayer & a_Player) +{ + a_Player.AwardAchievement(achBrewPotion); +} + + + + + +void cSlotAreaBrewingstand::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots, bool a_BackFill) +{ + int SlotNum = -1; + cBrewingRecipes * BR = cRoot::Get()->GetBrewingRecipes(); + if (BR->IsBottle(a_ItemStack)) + { + for (int i = 0;i < 3;i++) + { + if (GetSlot(i, a_Player)->IsEmpty()) + { + SlotNum = i; + break; + } + } + + if (SlotNum == -1) + { + // All slots are full + return; + } + } + else if (BR->IsIngredient(a_ItemStack)) + { + SlotNum = 3; + } + else + { + return; + } + + const cItem * Slot = GetSlot(SlotNum, a_Player); + if (!Slot->IsEqual(a_ItemStack) && (!Slot->IsEmpty() || a_KeepEmptySlots)) + { + // Different items + return; + } + + char NumFit = ItemHandler(Slot->m_ItemType)->GetMaxStackSize() - Slot->m_ItemCount; + if (NumFit <= 0) + { + // Full stack already + return; + } + NumFit = std::min(NumFit, a_ItemStack.m_ItemCount); + + if (a_ShouldApply) + { + cItem NewSlot(a_ItemStack); + NewSlot.m_ItemCount = Slot->m_ItemCount + NumFit; + SetSlot(SlotNum, a_Player, NewSlot); + } + a_ItemStack.m_ItemCount -= NumFit; + if (a_ItemStack.IsEmpty()) + { + return; + } +} + + + + + +const cItem * cSlotAreaBrewingstand::GetSlot(int a_SlotNum, cPlayer & a_Player) const +{ + UNUSED(a_Player); + // a_SlotNum ranges from 0 to 3, query the items from the underlying brewing stand: + return &(m_Brewingstand->GetSlot(a_SlotNum)); +} + + + + + +void cSlotAreaBrewingstand::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) +{ + UNUSED(a_Player); + m_Brewingstand->SetSlot(a_SlotNum, a_Item); +} + + + + + +void cSlotAreaBrewingstand::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) +{ + UNUSED(a_SlotNum); + // Something has changed in the window, broadcast the entire window to all clients + ASSERT(a_ItemGrid == &(m_Brewingstand->GetContents())); + + m_ParentWindow.BroadcastWholeWindow(); +} + + //////////////////////////////////////////////////////////////////////////////// // cSlotAreaMinecartWithChest: @@ -2342,3 +2579,4 @@ cItem * cSlotAreaTemporary::GetPlayerSlots(cPlayer & a_Player) + diff --git a/src/UI/SlotArea.h b/src/UI/SlotArea.h index b150c47a1..0ff36ce50 100644 --- a/src/UI/SlotArea.h +++ b/src/UI/SlotArea.h @@ -16,6 +16,7 @@ class cWindow; class cPlayer; class cBeaconEntity; +class cBrewingstandEntity; class cChestEntity; class cEnderChestEntity; class cFurnaceEntity; @@ -456,6 +457,35 @@ protected: +class cSlotAreaBrewingstand : + public cSlotArea, + public cItemGrid::cListener +{ + typedef cSlotArea super; + +public: + cSlotAreaBrewingstand(cBrewingstandEntity * a_Brewingstand, cWindow & a_ParentWindow); + + virtual ~cSlotAreaBrewingstand(); + + virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override; + virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots, bool a_BackFill) override; + virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override; + virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override; +protected: + cBrewingstandEntity * m_Brewingstand; + + // cItemGrid::cListener overrides: + virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override; + + /** Called after an item has been brewed to handle statistics etc. */ + void HandleBrewedItem(cPlayer & a_Player); +} ; + + + + + class cSlotAreaMinecartWithChest : public cSlotArea { diff --git a/src/UI/Window.h b/src/UI/Window.h index cb8f40767..76d22a12c 100644 --- a/src/UI/Window.h +++ b/src/UI/Window.h @@ -18,6 +18,7 @@ class cPlayer; class cWindowOwner; class cClientHandle; +class cBrewingstandEntity; class cChestEntity; class cEnderChestEntity; class cFurnaceEntity; diff --git a/src/World.cpp b/src/World.cpp index de0dc6ea5..bd06af1b7 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -1151,6 +1151,15 @@ bool cWorld::ForEachBlockEntityInChunk(int a_ChunkX, int a_ChunkZ, cBlockEntityC +bool cWorld::ForEachBrewingstandInChunk(int a_ChunkX, int a_ChunkZ, cBrewingstandCallback & a_Callback) +{ + return m_ChunkMap->ForEachBrewingstandInChunk(a_ChunkX, a_ChunkZ, a_Callback); +} + + + + + bool cWorld::ForEachChestInChunk(int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback) { return m_ChunkMap->ForEachChestInChunk(a_ChunkX, a_ChunkZ, a_Callback); @@ -1260,6 +1269,15 @@ bool cWorld::DoWithBeaconAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBeaconCal +bool cWorld::DoWithBrewingstandAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBrewingstandCallback & a_Callback) +{ + return m_ChunkMap->DoWithBrewingstandAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + bool cWorld::DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback) { return m_ChunkMap->DoWithChestAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); diff --git a/src/World.h b/src/World.h index 8529ffb4d..30ac52763 100644 --- a/src/World.h +++ b/src/World.h @@ -46,6 +46,7 @@ class cBlockEntity; class cWorldGenerator; // The generator that actually generates the chunks for a single world class cChunkGenerator; // The thread responsible for generating chunks class cBeaconEntity; +class cBrewingstandEntity; class cChestEntity; class cDispenserEntity; class cFlowerPotEntity; @@ -66,6 +67,7 @@ typedef std::vector cSetChunkDataPtrs; typedef cItemCallback cPlayerListCallback; typedef cItemCallback cEntityCallback; typedef cItemCallback cBeaconCallback; +typedef cItemCallback cBrewingstandCallback; typedef cItemCallback cChestCallback; typedef cItemCallback cDispenserCallback; typedef cItemCallback cFurnaceCallback; @@ -496,6 +498,9 @@ public: /** Calls the callback for each block entity in the specified chunk; returns true if all block entities processed, false if the callback aborted by returning true */ bool ForEachBlockEntityInChunk(int a_ChunkX, int a_ChunkZ, cBlockEntityCallback & a_Callback); // Exported in ManualBindings.cpp + /** Calls the callback for each brewingstand in the specified chunk; returns true if all brewingstands processed, false if the callback aborted by returning true */ + bool ForEachBrewingstandInChunk(int a_ChunkX, int a_ChunkZ, cBrewingstandCallback & a_Callback); // Exported in ManualBindings.cpp + /** Calls the callback for each chest in the specified chunk; returns true if all chests processed, false if the callback aborted by returning true */ bool ForEachChestInChunk(int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback); // Exported in ManualBindings.cpp @@ -532,6 +537,9 @@ public: /** Calls the callback for the beacon at the specified coords; returns false if there's no beacon at those coords, true if found */ bool DoWithBeaconAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBeaconCallback & a_Callback); // Exported in ManualBindings.cpp + /** Calls the callback for the brewingstand at the specified coords; returns false if there's no brewingstand at those coords, true if found */ + bool DoWithBrewingstandAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBrewingstandCallback & a_Callback); // Lua-acessible + /** Calls the callback for the chest at the specified coords; returns false if there's no chest at those coords, true if found */ bool DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback); // Exported in ManualBindings.cpp diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp index e2ba416c6..607a9c182 100644 --- a/src/WorldStorage/NBTChunkSerializer.cpp +++ b/src/WorldStorage/NBTChunkSerializer.cpp @@ -11,6 +11,7 @@ #include "FastNBT.h" #include "../BlockEntities/BeaconEntity.h" +#include "../BlockEntities/BrewingstandEntity.h" #include "../BlockEntities/ChestEntity.h" #include "../BlockEntities/CommandBlockEntity.h" #include "../BlockEntities/DispenserEntity.h" @@ -196,6 +197,21 @@ void cNBTChunkSerializer::AddBeaconEntity(cBeaconEntity * a_Entity) +void cNBTChunkSerializer::AddBrewingstandEntity(cBrewingstandEntity * a_Brewingstand) +{ + m_Writer.BeginCompound(""); + AddBasicTileEntity(a_Brewingstand, "Brewingstand"); + m_Writer.BeginList("Items", TAG_Compound); + AddItemGrid(a_Brewingstand->GetContents()); + m_Writer.EndList(); + m_Writer.AddShort("BrewTime", a_Brewingstand->GetTimeBrewed()); + m_Writer.EndCompound(); +} + + + + + void cNBTChunkSerializer::AddChestEntity(cChestEntity * a_Entity, BLOCKTYPE a_ChestType) { m_Writer.BeginCompound(""); @@ -938,6 +954,7 @@ void cNBTChunkSerializer::BlockEntity(cBlockEntity * a_Entity) switch (a_Entity->GetBlockType()) { case E_BLOCK_BEACON: AddBeaconEntity (reinterpret_cast (a_Entity)); break; + case E_BLOCK_BREWING_STAND: AddBrewingstandEntity(reinterpret_cast(a_Entity)); break; case E_BLOCK_CHEST: AddChestEntity (reinterpret_cast (a_Entity), a_Entity->GetBlockType()); break; case E_BLOCK_COMMAND_BLOCK: AddCommandBlockEntity(reinterpret_cast(a_Entity)); break; case E_BLOCK_DISPENSER: AddDispenserEntity (reinterpret_cast (a_Entity)); break; diff --git a/src/WorldStorage/NBTChunkSerializer.h b/src/WorldStorage/NBTChunkSerializer.h index 956738911..9cdfb1a76 100644 --- a/src/WorldStorage/NBTChunkSerializer.h +++ b/src/WorldStorage/NBTChunkSerializer.h @@ -21,6 +21,7 @@ class cEntity; class cBlockEntity; class cBoat; class cBeaconEntity; +class cBrewingstandEntity; class cChestEntity; class cCommandBlockEntity; class cDispenserEntity; @@ -97,6 +98,7 @@ protected: // Block entities: void AddBasicTileEntity (cBlockEntity * a_Entity, const char * a_EntityTypeID); void AddBeaconEntity (cBeaconEntity * a_Entity); + void AddBrewingstandEntity(cBrewingstandEntity * a_Brewingstand); void AddChestEntity (cChestEntity * a_Entity, BLOCKTYPE a_ChestType); void AddDispenserEntity (cDispenserEntity * a_Entity); void AddDropperEntity (cDropperEntity * a_Entity); diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index f0c990037..38ca1cdd9 100755 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -18,6 +18,7 @@ #include "../Root.h" #include "../BlockEntities/BeaconEntity.h" +#include "../BlockEntities/BrewingstandEntity.h" #include "../BlockEntities/ChestEntity.h" #include "../BlockEntities/CommandBlockEntity.h" #include "../BlockEntities/DispenserEntity.h" @@ -689,6 +690,7 @@ cBlockEntity * cWSSAnvil::LoadBlockEntityFromNBT(const cParsedNBT & a_NBT, int a { // Specific entity loaders: case E_BLOCK_BEACON: return LoadBeaconFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ); + case E_BLOCK_BREWING_STAND: return LoadBrewingstandFromNBT(a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_BREWING_STAND, a_BlockMeta); case E_BLOCK_CHEST: return LoadChestFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_CHEST); case E_BLOCK_COMMAND_BLOCK: return LoadCommandBlockFromNBT(a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ); case E_BLOCK_DISPENSER: return LoadDispenserFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ); @@ -926,6 +928,55 @@ cBlockEntity * cWSSAnvil::LoadBeaconFromNBT(const cParsedNBT & a_NBT, int a_TagI +cBlockEntity * cWSSAnvil::LoadBrewingstandFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + // Check if the data has a proper type: + if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Brewingstand")) + { + return nullptr; + } + + int Items = a_NBT.FindChildByName(a_TagIdx, "Items"); + if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List)) + { + return nullptr; // Make it an empty brewingstand - the chunk loader will provide an empty cBrewingstandEntity for this + } + + std::unique_ptr Brewingstand(new cBrewingstandEntity(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, m_World)); + + // Load slots: + for (int Child = a_NBT.GetFirstChild(Items); Child != -1; Child = a_NBT.GetNextSibling(Child)) + { + int Slot = a_NBT.FindChildByName(Child, "Slot"); + if ((Slot < 0) || (a_NBT.GetType(Slot) != TAG_Byte)) + { + continue; + } + cItem Item; + if (LoadItemFromNBT(Item, a_NBT, Child)) + { + Brewingstand->SetSlot(a_NBT.GetByte(Slot), Item); + } + } // for itr - ItemDefs[] + + // Load brewing time: + int BrewTime = a_NBT.FindChildByName(a_TagIdx, "BrewTime"); + if (BrewTime >= 0) + { + Int16 tb = a_NBT.GetShort(BrewTime); + Brewingstand->setTimeBrewed(tb); + } + + // Restart brewing: + Brewingstand->GetRecipes(); + Brewingstand->ContinueBrewing(); + return Brewingstand.release(); +} + + + + + cBlockEntity * cWSSAnvil::LoadChestFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_ChestBlockType) { // Check if the data has a proper type: diff --git a/src/WorldStorage/WSSAnvil.h b/src/WorldStorage/WSSAnvil.h index 6c15877ff..03fa22457 100755 --- a/src/WorldStorage/WSSAnvil.h +++ b/src/WorldStorage/WSSAnvil.h @@ -149,6 +149,7 @@ protected: bool CheckBlockEntityType(const cParsedNBT & a_NBT, int a_TagIdx, const char * a_ExpectedType); cBlockEntity * LoadBeaconFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ); + cBlockEntity * LoadBrewingstandFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); cBlockEntity * LoadChestFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_ChestBlockType); cBlockEntity * LoadCommandBlockFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ); cBlockEntity * LoadDispenserFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ); -- cgit v1.2.3