From 347e80bdd88f894394373745859f405dc82c1819 Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Sat, 7 Dec 2013 00:18:58 +0000 Subject: Added basic ender chests Note that they just mirror chests now, so no per player inventory. --- VC2013/MCServer.vcxproj | 2 + VC2013/MCServer.vcxproj.filters | 6 ++ src/BlockEntities/BlockEntity.cpp | 22 +++--- src/BlockEntities/EnderChestEntity.cpp | 130 +++++++++++++++++++++++++++++++++ src/BlockEntities/EnderChestEntity.h | 59 +++++++++++++++ src/Blocks/BlockEnderchest.h | 49 ++++++++++++- src/Blocks/BlockFurnace.h | 1 - src/Chunk.cpp | 2 + src/UI/SlotArea.cpp | 33 +++++++++ src/UI/SlotArea.h | 18 +++++ src/UI/Window.cpp | 38 ++++++++++ src/UI/Window.h | 17 +++++ 12 files changed, 363 insertions(+), 14 deletions(-) create mode 100644 src/BlockEntities/EnderChestEntity.cpp create mode 100644 src/BlockEntities/EnderChestEntity.h diff --git a/VC2013/MCServer.vcxproj b/VC2013/MCServer.vcxproj index 9c640f6dd..098253ac3 100644 --- a/VC2013/MCServer.vcxproj +++ b/VC2013/MCServer.vcxproj @@ -224,6 +224,7 @@ + @@ -615,6 +616,7 @@ + diff --git a/VC2013/MCServer.vcxproj.filters b/VC2013/MCServer.vcxproj.filters index be0a37dc1..4e22f5517 100644 --- a/VC2013/MCServer.vcxproj.filters +++ b/VC2013/MCServer.vcxproj.filters @@ -945,6 +945,9 @@ Source Files\HTTPServer + + Source Files\BlockEntities + @@ -1654,6 +1657,9 @@ Source Files\HTTPServer + + Source Files\BlockEntities + diff --git a/src/BlockEntities/BlockEntity.cpp b/src/BlockEntities/BlockEntity.cpp index 41a488717..5d7e4a37a 100644 --- a/src/BlockEntities/BlockEntity.cpp +++ b/src/BlockEntities/BlockEntity.cpp @@ -8,6 +8,7 @@ #include "ChestEntity.h" #include "DispenserEntity.h" #include "DropperEntity.h" +#include "EnderChestEntity.h" #include "FurnaceEntity.h" #include "HopperEntity.h" #include "JukeboxEntity.h" @@ -22,16 +23,17 @@ cBlockEntity * cBlockEntity::CreateByBlockType(BLOCKTYPE a_BlockType, NIBBLETYPE { switch (a_BlockType) { - case E_BLOCK_CHEST: return new cChestEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); - case E_BLOCK_DISPENSER: return new cDispenserEntity(a_BlockX, a_BlockY, a_BlockZ, a_World); - case E_BLOCK_DROPPER: return new cDropperEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); - case E_BLOCK_LIT_FURNACE: return new cFurnaceEntity (a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_World); - case E_BLOCK_FURNACE: return new cFurnaceEntity (a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_World); - case E_BLOCK_HOPPER: return new cHopperEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); - case E_BLOCK_SIGN_POST: return new cSignEntity (a_BlockType, a_BlockX, a_BlockY, a_BlockZ, a_World); - case E_BLOCK_WALLSIGN: return new cSignEntity (a_BlockType, a_BlockX, a_BlockY, a_BlockZ, a_World); - case E_BLOCK_NOTE_BLOCK: return new cNoteEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); - case E_BLOCK_JUKEBOX: return new cJukeboxEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); + case E_BLOCK_CHEST: return new cChestEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); + case E_BLOCK_DISPENSER: return new cDispenserEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); + case E_BLOCK_DROPPER: return new cDropperEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); + case E_BLOCK_ENDER_CHEST: return new cEnderChestEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); + case E_BLOCK_LIT_FURNACE: return new cFurnaceEntity (a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_World); + case E_BLOCK_FURNACE: return new cFurnaceEntity (a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_World); + case E_BLOCK_HOPPER: return new cHopperEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); + case E_BLOCK_SIGN_POST: return new cSignEntity (a_BlockType, a_BlockX, a_BlockY, a_BlockZ, a_World); + case E_BLOCK_WALLSIGN: return new cSignEntity (a_BlockType, a_BlockX, a_BlockY, a_BlockZ, a_World); + case E_BLOCK_NOTE_BLOCK: return new cNoteEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); + case E_BLOCK_JUKEBOX: return new cJukeboxEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); } LOGD("%s: Requesting creation of an unknown block entity - block type %d (%s)", __FUNCTION__, a_BlockType, ItemTypeToString(a_BlockType).c_str() diff --git a/src/BlockEntities/EnderChestEntity.cpp b/src/BlockEntities/EnderChestEntity.cpp new file mode 100644 index 000000000..e53930798 --- /dev/null +++ b/src/BlockEntities/EnderChestEntity.cpp @@ -0,0 +1,130 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "EnderChestEntity.h" +#include "../Item.h" +#include "../Entities/Player.h" +#include "../UI/Window.h" +#include "json/json.h" + + + + + +cEnderChestEntity::cEnderChestEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : + super(E_BLOCK_ENDER_CHEST, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World) +{ + cBlockEntityWindowOwner::SetBlockEntity(this); +} + + + + + +cEnderChestEntity::~cEnderChestEntity() +{ + cWindow * Window = GetWindow(); + if (Window != NULL) + { + Window->OwnerDestroyed(); + } +} + + + + + +bool cEnderChestEntity::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++; + } + return true; +} + + + + + +void cEnderChestEntity::SaveToJson(Json::Value & a_Value) +{ + a_Value["x"] = m_PosX; + a_Value["y"] = m_PosY; + a_Value["z"] = m_PosZ; + + Json::Value AllSlots; + for (int i = m_Contents.GetNumSlots() - 1; i >= 0; i--) + { + Json::Value Slot; + m_Contents.GetSlot(i).GetJson(Slot); + AllSlots.append(Slot); + } + a_Value["Slots"] = AllSlots; +} + + + + + +void cEnderChestEntity::SendTo(cClientHandle & a_Client) +{ + // The chest entity doesn't need anything sent to the client when it's created / gets in the viewdistance + // All the actual handling is in the cWindow UI code that gets called when the chest is rclked + + UNUSED(a_Client); +} + + + + + +void cEnderChestEntity::UsedBy(cPlayer * a_Player) +{ + // If the window is not created, open it anew: + cWindow * Window = GetWindow(); + if (Window == NULL) + { + OpenNewWindow(); + Window = GetWindow(); + } + + // Open the window for the player: + if (Window != NULL) + { + if (a_Player->GetWindow() != Window) + { + a_Player->OpenWindow(Window); + } + } + + // This is rather a hack + // Instead of marking the chunk as dirty upon chest contents change, we mark it dirty now + // We cannot properly detect contents change, but such a change doesn't happen without a player opening the chest first. + // The few false positives aren't much to worry about + int ChunkX, ChunkZ; + cChunkDef::BlockToChunk(m_PosX, m_PosZ, ChunkX, ChunkZ); + m_World->MarkChunkDirty(ChunkX, ChunkZ); +} + + + + + +void cEnderChestEntity::OpenNewWindow(void) +{ + OpenWindow(new cEnderChestWindow(this)); +} + + + + diff --git a/src/BlockEntities/EnderChestEntity.h b/src/BlockEntities/EnderChestEntity.h new file mode 100644 index 000000000..683b652b2 --- /dev/null +++ b/src/BlockEntities/EnderChestEntity.h @@ -0,0 +1,59 @@ + +#pragma once + +#include "BlockEntityWithItems.h" +#include "../UI/WindowOwner.h" + + + + + +namespace Json +{ + class Value; +}; + +class cClientHandle; +class cServer; +class cNBTData; + + + + + +class cEnderChestEntity : // tolua_export + public cBlockEntityWindowOwner, + // tolua_begin + public cBlockEntityWithItems +{ + typedef cBlockEntityWithItems super; + +public: + enum { + ContentsHeight = 3, + ContentsWidth = 9, + } ; + + // tolua_end + + /// Constructor used for normal operation + cEnderChestEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); + + virtual ~cEnderChestEntity(); + + static const char * GetClassStatic(void) { return "cEnderChestEntity"; } + + 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; + + /// Opens a new chest window for this chest. Scans for neighbors to open a double chest window, if appropriate. + void OpenNewWindow(void); +} ; // tolua_export + + + + diff --git a/src/Blocks/BlockEnderchest.h b/src/Blocks/BlockEnderchest.h index 0ce813f1c..50d8e38e0 100644 --- a/src/Blocks/BlockEnderchest.h +++ b/src/Blocks/BlockEnderchest.h @@ -1,18 +1,18 @@ #pragma once -#include "BlockHandler.h" +#include "BlockEntity.h" class cBlockEnderchestHandler : - public cBlockHandler + public cBlockEntityHandler { public: cBlockEnderchestHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) + : cBlockEntityHandler(a_BlockType) { } @@ -21,6 +21,49 @@ public: //todo: Drop Ender Chest if using silk touch pickaxe a_Pickups.push_back(cItem(E_BLOCK_OBSIDIAN, 8, 0)); } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + a_BlockMeta = RotationToMetaData(a_Player->GetRotation()); + return true; + } + + virtual const char * GetStepSound(void) override + { + return "step.stone"; + } + + static NIBBLETYPE RotationToMetaData(double a_Rotation) + { + a_Rotation += 90 + 45; // So its not aligned with axis + + if (a_Rotation > 360.f) + { + a_Rotation -= 360.f; + } + if ((a_Rotation >= 0.f) && (a_Rotation < 90.f)) + { + return 0x4; + } + else if ((a_Rotation >= 180) && (a_Rotation < 270)) + { + return 0x5; + } + else if ((a_Rotation >= 90) && (a_Rotation < 180)) + { + return 0x2; + } + else + { + return 0x3; + } + } } ; diff --git a/src/Blocks/BlockFurnace.h b/src/Blocks/BlockFurnace.h index fe35893d5..5b067d7b7 100644 --- a/src/Blocks/BlockFurnace.h +++ b/src/Blocks/BlockFurnace.h @@ -4,7 +4,6 @@ #include "BlockEntity.h" #include "../World.h" #include "../Piston.h" -#include "../Entities/Player.h" diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 1c03753c7..4eb11718d 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -1301,6 +1301,7 @@ void cChunk::CreateBlockEntities(void) case E_BLOCK_CHEST: case E_BLOCK_DISPENSER: case E_BLOCK_DROPPER: + case E_BLOCK_ENDER_CHEST: case E_BLOCK_LIT_FURNACE: case E_BLOCK_FURNACE: case E_BLOCK_HOPPER: @@ -1413,6 +1414,7 @@ void cChunk::SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, case E_BLOCK_CHEST: case E_BLOCK_DISPENSER: case E_BLOCK_DROPPER: + case E_BLOCK_ENDER_CHEST: case E_BLOCK_LIT_FURNACE: case E_BLOCK_FURNACE: case E_BLOCK_HOPPER: diff --git a/src/UI/SlotArea.cpp b/src/UI/SlotArea.cpp index 7fd7cd996..e743f4bb3 100644 --- a/src/UI/SlotArea.cpp +++ b/src/UI/SlotArea.cpp @@ -8,6 +8,7 @@ #include "../Entities/Player.h" #include "../BlockEntities/ChestEntity.h" #include "../BlockEntities/DropSpenserEntity.h" +#include "../BlockEntities/EnderChestEntity.h" #include "../BlockEntities/FurnaceEntity.h" #include "../Items/ItemHandler.h" #include "Window.h" @@ -556,6 +557,38 @@ cCraftingRecipe & cSlotAreaCrafting::GetRecipeForPlayer(cPlayer & a_Player) +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSlotAreaEnderChest: + +cSlotAreaEnderChest::cSlotAreaEnderChest(cEnderChestEntity * a_EnderChest, cWindow & a_ParentWindow) : + cSlotArea(27, a_ParentWindow), + m_EnderChest(a_EnderChest) +{ +} + + + + + +const cItem * cSlotAreaEnderChest::GetSlot(int a_SlotNum, cPlayer & a_Player) const +{ + // a_SlotNum ranges from 0 to 26, use that to index the chest entity's inventory directly: + return &(m_EnderChest->GetSlot(a_SlotNum)); +} + + + + + +void cSlotAreaEnderChest::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) +{ + m_EnderChest->SetSlot(a_SlotNum, a_Item); +} + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cSlotAreaFurnace: diff --git a/src/UI/SlotArea.h b/src/UI/SlotArea.h index b1944d901..d31c87e0c 100644 --- a/src/UI/SlotArea.h +++ b/src/UI/SlotArea.h @@ -16,6 +16,7 @@ class cWindow; class cPlayer; class cChestEntity; class cDropSpenserEntity; +class cEnderChestEntity; class cFurnaceEntity; class cCraftingRecipe; @@ -286,6 +287,23 @@ protected: +class cSlotAreaEnderChest : + public cSlotArea +{ +public: + cSlotAreaEnderChest(cEnderChestEntity * a_EnderChest, cWindow & a_ParentWindow); + + 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: + cEnderChestEntity * m_EnderChest; +}; + + + + + class cSlotAreaFurnace : public cSlotArea, public cItemGrid::cListener diff --git a/src/UI/Window.cpp b/src/UI/Window.cpp index f5c62692f..5ca31fa3e 100644 --- a/src/UI/Window.cpp +++ b/src/UI/Window.cpp @@ -11,6 +11,7 @@ #include "../Items/ItemHandler.h" #include "../BlockEntities/ChestEntity.h" #include "../BlockEntities/DropSpenserEntity.h" +#include "../BlockEntities/EnderChestEntity.h" #include "../BlockEntities/HopperEntity.h" @@ -853,6 +854,43 @@ cDropSpenserWindow::cDropSpenserWindow(int a_BlockX, int a_BlockY, int a_BlockZ, +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cEnderChestWindow: + +cEnderChestWindow::cEnderChestWindow(cEnderChestEntity * a_EnderChest) : + cWindow(wtChest, "Ender Chest"), + m_World(a_EnderChest->GetWorld()), + m_BlockX(a_EnderChest->GetPosX()), + m_BlockY(a_EnderChest->GetPosY()), + m_BlockZ(a_EnderChest->GetPosZ()) +{ + m_SlotAreas.push_back(new cSlotAreaEnderChest(a_EnderChest, *this)); + m_SlotAreas.push_back(new cSlotAreaInventory(*this)); + m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); + + // Play the opening sound: + m_World->BroadcastSoundEffect("random.chestopen", m_BlockX * 8, m_BlockY * 8, m_BlockZ * 8, 1, 1); + + // Send out the chest-open packet: + m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 1, E_BLOCK_ENDER_CHEST); +} + + + + + +cEnderChestWindow::~cEnderChestWindow() +{ + // Send out the chest-close packet: + m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 0, E_BLOCK_ENDER_CHEST); + + m_World->BroadcastSoundEffect("random.chestclosed", m_BlockX * 8, m_BlockY * 8, m_BlockZ * 8, 1, 1); +} + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cHopperWindow: diff --git a/src/UI/Window.h b/src/UI/Window.h index c44b900d7..030182888 100644 --- a/src/UI/Window.h +++ b/src/UI/Window.h @@ -20,6 +20,7 @@ class cWindowOwner; class cClientHandle; class cChestEntity; class cDropSpenserEntity; +class cEnderChestEntity; class cFurnaceEntity; class cHopperEntity; class cSlotArea; @@ -283,6 +284,22 @@ protected: +class cEnderChestWindow : + public cWindow +{ +public: + cEnderChestWindow(cEnderChestEntity * a_EnderChest); + ~cEnderChestWindow(); + +protected: + cWorld * m_World; + int m_BlockX, m_BlockY, m_BlockZ; // Position of the enderchest, for the window-close packet +}; + + + + + class cInventoryWindow : public cWindow { -- cgit v1.2.3