void LoadLootTemplates_Prospecting() { TC_LOG_INFO("server.loading", "Loading prospecting loot templates..."); uint32 oldMSTime = getMSTime(); LootIdSet lootIdSet; uint32 count = LootTemplates_Prospecting.LoadAndCollectLootIds(lootIdSet); // remove real entries and check existence loot ItemTemplateContainer const* its = sObjectMgr->GetItemTemplateStore(); for (ItemTemplateContainer::const_iterator itr = its->begin(); itr != its->end(); ++itr) { if (!(itr->second.GetFlags() & ITEM_FLAG_IS_PROSPECTABLE)) continue; if (lootIdSet.find(itr->second.GetId()) != lootIdSet.end()) lootIdSet.erase(itr->second.GetId()); } // output error for any still listed (not referenced from appropriate table) ids LootTemplates_Prospecting.ReportUnusedIds(lootIdSet); if (count) TC_LOG_INFO("server.loading", ">> Loaded %u prospecting loot templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); else TC_LOG_ERROR("server.loading", ">> Loaded 0 prospecting loot templates. DB table `prospecting_loot_template` is empty"); }
void LootTemplate::LootGroup::CheckLootRefs(LootTemplateMap const& /*store*/, LootIdSet* ref_set) const { for (LootStoreItemList::const_iterator ieItr = ExplicitlyChanced.begin(); ieItr != ExplicitlyChanced.end(); ++ieItr) { LootStoreItem* item = *ieItr; if (item->reference > 0) { if (!LootTemplates_Reference.GetLootFor(item->reference)) LootTemplates_Reference.ReportNonExistingId(item->reference, "Reference", item->itemid); else if (ref_set) ref_set->erase(item->reference); } } for (LootStoreItemList::const_iterator ieItr = EqualChanced.begin(); ieItr != EqualChanced.end(); ++ieItr) { LootStoreItem* item = *ieItr; if (item->reference > 0) { if (!LootTemplates_Reference.GetLootFor(item->reference)) LootTemplates_Reference.ReportNonExistingId(item->reference, "Reference", item->itemid); else if (ref_set) ref_set->erase(item->reference); } } }
void LoadLootTemplates_Pickpocketing() { TC_LOG_INFO("server.loading", "Loading pickpocketing loot templates..."); uint32 oldMSTime = getMSTime(); LootIdSet lootIdSet, lootIdSetUsed; uint32 count = LootTemplates_Pickpocketing.LoadAndCollectLootIds(lootIdSet); // Remove real entries and check loot existence CreatureTemplateContainer const* ctc = sObjectMgr->GetCreatureTemplates(); for (CreatureTemplateContainer::const_iterator itr = ctc->begin(); itr != ctc->end(); ++itr) { if (uint32 lootid = itr->second.pickpocketLootId) { if (lootIdSet.find(lootid) == lootIdSet.end()) LootTemplates_Pickpocketing.ReportNonExistingId(lootid, "Creature", itr->second.Entry); else lootIdSetUsed.insert(lootid); } } for (LootIdSet::const_iterator itr = lootIdSetUsed.begin(); itr != lootIdSetUsed.end(); ++itr) lootIdSet.erase(*itr); // output error for any still listed (not referenced from appropriate table) ids LootTemplates_Pickpocketing.ReportUnusedIds(lootIdSet); if (count) TC_LOG_INFO("server.loading", ">> Loaded %u pickpocketing loot templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); else TC_LOG_ERROR("server.loading", ">> Loaded 0 pickpocketing loot templates. DB table `pickpocketing_loot_template` is empty"); }
void LoadLootTemplates_Disenchant() { TC_LOG_INFO("server.loading", "Loading disenchanting loot templates..."); uint32 oldMSTime = getMSTime(); LootIdSet lootIdSet, lootIdSetUsed; uint32 count = LootTemplates_Disenchant.LoadAndCollectLootIds(lootIdSet); for (uint32 i = 0; i < sItemDisenchantLootStore.GetNumRows(); ++i) { ItemDisenchantLootEntry const* disenchant = sItemDisenchantLootStore.LookupEntry(i); if (!disenchant) continue; uint32 lootid = i; if (lootIdSet.find(lootid) == lootIdSet.end()) LootTemplates_Disenchant.ReportNonExistingId(lootid); else lootIdSetUsed.insert(lootid); } for (LootIdSet::const_iterator itr = lootIdSetUsed.begin(); itr != lootIdSetUsed.end(); ++itr) lootIdSet.erase(*itr); // output error for any still listed (not referenced from appropriate table) ids LootTemplates_Disenchant.ReportUnusedIds(lootIdSet); if (count) TC_LOG_INFO("server.loading", ">> Loaded %u disenchanting loot templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); else TC_LOG_ERROR("server.loading", ">> Loaded 0 disenchanting loot templates. DB table `disenchant_loot_template` is empty"); }
void Creature::setDeathState(DeathState s) { if(s == JUST_DIED) { m_deathTimer = m_corpseDelay*1000; // always save boss respawn time at death to prevent crash cheating if(sWorld.getConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATLY) || isWorldBoss()) SaveRespawnTime(); if(!IsStopped()) StopMoving(); } Unit::setDeathState(s); if(s == JUST_DIED) { SetUInt32Value(UNIT_NPC_FLAGS, 0); if(!isPet() && GetCreatureInfo()->SkinLootId) { LootStore skinStore = LootTemplates_Skinning; LootStore::iterator tab = skinStore.find(GetCreatureInfo()->SkinLootId); if ( tab != skinStore.end() ) SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); } Unit::setDeathState(CORPSE); } }
void FillLoot(Loot *loot, uint32 loot_id, LootStore& store) { loot->clear(); LootStore::iterator tab = store.find(loot_id); if (tab == store.end()) { sLog.outErrorDb("Loot id #%u used in `creature_template` or `gameobject` or fishing but it doesn't have records in appropriate *_loot_template table.",loot_id); return; } loot->items.reserve(min(tab->second.size(),MAX_NR_LOOT_ITEMS)); loot->quest_items.reserve(min(tab->second.size(),MAX_NR_QUEST_ITEMS)); // fill loot with all normal and quest items that have a chance HasChance hasChance(&store); HasQuestChance hasQuestChance; for(LootStoreItemList::iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter) { // There are stats of count variations for 100% drop - so urand used if ( loot->quest_items.size() < MAX_NR_QUEST_ITEMS && hasQuestChance(*item_iter) ) loot->quest_items.push_back(LootItem(*item_iter, urand(item_iter->mincount, item_iter->maxcount),Item::GenerateItemRandomPropertyId(item_iter->itemid))); else if ( loot->items.size() < MAX_NR_LOOT_ITEMS ) { LootStoreItem* LootedItem = hasChance(*item_iter); if ( LootedItem ) loot->items.push_back( LootItem(*LootedItem, urand(LootedItem->mincount, LootedItem->maxcount), Item::GenerateItemRandomPropertyId(LootedItem->itemid))); } } loot->unlootedCount = loot->items.size(); }
void LootTemplate::CheckLootRefs(LootTemplateMap const& store, LootIdSet* ref_set) const { for (LootStoreItemList::const_iterator ieItr = Entries.begin(); ieItr != Entries.end(); ++ieItr) { LootStoreItem* item = *ieItr; if (item->reference > 0) { if (!LootTemplates_Reference.GetLootFor(item->reference)) LootTemplates_Reference.ReportNonExistingId(item->reference, "Reference", item->itemid); else if (ref_set) ref_set->erase(item->reference); } } for (LootGroups::const_iterator grItr = Groups.begin(); grItr != Groups.end(); ++grItr) if (LootGroup* group = *grItr) group->CheckLootRefs(store, ref_set); }
LootStoreItem* operator() ( LootStoreItem& itm ) { // Quest loot handled separately if (itm.questChanceOrGroup > 0) return NULL; // Non-grouped loot if (itm.questChanceOrGroup == 0) { if ( itm.chance > 0 && roll_chance_f(itm.chance * sWorld.getRate(RATE_DROP_ITEMS)) ) return &itm; return NULL; } // Grouped loot int32 GroupId = itm.GetGroupId(); if (GroupId < 0 || GroupId >= MaxLootGroups) { sLog.outErrorDb("HasChance: wrong loot group in DB (%i) for item %u", itm.questChanceOrGroup,itm.itemid); return NULL; } if (itm.chance >= 0) { // Group of current loot - check for item chance in the group if (CumulativeChance[GroupId] == 0.0) RolledChance[GroupId] = rand_chance(); if (CumulativeChance[GroupId] >= RolledChance[GroupId]) // An item from the group already accepted return NULL; CumulativeChance[GroupId] += itm.chance; if (CumulativeChance[GroupId] >= RolledChance[GroupId]) return &itm; return NULL; } // Reference to a group of another loot int LootId = -int(itm.chance); float Chance = rand_chance(); float CumulChance = 0.0; LootStore::iterator tab = m_store->find(LootId); for(LootStoreItemList::iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter) { if ( item_iter->GetGroupId() == GroupId && item_iter->chance > 0 ) { CumulChance += item_iter->chance; if ( CumulChance >= Chance ) return &*item_iter; } } return NULL; }
void UnloadLoot() { LootTemplates_Creature.clear(); LootTemplates_Fishing.clear(); LootTemplates_Gameobject.clear(); LootTemplates_Item.clear(); LootTemplates_Pickpocketing.clear(); LootTemplates_Skinning.clear(); LootTemplates_Disenchant.clear(); }
void LoadLootTemplates_Spell() { // TODO: change this to use MiscValue from spell effect as id instead of spell id TC_LOG_INFO("server.loading", "Loading spell loot templates..."); uint32 oldMSTime = getMSTime(); LootIdSet lootIdSet; uint32 count = LootTemplates_Spell.LoadAndCollectLootIds(lootIdSet); // remove real entries and check existence loot for (uint32 spell_id = 1; spell_id < sSpellMgr->GetSpellInfoStoreSize(); ++spell_id) { SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell_id); if (!spellInfo) continue; // possible cases if (!spellInfo->IsLootCrafting()) continue; if (lootIdSet.find(spell_id) == lootIdSet.end()) { // not report about not trainable spells (optionally supported by DB) // ignore 61756 (Northrend Inscription Research (FAST QA VERSION) for example if (!spellInfo->HasAttribute(SPELL_ATTR0_NOT_SHAPESHIFT) || (spellInfo->HasAttribute(SPELL_ATTR0_TRADESPELL))) LootTemplates_Spell.ReportNonExistingId(spell_id, "Spell", spellInfo->Id); } else lootIdSet.erase(spell_id); } // output error for any still listed (not referenced from appropriate table) ids LootTemplates_Spell.ReportUnusedIds(lootIdSet); if (count) TC_LOG_INFO("server.loading", ">> Loaded %u spell loot templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); else TC_LOG_ERROR("server.loading", ">> Loaded 0 spell loot templates. DB table `spell_loot_template` is empty"); }
void LoadLootTemplates_Fishing() { TC_LOG_INFO("server.loading", "Loading fishing loot templates..."); uint32 oldMSTime = getMSTime(); LootIdSet lootIdSet; uint32 count = LootTemplates_Fishing.LoadAndCollectLootIds(lootIdSet); // remove real entries and check existence loot for (AreaTableEntry const* areaTable : sAreaTableStore) if (lootIdSet.find(areaTable->ID) != lootIdSet.end()) lootIdSet.erase(areaTable->ID); // output error for any still listed (not referenced from appropriate table) ids LootTemplates_Fishing.ReportUnusedIds(lootIdSet); if (count) TC_LOG_INFO("server.loading", ">> Loaded %u fishing loot templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); else TC_LOG_ERROR("server.loading", ">> Loaded 0 fishing loot templates. DB table `fishing_loot_template` is empty"); }
void LoadLootTemplates_Mail() { TC_LOG_INFO("server.loading", "Loading mail loot templates..."); uint32 oldMSTime = getMSTime(); LootIdSet lootIdSet; uint32 count = LootTemplates_Mail.LoadAndCollectLootIds(lootIdSet); // remove real entries and check existence loot for (uint32 i = 1; i < sMailTemplateStore.GetNumRows(); ++i) if (sMailTemplateStore.LookupEntry(i)) if (lootIdSet.find(i) != lootIdSet.end()) lootIdSet.erase(i); // output error for any still listed (not referenced from appropriate table) ids LootTemplates_Mail.ReportUnusedIds(lootIdSet); if (count) TC_LOG_INFO("server.loading", ">> Loaded %u mail loot templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); else TC_LOG_ERROR("server.loading", ">> Loaded 0 mail loot templates. DB table `mail_loot_template` is empty"); }
// Checks correctness of values bool LootStoreItem::IsValid(LootStore const& store, uint32 entry) const { if (mincount == 0) { TC_LOG_ERROR("sql.sql", "Table '%s' Entry %d Item %d: wrong MinCount (%d) - skipped", store.GetName(), entry, itemid, mincount); return false; } if (reference == 0) // item (quest or non-quest) entry, maybe grouped { ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemid); if (!proto) { TC_LOG_ERROR("sql.sql", "Table '%s' Entry %d Item %d: item entry not listed in `item_template` - skipped", store.GetName(), entry, itemid); return false; } if (chance == 0 && groupid == 0) // Zero chance is allowed for grouped entries only { TC_LOG_ERROR("sql.sql", "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 { TC_LOG_ERROR("sql.sql", "Table '%s' Entry %d Item %d: low chance (%f) - skipped", store.GetName(), entry, itemid, chance); return false; } if (maxcount < mincount) // wrong max count { TC_LOG_ERROR("sql.sql", "Table '%s' Entry %d Item %d: MaxCount (%u) less that MinCount (%i) - skipped", store.GetName(), entry, itemid, int32(maxcount), mincount); return false; } } else // if reference loot { if (needs_quest) TC_LOG_ERROR("sql.sql", "Table '%s' Entry %d Item %d: quest required will be ignored", store.GetName(), entry, itemid); else if (chance == 0) // no chance for the reference { TC_LOG_ERROR("sql.sql", "Table '%s' Entry %d Item %d: zero chance is specified for a reference, skipped", store.GetName(), entry, itemid); return false; } } return true; // Referenced template existence is checked at whole store level }
// Rolls for every item in the template and adds the rolled items the the loot void LootTemplate::Process(Loot& loot, bool rate, uint16 lootMode, uint8 groupId) const { if (groupId) // Group reference uses own processing of the group { if (groupId > Groups.size()) return; // Error message already printed at loading stage if (!Groups[groupId - 1]) return; Groups[groupId - 1]->Process(loot, lootMode); return; } // Rolling non-grouped items for (LootStoreItemList::const_iterator i = Entries.begin(); i != Entries.end(); ++i) { LootStoreItem* item = *i; if (!(item->lootmode & lootMode)) // Do not add if mode mismatch continue; if (!item->Roll(rate)) continue; // Bad luck for the entry if (item->reference > 0) // References processing { LootTemplate const* Referenced = LootTemplates_Reference.GetLootFor(item->reference); if (!Referenced) continue; // Error message already printed at loading stage uint32 maxcount = uint32(float(item->maxcount) * sWorld->getRate(RATE_DROP_ITEM_REFERENCED_AMOUNT)); for (uint32 loop = 0; loop < maxcount; ++loop) // Ref multiplicator Referenced->Process(loot, rate, lootMode, item->groupid); } else // Plain entries (not a reference, not grouped) loot.AddItem(*item); // Chance is already checked, just add } // Now processing groups for (LootGroups::const_iterator i = Groups.begin(); i != Groups.end(); ++i) if (LootGroup* group = *i) group->Process(loot, lootMode); }
void LootTemplate::LootGroup::Verify(LootStore const& lootstore, uint32 id, uint8 group_id) const { float chance = RawTotalChance(); if (chance > 101.0f) /// @todo replace with 100% when DBs will be ready TC_LOG_ERROR("sql.sql", "Table '%s' entry %u group %d has total chance > 100%% (%f)", lootstore.GetName(), id, group_id, chance); if (chance >= 100.0f && !EqualChanced.empty()) TC_LOG_ERROR("sql.sql", "Table '%s' entry %u group %d has items with chance=0%% but group total chance >= 100%% (%f)", lootstore.GetName(), id, group_id, chance); }
void LoadLootTemplates_Reference() { TC_LOG_INFO("server.loading", "Loading reference loot templates..."); uint32 oldMSTime = getMSTime(); LootIdSet lootIdSet; LootTemplates_Reference.LoadAndCollectLootIds(lootIdSet); // check references and remove used LootTemplates_Creature.CheckLootRefs(&lootIdSet); LootTemplates_Fishing.CheckLootRefs(&lootIdSet); LootTemplates_Gameobject.CheckLootRefs(&lootIdSet); LootTemplates_Item.CheckLootRefs(&lootIdSet); LootTemplates_Milling.CheckLootRefs(&lootIdSet); LootTemplates_Pickpocketing.CheckLootRefs(&lootIdSet); LootTemplates_Skinning.CheckLootRefs(&lootIdSet); LootTemplates_Disenchant.CheckLootRefs(&lootIdSet); LootTemplates_Prospecting.CheckLootRefs(&lootIdSet); LootTemplates_Mail.CheckLootRefs(&lootIdSet); LootTemplates_Reference.CheckLootRefs(&lootIdSet); // output error for any still listed ids (not referenced from any loot table) LootTemplates_Reference.ReportUnusedIds(lootIdSet); TC_LOG_INFO("server.loading", ">> Loaded refence loot templates in %u ms", GetMSTimeDiffToNow(oldMSTime)); }