From 0bacda32692729e4b9743f91d92cd329e198d73a Mon Sep 17 00:00:00 2001 From: peterbell10 Date: Sat, 21 Oct 2017 17:56:09 +0100 Subject: Implement horse inventory (#4053) * Implement horse inventory * Fix sign conversions * Add API doc for ItemCategory::IsHorseArmor * Improve HandleOpenHorseInventory comment and style fixes. --- Server/Plugins/APIDump/APIDesc.lua | 18 +++++ src/ClientHandle.cpp | 12 ++++ src/ClientHandle.h | 4 ++ src/Defines.h | 19 ++++++ src/Entities/Player.cpp | 30 +++++++++ src/Entities/Player.h | 4 ++ src/Mobs/Horse.cpp | 134 +++++++++++++++++++++++++++++++------ src/Mobs/Horse.h | 28 ++++++-- src/Protocol/Protocol_1_8.cpp | 5 +- src/Protocol/Protocol_1_9.cpp | 5 +- src/UI/CMakeLists.txt | 2 + src/UI/HorseWindow.cpp | 61 +++++++++++++++++ src/UI/HorseWindow.h | 34 ++++++++++ src/UI/SlotArea.cpp | 119 ++++++++++++++++++++++++++++++++ src/UI/SlotArea.h | 25 +++++++ 15 files changed, 473 insertions(+), 27 deletions(-) create mode 100644 src/UI/HorseWindow.cpp create mode 100644 src/UI/HorseWindow.h diff --git a/Server/Plugins/APIDump/APIDesc.lua b/Server/Plugins/APIDump/APIDesc.lua index 1548cf97d..3b37f2fdf 100644 --- a/Server/Plugins/APIDump/APIDesc.lua +++ b/Server/Plugins/APIDump/APIDesc.lua @@ -16977,6 +16977,24 @@ end }, Notes = "Returns true if the specified item type is any kind of a hoe.", }, + IsHorseArmor= + { + IsStatic = true, + Params = + { + { + Name = "ItemType", + Type = "number", + }, + }, + Returns = + { + { + Type = "boolean", + }, + }, + Notes = "Returns true if the specified item type is any kind of a horse armor.", + }, IsLeggings = { IsStatic = true, diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 020707238..8ed3a6056 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -678,6 +678,18 @@ void cClientHandle::HandleNPCTrade(int a_SlotNum) +void cClientHandle::HandleOpenHorseInventory(UInt32 a_EntityID) +{ + if (m_Player->GetUniqueID() == a_EntityID) + { + m_Player->OpenHorseInventory(); + } +} + + + + + void cClientHandle::HandlePing(void) { /* TODO: unused function, handles Legacy Server List Ping diff --git a/src/ClientHandle.h b/src/ClientHandle.h index 8ded5a5ad..42e2ee10a 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -338,6 +338,10 @@ public: // tolua_export the NPC UI. */ void HandleNPCTrade(int a_SlotNum); + /** Handles a player opening their inventory while riding a horse. + @param a_EntityID ID of the player that is to open the inventory. Should be the same as GetPlayer()->GetUniqueID(). */ + void HandleOpenHorseInventory(UInt32 a_EntityID); + void HandlePing (void); void HandlePlayerAbilities (bool a_CanFly, bool a_IsFlying, float FlyingSpeed, float WalkingSpeed); void HandlePlayerLook (float a_Rotation, float a_Pitch, bool a_IsOnGround); diff --git a/src/Defines.h b/src/Defines.h index a3222f73a..7a7f4c598 100644 --- a/src/Defines.h +++ b/src/Defines.h @@ -1175,6 +1175,25 @@ namespace ItemCategory IsBoots(a_ItemType) ); } + + + + inline bool IsHorseArmor(short a_ItemType) + { + switch (a_ItemType) + { + case E_ITEM_IRON_HORSE_ARMOR: + case E_ITEM_GOLD_HORSE_ARMOR: + case E_ITEM_DIAMOND_HORSE_ARMOR: + { + return true; + } + default: + { + return false; + } + } + } } // tolua_end diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 0769499b1..22f0655f2 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -5,6 +5,7 @@ #include "Player.h" #include "Mobs/Wolf.h" +#include "Mobs/Horse.h" #include "../BoundingBox.h" #include "../ChatColor.h" #include "../Server.h" @@ -2202,6 +2203,35 @@ bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World) +void cPlayer::OpenHorseInventory() +{ + if ( + (m_AttachedTo == nullptr) || + !m_AttachedTo->IsMob() + ) + { + return; + } + + auto & Mob = static_cast(*m_AttachedTo); + + if (Mob.GetMobType() != mtHorse) + { + return; + } + + auto & Horse = static_cast(Mob); + // The client sends requests for untame horses as well but shouldn't actually open + if (Horse.IsTame()) + { + Horse.PlayerOpenWindow(*this); + } +} + + + + + bool cPlayer::SaveToDisk() { cFile::CreateFolder(FILE_IO_PREFIX + AString("players/")); // Create the "players" folder, if it doesn't exist yet (#1268) diff --git a/src/Entities/Player.h b/src/Entities/Player.h index 2685622ad..f56841613 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -409,6 +409,10 @@ public: const AString & GetLoadedWorldName() { return m_LoadedWorldName; } + /** Opens the inventory of any tame horse the player is riding. + If the player is not riding a horse or if the horse is untamed, does nothing. */ + void OpenHorseInventory(); + void UseEquippedItem(int a_Amount = 1); void SendHealth(void); diff --git a/src/Mobs/Horse.cpp b/src/Mobs/Horse.cpp index 07d3f7481..77edc0d27 100644 --- a/src/Mobs/Horse.cpp +++ b/src/Mobs/Horse.cpp @@ -5,6 +5,7 @@ #include "../EffectID.h" #include "../Entities/Player.h" #include "Broadcaster.h" +#include "UI/HorseWindow.h" @@ -12,16 +13,15 @@ cHorse::cHorse(int Type, int Color, int Style, int TameTimes) : super("Horse", mtHorse, "entity.horse.hurt", "entity.horse.death", 1.4, 1.6), + cEntityWindowOwner(this), m_bHasChest(false), m_bIsEating(false), m_bIsRearing(false), m_bIsMouthOpen(false), m_bIsTame(false), - m_bIsSaddled(false), m_Type(Type), m_Color(Color), m_Style(Style), - m_Armour(0), m_TimesToTame(TameTimes), m_TameAttemptTimes(0), m_RearTickCount(0), @@ -33,6 +33,19 @@ cHorse::cHorse(int Type, int Color, int Style, int TameTimes) : +cHorse::~cHorse() +{ + auto Window = GetWindow(); + if (Window != nullptr) + { + Window->OwnerDestroyed(); + } +} + + + + + void cHorse::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); @@ -108,22 +121,24 @@ void cHorse::OnRightClicked(cPlayer & a_Player) if (m_bIsTame) { - if (!m_bIsSaddled) + if (a_Player.IsCrouched()) { - if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_SADDLE) - { - // Saddle the horse: - if (!a_Player.IsGameModeCreative()) - { - a_Player.GetInventory().RemoveOneEquippedItem(); - } - m_bIsSaddled = true; - m_World->BroadcastEntityMetadata(*this); - } - else - { - a_Player.AttachTo(this); - } + PlayerOpenWindow(a_Player); + return; + } + + auto EquipedItemType = a_Player.GetEquippedItem().m_ItemType; + + if ( + !IsSaddled() && + ( + (EquipedItemType == E_ITEM_SADDLE) || + ItemCategory::IsHorseArmor(EquipedItemType) + ) + ) + { + // Player is holding a horse inventory item, open the window: + PlayerOpenWindow(a_Player); } else { @@ -167,6 +182,65 @@ void cHorse::OnRightClicked(cPlayer & a_Player) +void cHorse::SetHorseSaddle(cItem a_Saddle) +{ + if (a_Saddle.m_ItemType == E_ITEM_SADDLE) + { + m_World->BroadcastSoundEffect("entity.horse.saddle", GetPosition(), 1.0f, 0.8f); + } + else if (!a_Saddle.IsEmpty()) + { + return; // Invalid item + } + + m_Saddle = std::move(a_Saddle); + m_World->BroadcastEntityMetadata(*this); +} + + + + + +void cHorse::SetHorseArmor(cItem a_Armor) +{ + if (ItemCategory::IsHorseArmor(a_Armor.m_ItemType)) + { + m_World->BroadcastSoundEffect("entity.horse.armor", GetPosition(), 1.0f, 0.8f); + } + else if (!a_Armor.IsEmpty()) + { + return; // Invalid item + } + + m_Armor = std::move(a_Armor); + m_World->BroadcastEntityMetadata(*this); +} + + + + + +int cHorse::GetHorseArmour(void) const +{ + switch (m_Armor.m_ItemType) + { + case E_ITEM_EMPTY: return 0; + case E_ITEM_IRON_HORSE_ARMOR: return 1; + case E_ITEM_GOLD_HORSE_ARMOR: return 2; + case E_ITEM_DIAMOND_HORSE_ARMOR: return 3; + + default: + { + LOGWARN("cHorse::GetHorseArmour: Invalid armour item (%d)", m_Armor.m_ItemType); + return 0; + } + } +} + + + + + void cHorse::GetDrops(cItems & a_Drops, cEntity * a_Killer) { if (IsBaby()) @@ -180,9 +254,13 @@ void cHorse::GetDrops(cItems & a_Drops, cEntity * a_Killer) LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); } AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_LEATHER); - if (m_bIsSaddled) + if (IsSaddled()) { - a_Drops.push_back(cItem(E_ITEM_SADDLE, 1)); + a_Drops.push_back(m_Saddle); + } + if (!m_Armor.IsEmpty()) + { + a_Drops.push_back(m_Armor); } } @@ -205,8 +283,24 @@ void cHorse::InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) void cHorse::HandleSpeedFromAttachee(float a_Forward, float a_Sideways) { - if ((m_bIsTame) && (m_bIsSaddled)) + if ((m_bIsTame) && IsSaddled()) { super::HandleSpeedFromAttachee(a_Forward * m_MaxSpeed, a_Sideways * m_MaxSpeed); } } + + + + + +void cHorse::PlayerOpenWindow(cPlayer & a_Player) +{ + auto Window = GetWindow(); + if (Window == nullptr) + { + Window = new cHorseWindow(*this); + OpenWindow(Window); + } + + a_Player.OpenWindow(*Window); +} diff --git a/src/Mobs/Horse.h b/src/Mobs/Horse.h index 82026a0ee..38625001e 100644 --- a/src/Mobs/Horse.h +++ b/src/Mobs/Horse.h @@ -2,18 +2,21 @@ #pragma once #include "PassiveMonster.h" +#include "UI/WindowOwner.h" class cHorse : - public cPassiveMonster + public cPassiveMonster, + public cEntityWindowOwner { typedef cPassiveMonster super; public: cHorse(int Type, int Color, int Style, int TameTimes); + virtual ~cHorse() override; CLASS_PROTODEF(cHorse) @@ -23,7 +26,7 @@ public: virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void OnRightClicked(cPlayer & a_Player) override; - bool IsSaddled (void) const {return m_bIsSaddled; } + bool IsSaddled (void) const {return !m_Saddle.IsEmpty(); } bool IsChested (void) const {return m_bHasChest; } bool IsEating (void) const {return m_bIsEating; } bool IsRearing (void) const {return m_bIsRearing; } @@ -32,7 +35,18 @@ public: int GetHorseType (void) const {return m_Type; } int GetHorseColor (void) const {return m_Color; } int GetHorseStyle (void) const {return m_Style; } - int GetHorseArmour (void) const {return m_Armour;} + int GetHorseArmour (void) const; + + /** Set the horse's saddle to the given item. + @param a_SaddleItem should be either a saddle or empty. */ + void SetHorseSaddle(cItem a_SaddleItem); + + /** Set the horse's armor slot to the given item. + @param a_SaddleItem should be either a type of horse armor or empty. */ + void SetHorseArmor(cItem a_ArmorItem); + + const cItem & GetHorseSaddle() const { return m_Saddle; } + const cItem & GetHorseArmorItem() const { return m_Armor; } virtual void GetBreedingItems(cItems & a_Items) override { @@ -40,11 +54,15 @@ public: a_Items.Add(E_ITEM_GOLDEN_APPLE); } + void PlayerOpenWindow(cPlayer & a_Player); + private: - bool m_bHasChest, m_bIsEating, m_bIsRearing, m_bIsMouthOpen, m_bIsTame, m_bIsSaddled; - int m_Type, m_Color, m_Style, m_Armour, m_TimesToTame, m_TameAttemptTimes, m_RearTickCount; + bool m_bHasChest, m_bIsEating, m_bIsRearing, m_bIsMouthOpen, m_bIsTame; + int m_Type, m_Color, m_Style, m_TimesToTame, m_TameAttemptTimes, m_RearTickCount; float m_MaxSpeed; + cItem m_Saddle; + cItem m_Armor; } ; diff --git a/src/Protocol/Protocol_1_8.cpp b/src/Protocol/Protocol_1_8.cpp index c384f85b9..e7f197d3d 100644 --- a/src/Protocol/Protocol_1_8.cpp +++ b/src/Protocol/Protocol_1_8.cpp @@ -39,6 +39,7 @@ Implements the 1.8 protocol classes: #include "../Mobs/IncludeAllMonsters.h" #include "../UI/Window.h" +#include "../UI/HorseWindow.h" #include "../BlockEntities/BeaconEntity.h" #include "../BlockEntities/CommandBlockEntity.h" @@ -1678,7 +1679,8 @@ void cProtocol_1_8_0::SendWindowOpen(const cWindow & a_Window) if (a_Window.GetWindowType() == cWindow::wtAnimalChest) { - Pkt.WriteBEInt32(0); // TODO: The animal's EntityID + UInt32 HorseID = static_cast(a_Window).GetHorseID(); + Pkt.WriteBEInt32(static_cast(HorseID)); } } @@ -2428,6 +2430,7 @@ void cProtocol_1_8_0::HandlePacketEntityAction(cByteBuffer & a_ByteBuffer) case 2: m_Client->HandleEntityLeaveBed(PlayerID); break; // Leave Bed case 3: m_Client->HandleEntitySprinting(PlayerID, true); break; // Start sprinting case 4: m_Client->HandleEntitySprinting(PlayerID, false); break; // Stop sprinting + case 6: m_Client->HandleOpenHorseInventory(PlayerID); break; // Open horse inventory } } diff --git a/src/Protocol/Protocol_1_9.cpp b/src/Protocol/Protocol_1_9.cpp index fcfc6674a..9dcf8e3aa 100644 --- a/src/Protocol/Protocol_1_9.cpp +++ b/src/Protocol/Protocol_1_9.cpp @@ -48,6 +48,7 @@ Implements the 1.9 protocol classes: #include "../Mobs/IncludeAllMonsters.h" #include "../UI/Window.h" +#include "../UI/HorseWindow.h" #include "../BlockEntities/BeaconEntity.h" #include "../BlockEntities/CommandBlockEntity.h" @@ -1723,7 +1724,8 @@ void cProtocol_1_9_0::SendWindowOpen(const cWindow & a_Window) if (a_Window.GetWindowType() == cWindow::wtAnimalChest) { - Pkt.WriteBEInt32(0); // TODO: The animal's EntityID + UInt32 HorseID = static_cast(a_Window).GetHorseID(); + Pkt.WriteBEInt32(static_cast(HorseID)); } } @@ -2517,6 +2519,7 @@ void cProtocol_1_9_0::HandlePacketEntityAction(cByteBuffer & a_ByteBuffer) case 2: m_Client->HandleEntityLeaveBed(PlayerID); break; // Leave Bed case 3: m_Client->HandleEntitySprinting(PlayerID, true); break; // Start sprinting case 4: m_Client->HandleEntitySprinting(PlayerID, false); break; // Stop sprinting + case 7: m_Client->HandleOpenHorseInventory(PlayerID); break; // Open horse inventory } } diff --git a/src/UI/CMakeLists.txt b/src/UI/CMakeLists.txt index 54236a929..6477882b3 100644 --- a/src/UI/CMakeLists.txt +++ b/src/UI/CMakeLists.txt @@ -15,6 +15,7 @@ SET (SRCS EnderChestWindow.cpp FurnaceWindow.cpp HopperWindow.cpp + HorseWindow.cpp InventoryWindow.cpp) SET (HDRS @@ -30,6 +31,7 @@ SET (HDRS EnderChestWindow.h FurnaceWindow.h HopperWindow.h + HorseWindow.h InventoryWindow.h MinecartWithChestWindow.h WindowOwner.h) diff --git a/src/UI/HorseWindow.cpp b/src/UI/HorseWindow.cpp new file mode 100644 index 000000000..ab060609d --- /dev/null +++ b/src/UI/HorseWindow.cpp @@ -0,0 +1,61 @@ + +// HorseWindow.cpp + +// Representing the UI window for a horse entity + +#include "Globals.h" +#include "Mobs/Horse.h" +#include "UI/HorseWindow.h" +#include "UI/SlotArea.h" + + + + + +cHorseWindow::cHorseWindow(cHorse & a_Horse): + Super(wtAnimalChest, "Horse"), + m_Horse(a_Horse) +{ + m_SlotAreas.push_back(new cSlotAreaHorse(a_Horse, *this)); + m_SlotAreas.push_back(new cSlotAreaInventory(*this)); + m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); +} + + + + + +void cHorseWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) +{ + cSlotAreas AreasInOrder; + + if (a_ClickedArea == m_SlotAreas[0]) + { + // Horse Area + AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */ + AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */ + Super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, true); + } + else + { + // Inventory or Hotbar + if (ItemCategory::IsHorseArmor(a_ItemStack.m_ItemType) || (a_ItemStack.m_ItemType == E_ITEM_SADDLE)) + { + AreasInOrder.push_back(m_SlotAreas[0]); /* Horse */ + Super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false); + } + } +} + + + + + +UInt32 cHorseWindow::GetHorseID() const +{ + return m_Horse.GetUniqueID(); +} + + + + diff --git a/src/UI/HorseWindow.h b/src/UI/HorseWindow.h new file mode 100644 index 000000000..77bc2d627 --- /dev/null +++ b/src/UI/HorseWindow.h @@ -0,0 +1,34 @@ + +// HorseWindow.h + +// Representing the UI window for a horse entity + +#pragma once + +#include "Window.h" + +class cHorse; + + + + + +class cHorseWindow : + public cWindow +{ + using Super = cWindow; +public: + cHorseWindow(cHorse & a_Horse); + + virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) override; + + /** Returns the horse's entity ID. */ + UInt32 GetHorseID() const; + +private: + cHorse & m_Horse; +}; + + + + diff --git a/src/UI/SlotArea.cpp b/src/UI/SlotArea.cpp index 865e9cb44..d093bb337 100644 --- a/src/UI/SlotArea.cpp +++ b/src/UI/SlotArea.cpp @@ -21,6 +21,7 @@ #include "../BlockArea.h" #include "../EffectID.h" #include "../ClientHandle.h" +#include "Mobs/Horse.h" @@ -2596,3 +2597,121 @@ cItem * cSlotAreaTemporary::GetPlayerSlots(cPlayer & a_Player) +//////////////////////////////////////////////////////////////////////////////// +// cSlotAreaHorse: + +cSlotAreaHorse::cSlotAreaHorse(cHorse & a_Horse, cWindow & a_ParentWindow) : + cSlotArea(2, a_ParentWindow), + m_Horse(a_Horse) +{ +} + + + + + +void cSlotAreaHorse::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) +{ + cItem & DraggingItem = a_Player.GetDraggingItem(); + + switch (a_ClickAction) + { + case caLeftClick: + case caRightClick: + case caDblClick: + { + // Check for invalid item types + if (DraggingItem.IsEmpty()) + { + break; + } + + switch (a_SlotNum) + { + case SaddleSlot: + { + if (DraggingItem.m_ItemType != E_ITEM_SADDLE) + { + return; + } + } + case ArmorSlot: + { + if (!ItemCategory::IsHorseArmor(DraggingItem.m_ItemType)) + { + return; + } + } + default: break; + } + } + default: break; + } + + cSlotArea::Clicked(a_Player, a_SlotNum, a_ClickAction, a_ClickedItem); +} + + + + + +const cItem * cSlotAreaHorse::GetSlot(int a_SlotNum, cPlayer & a_Player) const +{ + static const cItem InvalidItem; + switch (a_SlotNum) + { + case SaddleSlot: return &m_Horse.GetHorseSaddle(); + case ArmorSlot: return &m_Horse.GetHorseArmorItem(); + default: + { + LOGWARN("cSlotAreaHorse::GetSlot: Invalid slot number %d", a_SlotNum); + return &InvalidItem; + } + } +} + + + + + +void cSlotAreaHorse::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) +{ + switch (a_SlotNum) + { + case SaddleSlot: m_Horse.SetHorseSaddle(a_Item); break; + case ArmorSlot: m_Horse.SetHorseArmor(a_Item); break; + default: + { + LOGWARN("cSlotAreaHorse::SetSlot: Invalid slot number %d", a_SlotNum); + } + } +} + + + + + +void cSlotAreaHorse::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots, bool a_BackFill) +{ + if (ItemCategory::IsHorseArmor(a_ItemStack.m_ItemType) && m_Horse.GetHorseArmorItem().IsEmpty()) + { + if (a_ShouldApply) + { + m_Horse.SetHorseArmor(a_ItemStack.CopyOne()); + } + --a_ItemStack.m_ItemCount; + } + else if ((a_ItemStack.m_ItemType == E_ITEM_SADDLE) && !m_Horse.IsSaddled()) + { + if (a_ShouldApply) + { + m_Horse.SetHorseSaddle(a_ItemStack.CopyOne()); + } + --a_ItemStack.m_ItemCount; + } +} + + + + + diff --git a/src/UI/SlotArea.h b/src/UI/SlotArea.h index be21cdada..4463cb315 100644 --- a/src/UI/SlotArea.h +++ b/src/UI/SlotArea.h @@ -14,6 +14,7 @@ +class cHorse; class cWindow; class cPlayer; class cBeaconEntity; @@ -520,3 +521,27 @@ protected: + +/** Slot area holding horse saddle and armor. */ +class cSlotAreaHorse: + public cSlotArea +{ +public: + enum + { + SaddleSlot, + ArmorSlot + }; + + cSlotAreaHorse(cHorse & a_Horse, cWindow & a_ParentWindow); + virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) 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; + virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots, bool a_BackFill) override; +private: + cHorse & m_Horse; +}; + + + + -- cgit v1.2.3