diff options
Diffstat (limited to '')
-rw-r--r-- | src/UI/CMakeLists.txt | 16 | ||||
-rw-r--r-- | src/UI/SlotArea.cpp | 876 | ||||
-rw-r--r-- | src/UI/SlotArea.h | 71 | ||||
-rw-r--r-- | src/UI/Window.cpp | 173 | ||||
-rw-r--r-- | src/UI/Window.h | 46 | ||||
-rw-r--r-- | src/UI/WindowOwner.h | 2 |
6 files changed, 1082 insertions, 102 deletions
diff --git a/src/UI/CMakeLists.txt b/src/UI/CMakeLists.txt index 5b5b8cc18..2b094ef1d 100644 --- a/src/UI/CMakeLists.txt +++ b/src/UI/CMakeLists.txt @@ -4,9 +4,15 @@ project (MCServer) include_directories ("${PROJECT_SOURCE_DIR}/../") -file(GLOB SOURCE - "*.cpp" - "*.h" -) +SET (SRCS + SlotArea.cpp + Window.cpp) -add_library(UI ${SOURCE}) +SET (HDRS + SlotArea.h + Window.h + WindowOwner.h) + +if(NOT MSVC) + add_library(UI ${SRCS} ${HDRS}) +endif() diff --git a/src/UI/SlotArea.cpp b/src/UI/SlotArea.cpp index 87b4032e0..b5f84c24c 100644 --- a/src/UI/SlotArea.cpp +++ b/src/UI/SlotArea.cpp @@ -20,7 +20,7 @@ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// // cSlotArea: cSlotArea::cSlotArea(int a_NumSlots, cWindow & a_ParentWindow) : @@ -36,8 +36,8 @@ cSlotArea::cSlotArea(int a_NumSlots, cWindow & a_ParentWindow) : void cSlotArea::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) { /* - LOGD("Slot area with %d slots clicked at slot number %d, clicked item %s, slot item %s", - GetNumSlots(), a_SlotNum, + 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() ); @@ -60,12 +60,35 @@ void cSlotArea::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickA ShiftClicked(a_Player, a_SlotNum, a_ClickedItem); return; } - case caDblClick: { DblClicked(a_Player, a_SlotNum); return; } + case caMiddleClick: + { + MiddleClicked(a_Player, a_SlotNum); + return; + } + case caDropKey: + case caCtrlDropKey: + { + DropClicked(a_Player, a_SlotNum, (a_ClickAction == caCtrlDropKey)); + return; + } + case caNumber1: + case caNumber2: + case caNumber3: + case caNumber4: + case caNumber5: + case caNumber6: + case caNumber7: + case caNumber8: + case caNumber9: + { + NumberClicked(a_Player, a_SlotNum, a_ClickAction); + return; + } default: { break; @@ -85,9 +108,9 @@ void cSlotArea::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickA { case caRightClick: { - if (DraggingItem.m_ItemType <= 0) // Empty-handed? + if (DraggingItem.m_ItemType <= 0) // Empty-handed? { - DraggingItem = Slot.CopyOne(); // Obtain copy of slot to preserve lore, enchantments, etc. + DraggingItem = Slot.CopyOne(); // Obtain copy of slot to preserve lore, enchantments, etc. DraggingItem.m_ItemCount = (char)(((float)Slot.m_ItemCount) / 2.f + 0.5f); Slot.m_ItemCount -= DraggingItem.m_ItemCount; @@ -105,7 +128,7 @@ void cSlotArea::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickA { char OldSlotCount = Slot.m_ItemCount; - Slot = DraggingItem.CopyOne(); // See above + Slot = DraggingItem.CopyOne(); // See above OldSlotCount++; Slot.m_ItemCount = OldSlotCount; @@ -226,6 +249,77 @@ void cSlotArea::DblClicked(cPlayer & a_Player, int a_SlotNum) +void cSlotArea::MiddleClicked(cPlayer & a_Player, int a_SlotNum) +{ + cItem Slot(*GetSlot(a_SlotNum, a_Player)); + cItem & DraggingItem = a_Player.GetDraggingItem(); + + if (!a_Player.IsGameModeCreative() || Slot.IsEmpty() || !DraggingItem.IsEmpty()) + { + return; + } + + DraggingItem = Slot; + DraggingItem.m_ItemCount = DraggingItem.GetMaxStackSize(); +} + + + + + +void cSlotArea::DropClicked(cPlayer & a_Player, int a_SlotNum, bool a_DropStack) +{ + cItem Slot(*GetSlot(a_SlotNum, a_Player)); + if (Slot.IsEmpty()) + { + return; + } + + cItem ItemToDrop = Slot.CopyOne(); + if (a_DropStack) + { + ItemToDrop.m_ItemCount = Slot.m_ItemCount; + } + + Slot.m_ItemCount -= ItemToDrop.m_ItemCount; + if (Slot.m_ItemCount <= 0) + { + Slot.Empty(); + } + SetSlot(a_SlotNum, a_Player, Slot); + + a_Player.TossPickup(ItemToDrop); +} + + + + + +void cSlotArea::NumberClicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction) +{ + if ((a_ClickAction < caNumber1) || (a_ClickAction > caNumber9)) + { + return; + } + + int HotbarSlot = (int)a_ClickAction - (int)caNumber1; + cItem ItemInHotbar(a_Player.GetInventory().GetHotbarSlot(HotbarSlot)); + cItem ItemInSlot(*GetSlot(a_SlotNum, a_Player)); + + // The items are equal. Do nothing. + if (ItemInHotbar.IsEqual(ItemInSlot)) + { + return; + } + + a_Player.GetInventory().SetHotbarSlot(HotbarSlot, ItemInSlot); + SetSlot(a_SlotNum, a_Player, ItemInHotbar); +} + + + + + void cSlotArea::OnPlayerAdded(cPlayer & a_Player) { UNUSED(a_Player); @@ -244,7 +338,7 @@ void cSlotArea::OnPlayerRemoved(cPlayer & a_Player) -void cSlotArea::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_Apply, bool a_KeepEmptySlots) +void cSlotArea::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) { for (int i = 0; i < m_NumSlots; i++) { @@ -264,7 +358,7 @@ void cSlotArea::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ { NumFit = a_ItemStack.m_ItemCount; } - if (a_Apply) + if (a_ShouldApply) { cItem NewSlot(a_ItemStack); NewSlot.m_ItemCount = Slot->m_ItemCount + NumFit; @@ -315,7 +409,7 @@ bool cSlotArea::CollectItemsToHand(cItem & a_Dragging, cPlayer & a_Player, bool -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// // cSlotAreaChest: cSlotAreaChest::cSlotAreaChest(cChestEntity * a_Chest, cWindow & a_ParentWindow) : @@ -347,7 +441,7 @@ void cSlotAreaChest::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// // cSlotAreaDoubleChest: cSlotAreaDoubleChest::cSlotAreaDoubleChest(cChestEntity * a_TopChest, cChestEntity * a_BottomChest, cWindow & a_ParentWindow) : @@ -394,7 +488,7 @@ void cSlotAreaDoubleChest::SetSlot(int a_SlotNum, cPlayer & a_Player, const cIte -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// // cSlotAreaCrafting: cSlotAreaCrafting::cSlotAreaCrafting(int a_GridSize, cWindow & a_ParentWindow) : @@ -410,6 +504,12 @@ cSlotAreaCrafting::cSlotAreaCrafting(int a_GridSize, cWindow & a_ParentWindow) : void cSlotAreaCrafting::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) { + if (a_ClickAction == caMiddleClick) + { + MiddleClicked(a_Player, a_SlotNum); + return; + } + // Override for craft result slot if (a_SlotNum == 0) { @@ -417,12 +517,17 @@ void cSlotAreaCrafting::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction { ShiftClickedResult(a_Player); } + else if ((a_ClickAction == caDropKey) || (a_ClickAction == caCtrlDropKey)) + { + DropClickedResult(a_Player); + } else { ClickedResult(a_Player); } return; } + super::Clicked(a_Player, a_SlotNum, a_ClickAction, a_ClickedItem); UpdateRecipe(a_Player); } @@ -468,6 +573,20 @@ void cSlotAreaCrafting::OnPlayerRemoved(cPlayer & a_Player) +void cSlotAreaCrafting::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) +{ + // Update the recipe after setting the slot, if the slot is not the result slot: + super::SetSlot(a_SlotNum, a_Player, a_Item); + if (a_SlotNum != 0) + { + UpdateRecipe(a_Player); + } +} + + + + + void cSlotAreaCrafting::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) { UNUSED(a_ItemStack); @@ -496,6 +615,8 @@ void cSlotAreaCrafting::ClickedResult(cPlayer & a_Player) DraggingItem = Result; Recipe.ConsumeIngredients(Grid); Grid.CopyToItems(PlayerSlots); + + HandleCraftItem(Result, a_Player); } else if (DraggingItem.IsEqual(Result)) { @@ -505,6 +626,8 @@ void cSlotAreaCrafting::ClickedResult(cPlayer & a_Player) DraggingItem.m_ItemCount += Result.m_ItemCount; Recipe.ConsumeIngredients(Grid); Grid.CopyToItems(PlayerSlots); + + HandleCraftItem(Result, a_Player); } } @@ -541,16 +664,20 @@ void cSlotAreaCrafting::ShiftClickedResult(cPlayer & a_Player) // Distribute the result, this time for real: ResultCopy = Result; m_ParentWindow.DistributeStack(ResultCopy, a_Player, this, true); - + // Remove the ingredients from the crafting grid and update the recipe: cCraftingRecipe & Recipe = GetRecipeForPlayer(a_Player); cCraftingGrid Grid(PlayerSlots, m_GridSize, m_GridSize); Recipe.ConsumeIngredients(Grid); Grid.CopyToItems(PlayerSlots); UpdateRecipe(a_Player); + + // Broadcast the window, we sometimes move items to different locations than Vanilla, causing needless desyncs: + m_ParentWindow.BroadcastWholeWindow(); + + // If the recipe has changed, bail out: if (!Recipe.GetResult().IsEqual(Result)) { - // The recipe has changed, bail out return; } } @@ -560,6 +687,27 @@ void cSlotAreaCrafting::ShiftClickedResult(cPlayer & a_Player) +void cSlotAreaCrafting::DropClickedResult(cPlayer & a_Player) +{ + // Get the current recipe: + cCraftingRecipe & Recipe = GetRecipeForPlayer(a_Player); + const cItem & Result = Recipe.GetResult(); + + cItem * PlayerSlots = GetPlayerSlots(a_Player) + 1; + cCraftingGrid Grid(PlayerSlots, m_GridSize, m_GridSize); + + a_Player.TossPickup(Result); + Recipe.ConsumeIngredients(Grid); + Grid.CopyToItems(PlayerSlots); + + HandleCraftItem(Result, a_Player); + UpdateRecipe(a_Player); +} + + + + + void cSlotAreaCrafting::UpdateRecipe(cPlayer & a_Player) { cCraftingGrid Grid(GetPlayerSlots(a_Player) + 1, m_GridSize, m_GridSize); @@ -594,8 +742,456 @@ cCraftingRecipe & cSlotAreaCrafting::GetRecipeForPlayer(cPlayer & a_Player) +void cSlotAreaCrafting::HandleCraftItem(const cItem & a_Result, cPlayer & a_Player) +{ + switch (a_Result.m_ItemType) + { + case E_BLOCK_WORKBENCH: a_Player.AwardAchievement(achCraftWorkbench); break; + case E_BLOCK_FURNACE: a_Player.AwardAchievement(achCraftFurnace); break; + case E_BLOCK_CAKE: a_Player.AwardAchievement(achBakeCake); break; + case E_BLOCK_ENCHANTMENT_TABLE: a_Player.AwardAchievement(achCraftEnchantTable); break; + case E_BLOCK_BOOKCASE: a_Player.AwardAchievement(achBookshelf); break; + case E_ITEM_WOODEN_PICKAXE: a_Player.AwardAchievement(achCraftPickaxe); break; + case E_ITEM_WOODEN_SWORD: a_Player.AwardAchievement(achCraftSword); break; + case E_ITEM_STONE_PICKAXE: a_Player.AwardAchievement(achCraftBetterPick); break; + case E_ITEM_WOODEN_HOE: a_Player.AwardAchievement(achCraftHoe); break; + case E_ITEM_BREAD: a_Player.AwardAchievement(achMakeBread); break; + default: break; + } +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cSlotAreaAnvil: + +cSlotAreaAnvil::cSlotAreaAnvil(cAnvilWindow & a_ParentWindow) : + cSlotAreaTemporary(3, a_ParentWindow), + m_MaximumCost(0), + m_StackSizeToBeUsedInRepair(0) +{ +} + + + + + +void cSlotAreaAnvil::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) +{ + ASSERT((a_SlotNum >= 0) && (a_SlotNum < GetNumSlots())); + if (a_SlotNum != 2) + { + super::Clicked(a_Player, a_SlotNum, a_ClickAction, a_ClickedItem); + UpdateResult(a_Player); + return; + } + + bool bAsync = false; + if (GetSlot(a_SlotNum, a_Player) == NULL) + { + LOGWARNING("GetSlot(%d) returned NULL! Ignoring click", a_SlotNum); + return; + } + + switch (a_ClickAction) + { + case caDblClick: + { + return; + } + case caShiftLeftClick: + case caShiftRightClick: + { + ShiftClicked(a_Player, a_SlotNum, a_ClickedItem); + return; + } + case caMiddleClick: + { + MiddleClicked(a_Player, a_SlotNum); + return; + } + case caDropKey: + case caCtrlDropKey: + { + if (CanTakeResultItem(a_Player)) + { + DropClicked(a_Player, a_SlotNum, true); + OnTakeResult(a_Player); + } + return; + } + default: + { + break; + } + } + + cItem Slot(*GetSlot(a_SlotNum, a_Player)); + if (!Slot.IsSameType(a_ClickedItem)) + { + LOGWARNING("*** Window lost sync at item %d in SlotArea with %d items ***", a_SlotNum, m_NumSlots); + LOGWARNING("My item: %s", ItemToFullString(Slot).c_str()); + LOGWARNING("Their item: %s", ItemToFullString(a_ClickedItem).c_str()); + bAsync = true; + } + cItem & DraggingItem = a_Player.GetDraggingItem(); + + if (Slot.IsEmpty()) + { + return; + } + if (!DraggingItem.IsEmpty()) + { + if (!(DraggingItem.IsEqual(Slot) && ((DraggingItem.m_ItemCount + Slot.m_ItemCount) <= cItemHandler::GetItemHandler(Slot)->GetMaxStackSize()))) + { + return; + } + } + + if (!CanTakeResultItem(a_Player)) + { + return; + } + + cItem NewItem = cItem(Slot); + NewItem.m_ItemCount += DraggingItem.m_ItemCount; + + Slot.Empty(); + DraggingItem.Empty(); + SetSlot(a_SlotNum, a_Player, Slot); + + DraggingItem = NewItem; + OnTakeResult(a_Player); + + if (bAsync) + { + m_ParentWindow.BroadcastWholeWindow(); + } +} + + + + + +void cSlotAreaAnvil::ShiftClicked(cPlayer & a_Player, int a_SlotNum, const cItem & a_ClickedItem) +{ + if (a_SlotNum != 2) + { + super::ShiftClicked(a_Player, a_SlotNum, a_ClickedItem); + UpdateResult(a_Player); + return; + } + + // Make a copy of the slot, distribute it among the other areas, then update the slot to contain the leftover: + cItem Slot(*GetSlot(a_SlotNum, a_Player)); + + if (Slot.IsEmpty() || !CanTakeResultItem(a_Player)) + { + return; + } + + m_ParentWindow.DistributeStack(Slot, a_Player, this, true); + if (Slot.IsEmpty()) + { + Slot.Empty(); + OnTakeResult(a_Player); + } + SetSlot(a_SlotNum, a_Player, Slot); + + // Some clients try to guess our actions and not always right (armor slots in 1.2.5), so we fix them: + m_ParentWindow.BroadcastWholeWindow(); +} + + + + + +void cSlotAreaAnvil::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) +{ + for (int i = 0; i < 2; i++) + { + const cItem * Slot = GetSlot(i, a_Player); + if (!Slot->IsEqual(a_ItemStack) && (!Slot->IsEmpty() || a_KeepEmptySlots)) + { + // Different items + continue; + } + int NumFit = ItemHandler(Slot->m_ItemType)->GetMaxStackSize() - Slot->m_ItemCount; + if (NumFit <= 0) + { + // Full stack already + continue; + } + if (NumFit > a_ItemStack.m_ItemCount) + { + NumFit = a_ItemStack.m_ItemCount; + } + if (a_ShouldApply) + { + cItem NewSlot(a_ItemStack); + NewSlot.m_ItemCount = Slot->m_ItemCount + NumFit; + SetSlot(i, a_Player, NewSlot); + } + a_ItemStack.m_ItemCount -= NumFit; + if (a_ItemStack.IsEmpty()) + { + UpdateResult(a_Player); + return; + } + } // for i - Slots + UpdateResult(a_Player); +} + + + + + +void cSlotAreaAnvil::OnTakeResult(cPlayer & a_Player) +{ + if (!a_Player.IsGameModeCreative()) + { + a_Player.DeltaExperience(-cPlayer::XpForLevel(m_MaximumCost)); + } + SetSlot(0, a_Player, cItem()); + + if (m_StackSizeToBeUsedInRepair > 0) + { + const cItem * Item = GetSlot(1, a_Player); + if (!Item->IsEmpty() && (Item->m_ItemCount > m_StackSizeToBeUsedInRepair)) + { + cItem NewSecondItem(*Item); + NewSecondItem.m_ItemCount -= m_StackSizeToBeUsedInRepair; + m_StackSizeToBeUsedInRepair = 0; + SetSlot(1, a_Player, NewSecondItem); + } + else + { + SetSlot(1, a_Player, cItem()); + } + } + else + { + SetSlot(1, a_Player, cItem()); + } + m_ParentWindow.SetProperty(0, m_MaximumCost, a_Player); + + m_MaximumCost = 0; + ((cAnvilWindow*)&m_ParentWindow)->SetRepairedItemName("", NULL); + + int PosX, PosY, PosZ; + ((cAnvilWindow*)&m_ParentWindow)->GetBlockPos(PosX, PosY, PosZ); + + BLOCKTYPE Block; + NIBBLETYPE BlockMeta; + a_Player.GetWorld()->GetBlockTypeMeta(PosX, PosY, PosZ, Block, BlockMeta); + + cFastRandom Random; + if (!a_Player.IsGameModeCreative() && (Block == E_BLOCK_ANVIL) && (Random.NextFloat(1.0F) < 0.12F)) + { + NIBBLETYPE Orientation = BlockMeta & 0x3; + NIBBLETYPE AnvilDamage = BlockMeta >> 2; + ++AnvilDamage; + + if (AnvilDamage > 2) + { + // Anvil will break + a_Player.GetWorld()->SetBlock(PosX, PosY, PosZ, E_BLOCK_AIR, (NIBBLETYPE)0); + a_Player.GetWorld()->BroadcastSoundParticleEffect(1020, PosX, PosY, PosZ, 0); + a_Player.CloseWindow(false); + } + else + { + a_Player.GetWorld()->SetBlockMeta(PosX, PosY, PosZ, Orientation | (AnvilDamage << 2)); + a_Player.GetWorld()->BroadcastSoundParticleEffect(1021, PosX, PosY, PosZ, 0); + } + } + else + { + a_Player.GetWorld()->BroadcastSoundParticleEffect(1021, PosX, PosY, PosZ, 0); + } +} + + + + -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool cSlotAreaAnvil::CanTakeResultItem(cPlayer & a_Player) +{ + return ( + ( + a_Player.IsGameModeCreative() || // Is the player in gamemode? + (a_Player.GetXpLevel() >= m_MaximumCost) // or the player have enough exp? + ) && + (!GetSlot(2, a_Player)->IsEmpty()) && // Is a item in the result slot? + (m_MaximumCost > 0) // When no maximum cost is set, the item isn't set from the UpdateResult() method and can't be a valid enchanting result. + ); +} + + + + + +void cSlotAreaAnvil::OnPlayerRemoved(cPlayer & a_Player) +{ + TossItems(a_Player, 0, 2); + super::OnPlayerRemoved(a_Player); +} + + + + + +void cSlotAreaAnvil::UpdateResult(cPlayer & a_Player) +{ + cItem Input(*GetSlot(0, a_Player)); + cItem SecondInput(*GetSlot(1, a_Player)); + cItem Output(*GetSlot(2, a_Player)); + + if (Input.IsEmpty() && !Output.IsEmpty()) + { + Output.Empty(); + SetSlot(2, a_Player, Output); + m_ParentWindow.SetProperty(0, 0, a_Player); + return; + } + + m_MaximumCost = 0; + m_StackSizeToBeUsedInRepair = 0; + int RepairCost = Input.m_RepairCost; + int NeedExp = 0; + bool IsEnchantBook = false; + if (!SecondInput.IsEmpty()) + { + IsEnchantBook = (SecondInput.m_ItemType == E_ITEM_ENCHANTED_BOOK); + + RepairCost += SecondInput.m_RepairCost; + if (Input.IsDamageable() && cItemHandler::GetItemHandler(Input)->CanRepairWithRawMaterial(SecondInput.m_ItemType)) + { + // Tool and armor repair with special item (iron / gold / diamond / ...) + int DamageDiff = std::min((int)Input.m_ItemDamage, (int)Input.GetMaxDamage() / 4); + if (DamageDiff <= 0) + { + // No enchantment + Output.Empty(); + SetSlot(2, a_Player, Output); + m_ParentWindow.SetProperty(0, 0, a_Player); + return; + } + + int x = 0; + while ((DamageDiff > 0) && (x < SecondInput.m_ItemCount)) + { + Input.m_ItemDamage -= DamageDiff; + NeedExp += std::max(1, DamageDiff / 100) + (int)Input.m_Enchantments.Count(); + DamageDiff = std::min((int)Input.m_ItemDamage, (int)Input.GetMaxDamage() / 4); + + ++x; + } + m_StackSizeToBeUsedInRepair = x; + } + else + { + // Tool and armor repair with two tools / armors + if (!IsEnchantBook && (!Input.IsSameType(SecondInput) || !Input.IsDamageable())) + { + // No enchantment + Output.Empty(); + SetSlot(2, a_Player, Output); + m_ParentWindow.SetProperty(0, 0, a_Player); + return; + } + + if ((Input.GetMaxDamage() > 0) && !IsEnchantBook) + { + int FirstDamageDiff = Input.GetMaxDamage() - Input.m_ItemDamage; + int SecondDamageDiff = SecondInput.GetMaxDamage() - SecondInput.m_ItemDamage; + int Damage = SecondDamageDiff + Input.GetMaxDamage() * 12 / 100; + + int NewItemDamage = Input.GetMaxDamage() - (FirstDamageDiff + Damage); + if (NewItemDamage > 0) + { + NewItemDamage = 0; + } + + if (NewItemDamage < Input.m_ItemDamage) + { + Input.m_ItemDamage = NewItemDamage; + NeedExp += std::max(1, Damage / 100); + } + } + + // TODO: Add enchantments. + } + } + + int NameChangeExp = 0; + const AString & RepairedItemName = ((cAnvilWindow*)&m_ParentWindow)->GetRepairedItemName(); + if (RepairedItemName.empty()) + { + // Remove custom name + if (!Input.m_CustomName.empty()) + { + NameChangeExp = (Input.IsDamageable()) ? 7 : (Input.m_ItemCount * 5); + NeedExp += NameChangeExp; + Input.m_CustomName = ""; + } + } + else if (RepairedItemName != Input.m_CustomName) + { + // Change custom name + NameChangeExp = (Input.IsDamageable()) ? 7 : (Input.m_ItemCount * 5); + NeedExp += NameChangeExp; + + if (!Input.m_CustomName.empty()) + { + RepairCost += NameChangeExp / 2; + } + + Input.m_CustomName = RepairedItemName; + } + + // TODO: Add enchantment exp cost. + + m_MaximumCost = RepairCost + NeedExp; + + if (NeedExp < 0) + { + Input.Empty(); + } + + if ((NameChangeExp == NeedExp) && (NameChangeExp > 0) && (m_MaximumCost >= 40)) + { + m_MaximumCost = 39; + } + if (m_MaximumCost >= 40 && !a_Player.IsGameModeCreative()) + { + Input.Empty(); + } + + if (!Input.IsEmpty()) + { + RepairCost = std::max(Input.m_RepairCost, SecondInput.m_RepairCost); + if (!Input.m_CustomName.empty()) + { + RepairCost -= 9; + } + RepairCost = std::max(RepairCost, 0); + RepairCost += 2; + Input.m_RepairCost = RepairCost; + } + + SetSlot(2, a_Player, Input); + m_ParentWindow.SetProperty(0, m_MaximumCost, a_Player); +} + + + + + + +//////////////////////////////////////////////////////////////////////////////// // cSlotAreaEnchanting: cSlotAreaEnchanting::cSlotAreaEnchanting(cEnchantingWindow & a_ParentWindow) : @@ -627,12 +1223,16 @@ void cSlotAreaEnchanting::Clicked(cPlayer & a_Player, int a_SlotNum, eClickActio ShiftClicked(a_Player, a_SlotNum, a_ClickedItem); return; } - case caDblClick: { DblClicked(a_Player, a_SlotNum); return; } + case caMiddleClick: + { + MiddleClicked(a_Player, a_SlotNum); + return; + } default: { break; @@ -680,7 +1280,7 @@ void cSlotAreaEnchanting::Clicked(cPlayer & a_Player, int a_SlotNum, eClickActio } case caLeftClick: - { + { // Left-clicked if (DraggingItem.IsEmpty()) { @@ -896,7 +1496,7 @@ int cSlotAreaEnchanting::GetBookshelvesCount(cWorld * a_World) -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// // cSlotAreaEnderChest: cSlotAreaEnderChest::cSlotAreaEnderChest(cEnderChestEntity * a_EnderChest, cWindow & a_ParentWindow) : @@ -911,8 +1511,7 @@ cSlotAreaEnderChest::cSlotAreaEnderChest(cEnderChestEntity * a_EnderChest, cWind const cItem * cSlotAreaEnderChest::GetSlot(int a_SlotNum, cPlayer & a_Player) const { - // a_SlotNum ranges from 0 to 26, use that to index the chest entity's inventory directly: - return &(m_EnderChest->GetSlot(a_SlotNum)); + return &(a_Player.GetEnderChestContents().GetSlot(a_SlotNum)); } @@ -921,14 +1520,14 @@ const cItem * cSlotAreaEnderChest::GetSlot(int a_SlotNum, cPlayer & a_Player) co void cSlotAreaEnderChest::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) { - m_EnderChest->SetSlot(a_SlotNum, a_Item); + a_Player.GetEnderChestContents().SetSlot(a_SlotNum, a_Item); } -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// // cSlotAreaFurnace: cSlotAreaFurnace::cSlotAreaFurnace(cFurnaceEntity * a_Furnace, cWindow & a_ParentWindow) : @@ -953,14 +1552,162 @@ cSlotAreaFurnace::~cSlotAreaFurnace() void cSlotAreaFurnace::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) { - super::Clicked(a_Player, a_SlotNum, a_ClickAction, a_ClickedItem); - if (m_Furnace == NULL) { LOGERROR("cSlotAreaFurnace::Clicked(): m_Furnace == NULL"); ASSERT(!"cSlotAreaFurnace::Clicked(): m_Furnace == NULL"); return; } + + if (a_SlotNum == 2) + { + 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.IsSameType(a_ClickedItem)) + { + LOGWARNING("*** Window lost sync at item %d in SlotArea with %d items ***", a_SlotNum, m_NumSlots); + LOGWARNING("My item: %s", ItemToFullString(Slot).c_str()); + LOGWARNING("Their item: %s", ItemToFullString(a_ClickedItem).c_str()); + bAsync = true; + } + + switch (a_ClickAction) + { + case caShiftLeftClick: + case caShiftRightClick: + { + HandleSmeltItem(Slot, a_Player); + ShiftClicked(a_Player, a_SlotNum, Slot); + return; + } + case caMiddleClick: + { + MiddleClicked(a_Player, a_SlotNum); + return; + } + case caDropKey: + case caCtrlDropKey: + { + DropClicked(a_Player, a_SlotNum, (a_SlotNum == caCtrlDropKey)); + Slot.m_ItemCount = Slot.m_ItemCount - GetSlot(a_SlotNum, a_Player)->m_ItemCount; + HandleSmeltItem(Slot, a_Player); + return; + } + default: + { + break; + } + } + + cItem & DraggingItem = a_Player.GetDraggingItem(); + if (!DraggingItem.IsEmpty()) + { + if (a_ClickAction == caDblClick) + { + return; + } + if (!DraggingItem.IsEqual(Slot)) + { + return; + } + if ((DraggingItem.m_ItemCount + Slot.m_ItemCount) > Slot.GetMaxStackSize()) + { + return; + } + + DraggingItem.m_ItemCount += Slot.m_ItemCount; + HandleSmeltItem(Slot, a_Player); + Slot.Empty(); + } + else + { + switch (a_ClickAction) + { + case caDblClick: + { + DblClicked(a_Player, a_SlotNum); + return; + } + case caLeftClick: + { + DraggingItem = Slot; + HandleSmeltItem(Slot, a_Player); + Slot.Empty(); + break; + } + case caRightClick: + { + DraggingItem = Slot.CopyOne(); + DraggingItem.m_ItemCount = (char)(((float)Slot.m_ItemCount) / 2.f + 0.5f); + Slot.m_ItemCount -= DraggingItem.m_ItemCount; + + if (Slot.m_ItemCount <= 0) + { + Slot.Empty(); + } + HandleSmeltItem(DraggingItem, a_Player); + break; + } + default: + { + ASSERT(!"Unhandled click type!"); + } + } + } + + SetSlot(a_SlotNum, a_Player, Slot); + if (bAsync) + { + m_ParentWindow.BroadcastWholeWindow(); + } + return; + } + + super::Clicked(a_Player, a_SlotNum, a_ClickAction, a_ClickedItem); +} + + + + + +void cSlotAreaFurnace::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) +{ + for (int i = 0; i < 2; i++) + { + const cItem * Slot = GetSlot(i, a_Player); + if (!Slot->IsEqual(a_ItemStack) && (!Slot->IsEmpty() || a_KeepEmptySlots)) + { + // Different items + continue; + } + int NumFit = ItemHandler(Slot->m_ItemType)->GetMaxStackSize() - Slot->m_ItemCount; + if (NumFit <= 0) + { + // Full stack already + continue; + } + if (NumFit > a_ItemStack.m_ItemCount) + { + NumFit = a_ItemStack.m_ItemCount; + } + if (a_ShouldApply) + { + cItem NewSlot(a_ItemStack); + NewSlot.m_ItemCount = Slot->m_ItemCount + NumFit; + SetSlot(i, a_Player, NewSlot); + } + a_ItemStack.m_ItemCount -= NumFit; + if (a_ItemStack.IsEmpty()) + { + return; + } + } // for i - Slots } @@ -969,6 +1716,7 @@ void cSlotAreaFurnace::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a const cItem * cSlotAreaFurnace::GetSlot(int a_SlotNum, cPlayer & a_Player) const { + UNUSED(a_Player); // a_SlotNum ranges from 0 to 2, query the items from the underlying furnace: return &(m_Furnace->GetSlot(a_SlotNum)); } @@ -979,6 +1727,7 @@ const cItem * cSlotAreaFurnace::GetSlot(int a_SlotNum, cPlayer & a_Player) const void cSlotAreaFurnace::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) { + UNUSED(a_Player); m_Furnace->SetSlot(a_SlotNum, a_Item); } @@ -988,9 +1737,10 @@ void cSlotAreaFurnace::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & void cSlotAreaFurnace::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) { + UNUSED(a_SlotNum); // Something has changed in the window, broadcast the entire window to all clients ASSERT(a_ItemGrid == &(m_Furnace->GetContents())); - + m_ParentWindow.BroadcastWholeWindow(); } @@ -998,7 +1748,22 @@ void cSlotAreaFurnace::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void cSlotAreaFurnace::HandleSmeltItem(const cItem & a_Result, cPlayer & a_Player) +{ + /** TODO 2014-05-12 xdot: Figure out when to call this method. */ + switch (a_Result.m_ItemType) + { + case E_ITEM_IRON: a_Player.AwardAchievement(achAcquireIron); break; + case E_ITEM_COOKED_FISH: a_Player.AwardAchievement(achCookFish); break; + default: break; + } +} + + + + + +//////////////////////////////////////////////////////////////////////////////// // cSlotAreaInventoryBase: cSlotAreaInventoryBase::cSlotAreaInventoryBase(int a_NumSlots, int a_SlotOffset, cWindow & a_ParentWindow) : @@ -1015,6 +1780,12 @@ void cSlotAreaInventoryBase::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAc { if (a_Player.IsGameModeCreative() && (m_ParentWindow.GetWindowType() == cWindow::wtInventory)) { + if ((a_ClickAction == caDropKey) || (a_ClickAction == caCtrlDropKey)) + { + DropClicked(a_Player, a_SlotNum, (a_ClickAction == caCtrlDropKey)); + return; + } + // Creative inventory must treat a_ClickedItem as a DraggedItem instead, replacing the inventory slot with it SetSlot(a_SlotNum, a_Player, a_ClickedItem); return; @@ -1048,7 +1819,7 @@ void cSlotAreaInventoryBase::SetSlot(int a_SlotNum, cPlayer & a_Player, const cI -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// // cSlotAreaArmor: void cSlotAreaArmor::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) @@ -1095,23 +1866,48 @@ void cSlotAreaArmor::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_C { ASSERT((a_SlotNum >= 0) && (a_SlotNum < GetNumSlots())); - bool bAsync = false; - if (GetSlot(a_SlotNum, a_Player) == NULL) + // When the player is in creative mode, the client sends the new item as a_ClickedItem, not the current item in the slot. + if (a_Player.IsGameModeCreative() && (m_ParentWindow.GetWindowType() == cWindow::wtInventory)) { - LOGWARNING("GetSlot(%d) returned NULL! Ignoring click", a_SlotNum); + if ((a_ClickAction == caDropKey) || (a_ClickAction == caCtrlDropKey)) + { + DropClicked(a_Player, a_SlotNum, (a_ClickAction == caCtrlDropKey)); + return; + } + + SetSlot(a_SlotNum, a_Player, a_ClickedItem); return; } - if ((a_ClickAction == caShiftLeftClick) || (a_ClickAction == caShiftRightClick)) + bool bAsync = false; + if (GetSlot(a_SlotNum, a_Player) == NULL) { - ShiftClicked(a_Player, a_SlotNum, a_ClickedItem); + LOGWARNING("GetSlot(%d) returned NULL! Ignoring click", a_SlotNum); return; } - // Armors haven't a dbl click - if (a_ClickAction == caDblClick) + switch (a_ClickAction) { - return; + case caDblClick: + { + // Armors haven't a dbl click + return; + } + case caShiftLeftClick: + case caShiftRightClick: + { + ShiftClicked(a_Player, a_SlotNum, a_ClickedItem); + return; + } + case caMiddleClick: + { + MiddleClicked(a_Player, a_SlotNum); + return; + } + default: + { + break; + } } cItem Slot(*GetSlot(a_SlotNum, a_Player)); @@ -1130,7 +1926,7 @@ void cSlotAreaArmor::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_C return; } - if (DraggingItem.IsEmpty() || CanPlaceInSlot(a_SlotNum, DraggingItem)) + if (DraggingItem.IsEmpty() || CanPlaceArmorInSlot(a_SlotNum, DraggingItem)) { // Swap contents cItem tmp(DraggingItem); @@ -1149,7 +1945,7 @@ void cSlotAreaArmor::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_C -bool cSlotAreaArmor::CanPlaceInSlot(int a_SlotNum, const cItem & a_Item) +bool cSlotAreaArmor::CanPlaceArmorInSlot(int a_SlotNum, const cItem & a_Item) { switch (a_SlotNum) { @@ -1165,7 +1961,7 @@ bool cSlotAreaArmor::CanPlaceInSlot(int a_SlotNum, const cItem & a_Item) -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// // cSlotAreaItemGrid: cSlotAreaItemGrid::cSlotAreaItemGrid(cItemGrid & a_ItemGrid, cWindow & a_ParentWindow) : @@ -1216,7 +2012,7 @@ void cSlotAreaItemGrid::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// // cSlotAreaTemporary: cSlotAreaTemporary::cSlotAreaTemporary(int a_NumSlots, cWindow & a_ParentWindow) : @@ -1321,7 +2117,7 @@ void cSlotAreaTemporary::TossItems(cPlayer & a_Player, int a_Begin, int a_End) double vX = 0, vY = 0, vZ = 0; EulerToVector(-a_Player.GetYaw(), 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 * 3, vY * 3, vZ * 3, true); // 'true' because player created + a_Player.GetWorld()->SpawnItemPickups(Drops, a_Player.GetPosX(), a_Player.GetPosY() + 1.6f, a_Player.GetPosZ(), vX * 3, vY * 3, vZ * 3, true); // 'true' because player created } diff --git a/src/UI/SlotArea.h b/src/UI/SlotArea.h index 254722822..fa842bb81 100644 --- a/src/UI/SlotArea.h +++ b/src/UI/SlotArea.h @@ -9,6 +9,7 @@ #pragma once #include "../Inventory.h" +#include "Window.h" @@ -45,10 +46,19 @@ public: /// Called from Clicked when the action is a shiftclick (left or right) virtual void ShiftClicked(cPlayer & a_Player, int a_SlotNum, const cItem & a_ClickedItem); - + /// Called from Clicked when the action is a caDblClick virtual void DblClicked(cPlayer & a_Player, int a_SlotNum); - + + /** Called from Clicked when the action is a middleclick */ + virtual void MiddleClicked(cPlayer & a_Player, int a_SlotNum); + + /** Called from Clicked when the action is a drop click. */ + virtual void DropClicked(cPlayer & a_Player, int a_SlotNum, bool a_DropStack); + + /** Called from Clicked when the action is a number click. */ + virtual void NumberClicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction); + /// Called when a new player opens the same parent window. The window already tracks the player. CS-locked. virtual void OnPlayerAdded(cPlayer & a_Player); @@ -125,7 +135,7 @@ class cSlotAreaHotBar : typedef cSlotAreaInventoryBase super; public: - cSlotAreaHotBar(cWindow & a_ParentWindow) : + cSlotAreaHotBar(cWindow & a_ParentWindow) : cSlotAreaInventoryBase(cInventory::invHotbarCount, cInventory::invHotbarOffset, a_ParentWindow) { } @@ -140,7 +150,7 @@ class cSlotAreaArmor : public cSlotAreaInventoryBase { public: - cSlotAreaArmor(cWindow & a_ParentWindow) : + cSlotAreaArmor(cWindow & a_ParentWindow) : cSlotAreaInventoryBase(cInventory::invArmorCount, cInventory::invArmorOffset, a_ParentWindow) { } @@ -151,7 +161,7 @@ public: /** Called when a player clicks in the window. Parameters taken from the click packet. */ virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override; - bool CanPlaceInSlot(int a_SlotNum, const cItem & a_Item); + static bool CanPlaceArmorInSlot(int a_SlotNum, const cItem & a_Item); } ; @@ -231,10 +241,12 @@ public: virtual void Clicked (cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override; virtual void DblClicked (cPlayer & a_Player, int a_SlotNum); virtual void OnPlayerRemoved(cPlayer & a_Player) override; + virtual void SetSlot (int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override; // Distributing items into this area is completely disabled virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override; + protected: /// Maps player's EntityID -> current recipe; not a std::map because cCraftingGrid needs proper constructor params typedef std::list<std::pair<int, cCraftingRecipe> > cRecipeMap; @@ -247,12 +259,55 @@ protected: /// Handles a shift-click in the result slot. Crafts using the current recipe until it changes or no more space for result. void ShiftClickedResult(cPlayer & a_Player); - + + /** Handles a drop-click in the result slot. */ + void DropClickedResult(cPlayer & a_Player); + /// Updates the current recipe and result slot based on the ingredients currently in the crafting grid of the specified player void UpdateRecipe(cPlayer & a_Player); /// Retrieves the recipe for the specified player from the map, or creates one if not found cCraftingRecipe & GetRecipeForPlayer(cPlayer & a_Player); + + /// Called after an item has been crafted to handle statistics e.t.c. + void HandleCraftItem(const cItem & a_Result, cPlayer & a_Player); +} ; + + + + + +class cSlotAreaAnvil : + public cSlotAreaTemporary +{ + typedef cSlotAreaTemporary super; + +public: + cSlotAreaAnvil(cAnvilWindow & a_ParentWindow); + + // cSlotArea overrides: + virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override; + virtual void ShiftClicked(cPlayer & a_Player, int a_SlotNum, const cItem & a_ClickedItem) override; + virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override; + + // cSlotAreaTemporary overrides: + virtual void OnPlayerRemoved(cPlayer & a_Player) override; + + /** Can the player take the item from the slot? */ + bool CanTakeResultItem(cPlayer & a_Player); + + /** This function will call, when the player take the item from the slot. */ + void OnTakeResult(cPlayer & a_Player); + + /** Handles a click in the item slot. */ + void UpdateResult(cPlayer & a_Player); + +protected: + /** The maximum cost of repairing/renaming in the anvil. */ + int m_MaximumCost; + + /** The stack size of the second item where was used for repair */ + char m_StackSizeToBeUsedInRepair; } ; @@ -351,6 +406,7 @@ public: virtual ~cSlotAreaFurnace(); virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override; + virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override; virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override; virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override; @@ -359,6 +415,9 @@ protected: // cItemGrid::cListener overrides: virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override; + + /// Called after an item has been smelted to handle statistics e.t.c. + void HandleSmeltItem(const cItem & a_Result, cPlayer & a_Player); } ; diff --git a/src/UI/Window.cpp b/src/UI/Window.cpp index 0a78578fc..4731f282b 100644 --- a/src/UI/Window.cpp +++ b/src/UI/Window.cpp @@ -146,7 +146,6 @@ void cWindow::GetSlots(cPlayer & a_Player, cItems & a_Slots) const int NumSlots = (*itr)->GetNumSlots(); for (int i = 0; i < NumSlots; i++) { - const cItem * Item = (*itr)->GetSlot(i, a_Player); if (Item == NULL) { @@ -165,12 +164,12 @@ void cWindow::GetSlots(cPlayer & a_Player, cItems & a_Slots) const void cWindow::Clicked( - cPlayer & a_Player, + cPlayer & a_Player, int a_WindowID, short a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem ) { - cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager(); + cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager(); if (a_WindowID != m_WindowID) { LOGWARNING("%s: Wrong window ID (exp %d, got %d) received from \"%s\"; ignoring click.", __FUNCTION__, m_WindowID, a_WindowID, a_Player.GetName().c_str()); @@ -179,6 +178,7 @@ void cWindow::Clicked( switch (a_ClickAction) { + case caLeftClickOutside: case caRightClickOutside: { if (PlgMgr->CallHookPlayerTossingItem(a_Player)) @@ -191,25 +191,16 @@ void cWindow::Clicked( a_Player.TossPickup(a_ClickedItem); } - // Toss one of the dragged items: - a_Player.TossHeldItem(); - return; - } - case caLeftClickOutside: - { - if (PlgMgr->CallHookPlayerTossingItem(a_Player)) + if (a_ClickAction == caLeftClickOutside) { - // A plugin doesn't agree with the tossing. The plugin itself is responsible for handling the consequences (possible inventory mismatch) - return; + // Toss all dragged items: + a_Player.TossHeldItem(a_Player.GetDraggingItem().m_ItemCount); } - - if (a_Player.IsGameModeCreative()) + else { - a_Player.TossPickup(a_ClickedItem); + // Toss one of the dragged items: + a_Player.TossHeldItem(); } - - // Toss all dragged items: - a_Player.TossHeldItem(a_Player.GetDraggingItem().m_ItemCount); return; } case caLeftClickOutsideHoldNothing: @@ -283,7 +274,7 @@ void cWindow::OpenedByPlayer(cPlayer & a_Player) bool cWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) { // Checks whether the player is still holding an item - if (a_Player.IsDraggingItem()) + if (!a_Player.GetDraggingItem().IsEmpty()) { LOGD("Player holds item! Dropping it..."); a_Player.TossHeldItem(a_Player.GetDraggingItem().m_ItemCount); @@ -328,9 +319,9 @@ void cWindow::OwnerDestroyed() // Close window for each player. Note that the last one needs special handling while (m_OpenedBy.size() > 1) { - (*m_OpenedBy.begin() )->CloseWindow(); + (*m_OpenedBy.begin())->CloseWindow(); } - (*m_OpenedBy.begin() )->CloseWindow(); + (*m_OpenedBy.begin())->CloseWindow(); } @@ -591,7 +582,7 @@ void cWindow::OnLeftPaintEnd(cPlayer & a_Player) const cSlotNums & SlotNums = a_Player.GetInventoryPaintSlots(); cItem ToDistribute(a_Player.GetDraggingItem()); - int ToEachSlot = (int)ToDistribute.m_ItemCount / SlotNums.size(); + int ToEachSlot = (int)ToDistribute.m_ItemCount / (int)SlotNums.size(); int NumDistributed = DistributeItemToSlots(a_Player, ToDistribute, ToEachSlot, SlotNums); @@ -772,7 +763,7 @@ void cWindow::SetProperty(int a_Property, int a_Value, cPlayer & a_Player) -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// // cInventoryWindow: cInventoryWindow::cInventoryWindow(cPlayer & a_Player) : @@ -789,7 +780,7 @@ cInventoryWindow::cInventoryWindow(cPlayer & a_Player) : -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// // cCraftingWindow: cCraftingWindow::cCraftingWindow(int a_BlockX, int a_BlockY, int a_BlockZ) : @@ -804,11 +795,57 @@ cCraftingWindow::cCraftingWindow(int a_BlockX, int a_BlockY, int a_BlockZ) : -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// cAnvilWindow: + +cAnvilWindow::cAnvilWindow(int a_BlockX, int a_BlockY, int a_BlockZ) : + cWindow(wtAnvil, "Repair"), + m_RepairedItemName(""), + m_BlockX(a_BlockX), + m_BlockY(a_BlockY), + m_BlockZ(a_BlockZ) +{ + m_AnvilSlotArea = new cSlotAreaAnvil(*this); + m_SlotAreas.push_back(m_AnvilSlotArea); + m_SlotAreas.push_back(new cSlotAreaInventory(*this)); + m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); +} + + + + + +void cAnvilWindow::SetRepairedItemName(const AString & a_Name, cPlayer * a_Player) +{ + m_RepairedItemName = a_Name; + + if (a_Player != NULL) + { + m_AnvilSlotArea->UpdateResult(*a_Player); + } +} + + + + + +void cAnvilWindow::GetBlockPos(int & a_PosX, int & a_PosY, int & a_PosZ) +{ + a_PosX = m_BlockX; + a_PosY = m_BlockY; + a_PosZ = m_BlockZ; +} + + + + + +//////////////////////////////////////////////////////////////////////////////// // cEnchantingWindow: cEnchantingWindow::cEnchantingWindow(int a_BlockX, int a_BlockY, int a_BlockZ) : cWindow(wtEnchantment, "Enchant"), + m_SlotArea(), m_BlockX(a_BlockX), m_BlockY(a_BlockY), m_BlockZ(a_BlockZ) @@ -864,25 +901,27 @@ void cEnchantingWindow::GetBlockPos(int & a_PosX, int & a_PosY, int & a_PosZ) -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// // cChestWindow: cChestWindow::cChestWindow(cChestEntity * a_Chest) : - cWindow(wtChest, "Chest"), + cWindow(wtChest, (a_Chest->GetBlockType() == E_BLOCK_CHEST) ? "Chest" : "Trapped Chest"), m_World(a_Chest->GetWorld()), m_BlockX(a_Chest->GetPosX()), m_BlockY(a_Chest->GetPosY()), - m_BlockZ(a_Chest->GetPosZ()) + m_BlockZ(a_Chest->GetPosZ()), + m_PrimaryChest(a_Chest), + m_SecondaryChest(NULL) { 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); + m_World->BroadcastSoundEffect("random.chestopen", (double)m_BlockX, (double)m_BlockY, (double)m_BlockZ, 1, 1); // Send out the chest-open packet: - m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 1, E_BLOCK_CHEST); + m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 1, a_Chest->GetBlockType()); } @@ -890,11 +929,13 @@ cChestWindow::cChestWindow(cChestEntity * a_Chest) : cChestWindow::cChestWindow(cChestEntity * a_PrimaryChest, cChestEntity * a_SecondaryChest) : - cWindow(wtChest, "Double Chest"), + cWindow(wtChest, (a_PrimaryChest->GetBlockType() == E_BLOCK_CHEST) ? "Double Chest" : "Double Trapped Chest"), m_World(a_PrimaryChest->GetWorld()), m_BlockX(a_PrimaryChest->GetPosX()), m_BlockY(a_PrimaryChest->GetPosY()), - m_BlockZ(a_PrimaryChest->GetPosZ()) + m_BlockZ(a_PrimaryChest->GetPosZ()), + m_PrimaryChest(a_PrimaryChest), + m_SecondaryChest(a_SecondaryChest) { m_SlotAreas.push_back(new cSlotAreaDoubleChest(a_PrimaryChest, a_SecondaryChest, *this)); m_SlotAreas.push_back(new cSlotAreaInventory(*this)); @@ -903,10 +944,55 @@ cChestWindow::cChestWindow(cChestEntity * a_PrimaryChest, cChestEntity * a_Secon m_ShouldDistributeToHotbarFirst = false; // Play the opening sound: - m_World->BroadcastSoundEffect("random.chestopen", m_BlockX * 8, m_BlockY * 8, m_BlockZ * 8, 1, 1); + m_World->BroadcastSoundEffect("random.chestopen", (double)m_BlockX, (double)m_BlockY, (double)m_BlockZ, 1, 1); // Send out the chest-open packet: - m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 1, E_BLOCK_CHEST); + m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 1, a_PrimaryChest->GetBlockType()); +} + + + + + +void cChestWindow::OpenedByPlayer(cPlayer & a_Player) +{ + int ChunkX, ChunkZ; + + m_PrimaryChest->SetNumberOfPlayers(m_PrimaryChest->GetNumberOfPlayers() + 1); + cChunkDef::BlockToChunk(m_PrimaryChest->GetPosX(), m_PrimaryChest->GetPosZ(), ChunkX, ChunkZ); + m_PrimaryChest->GetWorld()->MarkRedstoneDirty(ChunkX, ChunkZ); + + if (m_SecondaryChest != NULL) + { + m_SecondaryChest->SetNumberOfPlayers(m_SecondaryChest->GetNumberOfPlayers() + 1); + cChunkDef::BlockToChunk(m_SecondaryChest->GetPosX(), m_SecondaryChest->GetPosZ(), ChunkX, ChunkZ); + m_SecondaryChest->GetWorld()->MarkRedstoneDirty(ChunkX, ChunkZ); + } + + cWindow::OpenedByPlayer(a_Player); +} + + + + + +bool cChestWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) +{ + int ChunkX, ChunkZ; + + m_PrimaryChest->SetNumberOfPlayers(m_PrimaryChest->GetNumberOfPlayers() - 1); + cChunkDef::BlockToChunk(m_PrimaryChest->GetPosX(), m_PrimaryChest->GetPosZ(), ChunkX, ChunkZ); + m_PrimaryChest->GetWorld()->MarkRedstoneDirty(ChunkX, ChunkZ); + + if (m_SecondaryChest != NULL) + { + m_SecondaryChest->SetNumberOfPlayers(m_SecondaryChest->GetNumberOfPlayers() - 1); + cChunkDef::BlockToChunk(m_SecondaryChest->GetPosX(), m_SecondaryChest->GetPosZ(), ChunkX, ChunkZ); + m_SecondaryChest->GetWorld()->MarkRedstoneDirty(ChunkX, ChunkZ); + } + + cWindow::ClosedByPlayer(a_Player, a_CanRefuse); + return true; } @@ -916,20 +1002,20 @@ cChestWindow::cChestWindow(cChestEntity * a_PrimaryChest, cChestEntity * a_Secon cChestWindow::~cChestWindow() { // Send out the chest-close packet: - m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 0, E_BLOCK_CHEST); + m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 0, m_PrimaryChest->GetBlockType()); - m_World->BroadcastSoundEffect("random.chestclosed", m_BlockX * 8, m_BlockY * 8, m_BlockZ * 8, 1, 1); + m_World->BroadcastSoundEffect("random.chestclosed", (double)m_BlockX, (double)m_BlockY, (double)m_BlockZ, 1, 1); } -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// // cDropSpenserWindow: cDropSpenserWindow::cDropSpenserWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserEntity * a_DropSpenser) : - cWindow(wtDropSpenser, "Dropspenser") + cWindow(wtDropSpenser, (a_DropSpenser->GetBlockType() == E_BLOCK_DISPENSER) ? "Dispenser" : "Dropper") { m_ShouldDistributeToHotbarFirst = false; m_SlotAreas.push_back(new cSlotAreaItemGrid(a_DropSpenser->GetContents(), *this)); @@ -941,7 +1027,7 @@ cDropSpenserWindow::cDropSpenserWindow(int a_BlockX, int a_BlockY, int a_BlockZ, -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// // cEnderChestWindow: cEnderChestWindow::cEnderChestWindow(cEnderChestEntity * a_EnderChest) : @@ -956,7 +1042,7 @@ cEnderChestWindow::cEnderChestWindow(cEnderChestEntity * a_EnderChest) : 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); + m_World->BroadcastSoundEffect("random.chestopen", (double)m_BlockX, (double)m_BlockY, (double)m_BlockZ, 1, 1); // Send out the chest-open packet: m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 1, E_BLOCK_ENDER_CHEST); @@ -971,14 +1057,15 @@ cEnderChestWindow::~cEnderChestWindow() // Send out the chest-close packet: m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 0, E_BLOCK_ENDER_CHEST); - m_World->BroadcastSoundEffect("random.chestclosed", m_BlockX * 8, m_BlockY * 8, m_BlockZ * 8, 1, 1); + // Play the closing sound + m_World->BroadcastSoundEffect("random.chestclosed", (double)m_BlockX, (double)m_BlockY, (double)m_BlockZ, 1, 1); } -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// // cHopperWindow: cHopperWindow::cHopperWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cHopperEntity * a_Hopper) : @@ -994,7 +1081,7 @@ cHopperWindow::cHopperWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cHopperEn -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// // cFurnaceWindow: cFurnaceWindow::cFurnaceWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceEntity * a_Furnace) : diff --git a/src/UI/Window.h b/src/UI/Window.h index 1ca67bfd8..97db0ca88 100644 --- a/src/UI/Window.h +++ b/src/UI/Window.h @@ -24,6 +24,7 @@ class cEnderChestEntity; class cFurnaceEntity; class cHopperEntity; class cSlotArea; +class cSlotAreaAnvil; class cWorld; typedef std::list<cPlayer *> cPlayerList; @@ -35,12 +36,12 @@ typedef std::vector<cSlotArea *> cSlotAreas; // tolua_begin -/** +/** Represents a UI window. Each window has a list of players that are currently using it When there's no player using a window, it is destroyed. -A window consists of several areas of slots with similar functionality - for example the crafting grid area, or +A window consists of several areas of slots with similar functionality - for example the crafting grid area, or the inventory area. Each area knows what its slots are (GetSlot() function) and can handle mouse clicks. The window acts only as a top-level container for those areas, redirecting the click events to the correct areas. Inventory painting, introduced in 1.5, is handled by the window, too @@ -76,7 +77,7 @@ public: int GetWindowType(void) const { return m_WindowType; } // tolua_export cWindowOwner * GetOwner(void) { return m_Owner; } - void SetOwner( cWindowOwner * a_Owner ) { m_Owner = a_Owner; } + void SetOwner( cWindowOwner * a_Owner) { m_Owner = a_Owner; } /// Returns the total number of slots int GetNumSlots(void) const; @@ -108,12 +109,12 @@ public: /// Handles a click event from a player void Clicked( - cPlayer & a_Player, int a_WindowID, + cPlayer & a_Player, int a_WindowID, short a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem ); - void OpenedByPlayer(cPlayer & a_Player); + virtual void OpenedByPlayer(cPlayer & a_Player); /// Called when a player closes this window; notifies all slot areas. Returns true if close accepted virtual bool ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse); @@ -133,7 +134,7 @@ public: // tolua_begin const AString & GetWindowTitle() const { return m_WindowTitle; } - void SetWindowTitle(const AString & a_WindowTitle ) { m_WindowTitle = a_WindowTitle; } + void SetWindowTitle(const AString & a_WindowTitle) { m_WindowTitle = a_WindowTitle; } /// Sends the UpdateWindowProperty (0x69) packet to all clients of the window virtual void SetProperty(int a_Property, int a_Value); @@ -231,6 +232,32 @@ public: +class cAnvilWindow : + public cWindow +{ + typedef cWindow super; +public: + cAnvilWindow(int a_BlockX, int a_BlockY, int a_BlockZ); + + /** Gets the repaired item name. */ + AString GetRepairedItemName(void) const { return m_RepairedItemName; } + + /** Set the repaired item name. */ + void SetRepairedItemName(const AString & a_Name, cPlayer * a_Player); + + /** Gets the Position from the Anvil */ + void GetBlockPos(int & a_PosX, int & a_PosY, int & a_PosZ); + +protected: + cSlotAreaAnvil * m_AnvilSlotArea; + AString m_RepairedItemName; + int m_BlockX, m_BlockY, m_BlockZ; +} ; + + + + + class cEnchantingWindow : public cWindow { @@ -243,7 +270,7 @@ public: /** Return the Value of a Property */ int GetPropertyValue(int a_Property); - /** Set the Position Values to the Position of the Enchantment Table */ + /** Get the Position from the Enchantment Table */ void GetBlockPos(int & a_PosX, int & a_PosY, int & a_PosZ); cSlotArea * m_SlotArea; @@ -300,10 +327,15 @@ public: cChestWindow(cChestEntity * a_Chest); cChestWindow(cChestEntity * a_PrimaryChest, cChestEntity * a_SecondaryChest); ~cChestWindow(); + + virtual bool ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) override; + virtual void OpenedByPlayer(cPlayer & a_Player) override; protected: cWorld * m_World; int m_BlockX, m_BlockY, m_BlockZ; // Position of the chest, for the window-close packet + cChestEntity * m_PrimaryChest; + cChestEntity * m_SecondaryChest; } ; diff --git a/src/UI/WindowOwner.h b/src/UI/WindowOwner.h index e3c73edc4..7a7941e37 100644 --- a/src/UI/WindowOwner.h +++ b/src/UI/WindowOwner.h @@ -6,7 +6,7 @@ #include "Window.h" /* -Being a descendant of cWindowOwner means that the class can own one window. That window can be +Being a descendant of cWindowOwner means that the class can own one window. That window can be queried, opened by other players, closed by players and finally destroyed. Also, a cWindowOwner can be queried for the block coords where the window is displayed. That will be used for entities / players in motion to close their windows when they get too far away from the window "source". |