From 03c6bb9f85b929a99df2c800b8ba7d8ef4a8ec43 Mon Sep 17 00:00:00 2001 From: "madmaxoft@gmail.com" Date: Thu, 13 Jun 2013 07:36:43 +0000 Subject: [PATCH] Added hopper entity, it can suck items out of chests, dispensers, droppers and other hopppers above it. git-svn-id: http://mc-server.googlecode.com/svn/trunk@1587 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- VC2008/MCServer.vcproj | 8 + source/BlockEntities/HopperEntity.cpp | 291 +++++++++++++++++++++ source/BlockEntities/HopperEntity.h | 81 ++++++ source/Chunk.cpp | 36 ++- source/Chunk.h | 5 +- source/ItemGrid.cpp | 10 +- source/UI/SlotArea.cpp | 20 ++ source/UI/SlotArea.h | 8 +- source/UI/Window.cpp | 53 ++++ source/UI/Window.h | 24 ++ source/WorldStorage/NBTChunkSerializer.cpp | 34 ++- source/WorldStorage/NBTChunkSerializer.h | 24 +- source/WorldStorage/WSSAnvil.cpp | 112 +++++--- source/WorldStorage/WSSAnvil.h | 5 +- 14 files changed, 630 insertions(+), 81 deletions(-) create mode 100644 source/BlockEntities/HopperEntity.cpp create mode 100644 source/BlockEntities/HopperEntity.h diff --git a/VC2008/MCServer.vcproj b/VC2008/MCServer.vcproj index bbc3cb663..83e9137e7 100644 --- a/VC2008/MCServer.vcproj +++ b/VC2008/MCServer.vcproj @@ -2356,6 +2356,14 @@ RelativePath="..\source\BlockEntities\FurnaceEntity.h" > + + + + diff --git a/source/BlockEntities/HopperEntity.cpp b/source/BlockEntities/HopperEntity.cpp new file mode 100644 index 000000000..68974fdd3 --- /dev/null +++ b/source/BlockEntities/HopperEntity.cpp @@ -0,0 +1,291 @@ + +// HopperEntity.cpp + +// Implements the cHopperEntity representing a hopper block entity + +#include "Globals.h" +#include "HopperEntity.h" +#include "../Chunk.h" +#include "../Player.h" +#include "ChestEntity.h" +#include "DropSpenserEntity.h" + + + + + +cHopperEntity::cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ) : + super(E_BLOCK_HOPPER, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, NULL) +{ +} + + + + + +cHopperEntity::cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : + super(E_BLOCK_HOPPER, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World) +{ +} + + + + + +bool cHopperEntity::Tick(float a_Dt, cChunk & a_Chunk) +{ + Int64 CurrentTick = a_Chunk.GetWorld()->GetWorldAge(); + + bool res = false; + res = MoveItemsIn (a_Chunk, CurrentTick) || res; + res = MovePickupsIn(a_Chunk, CurrentTick) || res; + res = MoveItemsOut (a_Chunk, CurrentTick) || res; + return res; +} + + + + + +void cHopperEntity::SaveToJson(Json::Value & a_Value) +{ + // TODO + LOGWARNING("%s: Not implemented yet", __FUNCTION__); +} + + + + + +void cHopperEntity::SendTo(cClientHandle & a_Client) +{ + // The hopper 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 hopper is rclked + + UNUSED(a_Client); +} + + + + + +void cHopperEntity::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_PosY, m_PosZ, ChunkX, ChunkZ); + m_World->MarkChunkDirty(ChunkX, ChunkZ); +} + + + + + +/// Opens a new window UI for this hopper +void cHopperEntity::OpenNewWindow(void) +{ + OpenWindow(new cHopperWindow(m_PosX, m_PosY, m_PosZ, this)); +} + + + + + +/// Moves items from the container above it into this hopper. Returns true if the contents have changed. +bool cHopperEntity::MoveItemsIn(cChunk & a_Chunk, Int64 a_CurrentTick) +{ + if (m_PosY >= cChunkDef::Height) + { + // This hopper is at the top of the world, no more blocks above + return false; + } + + if (a_CurrentTick - m_LastMoveItemsInTick < TICKS_PER_TRANSFER) + { + // Too early after the previous transfer + return false; + } + + // Try moving an item in: + bool res = false; + switch (a_Chunk.GetBlock(m_RelX, m_PosY + 1, m_RelZ)) + { + case E_BLOCK_CHEST: res = MoveItemsFromChest(a_Chunk); break; + case E_BLOCK_FURNACE: res = MoveItemsFromFurnace(a_Chunk); break; + case E_BLOCK_DISPENSER: + case E_BLOCK_DROPPER: res = MoveItemsFromGrid(((cDropSpenserEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ))->GetContents()); break; + case E_BLOCK_HOPPER: res = MoveItemsFromGrid(((cHopperEntity *) a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ))->GetContents()); break; + } + + // If the item has been moved, reset the last tick: + if (res) + { + m_LastMoveItemsInTick = a_CurrentTick; + } + + return res; +} + + + + + +/// Moves pickups from above this hopper into it. Returns true if the contents have changed. +bool cHopperEntity::MovePickupsIn(cChunk & a_Chunk, Int64 a_CurrentTick) +{ + // TODO + return false; +} + + + + + +/// Moves items out from this hopper into the destination. Returns true if the contents have changed. +bool cHopperEntity::MoveItemsOut(cChunk & a_Chunk, Int64 a_CurrentTick) +{ + if (a_CurrentTick - m_LastMoveItemsOutTick < TICKS_PER_TRANSFER) + { + // Too early after the previous transfer + return false; + } + + // TODO + return false; +} + + + + + +/// Moves items from a chest (dblchest) above the hopper into this hopper. Returns true if contents have changed. +bool cHopperEntity::MoveItemsFromChest(cChunk & a_Chunk) +{ + if (MoveItemsFromGrid(((cChestEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ))->GetContents())) + { + // Moved the item from the chest directly above the hopper + return true; + } + + // Check if the chest is a double-chest, if so, try to move from there: + static const struct + { + int x, z; + } + Coords [] = + { + {1, 0}, + {-1, 0}, + {0, 1}, + {0, -1}, + } ; + for (int i = 0; i < ARRAYCOUNT(Coords); i++) + { + int x = m_RelX + Coords[i].x; + int z = m_RelZ + Coords[i].z; + cChunk * Neighbor = a_Chunk.GetRelNeighborChunkAdjustCoords(x, z); + if ( + (Neighbor == NULL) || + (Neighbor->GetBlock(x, m_PosY + 1, z) != E_BLOCK_CHEST) + ) + { + continue; + } + if (MoveItemsFromGrid(((cChestEntity *)Neighbor->GetBlockEntity(x, m_PosY, z))->GetContents())) + { + return true; + } + return false; + } + + // The chest was single and nothing could be moved + return false; +} + + + + + +/// Moves items from a furnace above the hopper into this hopper. Returns true if contents have changed. +bool cHopperEntity::MoveItemsFromFurnace(cChunk & a_Chunk) +{ + // TODO + return false; +} + + + + + +/// Moves items from the specified ItemGrid into this hopper. Returns true if contents have changed. +bool cHopperEntity::MoveItemsFromGrid(cItemGrid & a_Grid) +{ + int NumSlots = a_Grid.GetNumSlots(); + + // First try adding items of types already in the hopper: + for (int i = 0; i < NumSlots; i++) + { + if (a_Grid.IsSlotEmpty(i)) + { + continue; + } + if (MoveItemsFromSlot(a_Grid.GetSlot(i), false)) + { + a_Grid.ChangeSlotCount(i, -1); + return true; + } + } + + // No already existing stack can be topped up, try again with allowing new stacks: + for (int i = 0; i < NumSlots; i++) + { + if (a_Grid.IsSlotEmpty(i)) + { + continue; + } + if (MoveItemsFromSlot(a_Grid.GetSlot(i), true)) + { + a_Grid.ChangeSlotCount(i, -1); + return true; + } + } + return false; +} + + + + + +/// Moves one of the specified itemstack into this hopper. Returns true if contents have changed. Doesn't change the itemstack. +bool cHopperEntity::MoveItemsFromSlot(const cItem & a_ItemStack, bool a_AllowNewStacks) +{ + if (m_Contents.AddItem(a_ItemStack.CopyOne(), a_AllowNewStacks) > 0) + { + return true; + } + return false; +} + + + + diff --git a/source/BlockEntities/HopperEntity.h b/source/BlockEntities/HopperEntity.h new file mode 100644 index 000000000..73c2a536c --- /dev/null +++ b/source/BlockEntities/HopperEntity.h @@ -0,0 +1,81 @@ + +// HopperEntity.h + +// Declares the cHopperEntity representing a hopper block entity + + + + + +#pragma once + +#include "BlockEntityWithItems.h" +#include "../UI/WindowOwner.h" + + + + + +class cHopperEntity : // tolua_export + public cBlockEntityWindowOwner, + // tolua_begin + public cBlockEntityWithItems +{ + typedef cBlockEntityWithItems super; + +public: + enum { + ContentsHeight = 1, + ContentsWidth = 5, + TICKS_PER_TRANSFER = 8, ///< How many ticks at minimum between two item transfers to or from the hopper + } ; + + /// Constructor used while generating a chunk; sets m_World to NULL + cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ); + + // tolua_end + + /// Constructor used for normal operation + cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); + + static const char * GetClassStatic(void) { return "cHopperEntity"; } + +protected: + + Int64 m_LastMoveItemsInTick; + Int64 m_LastMoveItemsOutTick; + + // cBlockEntity overrides: + 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 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); + + /// Moves items from the container above it into this hopper. Returns true if the contents have changed. + bool MoveItemsIn(cChunk & a_Chunk, Int64 a_CurrentTick); + + /// Moves pickups from above this hopper into it. Returns true if the contents have changed. + bool MovePickupsIn(cChunk & a_Chunk, Int64 a_CurrentTick); + + /// Moves items out from this hopper into the destination. Returns true if the contents have changed. + bool MoveItemsOut(cChunk & a_Chunk, Int64 a_CurrentTick); + + /// Moves items from a chest (dblchest) above the hopper into this hopper. Returns true if contents have changed. + bool MoveItemsFromChest(cChunk & a_Chunk); + + /// Moves items from a furnace above the hopper into this hopper. Returns true if contents have changed. + bool MoveItemsFromFurnace(cChunk & a_Chunk); + + /// Moves items from the specified ItemGrid into this hopper. Returns true if contents have changed. + bool MoveItemsFromGrid(cItemGrid & a_Grid); + + /// Moves one of the specified itemstack into this hopper. Returns true if contents have changed. Doesn't change the itemstack. + bool MoveItemsFromSlot(const cItem & a_ItemStack, bool a_AllowNewStacks); +} ; + + + + diff --git a/source/Chunk.cpp b/source/Chunk.cpp index c2afb9d5c..78074a7af 100644 --- a/source/Chunk.cpp +++ b/source/Chunk.cpp @@ -16,6 +16,7 @@ #include "BlockEntities/DispenserEntity.h" #include "BlockEntities/DropperEntity.h" #include "BlockEntities/FurnaceEntity.h" +#include "BlockEntities/HopperEntity.h" #include "BlockEntities/JukeboxEntity.h" #include "BlockEntities/NoteEntity.h" #include "BlockEntities/SignEntity.h" @@ -1222,7 +1223,7 @@ void cChunk::CreateBlockEntities(void) { if (!HasBlockEntityAt(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width)) { - m_BlockEntities.push_back( new cChestEntity( x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World) ); + m_BlockEntities.push_back(new cChestEntity(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World)); } break; } @@ -1231,7 +1232,7 @@ void cChunk::CreateBlockEntities(void) { if (!HasBlockEntityAt(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width)) { - m_BlockEntities.push_back( new cDispenserEntity( x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World) ); + m_BlockEntities.push_back(new cDispenserEntity(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World)); } break; } @@ -1249,17 +1250,25 @@ void cChunk::CreateBlockEntities(void) { if (!HasBlockEntityAt(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width)) { - m_BlockEntities.push_back( new cFurnaceEntity( x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World) ); + m_BlockEntities.push_back(new cFurnaceEntity(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World)); } break; } + case E_BLOCK_HOPPER: + { + if (!HasBlockEntityAt(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width)) + { + m_BlockEntities.push_back(new cHopperEntity(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World)); + } + } + case E_BLOCK_SIGN_POST: case E_BLOCK_WALLSIGN: { if (!HasBlockEntityAt(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width)) { - m_BlockEntities.push_back( new cSignEntity( BlockType, x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World) ); + m_BlockEntities.push_back( new cSignEntity(BlockType, x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World)); } break; } @@ -1268,7 +1277,7 @@ void cChunk::CreateBlockEntities(void) { if (!HasBlockEntityAt(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width)) { - m_BlockEntities.push_back(new cNoteEntity(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World) ); + m_BlockEntities.push_back(new cNoteEntity(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World)); } break; } @@ -1277,7 +1286,7 @@ void cChunk::CreateBlockEntities(void) { if (!HasBlockEntityAt(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width)) { - m_BlockEntities.push_back(new cJukeboxEntity(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World) ); + m_BlockEntities.push_back(new cJukeboxEntity(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World)); } break; } @@ -1434,28 +1443,33 @@ void cChunk::SetBlock( int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType { case E_BLOCK_CHEST: { - AddBlockEntity( new cChestEntity( WorldPos.x, WorldPos.y, WorldPos.z, m_World) ); + AddBlockEntity(new cChestEntity( WorldPos.x, WorldPos.y, WorldPos.z, m_World)); break; } case E_BLOCK_DISPENSER: { - AddBlockEntity( new cDispenserEntity( WorldPos.x, WorldPos.y, WorldPos.z, m_World) ); + AddBlockEntity(new cDispenserEntity( WorldPos.x, WorldPos.y, WorldPos.z, m_World)); break; } case E_BLOCK_DROPPER: { - AddBlockEntity( new cDropperEntity( WorldPos.x, WorldPos.y, WorldPos.z, m_World) ); + AddBlockEntity(new cDropperEntity( WorldPos.x, WorldPos.y, WorldPos.z, m_World)); break; } case E_BLOCK_FURNACE: { - AddBlockEntity( new cFurnaceEntity( WorldPos.x, WorldPos.y, WorldPos.z, m_World) ); + AddBlockEntity(new cFurnaceEntity( WorldPos.x, WorldPos.y, WorldPos.z, m_World)); + break; + } + case E_BLOCK_HOPPER: + { + AddBlockEntity(new cHopperEntity( WorldPos.x, WorldPos.y, WorldPos.z, m_World)); break; } case E_BLOCK_SIGN_POST: case E_BLOCK_WALLSIGN: { - AddBlockEntity( new cSignEntity( (ENUM_BLOCK_ID)a_BlockType, WorldPos.x, WorldPos.y, WorldPos.z, m_World) ); + AddBlockEntity(new cSignEntity(a_BlockType, WorldPos.x, WorldPos.y, WorldPos.z, m_World)); break; } case E_BLOCK_NOTE_BLOCK: diff --git a/source/Chunk.h b/source/Chunk.h index 748227b70..07062e71b 100644 --- a/source/Chunk.h +++ b/source/Chunk.h @@ -310,6 +310,9 @@ public: cFluidSimulatorData * GetLavaSimulatorData (void) { return m_LavaSimulatorData; } cSandSimulatorChunkData & GetSandSimulatorData (void) { return m_SandSimulatorData; } + cBlockEntity * GetBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ); + cBlockEntity * GetBlockEntity(const Vector3i & a_BlockPos) { return GetBlockEntity(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z); } + private: friend class cChunkMap; @@ -362,8 +365,6 @@ private: void RemoveBlockEntity(cBlockEntity * a_BlockEntity); void AddBlockEntity (cBlockEntity * a_BlockEntity); - cBlockEntity * GetBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ); - cBlockEntity * GetBlockEntity(const Vector3i & a_BlockPos) { return GetBlockEntity(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z); } void SpreadLightOfBlock(NIBBLETYPE * a_LightBuffer, int a_X, int a_Y, int a_Z, char a_Falloff); diff --git a/source/ItemGrid.cpp b/source/ItemGrid.cpp index c62ea6bcc..1154250f1 100644 --- a/source/ItemGrid.cpp +++ b/source/ItemGrid.cpp @@ -616,9 +616,13 @@ void cItemGrid::RemoveListener(cListener & a_Listener) void cItemGrid::TriggerListeners(int a_SlotNum) { - cCSLock Lock(m_CSListeners); - m_IsInTriggerListeners = true; - for (cListeners::iterator itr = m_Listeners.begin(), end = m_Listeners.end(); itr != end; ++itr) + cListeners Listeners; + { + cCSLock Lock(m_CSListeners); + m_IsInTriggerListeners = true; + Listeners = m_Listeners; + } + for (cListeners::iterator itr = Listeners.begin(), end = Listeners.end(); itr != end; ++itr) { (*itr)->OnSlotChanged(this, a_SlotNum); } // for itr - m_Listeners[] diff --git a/source/UI/SlotArea.cpp b/source/UI/SlotArea.cpp index 3f480817c..feeeb4add 100644 --- a/source/UI/SlotArea.cpp +++ b/source/UI/SlotArea.cpp @@ -665,6 +665,16 @@ cSlotAreaItemGrid::cSlotAreaItemGrid(cItemGrid & a_ItemGrid, cWindow & a_ParentW super(a_ItemGrid.GetNumSlots(), a_ParentWindow), m_ItemGrid(a_ItemGrid) { + m_ItemGrid.AddListener(*this); +} + + + + + +cSlotAreaItemGrid::~cSlotAreaItemGrid() +{ + m_ItemGrid.RemoveListener(*this); } @@ -689,6 +699,16 @@ void cSlotAreaItemGrid::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & +void cSlotAreaItemGrid::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) +{ + ASSERT(a_ItemGrid == &m_ItemGrid); + m_ParentWindow.BroadcastSlot(this, a_SlotNum); +} + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cSlotAreaTemporary: diff --git a/source/UI/SlotArea.h b/source/UI/SlotArea.h index 0ad5296db..2666f5209 100644 --- a/source/UI/SlotArea.h +++ b/source/UI/SlotArea.h @@ -143,18 +143,24 @@ public: /// Handles any slot area that is representing a cItemGrid; same items for all the players class cSlotAreaItemGrid : - public cSlotArea + public cSlotArea, + public cItemGrid::cListener { typedef cSlotArea super; public: cSlotAreaItemGrid(cItemGrid & a_ItemGrid, cWindow & a_ParentWindow); + virtual ~cSlotAreaItemGrid(); + 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: cItemGrid & m_ItemGrid; + + // cItemGrid::cListener overrides: + virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override; } ; diff --git a/source/UI/Window.cpp b/source/UI/Window.cpp index 3c28f33b0..161145d50 100644 --- a/source/UI/Window.cpp +++ b/source/UI/Window.cpp @@ -11,6 +11,7 @@ #include "../Inventory.h" #include "../Items/ItemHandler.h" #include "../BlockEntities/ChestEntity.h" +#include "../BlockEntities/HopperEntity.h" @@ -607,6 +608,40 @@ int cWindow::DistributeItemToSlots(cPlayer & a_Player, const cItem & a_Item, int +void cWindow::BroadcastSlot(cSlotArea * a_Area, int a_LocalSlotNum) +{ + // Translate local slot num into global slot num: + int SlotNum = 0; + bool HasFound = false; + for (cSlotAreas::const_iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr) + { + if (a_Area == *itr) + { + SlotNum += a_LocalSlotNum; + HasFound = true; + break; + } + SlotNum += (*itr)->GetNumSlots(); + } // for itr - m_SlotAreas[] + if (!HasFound) + { + LOGWARNING("%s: Invalid slot area parameter", __FUNCTION__); + ASSERT(!"Invalid slot area"); + return; + } + + // Broadcast the update packet: + cCSLock Lock(m_CS); + for (cPlayerList::iterator itr = m_OpenedBy.begin(); itr != m_OpenedBy.end(); ++itr) + { + (*itr)->GetClientHandle()->SendInventorySlot(m_WindowID, SlotNum, *a_Area->GetSlot(a_LocalSlotNum, **itr)); + } // for itr - m_OpenedBy[] +} + + + + + void cWindow::SendWholeWindow(cClientHandle & a_Client) { a_Client.SendWholeInventory(*this); @@ -741,6 +776,7 @@ cChestWindow::~cChestWindow() cDropSpenserWindow::cDropSpenserWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserEntity * a_DropSpenser) : cWindow(cWindow::DropSpenser, "MCS-DropSpenser") { + m_ShouldDistributeToHotbarFirst = false; m_SlotAreas.push_back(new cSlotAreaDropSpenser(a_DropSpenser, *this)); m_SlotAreas.push_back(new cSlotAreaInventory(*this)); m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); @@ -750,12 +786,29 @@ cDropSpenserWindow::cDropSpenserWindow(int a_BlockX, int a_BlockY, int a_BlockZ, +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cHopperWindow: + +cHopperWindow::cHopperWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cHopperEntity * a_Hopper) : + super(cWindow::Hopper, "MCS-Hopper") +{ + m_ShouldDistributeToHotbarFirst = false; + m_SlotAreas.push_back(new cSlotAreaItemGrid(a_Hopper->GetContents(), *this)); + m_SlotAreas.push_back(new cSlotAreaInventory(*this)); + m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); +} + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cFurnaceWindow: cFurnaceWindow::cFurnaceWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceEntity * a_Furnace) : cWindow(cWindow::Furnace, "MCS-Furnace") { + m_ShouldDistributeToHotbarFirst = false; m_SlotAreas.push_back(new cSlotAreaFurnace(a_Furnace, *this)); m_SlotAreas.push_back(new cSlotAreaInventory(*this)); m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); diff --git a/source/UI/Window.h b/source/UI/Window.h index 0ff87dc70..0be43e819 100644 --- a/source/UI/Window.h +++ b/source/UI/Window.h @@ -21,6 +21,7 @@ class cClientHandle; class cChestEntity; class cDropSpenserEntity; class cFurnaceEntity; +class cHopperEntity; class cSlotArea; class cWorld; @@ -110,8 +111,16 @@ public: /// Called when a player closes this window; notifies all slot areas. Returns true if close accepted virtual bool ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse); + /// Sends the specified slot's contents to all clients of this window; the slot is specified as local in an area + void BroadcastSlot(cSlotArea * a_Area, int a_LocalSlotNum); + + /// Sends the contents of the whole window to the specified client void SendWholeWindow(cClientHandle & a_Client); + + /// Sends the contents of the whole window to all clients of this window. void BroadcastWholeWindow(void); + + /// Sends the progressbar to all clients of this window void BroadcastInventoryProgress(short a_Progressbar, short a_Value); // tolua_begin @@ -194,6 +203,7 @@ protected: class cCraftingWindow : public cWindow { + typedef cWindow super; public: cCraftingWindow(int a_BlockX, int a_BlockY, int a_BlockZ); } ; @@ -205,6 +215,7 @@ public: class cFurnaceWindow : public cWindow { + typedef cWindow super; public: cFurnaceWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceEntity * a_Furnace); } ; @@ -216,6 +227,7 @@ public: class cDropSpenserWindow : public cWindow { + typedef cWindow super; public: cDropSpenserWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserEntity * a_Dispenser); } ; @@ -224,6 +236,18 @@ public: +class cHopperWindow : + public cWindow +{ + typedef cWindow super; +public: + cHopperWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cHopperEntity * a_Hopper); +} ; + + + + + class cChestWindow : public cWindow { diff --git a/source/WorldStorage/NBTChunkSerializer.cpp b/source/WorldStorage/NBTChunkSerializer.cpp index 2cf34a24f..9c4f8a2ae 100644 --- a/source/WorldStorage/NBTChunkSerializer.cpp +++ b/source/WorldStorage/NBTChunkSerializer.cpp @@ -9,6 +9,7 @@ #include "../BlockEntities/DispenserEntity.h" #include "../BlockEntities/DropperEntity.h" #include "../BlockEntities/FurnaceEntity.h" +#include "../BlockEntities/HopperEntity.h" #include "../BlockEntities/JukeboxEntity.h" #include "../BlockEntities/NoteEntity.h" #include "../BlockEntities/SignEntity.h" @@ -175,14 +176,25 @@ void cNBTChunkSerializer::AddFurnaceEntity(cFurnaceEntity * a_Furnace) -void cNBTChunkSerializer::AddSignEntity(cSignEntity * a_Sign) +void cNBTChunkSerializer::AddHopperEntity(cHopperEntity * a_Entity) { m_Writer.BeginCompound(""); - AddBasicTileEntity(a_Sign, "Sign"); - m_Writer.AddString("Text1", a_Sign->GetLine(0)); - m_Writer.AddString("Text2", a_Sign->GetLine(1)); - m_Writer.AddString("Text3", a_Sign->GetLine(2)); - m_Writer.AddString("Text4", a_Sign->GetLine(3)); + AddBasicTileEntity(a_Entity, "Hopper"); + m_Writer.BeginList("Items", TAG_Compound); + AddItemGrid(a_Entity->GetContents()); + m_Writer.EndList(); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddJukeboxEntity(cJukeboxEntity * a_Jukebox) +{ + m_Writer.BeginCompound(""); + AddBasicTileEntity(a_Jukebox, "RecordPlayer"); + m_Writer.AddInt("Record", a_Jukebox->GetRecord()); m_Writer.EndCompound(); } @@ -202,11 +214,14 @@ void cNBTChunkSerializer::AddNoteEntity(cNoteEntity * a_Note) -void cNBTChunkSerializer::AddJukeboxEntity(cJukeboxEntity * a_Jukebox) +void cNBTChunkSerializer::AddSignEntity(cSignEntity * a_Sign) { m_Writer.BeginCompound(""); - AddBasicTileEntity(a_Jukebox, "RecordPlayer"); - m_Writer.AddInt("Record", a_Jukebox->GetRecord()); + AddBasicTileEntity(a_Sign, "Sign"); + m_Writer.AddString("Text1", a_Sign->GetLine(0)); + m_Writer.AddString("Text2", a_Sign->GetLine(1)); + m_Writer.AddString("Text3", a_Sign->GetLine(2)); + m_Writer.AddString("Text4", a_Sign->GetLine(3)); m_Writer.EndCompound(); } @@ -428,6 +443,7 @@ void cNBTChunkSerializer::BlockEntity(cBlockEntity * a_Entity) case E_BLOCK_DISPENSER: AddDispenserEntity ((cDispenserEntity *) a_Entity); break; case E_BLOCK_DROPPER: AddDropperEntity ((cDropperEntity *) a_Entity); break; case E_BLOCK_FURNACE: AddFurnaceEntity ((cFurnaceEntity *) a_Entity); break; + case E_BLOCK_HOPPER: AddHopperEntity ((cHopperEntity *) a_Entity); break; case E_BLOCK_SIGN_POST: case E_BLOCK_WALLSIGN: AddSignEntity ((cSignEntity *) a_Entity); break; case E_BLOCK_NOTE_BLOCK: AddNoteEntity ((cNoteEntity *) a_Entity); break; diff --git a/source/WorldStorage/NBTChunkSerializer.h b/source/WorldStorage/NBTChunkSerializer.h index 47389dfe8..14c31be01 100644 --- a/source/WorldStorage/NBTChunkSerializer.h +++ b/source/WorldStorage/NBTChunkSerializer.h @@ -20,12 +20,13 @@ class cFastNBTWriter; class cEntity; class cBlockEntity; class cChestEntity; -class cFurnaceEntity; class cDispenserEntity; class cDropperEntity; -class cSignEntity; -class cNoteEntity; +class cFurnaceEntity; +class cHopperEntity; class cJukeboxEntity; +class cNoteEntity; +class cSignEntity; class cFallingBlock; class cMinecart; class cMinecartWithChest; @@ -78,17 +79,18 @@ protected: void AddItemGrid(const cItemGrid & a_Grid, int a_BeginSlotNum = 0); // Block entities: - void AddBasicTileEntity(cBlockEntity * a_Entity, const char * a_EntityTypeID); - void AddChestEntity(cChestEntity * a_Entity); + void AddBasicTileEntity(cBlockEntity * a_Entity, const char * a_EntityTypeID); + void AddChestEntity (cChestEntity * a_Entity); void AddDispenserEntity(cDispenserEntity * a_Entity); - void AddDropperEntity(cDropperEntity * a_Entity); - void AddFurnaceEntity(cFurnaceEntity * a_Furnace); - void AddSignEntity(cSignEntity * a_Sign); - void AddNoteEntity(cNoteEntity * a_Note); - void AddJukeboxEntity(cJukeboxEntity * a_Jukebox); - void AddBasicEntity(cEntity * a_Entity, const AString & a_ClassName); + void AddDropperEntity (cDropperEntity * a_Entity); + void AddFurnaceEntity (cFurnaceEntity * a_Furnace); + void AddHopperEntity (cHopperEntity * a_Entity); + void AddJukeboxEntity (cJukeboxEntity * a_Jukebox); + void AddNoteEntity (cNoteEntity * a_Note); + void AddSignEntity (cSignEntity * a_Sign); // Entities: + void AddBasicEntity (cEntity * a_Entity, const AString & a_ClassName); void AddFallingBlockEntity(cFallingBlock * a_FallingBlock); void AddMinecartEntity (cMinecart * a_Minecart); void AddMonsterEntity (cMonster * a_Monster); diff --git a/source/WorldStorage/WSSAnvil.cpp b/source/WorldStorage/WSSAnvil.cpp index 1b70083c2..43e6dce05 100644 --- a/source/WorldStorage/WSSAnvil.cpp +++ b/source/WorldStorage/WSSAnvil.cpp @@ -13,6 +13,7 @@ #include "../BlockEntities/DispenserEntity.h" #include "../BlockEntities/DropperEntity.h" #include "../BlockEntities/FurnaceEntity.h" +#include "../BlockEntities/HopperEntity.h" #include "../BlockEntities/JukeboxEntity.h" #include "../BlockEntities/NoteEntity.h" #include "../BlockEntities/SignEntity.h" @@ -568,6 +569,10 @@ void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, con { LoadFurnaceFromNBT(a_BlockEntities, a_NBT, Child); } + else if (strncmp(a_NBT.GetData(sID), "Hopper", a_NBT.GetDataLength(sID)) == 0) + { + LoadHopperFromNBT(a_BlockEntities, a_NBT, Child); + } else if (strncmp(a_NBT.GetData(sID), "Music", a_NBT.GetDataLength(sID)) == 0) { LoadNoteFromNBT(a_BlockEntities, a_NBT, Child); @@ -721,7 +726,7 @@ void cWSSAnvil::LoadDropperFromNBT(cBlockEntityList & a_BlockEntities, const cPa int Items = a_NBT.FindChildByName(a_TagIdx, "Items"); if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List)) { - return; // Make it an empty dispenser - the chunk loader will provide an empty cDispenserEntity for this + return; // Make it an empty dropper - the chunk loader will provide an empty cDropperEntity for this } std::auto_ptr Dropper(new cDropperEntity(x, y, z, m_World)); LoadItemGridFromNBT(Dropper->GetContents(), a_NBT, Items); @@ -781,6 +786,70 @@ void cWSSAnvil::LoadFurnaceFromNBT(cBlockEntityList & a_BlockEntities, const cPa +void cWSSAnvil::LoadHopperFromNBT(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; + } + int Items = a_NBT.FindChildByName(a_TagIdx, "Items"); + if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List)) + { + return; // Make it an empty hopper - the chunk loader will provide an empty cHopperEntity for this + } + std::auto_ptr Hopper(new cHopperEntity(x, y, z, m_World)); + LoadItemGridFromNBT(Hopper->GetContents(), a_NBT, Items); + a_BlockEntities.push_back(Hopper.release()); +} + + + + + +void cWSSAnvil::LoadJukeboxFromNBT(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 Jukebox(new cJukeboxEntity(x, y, z, m_World)); + int Record = a_NBT.FindChildByName(a_TagIdx, "Record"); + if (Record >= 0) + { + Jukebox->SetRecord(a_NBT.GetInt(Record)); + } + a_BlockEntities.push_back(Jukebox.release()); +} + + + + + +void cWSSAnvil::LoadNoteFromNBT(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 Note(new cNoteEntity(x, y, z, m_World)); + int note = a_NBT.FindChildByName(a_TagIdx, "note"); + if (note >= 0) + { + Note->SetPitch(a_NBT.GetByte(note)); + } + a_BlockEntities.push_back(Note.release()); +} + + + + + void cWSSAnvil::LoadSignFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) { ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); @@ -821,47 +890,6 @@ void cWSSAnvil::LoadSignFromNBT(cBlockEntityList & a_BlockEntities, const cParse -void cWSSAnvil::LoadNoteFromNBT(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 Note(new cNoteEntity(x, y, z, m_World)); - int note = a_NBT.FindChildByName(a_TagIdx, "note"); - if (note >= 0) - { - Note->SetPitch(a_NBT.GetByte(note)); - } - a_BlockEntities.push_back(Note.release()); -} - - - - - -void cWSSAnvil::LoadJukeboxFromNBT(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 Jukebox(new cJukeboxEntity(x, y, z, m_World)); - int Record = a_NBT.FindChildByName(a_TagIdx, "Record"); - if (Record >= 0) - { - Jukebox->SetRecord(a_NBT.GetInt(Record)); - } - a_BlockEntities.push_back(Jukebox.release()); -} - - - - void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_EntityTagIdx, const char * a_IDTag, int a_IDTagLength) { diff --git a/source/WorldStorage/WSSAnvil.h b/source/WorldStorage/WSSAnvil.h index 7281d687d..2dc17d2e9 100644 --- a/source/WorldStorage/WSSAnvil.h +++ b/source/WorldStorage/WSSAnvil.h @@ -131,9 +131,10 @@ protected: void LoadDispenserFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadDropperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadFurnaceFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); - void LoadSignFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); - void LoadNoteFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadHopperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadJukeboxFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadNoteFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadSignFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_EntityTagIdx, const char * a_IDTag, int a_IDTagLength);