summaryrefslogtreecommitdiffstats
path: root/source/BlockEntities/HopperEntity.cpp
diff options
context:
space:
mode:
authorAlexander Harkness <bearbin@gmail.com>2013-07-29 13:13:03 +0200
committerAlexander Harkness <bearbin@gmail.com>2013-07-29 13:13:03 +0200
commit53e22b11857fed62e2313d6d84d90f88ed412ffb (patch)
treec61e56725da7dff0154d566722651e2c39c9d6c6 /source/BlockEntities/HopperEntity.cpp
parentWebAdmin: Removed the duplicate memory usage querying (diff)
downloadcuberite-53e22b11857fed62e2313d6d84d90f88ed412ffb.tar
cuberite-53e22b11857fed62e2313d6d84d90f88ed412ffb.tar.gz
cuberite-53e22b11857fed62e2313d6d84d90f88ed412ffb.tar.bz2
cuberite-53e22b11857fed62e2313d6d84d90f88ed412ffb.tar.lz
cuberite-53e22b11857fed62e2313d6d84d90f88ed412ffb.tar.xz
cuberite-53e22b11857fed62e2313d6d84d90f88ed412ffb.tar.zst
cuberite-53e22b11857fed62e2313d6d84d90f88ed412ffb.zip
Diffstat (limited to 'source/BlockEntities/HopperEntity.cpp')
-rw-r--r--source/BlockEntities/HopperEntity.cpp1046
1 files changed, 523 insertions, 523 deletions
diff --git a/source/BlockEntities/HopperEntity.cpp b/source/BlockEntities/HopperEntity.cpp
index 3fadf727c..23e4b8096 100644
--- a/source/BlockEntities/HopperEntity.cpp
+++ b/source/BlockEntities/HopperEntity.cpp
@@ -1,523 +1,523 @@
-
-// 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"
-#include "FurnaceEntity.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),
- m_LastMoveItemsInTick(0),
- m_LastMoveItemsOutTick(0)
-{
-}
-
-
-
-
-
-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),
- m_LastMoveItemsInTick(0),
- m_LastMoveItemsOutTick(0)
-{
-}
-
-
-
-
-
-/** Returns the block coords of the block receiving the output items, based on the meta
-Returns false if unattached
-*/
-bool cHopperEntity::GetOutputBlockPos(NIBBLETYPE a_BlockMeta, int & a_OutputX, int & a_OutputY, int & a_OutputZ)
-{
- a_OutputX = m_PosX;
- a_OutputY = m_PosY;
- a_OutputZ = m_PosZ;
- switch (a_BlockMeta)
- {
- case E_META_HOPPER_FACING_XM: a_OutputX--; return true;
- case E_META_HOPPER_FACING_XP: a_OutputX++; return true;
- case E_META_HOPPER_FACING_YM: a_OutputY--; return true;
- case E_META_HOPPER_FACING_ZM: a_OutputZ--; return true;
- case E_META_HOPPER_FACING_ZP: a_OutputZ++; return true;
- default:
- {
- // Not attached
- return false;
- }
- }
-}
-
-
-
-
-
-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;
- case E_BLOCK_LIT_FURNACE: res = MoveItemsFromFurnace(a_Chunk); 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;
- }
-
- int bx, by, bz;
- NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ);
- if (!GetOutputBlockPos(Meta, bx, by, bz))
- {
- // Not attached to another container
- return false;
- }
- if (by < 0)
- {
- // Cannot output below the zero-th block level
- return false;
- }
-
- // Convert coords to relative:
- int rx = bx - a_Chunk.GetPosX() * cChunkDef::Width;
- int rz = bz - a_Chunk.GetPosZ() * cChunkDef::Width;
- cChunk * DestChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(rx, rz);
- if (DestChunk == NULL)
- {
- // The destination chunk has been unloaded, don't tick
- return false;
- }
-
- // Call proper moving function, based on the blocktype present at the coords:
- bool res = false;
- switch (DestChunk->GetBlock(rx, by, rz))
- {
- case E_BLOCK_CHEST: res = MoveItemsToChest(*DestChunk, bx, by, bz); break;
- case E_BLOCK_FURNACE: res = MoveItemsToFurnace(*DestChunk, bx, by, bz, Meta); break;
- case E_BLOCK_DISPENSER:
- case E_BLOCK_DROPPER: res = MoveItemsToGrid(((cDropSpenserEntity *)DestChunk->GetBlockEntity(bx, by, bz))->GetContents()); break;
- case E_BLOCK_HOPPER: res = MoveItemsToGrid(((cHopperEntity *) DestChunk->GetBlockEntity(bx, by, bz))->GetContents()); break;
- case E_BLOCK_LIT_FURNACE: res = MoveItemsToFurnace(*DestChunk, bx, by, bz, Meta); break;
- }
-
- // If the item has been moved, reset the last tick:
- if (res)
- {
- m_LastMoveItemsOutTick = a_CurrentTick;
- }
-
- return res;
-}
-
-
-
-
-
-/// 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)
-{
- cFurnaceEntity * Furnace = (cFurnaceEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ);
- ASSERT(Furnace != NULL);
-
- // Try move from the output slot:
- if (MoveItemsFromSlot(Furnace->GetOutputSlot(), true))
- {
- cItem NewOutput(Furnace->GetOutputSlot());
- Furnace->SetOutputSlot(NewOutput.AddCount(-1));
- return true;
- }
-
- // No output moved, check if we can move an empty bucket out of the fuel slot:
- if (Furnace->GetFuelSlot().m_ItemType == E_ITEM_BUCKET)
- {
- if (MoveItemsFromSlot(Furnace->GetFuelSlot(), true))
- {
- Furnace->SetFuelSlot(cItem());
- return true;
- }
- }
-
- // Nothing can be moved
- 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)
-{
- cItem One(a_ItemStack.CopyOne());
- if (m_Contents.AddItem(One, a_AllowNewStacks) > 0)
- {
- return true;
- }
- return false;
-}
-
-
-
-
-
-/// Moves items to the chest at the specified coords. Returns true if contents have changed
-bool cHopperEntity::MoveItemsToChest(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ)
-{
- // Try the chest directly connected to the hopper:
- if (MoveItemsToGrid(((cChestEntity *)a_Chunk.GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ))->GetContents()))
- {
- return true;
- }
-
- // Check if the chest is a double-chest, if so, try to move into the other half:
- 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 (MoveItemsToGrid(((cChestEntity *)Neighbor->GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ))->GetContents()))
- {
- return true;
- }
- return false;
- }
-
- // The chest was single and nothing could be moved
- return false;
-}
-
-
-
-
-
-/// Moves items to the furnace at the specified coords. Returns true if contents have changed
-bool cHopperEntity::MoveItemsToFurnace(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_HopperMeta)
-{
- cFurnaceEntity * Furnace = (cFurnaceEntity *)a_Chunk.GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ);
- if (a_HopperMeta == E_META_HOPPER_FACING_YM)
- {
- // Feed the input slot of the furnace
- return MoveItemsToSlot(Furnace->GetContents(), cFurnaceEntity::fsInput);
- }
- else
- {
- // Feed the fuel slot of the furnace
- return MoveItemsToSlot(Furnace->GetContents(), cFurnaceEntity::fsFuel);
- }
- return false;
-}
-
-
-
-
-
-/// Moves items to the specified ItemGrid. Returns true if contents have changed
-bool cHopperEntity::MoveItemsToGrid(cItemGrid & a_ItemGrid)
-{
- // Iterate through our slots, try to move from each one:
- for (int i = 0; i < ContentsWidth * ContentsHeight; i++)
- {
- const cItem & SrcItem = m_Contents.GetSlot(i);
- if (SrcItem.IsEmpty())
- {
- continue;
- }
-
- cItem ToAdd = SrcItem.CopyOne();
- if (a_ItemGrid.AddItem(ToAdd) > 0)
- {
- m_Contents.ChangeSlotCount(i, -1);
- return true;
- }
- }
- return false;
-}
-
-
-
-
-
-/// Moves one piece to the specified ItemGrid's slot. Returns true if contents have changed.
-bool cHopperEntity::MoveItemsToSlot(cItemGrid & a_ItemGrid, int a_DestSlotNum)
-{
- if (a_ItemGrid.IsSlotEmpty(a_DestSlotNum))
- {
- // The slot is empty, move the first non-empty slot from our contents:
- for (int i = 0; i < ContentsWidth * ContentsHeight; i++)
- {
- if (!m_Contents.IsSlotEmpty(i))
- {
- a_ItemGrid.SetSlot(a_DestSlotNum, m_Contents.GetSlot(i).CopyOne());
- m_Contents.ChangeSlotCount(i, -1);
- return true;
- }
- }
- return false;
- }
- else
- {
- // The slot is taken, try to top it up:
- const cItem & DestSlot = a_ItemGrid.GetSlot(a_DestSlotNum);
- if (DestSlot.IsFullStack())
- {
- return false;
- }
- for (int i = 0; i < ContentsWidth * ContentsHeight; i++)
- {
- if (m_Contents.GetSlot(i).IsStackableWith(DestSlot))
- {
- a_ItemGrid.ChangeSlotCount(a_DestSlotNum, 1);
- m_Contents.ChangeSlotCount(i, -1);
- return true;
- }
- }
- return false;
- }
-}
-
-
-
-
+
+// 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"
+#include "FurnaceEntity.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),
+ m_LastMoveItemsInTick(0),
+ m_LastMoveItemsOutTick(0)
+{
+}
+
+
+
+
+
+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),
+ m_LastMoveItemsInTick(0),
+ m_LastMoveItemsOutTick(0)
+{
+}
+
+
+
+
+
+/** Returns the block coords of the block receiving the output items, based on the meta
+Returns false if unattached
+*/
+bool cHopperEntity::GetOutputBlockPos(NIBBLETYPE a_BlockMeta, int & a_OutputX, int & a_OutputY, int & a_OutputZ)
+{
+ a_OutputX = m_PosX;
+ a_OutputY = m_PosY;
+ a_OutputZ = m_PosZ;
+ switch (a_BlockMeta)
+ {
+ case E_META_HOPPER_FACING_XM: a_OutputX--; return true;
+ case E_META_HOPPER_FACING_XP: a_OutputX++; return true;
+ case E_META_HOPPER_FACING_YM: a_OutputY--; return true;
+ case E_META_HOPPER_FACING_ZM: a_OutputZ--; return true;
+ case E_META_HOPPER_FACING_ZP: a_OutputZ++; return true;
+ default:
+ {
+ // Not attached
+ return false;
+ }
+ }
+}
+
+
+
+
+
+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;
+ case E_BLOCK_LIT_FURNACE: res = MoveItemsFromFurnace(a_Chunk); 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;
+ }
+
+ int bx, by, bz;
+ NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ);
+ if (!GetOutputBlockPos(Meta, bx, by, bz))
+ {
+ // Not attached to another container
+ return false;
+ }
+ if (by < 0)
+ {
+ // Cannot output below the zero-th block level
+ return false;
+ }
+
+ // Convert coords to relative:
+ int rx = bx - a_Chunk.GetPosX() * cChunkDef::Width;
+ int rz = bz - a_Chunk.GetPosZ() * cChunkDef::Width;
+ cChunk * DestChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(rx, rz);
+ if (DestChunk == NULL)
+ {
+ // The destination chunk has been unloaded, don't tick
+ return false;
+ }
+
+ // Call proper moving function, based on the blocktype present at the coords:
+ bool res = false;
+ switch (DestChunk->GetBlock(rx, by, rz))
+ {
+ case E_BLOCK_CHEST: res = MoveItemsToChest(*DestChunk, bx, by, bz); break;
+ case E_BLOCK_FURNACE: res = MoveItemsToFurnace(*DestChunk, bx, by, bz, Meta); break;
+ case E_BLOCK_DISPENSER:
+ case E_BLOCK_DROPPER: res = MoveItemsToGrid(((cDropSpenserEntity *)DestChunk->GetBlockEntity(bx, by, bz))->GetContents()); break;
+ case E_BLOCK_HOPPER: res = MoveItemsToGrid(((cHopperEntity *) DestChunk->GetBlockEntity(bx, by, bz))->GetContents()); break;
+ case E_BLOCK_LIT_FURNACE: res = MoveItemsToFurnace(*DestChunk, bx, by, bz, Meta); break;
+ }
+
+ // If the item has been moved, reset the last tick:
+ if (res)
+ {
+ m_LastMoveItemsOutTick = a_CurrentTick;
+ }
+
+ return res;
+}
+
+
+
+
+
+/// 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)
+{
+ cFurnaceEntity * Furnace = (cFurnaceEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ);
+ ASSERT(Furnace != NULL);
+
+ // Try move from the output slot:
+ if (MoveItemsFromSlot(Furnace->GetOutputSlot(), true))
+ {
+ cItem NewOutput(Furnace->GetOutputSlot());
+ Furnace->SetOutputSlot(NewOutput.AddCount(-1));
+ return true;
+ }
+
+ // No output moved, check if we can move an empty bucket out of the fuel slot:
+ if (Furnace->GetFuelSlot().m_ItemType == E_ITEM_BUCKET)
+ {
+ if (MoveItemsFromSlot(Furnace->GetFuelSlot(), true))
+ {
+ Furnace->SetFuelSlot(cItem());
+ return true;
+ }
+ }
+
+ // Nothing can be moved
+ 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)
+{
+ cItem One(a_ItemStack.CopyOne());
+ if (m_Contents.AddItem(One, a_AllowNewStacks) > 0)
+ {
+ return true;
+ }
+ return false;
+}
+
+
+
+
+
+/// Moves items to the chest at the specified coords. Returns true if contents have changed
+bool cHopperEntity::MoveItemsToChest(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ // Try the chest directly connected to the hopper:
+ if (MoveItemsToGrid(((cChestEntity *)a_Chunk.GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ))->GetContents()))
+ {
+ return true;
+ }
+
+ // Check if the chest is a double-chest, if so, try to move into the other half:
+ 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 (MoveItemsToGrid(((cChestEntity *)Neighbor->GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ))->GetContents()))
+ {
+ return true;
+ }
+ return false;
+ }
+
+ // The chest was single and nothing could be moved
+ return false;
+}
+
+
+
+
+
+/// Moves items to the furnace at the specified coords. Returns true if contents have changed
+bool cHopperEntity::MoveItemsToFurnace(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_HopperMeta)
+{
+ cFurnaceEntity * Furnace = (cFurnaceEntity *)a_Chunk.GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ);
+ if (a_HopperMeta == E_META_HOPPER_FACING_YM)
+ {
+ // Feed the input slot of the furnace
+ return MoveItemsToSlot(Furnace->GetContents(), cFurnaceEntity::fsInput);
+ }
+ else
+ {
+ // Feed the fuel slot of the furnace
+ return MoveItemsToSlot(Furnace->GetContents(), cFurnaceEntity::fsFuel);
+ }
+ return false;
+}
+
+
+
+
+
+/// Moves items to the specified ItemGrid. Returns true if contents have changed
+bool cHopperEntity::MoveItemsToGrid(cItemGrid & a_ItemGrid)
+{
+ // Iterate through our slots, try to move from each one:
+ for (int i = 0; i < ContentsWidth * ContentsHeight; i++)
+ {
+ const cItem & SrcItem = m_Contents.GetSlot(i);
+ if (SrcItem.IsEmpty())
+ {
+ continue;
+ }
+
+ cItem ToAdd = SrcItem.CopyOne();
+ if (a_ItemGrid.AddItem(ToAdd) > 0)
+ {
+ m_Contents.ChangeSlotCount(i, -1);
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+/// Moves one piece to the specified ItemGrid's slot. Returns true if contents have changed.
+bool cHopperEntity::MoveItemsToSlot(cItemGrid & a_ItemGrid, int a_DestSlotNum)
+{
+ if (a_ItemGrid.IsSlotEmpty(a_DestSlotNum))
+ {
+ // The slot is empty, move the first non-empty slot from our contents:
+ for (int i = 0; i < ContentsWidth * ContentsHeight; i++)
+ {
+ if (!m_Contents.IsSlotEmpty(i))
+ {
+ a_ItemGrid.SetSlot(a_DestSlotNum, m_Contents.GetSlot(i).CopyOne());
+ m_Contents.ChangeSlotCount(i, -1);
+ return true;
+ }
+ }
+ return false;
+ }
+ else
+ {
+ // The slot is taken, try to top it up:
+ const cItem & DestSlot = a_ItemGrid.GetSlot(a_DestSlotNum);
+ if (DestSlot.IsFullStack())
+ {
+ return false;
+ }
+ for (int i = 0; i < ContentsWidth * ContentsHeight; i++)
+ {
+ if (m_Contents.GetSlot(i).IsStackableWith(DestSlot))
+ {
+ a_ItemGrid.ChangeSlotCount(a_DestSlotNum, 1);
+ m_Contents.ChangeSlotCount(i, -1);
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+
+
+