Core/Loot: Implemented automatic flagging of tracking quests from loot

(cherry picked from commit d913e38cba)

# Conflicts:
#	sql/updates/world/cata_classic/2024_12_29_00_world.sql
This commit is contained in:
Shauren
2024-12-29 01:02:52 +01:00
committed by Ovahlord
parent 4084b85352
commit fd52be9fe5
9 changed files with 177 additions and 22 deletions

View File

@@ -0,0 +1,19 @@
UPDATE `trinity_string` SET `content_default`='├%.*s %dx |c%08x|Hitem:%d:0:0:0:0:0:0:0:0|h[%s]|h|r (#%05d)' WHERE `entry`=291;
UPDATE `trinity_string` SET `content_loc1`='├%.*s %dx |c%08x|Hitem:%d:0:0:0:0:0:0:0:0|h[%s]|h|r (#%05d)' WHERE `entry`=291 AND `content_loc1` IS NOT NULL;
UPDATE `trinity_string` SET `content_loc2`='├%.*s %dx |c%08x|Hitem:%d:0:0:0:0:0:0:0:0|h[%s]|h|r (#%05d)' WHERE `entry`=291 AND `content_loc2` IS NOT NULL;
UPDATE `trinity_string` SET `content_loc3`='├%.*s %dx |c%08x|Hitem:%d:0:0:0:0:0:0:0:0|h[%s]|h|r (#%05d)' WHERE `entry`=291 AND `content_loc3` IS NOT NULL;
UPDATE `trinity_string` SET `content_loc4`='├%.*s %dx |c%08x|Hitem:%d:0:0:0:0:0:0:0:0|h[%s]|h|r (#%05d)' WHERE `entry`=291 AND `content_loc4` IS NOT NULL;
UPDATE `trinity_string` SET `content_loc5`='├%.*s %dx |c%08x|Hitem:%d:0:0:0:0:0:0:0:0|h[%s]|h|r (#%05d)' WHERE `entry`=291 AND `content_loc5` IS NOT NULL;
UPDATE `trinity_string` SET `content_loc6`='├%.*s %dx |c%08x|Hitem:%d:0:0:0:0:0:0:0:0|h[%s]|h|r (#%05d)' WHERE `entry`=291 AND `content_loc6` IS NOT NULL;
UPDATE `trinity_string` SET `content_loc7`='├%.*s %dx |c%08x|Hitem:%d:0:0:0:0:0:0:0:0|h[%s]|h|r (#%05d)' WHERE `entry`=291 AND `content_loc7` IS NOT NULL;
UPDATE `trinity_string` SET `content_loc8`='├%.*s %dx |c%08x|Hitem:%d:0:0:0:0:0:0:0:0|h[%s]|h|r (#%05d)' WHERE `entry`=291 AND `content_loc8` IS NOT NULL;
UPDATE `trinity_string` SET `content_default`='├%.*s |cffffff00|Hquest:%u:0|h[%.*s]|h|r (#%05d)' WHERE `entry`=295;
UPDATE `trinity_string` SET `content_loc1`='├%.*s |cffffff00|Hquest:%u:0|h[%.*s]|h|r (#%05d)' WHERE `entry`=295 AND `content_loc1` IS NOT NULL;
UPDATE `trinity_string` SET `content_loc2`='├%.*s |cffffff00|Hquest:%u:0|h[%.*s]|h|r (#%05d)' WHERE `entry`=295 AND `content_loc2` IS NOT NULL;
UPDATE `trinity_string` SET `content_loc3`='├%.*s |cffffff00|Hquest:%u:0|h[%.*s]|h|r (#%05d)' WHERE `entry`=295 AND `content_loc3` IS NOT NULL;
UPDATE `trinity_string` SET `content_loc4`='├%.*s |cffffff00|Hquest:%u:0|h[%.*s]|h|r (#%05d)' WHERE `entry`=295 AND `content_loc4` IS NOT NULL;
UPDATE `trinity_string` SET `content_loc5`='├%.*s |cffffff00|Hquest:%u:0|h[%.*s]|h|r (#%05d)' WHERE `entry`=295 AND `content_loc5` IS NOT NULL;
UPDATE `trinity_string` SET `content_loc6`='├%.*s |cffffff00|Hquest:%u:0|h[%.*s]|h|r (#%05d)' WHERE `entry`=295 AND `content_loc6` IS NOT NULL;
UPDATE `trinity_string` SET `content_loc7`='├%.*s |cffffff00|Hquest:%u:0|h[%.*s]|h|r (#%05d)' WHERE `entry`=295 AND `content_loc7` IS NOT NULL;
UPDATE `trinity_string` SET `content_loc8`='├%.*s |cffffff00|Hquest:%u:0|h[%.*s]|h|r (#%05d)' WHERE `entry`=295 AND `content_loc8` IS NOT NULL;

