1
0

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
This commit is contained in:
Alexander Harkness 2020-04-02 12:42:15 +00:00 committed by GitHub
parent d5c58c6b17
commit cdc452916e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 228 additions and 80 deletions

View File

@ -6223,7 +6223,7 @@ These ItemGrids are available in the API and can be manipulated by the plugins,
Type = "number",
},
},
Notes = "Adds an item to the storage; if AllowNewStacks is true (default), will also create new stacks in empty slots. Returns the number of items added",
Notes = "Adds an item to the storage; if AllowNewStacks is true (default), will also create new stacks in empty slots. Fills existing stacks first and fills the hotbar before the main inventory. Returns the number of items added",
},
AddItems =
{
@ -6617,6 +6617,28 @@ These ItemGrids are available in the API and can be manipulated by the plugins,
},
Notes = "Removes one item from the hotbar's currently selected slot. Returns true on success.",
},
ReplaceOneEquippedItem =
{
Params =
{
{
Name = "Item",
Type = "cItem",
},
{
Name = "TryOtherSlots",
Type = "boolean",
IsOptional = true,
},
},
Returns =
{
{
Type = "number",
},
},
Notes = "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.",
},
SendEquippedSlot =
{
Notes = "Sends the equipped item slot to the client",
@ -6662,17 +6684,6 @@ These ItemGrids are available in the API and can be manipulated by the plugins,
},
Notes = "Sets the specified hotbar slot contents",
},
SetShieldSlot =
{
Params =
{
{
Name = "Item",
Type = "cItem",
},
},
Notes = "Sets the shield slot content",
},
SetInventorySlot =
{
Params =
@ -6688,6 +6699,17 @@ These ItemGrids are available in the API and can be manipulated by the plugins,
},
Notes = "Sets the specified main inventory slot contents",
},
SetShieldSlot =
{
Params =
{
{
Name = "Item",
Type = "cItem",
},
},
Notes = "Sets the shield slot content",
},
SetSlot =
{
Params =
@ -6703,6 +6725,17 @@ These ItemGrids are available in the API and can be manipulated by the plugins,
},
Notes = "Sets the specified slot contents",
},
SetEquippedItem =
{
Params =
{
{
Name = "Item",
Type = "cItem",
},
},
Notes = "Sets current item in the equipped hotbar slot",
},
},
Constants =
{
@ -10712,6 +10745,17 @@ a_Player:OpenWindow(Window);
},
Notes = "Places a block while impersonating the player. The {{OnPlayerPlacingBlock|HOOK_PLAYER_PLACING_BLOCK}} hook is called before the placement, and if it succeeds, the block is placed and the {{OnPlayerPlacedBlock|HOOK_PLAYER_PLACED_BLOCK}} hook is called. Returns true iff the block is successfully placed. Assumes that the block is in a currently loaded chunk.",
},
ReplaceOneEquippedItemTossRest =
{
Params =
{
{
Name = "Item",
Type = "cItem",
},
},
Notes = "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.",
},
Respawn =
{
Notes = "Restores the health, extinguishes fire, makes visible and sends the Respawn packet.",

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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<char>(PlacedCount);
if (ItemCountToToss == 0)
{
return;
}
cItem Pickup = a_Item;
Pickup.m_ItemCount = ItemCountToToss;
TossPickup(Pickup);
}
void cPlayer::TossHeldItem(char a_Amount)
{
cItems Drops;

View File

@ -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);

View File

@ -121,35 +121,38 @@ int cInventory::AddItem(const cItem & a_Item, bool a_AllowNewStacks)
}
}
for (int SlotIdx = 0; SlotIdx < m_InventorySlots.GetNumSlots(); ++SlotIdx)
{
auto & Slot = m_InventorySlots.GetSlot(SlotIdx);
if (Slot.IsEqual(a_Item))
{
cItemHandler Handler(Slot.m_ItemType);
int AmountToAdd = std::min(static_cast<char>(Handler.GetMaxStackSize() - Slot.m_ItemCount), ToAdd.m_ItemCount);
res += AmountToAdd;
cItem SlotAdjusted(Slot);
SlotAdjusted.m_ItemCount += AmountToAdd;
m_InventorySlots.SetSlot(SlotIdx, SlotAdjusted);
ToAdd.m_ItemCount -= AmountToAdd;
if (ToAdd.m_ItemCount == 0)
{
return res;
}
}
}
res += m_HotbarSlots.AddItem(ToAdd, a_AllowNewStacks);
// Add to existing stacks in the hotbar.
res += m_HotbarSlots.AddItem(ToAdd, false);
ToAdd.m_ItemCount = static_cast<char>(a_Item.m_ItemCount - res);
if (ToAdd.m_ItemCount == 0)
{
return res;
}
res += m_InventorySlots.AddItem(ToAdd, a_AllowNewStacks);
// Add to existing stacks in main inventory.
res += m_InventorySlots.AddItem(ToAdd, false);
ToAdd.m_ItemCount = static_cast<char>(a_Item.m_ItemCount - res);
if (ToAdd.m_ItemCount == 0)
{
return res;
}
// All existing stacks are now filled.
if (!a_AllowNewStacks)
{
return res;
}
// Try adding new stacks to the hotbar.
res += m_HotbarSlots.AddItem(ToAdd, true);
ToAdd.m_ItemCount = static_cast<char>(a_Item.m_ItemCount - res);
if (ToAdd.m_ItemCount == 0)
{
return res;
}
// 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<char>(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();

View File

@ -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 */

View File

@ -61,7 +61,7 @@ public:
{
Item.Empty();
}
a_Player->GetInventory().SetHotbarSlot(a_Player->GetInventory().GetEquippedSlotNum(), Item);
a_Player->GetInventory().SetEquippedItem(Item);
return true;
}

View File

@ -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;
}
} ;

View File

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

View File

@ -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;
}

View File

@ -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));
}
}
}

View File

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