summaryrefslogblamecommitdiffstats
path: root/source/UI/Window.cpp
blob: d222551055009d882630c3dfd24082b79c90e6f4 (plain) (tree)
1
2
3
4
5
6
7
8
9


                                                                                              
                   




                            
                        
                                 
                     
                           
















                                                                                 
                                               




                                      







                   




                                                                                                        




















































                                                                                                              





                                                                                                                                  
                                                   

















                                                                                                        






























                                                                                                                 
































































































                                                                                                                                               






                                                                                                                     
                                                    
                 

                                                                
                         





                                                                                                                       

                         
                
                                                     

                                                                                                                         


















                                                                                                   




























                                                                                                             
                           
 
























































                                                                                                                                                       
                                                          













                                                                                                                       
                                                          








                                                                                                                       

                                                    
                                     


                                     

                                                                  

                                                             
        

















                                                                                                          
                                                                                                 
                                                             
                                                          
        

                                                


                                                                                                          











                                                                                         

                                                                                                            






                                                                                                                       














                                                                                                                       






                                                                                                      
                                                          




 

#include "Globals.h"  // NOTE: MSVC stupidness requires this to be the same across all modules

#include "Window.h"
#include "../Item.h"
#include "../ClientHandle.h"
#include "../Player.h"
#include "../Pickup.h"
#include "../Inventory.h"
#include "WindowOwner.h"
#include "../Items/ItemHandler.h"
#include "SlotArea.h"
#include "../ChestEntity.h"





char cWindow::m_WindowIDCounter = 1;





cWindow::cWindow(cWindow::WindowType a_WindowType, const AString & a_WindowTitle)
	: m_WindowID(1 + (m_WindowIDCounter++ % 127))
	, m_WindowType(a_WindowType)
	, m_WindowTitle(a_WindowTitle)
	, m_Owner(NULL)
	, m_IsDestroyed(false)
	, m_ShouldDistributeToHotbarFirst(true)
{
	if (a_WindowType == Inventory)
	{
		m_WindowID = 0;
	}
}





cWindow::~cWindow()
{
	for (cSlotAreas::iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr)
	{
		delete *itr;
	}
	m_SlotAreas.clear();
}





int cWindow::GetNumSlots(void) const
{
	int res = 0;
	for (cSlotAreas::const_iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr)
	{
		res += (*itr)->GetNumSlots();
	}  // for itr - m_SlotAreas[]
	return res;
}





void cWindow::GetSlots(cPlayer & a_Player, cItems & a_Slots) const
{
	a_Slots.clear();
	a_Slots.reserve(GetNumSlots());
	for (cSlotAreas::const_iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr)
	{
		int NumSlots = (*itr)->GetNumSlots();
		for (int i = 0; i < NumSlots; i++)
		{
			
			const cItem * Item = (*itr)->GetSlot(i, a_Player);
			if (Item == NULL)
			{
				a_Slots.push_back(cItem());
			}
			else
			{
				a_Slots.push_back(*Item);
			}
		}
	}  // for itr - m_SlotAreas[]
}





void cWindow::Clicked(
	cPlayer & a_Player, 
	int a_WindowID, short a_SlotNum, bool a_IsRightClick, bool a_IsShiftPressed, 
	const cItem & a_ClickedItem
)
{
	if (a_WindowID != m_WindowID)
	{
		LOG("WRONG WINDOW ID! (exp %d, got %d) received from \"%s\"", m_WindowID, a_WindowID, a_Player.GetName().c_str());
		return;
	}

	if (a_SlotNum < 0)  // Outside window click
	{
		if (a_IsRightClick)
		{
			a_Player.TossItem(true);
		}
		else
		{
			a_Player.TossItem(true, a_Player.GetDraggingItem().m_ItemCount);
		}
		return;
	}

	int LocalSlotNum = a_SlotNum;
	int idx = 0;
	for (cSlotAreas::iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr)
	{
		if (LocalSlotNum < (*itr)->GetNumSlots())
		{
			(*itr)->Clicked(a_Player, LocalSlotNum, a_IsRightClick, a_IsShiftPressed, a_ClickedItem);
			return;
		}
		LocalSlotNum -= (*itr)->GetNumSlots();
		idx++;
	}
	
	LOGWARNING("Slot number higher than available window slots: %d, max %d received from \"%s\"; ignoring.",
		a_SlotNum, GetNumSlots(), a_Player.GetName().c_str()
	);
}





void cWindow::OpenedByPlayer(cPlayer & a_Player)
{
	{
		cCSLock Lock(m_CS);
		// If player is already in OpenedBy remove player first
		m_OpenedBy.remove(&a_Player);
		// Then add player
		m_OpenedBy.push_back(&a_Player);
		
		for (cSlotAreas::iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr)
		{
			(*itr)->OnPlayerAdded(a_Player);
		}  // for itr - m_SlotAreas[]
	}

	a_Player.GetClientHandle()->SendWindowOpen(m_WindowID, m_WindowType, m_WindowTitle, GetNumSlots() - c_NumInventorySlots);
}