View File

@@ -8220,7 +8220,7 @@ void Player::SendLoot(Loot& loot, bool aeLooting)
SendDirectMessage(packet.Write());
// add 'this' player as one of the players that are looting 'loot'
loot.OnLootOpened(GetMap(), GetGUID());
loot.OnLootOpened(GetMap(), this);
m_AELootView[loot.GetGUID()] = &loot;
if (loot.loot_type == LOOT_CORPSE && !loot.GetOwnerGUID().IsItem())
@@ -25467,6 +25467,9 @@ void Player::StoreLootItem(ObjectGuid lootWorldObjectGuid, uint8 lootSlot, Loot*
case LootItemType::Currency:
ModifyCurrency(item->itemid, item->count, CurrencyGainSource::Loot);
break;
case LootItemType::TrackingQuest:
// nothing to do, already handled
break;
}
if (ffaItem)

View File

@@ -57,6 +57,10 @@ LootItem::LootItem(LootStoreItem const& li) : itemid(li.itemid), conditions(li.c
type = LootItemType::Currency;
freeforall = true;
break;
case LootStoreItem::Type::TrackingQuest:
type = LootItemType::TrackingQuest;
freeforall = true;
break;
default:
break;
}
@@ -77,6 +81,8 @@ bool LootItem::AllowedForPlayer(Player const* player, Loot const* loot) const
return ItemAllowedForPlayer(player, loot, itemid, needs_quest, follow_loot_rules, false, conditions);
case LootItemType::Currency:
return CurrencyAllowedForPlayer(player, itemid, needs_quest, conditions);
case LootItemType::TrackingQuest:
return TrackingQuestAllowedForPlayer(player, itemid, conditions);
default:
break;
}
@@ -93,6 +99,8 @@ bool LootItem::AllowedForPlayer(Player const* player, LootStoreItem const& lootS
strictUsabilityCheck, lootStoreItem.conditions);
case LootStoreItem::Type::Currency:
return CurrencyAllowedForPlayer(player, lootStoreItem.itemid, lootStoreItem.needs_quest, lootStoreItem.conditions);
case LootStoreItem::Type::TrackingQuest:
return TrackingQuestAllowedForPlayer(player, lootStoreItem.itemid, lootStoreItem.conditions);
default:
break;
}
@@ -174,6 +182,18 @@ bool LootItem::CurrencyAllowedForPlayer(Player const* player, uint32 currencyId,
return true;
}
bool LootItem::TrackingQuestAllowedForPlayer(Player const* player, uint32 questId, ConditionsReference const& conditions)
{
// DB conditions check
if (!conditions.Meets(player))
return false;
if (player->IsQuestCompletedBitSet(questId))
return false;
return true;
}
void LootItem::AddAllowedLooter(Player const* player)
{
allowedGUIDs.insert(player->GetGUID());
@@ -740,9 +760,9 @@ void Loot::NotifyMoneyRemoved(Map const* map)
}
}
void Loot::OnLootOpened(Map* map, ObjectGuid looter)
void Loot::OnLootOpened(Map* map, Player* looter)
{
AddLooter(looter);
AddLooter(looter->GetGUID());
if (!_wasOpened)
{
_wasOpened = true;
@@ -770,18 +790,20 @@ void Loot::OnLootOpened(Map* map, ObjectGuid looter)
}
else if (_lootMethod == MASTER_LOOT)
{
if (looter == _lootMaster)
if (looter->GetGUID() == _lootMaster)
{
if (Player* lootMaster = ObjectAccessor::GetPlayer(map, looter))
{
WorldPackets::Loot::MasterLootCandidateList masterLootCandidateList;
masterLootCandidateList.LootObj = GetGUID();
masterLootCandidateList.Players = _allowedLooters;
lootMaster->SendDirectMessage(masterLootCandidateList.Write());
}
WorldPackets::Loot::MasterLootCandidateList masterLootCandidateList;
masterLootCandidateList.LootObj = GetGUID();
masterLootCandidateList.Players = _allowedLooters;
looter->SendDirectMessage(masterLootCandidateList.Write());
}
}
}
// Flag tracking quests as completed after all items were scanned for this player (some might depend on this quest not being completed)
//if (!_mailUnlootedItems)
if (std::vector<NotNormalLootItem>* ffaItems = Trinity::Containers::MapGetValuePtr(PlayerFFAItems, looter->GetGUID()))
AutoStoreTrackingQuests(looter, *ffaItems);
}
bool Loot::HasAllowedLooter(ObjectGuid const& looter) const
@@ -832,7 +854,7 @@ bool Loot::FillLoot(uint32 lootId, LootStore const& store, Player* lootOwner, bo
roundRobinPlayer = lootOwner->GetGUID();
for (GroupReference const* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
if (Player const* player = itr->GetSource()) // should actually be looted object instead of lootOwner but looter has to be really close so doesnt really matter
if (Player* player = itr->GetSource()) // should actually be looted object instead of lootOwner but looter has to be really close so doesnt really matter
if (player->IsAtGroupRewardDistance(lootOwner))
FillNotNormalLootFor(player);
@@ -905,6 +927,14 @@ void Loot::AddItem(LootStoreItem const& item)
items.push_back(generatedLoot);
break;
}
case LootStoreItem::Type::TrackingQuest:
{
LootItem generatedLoot(item);
generatedLoot.count = 1;
generatedLoot.LootListId = items.size();
items.push_back(generatedLoot);
break;
}
default:
break;
}
@@ -959,6 +989,11 @@ bool Loot::AutoStore(Player* player, uint8 bag, uint8 slot, bool broadcast, bool
case LootItemType::Currency:
player->ModifyCurrency(lootItem->itemid, lootItem->count, CurrencyGainSource::Loot);
break;
case LootItemType::TrackingQuest:
if (Quest const* quest = sObjectMgr->GetQuestTemplate(lootItem->itemid))
player->RewardQuest(quest, LootItemType::Item, 0, player, false);
break;
}
if (ffaitem)
@@ -973,6 +1008,20 @@ bool Loot::AutoStore(Player* player, uint8 bag, uint8 slot, bool broadcast, bool
return allLooted;
}
void Loot::AutoStoreTrackingQuests(Player* player, NotNormalLootItemList& ffaItems)
{
for (NotNormalLootItem& ffaItem : ffaItems)
{
if (items[ffaItem.LootListId].type != LootItemType::TrackingQuest)
continue;
--unlootedCount;
ffaItem.is_looted = true;
if (Quest const* quest = sObjectMgr->GetQuestTemplate(items[ffaItem.LootListId].itemid))
player->RewardQuest(quest, LootItemType::Item, 0, player, false);
}
}
void Loot::LootMoney()
{
gold = 0;
@@ -1113,7 +1162,7 @@ void Loot::Update()
}
}
void Loot::FillNotNormalLootFor(Player const* player)
void Loot::FillNotNormalLootFor(Player* player)
{
ObjectGuid plguid = player->GetGUID();
_allowedLooters.insert(plguid);
@@ -1140,7 +1189,13 @@ void Loot::FillNotNormalLootFor(Player const* player)
}
if (!ffaItems->empty())
{
// TODO: flag immediately for loot that is supposed to be mailed if unlooted, otherwise flag when sending SMSG_LOOT_RESPONSE
//if (_mailUnlootedItems)
// AutoStoreTrackingQuests(player, *ffaItems);
PlayerFFAItems[player->GetGUID()] = std::move(ffaItems);
}
}
//

