summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Bindings/AllToLua.pkg1
-rw-r--r--src/Bindings/CMakeLists.txt1
-rw-r--r--src/Bindings/ManualBindings.cpp2
-rw-r--r--src/BlockEntities/BeaconEntity.cpp295
-rw-r--r--src/BlockEntities/BeaconEntity.h79
-rw-r--r--src/BlockEntities/FlowerPotEntity.cpp2
-rw-r--r--src/Blocks/BlockHandler.cpp1
-rw-r--r--src/Blocks/BlockIce.h14
-rw-r--r--src/Blocks/BlockPiston.h1
-rw-r--r--src/Chunk.cpp33
-rw-r--r--src/Chunk.h5
-rw-r--r--src/ChunkMap.cpp19
-rw-r--r--src/ChunkMap.h5
-rw-r--r--src/ClientHandle.cpp58
-rw-r--r--src/ClientHandle.h5
-rw-r--r--src/Entities/EntityEffect.cpp2
-rw-r--r--src/Entities/Player.cpp53
-rw-r--r--src/Entities/Player.h5
-rw-r--r--src/Items/ItemFood.h54
-rw-r--r--src/Items/ItemGoldenApple.h58
-rw-r--r--src/Items/ItemHandler.cpp34
-rw-r--r--src/Items/ItemHandler.h16
-rw-r--r--src/Mobs/Enderman.cpp119
-rw-r--r--src/Mobs/Enderman.h2
-rw-r--r--src/Protocol/Protocol17x.cpp15
-rw-r--r--src/Simulator/FireSimulator.cpp20
-rw-r--r--src/UI/SlotArea.cpp195
-rw-r--r--src/UI/SlotArea.h29
-rw-r--r--src/UI/Window.cpp31
-rw-r--r--src/UI/Window.h21
-rw-r--r--src/World.cpp20
-rw-r--r--src/World.h5
-rw-r--r--src/WorldStorage/NBTChunkSerializer.cpp19
-rw-r--r--src/WorldStorage/NBTChunkSerializer.h2
-rw-r--r--src/WorldStorage/WSSAnvil.cpp50
-rw-r--r--src/WorldStorage/WSSAnvil.h1
-rw-r--r--src/WorldStorage/WSSCompact.cpp20
37 files changed, 1194 insertions, 98 deletions
diff --git a/src/Bindings/AllToLua.pkg b/src/Bindings/AllToLua.pkg
index d3e3f5b45..88faa9dfc 100644
--- a/src/Bindings/AllToLua.pkg
+++ b/src/Bindings/AllToLua.pkg
@@ -47,6 +47,7 @@ $cfile "../Inventory.h"
$cfile "../Enchantments.h"
$cfile "../Item.h"
$cfile "../ItemGrid.h"
+$cfile "../BlockEntities/BeaconEntity.h"
$cfile "../BlockEntities/BlockEntity.h"
$cfile "../BlockEntities/BlockEntityWithItems.h"
$cfile "../BlockEntities/ChestEntity.h"
diff --git a/src/Bindings/CMakeLists.txt b/src/Bindings/CMakeLists.txt
index 48e7ce79c..a2b381a26 100644
--- a/src/Bindings/CMakeLists.txt
+++ b/src/Bindings/CMakeLists.txt
@@ -53,6 +53,7 @@ set(BINDING_DEPENDENCIES
../Bindings/WebPlugin.h
../BiomeDef.h
../BlockArea.h
+ ../BlockEntities/BeaconEntity.h
../BlockEntities/BlockEntity.h
../BlockEntities/BlockEntityWithItems.h
../BlockEntities/ChestEntity.h
diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp
index 038173c99..34f0d7e30 100644
--- a/src/Bindings/ManualBindings.cpp
+++ b/src/Bindings/ManualBindings.cpp
@@ -16,6 +16,7 @@
#include "../WebAdmin.h"
#include "../ClientHandle.h"
#include "../BlockArea.h"
+#include "../BlockEntities/BeaconEntity.h"
#include "../BlockEntities/ChestEntity.h"
#include "../BlockEntities/CommandBlockEntity.h"
#include "../BlockEntities/DispenserEntity.h"
@@ -3063,6 +3064,7 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_beginmodule(tolua_S, "cWorld");
tolua_function(tolua_S, "ChunkStay", tolua_cWorld_ChunkStay);
tolua_function(tolua_S, "DoWithBlockEntityAt", tolua_DoWithXYZ<cWorld, cBlockEntity, &cWorld::DoWithBlockEntityAt>);
+ tolua_function(tolua_S, "DoWithBeaconAt", tolua_DoWithXYZ<cWorld, cBeaconEntity, &cWorld::DoWithBeaconAt>);
tolua_function(tolua_S, "DoWithChestAt", tolua_DoWithXYZ<cWorld, cChestEntity, &cWorld::DoWithChestAt>);
tolua_function(tolua_S, "DoWithDispenserAt", tolua_DoWithXYZ<cWorld, cDispenserEntity, &cWorld::DoWithDispenserAt>);
tolua_function(tolua_S, "DoWithDropSpenserAt", tolua_DoWithXYZ<cWorld, cDropSpenserEntity, &cWorld::DoWithDropSpenserAt>);
diff --git a/src/BlockEntities/BeaconEntity.cpp b/src/BlockEntities/BeaconEntity.cpp
index 4b9662797..805e5e61f 100644
--- a/src/BlockEntities/BeaconEntity.cpp
+++ b/src/BlockEntities/BeaconEntity.cpp
@@ -3,33 +3,31 @@
#include "BeaconEntity.h"
#include "../BlockArea.h"
+#include "../Entities/Player.h"
-cBeaconEntity::cBeaconEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) :
- super(E_BLOCK_BEACON, a_BlockX, a_BlockY, a_BlockZ, a_World)
+cBeaconEntity::cBeaconEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World)
+ : super(E_BLOCK_BEACON, a_BlockX, a_BlockY, a_BlockZ, 1, 1, a_World)
+ , m_IsActive(false)
+ , m_BeaconLevel(0)
+ , m_PrimaryEffect(cEntityEffect::effNoEffect)
+ , m_SecondaryEffect(cEntityEffect::effNoEffect)
{
+ UpdateBeacon();
}
-int cBeaconEntity::GetPyramidLevel(void)
+char cBeaconEntity::CalculatePyramidLevel(void)
{
cBlockArea Area;
- int MinY = GetPosY() - 4;
- if (MinY < 0)
- {
- MinY = 0;
- }
- int MaxY = GetPosY() - 1;
- if (MaxY < 0)
- {
- MaxY = 0;
- }
+ int MinY = std::max(GetPosY() - 4, 0);
+ int MaxY = std::max(GetPosY() - 1, 0);
Area.Read(
m_World,
@@ -42,7 +40,7 @@ int cBeaconEntity::GetPyramidLevel(void)
int Layer = 1;
int MiddleXZ = 4;
- for (int Y = Area.GetSizeY() - 1; Y > 0; Y--)
+ for (int Y = (Area.GetSizeY() - 1); Y >= 0; Y--)
{
for (int X = MiddleXZ - Layer; X <= (MiddleXZ + Layer); X++)
{
@@ -50,14 +48,97 @@ int cBeaconEntity::GetPyramidLevel(void)
{
if (!IsMineralBlock(Area.GetRelBlockType(X, Y, Z)))
{
- return Layer - 1;
+ return (Layer - 1);
}
}
}
Layer++;
}
- return Layer - 1;
+ return (Layer - 1);
+}
+
+
+
+
+
+bool cBeaconEntity::IsValidEffect(cEntityEffect::eType a_Effect, char a_BeaconLevel)
+{
+ switch (a_Effect)
+ {
+ case cEntityEffect::effRegeneration: return (a_BeaconLevel >= 4);
+ case cEntityEffect::effStrength: return (a_BeaconLevel >= 3);
+ case cEntityEffect::effResistance: return (a_BeaconLevel >= 2);
+ case cEntityEffect::effJumpBoost: return (a_BeaconLevel >= 2);
+ case cEntityEffect::effSpeed: return (a_BeaconLevel >= 1);
+ case cEntityEffect::effHaste: return (a_BeaconLevel >= 1);
+ case cEntityEffect::effNoEffect: return true;
+
+ default:
+ {
+ LOGD("%s: Invalid beacon effect: %d", __FUNCTION__, (int)a_Effect);
+ return false;
+ }
+ }
+}
+
+
+
+
+
+bool cBeaconEntity::SetPrimaryEffect(cEntityEffect::eType a_Effect)
+{
+ if (!IsValidEffect(a_Effect, m_BeaconLevel))
+ {
+ return false;
+ }
+
+ m_PrimaryEffect = a_Effect;
+
+ // Send window update:
+ if (GetWindow() != NULL)
+ {
+ GetWindow()->SetProperty(1, m_PrimaryEffect);
+ }
+ return true;
+}
+
+
+
+
+
+bool cBeaconEntity::SetSecondaryEffect(cEntityEffect::eType a_Effect)
+{
+ if (!IsValidEffect(a_Effect, m_BeaconLevel))
+ {
+ return false;
+ }
+
+ m_SecondaryEffect = a_Effect;
+
+ // Send window update:
+ if (GetWindow() != NULL)
+ {
+ GetWindow()->SetProperty(2, m_SecondaryEffect);
+ }
+ return true;
+}
+
+
+
+
+
+bool cBeaconEntity::IsBeaconBlocked(void)
+{
+ for (int Y = m_PosY; Y < cChunkDef::Height; ++Y)
+ {
+ BLOCKTYPE Block = m_World->GetBlock(m_PosX, Y, m_PosZ);
+ if (!cBlockInfo::IsTransparent(Block))
+ {
+ return true;
+ }
+ }
+ return false;
}
@@ -83,8 +164,113 @@ bool cBeaconEntity::IsMineralBlock(BLOCKTYPE a_BlockType)
+void cBeaconEntity::UpdateBeacon(void)
+{
+ int OldBeaconLevel = m_BeaconLevel;
+
+ if (IsBeaconBlocked())
+ {
+ m_IsActive = false;
+ m_BeaconLevel = 0;
+ }
+ else
+ {
+ m_BeaconLevel = CalculatePyramidLevel();
+ m_IsActive = (m_BeaconLevel > 0);
+ }
+
+ if (m_BeaconLevel != OldBeaconLevel)
+ {
+ // Send window update:
+ if (GetWindow() != NULL)
+ {
+ GetWindow()->SetProperty(0, m_BeaconLevel);
+ }
+ }
+
+ // TODO: Add achievement
+}
+
+
+
+
+
+void cBeaconEntity::GiveEffects(void)
+{
+ if (!m_IsActive || (m_BeaconLevel < 0))
+ {
+ return;
+ }
+
+ int Radius = m_BeaconLevel * 10 + 10;
+ short EffectLevel = 0;
+ if ((m_BeaconLevel >= 4) && (m_PrimaryEffect == m_SecondaryEffect))
+ {
+ EffectLevel = 1;
+ }
+
+ cEntityEffect::eType SecondaryEffect = cEntityEffect::effNoEffect;
+ if ((m_BeaconLevel >= 4) && (m_PrimaryEffect != m_SecondaryEffect) && (m_SecondaryEffect > 0))
+ {
+ SecondaryEffect = m_SecondaryEffect;
+ }
+
+ class cPlayerCallback : public cPlayerListCallback
+ {
+ int m_Radius;
+ int m_PosX, m_PosY, m_PosZ;
+ cEntityEffect::eType m_PrimaryEffect, m_SecondaryEffect;
+ short m_EffectLevel;
+
+ virtual bool Item(cPlayer * a_Player)
+ {
+ Vector3d PlayerPosition = Vector3d(a_Player->GetPosition());
+ if (PlayerPosition.y > (double)m_PosY)
+ {
+ PlayerPosition.y = (double)m_PosY;
+ }
+
+ // TODO: Vanilla minecraft uses an AABB check instead of a radius one
+ Vector3d BeaconPosition = Vector3d(m_PosX, m_PosY, m_PosZ);
+ if ((PlayerPosition - BeaconPosition).Length() <= m_Radius)
+ {
+ a_Player->AddEntityEffect(m_PrimaryEffect, 180, m_EffectLevel);
+
+ if (m_SecondaryEffect != cEntityEffect::effNoEffect)
+ {
+ a_Player->AddEntityEffect(m_SecondaryEffect, 180, 0);
+ }
+ }
+ return false;
+ }
+
+ public:
+ cPlayerCallback(int a_Radius, int a_PosX, int a_PosY, int a_PosZ, cEntityEffect::eType a_PrimaryEffect, cEntityEffect::eType a_SecondaryEffect, short a_EffectLevel)
+ : m_Radius(a_Radius)
+ , m_PosX(a_PosX)
+ , m_PosY(a_PosY)
+ , m_PosZ(a_PosZ)
+ , m_PrimaryEffect(a_PrimaryEffect)
+ , m_SecondaryEffect(a_SecondaryEffect)
+ , m_EffectLevel(a_EffectLevel)
+ {};
+
+ } PlayerCallback(Radius, m_PosX, m_PosY, m_PosZ, m_PrimaryEffect, SecondaryEffect, EffectLevel);
+ GetWorld()->ForEachPlayer(PlayerCallback);
+}
+
+
+
+
+
bool cBeaconEntity::Tick(float a_Dt, cChunk & a_Chunk)
{
+ // Update the beacon every 4 seconds
+ if ((GetWorld()->GetWorldAge() % 80) == 0)
+ {
+ UpdateBeacon();
+ GiveEffects();
+ }
return false;
}
@@ -92,23 +278,94 @@ bool cBeaconEntity::Tick(float a_Dt, cChunk & a_Chunk)
-void cBeaconEntity::SaveToJson(Json::Value& a_Value)
+void cBeaconEntity::UsedBy(cPlayer * a_Player)
{
+ cWindow * Window = GetWindow();
+ if (Window == NULL)
+ {
+ OpenWindow(new cBeaconWindow(m_PosX, m_PosY, m_PosZ, this));
+ Window = GetWindow();
+ }
+
+ if (Window != NULL)
+ {
+ // if (a_Player->GetWindow() != Window)
+ // -> Because mojang doesn't send a 'close window' packet when you click the cancel button in the beacon inventory ...
+ {
+ a_Player->OpenWindow(Window);
+ }
+ }
}
-void cBeaconEntity::SendTo(cClientHandle & a_Client)
+
+bool cBeaconEntity::LoadFromJson(const Json::Value & a_Value)
+{
+ m_PosX = a_Value.get("x", 0).asInt();
+ m_PosY = a_Value.get("y", 0).asInt();
+ m_PosZ = a_Value.get("z", 0).asInt();
+
+ Json::Value AllSlots = a_Value.get("Slots", 0);
+ int SlotIdx = 0;
+ for (Json::Value::iterator itr = AllSlots.begin(); itr != AllSlots.end(); ++itr)
+ {
+ cItem Item;
+ Item.FromJson(*itr);
+ SetSlot(SlotIdx, Item);
+ SlotIdx++;
+ }
+
+ m_BeaconLevel = (char)a_Value.get("Level", 0).asInt();
+ int PrimaryEffect = a_Value.get("PrimaryEffect", 0).asInt();
+ int SecondaryEffect = a_Value.get("SecondaryEffect", 0).asInt();
+
+ if ((PrimaryEffect >= 0) && (PrimaryEffect <= (int)cEntityEffect::effSaturation))
+ {
+ m_PrimaryEffect = (cEntityEffect::eType)PrimaryEffect;
+ }
+
+ if ((SecondaryEffect >= 0) && (SecondaryEffect <= (int)cEntityEffect::effSaturation))
+ {
+ m_SecondaryEffect = (cEntityEffect::eType)SecondaryEffect;
+ }
+
+ return true;
+}
+
+
+
+
+
+void cBeaconEntity::SaveToJson(Json::Value& a_Value)
{
+ a_Value["x"] = m_PosX;
+ a_Value["y"] = m_PosY;
+ a_Value["z"] = m_PosZ;
+
+ Json::Value AllSlots;
+ int NumSlots = m_Contents.GetNumSlots();
+ for (int i = 0; i < NumSlots; i++)
+ {
+ Json::Value Slot;
+ m_Contents.GetSlot(i).GetJson(Slot);
+ AllSlots.append(Slot);
+ }
+ a_Value["Slots"] = AllSlots;
+
+ a_Value["Level"] = m_BeaconLevel;
+ a_Value["PrimaryEffect"] = (int)m_PrimaryEffect;
+ a_Value["SecondaryEffect"] = (int)m_SecondaryEffect;
}
-void cBeaconEntity::UsedBy(cPlayer * a_Player)
+void cBeaconEntity::SendTo(cClientHandle & a_Client)
{
+ a_Client.SendUpdateBlockEntity(*this);
}
diff --git a/src/BlockEntities/BeaconEntity.h b/src/BlockEntities/BeaconEntity.h
index ee1eda391..0d7150aef 100644
--- a/src/BlockEntities/BeaconEntity.h
+++ b/src/BlockEntities/BeaconEntity.h
@@ -1,7 +1,14 @@
+// BeaconEntity.h
+
+// Declares the cBeaconEntity class representing a single beacon in the world
+
+
+
+
#pragma once
-#include "BlockEntity.h"
+#include "BlockEntityWithItems.h"
@@ -16,28 +23,70 @@ namespace Json
+// tolua_begin
class cBeaconEntity :
- public cBlockEntity
+ public cBlockEntityWithItems
{
- typedef cBlockEntity super;
+ typedef cBlockEntityWithItems super;
public:
-
- /** The initial constructor */
+ // tolua_end
+
cBeaconEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
-
- /** Returns the amount of layers the pyramid below the beacon has. */
- int GetPyramidLevel(void);
+
+ bool LoadFromJson(const Json::Value & a_Value);
+ // cBlockEntity overrides:
+ virtual void SaveToJson(Json::Value& a_Value) override;
+ virtual void SendTo(cClientHandle & a_Client) override;
+ virtual bool Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void UsedBy(cPlayer * a_Player) override;
+
+ /** Modify the beacon level. (It is needed to load the beacon corectly) */
+ void SetBeaconLevel(char a_Level) { m_BeaconLevel = a_Level; }
+
+ // tolua_begin
+
+ /** Is the beacon active? */
+ bool IsActive(void) const { return m_IsActive; }
+
+ /** Returns the beacon level. (0 - 4) */
+ char GetBeaconLevel(void) const { return m_BeaconLevel; }
+
+ cEntityEffect::eType GetPrimaryEffect(void) const { return m_PrimaryEffect; }
+ cEntityEffect::eType GetSecondaryEffect(void) const { return m_SecondaryEffect; }
+
+ /** Sets the primary effect. Returns false when the effect is invalid. */
+ bool SetPrimaryEffect(cEntityEffect::eType a_Effect);
+
+ /** Sets the secondary effect. Returns false when the effect is invalid. */
+ bool SetSecondaryEffect(cEntityEffect::eType a_Effect);
+
+ /** Calculate the amount of layers the pyramid below the beacon has. */
+ char CalculatePyramidLevel(void);
+
+ /** Is the beacon blocked by non-transparent blocks that are higher than the beacon? */
+ bool IsBeaconBlocked(void);
+
+ /** Update the beacon. */
+ void UpdateBeacon(void);
+
+ /** Give the near-players the effects. */
+ void GiveEffects(void);
/** Returns true if the block is a diamond block, a golden block, an iron block or an emerald block. */
static bool IsMineralBlock(BLOCKTYPE a_BlockType);
-
- // cBlockEntity overrides:
- virtual void SaveToJson(Json::Value& a_Value) override;
- virtual void SendTo(cClientHandle & a_Client) override;
- virtual void UsedBy(cPlayer * a_Player) override;
- virtual bool Tick(float a_Dt, cChunk & /* a_Chunk */) override;
-} ;
+
+ /** Returns true if the effect can be used. */
+ static bool IsValidEffect(cEntityEffect::eType a_Effect, char a_BeaconLevel);
+
+ // tolua_end
+
+protected:
+ bool m_IsActive;
+ char m_BeaconLevel;
+
+ cEntityEffect::eType m_PrimaryEffect, m_SecondaryEffect;
+} ; // tolua_export
diff --git a/src/BlockEntities/FlowerPotEntity.cpp b/src/BlockEntities/FlowerPotEntity.cpp
index 87bf8b921..e001634b8 100644
--- a/src/BlockEntities/FlowerPotEntity.cpp
+++ b/src/BlockEntities/FlowerPotEntity.cpp
@@ -1,7 +1,7 @@
// FlowerPotEntity.cpp
-// Implements the cFlowerPotEntity class representing a single sign in the world
+// Implements the cFlowerPotEntity class representing a single flower pot in the world
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "json/json.h"
diff --git a/src/Blocks/BlockHandler.cpp b/src/Blocks/BlockHandler.cpp
index ddb0186c9..52f7dd608 100644
--- a/src/Blocks/BlockHandler.cpp
+++ b/src/Blocks/BlockHandler.cpp
@@ -181,6 +181,7 @@ cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType)
case E_BLOCK_ACACIA_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType);
case E_BLOCK_ACTIVATOR_RAIL: return new cBlockRailHandler (a_BlockType);
case E_BLOCK_ANVIL: return new cBlockAnvilHandler (a_BlockType);
+ case E_BLOCK_BEACON: return new cBlockEntityHandler (a_BlockType);
case E_BLOCK_BED: return new cBlockBedHandler (a_BlockType);
case E_BLOCK_BIG_FLOWER: return new cBlockBigFlowerHandler (a_BlockType);
case E_BLOCK_BIRCH_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType);
diff --git a/src/Blocks/BlockIce.h b/src/Blocks/BlockIce.h
index c50623594..c38630fe3 100644
--- a/src/Blocks/BlockIce.h
+++ b/src/Blocks/BlockIce.h
@@ -24,9 +24,19 @@ public:
}
- virtual void OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ) override
+ virtual void OnDestroyedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) override
{
- // TODO: Ice destroyed with air below it should turn into air instead of water
+ if (a_Player->IsGameModeCreative() || (a_BlockY <= 0))
+ {
+ return;
+ }
+
+ BLOCKTYPE BlockBelow = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ);
+ if (!cBlockInfo::FullyOccupiesVoxel(BlockBelow) && !IsBlockLiquid(BlockBelow))
+ {
+ return;
+ }
+
a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_WATER, 0);
// This is called later than the real destroying of this ice block
}
diff --git a/src/Blocks/BlockPiston.h b/src/Blocks/BlockPiston.h
index bbb8af75b..0bec603e3 100644
--- a/src/Blocks/BlockPiston.h
+++ b/src/Blocks/BlockPiston.h
@@ -94,6 +94,7 @@ private:
switch (a_BlockType)
{
case E_BLOCK_ANVIL:
+ case E_BLOCK_BEACON:
case E_BLOCK_BEDROCK:
case E_BLOCK_BREWING_STAND:
case E_BLOCK_CHEST:
diff --git a/src/Chunk.cpp b/src/Chunk.cpp
index b83036b6d..cd7dc698c 100644
--- a/src/Chunk.cpp
+++ b/src/Chunk.cpp
@@ -11,6 +11,7 @@
#include "Server.h"
#include "zlib/zlib.h"
#include "Defines.h"
+#include "BlockEntities/BeaconEntity.h"
#include "BlockEntities/ChestEntity.h"
#include "BlockEntities/DispenserEntity.h"
#include "BlockEntities/DropperEntity.h"
@@ -2126,6 +2127,38 @@ bool cChunk::DoWithBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBloc
+bool cChunk::DoWithBeaconAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBeaconCallback & 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_BEACON)
+ {
+ // 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((cBeaconEntity *)*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 7eee3999c..5cde3f08f 100644
--- a/src/Chunk.h
+++ b/src/Chunk.h
@@ -28,6 +28,7 @@ class cServer;
class MTRand;
class cPlayer;
class cChunkMap;
+class cBeaconEntity;
class cChestEntity;
class cDispenserEntity;
class cFurnaceEntity;
@@ -45,6 +46,7 @@ class cMobSpawner;
typedef std::list<cClientHandle *> cClientHandleList;
typedef cItemCallback<cEntity> cEntityCallback;
+typedef cItemCallback<cBeaconEntity> cBeaconCallback;
typedef cItemCallback<cChestEntity> cChestCallback;
typedef cItemCallback<cDispenserEntity> cDispenserCallback;
typedef cItemCallback<cFurnaceEntity> cFurnaceCallback;
@@ -236,6 +238,9 @@ public:
/** Calls the callback for the block entity at the specified coords; returns false if there's no block entity at those coords, true if found */
bool DoWithBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBlockEntityCallback & a_Callback); // Lua-acessible
+ /** 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 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 05d219918..dd8be0631 100644
--- a/src/ChunkMap.cpp
+++ b/src/ChunkMap.cpp
@@ -1839,6 +1839,7 @@ void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_
}
case E_BLOCK_OBSIDIAN:
+ case E_BLOCK_BEACON:
case E_BLOCK_BEDROCK:
case E_BLOCK_WATER:
case E_BLOCK_LAVA:
@@ -2115,6 +2116,24 @@ bool cChunkMap::DoWithBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ, cB
+bool cChunkMap::DoWithBeaconAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBeaconCallback & 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, ZERO_CHUNK_Y, ChunkZ);
+ if ((Chunk == NULL) || !Chunk->IsValid())
+ {
+ return false;
+ }
+ return Chunk->DoWithBeaconAt(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 e33d9f894..1e9a0f982 100644
--- a/src/ChunkMap.h
+++ b/src/ChunkMap.h
@@ -19,6 +19,7 @@ class MTRand;
class cChunkStay;
class cChunk;
class cPlayer;
+class cBeaconEntity;
class cChestEntity;
class cDispenserEntity;
class cDropperEntity;
@@ -40,6 +41,7 @@ typedef std::list<cClientHandle *> cClientHandleList;
typedef cChunk * cChunkPtr;
typedef cItemCallback<cEntity> cEntityCallback;
typedef cItemCallback<cBlockEntity> cBlockEntityCallback;
+typedef cItemCallback<cBeaconEntity> cBeaconCallback;
typedef cItemCallback<cChestEntity> cChestCallback;
typedef cItemCallback<cDispenserEntity> cDispenserCallback;
typedef cItemCallback<cDropperEntity> cDropperCallback;
@@ -234,6 +236,9 @@ public:
/** Calls the callback for the block entity at the specified coords; returns false if there's no block entity at those coords, true if found */
bool DoWithBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBlockEntityCallback & a_Callback); // Lua-acessible
+ /** 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 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/ClientHandle.cpp b/src/ClientHandle.cpp
index 1b0d11650..d71c6a9d8 100644
--- a/src/ClientHandle.cpp
+++ b/src/ClientHandle.cpp
@@ -7,6 +7,7 @@
#include "Bindings/PluginManager.h"
#include "Entities/Player.h"
#include "Inventory.h"
+#include "BlockEntities/BeaconEntity.h"
#include "BlockEntities/ChestEntity.h"
#include "BlockEntities/CommandBlockEntity.h"
#include "BlockEntities/SignEntity.h"
@@ -635,6 +636,7 @@ void cClientHandle::HandlePlayerPos(double a_PosX, double a_PosY, double a_PosZ,
if (!m_Player->IsSwimming())
{
+ m_Player->GetStatManager().AddValue(statJumps, 1);
m_Player->AddFoodExhaustion(m_Player->IsSprinting() ? 0.8 : 0.2);
}
}
@@ -660,6 +662,10 @@ void cClientHandle::HandlePluginMessage(const AString & a_Channel, const AString
// Client <-> Server branding exchange
SendPluginMessage("MC|Brand", "MCServer");
}
+ else if (a_Channel == "MC|Beacon")
+ {
+ HandleBeaconSelection(a_Message.c_str(), a_Message.size());
+ }
else if (a_Channel == "MC|ItemName")
{
HandleAnvilItemName(a_Message.c_str(), a_Message.size());
@@ -747,6 +753,55 @@ void cClientHandle::UnregisterPluginChannels(const AStringVector & a_ChannelList
+void cClientHandle::HandleBeaconSelection(const char * a_Data, size_t a_Length)
+{
+ if (a_Length < 14)
+ {
+ SendChat("Failure setting beacon selection; bad request", mtFailure);
+ LOGD("Malformed MC|Beacon packet.");
+ return;
+ }
+
+ cWindow * Window = m_Player->GetWindow();
+ if ((Window == NULL) || (Window->GetWindowType() != cWindow::wtBeacon))
+ {
+ return;
+ }
+ cBeaconWindow * BeaconWindow = (cBeaconWindow *) Window;
+
+ if (Window->GetSlot(*m_Player, 0)->IsEmpty())
+ {
+ return;
+ }
+
+ cByteBuffer Buffer(a_Length);
+ Buffer.Write(a_Data, a_Length);
+
+ int PrimaryEffectID, SecondaryEffectID;
+ Buffer.ReadBEInt(PrimaryEffectID);
+ Buffer.ReadBEInt(SecondaryEffectID);
+
+ cEntityEffect::eType PrimaryEffect = cEntityEffect::effNoEffect;
+ if ((PrimaryEffectID >= 0) && (PrimaryEffectID <= (int)cEntityEffect::effSaturation))
+ {
+ PrimaryEffect = (cEntityEffect::eType)PrimaryEffectID;
+ }
+
+ cEntityEffect::eType SecondaryEffect = cEntityEffect::effNoEffect;
+ if ((SecondaryEffectID >= 0) && (SecondaryEffectID <= (int)cEntityEffect::effSaturation))
+ {
+ SecondaryEffect = (cEntityEffect::eType)SecondaryEffectID;
+ }
+
+ Window->SetSlot(*m_Player, 0, cItem());
+ BeaconWindow->GetBeaconEntity()->SetPrimaryEffect(PrimaryEffect);
+ BeaconWindow->GetBeaconEntity()->SetSecondaryEffect(SecondaryEffect);
+}
+
+
+
+
+
void cClientHandle::HandleCommandBlockMessage(const char * a_Data, size_t a_Length)
{
if (a_Length < 14)
@@ -1068,6 +1123,7 @@ void cClientHandle::HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_Blo
return;
}
+ m_Player->AddFoodExhaustion(0.025);
ItemHandler->OnBlockDestroyed(World, m_Player, m_Player->GetEquippedItem(), a_BlockX, a_BlockY, a_BlockZ);
// The ItemHandler is also responsible for spawning the pickups
cChunkInterface ChunkInterface(World->GetChunkMap());
@@ -1213,7 +1269,7 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
else if ((ItemHandler->IsFood() || ItemHandler->IsDrinkable(EquippedDamage)))
{
if ((m_Player->IsSatiated() || m_Player->IsGameModeCreative()) &&
- ItemHandler->IsFood())
+ ItemHandler->IsFood() && (Equipped.m_ItemType != E_ITEM_GOLDEN_APPLE))
{
// The player is satiated or in creative, and trying to eat
return;
diff --git a/src/ClientHandle.h b/src/ClientHandle.h
index 886f84959..ee1db3155 100644
--- a/src/ClientHandle.h
+++ b/src/ClientHandle.h
@@ -405,7 +405,10 @@ private:
/** Removes all of the channels from the list of current plugin channels. Ignores channels that are not found. */
void UnregisterPluginChannels(const AStringVector & a_ChannelList);
-
+
+ /** Handles the "MC|Beacon" plugin message */
+ void HandleBeaconSelection(const char * a_Data, size_t a_Length);
+
/** Handles the "MC|AdvCdm" plugin message */
void HandleCommandBlockMessage(const char * a_Data, size_t a_Length);
diff --git a/src/Entities/EntityEffect.cpp b/src/Entities/EntityEffect.cpp
index fdcbe822e..39314f256 100644
--- a/src/Entities/EntityEffect.cpp
+++ b/src/Entities/EntityEffect.cpp
@@ -309,7 +309,7 @@ void cEntityEffectHunger::OnTick(cPawn & a_Target)
if (a_Target.IsPlayer())
{
cPlayer & Target = (cPlayer &) a_Target;
- Target.SetFoodExhaustionLevel(Target.GetFoodExhaustionLevel() + 0.025); // 0.5 per second = 0.025 per tick
+ Target.AddFoodExhaustion(0.025 * ((double)GetIntensity() + 1.0)); // 0.5 per second = 0.025 per tick
}
}
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index 38182f7d0..ca3b1f367 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -545,7 +545,7 @@ void cPlayer::SetFoodTickTimer(int a_FoodTickTimer)
void cPlayer::SetFoodExhaustionLevel(double a_FoodExhaustionLevel)
{
- m_FoodExhaustionLevel = std::max(0.0, std::min(a_FoodExhaustionLevel, 4.0));
+ m_FoodExhaustionLevel = std::max(0.0, std::min(a_FoodExhaustionLevel, 40.0));
}
@@ -568,6 +568,18 @@ bool cPlayer::Feed(int a_Food, double a_Saturation)
+void cPlayer::AddFoodExhaustion(double a_Exhaustion)
+{
+ if (!IsGameModeCreative())
+ {
+ m_FoodExhaustionLevel = std::min(m_FoodExhaustionLevel + a_Exhaustion, 40.0);
+ }
+}
+
+
+
+
+
void cPlayer::FoodPoison(int a_NumTicks)
{
AddEntityEffect(cEntityEffect::effHunger, a_NumTicks, 0, 1);
@@ -1981,19 +1993,34 @@ void cPlayer::HandleFood(void)
return;
}
+ // Apply food exhaustion that has accumulated:
+ if (m_FoodExhaustionLevel > 4.0)
+ {
+ m_FoodExhaustionLevel -= 4.0;
+
+ if (m_FoodSaturationLevel > 0.0)
+ {
+ m_FoodSaturationLevel = std::max(m_FoodSaturationLevel - 1.0, 0.0);
+ }
+ else
+ {
+ SetFoodLevel(m_FoodLevel - 1);
+ }
+ }
+
// Heal or damage, based on the food level, using the m_FoodTickTimer:
- if ((m_FoodLevel > 17) || (m_FoodLevel <= 0))
+ if ((m_FoodLevel >= 18) || (m_FoodLevel <= 0))
{
m_FoodTickTimer++;
if (m_FoodTickTimer >= 80)
{
m_FoodTickTimer = 0;
- if ((m_FoodLevel > 17) && (GetHealth() < GetMaxHealth()))
+ if ((m_FoodLevel >= 18) && (GetHealth() < GetMaxHealth()))
{
// Regenerate health from food, incur 3 pts of food exhaustion:
Heal(1);
- m_FoodExhaustionLevel += 3.0;
+ AddFoodExhaustion(3.0);
}
else if ((m_FoodLevel <= 0) && (m_Health > 1))
{
@@ -2002,20 +2029,9 @@ void cPlayer::HandleFood(void)
}
}
}
-
- // Apply food exhaustion that has accumulated:
- if (m_FoodExhaustionLevel >= 4.0)
+ else
{
- m_FoodExhaustionLevel -= 4.0;
-
- if (m_FoodSaturationLevel >= 1.0)
- {
- m_FoodSaturationLevel -= 1.0;
- }
- else
- {
- SetFoodLevel(m_FoodLevel - 1);
- }
+ m_FoodTickTimer = 0;
}
}
@@ -2090,14 +2106,17 @@ void cPlayer::UpdateMovementStats(const Vector3d & a_DeltaPos)
else if (IsSubmerged())
{
m_Stats.AddValue(statDistDove, Value);
+ AddFoodExhaustion(0.00015 * (double)Value);
}
else if (IsSwimming())
{
m_Stats.AddValue(statDistSwum, Value);
+ AddFoodExhaustion(0.00015 * (double)Value);
}
else if (IsOnGround())
{
m_Stats.AddValue(statDistWalked, Value);
+ AddFoodExhaustion((m_IsSprinting ? 0.001 : 0.0001) * (double)Value);
}
else
{
diff --git a/src/Entities/Player.h b/src/Entities/Player.h
index 2bb1664a8..917e87a89 100644
--- a/src/Entities/Player.h
+++ b/src/Entities/Player.h
@@ -284,10 +284,7 @@ public:
bool Feed(int a_Food, double a_Saturation);
/** Adds the specified exhaustion to m_FoodExhaustion. Expects only positive values. */
- void AddFoodExhaustion(double a_Exhaustion)
- {
- m_FoodExhaustionLevel += a_Exhaustion;
- }
+ void AddFoodExhaustion(double a_Exhaustion);
/** Starts the food poisoning for the specified amount of ticks */
void FoodPoison(int a_NumTicks);
diff --git a/src/Items/ItemFood.h b/src/Items/ItemFood.h
index ff1d7991b..98050cad8 100644
--- a/src/Items/ItemFood.h
+++ b/src/Items/ItemFood.h
@@ -33,29 +33,69 @@ public:
case E_ITEM_BREAD: return FoodInfo(5, 6);
// Carrots handled in ItemSeeds
case E_ITEM_COOKED_CHICKEN: return FoodInfo(6, 7.2);
- case E_ITEM_COOKED_FISH: return FoodInfo(5, 6);
+ case E_ITEM_COOKED_FISH: return FoodInfo(5, 6); // TODO: Add other fish types
case E_ITEM_COOKED_PORKCHOP: return FoodInfo(8, 12.8);
case E_ITEM_COOKIE: return FoodInfo(2, 0.4);
- case E_ITEM_GOLDEN_APPLE: return FoodInfo(4, 9.6);
+ // Golden apple handled in ItemGoldenApple
case E_ITEM_GOLDEN_CARROT: return FoodInfo(6, 14.4);
case E_ITEM_MELON_SLICE: return FoodInfo(2, 1.2);
case E_ITEM_MUSHROOM_SOUP: return FoodInfo(6, 7.2);
- case E_ITEM_POISONOUS_POTATO: return FoodInfo(2, 1.2, 60);
+ case E_ITEM_POISONOUS_POTATO: return FoodInfo(2, 1.2);
// Potatoes handled in ItemSeeds
case E_ITEM_PUMPKIN_PIE: return FoodInfo(8, 4.8);
case E_ITEM_RAW_BEEF: return FoodInfo(3, 1.8);
- case E_ITEM_RAW_CHICKEN: return FoodInfo(2, 1.2, 30);
+ case E_ITEM_RAW_CHICKEN: return FoodInfo(2, 1.2);
case E_ITEM_RAW_FISH: return FoodInfo(2, 1.2);
case E_ITEM_RAW_PORKCHOP: return FoodInfo(3, 1.8);
case E_ITEM_RED_APPLE: return FoodInfo(4, 2.4);
- case E_ITEM_ROTTEN_FLESH: return FoodInfo(4, 0.8, 80);
- case E_ITEM_SPIDER_EYE: return FoodInfo(2, 3.2, 100);
+ case E_ITEM_ROTTEN_FLESH: return FoodInfo(4, 0.8);
+ case E_ITEM_SPIDER_EYE: return FoodInfo(2, 3.2);
case E_ITEM_STEAK: return FoodInfo(8, 12.8);
}
LOGWARNING("%s: Unknown food item (%d), returning zero nutrition", __FUNCTION__, m_ItemType);
return FoodInfo(0, 0.f);
}
-
+
+ virtual bool GetEatEffect(cEntityEffect::eType & a_EffectType, int & a_EffectDurationTicks, short & a_EffectIntensity, float & a_Chance) override
+ {
+ switch (m_ItemType)
+ {
+ case E_ITEM_RAW_CHICKEN:
+ {
+ a_EffectType = cEntityEffect::effHunger;
+ a_EffectDurationTicks = 600;
+ a_EffectIntensity = 0;
+ a_Chance = 0.3f;
+ return true;
+ }
+ case E_ITEM_ROTTEN_FLESH:
+ {
+ a_EffectType = cEntityEffect::effHunger;
+ a_EffectDurationTicks = 600;
+ a_EffectIntensity = 0;
+ a_Chance = 0.8f;
+ return true;
+ }
+ case E_ITEM_SPIDER_EYE:
+ {
+ a_EffectType = cEntityEffect::effPoison;
+ a_EffectDurationTicks = 100;
+ a_EffectIntensity = 0;
+ a_Chance = 1.0f;
+ return true;
+ }
+ case E_ITEM_POISONOUS_POTATO:
+ {
+ a_EffectType = cEntityEffect::effPoison;
+ a_EffectDurationTicks = 100;
+ a_EffectIntensity = 0;
+ a_Chance = 0.6f;
+ return true;
+ }
+ }
+ return false;
+ }
+
};
diff --git a/src/Items/ItemGoldenApple.h b/src/Items/ItemGoldenApple.h
new file mode 100644
index 000000000..4e1096e65
--- /dev/null
+++ b/src/Items/ItemGoldenApple.h
@@ -0,0 +1,58 @@
+#pragma once
+
+#include "ItemFood.h"
+
+
+
+
+
+class cItemGoldenAppleHandler :
+ public cItemFoodHandler
+{
+ typedef cItemFoodHandler super;
+
+public:
+ cItemGoldenAppleHandler()
+ : super(E_ITEM_GOLDEN_APPLE)
+ {
+ }
+
+
+ virtual bool EatItem(cPlayer * a_Player, cItem * a_Item) override
+ {
+ // Feed the player:
+ FoodInfo Info = GetFoodInfo();
+ a_Player->Feed(Info.FoodLevel, Info.Saturation);
+
+ // Add the effects:
+ a_Player->AddEntityEffect(cEntityEffect::effAbsorption, 2400, 0);
+ a_Player->AddEntityEffect(cEntityEffect::effRegeneration, 100, 1);
+
+ // When the apple is a 'notch apple', give extra effects:
+ if (a_Item->m_ItemDamage > 0)
+ {
+ a_Player->AddEntityEffect(cEntityEffect::effRegeneration, 600, 4);
+ a_Player->AddEntityEffect(cEntityEffect::effResistance, 6000, 0);
+ a_Player->AddEntityEffect(cEntityEffect::effFireResistance, 6000, 0);
+ }
+
+ return true;
+ }
+
+
+ virtual FoodInfo GetFoodInfo(void) override
+ {
+ return FoodInfo(4, 9.6);
+ }
+
+
+ virtual bool GetEatEffect(cEntityEffect::eType & a_EffectType, int & a_EffectDurationTicks, short & a_EffectIntensity, float & a_Chance) override
+ {
+ return false;
+ }
+
+};
+
+
+
+
diff --git a/src/Items/ItemHandler.cpp b/src/Items/ItemHandler.cpp
index d36b5d663..1c1e3b5b9 100644
--- a/src/Items/ItemHandler.cpp
+++ b/src/Items/ItemHandler.cpp
@@ -25,6 +25,7 @@
#include "ItemFishingRod.h"
#include "ItemFlowerPot.h"
#include "ItemFood.h"
+#include "ItemGoldenApple.h"
#include "ItemItemFrame.h"
#include "ItemHoe.h"
#include "ItemLeaves.h"
@@ -106,7 +107,7 @@ cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType)
case E_ITEM_BED: return new cItemBedHandler(a_ItemType);
case E_ITEM_BOAT: return new cItemBoatHandler(a_ItemType);
case E_ITEM_BOTTLE_O_ENCHANTING: return new cItemBottleOEnchantingHandler();
- case E_ITEM_BOW: return new cItemBowHandler;
+ case E_ITEM_BOW: return new cItemBowHandler();
case E_ITEM_BREWING_STAND: return new cItemBrewingStandHandler(a_ItemType);
case E_ITEM_CAKE: return new cItemCakeHandler(a_ItemType);
case E_ITEM_CAULDRON: return new cItemCauldronHandler(a_ItemType);
@@ -120,6 +121,7 @@ cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType)
case E_ITEM_FISHING_ROD: return new cItemFishingRodHandler(a_ItemType);
case E_ITEM_FLINT_AND_STEEL: return new cItemLighterHandler(a_ItemType);
case E_ITEM_FLOWER_POT: return new cItemFlowerPotHandler(a_ItemType);
+ case E_ITEM_GOLDEN_APPLE: return new cItemGoldenAppleHandler();
case E_BLOCK_LILY_PAD: return new cItemLilypadHandler(a_ItemType);
case E_ITEM_MAP: return new cItemMapHandler();
case E_ITEM_MILK: return new cItemMilkHandler();
@@ -212,7 +214,6 @@ cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType)
case E_ITEM_COOKED_FISH:
case E_ITEM_COOKED_PORKCHOP:
case E_ITEM_COOKIE:
- case E_ITEM_GOLDEN_APPLE:
case E_ITEM_GOLDEN_CARROT:
case E_ITEM_MELON_SLICE:
case E_ITEM_MUSHROOM_SOUP:
@@ -578,6 +579,7 @@ bool cItemHandler::CanHarvestBlock(BLOCKTYPE a_BlockType)
case E_BLOCK_LAPIS_BLOCK:
case E_BLOCK_SNOW:
case E_BLOCK_VINES:
+ case E_BLOCK_PACKED_ICE:
{
return false;
}
@@ -618,29 +620,39 @@ bool cItemHandler::GetPlacementBlockTypeMeta(
+bool cItemHandler::GetEatEffect(cEntityEffect::eType & a_EffectType, int & a_EffectDurationTicks, short & a_EffectIntensity, float & a_Chance)
+{
+ return false;
+}
+
+
+
+
+
bool cItemHandler::EatItem(cPlayer * a_Player, cItem * a_Item)
{
UNUSED(a_Item);
-
- FoodInfo Info = GetFoodInfo();
+ FoodInfo Info = GetFoodInfo();
if ((Info.FoodLevel > 0) || (Info.Saturation > 0.f))
{
bool Success = a_Player->Feed(Info.FoodLevel, Info.Saturation);
-
- // If consumed and there's chance of foodpoisoning, do it:
- if (Success && (Info.PoisonChance > 0))
+
+ // Give effects
+ cEntityEffect::eType EffectType;
+ int EffectDurationTicks;
+ short EffectIntensity;
+ float Chance;
+ if (Success && GetEatEffect(EffectType, EffectDurationTicks, EffectIntensity, Chance))
{
cFastRandom r1;
- if ((r1.NextInt(100, a_Player->GetUniqueID()) - Info.PoisonChance) <= 0)
+ if (r1.NextFloat() < Chance)
{
- a_Player->FoodPoison(600); // Give the player food poisoning for 30 seconds.
+ a_Player->AddEntityEffect(EffectType, EffectDurationTicks, EffectIntensity, Chance);
}
}
-
return Success;
}
-
return false;
}
diff --git a/src/Items/ItemHandler.h b/src/Items/ItemHandler.h
index 1d5f59f3e..8b554ee34 100644
--- a/src/Items/ItemHandler.h
+++ b/src/Items/ItemHandler.h
@@ -3,6 +3,7 @@
#include "../Defines.h"
#include "../Item.h"
+#include "../Entities/EntityEffect.h"
@@ -71,23 +72,24 @@ public:
struct FoodInfo
{
- double Saturation;
int FoodLevel;
- int PoisonChance; // 0 - 100, in percent. 0 = no chance of poisoning, 100 = sure poisoning
+ double Saturation;
FoodInfo(int a_FoodLevel, double a_Saturation, int a_PoisonChance = 0) :
- Saturation(a_Saturation),
FoodLevel(a_FoodLevel),
- PoisonChance(a_PoisonChance)
+ Saturation(a_Saturation)
{
}
} ;
- /** Returns the FoodInfo for this item. (FoodRecovery, Saturation and PoisionChance) */
+ /** Returns the FoodInfo for this item. (FoodRecovery and Saturation) */
virtual FoodInfo GetFoodInfo();
-
+
+ /** If this function returns true, it sets the arguments to a effect who will be activated when you eat the item. */
+ virtual bool GetEatEffect(cEntityEffect::eType & a_EffectType, int & a_EffectDurationTicks, short & a_EffectIntensity, float & a_Chance);
+
/** Lets the player eat a selected item. Returns true if the player ate the item */
- virtual bool EatItem(cPlayer *a_Player, cItem *a_Item);
+ virtual bool EatItem(cPlayer * a_Player, cItem * a_Item);
/** Indicates if this item is a tool */
virtual bool IsTool(void);
diff --git a/src/Mobs/Enderman.cpp b/src/Mobs/Enderman.cpp
index becc99a86..a32e4e175 100644
--- a/src/Mobs/Enderman.cpp
+++ b/src/Mobs/Enderman.cpp
@@ -2,6 +2,74 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "Enderman.h"
+#include "../Entities/Player.h"
+#include "../Tracer.h"
+
+
+
+
+///////////////////////////////////////////////////////////////////////////
+// cPlayerLookCheck
+class cPlayerLookCheck :
+ public cPlayerListCallback
+{
+public:
+ cPlayerLookCheck(Vector3d a_EndermanPos) :
+ m_Player(NULL),
+ m_EndermanPos(a_EndermanPos)
+ {
+ }
+
+ virtual bool Item(cPlayer * a_Player) override
+ {
+ // Don't check players who are in creative gamemode
+ if (a_Player->IsGameModeCreative())
+ {
+ return false;
+ }
+
+ Vector3d Direction = m_EndermanPos - a_Player->GetPosition();
+
+ // Don't check players who are more then 64 blocks away
+ if (Direction.SqrLength() > 64)
+ {
+ return false;
+ }
+
+ // Don't check if the player has a pumpkin on his head
+ if (a_Player->GetEquippedHelmet().m_ItemType == E_BLOCK_PUMPKIN)
+ {
+ return false;
+ }
+
+
+ Vector3d LookVector = a_Player->GetLookVector();
+ double dot = Direction.Dot(LookVector);
+
+ // 0.09 rad ~ 5 degrees
+ // If the player's crosshair is within 5 degrees of the enderman, it counts as looking
+ if (dot > cos(0.09))
+ {
+ return false;
+ }
+
+ cTracer LineOfSight(a_Player->GetWorld());
+ if (LineOfSight.Trace(m_EndermanPos, Direction, (int)Direction.Length()))
+ {
+ // No direct line of sight
+ return false;
+ }
+
+ m_Player = a_Player;
+ return true;
+ }
+
+ cPlayer * GetPlayer(void) const { return m_Player; }
+
+protected:
+ cPlayer * m_Player;
+ Vector3d m_EndermanPos;
+} ;
@@ -32,3 +100,54 @@ void cEnderman::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+void cEnderman::CheckEventSeePlayer()
+{
+ if (m_Target != NULL)
+ {
+ return;
+ }
+
+ cPlayerLookCheck Callback(GetPosition());
+ if (m_World->ForEachPlayer(Callback))
+ {
+ return;
+ }
+
+ ASSERT(Callback.GetPlayer() != NULL);
+
+ int ChunkX, ChunkZ;
+ cChunkDef::BlockToChunk(POSX_TOINT, POSZ_TOINT, ChunkX, ChunkZ);
+
+ // Check if the chunk the enderman is in is lit
+ if (!m_World->IsChunkLighted(ChunkX, ChunkZ))
+ {
+ m_World->QueueLightChunk(ChunkX, ChunkZ);
+ return;
+ }
+
+ // Enderman only attack if the skylight is higher than 7
+ if (m_World->GetBlockSkyLight(POSX_TOINT, POSY_TOINT, POSZ_TOINT) <= 7)
+ {
+ // TODO: Teleport the enderman to a random spot
+ return;
+ }
+
+ if (!Callback.GetPlayer()->IsGameModeCreative())
+ {
+ super::EventSeePlayer(Callback.GetPlayer());
+ m_EMState = CHASING;
+ m_bIsScreaming = true;
+ GetWorld()->BroadcastEntityMetadata(*this);
+ }
+}
+
+
+
+
+
+void cEnderman::EventLosePlayer()
+{
+ super::EventLosePlayer();
+ m_bIsScreaming = false;
+ GetWorld()->BroadcastEntityMetadata(*this);
+}
diff --git a/src/Mobs/Enderman.h b/src/Mobs/Enderman.h
index aa2eff682..da857ee09 100644
--- a/src/Mobs/Enderman.h
+++ b/src/Mobs/Enderman.h
@@ -18,6 +18,8 @@ public:
CLASS_PROTODEF(cEnderman)
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+ virtual void CheckEventSeePlayer(void) override;
+ virtual void EventLosePlayer(void) override;
bool IsScreaming(void) const {return m_bIsScreaming; }
BLOCKTYPE GetCarriedBlock(void) const {return CarriedBlock; }
diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp
index 1ee056a8f..f419c01a7 100644
--- a/src/Protocol/Protocol17x.cpp
+++ b/src/Protocol/Protocol17x.cpp
@@ -37,6 +37,7 @@ Implements the 1.7.x protocol classes:
#include "../Mobs/IncludeAllMonsters.h"
#include "../UI/Window.h"
+#include "../BlockEntities/BeaconEntity.h"
#include "../BlockEntities/CommandBlockEntity.h"
#include "../BlockEntities/MobHeadEntity.h"
#include "../BlockEntities/FlowerPotEntity.h"
@@ -1328,6 +1329,7 @@ void cProtocol172::SendUpdateBlockEntity(cBlockEntity & a_BlockEntity)
{
case E_BLOCK_MOB_SPAWNER: Action = 1; break; // Update mob spawner spinny mob thing
case E_BLOCK_COMMAND_BLOCK: Action = 2; break; // Update command block text
+ case E_BLOCK_BEACON: Action = 3; break; // Update beacon entity
case E_BLOCK_HEAD: Action = 4; break; // Update Mobhead entity
case E_BLOCK_FLOWER_POT: Action = 5; break; // Update flower pot
default: ASSERT(!"Unhandled or unimplemented BlockEntity update request!"); break;
@@ -2581,6 +2583,19 @@ void cProtocol172::cPacketizer::WriteBlockEntity(const cBlockEntity & a_BlockEnt
switch (a_BlockEntity.GetBlockType())
{
+ case E_BLOCK_BEACON:
+ {
+ cBeaconEntity & BeaconEntity = (cBeaconEntity &)a_BlockEntity;
+
+ Writer.AddInt("x", BeaconEntity.GetPosX());
+ Writer.AddInt("y", BeaconEntity.GetPosY());
+ Writer.AddInt("z", BeaconEntity.GetPosZ());
+ Writer.AddInt("Primary", BeaconEntity.GetPrimaryEffect());
+ Writer.AddInt("Secondary", BeaconEntity.GetSecondaryEffect());
+ Writer.AddInt("Levels", BeaconEntity.GetBeaconLevel());
+ Writer.AddString("id", "Beacon"); // "Tile Entity ID" - MC wiki; vanilla server always seems to send this though
+ break;
+ }
case E_BLOCK_COMMAND_BLOCK:
{
cCommandBlockEntity & CommandBlockEntity = (cCommandBlockEntity &)a_BlockEntity;
diff --git a/src/Simulator/FireSimulator.cpp b/src/Simulator/FireSimulator.cpp
index 69dc7164e..cdb5abde2 100644
--- a/src/Simulator/FireSimulator.cpp
+++ b/src/Simulator/FireSimulator.cpp
@@ -359,18 +359,26 @@ void cFireSimulator::RemoveFuelNeighbors(cChunk * a_Chunk, int a_RelX, int a_Rel
continue;
}
+ int AbsX = (Neighbour->GetPosX() * cChunkDef::Width) + X;
+ int Y = a_RelY + gNeighborCoords[i].y;
+ int AbsZ = (Neighbour->GetPosZ() * cChunkDef::Width) + Z;
+
if (BlockType == E_BLOCK_TNT)
{
- int AbsX = X + Neighbour->GetPosX() * cChunkDef::Width;
- int AbsZ = Z + Neighbour->GetPosZ() * cChunkDef::Width;
-
- m_World.SpawnPrimedTNT(AbsX, a_RelY + gNeighborCoords[i].y, AbsZ, 0);
- Neighbour->SetBlock(X, a_RelY + gNeighborCoords[i].y, Z, E_BLOCK_AIR, 0);
+ m_World.SpawnPrimedTNT(AbsX, Y, AbsZ, 0);
+ Neighbour->SetBlock(X, a_RelY + Y, Z, E_BLOCK_AIR, 0);
return;
}
bool ShouldReplaceFuel = (m_World.GetTickRandomNumber(MAX_CHANCE_REPLACE_FUEL) < m_ReplaceFuelChance);
- Neighbour->SetBlock(X, a_RelY + gNeighborCoords[i].y, Z, ShouldReplaceFuel ? E_BLOCK_FIRE : E_BLOCK_AIR, 0);
+ if (ShouldReplaceFuel && !cRoot::Get()->GetPluginManager()->CallHookBlockSpread(&m_World, AbsX, Y, AbsZ, ssFireSpread))
+ {
+ Neighbour->SetBlock(X, Y, Z, E_BLOCK_FIRE, 0);
+ }
+ else
+ {
+ Neighbour->SetBlock(X, Y, Z, E_BLOCK_AIR, 0);
+ }
} // for i - Coords[]
}
diff --git a/src/UI/SlotArea.cpp b/src/UI/SlotArea.cpp
index b5f84c24c..4199bbf56 100644
--- a/src/UI/SlotArea.cpp
+++ b/src/UI/SlotArea.cpp
@@ -5,6 +5,7 @@
#include "Globals.h"
#include "SlotArea.h"
#include "../Entities/Player.h"
+#include "../BlockEntities/BeaconEntity.h"
#include "../BlockEntities/ChestEntity.h"
#include "../BlockEntities/DropSpenserEntity.h"
#include "../BlockEntities/EnderChestEntity.h"
@@ -1190,6 +1191,200 @@ void cSlotAreaAnvil::UpdateResult(cPlayer & a_Player)
+////////////////////////////////////////////////////////////////////////////////
+// cSlotAreaBeacon:
+
+cSlotAreaBeacon::cSlotAreaBeacon(cBeaconEntity * a_Beacon, cWindow & a_ParentWindow) :
+ cSlotArea(1, a_ParentWindow),
+ m_Beacon(a_Beacon)
+{
+ m_Beacon->GetContents().AddListener(*this);
+}
+
+
+
+
+
+cSlotAreaBeacon::~cSlotAreaBeacon()
+{
+ m_Beacon->GetContents().RemoveListener(*this);
+}
+
+
+
+
+bool cSlotAreaBeacon::IsPlaceableItem(short a_ItemType)
+{
+ switch (a_ItemType)
+ {
+ case E_ITEM_EMERALD:
+ case E_ITEM_DIAMOND:
+ case E_ITEM_GOLD:
+ case E_ITEM_IRON:
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+void cSlotAreaBeacon::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem)
+{
+ ASSERT((a_SlotNum >= 0) && (a_SlotNum < GetNumSlots()));
+
+ bool bAsync = false;
+ if (GetSlot(a_SlotNum, a_Player) == NULL)
+ {
+ LOGWARNING("GetSlot(%d) returned NULL! Ignoring click", a_SlotNum);
+ return;
+ }
+
+ switch (a_ClickAction)
+ {
+ case caShiftLeftClick:
+ case caShiftRightClick:
+ {
+ ShiftClicked(a_Player, a_SlotNum, a_ClickedItem);
+ return;
+ }
+ case caMiddleClick:
+ {
+ MiddleClicked(a_Player, a_SlotNum);
+ return;
+ }
+ case caDropKey:
+ case caCtrlDropKey:
+ {
+ DropClicked(a_Player, a_SlotNum, false);
+ return;
+ }
+ case caNumber1:
+ case caNumber2:
+ case caNumber3:
+ case caNumber4:
+ case caNumber5:
+ case caNumber6:
+ case caNumber7:
+ case caNumber8:
+ case caNumber9:
+ {
+ NumberClicked(a_Player, a_SlotNum, a_ClickAction);
+ return;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ 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;
+ }
+ cItem & DraggingItem = a_Player.GetDraggingItem();
+
+ if (DraggingItem.IsEmpty())
+ {
+ DraggingItem = Slot;
+ Slot.Empty();
+ }
+ else if (Slot.IsEmpty())
+ {
+ if (!IsPlaceableItem(DraggingItem.m_ItemType))
+ {
+ return;
+ }
+
+ Slot = DraggingItem.CopyOne();
+ DraggingItem.m_ItemCount -= 1;
+ if (DraggingItem.m_ItemCount <= 0)
+ {
+ DraggingItem.Empty();
+ }
+ }
+ else if (DraggingItem.m_ItemCount == 1)
+ {
+ if (!IsPlaceableItem(DraggingItem.m_ItemCount))
+ {
+ return;
+ }
+
+ // Switch contents
+ cItem tmp(DraggingItem);
+ DraggingItem = Slot;
+ Slot = tmp;
+ }
+
+ SetSlot(a_SlotNum, a_Player, Slot);
+ if (bAsync)
+ {
+ m_ParentWindow.BroadcastWholeWindow();
+ }
+}
+
+
+
+
+
+void cSlotAreaBeacon::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots)
+{
+ const cItem * Slot = GetSlot(0, a_Player);
+ if (!Slot->IsEmpty() || !IsPlaceableItem(a_ItemStack.m_ItemType) || (a_ItemStack.m_ItemCount != 1))
+ {
+ return;
+ }
+
+ if (a_ShouldApply)
+ {
+ SetSlot(0, a_Player, a_ItemStack.CopyOne());
+ }
+ a_ItemStack.Empty();
+}
+
+
+
+
+
+const cItem * cSlotAreaBeacon::GetSlot(int a_SlotNum, cPlayer & a_Player) const
+{
+ UNUSED(a_Player);
+ return &(m_Beacon->GetSlot(a_SlotNum));
+}
+
+
+
+
+
+void cSlotAreaBeacon::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
+{
+ UNUSED(a_Player);
+ m_Beacon->SetSlot(a_SlotNum, a_Item);
+}
+
+
+
+
+
+void cSlotAreaBeacon::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_Beacon->GetContents()));
+
+ m_ParentWindow.BroadcastWholeWindow();
+}
+
+
+
+
////////////////////////////////////////////////////////////////////////////////
// cSlotAreaEnchanting:
diff --git a/src/UI/SlotArea.h b/src/UI/SlotArea.h
index fa842bb81..9a96f2f3c 100644
--- a/src/UI/SlotArea.h
+++ b/src/UI/SlotArea.h
@@ -15,6 +15,7 @@
class cWindow;
class cPlayer;
+class cBeaconEntity;
class cChestEntity;
class cDropSpenserEntity;
class cEnderChestEntity;
@@ -314,6 +315,34 @@ protected:
+class cSlotAreaBeacon :
+ public cSlotArea,
+ public cItemGrid::cListener
+{
+ typedef cSlotArea super;
+
+public:
+ cSlotAreaBeacon(cBeaconEntity * a_Beacon, cWindow & a_ParentWindow);
+ virtual ~cSlotAreaBeacon();
+
+ bool IsPlaceableItem(short a_ItemType);
+
+ 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) 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:
+ cBeaconEntity * m_Beacon;
+
+ // cItemGrid::cListener overrides:
+ virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override;
+} ;
+
+
+
+
+
class cSlotAreaEnchanting :
public cSlotAreaTemporary
{
diff --git a/src/UI/Window.cpp b/src/UI/Window.cpp
index 4731f282b..8f4913030 100644
--- a/src/UI/Window.cpp
+++ b/src/UI/Window.cpp
@@ -9,6 +9,7 @@
#include "../Entities/Pickup.h"
#include "../Inventory.h"
#include "../Items/ItemHandler.h"
+#include "../BlockEntities/BeaconEntity.h"
#include "../BlockEntities/ChestEntity.h"
#include "../BlockEntities/DropSpenserEntity.h"
#include "../BlockEntities/EnderChestEntity.h"
@@ -841,6 +842,36 @@ void cAnvilWindow::GetBlockPos(int & a_PosX, int & a_PosY, int & a_PosZ)
////////////////////////////////////////////////////////////////////////////////
+// cBeaconWindow:
+
+cBeaconWindow::cBeaconWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cBeaconEntity * a_Beacon) :
+ cWindow(wtBeacon, "Beacon"),
+ m_Beacon(a_Beacon)
+{
+ m_ShouldDistributeToHotbarFirst = true;
+ m_SlotAreas.push_back(new cSlotAreaBeacon(m_Beacon, *this));
+ m_SlotAreas.push_back(new cSlotAreaInventory(*this));
+ m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
+}
+
+
+
+
+
+void cBeaconWindow::OpenedByPlayer(cPlayer & a_Player)
+{
+ super::OpenedByPlayer(a_Player);
+
+ a_Player.GetClientHandle()->SendWindowProperty(*this, 0, m_Beacon->GetBeaconLevel());
+ a_Player.GetClientHandle()->SendWindowProperty(*this, 1, m_Beacon->GetPrimaryEffect());
+ a_Player.GetClientHandle()->SendWindowProperty(*this, 2, m_Beacon->GetSecondaryEffect());
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
// cEnchantingWindow:
cEnchantingWindow::cEnchantingWindow(int a_BlockX, int a_BlockY, int a_BlockZ) :
diff --git a/src/UI/Window.h b/src/UI/Window.h
index 97db0ca88..9fb0e3b38 100644
--- a/src/UI/Window.h
+++ b/src/UI/Window.h
@@ -23,6 +23,7 @@ class cDropSpenserEntity;
class cEnderChestEntity;
class cFurnaceEntity;
class cHopperEntity;
+class cBeaconEntity;
class cSlotArea;
class cSlotAreaAnvil;
class cWorld;
@@ -258,6 +259,26 @@ protected:
+class cBeaconWindow :
+ public cWindow
+{
+ typedef cWindow super;
+public:
+ cBeaconWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cBeaconEntity * a_Beacon);
+
+ cBeaconEntity * GetBeaconEntity(void) const { return m_Beacon; }
+
+ // cWindow Overrides:
+ virtual void OpenedByPlayer(cPlayer & a_Player) override;
+
+protected:
+ cBeaconEntity * m_Beacon;
+} ;
+
+
+
+
+
class cEnchantingWindow :
public cWindow
{
diff --git a/src/World.cpp b/src/World.cpp
index 348498693..865ddfcc6 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -25,6 +25,7 @@
#include "Entities/TNTEntity.h"
#include "BlockEntities/CommandBlockEntity.h"
+#include "BlockEntities/BeaconEntity.h"
// Simulators:
#include "Simulator/SimulatorManager.h"
@@ -1232,6 +1233,15 @@ bool cWorld::DoWithBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBloc
+bool cWorld::DoWithBeaconAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBeaconCallback& a_Callback)
+{
+ return m_ChunkMap->DoWithBeaconAt(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);
@@ -3176,17 +3186,17 @@ void cWorld::SetChunkAlwaysTicked(int a_ChunkX, int a_ChunkZ, bool a_AlwaysTicke
cRedstoneSimulator * cWorld::InitializeRedstoneSimulator(cIniFile & a_IniFile)
{
- AString SimulatorName = a_IniFile.GetValueSet("Physics", "RedstoneSimulator", "");
+ AString SimulatorName = a_IniFile.GetValueSet("Physics", "RedstoneSimulator", "Incremental");
if (SimulatorName.empty())
{
- LOGWARNING("[Physics] RedstoneSimulator not present or empty in %s, using the default of \"incremental\".", GetIniFileName().c_str());
- SimulatorName = "incremental";
+ LOGWARNING("[Physics] RedstoneSimulator not present or empty in %s, using the default of \"Incremental\".", GetIniFileName().c_str());
+ SimulatorName = "Incremental";
}
cRedstoneSimulator * res = NULL;
- if (NoCaseCompare(SimulatorName, "incremental") == 0)
+ if (NoCaseCompare(SimulatorName, "Incremental") == 0)
{
res = new cIncrementalRedstoneSimulator(*this);
}
@@ -3210,7 +3220,7 @@ cFluidSimulator * cWorld::InitializeFluidSimulator(cIniFile & a_IniFile, const c
Printf(SimulatorNameKey, "%sSimulator", a_FluidName);
AString SimulatorSectionName;
Printf(SimulatorSectionName, "%sSimulator", a_FluidName);
- AString SimulatorName = a_IniFile.GetValueSet("Physics", SimulatorNameKey, "");
+ AString SimulatorName = a_IniFile.GetValueSet("Physics", SimulatorNameKey, "Vanilla");
if (SimulatorName.empty())
{
LOGWARNING("[Physics] %s not present or empty in %s, using the default of \"Vanilla\".", SimulatorNameKey.c_str(), GetIniFileName().c_str());
diff --git a/src/World.h b/src/World.h
index 4d0ccf2dd..90b798e8e 100644
--- a/src/World.h
+++ b/src/World.h
@@ -41,6 +41,7 @@ class cEntity;
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 cChestEntity;
class cDispenserEntity;
class cFlowerPotEntity;
@@ -59,6 +60,7 @@ typedef std::vector<cSetChunkDataPtr> cSetChunkDataPtrs;
typedef cItemCallback<cPlayer> cPlayerListCallback;
typedef cItemCallback<cEntity> cEntityCallback;
+typedef cItemCallback<cBeaconEntity> cBeaconCallback;
typedef cItemCallback<cChestEntity> cChestCallback;
typedef cItemCallback<cDispenserEntity> cDispenserCallback;
typedef cItemCallback<cFurnaceEntity> cFurnaceCallback;
@@ -523,6 +525,9 @@ public:
/** Calls the callback for the block entity at the specified coords; returns false if there's no block entity at those coords, true if found */
virtual bool DoWithBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBlockEntityCallback & a_Callback) override; // Exported in ManualBindings.cpp
+ /** 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 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 4857da1b6..601cd8833 100644
--- a/src/WorldStorage/NBTChunkSerializer.cpp
+++ b/src/WorldStorage/NBTChunkSerializer.cpp
@@ -10,6 +10,7 @@
#include "../StringCompression.h"
#include "FastNBT.h"
+#include "../BlockEntities/BeaconEntity.h"
#include "../BlockEntities/ChestEntity.h"
#include "../BlockEntities/CommandBlockEntity.h"
#include "../BlockEntities/DispenserEntity.h"
@@ -176,6 +177,23 @@ void cNBTChunkSerializer::AddBasicTileEntity(cBlockEntity * a_Entity, const char
+void cNBTChunkSerializer::AddBeaconEntity(cBeaconEntity * a_Entity)
+{
+ m_Writer.BeginCompound("");
+ AddBasicTileEntity(a_Entity, "Beacon");
+ m_Writer.AddInt("Levels", a_Entity->GetBeaconLevel());
+ m_Writer.AddInt("Primary", (int)a_Entity->GetPrimaryEffect());
+ m_Writer.AddInt("Secondary", (int)a_Entity->GetSecondaryEffect());
+ m_Writer.BeginList("Items", TAG_Compound);
+ AddItemGrid(a_Entity->GetContents());
+ m_Writer.EndList();
+ m_Writer.EndCompound();
+}
+
+
+
+
+
void cNBTChunkSerializer::AddChestEntity(cChestEntity * a_Entity, BLOCKTYPE a_ChestType)
{
m_Writer.BeginCompound("");
@@ -825,6 +843,7 @@ void cNBTChunkSerializer::BlockEntity(cBlockEntity * a_Entity)
// Add tile-entity into NBT:
switch (a_Entity->GetBlockType())
{
+ case E_BLOCK_BEACON: AddBeaconEntity ((cBeaconEntity *) a_Entity); break;
case E_BLOCK_CHEST: AddChestEntity ((cChestEntity *) a_Entity, a_Entity->GetBlockType()); break;
case E_BLOCK_COMMAND_BLOCK: AddCommandBlockEntity((cCommandBlockEntity *)a_Entity); break;
case E_BLOCK_DISPENSER: AddDispenserEntity ((cDispenserEntity *) a_Entity); break;
diff --git a/src/WorldStorage/NBTChunkSerializer.h b/src/WorldStorage/NBTChunkSerializer.h
index 710a06a70..4c229a65c 100644
--- a/src/WorldStorage/NBTChunkSerializer.h
+++ b/src/WorldStorage/NBTChunkSerializer.h
@@ -20,6 +20,7 @@ class cFastNBTWriter;
class cEntity;
class cBlockEntity;
class cBoat;
+class cBeaconEntity;
class cChestEntity;
class cCommandBlockEntity;
class cDispenserEntity;
@@ -93,6 +94,7 @@ protected:
// Block entities:
void AddBasicTileEntity(cBlockEntity * a_Entity, const char * a_EntityTypeID);
+ void AddBeaconEntity (cBeaconEntity * a_Entity);
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 71ff3ef99..d3a156ee1 100644
--- a/src/WorldStorage/WSSAnvil.cpp
+++ b/src/WorldStorage/WSSAnvil.cpp
@@ -16,6 +16,7 @@
#include "../StringCompression.h"
#include "../SetChunkData.h"
+#include "../BlockEntities/BeaconEntity.h"
#include "../BlockEntities/ChestEntity.h"
#include "../BlockEntities/CommandBlockEntity.h"
#include "../BlockEntities/DispenserEntity.h"
@@ -582,7 +583,11 @@ void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, con
{
continue;
}
- if (strncmp(a_NBT.GetData(sID), "Chest", a_NBT.GetDataLength(sID)) == 0)
+ if (strncmp(a_NBT.GetData(sID), "Beacon", a_NBT.GetDataLength(sID)) == 0)
+ {
+ LoadBeaconFromNBT(a_BlockEntities, a_NBT, Child);
+ }
+ else if (strncmp(a_NBT.GetData(sID), "Chest", a_NBT.GetDataLength(sID)) == 0)
{
LoadChestFromNBT(a_BlockEntities, a_NBT, Child, E_BLOCK_CHEST);
}
@@ -746,6 +751,49 @@ void cWSSAnvil::LoadItemGridFromNBT(cItemGrid & a_ItemGrid, const cParsedNBT & a
+void cWSSAnvil::LoadBeaconFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
+ int x, y, z;
+ if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
+ {
+ return;
+ }
+
+ std::auto_ptr<cBeaconEntity> Beacon(new cBeaconEntity(x, y, z, m_World));
+
+ int CurrentLine = a_NBT.FindChildByName(a_TagIdx, "Levels");
+ if (CurrentLine >= 0)
+ {
+ Beacon->SetBeaconLevel((char)a_NBT.GetInt(CurrentLine));
+ }
+
+ CurrentLine = a_NBT.FindChildByName(a_TagIdx, "Primary");
+ if (CurrentLine >= 0)
+ {
+ Beacon->SetPrimaryEffect((cEntityEffect::eType)a_NBT.GetInt(CurrentLine));
+ }
+
+ CurrentLine = a_NBT.FindChildByName(a_TagIdx, "Secondary");
+ if (CurrentLine >= 0)
+ {
+ Beacon->SetSecondaryEffect((cEntityEffect::eType)a_NBT.GetInt(CurrentLine));
+ }
+
+ // We are better than mojang, we load/save the beacon inventory!
+ int Items = a_NBT.FindChildByName(a_TagIdx, "Items");
+ if ((Items >= 0) && (a_NBT.GetType(Items) == TAG_List))
+ {
+ LoadItemGridFromNBT(Beacon->GetContents(), a_NBT, Items);
+ }
+
+ a_BlockEntities.push_back(Beacon.release());
+}
+
+
+
+
+
void cWSSAnvil::LoadChestFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE a_ChestType)
{
ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
diff --git a/src/WorldStorage/WSSAnvil.h b/src/WorldStorage/WSSAnvil.h
index 9f8714404..f8eeb8247 100644
--- a/src/WorldStorage/WSSAnvil.h
+++ b/src/WorldStorage/WSSAnvil.h
@@ -133,6 +133,7 @@ protected:
*/
void LoadItemGridFromNBT(cItemGrid & a_ItemGrid, const cParsedNBT & a_NBT, int a_ItemsTagIdx, int s_SlotOffset = 0);
+ void LoadBeaconFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadChestFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE a_ChestType);
void LoadDispenserFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadDropperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
diff --git a/src/WorldStorage/WSSCompact.cpp b/src/WorldStorage/WSSCompact.cpp
index ee47047a0..58f9e3cab 100644
--- a/src/WorldStorage/WSSCompact.cpp
+++ b/src/WorldStorage/WSSCompact.cpp
@@ -9,6 +9,7 @@
#include "zlib/zlib.h"
#include "json/json.h"
#include "../StringCompression.h"
+#include "../BlockEntities/BeaconEntity.h"
#include "../BlockEntities/ChestEntity.h"
#include "../BlockEntities/CommandBlockEntity.h"
#include "../BlockEntities/DispenserEntity.h"
@@ -75,6 +76,7 @@ void cJsonChunkSerializer::BlockEntity(cBlockEntity * a_BlockEntity)
const char * SaveInto = NULL;
switch (a_BlockEntity->GetBlockType())
{
+ case E_BLOCK_BEACON: SaveInto = "Beacons"; break;
case E_BLOCK_CHEST: SaveInto = "Chests"; break;
case E_BLOCK_DISPENSER: SaveInto = "Dispensers"; break;
case E_BLOCK_DROPPER: SaveInto = "Droppers"; break;
@@ -268,6 +270,24 @@ bool cWSSCompact::EraseChunkData(const cChunkCoords & a_Chunk)
void cWSSCompact::LoadEntitiesFromJson(Json::Value & a_Value, cEntityList & a_Entities, cBlockEntityList & a_BlockEntities, cWorld * a_World)
{
+ // Load beacon:
+ Json::Value AllBeacons = a_Value.get("Beacons", Json::nullValue);
+ if (!AllBeacons.empty())
+ {
+ for (Json::Value::iterator itr = AllBeacons.begin(); itr != AllBeacons.end(); ++itr)
+ {
+ std::auto_ptr<cBeaconEntity> BeaconEntity(new cBeaconEntity(0, 0, 0, a_World));
+ if (!BeaconEntity->LoadFromJson(*itr))
+ {
+ LOGWARNING("ERROR READING BEACON FROM JSON!");
+ }
+ else
+ {
+ a_BlockEntities.push_back(BeaconEntity.release());
+ }
+ } // for itr - AllBeacons[]
+ }
+
// Load chests:
Json::Value AllChests = a_Value.get("Chests", Json::nullValue);
if (!AllChests.empty())