Core/Loot: Implemented currency loot

This commit is contained in:
Shauren
2024-08-26 15:02:22 +02:00
parent 6b4270850f
commit 3e28ee080a
17 changed files with 576 additions and 237 deletions

View File

@@ -17,8 +17,8 @@
#include "Loot.h"
#include "Containers.h"
#include "DatabaseEnv.h"
#include "DB2Stores.h"
#include "DatabaseEnv.h"
#include "GameTime.h"
#include "Group.h"
#include "Item.h"
@@ -40,26 +40,26 @@
//
// Constructor, copies most fields from LootStoreItem and generates random count
LootItem::LootItem(LootStoreItem const& li)
LootItem::LootItem(LootStoreItem const& li) : itemid(li.itemid), conditions(li.conditions), needs_quest(li.needs_quest)
{
itemid = li.itemid;
LootListId = 0;
conditions = li.conditions;
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemid);
freeforall = proto && proto->HasFlag(ITEM_FLAG_MULTI_DROP);
follow_loot_rules = !li.needs_quest || (proto && proto->HasFlag(ITEM_FLAGS_CU_FOLLOW_LOOT_RULES));
needs_quest = li.needs_quest;
randomBonusListId = GenerateItemRandomBonusListId(itemid);
context = ItemContext::NONE;
count = 0;
is_looted = false;
is_blocked = false;
is_underthreshold = false;
is_counted = false;
rollWinnerGUID = ObjectGuid::Empty;
switch (li.type)
{
case LootStoreItem::Type::Item:
{
randomBonusListId = GenerateItemRandomBonusListId(itemid);
type = LootItemType::Item;
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemid);
freeforall = proto && proto->HasFlag(ITEM_FLAG_MULTI_DROP);
follow_loot_rules = !li.needs_quest || (proto && proto->HasFlag(ITEM_FLAGS_CU_FOLLOW_LOOT_RULES));
break;
}
case LootStoreItem::Type::Currency:
type = LootItemType::Currency;
freeforall = true;
break;
default:
break;
}
}
LootItem::LootItem(LootItem const&) = default;
@@ -71,10 +71,35 @@ LootItem::~LootItem() = default;
// Basic checks for player/item compatibility - if false no chance to see the item in the loot
bool LootItem::AllowedForPlayer(Player const* player, Loot const* loot) const
{
return AllowedForPlayer(player, loot, itemid, needs_quest, follow_loot_rules, false, conditions);
switch (type)
{
case LootItemType::Item:
return ItemAllowedForPlayer(player, loot, itemid, needs_quest, follow_loot_rules, false, conditions);
case LootItemType::Currency:
return CurrencyAllowedForPlayer(player, itemid, needs_quest, conditions);
default:
break;
}
return false;
}
bool LootItem::AllowedForPlayer(Player const* player, Loot const* loot, uint32 itemid, bool needs_quest, bool follow_loot_rules, bool strictUsabilityCheck,
bool LootItem::AllowedForPlayer(Player const* player, LootStoreItem const& lootStoreItem, bool strictUsabilityCheck)
{
switch (lootStoreItem.type)
{
case LootStoreItem::Type::Item:
return ItemAllowedForPlayer(player, nullptr, lootStoreItem.itemid, lootStoreItem.needs_quest,
!lootStoreItem.needs_quest || ASSERT_NOTNULL(sObjectMgr->GetItemTemplate(lootStoreItem.itemid))->HasFlag(ITEM_FLAGS_CU_FOLLOW_LOOT_RULES),
strictUsabilityCheck, lootStoreItem.conditions);
case LootStoreItem::Type::Currency:
return CurrencyAllowedForPlayer(player, lootStoreItem.itemid, lootStoreItem.needs_quest, lootStoreItem.conditions);
default:
break;
}
return false;
}
bool LootItem::ItemAllowedForPlayer(Player const* player, Loot const* loot, uint32 itemid, bool needs_quest, bool follow_loot_rules, bool strictUsabilityCheck,
ConditionsReference const& conditions)
{
// DB conditions check
@@ -128,14 +153,38 @@ bool LootItem::AllowedForPlayer(Player const* player, Loot const* loot, uint32 i
return true;
}
void LootItem::AddAllowedLooter(const Player* player)
bool LootItem::CurrencyAllowedForPlayer(Player const* player, uint32 currencyId, bool needs_quest, ConditionsReference const& conditions)
{
// DB conditions check
if (!conditions.Meets(player))
return false;
CurrencyTypesEntry const* currency = sCurrencyTypesStore.LookupEntry(currencyId);
if (!currency)
return false;
// not show loot for not own team
if (currency->GetFlags().HasFlag(CurrencyTypesFlags::IsHordeOnly) && player->GetTeam() != HORDE)
return false;
if (currency->GetFlags().HasFlag(CurrencyTypesFlags::IsAllianceOnly) && player->GetTeam() != ALLIANCE)
return false;
// check quest requirements
if (needs_quest && !player->HasQuestForCurrency(currencyId))
return false;
return true;
}
void LootItem::AddAllowedLooter(Player const* player)
{
allowedGUIDs.insert(player->GetGUID());
}
bool LootItem::HasAllowedLooter(ObjectGuid const& looter) const
{
return allowedGUIDs.find(looter) != allowedGUIDs.end();
return allowedGUIDs.contains(looter);
}
Optional<LootSlotType> LootItem::GetUiTypeForPlayer(Player const* player, Loot const& loot) const
@@ -143,7 +192,7 @@ Optional<LootSlotType> LootItem::GetUiTypeForPlayer(Player const* player, Loot c
if (is_looted)
return {};
if (allowedGUIDs.find(player->GetGUID()) == allowedGUIDs.end())
if (!allowedGUIDs.contains(player->GetGUID()))
return {};
if (freeforall)
@@ -604,7 +653,8 @@ void LootRoll::Finish(RollVoteMap::const_iterator winnerItr)
{
for (uint32 i = 0; i < loot.items.size(); ++i)
if (LootItem* disenchantLoot = loot.LootItemInSlot(i, player))
player->SendItemRetrievalMail(disenchantLoot->itemid, disenchantLoot->count, disenchantLoot->context);
if (disenchantLoot->type == LootItemType::Item)
player->SendItemRetrievalMail(disenchantLoot->itemid, disenchantLoot->count, disenchantLoot->context);
}
else
m_loot->NotifyItemRemoved(m_lootItem->LootListId, m_map);
@@ -791,7 +841,7 @@ bool Loot::FillLoot(uint32 lootId, LootStore const& store, Player* lootOwner, bo
for (LootItem& item : items)
{
if (!item.follow_loot_rules || item.freeforall)
if (!item.follow_loot_rules || item.freeforall || item.type != LootItemType::Item)
continue;
if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(item.itemid))
@@ -826,23 +876,40 @@ bool Loot::FillLoot(uint32 lootId, LootStore const& store, Player* lootOwner, bo
// Inserts the item into the loot (called by LootTemplate processors)
void Loot::AddItem(LootStoreItem const& item)
{
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(item.itemid);
if (!proto)
return;
uint32 count = urand(item.mincount, item.maxcount);
uint32 stacks = count / proto->GetMaxStackSize() + ((count % proto->GetMaxStackSize()) ? 1 : 0);
for (uint32 i = 0; i < stacks && items.size() < MAX_NR_LOOT_ITEMS; ++i)
switch (item.type)
{
LootItem generatedLoot(item);
generatedLoot.context = _itemContext;
generatedLoot.count = std::min(count, proto->GetMaxStackSize());
generatedLoot.LootListId = items.size();
generatedLoot.BonusListIDs = ItemBonusMgr::GetBonusListsForItem(generatedLoot.itemid, _itemContext);
case LootStoreItem::Type::Item:
{
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(item.itemid);
if (!proto)
return;
items.push_back(generatedLoot);
count -= proto->GetMaxStackSize();
uint32 count = urand(item.mincount, item.maxcount);
uint32 stacks = count / proto->GetMaxStackSize() + ((count % proto->GetMaxStackSize()) ? 1 : 0);
for (uint32 i = 0; i < stacks && items.size() < MAX_NR_LOOT_ITEMS; ++i)
{
LootItem generatedLoot(item);
generatedLoot.context = _itemContext;
generatedLoot.count = std::min(count, proto->GetMaxStackSize());
generatedLoot.LootListId = items.size();
generatedLoot.BonusListIDs = ItemBonusMgr::GetBonusListsForItem(generatedLoot.itemid, _itemContext);
items.push_back(generatedLoot);
count -= proto->GetMaxStackSize();
}
break;
}
case LootStoreItem::Type::Currency:
{
LootItem generatedLoot(item);
generatedLoot.count = urand(item.mincount, item.maxcount);
generatedLoot.LootListId = items.size();
items.push_back(generatedLoot);
break;
}
default:
break;
}
}
@@ -867,17 +934,36 @@ bool Loot::AutoStore(Player* player, uint8 bag, uint8 slot, bool broadcast, bool
if (!lootItem->rollWinnerGUID.IsEmpty() && lootItem->rollWinnerGUID != GetGUID())
continue;
ItemPosCountVec dest;
InventoryResult msg = player->CanStoreNewItem(bag, slot, dest, lootItem->itemid, lootItem->count);
if (msg != EQUIP_ERR_OK && slot != NULL_SLOT)
msg = player->CanStoreNewItem(bag, NULL_SLOT, dest, lootItem->itemid, lootItem->count);
if (msg != EQUIP_ERR_OK && bag != NULL_BAG)
msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, lootItem->itemid, lootItem->count);
if (msg != EQUIP_ERR_OK)
switch (lootItem->type)
{
player->SendEquipError(msg, nullptr, nullptr, lootItem->itemid);
allLooted = false;
continue;
case LootItemType::Item:
{
ItemPosCountVec dest;
InventoryResult msg = player->CanStoreNewItem(bag, slot, dest, lootItem->itemid, lootItem->count);
if (msg != EQUIP_ERR_OK && slot != NULL_SLOT)
msg = player->CanStoreNewItem(bag, NULL_SLOT, dest, lootItem->itemid, lootItem->count);
if (msg != EQUIP_ERR_OK && bag != NULL_BAG)
msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, lootItem->itemid, lootItem->count);
if (msg != EQUIP_ERR_OK)
{
player->SendEquipError(msg, nullptr, nullptr, lootItem->itemid);
allLooted = false;
continue;
}
if (Item* pItem = player->StoreNewItem(dest, lootItem->itemid, true, lootItem->randomBonusListId, GuidSet(), lootItem->context, &lootItem->BonusListIDs))
{
player->SendNewItem(pItem, lootItem->count, false, createdByPlayer, broadcast, GetDungeonEncounterId());
player->ApplyItemLootedSpell(pItem, true);
}
else
player->ApplyItemLootedSpell(sObjectMgr->GetItemTemplate(lootItem->itemid));
break;
}
case LootItemType::Currency:
player->ModifyCurrency(lootItem->itemid, lootItem->count, CurrencyGainSource::Loot);
break;
}
if (ffaitem)
@@ -887,14 +973,6 @@ bool Loot::AutoStore(Player* player, uint8 bag, uint8 slot, bool broadcast, bool
lootItem->is_looted = true;
--unlootedCount;
if (Item* pItem = player->StoreNewItem(dest, lootItem->itemid, true, lootItem->randomBonusListId, GuidSet(), lootItem->context, &lootItem->BonusListIDs))
{
player->SendNewItem(pItem, lootItem->count, false, createdByPlayer, broadcast, GetDungeonEncounterId());
player->ApplyItemLootedSpell(pItem, true);
}
else
player->ApplyItemLootedSpell(sObjectMgr->GetItemTemplate(lootItem->itemid));
}
return allLooted;
@@ -966,18 +1044,12 @@ bool Loot::hasItemFor(Player const* player) const
{
// quest items
for (LootItem const& lootItem : items)
if (!lootItem.is_looted && !lootItem.follow_loot_rules && lootItem.GetAllowedLooters().find(player->GetGUID()) != lootItem.GetAllowedLooters().end())
if (!lootItem.is_looted && !lootItem.follow_loot_rules && lootItem.GetAllowedLooters().contains(player->GetGUID()))
return true;
if (NotNormalLootItemList const* ffaItems = Trinity::Containers::MapGetValuePtr(GetPlayerFFAItems(), player->GetGUID()))
{
bool hasFfaItem = std::ranges::any_of(*ffaItems, [&](NotNormalLootItem const& ffaItem)
{
return !ffaItem.is_looted;
});
if (hasFfaItem)
if (std::ranges::any_of(*ffaItems, std::identity(), &NotNormalLootItem::is_looted))
return true;
}
return false;
}
@@ -1004,11 +1076,33 @@ void Loot::BuildLootResponse(WorldPackets::Loot::LootResponse& packet, Player co
if (!uiType)
continue;
WorldPackets::Loot::LootItemData& lootItem = packet.Items.emplace_back();
lootItem.LootListID = item.LootListId;
lootItem.UIType = *uiType;
lootItem.Quantity = item.count;
lootItem.Loot.Initialize(item);
switch (item.type)
{
case LootItemType::Item:
{
WorldPackets::Loot::LootItemData& lootItem = packet.Items.emplace_back();
lootItem.LootListID = item.LootListId;
lootItem.UIType = *uiType;
lootItem.Quantity = item.count;
lootItem.Loot.Initialize(item);
break;
}
case LootItemType::Currency:
{
WorldPackets::Loot::LootCurrency& lootCurrency = packet.Currencies.emplace_back();
lootCurrency.CurrencyID = item.itemid;
lootCurrency.Quantity = item.count;
lootCurrency.LootListID = item.LootListId;
lootCurrency.UIType = *uiType;
// fake visible quantity for SPELL_AURA_MOD_CURRENCY_CATEGORY_GAIN_PCT - handled in Player::ModifyCurrency
lootCurrency.Quantity = float(lootCurrency.Quantity) * viewer->GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_CURRENCY_CATEGORY_GAIN_PCT, sCurrencyTypesStore.AssertEntry(item.itemid)->CategoryID);
break;
}
default:
break;
}
}
}