diff options
Diffstat (limited to 'src/game/LootMgr.cpp')
-rw-r--r-- | src/game/LootMgr.cpp | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/src/game/LootMgr.cpp b/src/game/LootMgr.cpp index e43ab44c184..df11d909dd0 100644 --- a/src/game/LootMgr.cpp +++ b/src/game/LootMgr.cpp @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "LootMgr.h" #include "Log.h" #include "ObjectMgr.h" @@ -25,6 +26,7 @@ #include "Util.h" #include "SharedDefines.h" #include "SpellMgr.h" + static Rates const qualityToRate[MAX_ITEM_QUALITY] = { RATE_DROP_ITEM_POOR, // ITEM_QUALITY_POOR RATE_DROP_ITEM_NORMAL, // ITEM_QUALITY_NORMAL @@ -34,6 +36,7 @@ static Rates const qualityToRate[MAX_ITEM_QUALITY] = { RATE_DROP_ITEM_LEGENDARY, // ITEM_QUALITY_LEGENDARY RATE_DROP_ITEM_ARTIFACT, // ITEM_QUALITY_ARTIFACT }; + LootStore LootTemplates_Creature( "creature_loot_template", "creature entry", true); LootStore LootTemplates_Disenchant( "disenchant_loot_template", "item disenchant id", true); LootStore LootTemplates_Fishing( "fishing_loot_template", "area id", true); @@ -47,6 +50,7 @@ LootStore LootTemplates_Reference( "reference_loot_template", "reference i LootStore LootTemplates_Skinning( "skinning_loot_template", "creature skinning id", true); LootStore LootTemplates_Spell( "spell_loot_template", "spell id (random item creating)",false); + class LootTemplate::LootGroup // A set of loot definitions for items (refs are not allowed) { public: @@ -57,14 +61,17 @@ class LootTemplate::LootGroup // A set of loot def void Process(Loot& loot) const; // Rolls an item from the group (if any) and adds the item to the loot float RawTotalChance() const; // Overall chance for the group (without equal chanced items) float TotalChance() const; // Overall chance for the group + void Verify(LootStore const& lootstore, uint32 id, uint32 group_id) const; void CollectLootIds(LootIdSet& set) const; void CheckLootRefs(LootTemplateMap const& store, LootIdSet* ref_set) const; private: LootStoreItemList ExplicitlyChanced; // Entries with chances defined in DB LootStoreItemList EqualChanced; // Zero chances - every entry takes the same chance + LootStoreItem const * Roll() const; // Rolls an item from the group, returns NULL if all miss their chances }; + //Remove all data and free all memory void LootStore::Clear() { @@ -72,6 +79,7 @@ void LootStore::Clear() delete itr->second; m_LootTemplates.clear(); } + // Checks validity of the loot store // Actual checks are done within LootTemplate::Verify() which is called for every template void LootStore::Verify() const @@ -79,24 +87,31 @@ void LootStore::Verify() const for (LootTemplateMap::const_iterator i = m_LootTemplates.begin(); i != m_LootTemplates.end(); ++i ) i->second->Verify(*this, i->first); } + // Loads a *_loot_template DB table into loot store // All checks of the loaded template are called from here, no error reports at loot generation required void LootStore::LoadLootTable() { LootTemplateMap::const_iterator tab; uint32 count = 0; + // Clearing store (for reloading case) Clear(); + sLog.outString( "%s :", GetName()); + // 0 1 2 3 4 5 6 7 8 QueryResult *result = WorldDatabase.PQuery("SELECT entry, item, ChanceOrQuestChance, groupid, mincountOrRef, maxcount, lootcondition, condition_value1, condition_value2 FROM %s",GetName()); + if (result) { barGoLink bar(result->GetRowCount()); + do { Field *fields = result->Fetch(); bar.step(); + uint32 entry = fields[0].GetUInt32(); uint32 item = fields[1].GetUInt32(); float chanceOrQuestChance = fields[2].GetFloat(); @@ -106,22 +121,28 @@ void LootStore::LoadLootTable() ConditionType condition = (ConditionType)fields[6].GetUInt8(); uint32 cond_value1 = fields[7].GetUInt32(); uint32 cond_value2 = fields[8].GetUInt32(); + if(maxcount > std::numeric_limits<uint8>::max()) { sLog.outErrorDb("Table '%s' entry %d item %d: maxcount value (%u) to large. must be less %u - skipped", GetName(), entry, item, maxcount,std::numeric_limits<uint8>::max()); continue; // error already printed to log/console. } + if(!PlayerCondition::IsValid(condition,cond_value1, cond_value2)) { sLog.outErrorDb("... in table '%s' entry %u item %u", GetName(), entry, item); continue; // error already printed to log/console. } + // (condition + cond_value1/2) are converted into single conditionId uint16 conditionId = objmgr.GetConditionId(condition, cond_value1, cond_value2); + LootStoreItem storeitem = LootStoreItem(item, chanceOrQuestChance, group, conditionId, mincountOrRef, maxcount); + if (!storeitem.IsValid(*this,entry)) // Validity checks continue; + // Looking for the template of the entry // often entries are put together if (m_LootTemplates.empty() || tab->first != entry) @@ -136,12 +157,17 @@ void LootStore::LoadLootTable() } // else is empty - template Id and iter are the same // finally iter refers to already existed or just created <entry, LootTemplate> + // Adds current row to the template tab->second->AddEntry(storeitem); ++count; + } while (result->NextRow()); + delete result; + Verify(); // Checks validity of the loot store + sLog.outString(); sLog.outString( ">> Loaded %u loot definitions (%lu templates)", count, (unsigned long)m_LootTemplates.size()); } @@ -151,65 +177,84 @@ void LootStore::LoadLootTable() sLog.outErrorDb( ">> Loaded 0 loot definitions. DB table `%s` is empty.",GetName() ); } } + bool LootStore::HaveQuestLootFor(uint32 loot_id) const { LootTemplateMap::const_iterator itr = m_LootTemplates.find(loot_id); if(itr == m_LootTemplates.end()) return false; + // scan loot for quest items return itr->second->HasQuestDrop(m_LootTemplates); } + bool LootStore::HaveQuestLootForPlayer(uint32 loot_id,Player* player) const { LootTemplateMap::const_iterator tab = m_LootTemplates.find(loot_id); if (tab != m_LootTemplates.end()) if (tab->second->HasQuestDropForPlayer(m_LootTemplates, player)) return true; + return false; } + LootTemplate const* LootStore::GetLootFor(uint32 loot_id) const { LootTemplateMap::const_iterator tab = m_LootTemplates.find(loot_id); + if (tab == m_LootTemplates.end()) return NULL; + return tab->second; } + void LootStore::LoadAndCollectLootIds(LootIdSet& ids_set) { LoadLootTable(); + for(LootTemplateMap::const_iterator tab = m_LootTemplates.begin(); tab != m_LootTemplates.end(); ++tab) ids_set.insert(tab->first); } + void LootStore::CheckLootRefs(LootIdSet* ref_set) const { for(LootTemplateMap::const_iterator ltItr = m_LootTemplates.begin(); ltItr != m_LootTemplates.end(); ++ltItr) ltItr->second->CheckLootRefs(m_LootTemplates,ref_set); } + void LootStore::ReportUnusedIds(LootIdSet const& ids_set) const { // all still listed ids isn't referenced for(LootIdSet::const_iterator itr = ids_set.begin(); itr != ids_set.end(); ++itr) sLog.outErrorDb("Table '%s' entry %d isn't %s and not referenced from loot, and then useless.", GetName(), *itr,GetEntryName()); } + void LootStore::ReportNotExistedId(uint32 id) const { sLog.outErrorDb("Table '%s' entry %d (%s) not exist but used as loot id in DB.", GetName(), id,GetEntryName()); } + // // --------- LootStoreItem --------- // + // Checks if the entry (quest, non-quest, reference) takes it's chance (at loot generation) // RATE_DROP_ITEMS is no longer used for all types of entries bool LootStoreItem::Roll(bool rate) const { if(chance>=100.0f) return true; + if(mincountOrRef < 0) // reference case return roll_chance_f(chance* (rate ? sWorld.getRate(RATE_DROP_ITEM_REFERENCED) : 1.0f)); + ItemPrototype const *pProto = objmgr.GetItemPrototype(itemid); + float qualityModifier = pProto && rate ? sWorld.getRate(qualityToRate[pProto->Quality]) : 1.0f; + return roll_chance_f(chance*qualityModifier); } + // Checks correctness of values bool LootStoreItem::IsValid(LootStore const& store, uint32 entry) const { @@ -218,11 +263,13 @@ bool LootStoreItem::IsValid(LootStore const& store, uint32 entry) const sLog.outErrorDb("Table '%s' entry %d item %d: group (%u) must be less %u - skipped", store.GetName(), entry, itemid, group, 1 << 7); return false; } + if (mincountOrRef == 0) { sLog.outErrorDb("Table '%s' entry %d item %d: wrong mincountOrRef (%d) - skipped", store.GetName(), entry, itemid, mincountOrRef); return false; } + if( mincountOrRef > 0 ) // item (quest or non-quest) entry, maybe grouped { ItemPrototype const *proto = objmgr.GetItemPrototype(itemid); @@ -231,22 +278,26 @@ bool LootStoreItem::IsValid(LootStore const& store, uint32 entry) const sLog.outErrorDb("Table '%s' entry %d item %d: item entry not listed in `item_template` - skipped", store.GetName(), entry, itemid); return false; } + if( chance == 0 && group == 0) // Zero chance is allowed for grouped entries only { sLog.outErrorDb("Table '%s' entry %d item %d: equal-chanced grouped entry, but group not defined - skipped", store.GetName(), entry, itemid); return false; } + if( chance != 0 && chance < 0.000001f ) // loot with low chance { sLog.outErrorDb("Table '%s' entry %d item %d: low chance (%f) - skipped", store.GetName(), entry, itemid, chance); return false; } + if( maxcount < mincountOrRef) // wrong max count { sLog.outErrorDb("Table '%s' entry %d item %d: max count (%u) less that min count (%i) - skipped", store.GetName(), entry, itemid, uint32(maxcount), mincountOrRef); return false; } + } else // mincountOrRef < 0 { @@ -260,17 +311,22 @@ bool LootStoreItem::IsValid(LootStore const& store, uint32 entry) const } return true; // Referenced template existence is checked at whole store level } + // // --------- LootItem --------- // + // Constructor, copies most fields from LootStoreItem and generates random count LootItem::LootItem(LootStoreItem const& li) { itemid = li.itemid; conditionId = li.conditionId; + ItemPrototype const* proto = objmgr.GetItemPrototype(itemid); freeforall = proto && (proto->Flags & ITEM_FLAGS_PARTY_LOOT); + needs_quest = li.needs_quest; + count = urand(li.mincountOrRef, li.maxcount); // constructor called for mincountOrRef > 0 only randomSuffix = GenerateEnchSuffixFactor(itemid); randomPropertyId = Item::GenerateItemRandomPropertyId(itemid); @@ -279,12 +335,14 @@ LootItem::LootItem(LootStoreItem const& li) is_underthreshold = 0; is_counted = 0; } + // Basic checks for player/item compatibility - if false no chance to see the item in the loot bool LootItem::AllowedForPlayer(Player const * player) const { // DB conditions check if ( !objmgr.IsPlayerMeetToCondition(player,conditionId) ) return false; + if ( needs_quest ) { // Checking quests for quest-only drop (check only quests requirements in this case) @@ -298,11 +356,14 @@ bool LootItem::AllowedForPlayer(Player const * player) const if (pProto && pProto->StartQuest && player->GetQuestStatus(pProto->StartQuest) != QUEST_STATUS_NONE && !player->HasQuestForItem(itemid)) return false; } + return true; } + // // --------- Loot --------- // + // Inserts the item into the loot (called by LootTemplate processors) void Loot::AddItem(LootStoreItem const & item) { @@ -314,6 +375,7 @@ void Loot::AddItem(LootStoreItem const & item) else if (items.size() < MAX_NR_LOOT_ITEMS) // Non-quest drop { items.push_back(LootItem(item)); + // non-conditional one-player only items are counted here, // free for all items are counted in FillFFALoot(), // non-ffa conditionals are counted in FillNonQuestNonFFAConditionalLoot() @@ -325,21 +387,27 @@ void Loot::AddItem(LootStoreItem const & item) } } } + // Calls processor of corresponding LootTemplate (which handles everything including references) void Loot::FillLoot(uint32 loot_id, LootStore const& store, Player* loot_owner, bool personal) { // Must be provided if(!loot_owner) return; + LootTemplate const* tab = store.GetLootFor(loot_id); + if (!tab) { sLog.outErrorDb("Table '%s' loot id #%u used but it doesn't have records.",store.GetName(),loot_id); return; } + items.reserve(MAX_NR_LOOT_ITEMS); quest_items.reserve(MAX_NR_QUEST_ITEMS); + tab->Process(*this, store,store.IsRatesAllowed ()); // Processing is done there, callback via Loot::AddItem() + // Setting access rights for group loot case Group * pGroup=loot_owner->GetGroup(); if(!personal && pGroup) @@ -352,22 +420,28 @@ void Loot::FillLoot(uint32 loot_id, LootStore const& store, Player* loot_owner, else FillNotNormalLootFor(loot_owner); } + void Loot::FillNotNormalLootFor(Player* pl) { uint32 plguid = pl->GetGUIDLow(); + QuestItemMap::const_iterator qmapitr = PlayerQuestItems.find(plguid); if (qmapitr == PlayerQuestItems.end()) FillQuestLoot(pl); + qmapitr = PlayerFFAItems.find(plguid); if (qmapitr == PlayerFFAItems.end()) FillFFALoot(pl); + qmapitr = PlayerNonQuestNonFFAConditionalItems.find(plguid); if (qmapitr == PlayerNonQuestNonFFAConditionalItems.end()) FillNonQuestNonFFAConditionalLoot(pl); } + QuestItemList* Loot::FillFFALoot(Player* player) { QuestItemList *ql = new QuestItemList(); + for(uint8 i = 0; i < items.size(); ++i) { LootItem &item = items[i]; @@ -382,26 +456,32 @@ QuestItemList* Loot::FillFFALoot(Player* player) delete ql; return NULL; } + PlayerFFAItems[player->GetGUIDLow()] = ql; return ql; } + QuestItemList* Loot::FillQuestLoot(Player* player) { if (items.size() == MAX_NR_LOOT_ITEMS) return NULL; QuestItemList *ql = new QuestItemList(); + for(uint8 i = 0; i < quest_items.size(); ++i) { LootItem &item = quest_items[i]; if(!item.is_looted && item.AllowedForPlayer(player) ) { ql->push_back(QuestItem(i)); + // questitems get blocked when they first apper in a // player's quest vector // // increase once if one looter only, looter-times if free for all if (item.freeforall || !item.is_blocked) ++unlootedCount; + item.is_blocked = true; + if (items.size() + ql->size() == MAX_NR_LOOT_ITEMS) break; } @@ -411,12 +491,15 @@ QuestItemList* Loot::FillQuestLoot(Player* player) delete ql; return NULL; } + PlayerQuestItems[player->GetGUIDLow()] = ql; return ql; } + QuestItemList* Loot::FillNonQuestNonFFAConditionalLoot(Player* player) { QuestItemList *ql = new QuestItemList(); + for(uint8 i = 0; i < items.size(); ++i) { LootItem &item = items[i]; @@ -435,10 +518,13 @@ QuestItemList* Loot::FillNonQuestNonFFAConditionalLoot(Player* player) delete ql; return NULL; } + PlayerNonQuestNonFFAConditionalItems[player->GetGUIDLow()] = ql; return ql; } + //=================================================== + void Loot::NotifyItemRemoved(uint8 lootIndex) { // notify all players that are looting this that the item was removed @@ -454,6 +540,7 @@ void Loot::NotifyItemRemoved(uint8 lootIndex) PlayersLooting.erase(i); } } + void Loot::NotifyMoneyRemoved() { // notify all players that are looting this that the money was removed @@ -468,12 +555,14 @@ void Loot::NotifyMoneyRemoved() PlayersLooting.erase(i); } } + void Loot::NotifyQuestItemRemoved(uint8 questIndex) { // when a free for all questitem is looted // all players will get notified of it being removed // (other questitems can be looted by each group member) // bit inefficient but isn't called often + std::set<uint64>::iterator i_next; for(std::set<uint64>::iterator i = PlayersLooting.begin(); i != PlayersLooting.end(); i = i_next) { @@ -486,10 +575,12 @@ void Loot::NotifyQuestItemRemoved(uint8 questIndex) { // find where/if the player has the given item in it's vector QuestItemList& pql = *pq->second; + uint8 j; for (j = 0; j < pql.size(); ++j) if (pql[j].index == questIndex) break; + if (j < pql.size()) pl->SendNotifyLootItemRemoved(items.size()+j); } @@ -498,6 +589,7 @@ void Loot::NotifyQuestItemRemoved(uint8 questIndex) PlayersLooting.erase(i); } } + void Loot::generateMoneyLoot( uint32 minAmount, uint32 maxAmount ) { if (maxAmount > 0) @@ -510,6 +602,7 @@ void Loot::generateMoneyLoot( uint32 minAmount, uint32 maxAmount ) gold = uint32(urand(minAmount >> 8, maxAmount >> 8) * sWorld.getRate(RATE_DROP_MONEY)) << 8; } } + LootItem* Loot::LootItemInSlot(uint32 lootSlot, Player* player, QuestItem **qitem, QuestItem **ffaitem, QuestItem **conditem) { LootItem* item = NULL; @@ -566,15 +659,19 @@ LootItem* Loot::LootItemInSlot(uint32 lootSlot, Player* player, QuestItem **qite } } } + if(is_looted) return NULL; + return item; } + uint32 Loot::GetMaxSlotInLootFor(Player* player) const { QuestItemMap::const_iterator itr = PlayerQuestItems.find(player->GetGUIDLow()); return items.size() + (itr != PlayerQuestItems.end() ? itr->second->size() : 0); } + ByteBuffer& operator<<(ByteBuffer& b, LootItem const& li) { b << uint32(li.itemid); @@ -585,6 +682,7 @@ ByteBuffer& operator<<(ByteBuffer& b, LootItem const& li) //b << uint8(0); // slot type - will send after this function call return b; } + ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv) { if (lv.permission == NONE_PERMISSION) @@ -593,12 +691,17 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv) b << uint8(0); // item count return b; // nothing output more } + Loot &l = lv.loot; + uint8 itemsShown = 0; + //gold b << uint32(l.gold); + size_t count_pos = b.wpos(); // pos of item count byte b << uint8(0); // item count placeholder + switch (lv.permission) { case GROUP_PERMISSION: @@ -610,6 +713,7 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv) if (!l.items[i].is_looted && !l.items[i].freeforall && !l.items[i].conditionId && l.items[i].AllowedForPlayer(lv.viewer)) { uint8 slot_type = (l.items[i].is_blocked || l.items[i].is_underthreshold) ? 0 : 1; + b << uint8(i) << l.items[i]; //send the index and the item if it's not looted, and blocked or under threshold, free for all items will be sent later, only one-player loots here b << uint8(slot_type); // 0 - get 1 - look only ++itemsShown; @@ -635,6 +739,7 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv) default: return b; // nothing output more } + QuestItemMap const& lootPlayerQuestItems = l.GetPlayerQuestItems(); QuestItemMap::const_iterator q_itr = lootPlayerQuestItems.find(lv.viewer->GetGUIDLow()); if (q_itr != lootPlayerQuestItems.end()) @@ -652,6 +757,7 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv) } } } + QuestItemMap const& lootPlayerFFAItems = l.GetPlayerFFAItems(); QuestItemMap::const_iterator ffa_itr = lootPlayerFFAItems.find(lv.viewer->GetGUIDLow()); if (ffa_itr != lootPlayerFFAItems.end()) @@ -668,6 +774,7 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv) } } } + QuestItemMap const& lootPlayerNonQuestNonFFAConditionalItems = l.GetPlayerNonQuestNonFFAConditionalItems(); QuestItemMap::const_iterator nn_itr = lootPlayerNonQuestNonFFAConditionalItems.find(lv.viewer->GetGUIDLow()); if (nn_itr != lootPlayerNonQuestNonFFAConditionalItems.end()) @@ -684,13 +791,17 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv) } } } + //update number of items shown b.put<uint8>(count_pos,itemsShown); + return b; } + // // --------- LootTemplate::LootGroup --------- // + // Adds an entry to the group (at loading stage) void LootTemplate::LootGroup::AddEntry(LootStoreItem& item) { @@ -699,16 +810,19 @@ void LootTemplate::LootGroup::AddEntry(LootStoreItem& item) else EqualChanced.push_back(item); } + // Rolls an item from the group, returns NULL if all miss their chances LootStoreItem const * LootTemplate::LootGroup::Roll() const { if (!ExplicitlyChanced.empty()) // First explicitly chanced entries are checked { float Roll = rand_chance(); + for (uint32 i=0; i<ExplicitlyChanced.size(); ++i) //check each explicitly chanced entry in the template and modify its chance based on quality. { if(ExplicitlyChanced[i].chance>=100.0f) return &ExplicitlyChanced[i]; + Roll -= ExplicitlyChanced[i].chance; if (Roll < 0) return &ExplicitlyChanced[i]; @@ -716,8 +830,10 @@ LootStoreItem const * LootTemplate::LootGroup::Roll() const } if (!EqualChanced.empty()) // If nothing selected yet - an item is taken from equal-chanced part return &EqualChanced[irand(0, EqualChanced.size()-1)]; + return NULL; // Empty drop from the group } + // True if group includes at least 1 quest drop entry bool LootTemplate::LootGroup::HasQuestDrop() const { @@ -729,6 +845,7 @@ bool LootTemplate::LootGroup::HasQuestDrop() const return true; return false; } + // True if group includes at least 1 quest drop entry for active quests of the player bool LootTemplate::LootGroup::HasQuestDropForPlayer(Player const * player) const { @@ -740,6 +857,7 @@ bool LootTemplate::LootGroup::HasQuestDropForPlayer(Player const * player) const return true; return false; } + // Rolls an item from the group (if any takes its chance) and adds the item to the loot void LootTemplate::LootGroup::Process(Loot& loot) const { @@ -747,23 +865,30 @@ void LootTemplate::LootGroup::Process(Loot& loot) const if (item != NULL) loot.AddItem(*item); } + // Overall chance for the group without equal chanced items float LootTemplate::LootGroup::RawTotalChance() const { float result = 0; + for (LootStoreItemList::const_iterator i=ExplicitlyChanced.begin(); i != ExplicitlyChanced.end(); ++i) if ( !i->needs_quest ) result += i->chance; + return result; } + // Overall chance for the group float LootTemplate::LootGroup::TotalChance() const { float result = RawTotalChance(); + if (!EqualChanced.empty() && result < 100.0f) return 100.0f; + return result; } + void LootTemplate::LootGroup::Verify(LootStore const& lootstore, uint32 id, uint32 group_id) const { float chance = RawTotalChance(); @@ -771,11 +896,13 @@ void LootTemplate::LootGroup::Verify(LootStore const& lootstore, uint32 id, uint { sLog.outErrorDb("Table '%s' entry %u group %d has total chance > 100%% (%f)", lootstore.GetName(), id, group_id, chance); } + if(chance >= 100.0f && !EqualChanced.empty()) { sLog.outErrorDb("Table '%s' entry %u group %d has items with chance=0%% but group total chance >= 100%% (%f)", lootstore.GetName(), id, group_id, chance); } } + void LootTemplate::LootGroup::CheckLootRefs(LootTemplateMap const& store, LootIdSet* ref_set) const { for (LootStoreItemList::const_iterator ieItr=ExplicitlyChanced.begin(); ieItr != ExplicitlyChanced.end(); ++ieItr) @@ -788,6 +915,7 @@ void LootTemplate::LootGroup::CheckLootRefs(LootTemplateMap const& store, LootId ref_set->erase(-ieItr->mincountOrRef); } } + for (LootStoreItemList::const_iterator ieItr=EqualChanced.begin(); ieItr != EqualChanced.end(); ++ieItr) { if(ieItr->mincountOrRef < 0) @@ -799,9 +927,11 @@ void LootTemplate::LootGroup::CheckLootRefs(LootTemplateMap const& store, LootId } } } + // // --------- LootTemplate --------- // + // Adds an entry to the group (at loading stage) void LootTemplate::AddEntry(LootStoreItem& item) { @@ -814,6 +944,7 @@ void LootTemplate::AddEntry(LootStoreItem& item) else // Non-grouped entries and references are stored together Entries.push_back(item); } + // Rolls for every item in the template and adds the rolled items the the loot void LootTemplate::Process(Loot& loot, LootStore const& store, bool rate, uint8 groupId) const { @@ -821,29 +952,36 @@ void LootTemplate::Process(Loot& loot, LootStore const& store, bool rate, uint8 { if (groupId > Groups.size()) return; // Error message already printed at loading stage + Groups[groupId-1].Process(loot); return; } + // Rolling non-grouped items for (LootStoreItemList::const_iterator i = Entries.begin() ; i != Entries.end() ; ++i ) { if (!i->Roll(rate)) continue; // Bad luck for the entry + if (i->mincountOrRef < 0) // References processing { LootTemplate const* Referenced = LootTemplates_Reference.GetLootFor(-i->mincountOrRef); + if(!Referenced) continue; // Error message already printed at loading stage + for (uint32 loop=0; loop < i->maxcount; ++loop )// Ref multiplicator Referenced->Process(loot, store, rate, i->group); } else // Plain entries (not a reference, not grouped) loot.AddItem(*i); // Chance is already checked, just add } + // Now processing groups for (LootGroups::const_iterator i = Groups.begin( ) ; i != Groups.end( ) ; ++i ) i->Process(loot); } + // True if template includes at least 1 quest drop entry bool LootTemplate::HasQuestDrop(LootTemplateMap const& store, uint8 groupId) const { @@ -853,6 +991,7 @@ bool LootTemplate::HasQuestDrop(LootTemplateMap const& store, uint8 groupId) con return false; // Error message [should be] already printed at loading stage return Groups[groupId-1].HasQuestDrop(); } + for (LootStoreItemList::const_iterator i = Entries.begin(); i != Entries.end(); ++i ) { if (i->mincountOrRef < 0) // References @@ -866,12 +1005,15 @@ bool LootTemplate::HasQuestDrop(LootTemplateMap const& store, uint8 groupId) con else if ( i->needs_quest ) return true; // quest drop found } + // Now processing groups for (LootGroups::const_iterator i = Groups.begin() ; i != Groups.end() ; ++i ) if (i->HasQuestDrop()) return true; + return false; } + // True if template includes at least 1 quest drop for an active quest of the player bool LootTemplate::HasQuestDropForPlayer(LootTemplateMap const& store, Player const* player, uint8 groupId) const { @@ -881,6 +1023,7 @@ bool LootTemplate::HasQuestDropForPlayer(LootTemplateMap const& store, Player co return false; // Error message already printed at loading stage return Groups[groupId-1].HasQuestDropForPlayer(player); } + // Checking non-grouped entries for (LootStoreItemList::const_iterator i = Entries.begin() ; i != Entries.end() ; ++i ) { @@ -895,20 +1038,25 @@ bool LootTemplate::HasQuestDropForPlayer(LootTemplateMap const& store, Player co else if ( player->HasQuestForItem(i->itemid) ) return true; // active quest drop found } + // Now checking groups for (LootGroups::const_iterator i = Groups.begin(); i != Groups.end(); ++i ) if (i->HasQuestDropForPlayer(player)) return true; + return false; } + // Checks integrity of the template void LootTemplate::Verify(LootStore const& lootstore, uint32 id) const { // Checking group chances for (uint32 i=0; i < Groups.size(); ++i) Groups[i].Verify(lootstore,id,i+1); + // TODO: References validity checks } + void LootTemplate::CheckLootRefs(LootTemplateMap const& store, LootIdSet* ref_set) const { for(LootStoreItemList::const_iterator ieItr = Entries.begin(); ieItr != Entries.end(); ++ieItr) @@ -921,13 +1069,16 @@ void LootTemplate::CheckLootRefs(LootTemplateMap const& store, LootIdSet* ref_se ref_set->erase(-ieItr->mincountOrRef); } } + for(LootGroups::const_iterator grItr = Groups.begin(); grItr != Groups.end(); ++grItr) grItr->CheckLootRefs(store,ref_set); } + void LoadLootTemplates_Creature() { LootIdSet ids_set, ids_setUsed; LootTemplates_Creature.LoadAndCollectLootIds(ids_set); + // remove real entries and check existence loot for(uint32 i = 1; i < sCreatureStorage.MaxEntry; ++i ) { @@ -944,13 +1095,16 @@ void LoadLootTemplates_Creature() } for(LootIdSet::const_iterator itr = ids_setUsed.begin(); itr != ids_setUsed.end(); ++itr) ids_set.erase(*itr); + // output error for any still listed (not referenced from appropriate table) ids LootTemplates_Creature.ReportUnusedIds(ids_set); } + void LoadLootTemplates_Disenchant() { LootIdSet ids_set, ids_setUsed; LootTemplates_Disenchant.LoadAndCollectLootIds(ids_set); + // remove real entries and check existence loot for(uint32 i = 1; i < sItemStorage.MaxEntry; ++i ) { @@ -970,10 +1124,12 @@ void LoadLootTemplates_Disenchant() // output error for any still listed (not referenced from appropriate table) ids LootTemplates_Disenchant.ReportUnusedIds(ids_set); } + void LoadLootTemplates_Fishing() { LootIdSet ids_set; LootTemplates_Fishing.LoadAndCollectLootIds(ids_set); + // remove real entries and check existence loot for(uint32 i = 1; i < sAreaStore.GetNumRows(); ++i ) { @@ -981,13 +1137,16 @@ void LoadLootTemplates_Fishing() if(ids_set.count(areaEntry->ID)) ids_set.erase(areaEntry->ID); } + // output error for any still listed (not referenced from appropriate table) ids LootTemplates_Fishing.ReportUnusedIds(ids_set); } + void LoadLootTemplates_Gameobject() { LootIdSet ids_set, ids_setUsed; LootTemplates_Gameobject.LoadAndCollectLootIds(ids_set); + // remove real entries and check existence loot for(uint32 i = 1; i < sGOStorage.MaxEntry; ++i ) { @@ -1004,43 +1163,54 @@ void LoadLootTemplates_Gameobject() } for(LootIdSet::const_iterator itr = ids_setUsed.begin(); itr != ids_setUsed.end(); ++itr) ids_set.erase(*itr); + // output error for any still listed (not referenced from appropriate table) ids LootTemplates_Gameobject.ReportUnusedIds(ids_set); } + void LoadLootTemplates_Item() { LootIdSet ids_set; LootTemplates_Item.LoadAndCollectLootIds(ids_set); + // remove real entries and check existence loot for(uint32 i = 1; i < sItemStorage.MaxEntry; ++i ) if(ItemPrototype const* proto = sItemStorage.LookupEntry<ItemPrototype>(i)) if(ids_set.count(proto->ItemId)) ids_set.erase(proto->ItemId); + // output error for any still listed (not referenced from appropriate table) ids LootTemplates_Item.ReportUnusedIds(ids_set); } + void LoadLootTemplates_Milling() { LootIdSet ids_set; LootTemplates_Milling.LoadAndCollectLootIds(ids_set); + // remove real entries and check existence loot for(uint32 i = 1; i < sItemStorage.MaxEntry; ++i ) { ItemPrototype const* proto = sItemStorage.LookupEntry<ItemPrototype>(i); if(!proto) continue; + if((proto->BagFamily & BAG_FAMILY_MASK_HERBS)==0) continue; + if(ids_set.count(proto->ItemId)) ids_set.erase(proto->ItemId); } + // output error for any still listed (not referenced from appropriate table) ids LootTemplates_Milling.ReportUnusedIds(ids_set); } + void LoadLootTemplates_Pickpocketing() { LootIdSet ids_set, ids_setUsed; LootTemplates_Pickpocketing.LoadAndCollectLootIds(ids_set); + // remove real entries and check existence loot for(uint32 i = 1; i < sCreatureStorage.MaxEntry; ++i ) { @@ -1057,37 +1227,46 @@ void LoadLootTemplates_Pickpocketing() } for(LootIdSet::const_iterator itr = ids_setUsed.begin(); itr != ids_setUsed.end(); ++itr) ids_set.erase(*itr); + // output error for any still listed (not referenced from appropriate table) ids LootTemplates_Pickpocketing.ReportUnusedIds(ids_set); } + void LoadLootTemplates_Prospecting() { LootIdSet ids_set; LootTemplates_Prospecting.LoadAndCollectLootIds(ids_set); + // remove real entries and check existence loot for(uint32 i = 1; i < sItemStorage.MaxEntry; ++i ) { ItemPrototype const* proto = sItemStorage.LookupEntry<ItemPrototype>(i); if(!proto) continue; + if((proto->BagFamily & BAG_FAMILY_MASK_MINING_SUPP)==0) continue; + if(ids_set.count(proto->ItemId)) ids_set.erase(proto->ItemId); } + // output error for any still listed (not referenced from appropriate table) ids LootTemplates_Prospecting.ReportUnusedIds(ids_set); } + void LoadLootTemplates_QuestMail() { LootIdSet ids_set; LootTemplates_QuestMail.LoadAndCollectLootIds(ids_set); + // remove real entries and check existence loot ObjectMgr::QuestMap const& questMap = objmgr.GetQuestTemplates(); for(ObjectMgr::QuestMap::const_iterator itr = questMap.begin(); itr != questMap.end(); ++itr ) { if(!itr->second->GetRewMailTemplateId()) continue; + if(ids_set.count(itr->first)) ids_set.erase(itr->first); /* disabled reporting: some quest mails not include items @@ -1095,13 +1274,16 @@ void LoadLootTemplates_QuestMail() LootTemplates_QuestMail.ReportNotExistedId(itr->first); */ } + // output error for any still listed (not referenced from appropriate table) ids LootTemplates_QuestMail.ReportUnusedIds(ids_set); } + void LoadLootTemplates_Skinning() { LootIdSet ids_set, ids_setUsed; LootTemplates_Skinning.LoadAndCollectLootIds(ids_set); + // remove real entries and check existence loot for(uint32 i = 1; i < sCreatureStorage.MaxEntry; ++i ) { @@ -1118,22 +1300,27 @@ void LoadLootTemplates_Skinning() } for(LootIdSet::const_iterator itr = ids_setUsed.begin(); itr != ids_setUsed.end(); ++itr) ids_set.erase(*itr); + // output error for any still listed (not referenced from appropriate table) ids LootTemplates_Skinning.ReportUnusedIds(ids_set); } + void LoadLootTemplates_Spell() { LootIdSet ids_set; LootTemplates_Spell.LoadAndCollectLootIds(ids_set); + // remove real entries and check existence loot for(uint32 spell_id = 1; spell_id < sSpellStore.GetNumRows(); ++spell_id) { SpellEntry const* spellInfo = sSpellStore.LookupEntry (spell_id); if(!spellInfo) continue; + // possible cases if( !IsLootCraftingSpell(spellInfo)) continue; + if(!ids_set.count(spell_id)) { // not report about not trainable spells (optionally supported by DB) @@ -1146,13 +1333,16 @@ void LoadLootTemplates_Spell() else ids_set.erase(spell_id); } + // output error for any still listed (not referenced from appropriate table) ids LootTemplates_Spell.ReportUnusedIds(ids_set); } + void LoadLootTemplates_Reference() { LootIdSet ids_set; LootTemplates_Reference.LoadAndCollectLootIds(ids_set); + // check references and remove used LootTemplates_Creature.CheckLootRefs(&ids_set); LootTemplates_Fishing.CheckLootRefs(&ids_set); @@ -1165,6 +1355,7 @@ void LoadLootTemplates_Reference() LootTemplates_Prospecting.CheckLootRefs(&ids_set); LootTemplates_QuestMail.CheckLootRefs(&ids_set); LootTemplates_Reference.CheckLootRefs(&ids_set); + // output error for any still listed ids (not referenced from any loot table) LootTemplates_Reference.ReportUnusedIds(ids_set); } |