2013-07-29 07:13:03 -04:00
|
|
|
|
|
|
|
// HopperEntity.cpp
|
|
|
|
|
|
|
|
// Implements the cHopperEntity representing a hopper block entity
|
|
|
|
|
|
|
|
#include "Globals.h"
|
|
|
|
#include "HopperEntity.h"
|
|
|
|
#include "../Chunk.h"
|
2013-08-19 05:39:13 -04:00
|
|
|
#include "../Entities/Player.h"
|
2014-02-11 17:54:01 -05:00
|
|
|
#include "../Entities/Pickup.h"
|
2013-12-08 06:17:54 -05:00
|
|
|
#include "../Bindings/PluginManager.h"
|
2014-12-13 09:06:55 -05:00
|
|
|
#include "../UI/HopperWindow.h"
|
2013-07-29 07:13:03 -04:00
|
|
|
#include "ChestEntity.h"
|
|
|
|
#include "FurnaceEntity.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2019-09-29 08:59:24 -04:00
|
|
|
cHopperEntity::cHopperEntity(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, Vector3i a_Pos, cWorld * a_World):
|
|
|
|
super(a_BlockType, a_BlockMeta, a_Pos, ContentsWidth, ContentsHeight, a_World),
|
2013-07-29 07:13:03 -04:00
|
|
|
m_LastMoveItemsInTick(0),
|
2020-03-27 08:03:28 -04:00
|
|
|
m_LastMoveItemsOutTick(0),
|
|
|
|
m_Locked(false)
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
2017-06-15 09:32:33 -04:00
|
|
|
ASSERT(a_BlockType == E_BLOCK_HOPPER);
|
2013-07-29 07:13:03 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-03-27 08:03:28 -04:00
|
|
|
void cHopperEntity::SetLocked(bool a_Value)
|
|
|
|
{
|
|
|
|
m_Locked = a_Value;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2019-09-29 08:59:24 -04:00
|
|
|
std::pair<bool, Vector3i> cHopperEntity::GetOutputBlockPos(NIBBLETYPE a_BlockMeta)
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
2019-09-29 08:59:24 -04:00
|
|
|
auto pos = GetPos();
|
2013-07-29 07:13:03 -04:00
|
|
|
switch (a_BlockMeta)
|
|
|
|
{
|
2019-09-29 08:59:24 -04:00
|
|
|
case E_META_HOPPER_FACING_XM: return {true, pos.addedX(-1)};
|
|
|
|
case E_META_HOPPER_FACING_XP: return {true, pos.addedX( 1)};
|
|
|
|
case E_META_HOPPER_FACING_YM: return {true, pos.addedY(-1)};
|
|
|
|
case E_META_HOPPER_FACING_ZM: return {true, pos.addedZ(-1)};
|
|
|
|
case E_META_HOPPER_FACING_ZP: return {true, pos.addedZ( 1)};
|
2013-07-29 07:13:03 -04:00
|
|
|
default:
|
|
|
|
{
|
|
|
|
// Not attached
|
2019-09-29 08:59:24 -04:00
|
|
|
return {false, pos};
|
2013-07-29 07:13:03 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-06-15 09:32:33 -04:00
|
|
|
void cHopperEntity::CopyFrom(const cBlockEntity & a_Src)
|
|
|
|
{
|
2019-09-29 08:59:24 -04:00
|
|
|
super::CopyFrom(a_Src);
|
2018-05-02 03:50:36 -04:00
|
|
|
auto & src = static_cast<const cHopperEntity &>(a_Src);
|
2017-06-15 09:32:33 -04:00
|
|
|
m_LastMoveItemsInTick = src.m_LastMoveItemsInTick;
|
|
|
|
m_LastMoveItemsOutTick = src.m_LastMoveItemsOutTick;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-01-11 16:12:26 -05:00
|
|
|
bool cHopperEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
2014-02-24 14:29:59 -05:00
|
|
|
UNUSED(a_Dt);
|
2013-07-29 07:13:03 -04:00
|
|
|
Int64 CurrentTick = a_Chunk.GetWorld()->GetWorldAge();
|
2014-10-11 22:39:55 -04:00
|
|
|
|
2017-06-15 09:32:33 -04:00
|
|
|
bool isDirty = false;
|
2020-03-27 08:03:28 -04:00
|
|
|
if (!m_Locked)
|
|
|
|
{
|
|
|
|
isDirty = MoveItemsIn (a_Chunk, CurrentTick) || isDirty;
|
|
|
|
isDirty = MovePickupsIn(a_Chunk, CurrentTick) || isDirty;
|
|
|
|
isDirty = MoveItemsOut (a_Chunk, CurrentTick) || isDirty;
|
|
|
|
}
|
2017-06-15 09:32:33 -04:00
|
|
|
return isDirty;
|
2013-07-29 07:13:03 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
2014-10-11 22:39:55 -04:00
|
|
|
|
2013-07-29 07:13:03 -04:00
|
|
|
UNUSED(a_Client);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-12-01 17:12:44 -05:00
|
|
|
bool cHopperEntity::UsedBy(cPlayer * a_Player)
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
|
|
|
// If the window is not created, open it anew:
|
|
|
|
cWindow * Window = GetWindow();
|
2014-10-20 16:55:07 -04:00
|
|
|
if (Window == nullptr)
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
|
|
|
OpenNewWindow();
|
|
|
|
Window = GetWindow();
|
|
|
|
}
|
2014-10-11 22:39:55 -04:00
|
|
|
|
2013-07-29 07:13:03 -04:00
|
|
|
// Open the window for the player:
|
2014-10-20 16:55:07 -04:00
|
|
|
if (Window != nullptr)
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
|
|
|
if (a_Player->GetWindow() != Window)
|
|
|
|
{
|
2017-05-29 15:33:30 -04:00
|
|
|
a_Player->OpenWindow(*Window);
|
2013-07-29 07:13:03 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
2017-11-20 06:13:11 -05:00
|
|
|
cChunkCoords ChunkPos = cChunkDef::BlockToChunk(GetPos());
|
|
|
|
m_World->MarkChunkDirty(ChunkPos.m_ChunkX, ChunkPos.m_ChunkZ);
|
2015-12-01 17:12:44 -05:00
|
|
|
return true;
|
2013-07-29 07:13:03 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cHopperEntity::OpenNewWindow(void)
|
|
|
|
{
|
2019-09-29 08:59:24 -04:00
|
|
|
OpenWindow(new cHopperWindow(this));
|
2013-07-29 07:13:03 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool cHopperEntity::MoveItemsIn(cChunk & a_Chunk, Int64 a_CurrentTick)
|
|
|
|
{
|
2019-09-29 08:59:24 -04:00
|
|
|
if (m_Pos.y >= cChunkDef::Height)
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
|
|
|
// 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;
|
|
|
|
}
|
2014-10-11 22:39:55 -04:00
|
|
|
|
2013-07-29 07:13:03 -04:00
|
|
|
// Try moving an item in:
|
|
|
|
bool res = false;
|
2019-10-04 09:50:47 -04:00
|
|
|
switch (a_Chunk.GetBlock(GetRelPos().addedY(1)))
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
2014-07-06 18:50:22 -04:00
|
|
|
case E_BLOCK_TRAPPED_CHEST:
|
2013-08-11 08:57:07 -04:00
|
|
|
case E_BLOCK_CHEST:
|
|
|
|
{
|
|
|
|
// Chests have special handling because of double-chests
|
|
|
|
res = MoveItemsFromChest(a_Chunk);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case E_BLOCK_LIT_FURNACE:
|
|
|
|
case E_BLOCK_FURNACE:
|
|
|
|
{
|
|
|
|
// Furnaces have special handling because only the output and leftover fuel buckets shall be moved
|
|
|
|
res = MoveItemsFromFurnace(a_Chunk);
|
|
|
|
break;
|
|
|
|
}
|
2013-07-29 07:13:03 -04:00
|
|
|
case E_BLOCK_DISPENSER:
|
2013-08-11 08:57:07 -04:00
|
|
|
case E_BLOCK_DROPPER:
|
|
|
|
case E_BLOCK_HOPPER:
|
|
|
|
{
|
2019-10-04 09:50:47 -04:00
|
|
|
res = MoveItemsFromGrid(*static_cast<cBlockEntityWithItems *>(a_Chunk.GetBlockEntity(this->GetPos().addedY(1))));
|
2013-08-11 08:57:07 -04:00
|
|
|
break;
|
|
|
|
}
|
2013-07-29 07:13:03 -04:00
|
|
|
}
|
2014-10-11 22:39:55 -04:00
|
|
|
|
2013-07-29 07:13:03 -04:00
|
|
|
// If the item has been moved, reset the last tick:
|
|
|
|
if (res)
|
|
|
|
{
|
|
|
|
m_LastMoveItemsInTick = a_CurrentTick;
|
|
|
|
}
|
2014-10-11 22:39:55 -04:00
|
|
|
|
2013-07-29 07:13:03 -04:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool cHopperEntity::MovePickupsIn(cChunk & a_Chunk, Int64 a_CurrentTick)
|
|
|
|
{
|
2014-02-11 17:54:01 -05:00
|
|
|
UNUSED(a_CurrentTick);
|
|
|
|
|
2017-09-11 17:20:49 -04:00
|
|
|
class cHopperPickupSearchCallback
|
2014-02-11 17:54:01 -05:00
|
|
|
{
|
|
|
|
public:
|
2017-11-20 06:13:11 -05:00
|
|
|
cHopperPickupSearchCallback(Vector3i a_Pos, cItemGrid & a_Contents) :
|
2014-02-11 17:54:01 -05:00
|
|
|
m_Pos(a_Pos),
|
2014-02-12 17:01:22 -05:00
|
|
|
m_bFoundPickupsAbove(false),
|
|
|
|
m_Contents(a_Contents)
|
2014-02-11 17:54:01 -05:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-09-11 17:20:49 -04:00
|
|
|
bool operator () (cEntity & a_Entity)
|
2014-02-11 17:54:01 -05:00
|
|
|
{
|
2017-09-11 17:20:49 -04:00
|
|
|
if (!a_Entity.IsPickup())
|
2014-02-11 17:54:01 -05:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-09-11 17:20:49 -04:00
|
|
|
Vector3f EntityPos = a_Entity.GetPosition();
|
2014-10-11 22:39:55 -04:00
|
|
|
Vector3f BlockPos(m_Pos.x + 0.5f, static_cast<float>(m_Pos.y) + 1, m_Pos.z + 0.5f); // One block above hopper, and search from center outwards
|
2014-03-16 17:00:28 -04:00
|
|
|
double Distance = (EntityPos - BlockPos).Length();
|
2014-02-11 17:54:01 -05:00
|
|
|
|
|
|
|
if (Distance < 0.5)
|
|
|
|
{
|
2017-09-11 17:20:49 -04:00
|
|
|
if (TrySuckPickupIn(static_cast<cPickup &>(a_Entity)))
|
2014-02-11 17:54:01 -05:00
|
|
|
{
|
2014-02-12 17:01:22 -05:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-09-11 17:20:49 -04:00
|
|
|
bool TrySuckPickupIn(cPickup & a_Pickup)
|
2014-02-12 17:01:22 -05:00
|
|
|
{
|
2017-09-11 17:20:49 -04:00
|
|
|
cItem & Item = a_Pickup.GetItem();
|
2014-04-18 15:09:44 -04:00
|
|
|
|
2014-02-12 17:01:22 -05:00
|
|
|
for (int i = 0; i < ContentsWidth * ContentsHeight; i++)
|
|
|
|
{
|
|
|
|
if (m_Contents.IsSlotEmpty(i))
|
|
|
|
{
|
|
|
|
m_bFoundPickupsAbove = true;
|
2014-04-18 15:09:44 -04:00
|
|
|
m_Contents.SetSlot(i, Item);
|
2017-09-11 17:20:49 -04:00
|
|
|
a_Pickup.Destroy(); // Kill pickup
|
2014-02-13 15:20:37 -05:00
|
|
|
|
2014-02-12 17:01:22 -05:00
|
|
|
return true;
|
|
|
|
}
|
2014-04-18 15:09:44 -04:00
|
|
|
else if (m_Contents.GetSlot(i).IsEqual(Item) && !m_Contents.GetSlot(i).IsFullStack())
|
2014-02-12 17:01:22 -05:00
|
|
|
{
|
|
|
|
m_bFoundPickupsAbove = true;
|
2014-02-13 15:20:37 -05:00
|
|
|
|
2014-02-12 17:01:22 -05:00
|
|
|
int PreviousCount = m_Contents.GetSlot(i).m_ItemCount;
|
2014-10-11 22:39:55 -04:00
|
|
|
|
2014-07-17 16:15:34 -04:00
|
|
|
Item.m_ItemCount -= m_Contents.ChangeSlotCount(i, Item.m_ItemCount) - PreviousCount; // Set count to however many items were added
|
2014-10-11 22:39:55 -04:00
|
|
|
|
2014-04-18 15:09:44 -04:00
|
|
|
if (Item.IsEmpty())
|
2014-02-11 17:54:01 -05:00
|
|
|
{
|
2017-09-11 17:20:49 -04:00
|
|
|
a_Pickup.Destroy(); // Kill pickup if all items were added
|
2014-02-11 17:54:01 -05:00
|
|
|
}
|
2014-02-12 17:01:22 -05:00
|
|
|
return true;
|
2014-02-11 17:54:01 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FoundPickupsAbove(void) const
|
|
|
|
{
|
|
|
|
return m_bFoundPickupsAbove;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
2014-02-13 14:57:23 -05:00
|
|
|
Vector3i m_Pos;
|
2014-02-11 17:54:01 -05:00
|
|
|
bool m_bFoundPickupsAbove;
|
|
|
|
cItemGrid & m_Contents;
|
|
|
|
};
|
|
|
|
|
|
|
|
cHopperPickupSearchCallback HopperPickupSearchCallback(Vector3i(GetPosX(), GetPosY(), GetPosZ()), m_Contents);
|
|
|
|
a_Chunk.ForEachEntity(HopperPickupSearchCallback);
|
|
|
|
|
|
|
|
return HopperPickupSearchCallback.FoundPickupsAbove();
|
2013-07-29 07:13:03 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2014-10-11 22:39:55 -04:00
|
|
|
|
2014-06-24 03:46:04 -04:00
|
|
|
// Get the coords of the block where to output items:
|
2019-09-29 08:59:24 -04:00
|
|
|
auto meta = a_Chunk.GetMeta(GetRelPos());
|
|
|
|
auto out = GetOutputBlockPos(meta);
|
|
|
|
if (!out.first)
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
|
|
|
// Not attached to another container
|
|
|
|
return false;
|
|
|
|
}
|
2019-09-29 08:59:24 -04:00
|
|
|
if (out.second.y < 0)
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
|
|
|
// Cannot output below the zero-th block level
|
|
|
|
return false;
|
|
|
|
}
|
2014-10-11 22:39:55 -04:00
|
|
|
|
2013-07-29 07:13:03 -04:00
|
|
|
// Convert coords to relative:
|
2019-09-29 08:59:24 -04:00
|
|
|
auto relCoord = a_Chunk.AbsoluteToRelative(out.second);
|
2019-10-11 05:02:53 -04:00
|
|
|
auto destChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(relCoord);
|
2019-09-29 08:59:24 -04:00
|
|
|
if (destChunk == nullptr)
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
|
|
|
// The destination chunk has been unloaded, don't tick
|
|
|
|
return false;
|
|
|
|
}
|
2014-10-11 22:39:55 -04:00
|
|
|
|
2013-07-29 07:13:03 -04:00
|
|
|
// Call proper moving function, based on the blocktype present at the coords:
|
|
|
|
bool res = false;
|
2019-09-29 08:59:24 -04:00
|
|
|
auto absCoord = destChunk->RelativeToAbsolute(relCoord);
|
|
|
|
switch (destChunk->GetBlock(relCoord))
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
2014-07-06 18:50:22 -04:00
|
|
|
case E_BLOCK_TRAPPED_CHEST:
|
2013-08-11 08:57:07 -04:00
|
|
|
case E_BLOCK_CHEST:
|
|
|
|
{
|
|
|
|
// Chests have special handling because of double-chests
|
2019-09-29 08:59:24 -04:00
|
|
|
res = MoveItemsToChest(*destChunk, absCoord);
|
2013-08-11 08:57:07 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case E_BLOCK_LIT_FURNACE:
|
|
|
|
case E_BLOCK_FURNACE:
|
|
|
|
{
|
|
|
|
// Furnaces have special handling because of the direction-to-slot relation
|
2019-09-29 08:59:24 -04:00
|
|
|
res = MoveItemsToFurnace(*destChunk, absCoord, meta);
|
2013-08-11 08:57:07 -04:00
|
|
|
break;
|
|
|
|
}
|
2013-07-29 07:13:03 -04:00
|
|
|
case E_BLOCK_DISPENSER:
|
2013-08-11 08:57:07 -04:00
|
|
|
case E_BLOCK_DROPPER:
|
|
|
|
case E_BLOCK_HOPPER:
|
|
|
|
{
|
2019-09-29 08:59:24 -04:00
|
|
|
auto blockEntity = static_cast<cBlockEntityWithItems *>(destChunk->GetBlockEntity(absCoord));
|
|
|
|
if (blockEntity == nullptr)
|
2014-06-24 03:46:04 -04:00
|
|
|
{
|
2019-09-29 08:59:24 -04:00
|
|
|
FLOGWARNING("{0}: A block entity was not found where expected at {1}", __FUNCTION__, absCoord);
|
2014-06-24 03:46:04 -04:00
|
|
|
return false;
|
|
|
|
}
|
2019-09-29 08:59:24 -04:00
|
|
|
res = MoveItemsToGrid(*blockEntity);
|
2013-08-11 08:57:07 -04:00
|
|
|
break;
|
|
|
|
}
|
2013-07-29 07:13:03 -04:00
|
|
|
}
|
2014-10-11 22:39:55 -04:00
|
|
|
|
2013-07-29 07:13:03 -04:00
|
|
|
// If the item has been moved, reset the last tick:
|
|
|
|
if (res)
|
|
|
|
{
|
|
|
|
m_LastMoveItemsOutTick = a_CurrentTick;
|
|
|
|
}
|
2014-10-11 22:39:55 -04:00
|
|
|
|
2013-07-29 07:13:03 -04:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool cHopperEntity::MoveItemsFromChest(cChunk & a_Chunk)
|
|
|
|
{
|
2019-09-29 08:59:24 -04:00
|
|
|
auto chestPos = GetPos().addedY(1);
|
|
|
|
auto mainChest = static_cast<cChestEntity *>(a_Chunk.GetBlockEntity(chestPos));
|
|
|
|
if (mainChest == nullptr)
|
2014-06-24 03:46:04 -04:00
|
|
|
{
|
2019-09-29 08:59:24 -04:00
|
|
|
FLOGWARNING("{0}: A chest entity was not found where expected, at {1}", __FUNCTION__, chestPos);
|
2014-06-24 03:46:04 -04:00
|
|
|
return false;
|
|
|
|
}
|
2019-09-29 08:59:24 -04:00
|
|
|
if (MoveItemsFromGrid(*mainChest))
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
|
|
|
// Moved the item from the chest directly above the hopper
|
|
|
|
return true;
|
|
|
|
}
|
2014-10-11 22:39:55 -04:00
|
|
|
|
2014-07-12 17:06:25 -04:00
|
|
|
// Check if the chest is a double-chest (chest directly above was empty), if so, try to move from there:
|
2019-09-29 08:59:24 -04:00
|
|
|
static const Vector3i neighborOfs[] =
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
2019-09-29 08:59:24 -04:00
|
|
|
{ 1, 1, 0},
|
|
|
|
{-1, 1, 0},
|
|
|
|
{ 0, 1, 1},
|
|
|
|
{ 0, 1, -1},
|
2013-07-29 07:13:03 -04:00
|
|
|
} ;
|
2019-09-29 08:59:24 -04:00
|
|
|
for (const auto & ofs: neighborOfs)
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
2019-09-29 08:59:24 -04:00
|
|
|
auto neighborRelCoord = ofs.addedXZ(m_RelX, m_RelZ);
|
2019-10-11 05:02:53 -04:00
|
|
|
auto neighbor = a_Chunk.GetRelNeighborChunkAdjustCoords(neighborRelCoord);
|
2019-09-29 08:59:24 -04:00
|
|
|
if (neighbor == nullptr)
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2014-07-06 18:50:22 -04:00
|
|
|
|
2019-09-29 08:59:24 -04:00
|
|
|
BLOCKTYPE Block = neighbor->GetBlock(neighborRelCoord);
|
|
|
|
if (Block != mainChest->GetBlockType())
|
2014-07-06 18:50:22 -04:00
|
|
|
{
|
2014-07-12 17:34:32 -04:00
|
|
|
// Not the same kind of chest
|
2014-07-06 18:50:22 -04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-09-29 08:59:24 -04:00
|
|
|
auto neighborAbsCoord = neighbor->RelativeToAbsolute(neighborRelCoord);
|
|
|
|
auto sideChest = static_cast<cChestEntity *>(neighbor->GetBlockEntity(neighborAbsCoord));
|
|
|
|
if (sideChest == nullptr)
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
2019-09-29 08:59:24 -04:00
|
|
|
FLOGWARNING("{0}: A chest entity was not found where expected, at {1}", __FUNCTION__, neighborAbsCoord);
|
2014-06-24 03:46:04 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-09-29 08:59:24 -04:00
|
|
|
if (MoveItemsFromGrid(*sideChest))
|
2014-06-24 03:46:04 -04:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
2013-07-29 07:13:03 -04:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2014-10-11 22:39:55 -04:00
|
|
|
|
2019-09-29 08:59:24 -04:00
|
|
|
// The chest was empty
|
2013-07-29 07:13:03 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool cHopperEntity::MoveItemsFromFurnace(cChunk & a_Chunk)
|
|
|
|
{
|
2019-09-29 08:59:24 -04:00
|
|
|
auto furnace = static_cast<cFurnaceEntity *>(a_Chunk.GetBlockEntity(m_Pos.addedY(1)));
|
|
|
|
if (furnace == nullptr)
|
2014-06-24 03:46:04 -04:00
|
|
|
{
|
2019-09-29 08:59:24 -04:00
|
|
|
FLOGWARNING("{0}: A furnace entity was not found where expected, at {1}", __FUNCTION__, m_Pos.addedY(1));
|
2014-06-24 03:46:04 -04:00
|
|
|
return false;
|
|
|
|
}
|
2014-10-11 22:39:55 -04:00
|
|
|
|
2013-07-29 07:13:03 -04:00
|
|
|
// Try move from the output slot:
|
2019-09-29 08:59:24 -04:00
|
|
|
if (MoveItemsFromSlot(*furnace, cFurnaceEntity::fsOutput))
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
2019-09-29 08:59:24 -04:00
|
|
|
cItem NewOutput(furnace->GetOutputSlot());
|
|
|
|
furnace->SetOutputSlot(NewOutput.AddCount(-1));
|
2013-07-29 07:13:03 -04:00
|
|
|
return true;
|
|
|
|
}
|
2014-10-11 22:39:55 -04:00
|
|
|
|
2013-07-29 07:13:03 -04:00
|
|
|
// No output moved, check if we can move an empty bucket out of the fuel slot:
|
2019-09-29 08:59:24 -04:00
|
|
|
if (furnace->GetFuelSlot().m_ItemType == E_ITEM_BUCKET)
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
2019-09-29 08:59:24 -04:00
|
|
|
if (MoveItemsFromSlot(*furnace, cFurnaceEntity::fsFuel))
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
2019-09-29 08:59:24 -04:00
|
|
|
furnace->SetFuelSlot(cItem());
|
2013-07-29 07:13:03 -04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2014-10-11 22:39:55 -04:00
|
|
|
|
2013-07-29 07:13:03 -04:00
|
|
|
// Nothing can be moved
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-08-11 08:57:07 -04:00
|
|
|
bool cHopperEntity::MoveItemsFromGrid(cBlockEntityWithItems & a_Entity)
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
2019-09-29 08:59:24 -04:00
|
|
|
auto & Grid = a_Entity.GetContents();
|
2013-08-11 08:57:07 -04:00
|
|
|
int NumSlots = Grid.GetNumSlots();
|
2014-10-11 22:39:55 -04:00
|
|
|
|
2013-07-29 07:13:03 -04:00
|
|
|
for (int i = 0; i < NumSlots; i++)
|
|
|
|
{
|
2013-08-11 08:57:07 -04:00
|
|
|
if (Grid.IsSlotEmpty(i))
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2015-05-11 17:58:27 -04:00
|
|
|
if (MoveItemsFromSlot(a_Entity, i))
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
2013-08-11 08:57:07 -04:00
|
|
|
Grid.ChangeSlotCount(i, -1);
|
2013-07-29 07:13:03 -04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-05-11 17:58:27 -04:00
|
|
|
bool cHopperEntity::MoveItemsFromSlot(cBlockEntityWithItems & a_Entity, int a_SlotNum)
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
2013-08-11 08:57:07 -04:00
|
|
|
cItem One(a_Entity.GetSlot(a_SlotNum).CopyOne());
|
|
|
|
for (int i = 0; i < ContentsWidth * ContentsHeight; i++)
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
2013-08-11 08:57:07 -04:00
|
|
|
if (m_Contents.IsSlotEmpty(i))
|
|
|
|
{
|
2015-05-11 17:58:27 -04:00
|
|
|
if (cPluginManager::Get()->CallHookHopperPullingItem(*m_World, *this, i, a_Entity, a_SlotNum))
|
2013-08-11 08:57:07 -04:00
|
|
|
{
|
2015-05-11 17:58:27 -04:00
|
|
|
// Plugin disagrees with the move
|
|
|
|
continue;
|
2013-08-11 08:57:07 -04:00
|
|
|
}
|
2015-05-11 17:58:27 -04:00
|
|
|
|
2013-08-11 08:57:07 -04:00
|
|
|
m_Contents.SetSlot(i, One);
|
|
|
|
return true;
|
|
|
|
}
|
2014-01-16 14:00:49 -05:00
|
|
|
else if (m_Contents.GetSlot(i).IsEqual(One))
|
2013-08-11 08:57:07 -04:00
|
|
|
{
|
|
|
|
if (cPluginManager::Get()->CallHookHopperPullingItem(*m_World, *this, i, a_Entity, a_SlotNum))
|
|
|
|
{
|
|
|
|
// Plugin disagrees with the move
|
|
|
|
continue;
|
|
|
|
}
|
2014-10-11 22:39:55 -04:00
|
|
|
|
2015-05-11 17:58:27 -04:00
|
|
|
auto PreviousCount = m_Contents.GetSlot(i).m_ItemCount;
|
2013-08-11 08:57:07 -04:00
|
|
|
m_Contents.ChangeSlotCount(i, 1);
|
2015-05-11 17:58:27 -04:00
|
|
|
|
2015-06-16 19:27:50 -04:00
|
|
|
if (PreviousCount + 1 == m_Contents.GetSlot(i).m_ItemCount)
|
2015-05-11 17:58:27 -04:00
|
|
|
{
|
|
|
|
// Successfully added a new item. (Failure condition consistutes: stack full)
|
|
|
|
return true;
|
|
|
|
}
|
2013-08-11 08:57:07 -04:00
|
|
|
}
|
2013-07-29 07:13:03 -04:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2019-09-29 08:59:24 -04:00
|
|
|
bool cHopperEntity::MoveItemsToChest(cChunk & a_Chunk, Vector3i a_Coords)
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
|
|
|
// Try the chest directly connected to the hopper:
|
2019-09-29 08:59:24 -04:00
|
|
|
auto ConnectedChest = static_cast<cChestEntity *>(a_Chunk.GetBlockEntity(a_Coords));
|
2014-10-20 16:55:07 -04:00
|
|
|
if (ConnectedChest == nullptr)
|
2014-06-24 03:46:04 -04:00
|
|
|
{
|
2019-09-29 08:59:24 -04:00
|
|
|
FLOGWARNING("{0}: A chest entity was not found where expected, at {1}", __FUNCTION__, a_Coords);
|
2014-06-24 03:46:04 -04:00
|
|
|
return false;
|
|
|
|
}
|
2014-08-10 17:06:56 -04:00
|
|
|
if (MoveItemsToGrid(*ConnectedChest))
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
2014-07-12 17:06:25 -04:00
|
|
|
// Chest block directly connected was not full
|
2013-07-29 07:13:03 -04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-07-12 17:06:25 -04:00
|
|
|
// Check if the chest is a double-chest (chest block directly connected was full), if so, try to move into the other half:
|
2019-09-29 08:59:24 -04:00
|
|
|
static const Vector3i neighborOfs [] =
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
2019-09-29 08:59:24 -04:00
|
|
|
{ 1, 0, 0},
|
|
|
|
{-1, 0, 0},
|
|
|
|
{ 0, 0, 1},
|
|
|
|
{ 0, 0, -1},
|
2013-07-29 07:13:03 -04:00
|
|
|
} ;
|
2019-09-29 08:59:24 -04:00
|
|
|
auto relCoord = a_Chunk.AbsoluteToRelative(a_Coords);
|
|
|
|
for (const auto & ofs: neighborOfs)
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
2019-09-29 08:59:24 -04:00
|
|
|
auto otherHalfRelCoord = relCoord + ofs;
|
2019-10-11 05:02:53 -04:00
|
|
|
auto neighbor = a_Chunk.GetRelNeighborChunkAdjustCoords(otherHalfRelCoord);
|
2019-09-29 08:59:24 -04:00
|
|
|
if (neighbor == nullptr)
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2014-07-06 18:50:22 -04:00
|
|
|
|
2019-09-29 08:59:24 -04:00
|
|
|
auto Block = neighbor->GetBlock(otherHalfRelCoord);
|
2014-08-10 17:06:56 -04:00
|
|
|
if (Block != ConnectedChest->GetBlockType())
|
2014-07-06 18:50:22 -04:00
|
|
|
{
|
2014-07-12 17:34:32 -04:00
|
|
|
// Not the same kind of chest
|
2014-07-06 18:50:22 -04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-09-29 08:59:24 -04:00
|
|
|
auto chest = static_cast<cChestEntity *>(neighbor->GetBlockEntity(a_Coords + ofs));
|
|
|
|
if (chest == nullptr)
|
2014-06-24 03:46:04 -04:00
|
|
|
{
|
2019-09-29 08:59:24 -04:00
|
|
|
FLOGWARNING("{0}: A chest entity was not found where expected, at {1} ({2}, {3}})", __FUNCTION__, a_Coords + ofs, ofs.x, ofs.z);
|
2014-06-24 03:46:04 -04:00
|
|
|
continue;
|
|
|
|
}
|
2019-09-29 08:59:24 -04:00
|
|
|
if (MoveItemsToGrid(*chest))
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2014-10-11 22:39:55 -04:00
|
|
|
|
2013-07-29 07:13:03 -04:00
|
|
|
// The chest was single and nothing could be moved
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2019-09-29 08:59:24 -04:00
|
|
|
bool cHopperEntity::MoveItemsToFurnace(cChunk & a_Chunk, Vector3i a_Coords, NIBBLETYPE a_HopperMeta)
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
2019-09-29 08:59:24 -04:00
|
|
|
auto furnace = static_cast<cFurnaceEntity *>(a_Chunk.GetBlockEntity(a_Coords));
|
2013-07-29 07:13:03 -04:00
|
|
|
if (a_HopperMeta == E_META_HOPPER_FACING_YM)
|
|
|
|
{
|
|
|
|
// Feed the input slot of the furnace
|
2019-09-29 08:59:24 -04:00
|
|
|
return MoveItemsToSlot(*furnace, cFurnaceEntity::fsInput);
|
2013-07-29 07:13:03 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Feed the fuel slot of the furnace
|
2019-09-29 08:59:24 -04:00
|
|
|
return MoveItemsToSlot(*furnace, cFurnaceEntity::fsFuel);
|
2013-07-29 07:13:03 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-08-11 08:57:07 -04:00
|
|
|
bool cHopperEntity::MoveItemsToGrid(cBlockEntityWithItems & a_Entity)
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
|
|
|
// Iterate through our slots, try to move from each one:
|
2013-08-11 08:57:07 -04:00
|
|
|
int NumSlots = a_Entity.GetContents().GetNumSlots();
|
|
|
|
for (int i = 0; i < NumSlots; i++)
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
2013-08-11 08:57:07 -04:00
|
|
|
if (MoveItemsToSlot(a_Entity, i))
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-08-11 08:57:07 -04:00
|
|
|
bool cHopperEntity::MoveItemsToSlot(cBlockEntityWithItems & a_Entity, int a_DstSlotNum)
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
2013-08-11 08:57:07 -04:00
|
|
|
cItemGrid & Grid = a_Entity.GetContents();
|
|
|
|
if (Grid.IsSlotEmpty(a_DstSlotNum))
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
|
|
|
// 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))
|
|
|
|
{
|
2013-08-11 08:57:07 -04:00
|
|
|
if (cPluginManager::Get()->CallHookHopperPushingItem(*m_World, *this, i, a_Entity, a_DstSlotNum))
|
|
|
|
{
|
|
|
|
// A plugin disagrees with the move
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
Grid.SetSlot(a_DstSlotNum, m_Contents.GetSlot(i).CopyOne());
|
2013-07-29 07:13:03 -04:00
|
|
|
m_Contents.ChangeSlotCount(i, -1);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// The slot is taken, try to top it up:
|
2013-08-11 08:57:07 -04:00
|
|
|
const cItem & DestSlot = Grid.GetSlot(a_DstSlotNum);
|
2013-07-29 07:13:03 -04:00
|
|
|
if (DestSlot.IsFullStack())
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
for (int i = 0; i < ContentsWidth * ContentsHeight; i++)
|
|
|
|
{
|
2014-01-16 14:00:49 -05:00
|
|
|
if (m_Contents.GetSlot(i).IsEqual(DestSlot))
|
2013-07-29 07:13:03 -04:00
|
|
|
{
|
2013-08-11 08:57:07 -04:00
|
|
|
if (cPluginManager::Get()->CallHookHopperPushingItem(*m_World, *this, i, a_Entity, a_DstSlotNum))
|
|
|
|
{
|
|
|
|
// A plugin disagrees with the move
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
Grid.ChangeSlotCount(a_DstSlotNum, 1);
|
2013-07-29 07:13:03 -04:00
|
|
|
m_Contents.ChangeSlotCount(i, -1);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2015-07-31 10:49:10 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|