1
0

Implemented brewing

This commit is contained in:
Lukas Pioch 2015-09-24 10:48:33 +02:00
parent 0946de1f3c
commit 9749c3aac9
40 changed files with 1858 additions and 9 deletions

View File

@ -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." },

View File

@ -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 = [[

View File

@ -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}}." },

View File

@ -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
}

View File

@ -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
}

235
Server/brewing.txt Normal file
View File

@ -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

View File

@ -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"

View File

@ -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);

View File

@ -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>);

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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:

View File

@ -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);

View File

@ -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);

View File

@ -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();
}
}
}

View File

@ -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

View File

@ -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

View File

@ -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)
{
}

287
src/BrewingRecipes.cpp Normal file
View File

@ -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);
}

55
src/BrewingRecipes.h Normal file
View File

@ -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;
};

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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);
}

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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;
};

View File

@ -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

View File

@ -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<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:
@ -2342,3 +2579,4 @@ cItem * cSlotAreaTemporary::GetPlayerSlots(cPlayer & a_Player)

View File

@ -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
{

View File

@ -18,6 +18,7 @@
class cPlayer;
class cWindowOwner;
class cClientHandle;
class cBrewingstandEntity;
class cChestEntity;
class cEnderChestEntity;
class cFurnaceEntity;

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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:

View File

@ -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);