diff --git a/data/sql/updates/pending_db_characters/rev_1622403654219554600.sql b/data/sql/updates/pending_db_characters/rev_1622403654219554600.sql new file mode 100644 index 000000000..daf29c532 --- /dev/null +++ b/data/sql/updates/pending_db_characters/rev_1622403654219554600.sql @@ -0,0 +1,9 @@ +INSERT INTO `version_db_characters` (`sql_rev`) VALUES ('1622403654219554600'); + +ALTER TABLE `item_loot_storage` +ADD COLUMN `follow_loot_rules` TINYINT UNSIGNED NOT NULL AFTER `randomSuffix`, +ADD COLUMN `freeforall` TINYINT UNSIGNED NOT NULL AFTER `follow_loot_rules`, +ADD COLUMN `is_blocked` TINYINT UNSIGNED NOT NULL AFTER `freeforall`, +ADD COLUMN `is_counted` TINYINT UNSIGNED NOT NULL AFTER `is_blocked`, +ADD COLUMN `is_underthreshold` TINYINT UNSIGNED NOT NULL AFTER `is_counted`, +ADD COLUMN `needs_quest` TINYINT UNSIGNED NOT NULL AFTER `is_underthreshold`; diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index 8409b3216..e456e9e3d 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -509,9 +509,9 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_DEL_CHAR_ACTION_EXCEPT_SPEC, "DELETE FROM character_action WHERE spec<>? AND guid = ?", CONNECTION_ASYNC); // Items that hold loot or money - PrepareStatement(CHAR_SEL_ITEMCONTAINER_ITEMS, "SELECT containerGUID, itemid, count, randomPropertyId, randomSuffix FROM item_loot_storage", CONNECTION_SYNCH); + PrepareStatement(CHAR_SEL_ITEMCONTAINER_ITEMS, "SELECT containerGUID, itemid, count, randomPropertyId, randomSuffix, follow_loot_rules, freeforall, is_blocked, is_counted, is_underthreshold, needs_quest FROM item_loot_storage", CONNECTION_SYNCH); PrepareStatement(CHAR_DEL_ITEMCONTAINER_SINGLE_ITEM, "DELETE FROM item_loot_storage WHERE containerGUID = ? AND itemid = ? AND count = ? LIMIT 1", CONNECTION_ASYNC); - PrepareStatement(CHAR_INS_ITEMCONTAINER_SINGLE_ITEM, "INSERT INTO item_loot_storage (containerGUID, itemid, count, randomPropertyId, randomSuffix) VALUES (?, ?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_ITEMCONTAINER_SINGLE_ITEM, "INSERT INTO item_loot_storage (containerGUID, itemid, count, randomPropertyId, randomSuffix, follow_loot_rules, freeforall, is_blocked, is_counted, is_underthreshold, needs_quest) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_ITEMCONTAINER_CONTAINER, "DELETE FROM item_loot_storage WHERE containerGUID = ?", CONNECTION_ASYNC); // Calendar diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 792fb616d..019248d12 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -9379,7 +9379,7 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type) // Xinef: Store container id loot->containerGUID = item->GetGUID(); - if (!item->m_lootGenerated && !sLootItemStorage->LoadStoredLoot(item)) + if (!item->m_lootGenerated && !sLootItemStorage->LoadStoredLoot(item, this)) { item->m_lootGenerated = true; loot->clear(); diff --git a/src/server/game/Loot/LootItemStorage.cpp b/src/server/game/Loot/LootItemStorage.cpp index 658010ecb..c3ae49333 100644 --- a/src/server/game/Loot/LootItemStorage.cpp +++ b/src/server/game/Loot/LootItemStorage.cpp @@ -41,7 +41,8 @@ void LootItemStorage::LoadStorageFromDB() Field* fields = result->Fetch(); StoredLootItemList& itemList = lootItemStore[ObjectGuid::Create(fields[0].GetUInt32())]; - itemList.push_back(StoredLootItem(fields[1].GetUInt32(), fields[2].GetUInt32(), fields[3].GetInt32(), fields[4].GetUInt32())); + itemList.push_back(StoredLootItem(fields[1].GetUInt32(), fields[2].GetUInt32(), fields[3].GetInt32(), fields[4].GetUInt32(), fields[5].GetBool(), + fields[6].GetBool(), fields[7].GetBool(), fields[8].GetBool(), fields[9].GetBool(), fields[10].GetBool())); ++count; } while (result->NextRow()); @@ -79,14 +80,21 @@ void LootItemStorage::AddNewStoredLoot(Loot* loot, Player* /*player*/) // Gold at first if (loot->gold) { - itemList.push_back(StoredLootItem(0, loot->gold, 0, 0)); + itemList.push_back(StoredLootItem(0, loot->gold, 0, 0, false, false, false, false, false, false)); + uint8 index = 0; stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEMCONTAINER_SINGLE_ITEM); - stmt->setUInt32(0, loot->containerGUID.GetCounter()); - stmt->setUInt32(1, 0); - stmt->setUInt32(2, loot->gold); - stmt->setInt32(3, 0); - stmt->setUInt32(4, 0); + stmt->setUInt32(index++, loot->containerGUID.GetCounter()); + stmt->setUInt32(index++, 0); + stmt->setUInt32(index++, loot->gold); + stmt->setInt32(index++, 0); + stmt->setUInt32(index++, 0); + stmt->setBool(index++, false); + stmt->setBool(index++, false); + stmt->setBool(index++, false); + stmt->setBool(index++, false); + stmt->setBool(index++, false); + stmt->setBool(index++, false); trans->Append(stmt); } @@ -104,22 +112,36 @@ void LootItemStorage::AddNewStoredLoot(Loot* loot, Player* /*player*/) if (!itemTemplate || itemTemplate->IsCurrencyToken()) continue; - itemList.push_back(StoredLootItem(li->itemid, li->count, li->randomPropertyId, li->randomSuffix)); + itemList.push_back(StoredLootItem(li->itemid, li->count, li->randomPropertyId, li->randomSuffix, li->follow_loot_rules, li->freeforall, li->is_blocked, li->is_counted, + li->is_underthreshold, li->needs_quest)); + uint8 index = 0; stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEMCONTAINER_SINGLE_ITEM); - stmt->setUInt32(0, loot->containerGUID.GetCounter()); - stmt->setUInt32(1, li->itemid); - stmt->setUInt32(2, li->count); - stmt->setInt32 (3, li->randomPropertyId); - stmt->setUInt32(4, li->randomSuffix); + stmt->setUInt32(index++, loot->containerGUID.GetCounter()); + stmt->setUInt32(index++, li->itemid); + stmt->setUInt32(index++, li->count); + stmt->setInt32 (index++, li->randomPropertyId); + stmt->setUInt32(index++, li->randomSuffix); + stmt->setBool(index++, li->follow_loot_rules); + stmt->setBool(index++, li->freeforall); + stmt->setBool(index++, li->is_blocked); + stmt->setBool(index++, li->is_counted); + stmt->setBool(index++, li->is_underthreshold); + stmt->setBool(index++, li->needs_quest); trans->Append(stmt); } CharacterDatabase.CommitTransaction(trans); } -bool LootItemStorage::LoadStoredLoot(Item* item) +bool LootItemStorage::LoadStoredLoot(Item* item, Player* player) { + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(item->GetEntry()); + if (!proto) + { + return false; + } + Loot* loot = &item->loot; LootItemContainer::iterator itr = lootItemStore.find(loot->containerGUID); if (itr == lootItemStore.end()) @@ -134,22 +156,47 @@ bool LootItemStorage::LoadStoredLoot(Item* item) continue; } - LootItem li; - li.itemid = it2->itemid; - li.count = it2->count; - li.follow_loot_rules = false; - li.freeforall = false; - li.is_blocked = false; - li.is_counted = false; - li.is_underthreshold = false; - li.is_looted = false; - li.needs_quest = false; - li.randomPropertyId = it2->randomPropertyId; - li.randomSuffix = it2->randomSuffix; - li.rollWinnerGUID = ObjectGuid::Empty; + if (LootTemplate const* lt = LootTemplates_Item.GetLootFor(item->GetEntry())) + { + LootItem li; + li.itemid = it2->itemid; + li.count = it2->count; + li.follow_loot_rules = it2->follow_loot_rules; + li.freeforall = it2->freeforall; + li.is_blocked = it2->is_blocked; + li.is_counted = it2->is_counted; + li.is_underthreshold = it2->is_underthreshold; + li.is_looted = false; + li.needs_quest = it2->needs_quest; + li.randomPropertyId = it2->randomPropertyId; + li.randomSuffix = it2->randomSuffix; + li.rollWinnerGUID = ObjectGuid::Empty; - loot->items.push_back(li); - loot->unlootedCount++; + // Copy the extra loot conditions from the item in the loot template + lt->CopyConditions(&li); + + if (li.needs_quest) + { + loot->quest_items.push_back(li); + } + else + { + loot->items.push_back(li); + } + + // non-conditional one-player only items are counted here, + // free for all items are counted in FillFFALoot(), + // non-ffa conditionals are counted in FillNonQuestNonFFAConditionalLoot() + if ((!li.needs_quest && li.conditions.empty() && !(proto->Flags & ITEM_FLAG_MULTI_DROP)) || li.is_counted) + { + ++loot->unlootedCount; + } + } + } + + if (loot->unlootedCount) + { + loot->FillNotNormalLootFor(player, true); } // Mark the item if it has loot so it won't be generated again on open diff --git a/src/server/game/Loot/LootItemStorage.h b/src/server/game/Loot/LootItemStorage.h index dd9791588..16f3cf574 100644 --- a/src/server/game/Loot/LootItemStorage.h +++ b/src/server/game/Loot/LootItemStorage.h @@ -13,14 +13,21 @@ Xinef struct StoredLootItem { - StoredLootItem(uint32 i, uint32 c, int32 ri, uint32 rs) : - itemid(i), count(c), randomPropertyId(ri), randomSuffix(rs) { } + StoredLootItem(uint32 i, uint32 c, int32 ri, uint32 rs, bool follow_loot_rules, bool freeforall, bool is_blocked, bool is_counted, bool is_underthreshold, bool needs_quest) : + itemid(i), count(c), randomPropertyId(ri), randomSuffix(rs), follow_loot_rules(follow_loot_rules), freeforall(freeforall), is_blocked(is_blocked), + is_counted(is_counted), is_underthreshold(is_underthreshold), needs_quest(needs_quest) { } // If itemid == 0 - money amount is stored in count value uint32 itemid; uint32 count; int32 randomPropertyId; uint32 randomSuffix; + bool follow_loot_rules; + bool freeforall; + bool is_blocked; + bool is_counted; + bool is_underthreshold; + bool needs_quest; }; typedef std::list StoredLootItemList; @@ -39,7 +46,7 @@ public: void RemoveEntryFromDB(ObjectGuid containerGUID, uint32 itemid, uint32 count); void AddNewStoredLoot(Loot* loot, Player* player); - bool LoadStoredLoot(Item* item); + bool LoadStoredLoot(Item* item, Player* player); void RemoveStoredLootItem(ObjectGuid containerGUID, uint32 itemid, uint32 count, Loot* loot); void RemoveStoredLootMoney(ObjectGuid containerGUID, Loot* loot); diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp index 35d845991..991c17d26 100644 --- a/src/server/game/Loot/LootMgr.cpp +++ b/src/server/game/Loot/LootMgr.cpp @@ -1375,18 +1375,93 @@ void LootTemplate::CopyConditions(ConditionList conditions) group->CopyConditions(conditions); } -void LootTemplate::CopyConditions(LootItem* li) const +bool LootTemplate::CopyConditions(LootItem* li) const { - // Copies the conditions list from a template item to a LootItem for (LootStoreItemList::const_iterator _iter = Entries.begin(); _iter != Entries.end(); ++_iter) { LootStoreItem* item = *_iter; - if (item->itemid != li->itemid) + if (item->reference) + { + if (LootTemplate const* Referenced = LootTemplates_Reference.GetLootFor(item->reference)) + { + if (Referenced->CopyConditions(li)) + { + return true; + } + } + } + else + { + if (item->itemid != li->itemid) + { + continue; + } + + li->conditions = item->conditions; + return true; + } + } + + for (LootGroups::const_iterator groupItr = Groups.begin(); groupItr != Groups.end(); ++groupItr) + { + LootGroup* group = *groupItr; + if (!group) continue; - li->conditions = item->conditions; - break; + LootStoreItemList* itemList = group->GetExplicitlyChancedItemList(); + for (LootStoreItemList::iterator i = itemList->begin(); i != itemList->end(); ++i) + { + LootStoreItem* item = *i; + if (item->reference) + { + if (LootTemplate const* Referenced = LootTemplates_Reference.GetLootFor(item->reference)) + { + if (Referenced->CopyConditions(li)) + { + return true; + } + } + } + else + { + if (item->itemid != li->itemid) + { + continue; + } + + li->conditions = item->conditions; + return true; + } + } + + itemList = group->GetEqualChancedItemList(); + for (LootStoreItemList::iterator i = itemList->begin(); i != itemList->end(); ++i) + { + LootStoreItem* item = *i; + if (item->reference) + { + if (LootTemplate const* Referenced = LootTemplates_Reference.GetLootFor(item->reference)) + { + if (Referenced->CopyConditions(li)) + { + return true; + } + } + } + else + { + if (item->itemid != li->itemid) + { + continue; + } + + li->conditions = item->conditions; + return true; + } + } } + + return false; } // Rolls for every item in the template and adds the rolled items the the loot diff --git a/src/server/game/Loot/LootMgr.h b/src/server/game/Loot/LootMgr.h index 4c4dbd7fa..bcd7ae9a5 100644 --- a/src/server/game/Loot/LootMgr.h +++ b/src/server/game/Loot/LootMgr.h @@ -243,7 +243,7 @@ public: // Rolls for every item in the template and adds the rolled items the the loot void Process(Loot& loot, LootStore const& store, uint16 lootMode, Player const* player, uint8 groupId = 0) const; void CopyConditions(ConditionList conditions); - void CopyConditions(LootItem* li) const; + bool CopyConditions(LootItem* li) const; // True if template includes at least 1 quest drop entry [[nodiscard]] bool HasQuestDrop(LootTemplateMap const& store, uint8 groupId = 0) const; @@ -371,9 +371,9 @@ struct Loot bool hasItemForAll() const; bool hasItemFor(Player* player) const; [[nodiscard]] bool hasOverThresholdItem() const; + void FillNotNormalLootFor(Player* player, bool presentAtLooting); private: - void FillNotNormalLootFor(Player* player, bool presentAtLooting); QuestItemList* FillFFALoot(Player* player); QuestItemList* FillQuestLoot(Player* player); QuestItemList* FillNonQuestNonFFAConditionalLoot(Player* player, bool presentAtLooting);