From cdc452916e3ec7e61f4a1ad822666192593b4b08 Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Thu, 2 Apr 2020 12:42:15 +0000 Subject: Replace buckets to the selected hotbar slot, rather than the first available. (#4580) * Replace buckets to the selected hotbar slot, rather than the first available. Replicates vanilla behaviour, as well as being more logical. * Refactor cInventory::AddItem. Behaviour is now documented * Add new cInventory::ReplaceOneEquippedItem and ::SetEquippedItem methods * Return empty potion to the same slot after drinking * Replace buckets correctly in other situations, not simply water and lava Uses the new ReplaceOneEquippedItem method * Correct collecting water from source block with bottle * Add cPlayer::ReplaceOneEquippedItemTossRest method * Handle stacked filled buckets (in theory) Use new cPlayer::ReplaceOneEquippedItemTossRest method --- src/Blocks/BlockCauldron.h | 40 +++++++++++++++++--- src/ClientHandle.cpp | 6 +-- src/Entities/Player.cpp | 19 ++++++++++ src/Entities/Player.h | 7 ++++ src/Inventory.cpp | 92 +++++++++++++++++++++++++++++++++++++--------- src/Inventory.h | 10 +++++ src/Items/ItemArmor.h | 2 +- src/Items/ItemBottle.h | 7 +++- src/Items/ItemBucket.h | 32 +++------------- src/Items/ItemPotion.h | 3 +- src/Mobs/Cow.cpp | 4 +- src/Mobs/Mooshroom.cpp | 8 ++-- 12 files changed, 167 insertions(+), 63 deletions(-) (limited to 'src') diff --git a/src/Blocks/BlockCauldron.h b/src/Blocks/BlockCauldron.h index 5b45b2828..d6ef721ad 100644 --- a/src/Blocks/BlockCauldron.h +++ b/src/Blocks/BlockCauldron.h @@ -27,17 +27,31 @@ public: virtual bool OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override { NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta({a_BlockX, a_BlockY, a_BlockZ}); - switch (a_Player.GetEquippedItem().m_ItemType) + auto EquippedItem = a_Player.GetEquippedItem(); + switch (EquippedItem.m_ItemType) { + case E_ITEM_BUCKET: + { + if (Meta == 3) + { + a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 0); + // Give new bucket, filled with fluid when the gamemode is not creative: + if (!a_Player.IsGameModeCreative()) + { + a_Player.ReplaceOneEquippedItemTossRest(cItem(E_ITEM_WATER_BUCKET)); + } + } + break; + } case E_ITEM_WATER_BUCKET: { if (Meta < 3) { a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 3); + // Give empty bucket back when the gamemode is not creative: if (!a_Player.IsGameModeCreative()) { - a_Player.GetInventory().RemoveOneEquippedItem(); - a_Player.GetInventory().AddItem(cItem(E_ITEM_BUCKET)); + a_Player.ReplaceOneEquippedItemTossRest(cItem(E_ITEM_BUCKET)); } } break; @@ -47,11 +61,27 @@ public: if (Meta > 0) { a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, --Meta); - a_Player.GetInventory().RemoveOneEquippedItem(); - a_Player.GetInventory().AddItem(cItem(E_ITEM_POTION)); + // Give new potion when the gamemode is not creative: + if (!a_Player.IsGameModeCreative()) + { + a_Player.ReplaceOneEquippedItemTossRest(cItem(E_ITEM_POTION)); + } } break; } + case E_ITEM_POTION: + { + // Refill cauldron with water bottles. + if ((Meta < 3) && (EquippedItem.m_ItemDamage == 0)) + { + a_ChunkInterface.SetBlockMeta(Vector3i(a_BlockX, a_BlockY, a_BlockZ), ++Meta); + // Give empty bottle when the gamemode is not creative: + if (!a_Player.IsGameModeCreative()) + { + a_Player.ReplaceOneEquippedItemTossRest(cItem(E_ITEM_GLASS_BOTTLE)); + } + } + } } return true; } diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index ef121cf12..9a92a3005 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -1211,9 +1211,9 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eB cItem EquippedItem = m_Player->GetEquippedItem(); cItem OffhandItem = m_Player->GetOffHandEquipedItem(); - cInventory & Intentory = m_Player->GetInventory(); - Intentory.SetShieldSlot(EquippedItem); - Intentory.SetHotbarSlot(Intentory.GetEquippedSlotNum(), OffhandItem); + cInventory & Inventory = m_Player->GetInventory(); + Inventory.SetShieldSlot(EquippedItem); + Inventory.SetEquippedItem(OffhandItem); return; } diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index b5d7f7c83..02cb7378d 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -1959,6 +1959,25 @@ void cPlayer::TossEquippedItem(char a_Amount) +void cPlayer::ReplaceOneEquippedItemTossRest(const cItem & a_Item) +{ + auto PlacedCount = GetInventory().ReplaceOneEquippedItem(a_Item); + char ItemCountToToss = a_Item.m_ItemCount - static_cast(PlacedCount); + + if (ItemCountToToss == 0) + { + return; + } + + cItem Pickup = a_Item; + Pickup.m_ItemCount = ItemCountToToss; + TossPickup(Pickup); +} + + + + + void cPlayer::TossHeldItem(char a_Amount) { cItems Drops; diff --git a/src/Entities/Player.h b/src/Entities/Player.h index fafdd04eb..3ba87f748 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -313,6 +313,13 @@ public: /** tosses the item in the selected hotbar slot */ void TossEquippedItem(char a_Amount = 1); + /** Removes one item from the the current equipped item stack, and attempts to add the specified item stack + back to the same slot. If it is not possible to place the item in the same slot, tries to place the specified + item elsewhere in the inventory. If this is not possible, then any remaining items are tossed. If the currently + equipped slot is empty, its contents are simply set to the given Item. + */ + void ReplaceOneEquippedItemTossRest(const cItem &); + /** tosses the item held in hand (when in UI windows) */ void TossHeldItem(char a_Amount = 1); diff --git a/src/Inventory.cpp b/src/Inventory.cpp index d8b67835e..42c243f17 100644 --- a/src/Inventory.cpp +++ b/src/Inventory.cpp @@ -121,35 +121,38 @@ int cInventory::AddItem(const cItem & a_Item, bool a_AllowNewStacks) } } - for (int SlotIdx = 0; SlotIdx < m_InventorySlots.GetNumSlots(); ++SlotIdx) + // Add to existing stacks in the hotbar. + res += m_HotbarSlots.AddItem(ToAdd, false); + ToAdd.m_ItemCount = static_cast(a_Item.m_ItemCount - res); + if (ToAdd.m_ItemCount == 0) { - auto & Slot = m_InventorySlots.GetSlot(SlotIdx); - if (Slot.IsEqual(a_Item)) - { - cItemHandler Handler(Slot.m_ItemType); - int AmountToAdd = std::min(static_cast(Handler.GetMaxStackSize() - Slot.m_ItemCount), ToAdd.m_ItemCount); - res += AmountToAdd; + return res; + } - cItem SlotAdjusted(Slot); - SlotAdjusted.m_ItemCount += AmountToAdd; - m_InventorySlots.SetSlot(SlotIdx, SlotAdjusted); + // Add to existing stacks in main inventory. + res += m_InventorySlots.AddItem(ToAdd, false); + ToAdd.m_ItemCount = static_cast(a_Item.m_ItemCount - res); + if (ToAdd.m_ItemCount == 0) + { + return res; + } - ToAdd.m_ItemCount -= AmountToAdd; - if (ToAdd.m_ItemCount == 0) - { - return res; - } - } + // All existing stacks are now filled. + if (!a_AllowNewStacks) + { + return res; } - res += m_HotbarSlots.AddItem(ToAdd, a_AllowNewStacks); + // Try adding new stacks to the hotbar. + res += m_HotbarSlots.AddItem(ToAdd, true); ToAdd.m_ItemCount = static_cast(a_Item.m_ItemCount - res); if (ToAdd.m_ItemCount == 0) { return res; } - res += m_InventorySlots.AddItem(ToAdd, a_AllowNewStacks); + // Try adding new stacks to the main inventory. + res += m_InventorySlots.AddItem(ToAdd, true); return res; } @@ -219,6 +222,50 @@ bool cInventory::RemoveOneEquippedItem(void) +int cInventory::ReplaceOneEquippedItem(const cItem & a_Item, bool a_TryOtherSlots) +{ + // Ignore whether there was an item in the slot to remove. + RemoveOneEquippedItem(); + + auto EquippedItem = GetEquippedItem(); + if (EquippedItem.IsEmpty()) + { + SetEquippedItem(a_Item); + return a_Item.m_ItemCount; + } + + // Handle case when equipped item is the same as the replacement item. + cItem ItemsToAdd = a_Item; + if (EquippedItem.IsEqual(ItemsToAdd)) + { + cItemHandler Handler(ItemsToAdd.m_ItemType); + auto AmountToAdd = std::min(static_cast(Handler.GetMaxStackSize() - EquippedItem.m_ItemCount), ItemsToAdd.m_ItemCount); + + EquippedItem.m_ItemCount += AmountToAdd; + SetEquippedItem(EquippedItem); + ItemsToAdd.m_ItemCount -= AmountToAdd; + } + + auto ItemsAdded = a_Item.m_ItemCount - ItemsToAdd.m_ItemCount; + + if (ItemsToAdd.m_ItemCount == 0) + { + return ItemsAdded; + } + + if (!a_TryOtherSlots) + { + return ItemsAdded; + } + + // Try the rest of the inventory. + return AddItem(ItemsToAdd) + ItemsAdded; +} + + + + + int cInventory::HowManyItems(const cItem & a_Item) { return @@ -300,6 +347,15 @@ void cInventory::SetShieldSlot(const cItem & a_Item) +void cInventory::SetEquippedItem(const cItem & a_Item) +{ + SetHotbarSlot(GetEquippedSlotNum(), a_Item); +} + + + + + void cInventory::SendEquippedSlot() { int EquippedSlotNum = cInventory::invArmorCount + cInventory::invInventoryCount + GetEquippedSlotNum(); diff --git a/src/Inventory.h b/src/Inventory.h index b91b16c8b..7436d7528 100644 --- a/src/Inventory.h +++ b/src/Inventory.h @@ -71,6 +71,7 @@ public: /** Adds as many items out of a_ItemStack as can fit. If a_AllowNewStacks is set to false, only existing stacks can be topped up; if a_AllowNewStacks is set to true, empty slots can be used for the rest. + Fills existing stacks first and fills the hotbar before the main inventory. Returns the number of items that fit. */ int AddItem(const cItem & a_ItemStack, bool a_AllowNewStacks = true); @@ -90,6 +91,13 @@ public: /** Removes one item out of the currently equipped item stack, returns true if successful, false if empty-handed */ bool RemoveOneEquippedItem(void); + /** Removes one item from the the current equipped item stack, and attempts to add the specified item stack + back to the same slot. If it is not possible to place the item in the same slot, optionally (default true) tries to + place the specified item elsewhere in the inventory. Returns the number of items successfully added. If the + currently equipped slot is empty, its contents are simply set to the given Item. + */ + int ReplaceOneEquippedItem(const cItem & a_Item, bool a_TryOtherSlots = true); + /** Returns the number of items of type a_Item that are stored */ int HowManyItems(const cItem & a_Item); @@ -143,6 +151,8 @@ public: void SetHotbarSlot(int a_HotBarSlotNum, const cItem & a_Item); /** Sets current item in shield slot */ void SetShieldSlot(const cItem & a_Item); + /** Sets current item in the equipped hotbar slot */ + void SetEquippedItem(const cItem & a_Item); /** Sets equiped item to the a_SlotNum slot number */ void SetEquippedSlotNum(int a_SlotNum); /** Returns slot number of equiped item */ diff --git a/src/Items/ItemArmor.h b/src/Items/ItemArmor.h index 906993a5b..d218c22b3 100644 --- a/src/Items/ItemArmor.h +++ b/src/Items/ItemArmor.h @@ -61,7 +61,7 @@ public: { Item.Empty(); } - a_Player->GetInventory().SetHotbarSlot(a_Player->GetInventory().GetEquippedSlotNum(), Item); + a_Player->GetInventory().SetEquippedItem(Item); return true; } diff --git a/src/Items/ItemBottle.h b/src/Items/ItemBottle.h index b261937e5..18767dcde 100644 --- a/src/Items/ItemBottle.h +++ b/src/Items/ItemBottle.h @@ -83,8 +83,11 @@ public: return false; // Nothing in range. } - a_Player->GetInventory().RemoveOneEquippedItem(); - a_Player->GetInventory().AddItem(cItem(E_ITEM_POTION)); + // Give back a filled water bottle if gamemode is not creative: + if (!a_Player->IsGameModeCreative()) + { + a_Player->ReplaceOneEquippedItemTossRest(cItem(E_ITEM_POTION)); + } return true; } } ; diff --git a/src/Items/ItemBucket.h b/src/Items/ItemBucket.h index 7a91149d1..8affff6ca 100644 --- a/src/Items/ItemBucket.h +++ b/src/Items/ItemBucket.h @@ -70,15 +70,15 @@ public: } BLOCKTYPE Block = a_World->GetBlock(BlockPos.x, BlockPos.y, BlockPos.z); - ENUM_ITEM_ID NewItem; + ENUM_ITEM_ID NewItemType; if (IsBlockWater(Block)) { - NewItem = E_ITEM_WATER_BUCKET; + NewItemType = E_ITEM_WATER_BUCKET; } else if (IsBlockLava(Block)) { - NewItem = E_ITEM_LAVA_BUCKET; + NewItemType = E_ITEM_LAVA_BUCKET; } else { @@ -101,18 +101,7 @@ public: // Give new bucket, filled with fluid when the gamemode is not creative: if (!a_Player->IsGameModeCreative()) { - // Remove the bucket from the inventory - if (!a_Player->GetInventory().RemoveOneEquippedItem()) - { - LOG("Clicked with an empty bucket, but cannot remove one from the inventory? WTF?"); - ASSERT(!"Inventory bucket mismatch"); - return true; - } - if (a_Player->GetInventory().AddItem(cItem(NewItem)) != 1) - { - // The bucket didn't fit, toss it as a pickup: - a_Player->TossPickup(cItem(NewItem)); - } + a_Player->ReplaceOneEquippedItemTossRest(cItem(NewItemType)); } return true; @@ -154,19 +143,10 @@ public: return false; } + // Give back an empty bucket if the gamemode is not creative: if (!a_Player->IsGameModeCreative()) { - // Remove fluid bucket, add empty bucket: - if (!a_Player->GetInventory().RemoveOneEquippedItem()) - { - LOG("Clicked with a full bucket, but cannot remove one from the inventory? WTF?"); - ASSERT(!"Inventory bucket mismatch"); - return false; - } - if (!a_Player->GetInventory().AddItem(cItem(E_ITEM_BUCKET))) - { - return false; - } + a_Player->ReplaceOneEquippedItemTossRest(cItem(E_ITEM_BUCKET)); } // Wash away anything that was there prior to placing: diff --git a/src/Items/ItemPotion.h b/src/Items/ItemPotion.h index 470a7c67f..86330d1c0 100644 --- a/src/Items/ItemPotion.h +++ b/src/Items/ItemPotion.h @@ -77,8 +77,7 @@ public: if (!a_Player->IsGameModeCreative()) { - a_Player->GetInventory().RemoveOneEquippedItem(); - a_Player->GetInventory().AddItem(cItem(E_ITEM_GLASS_BOTTLE)); + a_Player->ReplaceOneEquippedItemTossRest(cItem(E_ITEM_GLASS_BOTTLE)); } return true; } diff --git a/src/Mobs/Cow.cpp b/src/Mobs/Cow.cpp index a7fb0d476..559b6caea 100644 --- a/src/Mobs/Cow.cpp +++ b/src/Mobs/Cow.cpp @@ -44,10 +44,10 @@ void cCow::OnRightClicked(cPlayer & a_Player) short HeldItem = a_Player.GetEquippedItem().m_ItemType; if (HeldItem == E_ITEM_BUCKET) { + // Milk the cow. if (!a_Player.IsGameModeCreative()) { - a_Player.GetInventory().RemoveOneEquippedItem(); - a_Player.GetInventory().AddItem(cItem(E_ITEM_MILK)); + a_Player.ReplaceOneEquippedItemTossRest(cItem(E_ITEM_MILK)); } } } diff --git a/src/Mobs/Mooshroom.cpp b/src/Mobs/Mooshroom.cpp index 31a1b8978..135a8ba90 100644 --- a/src/Mobs/Mooshroom.cpp +++ b/src/Mobs/Mooshroom.cpp @@ -43,18 +43,18 @@ void cMooshroom::OnRightClicked(cPlayer & a_Player) { case E_ITEM_BUCKET: { + // Milk the cow. if (!a_Player.IsGameModeCreative()) { - a_Player.GetInventory().RemoveOneEquippedItem(); - a_Player.GetInventory().AddItem(cItem(E_ITEM_MILK)); + a_Player.ReplaceOneEquippedItemTossRest(cItem(E_ITEM_MILK)); } } break; case E_ITEM_BOWL: { + // Soup the cow. if (!a_Player.IsGameModeCreative()) { - a_Player.GetInventory().RemoveOneEquippedItem(); - a_Player.GetInventory().AddItem(cItem(E_ITEM_MUSHROOM_SOUP)); + a_Player.ReplaceOneEquippedItemTossRest(cItem(E_ITEM_MUSHROOM_SOUP)); } } break; case E_ITEM_SHEARS: -- cgit v1.2.3