void cWindow::ClosedByPlayer(cPlayer & a_Player)
{
	ASSERT(m_WindowType != Inventory);  // Inventory windows must not be closed (the client would repeat the close packet, looping forever)
	
	// Checks whether the player is still holding an item
	if (a_Player.IsDraggingItem())
	{
		LOGD("Player holds item! Dropping it...");
		a_Player.TossItem(true, a_Player.GetDraggingItem().m_ItemCount);
	}

	cClientHandle * ClientHandle = a_Player.GetClientHandle();
	if (ClientHandle != NULL)
	{
		ClientHandle->SendWindowClose(m_WindowID);
	}

	{
		cCSLock Lock(m_CS);

		for (cSlotAreas::iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr)
		{
			(*itr)->OnPlayerRemoved(a_Player);
		}  // for itr - m_SlotAreas[]

		m_OpenedBy.remove(&a_Player);
		if (m_OpenedBy.empty())
		{
			Destroy();
		}
	}
	if (m_IsDestroyed)
	{
		delete this;
	}
}





void cWindow::OwnerDestroyed()
{
	m_Owner = NULL;
	// Close window for each player. Note that the last one needs special handling
	while (m_OpenedBy.size() > 1)
	{
		(*m_OpenedBy.begin() )->CloseWindow((char)GetWindowType());
	}
	(*m_OpenedBy.begin() )->CloseWindow((char)GetWindowType());
}





bool cWindow::ForEachPlayer(cItemCallback<cPlayer> & a_Callback)
{
	cCSLock Lock(m_CS);
	for (cPlayerList::iterator itr = m_OpenedBy.begin(), end = m_OpenedBy.end(); itr != end; ++itr)
	{
		if (a_Callback.Item(*itr))
		{
			return false;
		}
	}  // for itr - m_OpenedBy[]
	return true;
}





bool cWindow::ForEachClient(cItemCallback<cClientHandle> & a_Callback)
{
	cCSLock Lock(m_CS);
	for (cPlayerList::iterator itr = m_OpenedBy.begin(), end = m_OpenedBy.end(); itr != end; ++itr)
	{
		if (a_Callback.Item((*itr)->GetClientHandle()))
		{
			return false;
		}
	}  // for itr - m_OpenedBy[]
	return true;
}





void cWindow::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, cSlotArea * a_ExcludeArea, bool a_ShouldApply)
{
	// Ask each slot area to take as much of the stack as it can.
	// First ask only slots that already have the same kind of item
	// Then ask any remaining slots
	for (int Pass = 0; Pass < 2; ++Pass)
	{
		if (m_ShouldDistributeToHotbarFirst)
		{
			// First distribute into the hotbar:
			if (a_ExcludeArea != m_SlotAreas.back())
			{
				m_SlotAreas.back()->DistributeStack(a_ItemStack, a_Player, a_ShouldApply, (Pass == 0));
				if (a_ItemStack.IsEmpty())
				{
					// Distributed it all
					return;
				}
			}
		}
		
		// The distribute to all other areas:
		cSlotAreas::iterator end = m_ShouldDistributeToHotbarFirst ? (m_SlotAreas.end() - 1) : m_SlotAreas.end();
		for (cSlotAreas::iterator itr = m_SlotAreas.begin(); itr != end; ++itr)
		{
			if (*itr == a_ExcludeArea)
			{
				continue;
			}
			(*itr)->DistributeStack(a_ItemStack, a_Player, a_ShouldApply, (Pass == 0));
			if (a_ItemStack.IsEmpty())
			{
				// Distributed it all
				return;
			}
		}  // for itr - m_SlotAreas[]
	}  // for Pass - repeat twice
}





void cWindow::SendSlot(cPlayer & a_Player, cSlotArea * a_SlotArea, int a_RelativeSlotNum)
{
	int SlotBase = 0;
	bool Found = false;
	for (cSlotAreas::iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr)
	{
		if (*itr == a_SlotArea)
		{
			Found = true;
			break;
		}
		SlotBase += (*itr)->GetNumSlots();
	}  // for itr - m_SlotAreas[]
	if (!Found)
	{
		LOGERROR("cWindow::SendSlot(): unknown a_SlotArea");
		ASSERT(!"cWindow::SendSlot(): unknown a_SlotArea");
		return;
	}
	
	a_Player.GetClientHandle()->SendInventorySlot(
		m_WindowID, a_RelativeSlotNum + SlotBase, *(a_SlotArea->GetSlot(a_RelativeSlotNum, a_Player))
	);
}





