summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/BlockEntities/ChestEntity.cpp165
-rw-r--r--src/BlockEntities/ChestEntity.h17
2 files changed, 130 insertions, 52 deletions
diff --git a/src/BlockEntities/ChestEntity.cpp b/src/BlockEntities/ChestEntity.cpp
index c3fb44a1b..3fcf1b386 100644
--- a/src/BlockEntities/ChestEntity.cpp
+++ b/src/BlockEntities/ChestEntity.cpp
@@ -13,21 +13,33 @@
cChestEntity::cChestEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World, BLOCKTYPE a_Type) :
super(a_Type, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World),
- m_NumActivePlayers(0)
+ m_NumActivePlayers(0),
+ m_Neighbour(nullptr)
{
+ int ChunkX = 0, ChunkZ = 0;
+ cChunkDef::BlockToChunk(m_PosX, m_PosZ, ChunkX, ChunkZ);
+ if (
+ (m_World != nullptr) &&
+ m_World->IsChunkValid(ChunkX, ChunkZ)
+ )
+ {
+ ScanNeighbours();
+ }
}
-
cChestEntity::~cChestEntity()
{
- cWindow * Window = GetWindow();
- if (Window != nullptr)
+ if (m_Neighbour != nullptr)
{
- Window->OwnerDestroyed();
+ // Neighbour may share a window with us, force the window shut
+ m_Neighbour->DestroyWindow();
+ m_Neighbour->m_Neighbour = nullptr;
}
+
+ DestroyWindow();
}
@@ -46,12 +58,42 @@ void cChestEntity::SendTo(cClientHandle & a_Client)
bool cChestEntity::UsedBy(cPlayer * a_Player)
{
+ if (IsBlocked())
+ {
+ // Obstruction, don't open
+ return true;
+ }
+
+ if (m_Neighbour == nullptr)
+ {
+ ScanNeighbours();
+ }
+
+ // The primary chest should be the one with lesser X or Z coord:
+ cChestEntity * PrimaryChest = this;
+ if (m_Neighbour != nullptr)
+ {
+ if (m_Neighbour->IsBlocked())
+ {
+ // Obstruction, don't open
+ return true;
+ }
+
+ if (
+ (m_Neighbour->GetPosX() > GetPosX()) ||
+ (m_Neighbour->GetPosZ() > GetPosZ())
+ )
+ {
+ PrimaryChest = m_Neighbour;
+ }
+ }
+
// If the window is not created, open it anew:
- cWindow * Window = GetWindow();
+ cWindow * Window = PrimaryChest->GetWindow();
if (Window == nullptr)
{
- OpenNewWindow();
- Window = GetWindow();
+ PrimaryChest->OpenNewWindow();
+ Window = PrimaryChest->GetWindow();
}
// Open the window for the player:
@@ -77,62 +119,87 @@ bool cChestEntity::UsedBy(cPlayer * a_Player)
-void cChestEntity::OpenNewWindow(void)
+void cChestEntity::ScanNeighbours()
{
- // TODO: cats are an obstruction
- if ((GetPosY() < cChunkDef::Height - 1) && !cBlockInfo::IsTransparent(GetWorld()->GetBlock(GetPosX(), GetPosY() + 1, GetPosZ())))
- {
- // Obstruction, don't open
- return;
- }
-
- // Callback for opening together with neighbor chest:
- class cOpenDouble :
+ // Callback for finding neighbouring chest:
+ class cFindNeighbour :
public cChestCallback
{
- cChestEntity * m_ThisChest;
public:
- cOpenDouble(cChestEntity * a_ThisChest) :
- m_ThisChest(a_ThisChest)
+ cChestEntity * m_Neighbour;
+
+ cFindNeighbour() :
+ m_Neighbour(nullptr)
{
}
virtual bool Item(cChestEntity * a_Chest) override
{
- if ((a_Chest->GetPosY() < cChunkDef::Height - 1) && !cBlockInfo::IsTransparent(a_Chest->GetWorld()->GetBlock(a_Chest->GetPosX(), a_Chest->GetPosY() + 1, a_Chest->GetPosZ())))
- {
- // Obstruction, don't open
- return false;
- }
-
- // The primary chest should eb the one with lesser X or Z coord:
- cChestEntity * Primary = a_Chest;
- cChestEntity * Secondary = m_ThisChest;
- if (
- (Primary->GetPosX() > Secondary->GetPosX()) ||
- (Primary->GetPosZ() > Secondary->GetPosZ())
- )
- {
- std::swap(Primary, Secondary);
- }
- m_ThisChest->OpenWindow(new cChestWindow(Primary, Secondary));
+ m_Neighbour = a_Chest;
return false;
}
- } ;
+ };
- // Scan neighbors for adjacent chests:
- cOpenDouble OpenDbl(this);
+ // Scan horizontally adjacent blocks for any neighbouring chest:
+ cFindNeighbour FindNeighbour;
if (
- m_World->DoWithChestAt(m_PosX - 1, m_PosY, m_PosZ, OpenDbl) ||
- m_World->DoWithChestAt(m_PosX + 1, m_PosY, m_PosZ, OpenDbl) ||
- m_World->DoWithChestAt(m_PosX, m_PosY, m_PosZ - 1, OpenDbl) ||
- m_World->DoWithChestAt(m_PosX, m_PosY, m_PosZ + 1, OpenDbl)
+ m_World->DoWithChestAt(m_PosX - 1, m_PosY, m_PosZ, FindNeighbour) ||
+ m_World->DoWithChestAt(m_PosX + 1, m_PosY, m_PosZ, FindNeighbour) ||
+ m_World->DoWithChestAt(m_PosX, m_PosY, m_PosZ - 1, FindNeighbour) ||
+ m_World->DoWithChestAt(m_PosX, m_PosY, m_PosZ + 1, FindNeighbour)
)
{
- // The double-chest window has been opened in the callback
- return;
+ m_Neighbour = FindNeighbour.m_Neighbour;
+ m_Neighbour->m_Neighbour = this;
+ // Force neighbour's window shut. Does Mojang server do this or should a double window open?
+ m_Neighbour->DestroyWindow();
+ }
+}
+
+
+
+
+
+void cChestEntity::OpenNewWindow(void)
+{
+ if (m_Neighbour != nullptr)
+ {
+ ASSERT( // This should be the primary chest
+ (m_Neighbour->GetPosX() < GetPosX()) ||
+ (m_Neighbour->GetPosZ() < GetPosZ())
+ );
+ OpenWindow(new cChestWindow(this, m_Neighbour));
+ }
+ else
+ {
+ // There is no chest neighbour, open a single-chest window:
+ OpenWindow(new cChestWindow(this));
+ }
+}
+
+
+
+
+
+void cChestEntity::DestroyWindow()
+{
+ cWindow * Window = GetWindow();
+ if (Window != nullptr)
+ {
+ Window->OwnerDestroyed();
+ CloseWindow();
}
+}
+
+
- // There is no chest neighbor, open a single-chest window:
- OpenWindow(new cChestWindow(this));
+
+
+bool cChestEntity::IsBlocked()
+{
+ // TODO: cats are an obstruction
+ return (
+ (GetPosY() >= cChunkDef::Height - 1) ||
+ !cBlockInfo::IsTransparent(GetWorld()->GetBlock(GetPosX(), GetPosY() + 1, GetPosZ()))
+ );
}
diff --git a/src/BlockEntities/ChestEntity.h b/src/BlockEntities/ChestEntity.h
index 9a10cd9a5..dfd5ca0d0 100644
--- a/src/BlockEntities/ChestEntity.h
+++ b/src/BlockEntities/ChestEntity.h
@@ -39,9 +39,17 @@ public:
virtual void SendTo(cClientHandle & a_Client) override;
virtual bool 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);
+ /** Search horizontally adjacent blocks for neighbouring chests and links them together. */
+ void ScanNeighbours();
+
+ /** Opens a new chest window where this is the primary chest and any neighbour is the secondary. */
+ void OpenNewWindow();
+
+ /** Forces any players to close the owned window. */
+ void DestroyWindow();
+
+ /** Returns true if the chest should not be accessible by players. */
+ bool IsBlocked();
/** Gets the number of players who currently have this chest open */
int GetNumberOfPlayers(void) const { return m_NumActivePlayers; }
@@ -54,6 +62,9 @@ private:
/** Number of players who currently have this chest open */
int m_NumActivePlayers;
+ /** Neighbouring chest that links to form a double chest */
+ cChestEntity * m_Neighbour;
+
/** cItemGrid::cListener overrides: */
virtual void OnSlotChanged(cItemGrid * a_Grid, int a_SlotNum) override
{