diff options
Diffstat (limited to 'src/game/LootMgr.cpp')
-rw-r--r-- | src/game/LootMgr.cpp | 240 |
1 files changed, 179 insertions, 61 deletions
diff --git a/src/game/LootMgr.cpp b/src/game/LootMgr.cpp index c8511c3deeb..100e1b8c33a 100644 --- a/src/game/LootMgr.cpp +++ b/src/game/LootMgr.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,6 +25,7 @@ #include "World.h" #include "Util.h" #include "SharedDefines.h" +#include "SpellMgr.h" static Rates const qualityToRate[MAX_ITEM_QUALITY] = { RATE_DROP_ITEM_POOR, // ITEM_QUALITY_POOR @@ -36,16 +37,18 @@ static Rates const qualityToRate[MAX_ITEM_QUALITY] = { RATE_DROP_ITEM_ARTIFACT, // ITEM_QUALITY_ARTIFACT }; -LootStore LootTemplates_Creature( "creature_loot_template", "creature entry"); -LootStore LootTemplates_Disenchant( "disenchant_loot_template", "item disenchant id"); -LootStore LootTemplates_Fishing( "fishing_loot_template", "area id"); -LootStore LootTemplates_Gameobject( "gameobject_loot_template", "gameobject entry"); -LootStore LootTemplates_Item( "item_loot_template", "item entry"); -LootStore LootTemplates_Pickpocketing("pickpocketing_loot_template","creature pickpocket lootid"); -LootStore LootTemplates_Prospecting( "prospecting_loot_template", "item entry"); -LootStore LootTemplates_QuestMail( "quest_mail_loot_template", "quest id"); -LootStore LootTemplates_Reference( "reference_loot_template", "reference id"); -LootStore LootTemplates_Skinning( "skinning_loot_template", "creature skinning id"); +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); +LootStore LootTemplates_Gameobject( "gameobject_loot_template", "gameobject entry", true); +LootStore LootTemplates_Item( "item_loot_template", "item entry", true); +LootStore LootTemplates_Milling( "milling_loot_template", "item entry (herb)", true); +LootStore LootTemplates_Pickpocketing("pickpocketing_loot_template","creature pickpocket lootid", true); +LootStore LootTemplates_Prospecting( "prospecting_loot_template", "item entry (ore)", true); +LootStore LootTemplates_QuestMail( "quest_mail_loot_template", "quest id (with mail template)",false); +LootStore LootTemplates_Reference( "reference_loot_template", "reference id", false); +LootStore LootTemplates_Skinning( "skinning_loot_template", "creature skinning id", true); +LootStore LootTemplates_Spell( "spell_loot_template", "spell id (explicitly discovering ability)",false); class LootTemplate::LootGroup // A set of loot definitions for items (refs are not allowed) @@ -114,11 +117,18 @@ void LootStore::LoadLootTable() float chanceOrQuestChance = fields[2].GetFloat(); uint8 group = fields[3].GetUInt8(); int32 mincountOrRef = fields[4].GetInt32(); - uint8 maxcount = fields[5].GetUInt8(); + uint32 maxcount = fields[5].GetUInt32(); 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); @@ -230,17 +240,17 @@ void LootStore::ReportNotExistedId(uint32 id) const // 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() const +bool LootStoreItem::Roll(bool rate) const { if(chance>=100.f) return true; if(mincountOrRef < 0) // reference case - return roll_chance_f(chance*sWorld.getRate(RATE_DROP_ITEM_REFERENCED)); + return roll_chance_f(chance* (rate ? sWorld.getRate(RATE_DROP_ITEM_REFERENCED) : 1.0f)); ItemPrototype const *pProto = objmgr.GetItemPrototype(itemid); - float qualityModifier = pProto ? sWorld.getRate(qualityToRate[pProto->Quality]) : 1.0f; + float qualityModifier = pProto && rate ? sWorld.getRate(qualityToRate[pProto->Quality]) : 1.0f; return roll_chance_f(chance*qualityModifier); } @@ -248,6 +258,12 @@ bool LootStoreItem::Roll() const // Checks correctness of values bool LootStoreItem::IsValid(LootStore const& store, uint32 entry) const { + if(group >= 1 << 7) // it stored in 7 bit field + { + 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); @@ -275,6 +291,13 @@ bool LootStoreItem::IsValid(LootStore const& store, uint32 entry) const 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 { @@ -366,8 +389,12 @@ 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) +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) @@ -379,37 +406,36 @@ void Loot::FillLoot(uint32 loot_id, LootStore const& store, Player* loot_owner) items.reserve(MAX_NR_LOOT_ITEMS); quest_items.reserve(MAX_NR_QUEST_ITEMS); - tab->Process(*this, store); // Processing is done there, callback via Loot::AddItem() + tab->Process(*this, store,store.IsRatesAllowed ()); // Processing is done there, callback via Loot::AddItem() - // Setting access rights fow group-looting case - if(!loot_owner) - return; + // Setting access rights for group loot case Group * pGroup=loot_owner->GetGroup(); - if(!pGroup) - return; - for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) + if(!personal && pGroup) { - //fill the quest item map for every player in the recipient's group - Player* pl = itr->getSource(); - if(!pl) - continue; - uint32 plguid = pl->GetGUIDLow(); - QuestItemMap::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); - } + for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) + if(Player* pl = itr->getSource()) + FillNotNormalLootFor(pl); } + // ... for personal loot + else + FillNotNormalLootFor(loot_owner); +} + +void Loot::FillNotNormalLootFor(Player* pl) +{ + uint32 plguid = pl->GetGUIDLow(); + + QuestItemMap::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) @@ -640,6 +666,12 @@ LootItem* Loot::LootItemInSlot(uint32 lootSlot, Player* player, QuestItem **qite 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); @@ -653,12 +685,19 @@ ByteBuffer& operator<<(ByteBuffer& b, LootItem const& li) ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv) { + if (lv.permission == NONE_PERMISSION) + { + b << uint32(0); //gold + b << uint8(0); // item count + return b; // nothing output more + } + Loot &l = lv.loot; uint8 itemsShown = 0; //gold - b << uint32(lv.permission!=NONE_PERMISSION ? l.gold : 0); + b << uint32(l.gold); size_t count_pos = b.wpos(); // pos of item count byte b << uint8(0); // item count placeholder @@ -697,19 +736,21 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv) } break; } - case NONE_PERMISSION: default: return b; // nothing output more } - if (lv.qlist) + QuestItemMap const& lootPlayerQuestItems = l.GetPlayerQuestItems(); + QuestItemMap::const_iterator q_itr = lootPlayerQuestItems.find(lv.viewer->GetGUIDLow()); + if (q_itr != lootPlayerQuestItems.end()) { - for (QuestItemList::iterator qi = lv.qlist->begin() ; qi != lv.qlist->end(); ++qi) + QuestItemList *q_list = q_itr->second; + for (QuestItemList::iterator qi = q_list->begin() ; qi != q_list->end(); ++qi) { LootItem &item = l.quest_items[qi->index]; if (!qi->is_looted && !item.is_looted) { - b << uint8(l.items.size() + (qi - lv.qlist->begin())); + b << uint8(l.items.size() + (qi - q_list->begin())); b << item; b << uint8(0); // allow loot ++itemsShown; @@ -717,9 +758,12 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv) } } - if (lv.ffalist) + QuestItemMap const& lootPlayerFFAItems = l.GetPlayerFFAItems(); + QuestItemMap::const_iterator ffa_itr = lootPlayerFFAItems.find(lv.viewer->GetGUIDLow()); + if (ffa_itr != lootPlayerFFAItems.end()) { - for (QuestItemList::iterator fi = lv.ffalist->begin() ; fi != lv.ffalist->end(); ++fi) + QuestItemList *ffa_list = ffa_itr->second; + for (QuestItemList::iterator fi = ffa_list->begin() ; fi != ffa_list->end(); ++fi) { LootItem &item = l.items[fi->index]; if (!fi->is_looted && !item.is_looted) @@ -731,9 +775,12 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv) } } - if (lv.conditionallist) + QuestItemMap const& lootPlayerNonQuestNonFFAConditionalItems = l.GetPlayerNonQuestNonFFAConditionalItems(); + QuestItemMap::const_iterator nn_itr = lootPlayerNonQuestNonFFAConditionalItems.find(lv.viewer->GetGUIDLow()); + if (nn_itr != lootPlayerNonQuestNonFFAConditionalItems.end()) { - for (QuestItemList::iterator ci = lv.conditionallist->begin() ; ci != lv.conditionallist->end(); ++ci) + QuestItemList *conditional_list = nn_itr->second; + for (QuestItemList::iterator ci = conditional_list->begin() ; ci != conditional_list->end(); ++ci) { LootItem &item = l.items[ci->index]; if (!ci->is_looted && !item.is_looted) @@ -776,7 +823,6 @@ LootStoreItem const * LootTemplate::LootGroup::Roll() const if(ExplicitlyChanced[i].chance>=100.f) return &ExplicitlyChanced[i]; - ItemPrototype const *pProto = objmgr.GetItemPrototype(ExplicitlyChanced[i].itemid); Roll -= ExplicitlyChanced[i].chance; if (Roll < 0) return &ExplicitlyChanced[i]; @@ -900,7 +946,7 @@ void LootTemplate::AddEntry(LootStoreItem& item) } // Rolls for every item in the template and adds the rolled items the the loot -void LootTemplate::Process(Loot& loot, LootStore const& store, uint8 groupId) const +void LootTemplate::Process(Loot& loot, LootStore const& store, bool rate, uint8 groupId) const { if (groupId) // Group reference uses own processing of the group { @@ -914,7 +960,7 @@ void LootTemplate::Process(Loot& loot, LootStore const& store, uint8 groupId) co // Rolling non-grouped items for (LootStoreItemList::const_iterator i = Entries.begin() ; i != Entries.end() ; ++i ) { - if ( !i->Roll() ) + if (!i->Roll(rate)) continue; // Bad luck for the entry if (i->mincountOrRef < 0) // References processing @@ -925,7 +971,7 @@ void LootTemplate::Process(Loot& loot, LootStore const& store, uint8 groupId) co continue; // Error message already printed at loading stage for (uint32 loop=0; loop < i->maxcount; ++loop )// Ref multiplicator - Referenced->Process(loot, store, i->group); // Ref processing + Referenced->Process(loot, store, rate, i->group); } else // Plain entries (not a reference, not grouped) loot.AddItem(*i); // Chance is already checked, just add @@ -995,7 +1041,7 @@ bool LootTemplate::HasQuestDropForPlayer(LootTemplateMap const& store, Player co // Now checking groups for (LootGroups::const_iterator i = Groups.begin(); i != Groups.end(); ++i ) - if (i->HasQuestDrop()) + if (i->HasQuestDropForPlayer(player)) return true; return false; @@ -1137,6 +1183,29 @@ void LoadLootTemplates_Item() 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; @@ -1170,9 +1239,17 @@ void LoadLootTemplates_Prospecting() // 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); + { + 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); @@ -1186,8 +1263,17 @@ void LoadLootTemplates_QuestMail() // 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 + else + LootTemplates_QuestMail.ReportNotExistedId(itr->first); + */ + } // output error for any still listed (not referenced from appropriate table) ids LootTemplates_QuestMail.ReportUnusedIds(ids_set); @@ -1219,6 +1305,37 @@ void LoadLootTemplates_Skinning() 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) except with SPELL_ATTR_EX2_UNK14 (clams) + // 61756 (Northrend Inscription Research (FAST QA VERSION) for example + if ((spellInfo->Attributes & SPELL_ATTR_UNK5) || (spellInfo->AttributesEx2 & SPELL_ATTR_EX2_UNK14)) + LootTemplates_Spell.ReportNotExistedId(spell_id); + } + else + ids_set.erase(spell_id); + } + + // output error for any still listed (not referenced from appropriate table) ids + LootTemplates_QuestMail.ReportUnusedIds(ids_set); +} + void LoadLootTemplates_Reference() { LootIdSet ids_set; @@ -1229,6 +1346,7 @@ void LoadLootTemplates_Reference() LootTemplates_Fishing.CheckLootRefs(&ids_set); LootTemplates_Gameobject.CheckLootRefs(&ids_set); LootTemplates_Item.CheckLootRefs(&ids_set); + LootTemplates_Milling.CheckLootRefs(&ids_set); LootTemplates_Pickpocketing.CheckLootRefs(&ids_set); LootTemplates_Skinning.CheckLootRefs(&ids_set); LootTemplates_Disenchant.CheckLootRefs(&ids_set); |