From bc466f07a454271d4845a7e8c7f0822541c5afbd Mon Sep 17 00:00:00 2001 From: "madmaxoft@gmail.com" Date: Thu, 20 Sep 2012 13:25:54 +0000 Subject: Refactored windows. As described on the forum: http://forum.mc-server.org/showthread.php?tid=561 For now, only basic clicking works; shift-click not implemented yet. git-svn-id: http://mc-server.googlecode.com/svn/trunk@867 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- source/UI/SlotArea.cpp | 569 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 569 insertions(+) create mode 100644 source/UI/SlotArea.cpp (limited to 'source/UI/SlotArea.cpp') diff --git a/source/UI/SlotArea.cpp b/source/UI/SlotArea.cpp new file mode 100644 index 000000000..744492bff --- /dev/null +++ b/source/UI/SlotArea.cpp @@ -0,0 +1,569 @@ + +// SlotArea.cpp + +// Implements the cSlotArea class and its descendants + +#include "Globals.h" +#include "SlotArea.h" +#include "../cPlayer.h" +#include "../cChestEntity.h" +#include "../cFurnaceEntity.h" +#include "../Items/Item.h" +#include "cWindow.h" +#include "../CraftingRecipes.h" +#include "../cRoot.h" + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSlotArea: + +cSlotArea::cSlotArea(int a_NumSlots, cWindow & a_ParentWindow) : + m_NumSlots(a_NumSlots), + m_ParentWindow(a_ParentWindow) +{ + LOGD("Created a new cSlotArea with %d slots", a_NumSlots); +} + + + + + +void cSlotArea::Clicked(cPlayer & a_Player, int a_SlotNum, bool a_IsRightClick, bool a_IsShiftPressed, const cItem & a_ClickedItem) +{ + LOGD("Slot area with %d slots clicked at slot number %d, clicked item %s, slot item %s", + GetNumSlots(), a_SlotNum, + ItemToFullString(a_ClickedItem).c_str(), + ItemToFullString(*GetSlot(a_SlotNum, a_Player)).c_str() + ); + + ASSERT((a_SlotNum >= 0) && (a_SlotNum < GetNumSlots())); + + bool bAsync = false; + if (GetSlot(a_SlotNum, a_Player) == NULL) + { + LOGWARNING("GetSlot(%d) returned NULL! Ignoring click", a_SlotNum); + return; + } + + cItem Slot(*GetSlot(a_SlotNum, a_Player)); + if (!Slot.IsEqual(a_ClickedItem)) + { + LOGD("*** Window lost sync ***"); + LOGD("My Type: %i Their Type: %i", Slot.m_ItemID, a_ClickedItem.m_ItemID); + LOGD("My Count: %i Their Count: %i", Slot.m_ItemCount, a_ClickedItem.m_ItemCount); + LOGD("My Dmg: %i Their Dmg: %i", Slot.m_ItemHealth, a_ClickedItem.m_ItemHealth); + bAsync = true; + } + cItem & DraggingItem = a_Player.GetDraggingItem(); + if (a_IsRightClick) + { + // Right clicked + if (DraggingItem.m_ItemID <= 0) // Empty-handed? + { + DraggingItem.m_ItemCount = (char)(((float)Slot.m_ItemCount) / 2.f + 0.5f); + Slot.m_ItemCount -= DraggingItem.m_ItemCount; + DraggingItem.m_ItemID = Slot.m_ItemID; + DraggingItem.m_ItemHealth = Slot.m_ItemHealth; + + if (Slot.m_ItemCount <= 0) + { + Slot.Empty(); + } + } + else if ((Slot.m_ItemID <= 0) || DraggingItem.IsEqual(Slot)) + { + // Drop one item in slot + cItemHandler * Handler = ItemHandler(Slot.m_ItemID); + if ((DraggingItem.m_ItemCount > 0) && (Slot.m_ItemCount < Handler->GetMaxStackSize())) + { + Slot.m_ItemID = DraggingItem.m_ItemID; + Slot.m_ItemCount++; + Slot.m_ItemHealth = DraggingItem.m_ItemHealth; + DraggingItem.m_ItemCount--; + } + if (DraggingItem.m_ItemCount <= 0) + { + DraggingItem.Empty(); + } + } + else if (!DraggingItem.IsEqual(Slot)) + { + // Swap contents + cItem tmp(DraggingItem); + DraggingItem = Slot; + Slot = tmp; + } + } + else + { + // Left-clicked + if (!DraggingItem.IsEqual(Slot)) + { + // Switch contents + cItem tmp(DraggingItem); + DraggingItem = Slot; + Slot = tmp; + } + else + { + // Same type, add items: + cItemHandler * Handler = ItemHandler(DraggingItem.m_ItemID); + int FreeSlots = Handler->GetMaxStackSize() - Slot.m_ItemCount; + if (FreeSlots < 0) + { + ASSERT(!"Bad item stack size - where did we get more items in a slot than allowed?"); + FreeSlots = 0; + } + int Filling = (FreeSlots > DraggingItem.m_ItemCount) ? DraggingItem.m_ItemCount : FreeSlots; + Slot.m_ItemCount += (char)Filling; + DraggingItem.m_ItemCount -= (char)Filling; + if (DraggingItem.m_ItemCount <= 0) + { + DraggingItem.Empty(); + } + } + } + + if (bAsync) + { + m_ParentWindow.BroadcastWholeWindow(); + } + SetSlot(a_SlotNum, a_Player, Slot); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSlotAreaArmor: + +cSlotAreaArmor::cSlotAreaArmor(cWindow & a_ParentWindow) : + cSlotArea(4, a_ParentWindow) +{ +} + + + + + +const cItem * cSlotAreaArmor::GetSlot(int a_SlotNum, cPlayer & a_Player) +{ + // a_SlotNum ranges from 0 to 3, map that to the armor slots in player's inventory, 5 to 8: + return a_Player.GetInventory().GetSlot(a_SlotNum + 5); +} + + + + + +void cSlotAreaArmor::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) +{ + *(a_Player.GetInventory().GetSlot(a_SlotNum + 5)) = a_Item; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSlotAreaChest: + +cSlotAreaChest::cSlotAreaChest(cChestEntity *a_Chest, cWindow &a_ParentWindow) : + cSlotArea(27, a_ParentWindow), + m_Chest(a_Chest) +{ +} + + + + + +const cItem * cSlotAreaChest::GetSlot(int a_SlotNum, cPlayer & a_Player) +{ + // a_SlotNum ranges from 0 to 26, use that to index the chest entity's inventory directly: + return m_Chest->GetSlot(a_SlotNum); +} + + + + + +void cSlotAreaChest::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) +{ + m_Chest->SetSlot(a_SlotNum, a_Item); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSlotAreaCrafting: + +cSlotAreaCrafting::cSlotAreaCrafting(int a_GridSize, cWindow & a_ParentWindow) : + cSlotAreaTemporary(1 + a_GridSize * a_GridSize, a_ParentWindow), + m_GridSize(a_GridSize) +{ + ASSERT((a_GridSize == 2) || (a_GridSize == 3)); +} + + + + + +void cSlotAreaCrafting::Clicked(cPlayer & a_Player, int a_SlotNum, bool a_IsRightClick, bool a_IsShiftPressed, const cItem & a_ClickedItem) +{ + // Override for craft result slot + if (a_SlotNum == 0) + { + ClickedResult(a_Player, a_IsShiftPressed); + return; + } + super::Clicked(a_Player, a_SlotNum, a_IsRightClick, a_IsShiftPressed, a_ClickedItem); + UpdateRecipe(a_Player); +} + + + + + +void cSlotAreaCrafting::OnPlayerRemoved(cPlayer & a_Player) +{ + // Toss all items on the crafting grid: + TossItems(a_Player, 1, m_NumSlots); + + // Remove the current recipe from the player -> recipe map: + for (cRecipeMap::iterator itr = m_Recipes.begin(), end = m_Recipes.end(); itr != end; ++itr) + { + if (itr->first == a_Player.GetUniqueID()) + { + // Remove the player from the recipe map: + m_Recipes.erase(itr); + return; + } + } // for itr - m_Recipes[] + // Player not found - that is acceptable +} + + + + + +void cSlotAreaCrafting::ClickedResult(cPlayer & a_Player, bool a_IsShiftPressed) +{ + const cItem * ResultSlot = GetSlot(0, a_Player); + LOGD("Clicked in craft result slot, item there: %d:%d (%d times)", + ResultSlot->m_ItemID, ResultSlot->m_ItemHealth, ResultSlot->m_ItemCount + ); + cItem & DraggingItem = a_Player.GetDraggingItem(); + + // Get the current recipe: + cCraftingRecipe & Recipe = GetRecipeForPlayer(a_Player); + + cItem * PlayerSlots = GetPlayerSlots(a_Player) + 1; + cCraftingGrid Grid(PlayerSlots, m_GridSize, m_GridSize); + + // If possible, craft: + if (DraggingItem.IsEmpty()) + { + DraggingItem = Recipe.GetResult(); + Recipe.ConsumeIngredients(Grid); + Grid.CopyToItems(PlayerSlots); + } + else if (DraggingItem.IsEqual(Recipe.GetResult())) + { + cItemHandler * Handler = ItemHandler(Recipe.GetResult().m_ItemID); + if (DraggingItem.m_ItemCount + Recipe.GetResult().m_ItemCount <= Handler->GetMaxStackSize()) + { + DraggingItem.m_ItemCount += Recipe.GetResult().m_ItemCount; + Recipe.ConsumeIngredients(Grid); + Grid.CopyToItems(PlayerSlots); + } + } + + // Get the new recipe and update the result slot: + UpdateRecipe(a_Player); + + // We're done. Send all changes to the client and bail out: + m_ParentWindow.BroadcastWholeWindow(); +} + + + + + +void cSlotAreaCrafting::UpdateRecipe(cPlayer & a_Player) +{ + cCraftingGrid Grid(GetPlayerSlots(a_Player) + 1, m_GridSize, m_GridSize); + cCraftingRecipe & Recipe = GetRecipeForPlayer(a_Player); + cRoot::Get()->GetCraftingRecipes()->GetRecipe(&a_Player, Grid, Recipe); +} + + + + + +cCraftingRecipe & cSlotAreaCrafting::GetRecipeForPlayer(cPlayer & a_Player) +{ + for (cRecipeMap::iterator itr = m_Recipes.begin(), end = m_Recipes.end(); itr != end; ++itr) + { + if (itr->first == a_Player.GetUniqueID()) + { + return itr->second; + } + } // for itr - m_Recipes[] + + // Not found. Add a new one: + cCraftingGrid Grid(GetPlayerSlots(a_Player) + 1, m_GridSize, m_GridSize); + cCraftingRecipe Recipe(Grid); + cRoot::Get()->GetCraftingRecipes()->GetRecipe(&a_Player, Grid, Recipe); + m_Recipes.push_back(std::make_pair(a_Player.GetUniqueID(), Recipe)); + return m_Recipes.back().second; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSlotAreaFurnace: + +cSlotAreaFurnace::cSlotAreaFurnace(cFurnaceEntity * a_Furnace, cWindow & a_ParentWindow) : + cSlotArea(3, a_ParentWindow), + m_Furnace(a_Furnace) +{ +} + + + + + +void cSlotAreaFurnace::Clicked(cPlayer & a_Player, int a_SlotNum, bool a_IsRightClick, bool a_IsShiftPressed, const cItem & a_ClickedItem) +{ + cItem Fuel = *GetSlot(0, a_Player); + + super::Clicked(a_Player, a_SlotNum, a_IsRightClick, a_IsShiftPressed, a_ClickedItem); + + if (m_Furnace == NULL) + { + LOGERROR("cSlotAreaFurnace::Clicked(): m_Furnace == NULL"); + ASSERT(!"cSlotAreaFurnace::Clicked(): m_Furnace == NULL"); + return; + } + + if (Fuel.m_ItemID != GetSlot(0, a_Player)->m_ItemID) + { + m_Furnace->ResetCookTimer(); + } + + if (m_Furnace->StartCooking()) + { + m_ParentWindow.SendWholeWindow(*(a_Player.GetClientHandle())); + } +} + + + + + +const cItem * cSlotAreaFurnace::GetSlot(int a_SlotNum, cPlayer & a_Player) +{ + // a_SlotNum ranges from 0 to 2, query the items from the underlying furnace: + return m_Furnace->GetSlot(a_SlotNum); +} + + + + + +void cSlotAreaFurnace::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) +{ + m_Furnace->SetSlot(a_SlotNum, a_Item); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSlotAreaInventory: + +cSlotAreaInventory::cSlotAreaInventory(cWindow & a_ParentWindow) : + cSlotArea(27 + 9, a_ParentWindow) // 27 internal slots, 9 hotbar slots +{ +} + + + + + +void cSlotAreaInventory::Clicked(cPlayer & a_Player, int a_SlotNum, bool a_IsRightClick, bool a_IsShiftPressed, const cItem & a_ClickedItem) +{ + if ((a_Player.GetGameMode() == eGameMode_Creative) && (m_ParentWindow.GetWindowType() == cWindow::Inventory)) + { + // Creative inventory must treat a_ClickedItem as a DraggedItem instead, replacing the inventory slot with it + SetSlot(a_SlotNum, a_Player, a_ClickedItem); + return; + } + + // Survival inventory and all other windows' inventory has the same handling as normal slot areas + super::Clicked(a_Player, a_SlotNum, a_IsRightClick, a_IsShiftPressed, a_ClickedItem); + return; +} + + + + + +const cItem * cSlotAreaInventory::GetSlot(int a_SlotNum, cPlayer & a_Player) +{ + // a_SlotNum ranges from 0 to 35, map that to the player's inventory slots 9 to 44 + return a_Player.GetInventory().GetSlot(a_SlotNum + 9); +} + + + + + +void cSlotAreaInventory::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) +{ + *(a_Player.GetInventory().GetSlot(a_SlotNum + 9)) = a_Item; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSlotAreaTemporary: + +cSlotAreaTemporary::cSlotAreaTemporary(int a_NumSlots, cWindow & a_ParentWindow) : + cSlotArea(a_NumSlots, a_ParentWindow) +{ +} + + + + + +const cItem * cSlotAreaTemporary::GetSlot(int a_SlotNum, cPlayer & a_Player) +{ + cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID()); + if (itr == m_Items.end()) + { + LOGERROR("cSlotAreaTemporary: player \"%s\" not found for slot %d!", a_Player.GetName().c_str(), a_SlotNum); + ASSERT(!"cSlotAreaTemporary: player not found!"); + + // Player not found, this should not happen, ever! Return NULL, but things may break by this. + return NULL; + } + + if (a_SlotNum >= (int)(itr->second.size())) + { + LOGERROR("cSlotAreaTemporary: asking for more slots than actually stored!"); + ASSERT(!"cSlotAreaTemporary: asking for more slots than actually stored!"); + return NULL; + } + + LOGD("cSlotAreaTemporary: getting slot %d as %s", a_SlotNum, ItemToFullString(itr->second[a_SlotNum]).c_str()); + + return &(itr->second[a_SlotNum]); +} + + + + + +void cSlotAreaTemporary::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) +{ + LOGD("cSlotAreaTemporary: setting slot %d to %s", a_SlotNum, ItemToFullString(a_Item).c_str()); + + cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID()); + if (itr == m_Items.end()) + { + // Player not found + LOGWARNING("cSlotAreaTemporary: player not found!"); + return; + } + + if (a_SlotNum >= (int)(itr->second.size())) + { + LOGERROR("cSlotAreaTemporary: asking for more slots than actually stored!"); + return; + } + + itr->second[a_SlotNum] = a_Item; +} + + + + + +void cSlotAreaTemporary::OnPlayerAdded(cPlayer & a_Player) +{ + ASSERT(m_Items.find(a_Player.GetUniqueID()) == m_Items.end()); // The player shouldn't be in the itemmap, otherwise we probably have a leak + m_Items[a_Player.GetUniqueID()].resize(m_NumSlots); // Make the vector the specified size of empty items +} + + + + + +void cSlotAreaTemporary::OnPlayerRemoved(cPlayer & a_Player) +{ + cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID()); + ASSERT(itr != m_Items.end()); // The player should be in the list, otherwise a call to OnPlayerAdded() was mismatched + m_Items.erase(itr); +} + + + + + +void cSlotAreaTemporary::TossItems(cPlayer & a_Player, int a_Begin, int a_End) +{ + cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID()); + if (itr == m_Items.end()) + { + LOGWARNING("Player tossing items (%s) not found in the item map", a_Player.GetName().c_str()); + return; + } + + cItems Drops; + for (int i = a_Begin; i < a_End; i++) + { + cItem & Item = itr->second[i]; + if (!Item.IsEmpty()) + { + Drops.push_back(Item); + } + Item.Empty(); + } // for i - itr->second[] + + float vX = 0, vY = 0, vZ = 0; + EulerToVector(-a_Player.GetRotation(), a_Player.GetPitch(), vZ, vX, vY); + vY = -vY * 2 + 1.f; + a_Player.GetWorld()->SpawnItemPickups(Drops, a_Player.GetPosX(), a_Player.GetPosY() + 1.6f, a_Player.GetPosZ(), vX * 2, vY * 2, vZ * 2); +} + + + + + +cItem * cSlotAreaTemporary::GetPlayerSlots(cPlayer & a_Player) +{ + cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID()); + if (itr == m_Items.end()) + { + return NULL; + } + return &(itr->second[0]); +} + + + + -- cgit v1.2.3