1
0

Double chest window fix (#3735)

This commit is contained in:
peterbell10 2017-05-28 19:07:38 +01:00 committed by Lukas Pioch
parent de0c86a690
commit f261a03c14
2 changed files with 131 additions and 53 deletions

View File

@ -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();
}
// There is no chest neighbor, open a single-chest window:
OpenWindow(new cChestWindow(this));
}
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();
}
}
bool cChestEntity::IsBlocked()
{
// TODO: cats are an obstruction
return (
(GetPosY() >= cChunkDef::Height - 1) ||
!cBlockInfo::IsTransparent(GetWorld()->GetBlock(GetPosX(), GetPosY() + 1, GetPosZ()))
);
}

View File

@ -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
{