void cWindow::Destroy(void)
{
	if (m_Owner != NULL)
	{
		m_Owner->CloseWindow();
		m_Owner = NULL;
	}
	m_IsDestroyed = true;
}





void cWindow::SendWholeWindow(cClientHandle & a_Client)
{
	a_Client.SendWholeInventory(*this);
}





void cWindow::BroadcastWholeWindow(void)
{
	cCSLock Lock(m_CS);
	for (cPlayerList::iterator itr = m_OpenedBy.begin(); itr != m_OpenedBy.end(); ++itr)
	{
		SendWholeWindow(*(*itr)->GetClientHandle());
	}  // for itr - m_OpenedBy[]
}





void cWindow::BroadcastInventoryProgress(short a_Progressbar, short a_Value)
{
	cCSLock Lock(m_CS);
	for (cPlayerList::iterator itr = m_OpenedBy.begin(); itr != m_OpenedBy.end(); ++itr)
	{
		(*itr)->GetClientHandle()->SendInventoryProgress(m_WindowID, a_Progressbar, a_Value);
	}  // for itr - m_OpenedBy[]
}





///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cInventoryWindow:

cInventoryWindow::cInventoryWindow(cPlayer & a_Player) :
	cWindow(cWindow::Inventory, "MCS-Inventory"),
	m_Player(a_Player)
{
	m_SlotAreas.push_back(new cSlotAreaCrafting(2, *this));  // The creative inventory doesn't display it, but it's still counted into slot numbers
	m_SlotAreas.push_back(new cSlotAreaArmor(*this));
	m_SlotAreas.push_back(new cSlotAreaInventory(*this));
	m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
}





///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cCraftingWindow:

cCraftingWindow::cCraftingWindow(int a_BlockX, int a_BlockY, int a_BlockZ) :
	cWindow(cWindow::Workbench, "MCS-Workbench")
{
	m_SlotAreas.push_back(new cSlotAreaCrafting(3, *this));
	m_SlotAreas.push_back(new cSlotAreaInventory(*this));
	m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
}





///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cChestWindow:

cChestWindow::cChestWindow(cChestEntity * a_Chest) :
	cWindow(cWindow::Chest, "MCS-SingleChest"),
	m_World(a_Chest->GetWorld()),
	m_BlockX(a_Chest->GetPosX()),
	m_BlockY(a_Chest->GetPosY()),
	m_BlockZ(a_Chest->GetPosZ())
{
	m_SlotAreas.push_back(new cSlotAreaChest(a_Chest, *this));
	m_SlotAreas.push_back(new cSlotAreaInventory(*this));
	m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
	
	// Play the opening sound:
	m_World->BroadcastSoundEffect("random.chestopen", m_BlockX * 8, m_BlockY * 8, m_BlockZ * 8, 1, 1);

	// Send out the chest-open packet:
	m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 1, E_BLOCK_CHEST);
}





cChestWindow::cChestWindow(cChestEntity * a_PrimaryChest, cChestEntity * a_SecondaryChest) :
	cWindow(cWindow::Chest, "MCS-DoubleChest"),
	m_World(a_PrimaryChest->GetWorld()),
	m_BlockX(a_PrimaryChest->GetPosX()),
	m_BlockY(a_PrimaryChest->GetPosY()),
	m_BlockZ(a_PrimaryChest->GetPosZ())
{
	m_SlotAreas.push_back(new cSlotAreaDoubleChest(a_PrimaryChest, a_SecondaryChest, *this));
	m_SlotAreas.push_back(new cSlotAreaInventory(*this));
	m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
	
	m_ShouldDistributeToHotbarFirst = false;
	
	// Play the opening sound:
	m_World->BroadcastSoundEffect("random.chestopen", m_BlockX * 8, m_BlockY * 8, m_BlockZ * 8, 1, 1);

	// Send out the chest-open packet:
	m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 1, E_BLOCK_CHEST);
}





cChestWindow::~cChestWindow()
{
	// Send out the chest-close packet:
	m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 0, E_BLOCK_CHEST);

	m_World->BroadcastSoundEffect("random.chestclosed", m_BlockX * 8, m_BlockY * 8, m_BlockZ * 8, 1, 1);
}





///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cDispenserWindow:

cDispenserWindow::cDispenserWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserEntity * a_Dispenser) :
	cWindow(cWindow::Dispenser, "MCS-Dispenser")
{
	m_SlotAreas.push_back(new cSlotAreaDispenser(a_Dispenser, *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_SlotAreas.push_back(new cSlotAreaFurnace(a_Furnace, *this));
	m_SlotAreas.push_back(new cSlotAreaInventory(*this));
	m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
}