Files
mod-ale/src/LuaEngine/methods/LootMethods.h

606 lines
18 KiB
C++

/*
* Copyright (C) 2010 - 2025 Eluna Lua Engine <http://emudevs.com/>
* This program is free software licensed under GPL version 3
* Please see the included DOCS/LICENSE.md for more information
*/
#ifndef LOOTMETHODS_H
#define LOOTMETHODS_H
/***
* Represents loot that can be obtained from various sources like creatures, gameobjects, or items.
*
* Contains information about items that can be looted, their quantities, money, and loot state.
*
* Inherits all methods from: none
*/
namespace LuaLoot
{
/**
* Returns `true` if all loot has been taken from this [Loot], returns `false` otherwise.
*
* @return bool isLooted
*/
int IsLooted(lua_State* L, Loot* loot)
{
Eluna::Push(L, loot->isLooted());
return 1;
}
/**
* Adds an item to the [Loot] with the specified parameters.
*
* If an item with the same ID already exists and its count is less than 255, the count will be increased instead of adding a new entry.
*
* @param uint32 itemId : the ID of the item to add
* @param uint8 minCount : minimum count of the item
* @param uint8 maxCount : maximum count of the item
* @param float chance : chance for the item to drop (0-100)
* @param uint16 lootMode : loot mode for the item
* @param bool needsQuest = false : if `true`, the item requires a quest to be looted
* @param bool allowStacking = true : if `true`, allow items to stack in the loot window
*/
int AddItem(lua_State* L, Loot* loot)
{
uint32 itemid = Eluna::CHECKVAL<uint32>(L, 2);
uint8 min_count = Eluna::CHECKVAL<uint8>(L, 3);
uint8 max_count = Eluna::CHECKVAL<uint8>(L, 4);
float chance = Eluna::CHECKVAL<float>(L, 5);
uint16 loot_mode = Eluna::CHECKVAL<uint16>(L, 6);
bool needs_quest = Eluna::CHECKVAL<bool>(L, 7, false);
bool allow_stacking = Eluna::CHECKVAL<bool>(L, 8, true);
if (allow_stacking)
{
auto& container = needs_quest ? loot->quest_items : loot->items;
for (LootItem& lootitem : container)
{
if (lootitem.itemid == itemid && lootitem.count < 255)
{
uint32 add = std::max<uint32>(1u, min_count);
uint32 newCount = std::min<uint32>(255u, lootitem.count + add);
lootitem.count = static_cast<uint8>(newCount);
return 0;
}
}
}
LootStoreItem newLootStoreItem(itemid, 0, chance, needs_quest, loot_mode, 0, min_count, max_count);
loot->AddItem(newLootStoreItem);
return 0;
}
/**
* Returns `true` if the [Loot] contains the specified item, and returns `false` otherwise.
*
* @param uint32 itemId = 0 : the ID of the item to check for. If 0, checks if any item exists
* @param uint32 count = 0 : specific count to check for. If 0, ignores count
* @return bool hasItem
*/
int HasItem(lua_State* L, Loot* loot)
{
uint32 itemid = Eluna::CHECKVAL<uint32>(L, 2, false);
uint32 count = Eluna::CHECKVAL<uint32>(L, 3, false);
bool has_item = false;
if (itemid)
{
for (const LootItem &lootitem : loot->items)
{
if (lootitem.itemid == itemid && (count == 0 || lootitem.count == count))
{
has_item = true;
break;
}
}
}
else
{
for (const LootItem &lootitem : loot->items)
{
if (lootitem.itemid != 0)
{
has_item = true;
break;
}
}
}
Eluna::Push(L, has_item);
return 1;
}
/**
* Removes the specified item from the [Loot].
*
* If count is specified, removes only that amount. Otherwise removes all items with the ID.
*
* @param uint32 itemId : the ID of the item to remove
* @param bool isCountSpecified = false : if `true`, only removes the specified count
* @param uint32 count = 0 : amount to remove when isCountSpecified is true
*/
int RemoveItem(lua_State* L, Loot* loot)
{
uint32 itemid = Eluna::CHECKVAL<uint32>(L, 2);
bool isCountSpecified = Eluna::CHECKVAL<bool>(L, 3, false);
uint32 count = isCountSpecified ? Eluna::CHECKVAL<uint32>(L, 4) : 0;
auto removeFromContainer = [&](auto& container, uint32& remaining)
{
for (auto it = container.begin(); it != container.end(); )
{
if (it->itemid == itemid)
{
if (isCountSpecified)
{
if (it->count > remaining)
{
it->count -= static_cast<uint8>(remaining);
remaining = 0;
break;
}
else
{
remaining -= it->count;
it = container.erase(it);
if (remaining == 0)
break;
continue;
}
}
else
{
it = container.erase(it);
continue;
}
}
++it;
}
};
// Remove from regular items
removeFromContainer(loot->items, count);
// Remove from quest items as well
if (!isCountSpecified || count > 0)
removeFromContainer(loot->quest_items, count);
return 0;
}
/**
* Returns the amount of money in this [Loot].
*
* @return uint32 money : the amount of money in copper
*/
int GetMoney(lua_State* L, Loot* loot)
{
Eluna::Push(L, loot->gold);
return 1;
}
/**
* Sets the amount of money in this [Loot].
*
* @param uint32 money : the amount of money to set in copper
*/
int SetMoney(lua_State* L, Loot* loot)
{
uint32 gold = Eluna::CHECKVAL<uint32>(L, 2);
loot->gold = gold;
return 0;
}
/**
* Generates a random amount of money for this [Loot] within the specified range.
*
* @param uint32 minGold : minimum amount of money in copper
* @param uint32 maxGold : maximum amount of money in copper
*/
int GenerateMoney(lua_State* L, Loot* loot)
{
uint32 min_gold = Eluna::CHECKVAL<uint32>(L, 2);
uint32 max_gold = Eluna::CHECKVAL<uint32>(L, 3);
loot->generateMoneyLoot(min_gold, max_gold);
return 0;
}
/**
* Clears all items and money from this [Loot].
*/
int Clear(lua_State* /*L*/, Loot* loot)
{
loot->clear();
return 0;
}
/**
* Sets the number of unlooted items in this [Loot].
*
* @param uint32 count : the number of unlooted items
*/
int SetUnlootedCount(lua_State* L, Loot* loot)
{
uint32 count = Eluna::CHECKVAL<uint32>(L, 2);
loot->unlootedCount = count;
return 0;
}
/**
* Returns the number of unlooted items in this [Loot].
*
* @return uint32 unlootedCount
*/
int GetUnlootedCount(lua_State* L, Loot* loot)
{
Eluna::Push(L, loot->unlootedCount);
return 1;
}
/**
* Returns a table containing all items in this [Loot].
*
* Each item is represented as a table with the following fields:
* - id: item ID
* - index: item index in the loot list
* - count: quantity of the item
* - needs_quest: whether the item requires a quest
* - is_looted: whether the item has already been looted
* - roll_winner_guid: GUID of the player who won the item roll
*
* @return table items : array of item tables
*/
int GetItems(lua_State* L, Loot* loot)
{
lua_createtable(L, loot->items.size(), 0);
int tbl = lua_gettop(L);
for (unsigned int i = 0; i < loot->items.size(); i++)
{
lua_newtable(L);
Eluna::Push(L, loot->items[i].itemid);
lua_setfield(L, -2, "id");
Eluna::Push(L, loot->items[i].itemIndex);
lua_setfield(L, -2, "index");
Eluna::Push(L, loot->items[i].count);
lua_setfield(L, -2, "count");
Eluna::Push(L, loot->items[i].needs_quest);
lua_setfield(L, -2, "needs_quest");
Eluna::Push(L, loot->items[i].is_looted);
lua_setfield(L, -2, "is_looted");
Eluna::Push(L, loot->items[i].rollWinnerGUID);
lua_setfield(L, -2, "roll_winner_guid");
lua_rawseti(L, tbl, i + 1);
}
lua_settop(L, tbl);
return 1;
}
/**
* Returns a table containing all quest items in this [Loot].
*
* Each quest item is represented as a table with the following fields:
* - id: item ID
* - index: item index in the quest loot list
* - count: quantity of the item
* - needs_quest: whether the item requires a quest
* - is_looted: whether the item has already been looted
* - roll_winner_guid: GUID of the player who won the item roll
*
* @return table quest_items : array of quest item tables
*/
int GetQuestItems(lua_State* L, Loot* loot)
{
lua_createtable(L, loot->quest_items.size(), 0);
int tbl = lua_gettop(L);
for (unsigned int i = 0; i < loot->quest_items.size(); i++)
{
lua_newtable(L);
Eluna::Push(L, loot->quest_items[i].itemid);
lua_setfield(L, -2, "id");
Eluna::Push(L, loot->quest_items[i].itemIndex);
lua_setfield(L, -2, "index");
Eluna::Push(L, loot->quest_items[i].count);
lua_setfield(L, -2, "count");
Eluna::Push(L, loot->quest_items[i].needs_quest);
lua_setfield(L, -2, "needs_quest");
Eluna::Push(L, loot->quest_items[i].is_looted);
lua_setfield(L, -2, "is_looted");
Eluna::Push(L, loot->quest_items[i].rollWinnerGUID);
lua_setfield(L, -2, "roll_winner_guid");
lua_rawseti(L, tbl, i + 1);
}
lua_settop(L, tbl);
return 1;
}
/**
* Updates the index of all items in this [Loot] to match their position in the list.
*
* This should be called after removing items to ensure indices are sequential.
*/
int UpdateItemIndex(lua_State* /*L*/, Loot* loot)
{
uint32 index = 0;
for (unsigned int i = 0; i < loot->items.size(); ++i)
loot->items[i].itemIndex = index++;
for (unsigned int i = 0; i < loot->quest_items.size(); ++i)
loot->quest_items[i].itemIndex = index++;
return 0;
}
/**
* Sets the looted status of a specific item in this [Loot].
*
* @param uint32 itemId : the ID of the item
* @param uint32 count : specific count to match. If 0, ignores count
* @param bool looted = true : `true` to mark as looted, `false` to mark as unlooted
*/
int SetItemLooted(lua_State* L, Loot* loot)
{
uint32 itemid = Eluna::CHECKVAL<uint32>(L, 2);
uint32 count = Eluna::CHECKVAL<uint32>(L, 3);
bool looted = Eluna::CHECKVAL<bool>(L, 4, true);
for (auto &lootItem : loot->items)
{
if (lootItem.itemid == itemid && (count == 0 || lootItem.count == count))
{
lootItem.is_looted = looted;
break;
}
}
return 0;
}
/**
* Returns `true` if the [Loot] is completely empty (no items and no money), returns `false` otherwise.
*
* @return bool isEmpty
*/
int IsEmpty(lua_State* L, Loot* loot)
{
Eluna::Push(L, loot->empty());
return 1;
}
/**
* Returns the [Loot] type.
*
* @return [LootType] lootType
*/
int GetLootType(lua_State* L, Loot* loot)
{
Eluna::Push(L, loot->loot_type);
return 1;
}
/**
* Sets the [Loot] type.
*
* <pre>
* enum LootType
* {
* LOOT_NONE = 0,
* LOOT_CORPSE = 1,
* LOOT_PICKPOCKETING = 2,
* LOOT_FISHING = 3,
* LOOT_DISENCHANTING = 4,
* LOOT_SKINNING = 6,
* LOOT_PROSPECTING = 7,
* LOOT_MILLING = 8,
* LOOT_FISHINGHOLE = 20,
* LOOT_INSIGNIA = 21,
* LOOT_FISHING_JUNK = 22
* };
* </pre>
*
* @param [LootType] lootType : the loot type to set
*/
int SetLootType(lua_State* L, Loot* loot)
{
uint32 lootType = Eluna::CHECKVAL<uint32>(L, 2);
loot->loot_type = static_cast<LootType>(lootType);
return 0;
}
/**
* Returns the [Player] GUID that owns this loot for round robin distribution.
*
* @return ObjectGuid roundRobinPlayer : the player GUID
*/
int GetRoundRobinPlayer(lua_State* L, Loot* loot)
{
Eluna::Push(L, loot->roundRobinPlayer);
return 1;
}
/**
* Sets the [Player] GUID for round robin loot distribution.
*
* @param ObjectGuid playerGUID : the player GUID
*/
int SetRoundRobinPlayer(lua_State* L, Loot* loot)
{
ObjectGuid guid = Eluna::CHECKVAL<ObjectGuid>(L, 2);
loot->roundRobinPlayer = guid;
return 0;
}
/**
* Returns the [Player] GUID that owns this loot.
*
* @return ObjectGuid lootOwner : the player GUID
*/
int GetLootOwner(lua_State* L, Loot* loot)
{
Eluna::Push(L, loot->lootOwnerGUID);
return 1;
}
/**
* Sets the [Player] GUID that owns this loot.
*
* @param ObjectGuid playerGUID : the player GUID
*/
int SetLootOwner(lua_State* L, Loot* loot)
{
ObjectGuid guid = Eluna::CHECKVAL<ObjectGuid>(L, 2);
loot->lootOwnerGUID = guid;
return 0;
}
/**
* Returns the container GUID that holds this loot.
*
* @return ObjectGuid containerGUID : the container GUID
*/
int GetContainer(lua_State* L, Loot* loot)
{
Eluna::Push(L, loot->containerGUID);
return 1;
}
/**
* Sets the container GUID that holds this loot.
*
* @param ObjectGuid containerGUID : the container GUID
*/
int SetContainer(lua_State* L, Loot* loot)
{
ObjectGuid guid = Eluna::CHECKVAL<ObjectGuid>(L, 2);
loot->containerGUID = guid;
return 0;
}
/**
* Returns the source [WorldObject] GUID for this loot.
*
* @return ObjectGuid sourceGUID : the source [WorldObject] GUID
*/
int GetSourceWorldObject(lua_State* L, Loot* loot)
{
Eluna::Push(L, loot->sourceWorldObjectGUID);
return 1;
}
/**
* Sets the source [WorldObject] GUID for this loot.
*
* @param ObjectGuid sourceGUID : the source [WorldObject] GUID
*/
int SetSourceWorldObject(lua_State* L, Loot* loot)
{
ObjectGuid guid = Eluna::CHECKVAL<ObjectGuid>(L, 2);
loot->sourceWorldObjectGUID = guid;
return 0;
}
/**
* Returns `true` if the [Loot] contains quest items and returns `false` otherwise.
*
* @return bool hasQuestItems
*/
int HasQuestItems(lua_State* L, Loot* loot)
{
Eluna::Push(L, !loot->quest_items.empty());
return 1;
}
/**
* Returns `true` if the [Loot] has items available for all players and returns `false` otherwise.
*
* @return bool hasItemForAll
*/
int HasItemForAll(lua_State* L, Loot* loot)
{
Eluna::Push(L, loot->hasItemForAll());
return 1;
}
/**
* Returns `true` if the [Loot] has items that are over the group loot threshold and returns `false` otherwise.
*
* @return bool hasOverThresholdItem
*/
int HasOverThresholdItem(lua_State* L, Loot* loot)
{
Eluna::Push(L, loot->hasOverThresholdItem());
return 1;
}
/**
* Returns the total number of items (regular + quest items) in this [Loot].
*
* @return uint32 itemCount
*/
int GetItemCount(lua_State* L, Loot* loot)
{
Eluna::Push(L, static_cast<uint32>(loot->items.size() + loot->quest_items.size()));
return 1;
}
/**
* Returns the maximum loot slot index available for the specified [Player].
*
* @param [Player] player : the player to check slots for
* @return uint32 maxSlot
*/
int GetMaxSlotForPlayer(lua_State* L, Loot* loot)
{
Player* player = Eluna::CHECKOBJ<Player>(L, 2);
Eluna::Push(L, loot->GetMaxSlotInLootFor(player));
return 1;
}
/**
* Adds a [Player] to the list of players currently looting this [Loot].
*
* @param [Player] player : the player to add as a looter
*/
int AddLooter(lua_State* L, Loot* loot)
{
Player* player = Eluna::CHECKOBJ<Player>(L, 2);
loot->AddLooter(player->GetGUID());
return 0;
}
/**
* Removes a [Player] from the list of players currently looting this [Loot].
*
* @param [Player] player : the player to remove from looters
*/
int RemoveLooter(lua_State* L, Loot* loot)
{
Player* player = Eluna::CHECKOBJ<Player>(L, 2);
loot->RemoveLooter(player->GetGUID());
return 0;
}
};
#endif // LOOTMETHODS_H