mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-15 23:20:36 +01:00
Core/Loot: Implemented currency loot
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user