TrinityCore
Loading...
Searching...
No Matches
Loot.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "Loot.h"
19#include "DB2Stores.h"
20#include "DatabaseEnv.h"
21#include "GameTime.h"
22#include "Group.h"
23#include "Item.h"
24#include "ItemBonusMgr.h"
25#include "ItemTemplate.h"
26#include "Log.h"
27#include "LootMgr.h"
28#include "LootPackets.h"
29#include "Map.h"
30#include "MapUtils.h"
31#include "ObjectAccessor.h"
32#include "ObjectMgr.h"
33#include "Player.h"
34#include "Random.h"
35#include "World.h"
36#include "WorldSession.h"
37
38//
39// --------- LootItem ---------
40//
41
42// Constructor, copies most fields from LootStoreItem and generates random count
43LootItem::LootItem(LootStoreItem const& li) : itemid(li.itemid), conditions(li.conditions), needs_quest(li.needs_quest)
44{
45 switch (li.type)
46 {
48 {
51 ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemid);
52 freeforall = proto && proto->HasFlag(ITEM_FLAG_MULTI_DROP);
54 break;
55 }
58 freeforall = true;
59 break;
62 freeforall = true;
63 break;
64 default:
65 break;
66 }
67}
68
69LootItem::LootItem(LootItem const&) = default;
70LootItem::LootItem(LootItem&&) noexcept = default;
71LootItem& LootItem::operator=(LootItem const&) = default;
72LootItem& LootItem::operator=(LootItem&&) noexcept = default;
73LootItem::~LootItem() = default;
74
75// Basic checks for player/item compatibility - if false no chance to see the item in the loot
76bool LootItem::AllowedForPlayer(Player const* player, Loot const* loot) const
77{
78 switch (type)
79 {
81 return ItemAllowedForPlayer(player, loot, itemid, needs_quest, follow_loot_rules, false, conditions);
83 return CurrencyAllowedForPlayer(player, itemid, needs_quest, conditions);
85 return TrackingQuestAllowedForPlayer(player, itemid, conditions);
86 default:
87 break;
88 }
89 return false;
90}
91
92bool LootItem::AllowedForPlayer(Player const* player, LootStoreItem const& lootStoreItem, bool strictUsabilityCheck)
93{
94 switch (lootStoreItem.type)
95 {
97 return ItemAllowedForPlayer(player, nullptr, lootStoreItem.itemid, lootStoreItem.needs_quest,
98 !lootStoreItem.needs_quest || ASSERT_NOTNULL(sObjectMgr->GetItemTemplate(lootStoreItem.itemid))->HasFlag(ITEM_FLAGS_CU_FOLLOW_LOOT_RULES),
99 strictUsabilityCheck, lootStoreItem.conditions);
101 return CurrencyAllowedForPlayer(player, lootStoreItem.itemid, lootStoreItem.needs_quest, lootStoreItem.conditions);
103 return TrackingQuestAllowedForPlayer(player, lootStoreItem.itemid, lootStoreItem.conditions);
104 default:
105 break;
106 }
107 return false;
108}
109
110bool LootItem::ItemAllowedForPlayer(Player const* player, Loot const* loot, uint32 itemid, bool needs_quest, bool follow_loot_rules, bool strictUsabilityCheck,
111 ConditionsReference const& conditions)
112{
113 // DB conditions check
114 if (!conditions.Meets(player))
115 return false;
116
117 ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(itemid);
118 if (!pProto)
119 return false;
120
121 // not show loot for not own team
122 if (pProto->HasFlag(ITEM_FLAG2_FACTION_HORDE) && player->GetTeam() != HORDE)
123 return false;
124
125 if (pProto->HasFlag(ITEM_FLAG2_FACTION_ALLIANCE) && player->GetTeam() != ALLIANCE)
126 return false;
127
128 // Master looter can see all items even if the character can't loot them
129 if (loot && loot->GetLootMethod() == MASTER_LOOT && follow_loot_rules && loot->GetLootMasterGUID() == player->GetGUID())
130 return true;
131
132 // Don't allow loot for players without profession or those who already know the recipe
134 {
135 if (!player->HasSkill(pProto->GetRequiredSkill()))
136 return false;
137
138 for (ItemEffectEntry const* itemEffect : pProto->Effects)
139 {
140 if (itemEffect->TriggerType != ITEM_SPELLTRIGGER_ON_LEARN)
141 continue;
142
143 if (player->HasSpell(itemEffect->SpellID))
144 return false;
145 }
146 }
147
148 // check quest requirements
149 if (!pProto->HasFlag(ITEM_FLAGS_CU_IGNORE_QUEST_STATUS) && ((needs_quest || (pProto->GetStartQuest() && player->GetQuestStatus(pProto->GetStartQuest()) != QUEST_STATUS_NONE)) && !player->HasQuestForItem(itemid)))
150 return false;
151
152 if (strictUsabilityCheck)
153 {
154 if ((pProto->IsWeapon() || pProto->IsArmor()) && !pProto->IsUsableByLootSpecialization(player, true))
155 return false;
156
157 if (player->CanRollNeedForItem(pProto, nullptr, false) != EQUIP_ERR_OK)
158 return false;
159 }
160
161 return true;
162}
163
164bool LootItem::CurrencyAllowedForPlayer(Player const* player, uint32 currencyId, bool needs_quest, ConditionsReference const& conditions)
165{
166 // DB conditions check
167 if (!conditions.Meets(player))
168 return false;
169
170 CurrencyTypesEntry const* currency = sCurrencyTypesStore.LookupEntry(currencyId);
171 if (!currency)
172 return false;
173
174 // not show loot for not own team
175 if (currency->GetFlags().HasFlag(CurrencyTypesFlags::IsHordeOnly) && player->GetTeam() != HORDE)
176 return false;
177
178 if (currency->GetFlags().HasFlag(CurrencyTypesFlags::IsAllianceOnly) && player->GetTeam() != ALLIANCE)
179 return false;
180
181 // check quest requirements
182 if (needs_quest && !player->HasQuestForCurrency(currencyId))
183 return false;
184
185 return true;
186}
187
188bool LootItem::TrackingQuestAllowedForPlayer(Player const* player, uint32 questId, ConditionsReference const& conditions)
189{
190 // DB conditions check
191 if (!conditions.Meets(player))
192 return false;
193
194 if (player->IsQuestCompletedBitSet(questId))
195 return false;
196
197 return true;
198}
199
201{
202 allowedGUIDs.insert(player->GetGUID());
203}
204
205bool LootItem::HasAllowedLooter(ObjectGuid const& looter) const
206{
207 return allowedGUIDs.contains(looter);
208}
209
211{
212 if (is_looted)
213 return {};
214
215 if (!allowedGUIDs.contains(player->GetGUID()))
216 return {};
217
218 if (freeforall)
219 {
221 {
222 auto ffaItemItr = std::ranges::find(*ffaItems, LootListId, &NotNormalLootItem::LootListId);
223 if (ffaItemItr != ffaItems->end() && !ffaItemItr->is_looted)
225 }
226 return {};
227 }
228
231
232 switch (loot.GetLootMethod())
233 {
234 case FREE_FOR_ALL:
236 case ROUND_ROBIN:
237 if (!loot.roundRobinPlayer.IsEmpty() && loot.roundRobinPlayer != player->GetGUID())
238 return {};
239
241 case MASTER_LOOT:
243 {
244 if (!loot.roundRobinPlayer.IsEmpty() && loot.roundRobinPlayer != player->GetGUID())
245 return {};
246
248 }
249
251 case GROUP_LOOT:
254 if (!loot.roundRobinPlayer.IsEmpty() && loot.roundRobinPlayer != player->GetGUID())
255 return {};
256
257 if (is_blocked)
259
260 if (rollWinnerGUID.IsEmpty()) // all passed
262
263 if (rollWinnerGUID == player->GetGUID())
265
266 return {};
267 case PERSONAL_LOOT:
269 default:
270 break;
271 }
272
273 return {};
274}
275
276//
277// ------- Loot Roll -------
278//
279
280// Send the roll for the whole group
282{
283 ItemTemplate const* itemTemplate = ASSERT_NOTNULL(sObjectMgr->GetItemTemplate(m_lootItem->itemid));
284 for (auto const& [playerGuid, roll] : m_rollVoteMap)
285 {
286 if (roll.Vote != RollVote::NotEmitedYet)
287 continue;
288
289 Player* player = ObjectAccessor::GetPlayer(m_map, playerGuid);
290 if (!player)
291 continue;
292
294 startLootRoll.LootObj = m_loot->GetGUID();
295 startLootRoll.MapID = m_map->GetId();
296 startLootRoll.RollTime = LOOT_ROLL_TIMEOUT;
297 startLootRoll.Method = m_loot->GetLootMethod();
298 startLootRoll.ValidRolls = m_voteMask;
299 // In NEED_BEFORE_GREED need disabled for non-usable item for player
300 if (m_loot->GetLootMethod() == NEED_BEFORE_GREED && player->CanRollNeedForItem(itemTemplate, m_map, true) != EQUIP_ERR_OK)
301 startLootRoll.ValidRolls &= ~ROLL_FLAG_TYPE_NEED;
302
303 FillPacket(startLootRoll.Item);
306
307 player->SendDirectMessage(startLootRoll.Write());
308 }
309
310 // Handle auto pass option
311 for (auto const& [playerGuid, roll] : m_rollVoteMap)
312 {
313 if (roll.Vote != RollVote::Pass)
314 continue;
315
316 SendRoll(playerGuid, -1, RollVote::Pass, {});
317 }
318}
319
320// Send all passed message
322{
324 lootAllPassed.LootObj = m_loot->GetGUID();
325 FillPacket(lootAllPassed.Item);
326 lootAllPassed.Item.UIType = LOOT_SLOT_TYPE_ALLOW_LOOT;
328 lootAllPassed.Write();
329
330 for (auto const& [playerGuid, roll] : m_rollVoteMap)
331 {
332 if (roll.Vote != RollVote::NotValid)
333 continue;
334
335 Player* player = ObjectAccessor::GetPlayer(m_map, playerGuid);
336 if (!player)
337 continue;
338
339 player->SendDirectMessage(lootAllPassed.GetRawPacket());
340 }
341}
342
343// Send roll of targetGuid to the whole group (included targuetGuid)
344void LootRoll::SendRoll(ObjectGuid const& targetGuid, int32 rollNumber, RollVote rollType, Optional<ObjectGuid> const& rollWinner)
345{
347 lootRoll.LootObj = m_loot->GetGUID();
348 lootRoll.Player = targetGuid;
349 lootRoll.Roll = rollNumber;
350 lootRoll.RollType = AsUnderlyingType(rollType);
351 lootRoll.Autopassed = false;
352 FillPacket(lootRoll.Item);
355 lootRoll.Write();
356
357 for (auto const& [playerGuid, roll] : m_rollVoteMap)
358 {
359 if (roll.Vote == RollVote::NotValid)
360 continue;
361
362 if (playerGuid == rollWinner)
363 continue;
364
365 Player* player = ObjectAccessor::GetPlayer(m_map, playerGuid);
366 if (!player)
367 continue;
368
369 player->SendDirectMessage(lootRoll.GetRawPacket());
370 }
371
372 if (rollWinner)
373 {
374 if (Player* player = ObjectAccessor::GetPlayer(m_map, *rollWinner))
375 {
377 lootRoll.Clear();
378 player->SendDirectMessage(lootRoll.Write());
379 }
380 }
381}
382
383// Send roll 'value' of the whole group and the winner to the whole group
384void LootRoll::SendLootRollWon(ObjectGuid const& targetGuid, int32 rollNumber, RollVote rollType)
385{
386 // Send roll values
387 for (auto const& [playerGuid, roll] : m_rollVoteMap)
388 {
389 switch (roll.Vote)
390 {
391 case RollVote::Pass:
392 break;
395 SendRoll(playerGuid, 0, RollVote::Pass, targetGuid);
396 break;
397 default:
398 SendRoll(playerGuid, roll.RollNumber, roll.Vote, targetGuid);
399 break;
400 }
401 }
402
404 lootRollWon.LootObj = m_loot->GetGUID();
405 lootRollWon.Winner = targetGuid;
406 lootRollWon.Roll = rollNumber;
407 lootRollWon.RollType = AsUnderlyingType(rollType);
408 FillPacket(lootRollWon.Item);
409 lootRollWon.Item.UIType = LOOT_SLOT_TYPE_LOCKED;
411 lootRollWon.MainSpec = true; // offspec rolls not implemented
412 lootRollWon.Write();
413
414 for (auto const& [playerGuid, roll] : m_rollVoteMap)
415 {
416 if (roll.Vote == RollVote::NotValid)
417 continue;
418
419 if (playerGuid == targetGuid)
420 continue;
421
422 Player* player = ObjectAccessor::GetPlayer(m_map, playerGuid);
423 if (!player)
424 continue;
425
426 player->SendDirectMessage(lootRollWon.GetRawPacket());
427 }
428
429 if (Player* player = ObjectAccessor::GetPlayer(m_map, targetGuid))
430 {
432 lootRollWon.Clear();
433 player->SendDirectMessage(lootRollWon.Write());
434 }
435}
436
438{
439 lootItem.Quantity = m_lootItem->count;
440 lootItem.LootListID = m_lootItem->LootListId;
441 lootItem.CanTradeToTapList = m_lootItem->allowedGUIDs.size() > 1;
442 lootItem.Loot.Initialize(*m_lootItem);
443}
444
446{
447 if (m_isStarted)
449
450 for (auto const& [playerGuid, roll] : m_rollVoteMap)
451 {
452 if (roll.Vote != RollVote::NotEmitedYet)
453 continue;
454
455 Player* player = ObjectAccessor::GetPlayer(m_map, playerGuid);
456 if (!player)
457 continue;
458
459 player->RemoveLootRoll(this);
460 }
461}
462
463// Try to start the group roll for the specified item (it may fail for quest item or any condition
464// If this method return false the roll have to be removed from the container to avoid any problem
465bool LootRoll::TryToStart(Map* map, Loot& loot, uint32 lootListId, uint16 enchantingSkill)
466{
467 if (!m_isStarted)
468 {
469 if (lootListId >= loot.items.size())
470 return false;
471
472 m_map = map;
473
474 // initialize the data needed for the roll
475 m_lootItem = &loot.items[lootListId];
476
477 m_loot = &loot;
478 m_lootItem->is_blocked = true; // block the item while rolling
479
480 uint32 playerCount = 0;
481 for (ObjectGuid allowedLooter : m_lootItem->GetAllowedLooters())
482 {
483 Player* plr = ObjectAccessor::GetPlayer(m_map, allowedLooter);
484 if (!plr || !m_lootItem->HasAllowedLooter(plr->GetGUID())) // check if player meet the condition to be able to roll this item
485 {
486 m_rollVoteMap[allowedLooter].Vote = RollVote::NotValid;
487 continue;
488 }
489 // initialize player vote map
491 if (!plr->GetPassOnGroupLoot())
492 plr->AddLootRoll(this);
493
494 ++playerCount;
495 }
496
497 // initialize item prototype and check enchant possibilities for this group
498 ItemTemplate const* itemTemplate = ASSERT_NOTNULL(sObjectMgr->GetItemTemplate(m_lootItem->itemid));
500 if (itemTemplate->HasFlag(ITEM_FLAG2_CAN_ONLY_ROLL_GREED))
502 if (Optional<uint16> disenchantSkillRequired = GetItemDisenchantSkillRequired(); !disenchantSkillRequired || disenchantSkillRequired > enchantingSkill)
504
505 if (playerCount > 1) // check if more than one player can loot this item
506 {
507 // start the roll
510 m_isStarted = true;
511 return true;
512 }
513 // no need to start roll if one or less player can loot this item so place it under threshold
515 m_lootItem->is_blocked = false;
516 }
517 return false;
518}
519
520// Add vote from playerGuid
522{
523 ObjectGuid const& playerGuid = player->GetGUID();
524 RollVoteMap::iterator voterItr = m_rollVoteMap.find(playerGuid);
525 if (voterItr == m_rollVoteMap.end())
526 return false;
527
528 voterItr->second.Vote = vote;
529
530 if (vote != RollVote::Pass && vote != RollVote::NotValid)
531 voterItr->second.RollNumber = urand(1, 100);
532
533 switch (vote)
534 {
535 case RollVote::Pass: // Player choose pass
536 {
537 SendRoll(playerGuid, -1, RollVote::Pass, {});
538 break;
539 }
540 case RollVote::Need: // player choose Need
541 {
542 SendRoll(playerGuid, 0, RollVote::Need, {});
544 break;
545 }
546 case RollVote::Greed: // player choose Greed
547 {
548 SendRoll(playerGuid, -1, RollVote::Greed, {});
550 break;
551 }
552 case RollVote::Disenchant: // player choose Disenchant
553 {
554 SendRoll(playerGuid, -1, RollVote::Disenchant, {});
556 break;
557 }
558 default: // Roll removed case
559 return false;
560 }
561 return true;
562}
563
564// check if we can found a winner for this roll or if timer is expired
566{
567 RollVoteMap::const_iterator winnerItr = m_rollVoteMap.end();
568
569 if (AllPlayerVoted(winnerItr) || m_endTime <= GameTime::Now())
570 {
571 Finish(winnerItr);
572 return true;
573 }
574 return false;
575}
576
577bool LootRoll::IsLootItem(ObjectGuid const& lootObject, uint32 lootListId) const
578{
579 return m_loot->GetGUID() == lootObject && m_lootItem->LootListId == lootListId;
580}
581
587bool LootRoll::AllPlayerVoted(RollVoteMap::const_iterator& winnerItr)
588{
589 uint32 notVoted = 0;
590 bool isSomeoneNeed = false;
591
592 winnerItr = m_rollVoteMap.end();
593 for (RollVoteMap::const_iterator itr = m_rollVoteMap.begin(); itr != m_rollVoteMap.end(); ++itr)
594 {
595 switch (itr->second.Vote)
596 {
597 case RollVote::Need:
598 if (!isSomeoneNeed || winnerItr == m_rollVoteMap.end() || itr->second.RollNumber > winnerItr->second.RollNumber)
599 {
600 isSomeoneNeed = true; // first passage will force to set winner because need is prioritized
601 winnerItr = itr;
602 }
603 break;
604 case RollVote::Greed:
606 if (!isSomeoneNeed) // if at least one need is detected then winner can't be a greed
607 {
608 if (winnerItr == m_rollVoteMap.end() || itr->second.RollNumber > winnerItr->second.RollNumber)
609 winnerItr = itr;
610 }
611 break;
612 // Explicitly passing excludes a player from winning loot, so no action required.
613 case RollVote::Pass:
614 break;
616 ++notVoted;
617 break;
618 default:
619 break;
620 }
621 }
622
623 return notVoted == 0;
624}
625
627{
629 itemInstance.Initialize(*m_lootItem);
630
631 BonusData bonusData;
632 bonusData.Initialize(itemInstance);
633 if (!bonusData.CanDisenchant)
634 return {};
635
636 if (bonusData.DisenchantLootId)
637 return bonusData.DisenchantLootId;
638
639 ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(m_lootItem->itemid);
640
641 // ignore temporary item level scaling (pvp or timewalking)
642 uint32 itemLevel = Item::GetItemLevel(itemTemplate, bonusData, bonusData.RequiredLevel, 0, 0, 0, 0, false, 0);
643
644 ItemDisenchantLootEntry const* disenchantLoot = Item::GetBaseDisenchantLoot(itemTemplate, bonusData.Quality, itemLevel);
645 if (!disenchantLoot)
646 return {};
647
648 return disenchantLoot->ID;
649}
650
652{
654 itemInstance.Initialize(*m_lootItem);
655
656 BonusData bonusData;
657 bonusData.Initialize(itemInstance);
658 if (!bonusData.CanDisenchant)
659 return {};
660
661 ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(m_lootItem->itemid);
662
663 // ignore temporary item level scaling (pvp or timewalking)
664 uint32 itemLevel = Item::GetItemLevel(itemTemplate, bonusData, bonusData.RequiredLevel, 0, 0, 0, 0, false, 0);
665
666 ItemDisenchantLootEntry const* disenchantLoot = Item::GetBaseDisenchantLoot(itemTemplate, bonusData.Quality, itemLevel);
667 if (!disenchantLoot)
668 return {};
669
670 return disenchantLoot->SkillRequired;
671}
672
673// terminate the roll
674void LootRoll::Finish(RollVoteMap::const_iterator winnerItr)
675{
676 m_lootItem->is_blocked = false;
677 if (winnerItr == m_rollVoteMap.end())
678 {
680 }
681 else
682 {
683 m_lootItem->rollWinnerGUID = winnerItr->first;
684
685 SendLootRollWon(winnerItr->first, winnerItr->second.RollNumber, winnerItr->second.Vote);
686
687 if (Player* player = ObjectAccessor::FindConnectedPlayer(winnerItr->first))
688 {
689 if (winnerItr->second.Vote == RollVote::Need)
690 player->UpdateCriteria(CriteriaType::RollNeed, m_lootItem->itemid, winnerItr->second.RollNumber);
691 else if (winnerItr->second.Vote == RollVote::Disenchant)
692 player->UpdateCriteria(CriteriaType::CastSpell, 13262);
693 else
694 player->UpdateCriteria(CriteriaType::RollGreed, m_lootItem->itemid, winnerItr->second.RollNumber);
695
696 if (winnerItr->second.Vote == RollVote::Disenchant)
697 {
698 Loot loot(m_map, m_loot->GetOwnerGUID(), LOOT_DISENCHANTING, nullptr);
700 if (!loot.AutoStore(player, NULL_BAG, NULL_SLOT, true))
701 {
702 for (uint32 i = 0; i < loot.items.size(); ++i)
703 if (LootItem* disenchantLoot = loot.LootItemInSlot(i, player))
704 if (disenchantLoot->type == LootItemType::Item)
705 player->SendItemRetrievalMail(disenchantLoot->itemid, disenchantLoot->count, disenchantLoot->context);
706 }
707 else
709 }
710 else
711 player->StoreLootItem(m_loot->GetOwnerGUID(), m_lootItem->LootListId, m_loot);
712 }
713 }
714 m_isStarted = false;
715}
716
717//
718// --------- Loot ---------
719//
720
721Loot::Loot(Map* map, ObjectGuid owner, LootType type, Group const* group) : gold(0), unlootedCount(0), loot_type(type),
722 _guid(map ? ObjectGuid::Create<HighGuid::LootObject>(map->GetId(), 0, map->GenerateLowGuid<HighGuid::LootObject>()) : ObjectGuid::Empty),
723 _owner(owner), _itemContext(ItemContext::NONE), _lootMethod(group ? group->GetLootMethod() : FREE_FOR_ALL),
724 _lootMaster(group ? group->GetMasterLooterGuid() : ObjectGuid::Empty), _wasOpened(false), _changed(false), _dungeonEncounterId(0)
725{
726}
727
729{
730 GuidSet activeLooters = std::move(PlayersLooting);
731 for (ObjectGuid playerGuid : activeLooters)
732 if (Player* player = ObjectAccessor::FindConnectedPlayer(playerGuid))
733 player->GetSession()->DoLootRelease(this);
734}
735
736void Loot::NotifyLootList(Map const* map) const
737{
739
740 lootList.Owner = GetOwnerGUID();
741 lootList.LootObj = GetGUID();
742
744 lootList.Master = GetLootMasterGUID();
745
748
749 lootList.Write();
750
751 for (ObjectGuid allowedLooterGuid : _allowedLooters)
752 if (Player* allowedLooter = ObjectAccessor::GetPlayer(map, allowedLooterGuid))
753 allowedLooter->SendDirectMessage(lootList.GetRawPacket());
754}
755
756void Loot::NotifyItemRemoved(uint8 lootListId, Map const* map)
757{
758 // notify all players that are looting this that the item was removed
759 // convert the index to the slot the player sees
760 for (auto itr = PlayersLooting.begin(); itr != PlayersLooting.end();)
761 {
762 LootItem const& item = items[lootListId];
763 if (item.GetAllowedLooters().find(*itr) == item.GetAllowedLooters().end())
764 {
765 ++itr;
766 continue;
767 }
768
769 if (Player* player = ObjectAccessor::GetPlayer(map, *itr))
770 {
771 player->SendNotifyLootItemRemoved(GetGUID(), GetOwnerGUID(), lootListId);
772 ++itr;
773 }
774 else
775 itr = PlayersLooting.erase(itr);
776 }
777}
778
780{
781 // notify all players that are looting this that the money was removed
782 for (auto itr = PlayersLooting.begin(); itr != PlayersLooting.end();)
783 {
784 if (Player* player = ObjectAccessor::GetPlayer(map, *itr))
785 {
786 player->SendNotifyLootMoneyRemoved(GetGUID());
787 ++itr;
788 }
789 else
790 itr = PlayersLooting.erase(itr);
791 }
792}
793
794void Loot::OnLootOpened(Map* map, Player* looter)
795{
796 AddLooter(looter->GetGUID());
797 if (!_wasOpened)
798 {
799 _wasOpened = true;
800
802 {
803 uint16 maxEnchantingSkill = 0;
804 for (ObjectGuid allowedLooterGuid : _allowedLooters)
805 if (Player* allowedLooter = ObjectAccessor::GetPlayer(map, allowedLooterGuid))
806 maxEnchantingSkill = std::max(maxEnchantingSkill, allowedLooter->GetSkillValue(SKILL_ENCHANTING));
807
808 for (uint32 lootListId = 0; lootListId < items.size(); ++lootListId)
809 {
810 LootItem& item = items[lootListId];
811 if (!item.is_blocked)
812 continue;
813
814 auto&& [itr, inserted] = _rolls.try_emplace(lootListId);
815 if (!itr->second.TryToStart(map, *this, lootListId, maxEnchantingSkill))
816 _rolls.erase(itr);
817 }
818
819 if (!_rolls.empty())
820 _changed = true;
821 }
822 else if (_lootMethod == MASTER_LOOT)
823 {
824 if (looter->GetGUID() == _lootMaster)
825 {
826 WorldPackets::Loot::MasterLootCandidateList masterLootCandidateList;
827 masterLootCandidateList.LootObj = GetGUID();
828 masterLootCandidateList.Players = _allowedLooters;
829 looter->SendDirectMessage(masterLootCandidateList.Write());
830 }
831 }
832 }
833
834 // Flag tracking quests as completed after all items were scanned for this player (some might depend on this quest not being completed)
835 //if (!_mailUnlootedItems)
836 if (std::vector<NotNormalLootItem>* ffaItems = Trinity::Containers::MapGetValuePtr(PlayerFFAItems, looter->GetGUID()))
837 AutoStoreTrackingQuests(looter, *ffaItems);
838}
839
840bool Loot::HasAllowedLooter(ObjectGuid const& looter) const
841{
842 return _allowedLooters.find(looter) != _allowedLooters.end();
843}
844
845void Loot::generateMoneyLoot(uint32 minAmount, uint32 maxAmount)
846{
847 if (maxAmount > 0)
848 {
849 if (maxAmount <= minAmount)
850 gold = uint32(maxAmount * sWorld->getRate(RATE_DROP_MONEY));
851 else if ((maxAmount - minAmount) < 32700)
852 gold = uint32(urand(minAmount, maxAmount) * sWorld->getRate(RATE_DROP_MONEY));
853 else
854 gold = uint32(urand(minAmount >> 8, maxAmount >> 8) * sWorld->getRate(RATE_DROP_MONEY)) << 8;
855 }
856}
857
858// Calls processor of corresponding LootTemplate (which handles everything including references)
859bool Loot::FillLoot(uint32 lootId, LootStore const& store, Player* lootOwner, bool personal, bool noEmptyError, uint16 lootMode /*= LOOT_MODE_DEFAULT*/, ItemContext context /*= ItemContext::NONE*/)
860{
861 // Must be provided
862 if (!lootOwner)
863 return false;
864
865 LootTemplate const* tab = store.GetLootFor(lootId);
866
867 if (!tab)
868 {
869 if (!noEmptyError)
870 TC_LOG_ERROR("sql.sql", "Table '{}' loot id #{} used but it doesn't have records.", store.GetName(), lootId);
871 return false;
872 }
873
874 _itemContext = context;
875
876 items.reserve(MAX_NR_LOOT_ITEMS);
877
878 tab->Process(*this, store.IsRatesAllowed(), lootMode, 0); // Processing is done there, callback via Loot::AddItem()
879
880 // Setting access rights for group loot case
881 Group const* group = lootOwner->GetGroup();
882 if (!personal && group)
883 {
884 if (loot_type == LOOT_CORPSE)
885 roundRobinPlayer = lootOwner->GetGUID();
886
887 for (GroupReference const& itr : group->GetMembers())
888 {
889 Player* member = itr.GetSource(); // should actually be looted object instead of lootOwner but looter has to be really close so doesnt really matter
890 if (member->IsAtGroupRewardDistance(lootOwner))
891 FillNotNormalLootFor(member);
892 }
893
894 for (LootItem& item : items)
895 {
896 if (!item.follow_loot_rules || item.freeforall || item.type != LootItemType::Item)
897 continue;
898
899 if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(item.itemid))
900 {
901 if (proto->GetQuality() < uint32(group->GetLootThreshold()))
902 item.is_underthreshold = true;
903 else
904 {
905 switch (_lootMethod)
906 {
907 case MASTER_LOOT:
908 case GROUP_LOOT:
910 {
911 item.is_blocked = true;
912 break;
913 }
914 default:
915 break;
916 }
917 }
918 }
919 }
920 }
921 // ... for personal loot
922 else
923 FillNotNormalLootFor(lootOwner);
924
925 return true;
926}
927
928// Inserts the item into the loot (called by LootTemplate processors)
930{
931 switch (item.type)
932 {
934 {
935 ItemTemplate const* proto = sObjectMgr->GetItemTemplate(item.itemid);
936 if (!proto)
937 return;
938
939 uint32 count = urand(item.mincount, item.maxcount);
940 uint32 stacks = count / proto->GetMaxStackSize() + ((count % proto->GetMaxStackSize()) ? 1 : 0);
941
942 for (uint32 i = 0; i < stacks && items.size() < MAX_NR_LOOT_ITEMS; ++i)
943 {
944 LootItem generatedLoot(item);
945 generatedLoot.context = _itemContext;
946 generatedLoot.count = std::min(count, proto->GetMaxStackSize());
947 generatedLoot.LootListId = items.size();
949
950 items.push_back(generatedLoot);
951 count -= proto->GetMaxStackSize();
952 }
953 break;
954 }
956 {
957 LootItem generatedLoot(item);
958 generatedLoot.count = urand(item.mincount, item.maxcount);
959 generatedLoot.LootListId = items.size();
960 items.push_back(generatedLoot);
961 break;
962 }
964 {
965 LootItem generatedLoot(item);
966 generatedLoot.count = 1;
967 generatedLoot.LootListId = items.size();
968 items.push_back(generatedLoot);
969 break;
970 }
971 default:
972 break;
973 }
974}
975
976bool Loot::AutoStore(Player* player, uint8 bag, uint8 slot, bool broadcast, bool createdByPlayer)
977{
978 bool allLooted = true;
979 for (uint32 i = 0; i < items.size(); ++i)
980 {
981 NotNormalLootItem* ffaitem = nullptr;
982
983 LootItem* lootItem = LootItemInSlot(i, player, &ffaitem);
984 if (!lootItem || lootItem->is_looted)
985 continue;
986
987 if (!lootItem->HasAllowedLooter(player->GetGUID()))
988 continue;
989
990 if (lootItem->is_blocked)
991 continue;
992
993 // dont allow protected item to be looted by someone else
994 if (!lootItem->rollWinnerGUID.IsEmpty() && lootItem->rollWinnerGUID != GetGUID())
995 continue;
996
997 switch (lootItem->type)
998 {
1000 {
1001 ItemPosCountVec dest;
1002 InventoryResult msg = player->CanStoreNewItem(bag, slot, dest, lootItem->itemid, lootItem->count);
1003 if (msg != EQUIP_ERR_OK && slot != NULL_SLOT)
1004 msg = player->CanStoreNewItem(bag, NULL_SLOT, dest, lootItem->itemid, lootItem->count);
1005 if (msg != EQUIP_ERR_OK && bag != NULL_BAG)
1006 msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, lootItem->itemid, lootItem->count);
1007 if (msg != EQUIP_ERR_OK)
1008 {
1009 player->SendEquipError(msg, nullptr, nullptr, lootItem->itemid);
1010 allLooted = false;
1011 continue;
1012 }
1013
1014 if (Item* pItem = player->StoreNewItem(dest, lootItem->itemid, true, lootItem->randomBonusListId, GuidSet(), lootItem->context, &lootItem->BonusListIDs))
1015 {
1016 player->SendNewItem(pItem, lootItem->count, false, createdByPlayer, broadcast, GetDungeonEncounterId());
1017 player->ApplyItemLootedSpell(pItem, true);
1018 }
1019 else
1020 player->ApplyItemLootedSpell(sObjectMgr->GetItemTemplate(lootItem->itemid));
1021
1022 break;
1023 }
1025 player->ModifyCurrency(lootItem->itemid, lootItem->count, CurrencyGainSource::Loot);
1026 break;
1028
1029 if (Quest const* quest = sObjectMgr->GetQuestTemplate(lootItem->itemid))
1030 player->RewardQuest(quest, LootItemType::Item, 0, player, false);
1031 break;
1032 }
1033
1034 if (ffaitem)
1035 ffaitem->is_looted = true;
1036
1037 if (!lootItem->freeforall)
1038 lootItem->is_looted = true;
1039
1040 --unlootedCount;
1041 }
1042
1043 return allLooted;
1044}
1045
1047{
1048 for (NotNormalLootItem& ffaItem : ffaItems)
1049 {
1050 if (items[ffaItem.LootListId].type != LootItemType::TrackingQuest)
1051 continue;
1052
1053 --unlootedCount;
1054 ffaItem.is_looted = true;
1055 if (Quest const* quest = sObjectMgr->GetQuestTemplate(items[ffaItem.LootListId].itemid))
1056 player->RewardQuest(quest, LootItemType::Item, 0, player, false);
1057 }
1058}
1059
1061{
1062 gold = 0;
1063 _changed = true;
1064}
1065
1066LootItem const* Loot::GetItemInSlot(uint32 lootListId) const
1067{
1068 if (lootListId < items.size())
1069 return &items[lootListId];
1070
1071 return nullptr;
1072}
1073
1074LootItem* Loot::LootItemInSlot(uint32 lootListId, Player const* player, NotNormalLootItem** ffaItem)
1075{
1076 if (lootListId >= items.size())
1077 return nullptr;
1078
1079 LootItem* item = &items[lootListId];
1080 bool is_looted = item->is_looted;
1081
1082 if (item->freeforall)
1083 {
1084 auto itr = PlayerFFAItems.find(player->GetGUID());
1085 if (itr != PlayerFFAItems.end())
1086 {
1087 for (NotNormalLootItem& notNormalLootItem : *itr->second)
1088 {
1089 if (notNormalLootItem.LootListId == lootListId)
1090 {
1091 is_looted = notNormalLootItem.is_looted;
1092 if (ffaItem)
1093 *ffaItem = &notNormalLootItem;
1094
1095 break;
1096 }
1097 }
1098 }
1099 }
1100
1101 if (is_looted)
1102 return nullptr;
1103
1104 _changed = true;
1105 return item;
1106}
1107
1108// return true if there is any item that is lootable for any player (not quest item, FFA or conditional)
1110{
1111 // Gold is always lootable
1112 if (gold)
1113 return true;
1114
1115 for (LootItem const& item : items)
1116 if (!item.is_looted && item.follow_loot_rules && !item.freeforall && item.conditions.IsEmpty())
1117 return true;
1118 return false;
1119}
1120
1121// return true if there is any FFA, quest or conditional item for the player.
1122bool Loot::hasItemFor(Player const* player) const
1123{
1124 // quest items
1125 for (LootItem const& lootItem : items)
1126 if (!lootItem.is_looted && !lootItem.follow_loot_rules && lootItem.GetAllowedLooters().contains(player->GetGUID()))
1127 return true;
1128
1130 if (std::ranges::any_of(*ffaItems, &NotNormalLootItem::is_looted))
1131 return true;
1132
1133 return false;
1134}
1135
1136// return true if there is any item over the group threshold (i.e. not underthreshold).
1138{
1139 for (uint8 i = 0; i < items.size(); ++i)
1140 {
1141 if (!items[i].is_looted && !items[i].is_underthreshold && !items[i].freeforall)
1142 return true;
1143 }
1144
1145 return false;
1146}
1147
1149{
1150 packet.Coins = gold;
1151
1152 for (LootItem const& item : items)
1153 {
1154 Optional<LootSlotType> uiType = item.GetUiTypeForPlayer(viewer, *this);
1155 if (!uiType)
1156 continue;
1157
1158 switch (item.type)
1159 {
1160 case LootItemType::Item:
1161 {
1162 WorldPackets::Loot::LootItemData& lootItem = packet.Items.emplace_back();
1163 lootItem.LootListID = item.LootListId;
1164 lootItem.Type = item.type;
1165 lootItem.UIType = *uiType;
1166 lootItem.Quantity = item.count;
1167 lootItem.Loot.Initialize(item);
1168 break;
1169 }
1171 {
1172 WorldPackets::Loot::LootCurrency& lootCurrency = packet.Currencies.emplace_back();
1173 lootCurrency.CurrencyID = item.itemid;
1174 lootCurrency.Quantity = item.count;
1175 lootCurrency.LootListID = item.LootListId;
1176 lootCurrency.UIType = *uiType;
1177
1178 // fake visible quantity for SPELL_AURA_MOD_CURRENCY_CATEGORY_GAIN_PCT - handled in Player::ModifyCurrency
1179 lootCurrency.Quantity = float(lootCurrency.Quantity) * viewer->GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_CURRENCY_CATEGORY_GAIN_PCT, sCurrencyTypesStore.AssertEntry(item.itemid)->CategoryID);
1180 break;
1181 }
1182 default:
1183 break;
1184 }
1185
1186 }
1187}
1188
1190{
1191 for (auto itr = _rolls.begin(); itr != _rolls.end(); )
1192 {
1193 if (itr->second.UpdateRoll())
1194 itr = _rolls.erase(itr);
1195 else
1196 ++itr;
1197 }
1198}
1199
1201{
1202 ObjectGuid plguid = player->GetGUID();
1203 _allowedLooters.insert(plguid);
1204
1205 std::unique_ptr<NotNormalLootItemList> ffaItems = std::make_unique<NotNormalLootItemList>();
1206
1207 for (LootItem& item : items)
1208 {
1209 if (!item.AllowedForPlayer(player, this))
1210 continue;
1211
1212 item.AddAllowedLooter(player);
1213
1214 if (item.freeforall)
1215 {
1216 ffaItems->emplace_back(item.LootListId);
1217 ++unlootedCount;
1218 }
1219 else if (!item.is_counted)
1220 {
1221 item.is_counted = true;
1222 ++unlootedCount;
1223 }
1224 }
1225
1226 if (!ffaItems->empty())
1227 {
1228 // TODO: flag immediately for loot that is supposed to be mailed if unlooted, otherwise flag when sending SMSG_LOOT_RESPONSE
1229 //if (_mailUnlootedItems)
1230 // AutoStoreTrackingQuests(player, *ffaItems);
1231
1232 PlayerFFAItems[player->GetGUID()] = std::move(ffaItems);
1233 }
1234}
1235
1236//
1237// --------- AELootResult ---------
1238//
1239
1240void AELootResult::Add(Item* item, uint8 count, LootType lootType, uint32 dungeonEncounterId)
1241{
1242 auto itr = _byItem.find(item);
1243 if (itr != _byItem.end())
1244 _byOrder[itr->second].count += count;
1245 else
1246 {
1247 _byItem[item] = _byOrder.size();
1248 ResultValue value;
1249 value.item = item;
1250 value.count = count;
1251 value.lootType = lootType;
1252 value.dungeonEncounterId = dungeonEncounterId;
1253 _byOrder.push_back(value);
1254 }
1255}
1256
1257AELootResult::OrderedStorage::const_iterator AELootResult::begin() const
1258{
1259 return _byOrder.begin();
1260}
1261
1262AELootResult::OrderedStorage::const_iterator AELootResult::end() const
1263{
1264 return _byOrder.end();
1265}
DB2Storage< CurrencyTypesEntry > sCurrencyTypesStore("CurrencyTypes.db2", &CurrencyTypesLoadInfo::Instance)
ItemContext
Definition DBCEnums.h:1315
uint8_t uint8
Definition Define.h:156
int32_t int32
Definition Define.h:150
uint16_t uint16
Definition Define.h:155
uint32_t uint32
Definition Define.h:154
#define ASSERT_NOTNULL(pointer)
Definition Errors.h:82
InventoryResult
Definition ItemDefines.h:25
@ EQUIP_ERR_OK
Definition ItemDefines.h:26
ItemRandomBonusListId GenerateItemRandomBonusListId(uint32 item_id)
@ ITEM_FLAG2_FACTION_HORDE
@ ITEM_FLAG2_FACTION_ALLIANCE
@ ITEM_FLAG2_CAN_ONLY_ROLL_GREED
@ ITEM_SPELLTRIGGER_ON_LEARN
@ ITEM_FLAG_HIDE_UNUSABLE_RECIPE
@ ITEM_FLAG_MULTI_DROP
@ ITEM_FLAGS_CU_FOLLOW_LOOT_RULES
@ ITEM_FLAGS_CU_IGNORE_QUEST_STATUS
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:190
LootStore LootTemplates_Disenchant("disenchant_loot_template", "item disenchant id", true)
LootType
Definition Loot.h:99
@ LOOT_CORPSE
Definition Loot.h:102
@ LOOT_DISENCHANTING
Definition Loot.h:105
@ PERSONAL_LOOT
Definition Loot.h:95
@ NEED_BEFORE_GREED
Definition Loot.h:94
@ GROUP_LOOT
Definition Loot.h:93
@ MASTER_LOOT
Definition Loot.h:92
@ ROUND_ROBIN
Definition Loot.h:91
@ FREE_FOR_ALL
Definition Loot.h:90
RollMask
Definition Loot.h:75
@ ROLL_FLAG_TYPE_DISENCHANT
Definition Loot.h:79
@ ROLL_ALL_TYPE_MASK
Definition Loot.h:83
@ ROLL_FLAG_TYPE_NEED
Definition Loot.h:77
@ LOOT_SLOT_TYPE_MASTER
Definition Loot.h:160
@ LOOT_SLOT_TYPE_ROLL_ONGOING
Definition Loot.h:159
@ LOOT_SLOT_TYPE_ALLOW_LOOT
Definition Loot.h:158
@ LOOT_SLOT_TYPE_OWNER
Definition Loot.h:162
@ LOOT_SLOT_TYPE_LOCKED
Definition Loot.h:161
std::vector< NotNormalLootItem > NotNormalLootItemList
Definition Loot.h:234
#define MAX_NR_LOOT_ITEMS
Definition Loot.h:86
RollVote
Definition Loot.h:65
constexpr Minutes LOOT_ROLL_TIMEOUT
Definition Loot.h:34
std::set< ObjectGuid > GuidSet
Definition ObjectGuid.h:432
HighGuid
Definition ObjectGuid.h:109
#define sObjectMgr
Definition ObjectMgr.h:1885
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition Optional.h:25
std::vector< ItemPosCount > ItemPosCountVec
Definition Player.h:841
@ QUEST_STATUS_NONE
Definition QuestDef.h:147
uint32 urand(uint32 min, uint32 max)
Definition Random.cpp:42
@ ALLIANCE
@ HORDE
@ LOOT_MODE_DEFAULT
@ SKILL_ENCHANTING
@ SPELL_AURA_MOD_CURRENCY_CATEGORY_GAIN_PCT
@ NULL_BAG
Definition Unit.h:63
@ NULL_SLOT
Definition Unit.h:64
constexpr std::underlying_type< E >::type AsUnderlyingType(E enumValue)
Definition Util.h:565
OrderedStorage _byOrder
Definition Loot.h:381
std::unordered_map< Item *, OrderedStorage::size_type > _byItem
Definition Loot.h:382
OrderedStorage::const_iterator end() const
Definition Loot.cpp:1262
OrderedStorage::const_iterator begin() const
Definition Loot.cpp:1257
void Add(Item *item, uint8 count, LootType lootType, uint32 dungeonEncounterId)
Definition Loot.cpp:1240
ObjectGuid const & GetGUID() const
Definition BaseEntity.h:163
Definition Group.h:205
ItemQualities GetLootThreshold() const
Definition Group.cpp:1680
GroupRefManager & GetMembers()
Definition Group.h:332
Definition Item.h:179
static ItemDisenchantLootEntry const * GetBaseDisenchantLoot(ItemTemplate const *itemTemplate, uint32 quality, uint32 itemLevel)
Definition Item.cpp:2411
uint32 GetItemLevel(Player const *owner) const
Definition Item.cpp:2265
bool PlayerVote(Player *player, RollVote vote)
Definition Loot.cpp:521
bool UpdateRoll()
Definition Loot.cpp:565
TimePoint m_endTime
Definition Loot.h:282
Optional< uint16 > GetItemDisenchantSkillRequired() const
Definition Loot.cpp:651
void Finish(RollVoteMap::const_iterator winnerItr)
Definition Loot.cpp:674
bool AllPlayerVoted(RollVoteMap::const_iterator &winnerItr)
Check if all player have voted and return true in that case. Also return current winner.
Definition Loot.cpp:587
bool TryToStart(Map *map, Loot &loot, uint32 lootListId, uint16 enchantingSkill)
Definition Loot.cpp:465
bool m_isStarted
Definition Loot.h:278
LootItem * m_lootItem
Definition Loot.h:279
Optional< uint32 > GetItemDisenchantLootId() const
Definition Loot.cpp:626
void SendStartRoll()
Definition Loot.cpp:281
Loot * m_loot
Definition Loot.h:280
bool IsLootItem(ObjectGuid const &lootObject, uint32 lootListId) const
Definition Loot.cpp:577
RollVoteMap m_rollVoteMap
Definition Loot.h:277
~LootRoll()
Definition Loot.cpp:445
RollMask m_voteMask
Definition Loot.h:281
void SendLootRollWon(ObjectGuid const &targetGuid, int32 rollNumber, RollVote rollType)
Definition Loot.cpp:384
Map * m_map
Definition Loot.h:276
void SendAllPassed()
Definition Loot.cpp:321
void SendRoll(ObjectGuid const &targetGuid, int32 rollNumber, RollVote rollType, Optional< ObjectGuid > const &rollWinner)
Definition Loot.cpp:344
void FillPacket(WorldPackets::Loot::LootItemData &lootItem) const
Definition Loot.cpp:437
LootTemplate const * GetLootFor(uint32 loot_id) const
Definition LootMgr.cpp:207
bool IsRatesAllowed() const
Definition LootMgr.h:101
char const * GetName() const
Definition LootMgr.h:99
void Process(Loot &loot, bool rate, uint16 lootMode, uint8 groupId, Player const *personalLooter=nullptr) const
Definition LootMgr.cpp:641
Definition Map.h:225
uint32 GetId() const
Definition Map.cpp:3257
bool IsEmpty() const
Definition ObjectGuid.h:362
void SendEquipError(InventoryResult msg, Item const *item1=nullptr, Item const *item2=nullptr, uint32 itemId=0) const
Definition Player.cpp:13130
Item * StoreNewItem(ItemPosCountVec const &pos, uint32 itemId, bool update, ItemRandomBonusListId randomBonusListId=0, GuidSet const &allowedLooters=GuidSet(), ItemContext context=ItemContext::NONE, std::vector< int32 > const *bonusListIDs=nullptr, bool addToCollection=true)
Definition Player.cpp:11370
void ApplyItemLootedSpell(Item *item, bool apply)
Definition Player.cpp:8912
InventoryResult CanRollNeedForItem(ItemTemplate const *item, Map const *map, bool restrictOnlyLfg) const
Definition Player.cpp:11326
void SendDirectMessage(WorldPacket const *data) const
Definition Player.cpp:6283
bool HasQuestForItem(uint32 itemId) const
Definition Player.cpp:16963
bool HasQuestForCurrency(uint32 currencyId) const
Definition Player.cpp:17037
bool IsQuestCompletedBitSet(uint32 questId) const
Definition Player.cpp:16517
bool IsAtGroupRewardDistance(WorldObject const *pRewardSource) const
Definition Player.cpp:26434
bool HasSkill(uint32 skill) const
Definition Player.cpp:5989
void UpdateCriteria(CriteriaType type, uint64 miscValue1=0, uint64 miscValue2=0, uint64 miscValue3=0, WorldObject *ref=nullptr)
Definition Player.cpp:27588
void AddLootRoll(LootRoll *roll)
Definition Player.cpp:9098
QuestStatus GetQuestStatus(uint32 quest_id) const
Definition Player.cpp:15962
bool GetPassOnGroupLoot() const
Definition Player.h:2821
bool HasSpell(uint32 spell) const override
Definition Player.cpp:3735
Group * GetGroup(Optional< uint8 > partyIndex)
Definition Player.h:2796
void RemoveLootRoll(LootRoll *roll)
Definition Player.cpp:9103
void ModifyCurrency(uint32 id, int32 amount, CurrencyGainSource gainSource=CurrencyGainSource::Cheat, CurrencyDestroyReason destroyReason=CurrencyDestroyReason::Cheat)
Modify currency amount.
Definition Player.cpp:7146
InventoryResult CanStoreNewItem(uint8 bag, uint8 slot, ItemPosCountVec &dest, uint32 item, uint32 count, uint32 *no_space_count=nullptr) const
Definition Player.cpp:10050
void RewardQuest(Quest const *quest, LootItemType rewardType, uint32 rewardId, Object *questGiver, bool announce=true)
Definition Player.cpp:15061
Team GetTeam() const
Definition Player.h:2423
void SendNewItem(Item *item, uint32 quantity, bool received, bool created, bool broadcast=false, uint32 dungeonEncounterId=0)
Definition Player.cpp:13878
float GetTotalAuraMultiplierByMiscValue(AuraType auraType, int32 misc_value) const
Definition Unit.cpp:5139
WorldPacket const * Write() override
Optional< ObjectGuid > Master
WorldPacket const * Write() override
Optional< ObjectGuid > RoundRobinWinner
std::vector< LootItemData > Items
Definition LootPackets.h:75
std::vector< LootCurrency > Currencies
Definition LootPackets.h:76
WorldPacket const * Write() override
int32 Roll
Roll value can be negative, it means that it is an "offspec" roll but only during roll selection broa...
bool Autopassed
Triggers message |HlootHistory:d|h[Loot]|h: You automatically passed on: s because you cannot loot th...
WorldPacket const * Write() override
WorldPacket const * Write() override
WorldPacket const * Write() override
Duration< Milliseconds, uint32 > RollTime
WorldPacket const * GetRawPacket() const
Definition Packet.h:38
#define sWorld
Definition World.h:916
@ RATE_DROP_MONEY
Definition World.h:476
TimePoint Now()
Current chrono steady_clock time point.
Definition GameTime.cpp:67
std::vector< int32 > GetBonusListsForItem(uint32 itemId, ItemBonusGenerationParams const &params)
TC_GAME_API Player * GetPlayer(Map const *, ObjectGuid const &guid)
TC_GAME_API Player * FindConnectedPlayer(ObjectGuid const &)
auto MapGetValuePtr(M &map, typename M::key_type const &key)
Definition MapUtils.h:37
int32 RequiredLevel
Definition Item.h:65
uint32 DisenchantLootId
Definition Item.h:75
bool CanDisenchant
Definition Item.h:93
uint32 Quality
Definition Item.h:62
void Initialize(ItemTemplate const *proto)
Definition Item.cpp:2886
bool Meets(WorldObject const *object) const
EnumFlag< CurrencyTypesFlags > GetFlags() const
uint32 GetMaxStackSize() const
uint32 GetStartQuest() const
std::vector< ItemEffectEntry const * > Effects
bool HasFlag(ItemFlags flag) const
bool IsUsableByLootSpecialization(Player const *player, bool alwaysAllowBoundToAccount) const
bool IsArmor() const
bool IsWeapon() const
uint32 GetRequiredSkill() const
bool HasAllowedLooter(ObjectGuid const &looter) const
Definition Loot.cpp:205
std::vector< int32 > BonusListIDs
Definition Loot.h:181
uint32 itemid
Definition Loot.h:178
bool is_blocked
Definition Loot.h:189
Optional< LootSlotType > GetUiTypeForPlayer(Player const *player, Loot const &loot) const
Definition Loot.cpp:210
void AddAllowedLooter(Player const *player)
Definition Loot.cpp:200
ObjectGuid rollWinnerGUID
Definition Loot.h:185
bool needs_quest
Definition Loot.h:193
bool follow_loot_rules
Definition Loot.h:194
bool is_underthreshold
Definition Loot.h:191
GuidSet allowedGUIDs
Definition Loot.h:184
uint32 count
Definition Loot.h:186
bool AllowedForPlayer(Player const *player, Loot const *loot) const
Definition Loot.cpp:76
LootItem()=default
uint32 LootListId
Definition Loot.h:179
static bool CurrencyAllowedForPlayer(Player const *player, uint32 currencyId, bool needs_quest, ConditionsReference const &conditions)
Definition Loot.cpp:164
bool is_looted
Definition Loot.h:188
static bool ItemAllowedForPlayer(Player const *player, Loot const *loot, uint32 itemid, bool needs_quest, bool follow_loot_rules, bool strictUsabilityCheck, ConditionsReference const &conditions)
Definition Loot.cpp:110
ItemRandomBonusListId randomBonusListId
Definition Loot.h:180
ItemContext context
Definition Loot.h:182
ConditionsReference conditions
Definition Loot.h:183
GuidSet const & GetAllowedLooters() const
Definition Loot.h:217
LootItemType type
Definition Loot.h:187
bool freeforall
Definition Loot.h:190
static bool TrackingQuestAllowedForPlayer(Player const *player, uint32 questId, ConditionsReference const &conditions)
Definition Loot.cpp:188
ConditionsReference conditions
Definition LootMgr.h:56
bool needs_quest
Definition LootMgr.h:52
uint32 itemid
Definition LootMgr.h:48
uint8 maxcount
Definition LootMgr.h:55
uint8 mincount
Definition LootMgr.h:54
Definition Loot.h:286
void AutoStoreTrackingQuests(Player *player, NotNormalLootItemList &ffaItems)
Definition Loot.cpp:1046
void OnLootOpened(Map *map, Player *looter)
Definition Loot.cpp:794
bool _wasOpened
Definition Loot.h:358
void AddLooter(ObjectGuid GUID)
Definition Loot.h:319
void FillNotNormalLootFor(Player *player)
Definition Loot.cpp:1200
void NotifyLootList(Map const *map) const
Definition Loot.cpp:736
ItemContext _itemContext
Definition Loot.h:353
bool AutoStore(Player *player, uint8 bag, uint8 slot, bool broadcast=false, bool createdByPlayer=false)
Definition Loot.cpp:976
GuidUnorderedSet _allowedLooters
Definition Loot.h:357
bool hasOverThresholdItem() const
Definition Loot.cpp:1137
void LootMoney()
Definition Loot.cpp:1060
NotNormalLootItemMap PlayerFFAItems
Definition Loot.h:348
void BuildLootResponse(WorldPackets::Loot::LootResponse &packet, Player const *viewer) const
Definition Loot.cpp:1148
bool _changed
Definition Loot.h:359
void NotifyMoneyRemoved(Map const *map)
Definition Loot.cpp:779
LootMethod _lootMethod
Definition Loot.h:354
void NotifyItemRemoved(uint8 lootListId, Map const *map)
Definition Loot.cpp:756
Loot(Map *map, ObjectGuid owner, LootType type, Group const *group)
Definition Loot.cpp:721
uint8 unlootedCount
Definition Loot.h:291
void AddItem(LootStoreItem const &item)
Definition Loot.cpp:929
void Update()
Definition Loot.cpp:1189
LootItem const * GetItemInSlot(uint32 lootListId) const
Definition Loot.cpp:1066
ObjectGuid roundRobinPlayer
Definition Loot.h:292
ObjectGuid const & GetOwnerGUID() const
Definition Loot.h:304
GuidSet PlayersLooting
Definition Loot.h:347
void generateMoneyLoot(uint32 minAmount, uint32 maxAmount)
Definition Loot.cpp:845
bool hasItemFor(Player const *player) const
Definition Loot.cpp:1122
uint32 gold
Definition Loot.h:290
bool hasItemForAll() const
Definition Loot.cpp:1109
LootItem * LootItemInSlot(uint32 lootListId, Player const *player, NotNormalLootItem **ffaItem=nullptr)
Definition Loot.cpp:1074
ObjectGuid const & GetGUID() const
Definition Loot.h:303
~Loot()
Definition Loot.cpp:728
std::vector< LootItem > items
Definition Loot.h:289
std::unordered_map< uint32, LootRoll > _rolls
Definition Loot.h:355
ObjectGuid _lootMaster
Definition Loot.h:356
NotNormalLootItemMap const & GetPlayerFFAItems() const
Definition Loot.h:287
bool FillLoot(uint32 lootId, LootStore const &store, Player *lootOwner, bool personal, bool noEmptyError=false, uint16 lootMode=LOOT_MODE_DEFAULT, ItemContext context=ItemContext::NONE)
Definition Loot.cpp:859
uint32 GetDungeonEncounterId() const
Definition Loot.h:309
ObjectGuid const & GetLootMasterGUID() const
Definition Loot.h:308
LootMethod GetLootMethod() const
Definition Loot.h:307
LootType loot_type
Definition Loot.h:293
bool HasAllowedLooter(ObjectGuid const &looter) const
Definition Loot.cpp:840
uint8 LootListId
Definition Loot.h:224
void Initialize(::Item const *item)
WorldPackets::Item::ItemInstance Loot
Definition LootPackets.h:50