diff --git a/src/BlockEntities/BeaconEntity.cpp b/src/BlockEntities/BeaconEntity.cpp index 4b9662797..af6c124c0 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_PrimaryPotion(cEntityEffect::effNoEffect) + , m_SecondaryPotion(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,122 @@ 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::IsValidPotion(cEntityEffect::eType a_Potion, char a_BeaconLevel) +{ + if (a_Potion == cEntityEffect::effNoEffect) + { + return true; + } + + switch (a_BeaconLevel) + { + case 4: + { + // Beacon level 4 + if (a_Potion == cEntityEffect::effRegeneration) + { + return true; + } + } + case 3: + { + // Beacon level 3 + if (a_Potion == cEntityEffect::effStrength) + { + return true; + } + } + case 2: + { + // Beacon level 2 + switch (a_Potion) + { + case cEntityEffect::effResistance: + case cEntityEffect::effJumpBoost: + { + return true; + } + } + } + case 1: + { + // Beacon level 1 + switch (a_Potion) + { + case cEntityEffect::effSpeed: + case cEntityEffect::effHaste: + { + return true; + } + } + } + } + return false; +} + + + + + +bool cBeaconEntity::SelectPrimaryPotion(cEntityEffect::eType a_Potion) +{ + LOG("SelectPrimaryPotion!"); + if (!IsValidPotion(a_Potion, m_BeaconLevel)) + { + LOG("FALLSE!"); + return false; + } + + m_PrimaryPotion = a_Potion; + return true; +} + + + + + +bool cBeaconEntity::SelectSecondaryPotion(cEntityEffect::eType a_Potion) +{ + if (!IsValidPotion(a_Potion, m_BeaconLevel)) + { + return false; + } + + m_SecondaryPotion = a_Potion; + return true; +} + + + + + +bool cBeaconEntity::IsBeaconBlocked(void) +{ + bool IsBlocked = false; + for (int Y = m_PosY; Y < cChunkDef::Height; ++Y) + { + BLOCKTYPE Block = m_World->GetBlock(m_PosX, Y, m_PosZ); + if (!cBlockInfo::IsTransparent(Block)) + { + IsBlocked = true; + break; + } + } + return IsBlocked; } @@ -83,8 +189,102 @@ bool cBeaconEntity::IsMineralBlock(BLOCKTYPE a_BlockType) +void cBeaconEntity::UpdateBeacon(void) +{ + if (IsBeaconBlocked()) + { + m_IsActive = false; + m_BeaconLevel = 0; + } + else + { + m_BeaconLevel = CalculatePyramidLevel(); + m_IsActive = (m_BeaconLevel > 0); + } + + // 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_PrimaryPotion == m_SecondaryPotion)) + { + EffectLevel = 1; + } + + cEntityEffect::eType SecondaryPotion = cEntityEffect::effNoEffect; + if ((m_BeaconLevel >= 4) && (m_PrimaryPotion != m_SecondaryPotion) && (m_SecondaryPotion > 0)) + { + SecondaryPotion = m_SecondaryPotion; + } + + class cPlayerCallback : public cPlayerListCallback + { + int m_Radius; + int m_PosX, m_PosY, m_PosZ; + cEntityEffect::eType m_PrimaryPotion, m_SecondaryPotion; + 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_PrimaryPotion, 180, m_EffectLevel); + + if (m_SecondaryPotion != cEntityEffect::effNoEffect) + { + a_Player->AddEntityEffect(m_SecondaryPotion, 180, 0); + } + } + return false; + } + + public: + cPlayerCallback(int a_Radius, int a_PosX, int a_PosY, int a_PosZ, cEntityEffect::eType a_PrimaryPotion, cEntityEffect::eType a_SecondaryPotion, short a_EffectLevel) + : m_Radius(a_Radius) + , m_PosX(a_PosX) + , m_PosY(a_PosY) + , m_PosZ(a_PosZ) + , m_PrimaryPotion(a_PrimaryPotion) + , m_SecondaryPotion(a_SecondaryPotion) + , m_EffectLevel(a_EffectLevel) + {}; + + } PlayerCallback(Radius, m_PosX, m_PosY, m_PosZ, m_PrimaryPotion, SecondaryPotion, 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 +292,94 @@ bool cBeaconEntity::Tick(float a_Dt, cChunk & a_Chunk) -void cBeaconEntity::SaveToJson(Json::Value& a_Value) -{ -} - - - - -void cBeaconEntity::SendTo(cClientHandle & a_Client) -{ -} - - - - - 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); + } + } +} + + + + + +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 PrimaryPotion = a_Value.get("PrimaryPotion", 0).asInt(); + int SecondaryPotion = a_Value.get("SecondaryPotion", 0).asInt(); + + if ((PrimaryPotion >= 0) && (PrimaryPotion <= (int)cEntityEffect::effSaturation)) + { + m_PrimaryPotion = (cEntityEffect::eType)PrimaryPotion; + } + + if ((SecondaryPotion >= 0) && (SecondaryPotion <= (int)cEntityEffect::effSaturation)) + { + m_SecondaryPotion = (cEntityEffect::eType)SecondaryPotion; + } + + 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["PrimaryPotion"] = (int)m_PrimaryPotion; + a_Value["SecondaryPotion"] = (int)m_SecondaryPotion; +} + + + + + +void cBeaconEntity::SendTo(cClientHandle & a_Client) +{ + a_Client.SendUpdateBlockEntity(*this); } diff --git a/src/BlockEntities/BeaconEntity.h b/src/BlockEntities/BeaconEntity.h index ee1eda391..52111e82a 100644 --- a/src/BlockEntities/BeaconEntity.h +++ b/src/BlockEntities/BeaconEntity.h @@ -1,7 +1,7 @@ #pragma once -#include "BlockEntity.h" +#include "BlockEntityWithItems.h" @@ -17,26 +17,60 @@ namespace Json class cBeaconEntity : - public cBlockEntity + public cBlockEntityWithItems { - typedef cBlockEntity super; + typedef cBlockEntityWithItems super; public: - - /** The initial constructor */ 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); + + /** Is the beacon active? */ + bool IsActive(void) const { return m_IsActive; } + + /** Returns the beacon level. (0 - 4) */ + char GetBeaconLevel(void) const { return m_BeaconLevel; } + + char GetPrimaryPotion(void) const { return m_PrimaryPotion; } + char GetSecondaryPotion(void) const { return m_SecondaryPotion; } + + /** Select the primary potion. Returns false when the potion is invalid.*/ + bool SelectPrimaryPotion(cEntityEffect::eType a_Potion); + + /** Select the secondary potion. Returns false when the potion is invalid. */ + bool SelectSecondaryPotion(cEntityEffect::eType a_Potion); + + /** 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); /** 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); + + /** Returns true if the potion can be used. */ + static bool IsValidPotion(cEntityEffect::eType a_Potion, char a_BeaconLevel); + + /** Update the beacon. */ + void UpdateBeacon(void); + + /** Give the near-players the effects. */ + void GiveEffects(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 void UsedBy(cPlayer * a_Player) override; - virtual bool Tick(float a_Dt, cChunk & /* a_Chunk */) override; + 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; + +protected: + bool m_IsActive; + char m_BeaconLevel; + + cEntityEffect::eType m_PrimaryPotion, m_SecondaryPotion; + } ; 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/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/ChunkMap.cpp b/src/ChunkMap.cpp index 05d219918..38e0cd82d 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: diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 30ec737be..849de2ce1 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -31,6 +31,7 @@ #include "Items/ItemSword.h" #include "polarssl/md5.h" +#include "BlockEntities/BeaconEntity.h" @@ -659,6 +660,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()); @@ -746,6 +751,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 PrimaryPotionID, SecondaryPotionID; + Buffer.ReadBEInt(PrimaryPotionID); + Buffer.ReadBEInt(SecondaryPotionID); + + cEntityEffect::eType PrimaryPotion = cEntityEffect::effNoEffect; + if ((PrimaryPotionID >= 0) && (PrimaryPotionID <= (int)cEntityEffect::effSaturation)) + { + PrimaryPotion = (cEntityEffect::eType)PrimaryPotionID; + } + + cEntityEffect::eType SecondaryPotion = cEntityEffect::effNoEffect; + if ((SecondaryPotionID >= 0) && (SecondaryPotionID <= (int)cEntityEffect::effSaturation)) + { + SecondaryPotion = (cEntityEffect::eType)SecondaryPotionID; + } + + Window->SetSlot(*m_Player, 0, cItem()); + BeaconWindow->GetBeaconEntity()->SelectPrimaryPotion(PrimaryPotion); + BeaconWindow->GetBeaconEntity()->SelectSecondaryPotion(SecondaryPotion); +} + + + + + void cClientHandle::HandleCommandBlockMessage(const char * a_Data, size_t a_Length) { if (a_Length < 14) diff --git a/src/ClientHandle.h b/src/ClientHandle.h index 48eba4de1..f3f1da417 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -399,7 +399,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/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index 45d39e0e9..8a68edd1f 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.GetPrimaryPotion()); + Writer.AddInt("Secondary", BeaconEntity.GetSecondaryPotion()); + 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/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..aa129bfe8 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" @@ -840,6 +841,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->GetPrimaryPotion()); + a_Player.GetClientHandle()->SendWindowProperty(*this, 2, m_Beacon->GetSecondaryPotion()); +} + + + + + //////////////////////////////////////////////////////////////////////////////// // cEnchantingWindow: 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 {