diff options
Diffstat (limited to 'src')
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()) |