View File

@@ -212,6 +212,7 @@ struct TC_GAME_API LootItem
static bool ItemAllowedForPlayer(Player const* player, Loot const* loot, uint32 itemid, bool needs_quest, bool follow_loot_rules, bool strictUsabilityCheck,
ConditionsReference const& conditions);
static bool CurrencyAllowedForPlayer(Player const* player, uint32 currencyId, bool needs_quest, ConditionsReference const& conditions);
static bool TrackingQuestAllowedForPlayer(Player const* player, uint32 questId, ConditionsReference const& conditions);
void AddAllowedLooter(Player const* player);
GuidSet const& GetAllowedLooters() const { return allowedGUIDs; }
bool HasAllowedLooter(ObjectGuid const& looter) const;
@@ -313,7 +314,7 @@ struct TC_GAME_API Loot
void NotifyLootList(Map const* map) const;
void NotifyItemRemoved(uint8 lootListId, Map const* map);
void NotifyMoneyRemoved(Map const* map);
void OnLootOpened(Map* map, ObjectGuid looter);
void OnLootOpened(Map* map, Player* looter);
void AddLooter(ObjectGuid GUID) { PlayersLooting.insert(GUID); }
void RemoveLooter(ObjectGuid GUID) { PlayersLooting.erase(GUID); }
@@ -321,12 +322,13 @@ struct TC_GAME_API Loot
void generateMoneyLoot(uint32 minAmount, uint32 maxAmount);
bool FillLoot(uint32 lootId, LootStore const& store, Player* lootOwner, bool personal, bool noEmptyError = false, uint16 lootMode = LOOT_MODE_DEFAULT, ItemContext context = ItemContext::NONE);
void FillNotNormalLootFor(Player const* player); // count unlooted items
void FillNotNormalLootFor(Player* player); // count unlooted items
// Inserts the item into the loot (called by LootTemplate processors)
void AddItem(LootStoreItem const& item);
bool AutoStore(Player* player, uint8 bag, uint8 slot, bool broadcast = false, bool createdByPlayer = false);
void AutoStoreTrackingQuests(Player* player, NotNormalLootItemList& ffaItems);
void LootMoney();
LootItem const* GetItemInSlot(uint32 lootListId) const;

