summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--Server/Plugins/APIDump/APIDesc.lua3
-rw-r--r--Server/Plugins/APIDump/Classes/BlockEntities.lua41
-rw-r--r--Server/Plugins/APIDump/Classes/Plugins.lua2
-rw-r--r--Server/Plugins/APIDump/Hooks/OnBrewingCompleted.lua26
-rw-r--r--Server/Plugins/APIDump/Hooks/OnBrewingCompleting.lua28
-rw-r--r--Server/brewing.txt235
-rw-r--r--src/Bindings/AllToLua.pkg1
-rw-r--r--src/Bindings/ManualBindings.cpp50
-rw-r--r--src/Bindings/ManualBindings_World.cpp2
-rw-r--r--src/Bindings/Plugin.h2
-rw-r--r--src/Bindings/PluginLua.cpp48
-rw-r--r--src/Bindings/PluginLua.h2
-rw-r--r--src/Bindings/PluginManager.cpp38
-rw-r--r--src/Bindings/PluginManager.h5
-rw-r--r--src/BlockEntities/BlockEntity.cpp2
-rw-r--r--src/BlockEntities/BrewingstandEntity.cpp309
-rw-r--r--src/BlockEntities/BrewingstandEntity.h136
-rw-r--r--src/BlockEntities/CMakeLists.txt2
-rw-r--r--src/Blocks/BlockBrewingStand.h8
-rw-r--r--src/BrewingRecipes.cpp287
-rw-r--r--src/BrewingRecipes.h55
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/Chunk.cpp53
-rw-r--r--src/Chunk.h8
-rw-r--r--src/ChunkMap.cpp33
-rw-r--r--src/ChunkMap.h9
-rw-r--r--src/Root.cpp8
-rw-r--r--src/Root.h3
-rw-r--r--src/UI/BrewingstandWindow.cpp70
-rw-r--r--src/UI/BrewingstandWindow.h31
-rw-r--r--src/UI/CMakeLists.txt2
-rw-r--r--src/UI/SlotArea.cpp238
-rw-r--r--src/UI/SlotArea.h30
-rw-r--r--src/UI/Window.h1
-rw-r--r--src/World.cpp18
-rw-r--r--src/World.h8
-rw-r--r--src/WorldStorage/NBTChunkSerializer.cpp17
-rw-r--r--src/WorldStorage/NBTChunkSerializer.h2
-rwxr-xr-xsrc/WorldStorage/WSSAnvil.cpp51
-rwxr-xr-xsrc/WorldStorage/WSSAnvil.h1
40 files changed, 1858 insertions, 9 deletions
diff --git a/Server/Plugins/APIDump/APIDesc.lua b/Server/Plugins/APIDump/APIDesc.lua
index 33d351104..02ce1f894 100644
--- a/Server/Plugins/APIDump/APIDesc.lua
+++ b/Server/Plugins/APIDump/APIDesc.lua
@@ -2067,6 +2067,7 @@ a_Player:OpenWindow(Window);
ForEachPlayer = { Params = "CallbackFunction", Return = "", Notes = "Calls the given callback function for each player. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|cPlayer}})</pre>" },
ForEachWorld = { Params = "CallbackFunction", Return = "", Notes = "Calls the given callback function for each world. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cWorld|cWorld}})</pre>" },
Get = { Params = "", Return = "Root object", Notes = "(STATIC) This function returns the cRoot object." },
+ GetBrewingRecipe = { Params = "{{cItem|cItem}}, {{cItem|cItem}}", Return = "{{cItem|cItem}}", Notes = "(STATIC) Returns the result item, if a recipe has been found. If no recipe is found, returns no value." },
GetBuildCommitID = { Params = "", Return = "string", Notes = "(STATIC) For official builds (Travis CI / Jenkins) it returns the exact commit hash used for the build. For unofficial local builds, returns the approximate commit hash (since the true one cannot be determined), formatted as \"approx: &lt;CommitHash&gt;\"." },
GetBuildDateTime = { Params = "", Return = "string", Notes = "(STATIC) For official builds (Travic CI / Jenkins) it returns the date and time of the build. For unofficial local builds, returns the approximate datetime of the commit (since the true one cannot be determined), formatted as \"approx: &lt;DateTime-iso8601&gt;\"." },
GetBuildID = { Params = "", Return = "string", Notes = "(STATIC) For official builds (Travis CI / Jenkins) it returns the unique ID of the build, as recognized by the build system. For unofficial local builds, returns the string \"Unknown\"." },
@@ -2333,6 +2334,7 @@ local CompressedString = cStringCompression.CompressStringGZIP("DataToCompress")
DigBlock = { Params = "X, Y, Z", Return = "", Notes = "Replaces the specified block with air, without dropping the usual pickups for the block. Wakes up the simulators for the block and its neighbors." },
DoExplosionAt = { Params = "Force, X, Y, Z, CanCauseFire, Source, SourceData", Return = "", Notes = "Creates an explosion of the specified relative force in the specified position. If CanCauseFire is set, the explosion will set blocks on fire, too. The Source parameter specifies the source of the explosion, one of the esXXX constants. The SourceData parameter is specific to each source type, usually it provides more info about the source." },
DoWithBlockEntityAt = { Params = "BlockX, BlockY, BlockZ, CallbackFunction", Return = "bool", Notes = "If there is a block entity at the specified coords, calls the CallbackFunction with the {{cBlockEntity}} parameter representing the block entity. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cBlockEntity|BlockEntity}})</pre> The function returns false if there is no block entity, or if there is, it returns the bool value that the callback has returned. Use {{tolua}}.cast() to cast the Callback's BlockEntity parameter to the correct {{cBlockEntity}} descendant." },
+ DoWithBrewingstandAt = { Params = "BlockX, BlockY, BlockZ, CallbackFunction", Return = "bool", Notes = "If there is a brewingstand at the specified coords, calls the CallbackFunction with the {{cBrewingstandEntity}} parameter representing the brewingstand. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cBrewingstandEntity|cBrewingstandEntity}})</pre> The function returns false if there is no brewingstand, or if there is, it returns the bool value that the callback has returned." },
DoWithBeaconAt = { Params = "BlockX, BlockY, BlockZ, CallbackFunction", Return = "bool", Notes = "If there is a beacon at the specified coords, calls the CallbackFunction with the {{cBeaconEntity}} parameter representing the beacon. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cBeaconEntity|BeaconEntity}})</pre> The function returns false if there is no beacon, or if there is, it returns the bool value that the callback has returned." },
DoWithChestAt = { Params = "BlockX, BlockY, BlockZ, CallbackFunction", Return = "bool", Notes = "If there is a chest at the specified coords, calls the CallbackFunction with the {{cChestEntity}} parameter representing the chest. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cChestEntity|ChestEntity}})</pre> The function returns false if there is no chest, or if there is, it returns the bool value that the callback has returned." },
DoWithCommandBlockAt = { Params = "BlockX, BlockY, BlockZ, CallbackFunction", Return = "bool", Notes = "If there is a command block at the specified coords, calls the CallbackFunction with the {{cCommandBlockEntity}} parameter representing the command block. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cCommandBlockEntity|CommandBlockEntity}})</pre> The function returns false if there is no command block, or if there is, it returns the bool value that the callback has returned." },
@@ -2353,6 +2355,7 @@ local CompressedString = cStringCompression.CompressStringGZIP("DataToCompress")
},
FindAndDoWithPlayer = { Params = "PlayerName, CallbackFunction", Return = "bool", Notes = "Calls the given callback function for the player with the name best matching the name string provided.<br>This function is case-insensitive and will match partial names.<br>Returns false if player not found or there is ambiguity, true otherwise. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}})</pre>" },
ForEachBlockEntityInChunk = { Params = "ChunkX, ChunkZ, CallbackFunction", Return = "bool", Notes = "Calls the specified callback for each block entity in the chunk. Returns true if all block entities in the chunk have been processed (including when there are zero block entities), or false if the callback has aborted the enumeration by returning true. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cBlockEntity|BlockEntity}})</pre> The callback should return false or no value to continue with the next block entity, or true to abort the enumeration. Use {{tolua}}.cast() to cast the Callback's BlockEntity parameter to the correct {{cBlockEntity}} descendant." },
+ ForEachBrewingstandInChunk = { Params = "ChunkX, ChunkZ, CallbackFunction", Return = "bool", Notes = "Calls the specified callback for each brewingstand in the chunk. Returns true if all brewingstands in the chunk have been processed (including when there are zero brewingstands), or false if the callback has aborted the enumeration by returning true. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cBrewingstandEntity|cBrewingstandEntity}})</pre> The callback should return false or no value to continue with the next brewingstand, or true to abort the enumeration." },
ForEachChestInChunk = { Params = "ChunkX, ChunkZ, CallbackFunction", Return = "bool", Notes = "Calls the specified callback for each chest in the chunk. Returns true if all chests in the chunk have been processed (including when there are zero chests), or false if the callback has aborted the enumeration by returning true. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cChestEntity|ChestEntity}})</pre> The callback should return false or no value to continue with the next chest, or true to abort the enumeration." },
ForEachEntity = { Params = "CallbackFunction", Return = "bool", Notes = "Calls the specified callback for each entity in the loaded world. Returns true if all the entities have been processed (including when there are zero entities), or false if the callback function has aborted the enumeration by returning true. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cEntity|Entity}})</pre> The callback should return false or no value to continue with the next entity, or true to abort the enumeration." },
ForEachEntityInBox = { Params = "{{cBoundingBox|Box}}, CallbackFunction", Return = "bool", Notes = "Calls the specified callback for each entity in the specified bounding box. Returns true if all the entities have been processed (including when there are zero entities), or false if the callback function has aborted the enumeration by returning true. If any chunk within the bounding box is not valid, it is silently skipped without any notification. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cEntity|Entity}})</pre> The callback should return false or no value to continue with the next entity, or true to abort the enumeration." },
diff --git a/Server/Plugins/APIDump/Classes/BlockEntities.lua b/Server/Plugins/APIDump/Classes/BlockEntities.lua
index daa658654..019e702eb 100644
--- a/Server/Plugins/APIDump/Classes/BlockEntities.lua
+++ b/Server/Plugins/APIDump/Classes/BlockEntities.lua
@@ -76,6 +76,47 @@ return
},
},
+ cBrewingstandEntity =
+ {
+ Desc = [[
+ This class represents a brewingstand entity in the world.</p>
+ <p>
+ See also the {{cRoot}}:GetBrewingRecipe() function.
+ ]],
+ Functions =
+ {
+ GetBrewingTimeLeft = { Params = "", Return = "number", Notes = "Returns the time until the current items finishes brewing, in ticks" },
+ GetTimeBrewed = { Params = "", Return = "number", Notes = "Returns the time that the current items has been brewing, in ticks" },
+ GetLeftBottleSlot = { Params = "", Return = "{{cItem|cItem}}", Notes = "Returns the item in the left bottle slot" },
+ GetMiddleBottleSlot = { Params = "", Return = "{{cItem|cItem}}", Notes = "Returns the item in the middle bottle slot" },
+ GetRightBottleSlot = { Params = "", Return = "{{cItem|cItem}}", Notes = "Returns the item in the right bottle slot" },
+ GetIndgredientSlot = { Params = "", Return = "{{cItem|cItem}}", Notes = "Returns the item in the ingredient slot" },
+ GetResultItem = { Params = "number", Return = "{{cItem|cItem}}", Notes = "Returns the expected result item for the given slot number." },
+ SetLeftBottleSlot = { Params = "{{cItem|cItem}}", Return = "", Notes = "Sets the item in the left bottle slot" },
+ SetMiddleBottleSlot = { Params = "{{cItem|cItem}}", Return = "", Notes = "Sets the item in the middle bottle slot" },
+ SetRightBottleSlot = { Params = "{{cItem|cItem}}", Return = "", Notes = "Sets the item in the right bottle slot" },
+ SetIngredientSlot = { Params = "{{cItem|cItem}}", Return = "", Notes = "Sets the item in the ingredient bottle slot" },
+ },
+ Constants =
+ {
+ bsLeftBottle = { Notes = "Index of the left bottle slot" },
+ bsMiddleBottle = { Notes = "Index of the middle bottle slot" },
+ bsRightBottle = { Notes = "Index of the right bottle slot" },
+ bsIngredient = { Notes = "Index of the ingredient slot" },
+ ContentsWidth = { Notes = "Width (X) of the {{cItemGrid|cItemGrid}} representing the contents" },
+ ContentsHeight = { Notes = "Height (Y) of the {{cItemGrid|cItemGrid}} representing the contents" },
+ },
+ ConstantGroups =
+ {
+ SlotIndices =
+ {
+ Include = "bs.*",
+ TextBefore = "When using the GetSlot() or SetSlot() function, use these constants for slot index:",
+ },
+ },
+ Inherits = "cBlockEntityWithItems"
+ }, -- cBrewingstandEntity
+
cChestEntity =
{
Desc = [[
diff --git a/Server/Plugins/APIDump/Classes/Plugins.lua b/Server/Plugins/APIDump/Classes/Plugins.lua
index f892ad8f6..758598242 100644
--- a/Server/Plugins/APIDump/Classes/Plugins.lua
+++ b/Server/Plugins/APIDump/Classes/Plugins.lua
@@ -110,6 +110,8 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage);
crUnknownCommand = { Notes = "When the given command doesn't exist." },
HOOK_BLOCK_SPREAD = { Notes = "Called when a block spreads based on world conditions" },
HOOK_BLOCK_TO_PICKUPS = { Notes = "Called when a block has been dug and is being converted to pickups. The server has provided the default pickups and the plugins may modify them." },
+ HOOK_BREWING_COMPLETING = { "Called before a brewing stand completes a brewing process." },
+ HOOK_BREWING_COMPLETED = { "Called when a brewing stand completed a brewing process." },
HOOK_CHAT = { Notes = "Called when a client sends a chat message that is not a command. The plugin may modify the chat message" },
HOOK_CHUNK_AVAILABLE = { Notes = "Called when a chunk is loaded or generated and becomes available in the {{cWorld|world}}." },
HOOK_CHUNK_GENERATED = { Notes = "Called after a chunk is generated. A plugin may do last modifications on the generated chunk before it is handed of to the {{cWorld|world}}." },
diff --git a/Server/Plugins/APIDump/Hooks/OnBrewingCompleted.lua b/Server/Plugins/APIDump/Hooks/OnBrewingCompleted.lua
new file mode 100644
index 000000000..316227739
--- /dev/null
+++ b/Server/Plugins/APIDump/Hooks/OnBrewingCompleted.lua
@@ -0,0 +1,26 @@
+return
+{
+ HOOK_BREWING_COMPLETED =
+ {
+ CalledWhen = "A brewing process is completed.",
+ DefaultFnName = "OnBrewingCompleted", -- also used as pagename
+ Desc = [[
+ This hook is called whenever a {{cBrewingstand|brewing stand}} has completed the brewing process.
+ See also the {{OnBrewingCompleting|HOOK_BREWING_COMPLETING}} hook for a similar hook, is called when a
+ brewing process is completing.
+ ]],
+ Params =
+ {
+ { Name = "World", Type = "{{cWorld}}", Notes = "World where the brewing stand resides." },
+ { Name = "Brewingstand", Type = "{{cBrewingstand}}", Notes = "The brewing stand that completed the brewing process." },
+ },
+ Returns = [[
+ If the function returns false or no value, Cuberite calls other plugins with this event. If the
+ function returns true, no other plugin is called for this event.</p>
+ ]],
+ }, -- HOOK_BREWING_COMPLETED
+}
+
+
+
+
diff --git a/Server/Plugins/APIDump/Hooks/OnBrewingCompleting.lua b/Server/Plugins/APIDump/Hooks/OnBrewingCompleting.lua
new file mode 100644
index 000000000..14429c8ff
--- /dev/null
+++ b/Server/Plugins/APIDump/Hooks/OnBrewingCompleting.lua
@@ -0,0 +1,28 @@
+return
+{
+ HOOK_BREWING_COMPLETING =
+ {
+ CalledWhen = "A brewing process is completing.",
+ DefaultFnName = "OnBrewingCompleting", -- also used as pagename
+ Desc = [[
+ This hook is called whenever a {{cBrewingstand|brewing stand}} is completing the brewing process. Plugins may
+ refuse the completing of the brewing process.<p>
+ See also the {{OnBrewingCompleted|HOOK_BREWING_COMPLETED}} hook for a similar hook, is called after the
+ brewing process has been completed.
+ ]],
+ Params =
+ {
+ { Name = "World", Type = "{{cWorld}}", Notes = "World where the brewing stand resides." },
+ { Name = "Brewingstand", Type = "{{cBrewingstand}}", Notes = "The brewing stand that completes the brewing process." },
+ },
+ Returns = [[
+ If the function returns false or no value, Cuberite calls other plugins with this event. If the function returns true,
+ no other plugin's callback is called and the brewing process is canceled.
+ <p>
+ ]],
+ }, -- HOOK_BREWING_COMPLETING
+}
+
+
+
+
diff --git a/Server/brewing.txt b/Server/brewing.txt
new file mode 100644
index 000000000..af690d81e
--- /dev/null
+++ b/Server/brewing.txt
@@ -0,0 +1,235 @@
+#*****************#
+# Brewing Recipes #
+#*****************#
+# The time for a brewing recipe is always 20 seconds (400 ticks).
+#
+# Minecraft-Wiki Brewing:
+# http://minecraft.gamepedia.com/Brewing
+#
+# A brewing recipe has this format:
+# Data Value + Ingredient = Potion
+
+
+### Primary ###
+# Akward Potion
+0 + netherwart = 16
+
+# Mundane Potion
+0 + redstonedust = 64
+0 + ghasttear = 8192
+0 + glisteringmelon = 8192
+0 + blazepowder = 8192
+0 + magmacream = 8192
+0 + sugar = 8192
+0 + spidereye = 8192
+0 + rabbitsfoot = 8192
+
+# Thick Potion
+0 + glowstonedust = 32
+
+# Potion of Weakness
+0 + fermentedspidereye = 8264
+
+
+
+
+### Secondary ###
+
+## Positive ##
+
+# Potion of Healing
+16 + glisteringmelon = 8197
+
+# Potion of Fire Resistance
+16 + magmacream = 8195
+
+# Potion of Regeneration
+16 + ghasttear = 8193
+
+# Potion of Strength
+16 + blazepowder = 8201
+
+# Potion of Swiftness
+16 + sugar = 8194
+
+# Potion of Night Vision
+16 + goldencarrot = 8198
+
+# Potion of Water Breathing
+16 + pufferfish = 8205
+
+# Potion of Leaping
+16 + rabbitsfoot = 8203
+
+
+## Negative ##
+
+# Potion of Poison
+16 + spidereye = 8196
+
+# Potion of Weakness
+16 + fermentedspidereye = 8200
+32 + fermentedspidereye = 8200
+64 + fermentedspidereye = 8200
+
+# Potion of Weakness (extended)
+8192 + fermentedspidereye = 8264
+
+
+
+
+### Tertiary ###
+
+## Positive ##
+
+# Potion of Fire Resistance (extended)
+8195 + redstonedust = 8259
+8227 + redstonedust = 8259
+
+# Potion of Healing II
+8197 + glowstonedust = 8229
+8261 + glowstonedust = 8229
+
+# Potion of Regeneration (extended)
+8193 + redstonedust = 8257
+8225 + redstonedust = 8257
+
+# Potion of Regeneration II
+8193 + glowstonedust = 8225
+8257 + glowstonedust = 8225
+
+# Potion of Strength (extended)
+8201 + redstonedust = 8265
+8233 + redstonedust = 8265
+
+# Potion of Strength II
+8201 + glowstonedust = 8233
+8265 + glowstonedust = 8233
+
+# Potion of Swiftness (extended)
+8194 + redstonedust = 8258
+8226 + redstonedust = 8258
+
+# Potion of Swiftness II
+8194 + glowstonedust = 8226
+8258 + glowstonedust = 8226
+
+# Potion of Night Vision (extended)
+8198 + redstonedust = 8262
+
+# Potion of Invisibility
+8198 + fermentedspidereye = 8206
+
+# Potion of Invisibility (extended)
+8262 + fermentedspidereye = 8270
+
+# Potion of Invisibility (extended)
+8206 + redstonedust = 8270
+
+# Potion of Water Breathing (extended)
+8205 + redstonedust = 8269
+8237 + redstonedust = 8269
+
+# Potion of Leaping II
+8203 + glowstonedust = 8235
+8267 + glowstonedust = 8235
+
+# Potion of Leaping (extended)
+8203 + redstonedust = 8267
+8235 + redstonedust = 8267
+
+
+## Negative ##
+
+# Potion of Harming
+8197 + fermentedspidereye = 8204
+8196 + fermentedspidereye = 8204
+8260 + fermentedspidereye = 8204
+
+# Potion of Harming II
+8229 + fermentedspidereye = 8236
+8228 + fermentedspidereye = 8236
+
+# Potion of Harming II
+8204 + glowstonedust = 8236
+8268 + glowstonedust = 8236
+
+# Potion of Poison (extended)
+8196 + redstonedust = 8260
+8228 + redstonedust = 8260
+
+# Potion of Poison II
+8196 + glowstonedust = 8228
+8260 + glowstonedust = 8228
+
+# Potion of Slowness
+8195 + fermentedspidereye = 8202
+8194 + fermentedspidereye = 8202
+8203 + fermentedspidereye = 8202
+
+# Potion of Slowness (extended)
+8202 + redstonedust = 8266
+8234 + redstonedust = 8266
+
+# Potion of Slowness (extended)
+8259 + fermentedspidereye = 8266
+8258 + fermentedspidereye = 8266
+8267 + fermentedspidereye = 8266
+
+# Potion of Weakness
+8201 + fermentedspidereye = 8200
+8193 + fermentedspidereye = 8200
+
+# Potion of Weakness (extended)
+8265 + fermentedspidereye = 8264
+8257 + fermentedspidereye = 8264
+
+# Potion of Weakness (extended)
+8200 + redstonedust = 8264
+8232 + redstonedust = 8264
+
+
+
+
+### Reverted ###
+
+## Glowstone ##
+
+# Potion of Fire Resistance (reverted)
+8259 + glowstonedust = 8227
+
+# Potion of Night Vision (reverted)
+8262 + glowstonedust = 8230
+
+# Potion of Weakness (reverted)
+8264 + glowstonedust = 8232
+
+# Potion of Slowness (reverted)
+8266 + glowstonedust = 8234
+
+# Potion of Water Breathing (reverted)
+8269 + glowstonedust = 8237
+
+
+## Redstone ##
+
+# Potion of Healing (reverted)
+8229 + redstonedust = 8261
+
+# Potion of Harming (reverted)
+8236 + redstonedust = 8268
+
+
+## Fermented Spider Eye ##
+
+# Potion of Harming (reverted)
+8261 + fermentedspidereye = 8268
+8260 + fermentedspidereye = 8268
+
+# Potion of Slowness (reverted)
+8227 + fermentedspidereye = 8234
+8226 + fermentedspidereye = 8234
+
+# Potion of Weakness (reverted)
+8233 + fermentedspidereye = 8232
+8225 + fermentedspidereye = 8232
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 <cRoot, cPlayer, &cRoot::DoWithPlayerByUUID>);
tolua_function(tolua_S, "ForEachPlayer", ForEach<cRoot, cPlayer, &cRoot::ForEachPlayer>);
tolua_function(tolua_S, "ForEachWorld", ForEach<cRoot, cWorld, &cRoot::ForEachWorld>);
+ 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<cWorld, cBlockEntity, &cWorld::DoWithBlockEntityAt>);
tolua_function(tolua_S, "DoWithBeaconAt", DoWithXYZ<cWorld, cBeaconEntity, &cWorld::DoWithBeaconAt>);
+ tolua_function(tolua_S, "DoWithBrewingstandAt", DoWithXYZ<cWorld, cBrewingstandEntity, &cWorld::DoWithBrewingstandAt>);
tolua_function(tolua_S, "DoWithChestAt", DoWithXYZ<cWorld, cChestEntity, &cWorld::DoWithChestAt>);
tolua_function(tolua_S, "DoWithDispenserAt", DoWithXYZ<cWorld, cDispenserEntity, &cWorld::DoWithDispenserAt>);
tolua_function(tolua_S, "DoWithDropSpenserAt", DoWithXYZ<cWorld, cDropSpenserEntity, &cWorld::DoWithDropSpenserAt>);
@@ -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<cWorld, cBlockEntity, &cWorld::ForEachBlockEntityInChunk>);
+ tolua_function(tolua_S, "ForEachBrewingstandInChunk", ForEachInChunk<cWorld, cBrewingstandEntity, &cWorld::ForEachBrewingstandInChunk>);
tolua_function(tolua_S, "ForEachChestInChunk", ForEachInChunk<cWorld, cChestEntity, &cWorld::ForEachChestInChunk>);
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<int>(**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<int>(**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<cBlockEntityHandler, 0x07, 0x02, 0x05, 0x03, 0x04>
{
public:
cBlockBrewingStandHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
+ : cMetaRotator<cBlockEntityHandler, 0x07, 0x02, 0x05, 0x03, 0x04>(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 <fstream>
+
+#define BREWING_RECIPE_FILE "brewing.txt"
+
+
+
+
+
+typedef std::vector<std::unique_ptr<cBrewingRecipes::cRecipe>> 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<long>(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<cItem> InputItem = cpp14::make_unique<cItem>();
+ std::unique_ptr<cItem> IngredientItem = cpp14::make_unique<cItem>();
+ std::unique_ptr<cItem> OutputItem = cpp14::make_unique<cItem>();
+
+ 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<short>(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<short>(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<cRecipe> Recipe = cpp14::make_unique<cRecipe>();
+ 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<cItem> InputItem = cpp14::make_unique<cItem>();
+ std::unique_ptr<cItem> IngredientItem = cpp14::make_unique<cItem>();
+ std::unique_ptr<cItem> OutputItem = cpp14::make_unique<cItem>();
+
+ 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<cRecipe> Recipe = cpp14::make_unique<cRecipe>();
+ 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<cRecipe> * 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<cItem> InputItem = cpp14::make_unique<cItem>();
+ std::unique_ptr<cItem> IngredientItem = cpp14::make_unique<cItem>();
+ std::unique_ptr<cItem> OutputItem = cpp14::make_unique<cItem>();
+
+ 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<cRecipe> Recipe = cpp14::make_unique<cRecipe>();
+ 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<cItem> Input;
+ std::unique_ptr<cItem> Output;
+ std::unique_ptr<cItem> 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<sBrewingRecipeState> 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<cBrewingstandEntity *>(*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<cBrewingstandEntity *>(*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<cClientHandle *> cClientHandleList;
typedef cItemCallback<cEntity> cEntityCallback;
typedef cItemCallback<cBeaconEntity> cBeaconCallback;
+typedef cItemCallback<cBrewingstandEntity> cBrewingstandCallback;
typedef cItemCallback<cChestEntity> cChestCallback;
typedef cItemCallback<cDispenserEntity> cDispenserCallback;
typedef cItemCallback<cFurnaceEntity> 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<cEntity> cEntityCallback;
typedef cItemCallback<cBlockEntity> cBlockEntityCallback;
typedef cItemCallback<cBeaconEntity> cBeaconCallback;
+typedef cItemCallback<cBrewingstandEntity> cBrewingstandCallback;
typedef cItemCallback<cChestEntity> cChestCallback;
typedef cItemCallback<cDispenserEntity> cDispenserCallback;
typedef cItemCallback<cDropperEntity> 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<cSettingsRepositoryInterface> 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<cBrewingRecipes> 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"
@@ -1935,6 +1936,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<char>(static_cast<float>(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:
cSlotAreaMinecartWithChest::cSlotAreaMinecartWithChest(cMinecartWithChest * a_Chest, cWindow & a_ParentWindow) :
@@ -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<cSetChunkDataPtr> cSetChunkDataPtrs;
typedef cItemCallback<cPlayer> cPlayerListCallback;
typedef cItemCallback<cEntity> cEntityCallback;
typedef cItemCallback<cBeaconEntity> cBeaconCallback;
+typedef cItemCallback<cBrewingstandEntity> cBrewingstandCallback;
typedef cItemCallback<cChestEntity> cChestCallback;
typedef cItemCallback<cDispenserEntity> cDispenserCallback;
typedef cItemCallback<cFurnaceEntity> 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<cBeaconEntity *> (a_Entity)); break;
+ case E_BLOCK_BREWING_STAND: AddBrewingstandEntity(reinterpret_cast<cBrewingstandEntity *>(a_Entity)); break;
case E_BLOCK_CHEST: AddChestEntity (reinterpret_cast<cChestEntity *> (a_Entity), a_Entity->GetBlockType()); break;
case E_BLOCK_COMMAND_BLOCK: AddCommandBlockEntity(reinterpret_cast<cCommandBlockEntity *>(a_Entity)); break;
case E_BLOCK_DISPENSER: AddDispenserEntity (reinterpret_cast<cDispenserEntity *> (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<cBrewingstandEntity> 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);