View File

@@ -22,8 +22,9 @@
enum class LootItemType : uint8
{
Item = 0,
Currency = 1
Item = 0,
Currency = 1,
TrackingQuest = 2
};
#endif // LootItemType_h__

View File

@@ -271,6 +271,8 @@ bool LootStoreItem::Roll(bool rate) const
return roll_chance_f(chance * qualityModifier);
}
case Type::TrackingQuest:
return roll_chance_f(chance);
default:
break;
}
@@ -364,6 +366,45 @@ bool LootStoreItem::IsValid(LootStore const& store, uint32 entry) const
}
break;
}
case Type::TrackingQuest:
{
Quest const* quest = sObjectMgr->GetQuestTemplate(itemid);
if (!quest)
{
TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} ItemType {} Item {}: quest does not exist - skipped",
store.GetName(), entry, type, itemid);
return false;
}
if (!quest->HasFlag(QUEST_FLAGS_TRACKING_EVENT))
{
TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} ItemType {} Item {}: quest is not a tracking flag - skipped",
store.GetName(), entry, type, itemid);
return false;
}
if (chance == 0 && groupid == 0) // Zero chance is allowed for grouped entries only
{
TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} ItemType {} Item {}: equal-chanced grouped entry, but group not defined - skipped",
store.GetName(), entry, type, itemid);
return false;
}
if (chance != 0 && chance < 0.0001f) // loot with low chance
{
TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} ItemType {} Item {}: low chance ({}) - skipped",
store.GetName(), entry, type, itemid, chance);
return false;
}
if (mincount != 1 || maxcount) // wrong count
{
TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} ItemType {} Item {}: tracking quest has count other than 1 (MinCount {} MaxCount {}) - skipped",
store.GetName(), entry, type, itemid, int32(maxcount), mincount);
return false;
}
break;
}
default:
TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} Item {}: invalid ItemType {}, skipped",
@@ -579,6 +620,10 @@ void LootTemplate::CopyConditions(LootItem* li) const
if (li->type != LootItemType::Currency)
continue;
break;
case LootStoreItem::Type::TrackingQuest:
if (li->type != LootItemType::TrackingQuest)
continue;
break;
default:
break;
}
@@ -619,6 +664,7 @@ void LootTemplate::Process(Loot& loot, bool rate, uint16 lootMode, uint8 groupId
{
case LootStoreItem::Type::Item:
case LootStoreItem::Type::Currency:
case LootStoreItem::Type::TrackingQuest:
// Plain entries (not a reference, not grouped)
// Chance is already checked, just add
if (!personalLooter || LootItem::AllowedForPlayer(personalLooter, *item, true))
@@ -729,6 +775,7 @@ void LootTemplate::ProcessPersonalLoot(std::unordered_map<Player*, std::unique_p
break;
}
case LootStoreItem::Type::Currency:
case LootStoreItem::Type::TrackingQuest:
{
// Plain entries (not a reference, not grouped)
// Chance is already checked, just add
@@ -786,6 +833,7 @@ bool LootTemplate::HasDropForPlayer(Player const* player, uint8 groupId, bool st
{
case LootStoreItem::Type::Item:
case LootStoreItem::Type::Currency:
case LootStoreItem::Type::TrackingQuest:
if (LootItem::AllowedForPlayer(player, *lootStoreItem, strictUsabilityCheck))
return true; // active quest drop found
break;

View File

@@ -39,9 +39,10 @@ struct TC_GAME_API LootStoreItem
{
enum class Type : int8
{
Item = 0,
Reference = 1,
Currency = 2,
Item = 0,
Reference = 1,
Currency = 2,
TrackingQuest = 3,
};
uint32 itemid; // id of the item

View File

@@ -343,7 +343,7 @@ enum TrinityStrings
LANG_COMMAND_NPC_SHOWLOOT_MONEY = 292,
LANG_COMMAND_NPC_SHOWLOOT_LABEL_2 = 293,
LANG_COMMAND_NPC_SHOWLOOT_SUBLABEL = 294,
LANG_COMMAND_NPC_SHOWLOOT_ENTRY_2 = 295,
LANG_COMMAND_NPC_SHOWLOOT_TRACKING_QUEST = 295,
LANG_COMMAND_NPC_SHOWLOOT_CURRENCY = 296,
// END

View File

@@ -1174,7 +1174,7 @@ public:
name = itemTemplate->GetName(handler->GetSessionDbcLocale());
if (!name)
name = "Unknown item";
handler->PSendSysMessage(alternateString ? LANG_COMMAND_NPC_SHOWLOOT_ENTRY_2 : LANG_COMMAND_NPC_SHOWLOOT_ENTRY,
handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_ENTRY, alternateString ? 6 : 3 /*number of bytes from following string*/, "\xE2\x94\x80\xE2\x94\x80",
itemCount, ItemQualityColors[itemTemplate ? static_cast<ItemQualities>(itemTemplate->GetQuality()) : ITEM_QUALITY_POOR], itemId, name, itemId);
}
@@ -1190,6 +1190,23 @@ public:
count, ItemQualityColors[currency ? static_cast<ItemQualities>(currency->Quality) : ITEM_QUALITY_POOR], currencyId, count, name, currencyId);
}
static void _ShowLootTrackingQuestCurrencyEntry(ChatHandler* handler, uint32 questId, bool alternateString = false)
{
Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
std::string_view name;
if (quest)
{
name = quest->GetLogTitle();
if (handler->GetSessionDbLocaleIndex() != LOCALE_enUS)
if (QuestTemplateLocale const* localeData = sObjectMgr->GetQuestLocale(questId))
ObjectMgr::GetLocaleString(localeData->LogTitle, handler->GetSessionDbLocaleIndex(), name);
}
if (name.empty())
name = "Unknown quest";
handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_TRACKING_QUEST, alternateString ? 6 : 3 /*number of bytes from following string*/, "\xE2\x94\x80\xE2\x94\x80",
questId, STRING_VIEW_FMT_ARG(name), questId);
}
static void _IterateNotNormalLootMap(ChatHandler* handler, NotNormalLootItemMap const& map, std::vector<LootItem> const& items)
{
for (NotNormalLootItemMap::value_type const& pair : map)
@@ -1212,6 +1229,9 @@ public:
case LootItemType::Currency:
_ShowLootCurrencyEntry(handler, item.itemid, item.count, true);
break;
case LootItemType::TrackingQuest:
_ShowLootTrackingQuestCurrencyEntry(handler, item.itemid, true);
break;
}
}
}
@@ -1237,6 +1257,9 @@ public:
case LootItemType::Currency:
_ShowLootCurrencyEntry(handler, item.itemid, item.count);
break;
case LootItemType::TrackingQuest:
_ShowLootTrackingQuestCurrencyEntry(handler, item.itemid);
break;
}
}
}
@@ -1256,6 +1279,9 @@ public:
case LootItemType::Currency:
_ShowLootCurrencyEntry(handler, item.itemid, item.count);
break;
case LootItemType::TrackingQuest:
_ShowLootTrackingQuestCurrencyEntry(handler, item.itemid);
break;
}
}
}