diff options
Diffstat (limited to 'src/server/game/Globals/ObjectMgr.cpp')
-rw-r--r-- | src/server/game/Globals/ObjectMgr.cpp | 1995 |
1 files changed, 1058 insertions, 937 deletions
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index effd2057c68..3b0cb8b736d 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -23,6 +23,8 @@ #include "Chat.h" #include "Common.h" #include "DatabaseEnv.h" +#include "DB2Structure.h" +#include "DB2Stores.h" #include "DisableMgr.h" #include "GameEventMgr.h" #include "GossipDef.h" @@ -171,7 +173,9 @@ LanguageDesc lang_description[LANGUAGES_COUNT] = { LANG_DRAENEI, 29932, SKILL_LANG_DRAENEI }, { LANG_ZOMBIE, 0, 0 }, { LANG_GNOMISH_BINARY, 0, 0 }, - { LANG_GOBLIN_BINARY, 0, 0 } + { LANG_GOBLIN_BINARY, 0, 0 }, + { LANG_WORGEN, 69270, SKILL_LANG_WORGEN }, + { LANG_GOBLIN, 69269, SKILL_LANG_GOBLIN } }; LanguageDesc const* GetLanguageDescByID(uint32 lang) @@ -226,6 +230,7 @@ ObjectMgr::ObjectMgr(): _itemTextId(1), _mailId(1), _hiPetNumber(1), + _voidItemId(1), _hiCharGuid(1), _hiCreatureGuid(1), _hiPetGuid(1), @@ -237,11 +242,8 @@ ObjectMgr::ObjectMgr(): _hiMoTransGuid(1) { for (uint8 i = 0; i < MAX_CLASSES; ++i) - { - _playerClassInfo[i] = NULL; for (uint8 j = 0; j < MAX_RACES; ++j) _playerInfo[j][i] = NULL; - } } ObjectMgr::~ObjectMgr() @@ -252,14 +254,6 @@ ObjectMgr::~ObjectMgr() for (PetLevelInfoContainer::iterator i = _petInfoStore.begin(); i != _petInfoStore.end(); ++i) delete[] i->second; - // free only if loaded - for (int class_ = 0; class_ < MAX_CLASSES; ++class_) - { - if (_playerClassInfo[class_]) - delete[] _playerClassInfo[class_]->levelInfo; - delete _playerClassInfo[class_]; - } - for (int race = 0; race < MAX_RACES; ++race) { for (int class_ = 0; class_ < MAX_CLASSES; ++class_) @@ -274,6 +268,7 @@ ObjectMgr::~ObjectMgr() itr->second.Clear(); _cacheTrainerSpellStore.clear(); + _graveyardOrientations.clear(); for (DungeonEncounterContainer::iterator itr =_dungeonEncounterStore.begin(); itr != _dungeonEncounterStore.end(); ++itr) for (DungeonEncounterList::iterator encounterItr = itr->second.begin(); encounterItr != itr->second.end(); ++encounterItr) @@ -294,6 +289,34 @@ void ObjectMgr::AddLocaleString(std::string const& s, LocaleConstant locale, Str } } +void ObjectMgr::LoadGraveyardOrientations() +{ + uint32 oldMSTime = getMSTime(); + + _graveyardOrientations.clear(); + + QueryResult result = WorldDatabase.Query("SELECT id, orientation FROM graveyard_orientation"); + + if (!result) + return; + + do + { + Field* fields = result->Fetch(); + + uint32 id = fields[0].GetUInt32(); + if (!sWorldSafeLocsStore.LookupEntry(id)) + { + sLog->outError(LOG_FILTER_SERVER_LOADING, "Graveyard %u referenced in graveyard_orientation doesn't exist.", id); + continue; + } + _graveyardOrientations[id] = fields[1].GetFloat(); + + } while (result->NextRow()); + + sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %lu graveyard orientations in %u ms", (unsigned long)_graveyardOrientations.size(), GetMSTimeDiffToNow(oldMSTime)); +} + void ObjectMgr::LoadCreatureLocales() { uint32 oldMSTime = getMSTime(); @@ -392,19 +415,19 @@ void ObjectMgr::LoadCreatureTemplates() // 0 1 2 3 4 5 6 7 8 QueryResult result = WorldDatabase.Query("SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, modelid1, modelid2, modelid3, " - // 9 10 11 12 13 14 15 16 17 18 19 20 21 - "modelid4, name, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, faction_A, faction_H, npcflag, speed_walk, speed_run, " - // 22 23 24 25 26 27 28 29 30 31 32 33 - "scale, rank, mindmg, maxdmg, dmgschool, attackpower, dmg_multiplier, baseattacktime, rangeattacktime, unit_class, unit_flags, unit_flags2, " - // 34 35 36 37 38 39 40 41 42 43 - "dynamicflags, family, trainer_type, trainer_spell, trainer_class, trainer_race, minrangedmg, maxrangedmg, rangedattackpower, type, " - // 44 45 46 47 48 49 50 51 52 53 54 - "type_flags, lootid, pickpocketloot, skinloot, resistance1, resistance2, resistance3, resistance4, resistance5, resistance6, spell1, " - // 55 56 57 58 59 60 61 62 63 64 65 66 67 - "spell2, spell3, spell4, spell5, spell6, spell7, spell8, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, " - // 68 69 70 71 72 73 74 75 76 77 78 - "InhabitType, HoverHeight, Health_mod, Mana_mod, Armor_mod, RacialLeader, questItem1, questItem2, questItem3, questItem4, questItem5, " - // 79 80 81 82 83 84 + // 9 10 11 12 13 14 15 16 17 18 19 20 21 + "modelid4, name, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, exp_unk, faction_A, faction_H, npcflag, speed_walk, " + // 22 23 24 25 26 27 28 29 30 31 32 33 34 + "speed_run, scale, rank, mindmg, maxdmg, dmgschool, attackpower, dmg_multiplier, baseattacktime, rangeattacktime, unit_class, unit_flags, unit_flags2, " + // 35 36 37 38 39 40 41 42 43 + "dynamicflags, family, trainer_type, trainer_class, trainer_race, minrangedmg, maxrangedmg, rangedattackpower, type, " + // 44 45 46 47 48 49 50 51 52 53 54 + "type_flags, type_flags2, lootid, pickpocketloot, skinloot, resistance1, resistance2, resistance3, resistance4, resistance5, resistance6, " + // 55 56 57 58 59 60 61 62 63 64 65 66 67 68 + "spell1, spell2, spell3, spell4, spell5, spell6, spell7, spell8, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, " + // 69 70 71 72 73 74 75 76 77 78 79 80 + "InhabitType, HoverHeight, Health_mod, Mana_mod, Mana_mod_extra, Armor_mod, RacialLeader, questItem1, questItem2, questItem3, questItem4, questItem5, " + // 81 82 83 84 85 86 " questItem6, movementId, RegenHealth, mechanic_immune_mask, flags_extra, ScriptName " "FROM creature_template;"); @@ -444,27 +467,27 @@ void ObjectMgr::LoadCreatureTemplates() creatureTemplate.minlevel = fields[14].GetUInt8(); creatureTemplate.maxlevel = fields[15].GetUInt8(); creatureTemplate.expansion = uint32(fields[16].GetInt16()); - creatureTemplate.faction_A = uint32(fields[17].GetUInt16()); - creatureTemplate.faction_H = uint32(fields[18].GetUInt16()); - creatureTemplate.npcflag = fields[19].GetUInt32(); - creatureTemplate.speed_walk = fields[20].GetFloat(); - creatureTemplate.speed_run = fields[21].GetFloat(); - creatureTemplate.scale = fields[22].GetFloat(); - creatureTemplate.rank = uint32(fields[23].GetUInt8()); - creatureTemplate.mindmg = fields[24].GetFloat(); - creatureTemplate.maxdmg = fields[25].GetFloat(); - creatureTemplate.dmgschool = uint32(fields[26].GetInt8()); - creatureTemplate.attackpower = fields[27].GetUInt32(); - creatureTemplate.dmg_multiplier = fields[28].GetFloat(); - creatureTemplate.baseattacktime = fields[29].GetUInt32(); - creatureTemplate.rangeattacktime = fields[30].GetUInt32(); - creatureTemplate.unit_class = uint32(fields[31].GetUInt8()); - creatureTemplate.unit_flags = fields[32].GetUInt32(); - creatureTemplate.unit_flags2 = fields[33].GetUInt32(); - creatureTemplate.dynamicflags = fields[34].GetUInt32(); - creatureTemplate.family = uint32(fields[35].GetUInt8()); - creatureTemplate.trainer_type = uint32(fields[36].GetUInt8()); - creatureTemplate.trainer_spell = fields[37].GetUInt32(); + creatureTemplate.expansionUnknown = uint32(fields[17].GetUInt16()); + creatureTemplate.faction_A = uint32(fields[18].GetUInt16()); + creatureTemplate.faction_H = uint32(fields[19].GetUInt16()); + creatureTemplate.npcflag = fields[20].GetUInt32(); + creatureTemplate.speed_walk = fields[21].GetFloat(); + creatureTemplate.speed_run = fields[22].GetFloat(); + creatureTemplate.scale = fields[23].GetFloat(); + creatureTemplate.rank = uint32(fields[24].GetUInt8()); + creatureTemplate.mindmg = fields[25].GetFloat(); + creatureTemplate.maxdmg = fields[26].GetFloat(); + creatureTemplate.dmgschool = uint32(fields[27].GetInt8()); + creatureTemplate.attackpower = fields[28].GetUInt32(); + creatureTemplate.dmg_multiplier = fields[29].GetFloat(); + creatureTemplate.baseattacktime = fields[30].GetUInt32(); + creatureTemplate.rangeattacktime = fields[31].GetUInt32(); + creatureTemplate.unit_class = uint32(fields[32].GetUInt8()); + creatureTemplate.unit_flags = fields[33].GetUInt32(); + creatureTemplate.unit_flags2 = fields[34].GetUInt32(); + creatureTemplate.dynamicflags = fields[35].GetUInt32(); + creatureTemplate.family = uint32(fields[36].GetUInt8()); + creatureTemplate.trainer_type = uint32(fields[37].GetUInt8()); creatureTemplate.trainer_class = uint32(fields[38].GetUInt8()); creatureTemplate.trainer_race = uint32(fields[39].GetUInt8()); creatureTemplate.minrangedmg = fields[40].GetFloat(); @@ -472,37 +495,39 @@ void ObjectMgr::LoadCreatureTemplates() creatureTemplate.rangedattackpower = uint32(fields[42].GetUInt16()); creatureTemplate.type = uint32(fields[43].GetUInt8()); creatureTemplate.type_flags = fields[44].GetUInt32(); - creatureTemplate.lootid = fields[45].GetUInt32(); - creatureTemplate.pickpocketLootId = fields[46].GetUInt32(); - creatureTemplate.SkinLootId = fields[47].GetUInt32(); + creatureTemplate.type_flags2 = fields[45].GetUInt32(); + creatureTemplate.lootid = fields[46].GetUInt32(); + creatureTemplate.pickpocketLootId = fields[47].GetUInt32(); + creatureTemplate.SkinLootId = fields[48].GetUInt32(); for (uint8 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i) - creatureTemplate.resistance[i] = fields[48 + i -1].GetInt16(); + creatureTemplate.resistance[i] = fields[49 + i - 1].GetInt16(); for (uint8 i = 0; i < CREATURE_MAX_SPELLS; ++i) - creatureTemplate.spells[i] = fields[54 + i].GetUInt32(); - - creatureTemplate.PetSpellDataId = fields[62].GetUInt32(); - creatureTemplate.VehicleId = fields[63].GetUInt32(); - creatureTemplate.mingold = fields[64].GetUInt32(); - creatureTemplate.maxgold = fields[65].GetUInt32(); - creatureTemplate.AIName = fields[66].GetString(); - creatureTemplate.MovementType = uint32(fields[67].GetUInt8()); - creatureTemplate.InhabitType = uint32(fields[68].GetUInt8()); - creatureTemplate.HoverHeight = fields[69].GetFloat(); - creatureTemplate.ModHealth = fields[70].GetFloat(); - creatureTemplate.ModMana = fields[71].GetFloat(); - creatureTemplate.ModArmor = fields[72].GetFloat(); - creatureTemplate.RacialLeader = fields[73].GetBool(); + creatureTemplate.spells[i] = fields[55 + i].GetUInt32(); + + creatureTemplate.PetSpellDataId = fields[63].GetUInt32(); + creatureTemplate.VehicleId = fields[64].GetUInt32(); + creatureTemplate.mingold = fields[65].GetUInt32(); + creatureTemplate.maxgold = fields[66].GetUInt32(); + creatureTemplate.AIName = fields[67].GetString(); + creatureTemplate.MovementType = uint32(fields[68].GetUInt8()); + creatureTemplate.InhabitType = uint32(fields[69].GetUInt8()); + creatureTemplate.HoverHeight = fields[70].GetFloat(); + creatureTemplate.ModHealth = fields[71].GetFloat(); + creatureTemplate.ModMana = fields[72].GetFloat(); + creatureTemplate.ModManaExtra = fields[73].GetFloat(); + creatureTemplate.ModArmor = fields[74].GetFloat(); + creatureTemplate.RacialLeader = fields[75].GetBool(); for (uint8 i = 0; i < MAX_CREATURE_QUEST_ITEMS; ++i) - creatureTemplate.questItems[i] = fields[74 + i].GetUInt32(); + creatureTemplate.questItems[i] = fields[76 + i].GetUInt32(); - creatureTemplate.movementId = fields[80].GetUInt32(); - creatureTemplate.RegenHealth = fields[81].GetBool(); - creatureTemplate.MechanicImmuneMask = fields[82].GetUInt32(); - creatureTemplate.flags_extra = fields[83].GetUInt32(); - creatureTemplate.ScriptID = GetScriptId(fields[84].GetCString()); + creatureTemplate.movementId = fields[82].GetUInt32(); + creatureTemplate.RegenHealth = fields[83].GetBool(); + creatureTemplate.MechanicImmuneMask = fields[84].GetUInt32(); + creatureTemplate.flags_extra = fields[85].GetUInt32(); + creatureTemplate.ScriptID = GetScriptId(fields[86].GetCString()); ++count; } @@ -664,12 +689,6 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo) continue; } - if (cInfo->trainer_spell != difficultyInfo->trainer_spell) - { - sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) has different `trainer_spell` in difficulty %u mode (Entry: %u).", cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff]); - continue; - } - if (!difficultyInfo->AIName.empty()) { sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) lists difficulty %u mode entry %u with `AIName` filled in. `AIName` of difficulty 0 mode creature is always used instead.", @@ -866,12 +885,18 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo) const_cast<CreatureTemplate*>(cInfo)->scale = 1.0f; } - if (cInfo->expansion > (MAX_CREATURE_BASE_HP - 1)) + if (cInfo->expansion > MAX_CREATURE_BASE_HP) { - sLog->outError(LOG_FILTER_SQL, "Table `creature_template` lists creature (Entry: %u) with expansion %u. Ignored and set to 0.", cInfo->Entry, cInfo->expansion); + sLog->outError(LOG_FILTER_SQL, "Table `creature_template` lists creature (Entry: %u) with `exp` %u. Ignored and set to 0.", cInfo->Entry, cInfo->expansion); const_cast<CreatureTemplate*>(cInfo)->expansion = 0; } + if (cInfo->expansionUnknown > MAX_CREATURE_BASE_HP) + { + sLog->outError(LOG_FILTER_SQL, "Table `creature_template` lists creature (Entry: %u) with `exp_unk` %u. Ignored and set to 0.", cInfo->Entry, cInfo->expansionUnknown); + const_cast<CreatureTemplate*>(cInfo)->expansionUnknown = 0; + } + if (uint32 badFlags = (cInfo->flags_extra & ~CREATURE_FLAG_EXTRA_DB_ALLOWED)) { sLog->outError(LOG_FILTER_SQL, "Table `creature_template` lists creature (Entry: %u) with disallowed `flags_extra` %u, removing incorrect flag.", cInfo->Entry, badFlags); @@ -1582,7 +1607,7 @@ void ObjectMgr::LoadCreatures() } if (data.spawnMask & ~spawnMasks[data.mapid]) - sLog->outError(LOG_FILTER_SQL, "Table `creature` have creature (GUID: %u) that have wrong spawn mask %u including not supported difficulty modes for map (Id: %u).", guid, data.spawnMask, data.mapid); + sLog->outError(LOG_FILTER_SQL, "Table `creature` have creature (GUID: %u) that have wrong spawn mask %u including not supported difficulty modes for map (Id: %u) spawnMasks[data.mapid]: %u.", guid, data.spawnMask, data.mapid, spawnMasks[data.mapid]); bool ok = true; for (uint32 diff = 0; diff < MAX_DIFFICULTY - 1 && ok; ++diff) @@ -2123,582 +2148,504 @@ void ObjectMgr::LoadItemLocales() sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %lu Item locale strings in %u ms", (unsigned long)_itemLocaleStore.size(), GetMSTimeDiffToNow(oldMSTime)); } -void ObjectMgr::LoadItemTemplates() +void FillItemDamageFields(float* minDamage, float* maxDamage, float* dps, uint32 itemLevel, uint32 itemClass, uint32 itemSubClass, uint32 quality, uint32 delay, float statScalingFactor, uint32 inventoryType, uint32 flags2) { - uint32 oldMSTime = getMSTime(); - - // 0 1 2 3 4 5 6 7 8 9 10 11 12 - QueryResult result = WorldDatabase.Query("SELECT entry, class, subclass, SoundOverrideSubclass, name, displayid, Quality, Flags, FlagsExtra, BuyCount, BuyPrice, SellPrice, InventoryType, " - // 13 14 15 16 17 18 19 20 - "AllowableClass, AllowableRace, ItemLevel, RequiredLevel, RequiredSkill, RequiredSkillRank, requiredspell, requiredhonorrank, " - // 21 22 23 24 25 26 27 28 - "RequiredCityRank, RequiredReputationFaction, RequiredReputationRank, maxcount, stackable, ContainerSlots, StatsCount, stat_type1, " - // 29 30 31 32 33 34 35 36 37 38 - "stat_value1, stat_type2, stat_value2, stat_type3, stat_value3, stat_type4, stat_value4, stat_type5, stat_value5, stat_type6, " - // 39 40 41 42 43 44 45 46 47 - "stat_value6, stat_type7, stat_value7, stat_type8, stat_value8, stat_type9, stat_value9, stat_type10, stat_value10, " - // 48 49 50 51 52 53 54 55 56 57 58 - "ScalingStatDistribution, ScalingStatValue, dmg_min1, dmg_max1, dmg_type1, dmg_min2, dmg_max2, dmg_type2, armor, holy_res, fire_res, " - // 59 60 61 62 63 64 65 66 67 68 - "nature_res, frost_res, shadow_res, arcane_res, delay, ammo_type, RangedModRange, spellid_1, spelltrigger_1, spellcharges_1, " - // 69 70 71 72 73 74 75 - "spellppmRate_1, spellcooldown_1, spellcategory_1, spellcategorycooldown_1, spellid_2, spelltrigger_2, spellcharges_2, " - // 76 77 78 79 80 81 82 - "spellppmRate_2, spellcooldown_2, spellcategory_2, spellcategorycooldown_2, spellid_3, spelltrigger_3, spellcharges_3, " - // 83 84 85 86 87 88 89 - "spellppmRate_3, spellcooldown_3, spellcategory_3, spellcategorycooldown_3, spellid_4, spelltrigger_4, spellcharges_4, " - // 90 91 92 93 94 95 96 - "spellppmRate_4, spellcooldown_4, spellcategory_4, spellcategorycooldown_4, spellid_5, spelltrigger_5, spellcharges_5, " - // 97 98 99 100 101 102 103 104 105 - "spellppmRate_5, spellcooldown_5, spellcategory_5, spellcategorycooldown_5, bonding, description, PageText, LanguageID, PageMaterial, " - // 106 107 108 109 110 111 112 113 114 115 116 117 - "startquest, lockid, Material, sheath, RandomProperty, RandomSuffix, block, itemset, MaxDurability, area, Map, BagFamily, " - // 118 119 120 121 122 123 124 125 - "TotemCategory, socketColor_1, socketContent_1, socketColor_2, socketContent_2, socketColor_3, socketContent_3, socketBonus, " - // 126 127 128 129 130 131 132 133 - "GemProperties, RequiredDisenchantSkill, ArmorDamageModifier, duration, ItemLimitCategory, HolidayId, ScriptName, DisenchantID, " - // 134 135 136 - "FoodType, minMoneyLoot, maxMoneyLoot, flagsCustom FROM item_template"); - - if (!result) - { - sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 item templates. DB table `item_template` is empty."); + *minDamage = *maxDamage = *dps = 0.0f; + if (itemClass != ITEM_CLASS_WEAPON || quality > ITEM_QUALITY_ARTIFACT) return; - } - _itemTemplateStore.rehash(result->GetRowCount()); - uint32 count = 0; - bool enforceDBCAttributes = sWorld->getBoolConfig(CONFIG_DBC_ENFORCE_ITEM_ATTRIBUTES); + DBCStorage<ItemDamageEntry>* store = NULL; + // get the right store here + if (inventoryType > 0xD + 13) + return; - do + switch (inventoryType) { - Field* fields = result->Fetch(); - - uint32 entry = fields[0].GetUInt32(); - - ItemTemplate& itemTemplate = _itemTemplateStore[entry]; - - itemTemplate.ItemId = entry; - itemTemplate.Class = uint32(fields[1].GetUInt8()); - itemTemplate.SubClass = uint32(fields[2].GetUInt8()); - itemTemplate.SoundOverrideSubclass = int32(fields[3].GetInt8()); - itemTemplate.Name1 = fields[4].GetString(); - itemTemplate.DisplayInfoID = fields[5].GetUInt32(); - itemTemplate.Quality = uint32(fields[6].GetUInt8()); - itemTemplate.Flags = uint32(fields[7].GetInt64()); - itemTemplate.Flags2 = fields[8].GetUInt32(); - itemTemplate.BuyCount = uint32(fields[9].GetUInt8()); - itemTemplate.BuyPrice = int32(fields[10].GetInt64()); - itemTemplate.SellPrice = fields[11].GetUInt32(); - itemTemplate.InventoryType = uint32(fields[12].GetUInt8()); - itemTemplate.AllowableClass = fields[13].GetInt32(); - itemTemplate.AllowableRace = fields[14].GetInt32(); - itemTemplate.ItemLevel = uint32(fields[15].GetUInt16()); - itemTemplate.RequiredLevel = uint32(fields[16].GetUInt8()); - itemTemplate.RequiredSkill = uint32(fields[17].GetUInt16()); - itemTemplate.RequiredSkillRank = uint32(fields[18].GetUInt16()); - itemTemplate.RequiredSpell = fields[19].GetUInt32(); - itemTemplate.RequiredHonorRank = fields[20].GetUInt32(); - itemTemplate.RequiredCityRank = fields[21].GetUInt32(); - itemTemplate.RequiredReputationFaction = uint32(fields[22].GetUInt16()); - itemTemplate.RequiredReputationRank = uint32(fields[23].GetUInt16()); - itemTemplate.MaxCount = fields[24].GetInt32(); - itemTemplate.Stackable = fields[25].GetInt32(); - itemTemplate.ContainerSlots = uint32(fields[26].GetUInt8()); - itemTemplate.StatsCount = uint32(fields[27].GetUInt8()); - - for (uint8 i = 0; i < itemTemplate.StatsCount; ++i) - { - itemTemplate.ItemStat[i].ItemStatType = uint32(fields[28 + i*2].GetUInt8()); - itemTemplate.ItemStat[i].ItemStatValue = int32(fields[29 + i*2].GetInt16()); - } - - itemTemplate.ScalingStatDistribution = uint32(fields[48].GetUInt16()); - itemTemplate.ScalingStatValue = fields[49].GetInt32(); - - for (uint8 i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i) - { - itemTemplate.Damage[i].DamageMin = fields[50 + i*3].GetFloat(); - itemTemplate.Damage[i].DamageMax = fields[51 + i*3].GetFloat(); - itemTemplate.Damage[i].DamageType = uint32(fields[52 + i*3].GetUInt8()); - } - - itemTemplate.Armor = uint32(fields[56].GetUInt16()); - itemTemplate.HolyRes = uint32(fields[57].GetUInt8()); - itemTemplate.FireRes = uint32(fields[58].GetUInt8()); - itemTemplate.NatureRes = uint32(fields[59].GetUInt8()); - itemTemplate.FrostRes = uint32(fields[60].GetUInt8()); - itemTemplate.ShadowRes = uint32(fields[61].GetUInt8()); - itemTemplate.ArcaneRes = uint32(fields[62].GetUInt8()); - itemTemplate.Delay = uint32(fields[63].GetUInt16()); - itemTemplate.AmmoType = uint32(fields[64].GetUInt8()); - itemTemplate.RangedModRange = fields[65].GetFloat(); - - for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) - { - itemTemplate.Spells[i].SpellId = fields[66 + i*7 ].GetInt32(); - itemTemplate.Spells[i].SpellTrigger = uint32(fields[67 + i*7].GetUInt8()); - itemTemplate.Spells[i].SpellCharges = int32(fields[68 + i*7].GetInt16()); - itemTemplate.Spells[i].SpellPPMRate = fields[69 + i*7].GetFloat(); - itemTemplate.Spells[i].SpellCooldown = fields[70 + i*7].GetInt32(); - itemTemplate.Spells[i].SpellCategory = uint32(fields[71 + i*7].GetUInt16()); - itemTemplate.Spells[i].SpellCategoryCooldown = fields[72 + i*7].GetInt32(); - } - - itemTemplate.Bonding = uint32(fields[101].GetUInt8()); - itemTemplate.Description = fields[102].GetString(); - itemTemplate.PageText = fields[103].GetUInt32(); - itemTemplate.LanguageID = uint32(fields[104].GetUInt8()); - itemTemplate.PageMaterial = uint32(fields[105].GetUInt8()); - itemTemplate.StartQuest = fields[106].GetUInt32(); - itemTemplate.LockID = fields[107].GetUInt32(); - itemTemplate.Material = int32(fields[108].GetInt8()); - itemTemplate.Sheath = uint32(fields[109].GetUInt8()); - itemTemplate.RandomProperty = fields[110].GetUInt32(); - itemTemplate.RandomSuffix = fields[111].GetInt32(); - itemTemplate.Block = fields[112].GetUInt32(); - itemTemplate.ItemSet = fields[113].GetUInt32(); - itemTemplate.MaxDurability = uint32(fields[114].GetUInt16()); - itemTemplate.Area = fields[115].GetUInt32(); - itemTemplate.Map = uint32(fields[116].GetUInt16()); - itemTemplate.BagFamily = fields[117].GetUInt32(); - itemTemplate.TotemCategory = fields[118].GetUInt32(); - - for (uint8 i = 0; i < MAX_ITEM_PROTO_SOCKETS; ++i) - { - itemTemplate.Socket[i].Color = uint32(fields[119 + i*2].GetUInt8()); - itemTemplate.Socket[i].Content = fields[120 + i*2].GetUInt32(); - } - - itemTemplate.socketBonus = fields[125].GetUInt32(); - itemTemplate.GemProperties = fields[126].GetUInt32(); - itemTemplate.RequiredDisenchantSkill = uint32(fields[127].GetInt16()); - itemTemplate.ArmorDamageModifier = fields[128].GetFloat(); - itemTemplate.Duration = fields[129].GetUInt32(); - itemTemplate.ItemLimitCategory = uint32(fields[130].GetInt16()); - itemTemplate.HolidayId = fields[131].GetUInt32(); - itemTemplate.ScriptId = sObjectMgr->GetScriptId(fields[132].GetCString()); - itemTemplate.DisenchantID = fields[133].GetUInt32(); - itemTemplate.FoodType = uint32(fields[134].GetUInt8()); - itemTemplate.MinMoneyLoot = fields[135].GetUInt32(); - itemTemplate.MaxMoneyLoot = fields[136].GetUInt32(); - itemTemplate.FlagsCu = fields[137].GetUInt32(); - - // Checks - - ItemEntry const* dbcitem = sItemStore.LookupEntry(entry); - - if (dbcitem) - { - if (itemTemplate.Class != dbcitem->Class) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) does not have a correct class %u, must be %u .", entry, itemTemplate.Class, dbcitem->Class); - if (enforceDBCAttributes) - itemTemplate.Class = dbcitem->Class; - } - - if (itemTemplate.SoundOverrideSubclass != dbcitem->SoundOverrideSubclass) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) does not have a correct SoundOverrideSubclass (%i), must be %i .", entry, itemTemplate.SoundOverrideSubclass, dbcitem->SoundOverrideSubclass); - if (enforceDBCAttributes) - itemTemplate.SoundOverrideSubclass = dbcitem->SoundOverrideSubclass; - } - if (itemTemplate.Material != dbcitem->Material) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) does not have a correct material (%i), must be %i .", entry, itemTemplate.Material, dbcitem->Material); - if (enforceDBCAttributes) - itemTemplate.Material = dbcitem->Material; - } - if (itemTemplate.InventoryType != dbcitem->InventoryType) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) does not have a correct inventory type (%u), must be %u .", entry, itemTemplate.InventoryType, dbcitem->InventoryType); - if (enforceDBCAttributes) - itemTemplate.InventoryType = dbcitem->InventoryType; - } - if (itemTemplate.DisplayInfoID != dbcitem->DisplayId) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) does not have a correct display id (%u), must be %u .", entry, itemTemplate.DisplayInfoID, dbcitem->DisplayId); - if (enforceDBCAttributes) - itemTemplate.DisplayInfoID = dbcitem->DisplayId; - } - if (itemTemplate.Sheath != dbcitem->Sheath) + case INVTYPE_AMMO: + store = &sItemDamageAmmoStore; + break; + case INVTYPE_2HWEAPON: + if (flags2 & ITEM_FLAGS_EXTRA_CASTER_WEAPON) + store = &sItemDamageTwoHandCasterStore; + else + store = &sItemDamageTwoHandStore; + break; + case INVTYPE_RANGED: + case INVTYPE_THROWN: + case INVTYPE_RANGEDRIGHT: + switch (itemSubClass) { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) does not have a correct sheathid (%u), must be %u .", entry, itemTemplate.Sheath, dbcitem->Sheath); - if (enforceDBCAttributes) - itemTemplate.Sheath = dbcitem->Sheath; + case ITEM_SUBCLASS_WEAPON_WAND: + store = &sItemDamageWandStore; + break; + case ITEM_SUBCLASS_WEAPON_THROWN: + store = &sItemDamageThrownStore; + break; + case ITEM_SUBCLASS_WEAPON_BOW: + case ITEM_SUBCLASS_WEAPON_GUN: + case ITEM_SUBCLASS_WEAPON_CROSSBOW: + store = &sItemDamageRangedStore; + break; + default: + return; } + break; + case INVTYPE_WEAPON: + case INVTYPE_WEAPONMAINHAND: + case INVTYPE_WEAPONOFFHAND: + if (flags2 & ITEM_FLAGS_EXTRA_CASTER_WEAPON) + store = &sItemDamageOneHandCasterStore; + else + store = &sItemDamageOneHandStore; + break; + default: + return; + } - } - else - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) does not exist in item.dbc! (not correct id?).", entry); - - if (itemTemplate.Class >= MAX_ITEM_CLASS) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has wrong Class value (%u)", entry, itemTemplate.Class); - itemTemplate.Class = ITEM_CLASS_MISC; - } - - if (itemTemplate.SubClass >= MaxItemSubclassValues[itemTemplate.Class]) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has wrong Subclass value (%u) for class %u", entry, itemTemplate.SubClass, itemTemplate.Class); - itemTemplate.SubClass = 0;// exist for all item classes - } - - if (itemTemplate.Quality >= MAX_ITEM_QUALITY) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has wrong Quality value (%u)", entry, itemTemplate.Quality); - itemTemplate.Quality = ITEM_QUALITY_NORMAL; - } - - if (itemTemplate.Flags2 & ITEM_FLAGS_EXTRA_HORDE_ONLY) - { - if (FactionEntry const* faction = sFactionStore.LookupEntry(HORDE)) - if ((itemTemplate.AllowableRace & faction->BaseRepRaceMask[0]) == 0) - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has value (%u) in `AllowableRace` races, not compatible with ITEM_FLAGS_EXTRA_HORDE_ONLY (%u) in Flags field, item cannot be equipped or used by these races.", - entry, itemTemplate.AllowableRace, ITEM_FLAGS_EXTRA_HORDE_ONLY); - - if (itemTemplate.Flags2 & ITEM_FLAGS_EXTRA_ALLIANCE_ONLY) - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has value (%u) in `Flags2` flags (ITEM_FLAGS_EXTRA_ALLIANCE_ONLY) and ITEM_FLAGS_EXTRA_HORDE_ONLY (%u) in Flags field, this is a wrong combination.", - entry, ITEM_FLAGS_EXTRA_ALLIANCE_ONLY, ITEM_FLAGS_EXTRA_HORDE_ONLY); - } - else if (itemTemplate.Flags2 & ITEM_FLAGS_EXTRA_ALLIANCE_ONLY) - { - if (FactionEntry const* faction = sFactionStore.LookupEntry(ALLIANCE)) - if ((itemTemplate.AllowableRace & faction->BaseRepRaceMask[0]) == 0) - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has value (%u) in `AllowableRace` races, not compatible with ITEM_FLAGS_EXTRA_ALLIANCE_ONLY (%u) in Flags field, item cannot be equipped or used by these races.", - entry, itemTemplate.AllowableRace, ITEM_FLAGS_EXTRA_ALLIANCE_ONLY); - } - - if (itemTemplate.BuyCount <= 0) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has wrong BuyCount value (%u), set to default(1).", entry, itemTemplate.BuyCount); - itemTemplate.BuyCount = 1; - } - - if (itemTemplate.InventoryType >= MAX_INVTYPE) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has wrong InventoryType value (%u)", entry, itemTemplate.InventoryType); - itemTemplate.InventoryType = INVTYPE_NON_EQUIP; - } - - if (itemTemplate.RequiredSkill >= MAX_SKILL_TYPE) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has wrong RequiredSkill value (%u)", entry, itemTemplate.RequiredSkill); - itemTemplate.RequiredSkill = 0; - } - - { - // can be used in equip slot, as page read use in inventory, or spell casting at use - bool req = itemTemplate.InventoryType != INVTYPE_NON_EQUIP || itemTemplate.PageText; - if (!req) - for (uint8 j = 0; j < MAX_ITEM_PROTO_SPELLS; ++j) - { - if (itemTemplate.Spells[j].SpellId) - { - req = true; - break; - } - } - - if (req) - { - if (!(itemTemplate.AllowableClass & CLASSMASK_ALL_PLAYABLE)) - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) does not have any playable classes (%u) in `AllowableClass` and can't be equipped or used.", entry, itemTemplate.AllowableClass); + if (!store) + return; - if (!(itemTemplate.AllowableRace & RACEMASK_ALL_PLAYABLE)) - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) does not have any playable races (%u) in `AllowableRace` and can't be equipped or used.", entry, itemTemplate.AllowableRace); - } - } + ItemDamageEntry const* damageInfo = store->LookupEntry(itemLevel); + if (!damageInfo) + return; - if (itemTemplate.RequiredSpell && !sSpellMgr->GetSpellInfo(itemTemplate.RequiredSpell)) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has a wrong (non-existing) spell in RequiredSpell (%u)", entry, itemTemplate.RequiredSpell); - itemTemplate.RequiredSpell = 0; - } + *dps = damageInfo->DPS[quality]; + float avgDamage = *dps * delay * 0.001f; + *minDamage = (statScalingFactor * -0.5f + 1.0f) * avgDamage; + *maxDamage = floor(float(avgDamage* (statScalingFactor * 0.5f + 1.0f) + 0.5f)); +} - if (itemTemplate.RequiredReputationRank >= MAX_REPUTATION_RANK) - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has wrong reputation rank in RequiredReputationRank (%u), item can't be used.", entry, itemTemplate.RequiredReputationRank); +uint32 FillItemArmor(uint32 itemlevel, uint32 itemClass, uint32 itemSubclass, uint32 quality, uint32 inventoryType) +{ + if (quality > ITEM_QUALITY_ARTIFACT) + return 0; - if (itemTemplate.RequiredReputationFaction) - { - if (!sFactionStore.LookupEntry(itemTemplate.RequiredReputationFaction)) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has wrong (not existing) faction in RequiredReputationFaction (%u)", entry, itemTemplate.RequiredReputationFaction); - itemTemplate.RequiredReputationFaction = 0; - } + // all items but shields + if (itemClass != ITEM_CLASS_ARMOR || itemSubclass != ITEM_SUBCLASS_ARMOR_SHIELD) + { + ItemArmorQualityEntry const* armorQuality = sItemArmorQualityStore.LookupEntry(itemlevel); + ItemArmorTotalEntry const* armorTotal = sItemArmorTotalStore.LookupEntry(itemlevel); + if (!armorQuality || !armorTotal) + return 0; - if (itemTemplate.RequiredReputationRank == MIN_REPUTATION_RANK) - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has min. reputation rank in RequiredReputationRank (0) but RequiredReputationFaction > 0, faction setting is useless.", entry); - } + if (inventoryType == INVTYPE_ROBE) + inventoryType = INVTYPE_CHEST; - if (itemTemplate.MaxCount < -1) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has too large negative in maxcount (%i), replace by value (-1) no storing limits.", entry, itemTemplate.MaxCount); - itemTemplate.MaxCount = -1; - } + ArmorLocationEntry const* location = sArmorLocationStore.LookupEntry(inventoryType); + if (!location) + return 0; - if (itemTemplate.Stackable == 0) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has wrong value in stackable (%i), replace by default 1.", entry, itemTemplate.Stackable); - itemTemplate.Stackable = 1; - } - else if (itemTemplate.Stackable < -1) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has too large negative in stackable (%i), replace by value (-1) no stacking limits.", entry, itemTemplate.Stackable); - itemTemplate.Stackable = -1; - } + if (itemSubclass < ITEM_SUBCLASS_ARMOR_CLOTH) + return 0; - if (itemTemplate.ContainerSlots > MAX_BAG_SIZE) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has too large value in ContainerSlots (%u), replace by hardcoded limit (%u).", entry, itemTemplate.ContainerSlots, MAX_BAG_SIZE); - itemTemplate.ContainerSlots = MAX_BAG_SIZE; - } + return uint32(armorQuality->Value[quality] * armorTotal->Value[itemSubclass - 1] * location->Value[itemSubclass - 1] + 0.5f); + } - if (itemTemplate.StatsCount > MAX_ITEM_PROTO_STATS) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has too large value in statscount (%u), replace by hardcoded limit (%u).", entry, itemTemplate.StatsCount, MAX_ITEM_PROTO_STATS); - itemTemplate.StatsCount = MAX_ITEM_PROTO_STATS; - } + // shields + ItemArmorShieldEntry const* shield = sItemArmorShieldStore.LookupEntry(itemlevel); + if (!shield) + return 0; - for (uint8 j = 0; j < itemTemplate.StatsCount; ++j) - { - // for ItemStatValue != 0 - if (itemTemplate.ItemStat[j].ItemStatValue && itemTemplate.ItemStat[j].ItemStatType >= MAX_ITEM_MOD) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has wrong (non-existing?) stat_type%d (%u)", entry, j+1, itemTemplate.ItemStat[j].ItemStatType); - itemTemplate.ItemStat[j].ItemStatType = 0; - } + return uint32(shield->Value[quality] + 0.5f); +} - switch (itemTemplate.ItemStat[j].ItemStatType) - { - case ITEM_MOD_SPELL_HEALING_DONE: - case ITEM_MOD_SPELL_DAMAGE_DONE: - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has deprecated stat_type%d (%u)", entry, j+1, itemTemplate.ItemStat[j].ItemStatType); - break; - default: - break; - } - } +uint32 FillMaxDurability(uint32 itemClass, uint32 itemSubClass, uint32 inventoryType, uint32 quality, uint32 itemLevel) +{ + if (itemClass != ITEM_CLASS_ARMOR && itemClass != ITEM_CLASS_WEAPON) + return 0; - for (uint8 j = 0; j < MAX_ITEM_PROTO_DAMAGES; ++j) - { - if (itemTemplate.Damage[j].DamageType >= MAX_SPELL_SCHOOL) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has wrong dmg_type%d (%u)", entry, j+1, itemTemplate.Damage[j].DamageType); - itemTemplate.Damage[j].DamageType = 0; - } - } + static float const qualityMultipliers[MAX_ITEM_QUALITY] = + { + 1.0f, 1.0f, 1.0f, 1.17f, 1.37f, 1.68f, 0.0f, 0.0f + }; - // special format - if ((itemTemplate.Spells[0].SpellId == 483) || (itemTemplate.Spells[0].SpellId == 55884)) - { - // spell_1 - if (itemTemplate.Spells[0].SpellTrigger != ITEM_SPELLTRIGGER_ON_USE) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u) for special learning format", entry, 0+1, itemTemplate.Spells[0].SpellTrigger); - itemTemplate.Spells[0].SpellId = 0; - itemTemplate.Spells[0].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE; - itemTemplate.Spells[1].SpellId = 0; - itemTemplate.Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE; - } + static float const armorMultipliers[MAX_INVTYPE] = + { + 0.00f, // INVTYPE_NON_EQUIP + 0.59f, // INVTYPE_HEAD + 0.00f, // INVTYPE_NECK + 0.59f, // INVTYPE_SHOULDERS + 0.00f, // INVTYPE_BODY + 1.00f, // INVTYPE_CHEST + 0.35f, // INVTYPE_WAIST + 0.75f, // INVTYPE_LEGS + 0.49f, // INVTYPE_FEET + 0.35f, // INVTYPE_WRISTS + 0.35f, // INVTYPE_HANDS + 0.00f, // INVTYPE_FINGER + 0.00f, // INVTYPE_TRINKET + 0.00f, // INVTYPE_WEAPON + 1.00f, // INVTYPE_SHIELD + 0.00f, // INVTYPE_RANGED + 0.00f, // INVTYPE_CLOAK + 0.00f, // INVTYPE_2HWEAPON + 0.00f, // INVTYPE_BAG + 0.00f, // INVTYPE_TABARD + 1.00f, // INVTYPE_ROBE + 0.00f, // INVTYPE_WEAPONMAINHAND + 0.00f, // INVTYPE_WEAPONOFFHAND + 0.00f, // INVTYPE_HOLDABLE + 0.00f, // INVTYPE_AMMO + 0.00f, // INVTYPE_THROWN + 0.00f, // INVTYPE_RANGEDRIGHT + 0.00f, // INVTYPE_QUIVER + 0.00f, // INVTYPE_RELIC + }; - // spell_2 have learning spell - if (itemTemplate.Spells[1].SpellTrigger != ITEM_SPELLTRIGGER_LEARN_SPELL_ID) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u) for special learning format.", entry, 1+1, itemTemplate.Spells[1].SpellTrigger); - itemTemplate.Spells[0].SpellId = 0; - itemTemplate.Spells[1].SpellId = 0; - itemTemplate.Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE; - } - else if (!itemTemplate.Spells[1].SpellId) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) does not have an expected spell in spellid_%d in special learning format.", entry, 1+1); - itemTemplate.Spells[0].SpellId = 0; - itemTemplate.Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE; - } - else if (itemTemplate.Spells[1].SpellId != -1) - { - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itemTemplate.Spells[1].SpellId); - if (!spellInfo && !DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, itemTemplate.Spells[1].SpellId, NULL)) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has wrong (not existing) spell in spellid_%d (%d)", entry, 1+1, itemTemplate.Spells[1].SpellId); - itemTemplate.Spells[0].SpellId = 0; - itemTemplate.Spells[1].SpellId = 0; - itemTemplate.Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE; - } - // allowed only in special format - else if ((itemTemplate.Spells[1].SpellId == 483) || (itemTemplate.Spells[1].SpellId == 55884)) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has broken spell in spellid_%d (%d)", entry, 1+1, itemTemplate.Spells[1].SpellId); - itemTemplate.Spells[0].SpellId = 0; - itemTemplate.Spells[1].SpellId = 0; - itemTemplate.Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE; - } - } + static float const weaponMultipliers[MAX_ITEM_SUBCLASS_WEAPON] = + { + 0.89f, // ITEM_SUBCLASS_WEAPON_AXE + 1.03f, // ITEM_SUBCLASS_WEAPON_AXE2 + 0.77f, // ITEM_SUBCLASS_WEAPON_BOW + 0.77f, // ITEM_SUBCLASS_WEAPON_GUN + 0.89f, // ITEM_SUBCLASS_WEAPON_MACE + 1.03f, // ITEM_SUBCLASS_WEAPON_MACE2 + 1.03f, // ITEM_SUBCLASS_WEAPON_POLEARM + 0.89f, // ITEM_SUBCLASS_WEAPON_SWORD + 1.03f, // ITEM_SUBCLASS_WEAPON_SWORD2 + 0.00f, // ITEM_SUBCLASS_WEAPON_Obsolete + 1.03f, // ITEM_SUBCLASS_WEAPON_STAFF + 0.00f, // ITEM_SUBCLASS_WEAPON_EXOTIC + 0.00f, // ITEM_SUBCLASS_WEAPON_EXOTIC2 + 0.64f, // ITEM_SUBCLASS_WEAPON_FIST_WEAPON + 0.00f, // ITEM_SUBCLASS_WEAPON_MISCELLANEOUS + 0.64f, // ITEM_SUBCLASS_WEAPON_DAGGER + 0.64f, // ITEM_SUBCLASS_WEAPON_THROWN + 0.00f, // ITEM_SUBCLASS_WEAPON_SPEAR + 0.77f, // ITEM_SUBCLASS_WEAPON_CROSSBOW + 0.64f, // ITEM_SUBCLASS_WEAPON_WAND + 0.64f, // ITEM_SUBCLASS_WEAPON_FISHING_POLE + }; - // spell_3*, spell_4*, spell_5* is empty - for (uint8 j = 2; j < MAX_ITEM_PROTO_SPELLS; ++j) - { - if (itemTemplate.Spells[j].SpellTrigger != ITEM_SPELLTRIGGER_ON_USE) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u)", entry, j+1, itemTemplate.Spells[j].SpellTrigger); - itemTemplate.Spells[j].SpellId = 0; - itemTemplate.Spells[j].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE; - } - else if (itemTemplate.Spells[j].SpellId != 0) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has wrong spell in spellid_%d (%d) for learning special format", entry, j+1, itemTemplate.Spells[j].SpellId); - itemTemplate.Spells[j].SpellId = 0; - } - } - } - // normal spell list - else - { - for (uint8 j = 0; j < MAX_ITEM_PROTO_SPELLS; ++j) - { - if (itemTemplate.Spells[j].SpellTrigger >= MAX_ITEM_SPELLTRIGGER || itemTemplate.Spells[j].SpellTrigger == ITEM_SPELLTRIGGER_LEARN_SPELL_ID) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u)", entry, j+1, itemTemplate.Spells[j].SpellTrigger); - itemTemplate.Spells[j].SpellId = 0; - itemTemplate.Spells[j].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE; - } + float levelPenalty = 1.0f; + if (itemLevel <= 28) + levelPenalty = 0.966f - float(28u - itemLevel) / 54.0f; - if (itemTemplate.Spells[j].SpellId && itemTemplate.Spells[j].SpellId != -1) - { - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itemTemplate.Spells[j].SpellId); - if (!spellInfo && !DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, itemTemplate.Spells[j].SpellId, NULL)) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has wrong (not existing) spell in spellid_%d (%d)", entry, j+1, itemTemplate.Spells[j].SpellId); - itemTemplate.Spells[j].SpellId = 0; - } - // allowed only in special format - else if ((itemTemplate.Spells[j].SpellId == 483) || (itemTemplate.Spells[j].SpellId == 55884)) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has broken spell in spellid_%d (%d)", entry, j+1, itemTemplate.Spells[j].SpellId); - itemTemplate.Spells[j].SpellId = 0; - } - } - } - } + if (itemClass == ITEM_CLASS_ARMOR) + { + if (inventoryType > INVTYPE_ROBE) + return 0; - if (itemTemplate.Bonding >= MAX_BIND_TYPE) - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has wrong Bonding value (%u)", entry, itemTemplate.Bonding); + return 5 * uint32(23.0f * qualityMultipliers[quality] * armorMultipliers[inventoryType] * levelPenalty + 0.5f); + } - if (itemTemplate.PageText && !GetPageText(itemTemplate.PageText)) - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has non existing first page (Id:%u)", entry, itemTemplate.PageText); + return 5 * uint32(17.0f * qualityMultipliers[quality] * weaponMultipliers[itemSubClass] * levelPenalty + 0.5f); +}; - if (itemTemplate.LockID && !sLockStore.LookupEntry(itemTemplate.LockID)) - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has wrong LockID (%u)", entry, itemTemplate.LockID); +void FillDisenchantFields(uint32* disenchantID, uint32* requiredDisenchantSkill, ItemTemplate const& itemTemplate) +{ + *disenchantID = 0; + *(int32*)requiredDisenchantSkill = -1; + if ((itemTemplate.Flags & (ITEM_PROTO_FLAG_CONJURED | ITEM_PROTO_FLAG_UNK6)) || + itemTemplate.Bonding == BIND_QUEST_ITEM || itemTemplate.Area || itemTemplate.Map || + itemTemplate.Stackable > 1 || + itemTemplate.Quality < ITEM_QUALITY_UNCOMMON || itemTemplate.Quality > ITEM_QUALITY_EPIC || + !(itemTemplate.Class == ITEM_CLASS_ARMOR || itemTemplate.Class == ITEM_CLASS_WEAPON) || + !(Item::GetSpecialPrice(&itemTemplate) || sItemCurrencyCostStore.LookupEntry(itemTemplate.ItemId))) + return; - if (itemTemplate.Sheath >= MAX_SHEATHETYPE) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has wrong Sheath (%u)", entry, itemTemplate.Sheath); - itemTemplate.Sheath = SHEATHETYPE_NONE; - } + for (uint32 i = 0; i < sItemDisenchantLootStore.GetNumRows(); ++i) + { + ItemDisenchantLootEntry const* disenchant = sItemDisenchantLootStore.LookupEntry(i); + if (!disenchant) + continue; - if (itemTemplate.RandomProperty) + if (disenchant->ItemClass == itemTemplate.Class && + disenchant->ItemQuality == itemTemplate.Quality && + disenchant->MinItemLevel <= itemTemplate.ItemLevel && + disenchant->MaxItemLevel >= itemTemplate.ItemLevel) { - // To be implemented later - if (itemTemplate.RandomProperty == -1) - itemTemplate.RandomProperty = 0; - - else if (!sItemRandomPropertiesStore.LookupEntry(GetItemEnchantMod(itemTemplate.RandomProperty))) + if (disenchant->Id == 60 || disenchant->Id == 61) // epic item disenchant ilvl range 66-99 (classic) { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has unknown (wrong or not listed in `item_enchantment_template`) RandomProperty (%u)", entry, itemTemplate.RandomProperty); - itemTemplate.RandomProperty = 0; + if (itemTemplate.RequiredLevel > 60 || itemTemplate.RequiredSkillRank > 300) + continue; // skip to epic item disenchant ilvl range 90-199 (TBC) } - } - - if (itemTemplate.RandomSuffix && !sItemRandomSuffixStore.LookupEntry(GetItemEnchantMod(itemTemplate.RandomSuffix))) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has wrong RandomSuffix (%u)", entry, itemTemplate.RandomSuffix); - itemTemplate.RandomSuffix = 0; - } - - if (itemTemplate.ItemSet && !sItemSetStore.LookupEntry(itemTemplate.ItemSet)) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) have wrong ItemSet (%u)", entry, itemTemplate.ItemSet); - itemTemplate.ItemSet = 0; - } - - if (itemTemplate.Area && !GetAreaEntryByAreaID(itemTemplate.Area)) - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has wrong Area (%u)", entry, itemTemplate.Area); - - if (itemTemplate.Map && !sMapStore.LookupEntry(itemTemplate.Map)) - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has wrong Map (%u)", entry, itemTemplate.Map); - - if (itemTemplate.BagFamily) - { - // check bits - for (uint32 j = 0; j < sizeof(itemTemplate.BagFamily)*8; ++j) + else if (disenchant->Id == 66 || disenchant->Id == 67) // epic item disenchant ilvl range 90-199 (TBC) { - uint32 mask = 1 << j; - if ((itemTemplate.BagFamily & mask) == 0) + if (itemTemplate.RequiredLevel <= 60 || (itemTemplate.RequiredSkill && itemTemplate.RequiredSkillRank <= 300)) continue; - - ItemBagFamilyEntry const* bf = sItemBagFamilyStore.LookupEntry(j+1); - if (!bf) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has bag family bit set not listed in ItemBagFamily.dbc, remove bit", entry); - itemTemplate.BagFamily &= ~mask; - continue; - } - - if (BAG_FAMILY_MASK_CURRENCY_TOKENS & mask) - { - CurrencyTypesEntry const* ctEntry = sCurrencyTypesStore.LookupEntry(itemTemplate.ItemId); - if (!ctEntry) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has currency bag family bit set in BagFamily but not listed in CurrencyTypes.dbc, remove bit", entry); - itemTemplate.BagFamily &= ~mask; - } - } } - } - if (itemTemplate.TotemCategory && !sTotemCategoryStore.LookupEntry(itemTemplate.TotemCategory)) - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has wrong TotemCategory (%u)", entry, itemTemplate.TotemCategory); - - for (uint8 j = 0; j < MAX_ITEM_PROTO_SOCKETS; ++j) - { - if (itemTemplate.Socket[j].Color && (itemTemplate.Socket[j].Color & SOCKET_COLOR_ALL) != itemTemplate.Socket[j].Color) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has wrong socketColor_%d (%u)", entry, j+1, itemTemplate.Socket[j].Color); - itemTemplate.Socket[j].Color = 0; - } + *disenchantID = disenchant->Id; + *requiredDisenchantSkill = disenchant->RequiredDisenchantSkill; + return; } + } +} - if (itemTemplate.GemProperties && !sGemPropertiesStore.LookupEntry(itemTemplate.GemProperties)) - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has wrong GemProperties (%u)", entry, itemTemplate.GemProperties); - - if (itemTemplate.FoodType >= MAX_PET_DIET) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has wrong FoodType value (%u)", entry, itemTemplate.FoodType); - itemTemplate.FoodType = 0; - } +void ObjectMgr::LoadItemTemplates() +{ + uint32 oldMSTime = getMSTime(); + uint32 sparseCount = 0; + uint32 dbCount = 0; - if (itemTemplate.ItemLimitCategory && !sItemLimitCategoryStore.LookupEntry(itemTemplate.ItemLimitCategory)) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has wrong LimitCategory value (%u)", entry, itemTemplate.ItemLimitCategory); - itemTemplate.ItemLimitCategory = 0; - } + for (uint32 itemId = 0; itemId < sItemSparseStore.GetNumRows(); ++itemId) + { + ItemSparseEntry const* sparse = sItemSparseStore.LookupEntry(itemId); + ItemEntry const* db2Data = sItemStore.LookupEntry(itemId); + if (!sparse || !db2Data) + continue; - if (itemTemplate.HolidayId && !sHolidaysStore.LookupEntry(itemTemplate.HolidayId)) - { - sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) has wrong HolidayId value (%u)", entry, itemTemplate.HolidayId); - itemTemplate.HolidayId = 0; - } + ItemTemplate& itemTemplate = _itemTemplateStore[itemId]; + + itemTemplate.ItemId = itemId; + itemTemplate.Class = db2Data->Class; + itemTemplate.SubClass = db2Data->SubClass; + itemTemplate.SoundOverrideSubclass = db2Data->SoundOverrideSubclass; + itemTemplate.Name1 = sparse->Name; + itemTemplate.DisplayInfoID = db2Data->DisplayId; + itemTemplate.Quality = sparse->Quality; + itemTemplate.Flags = sparse->Flags; + itemTemplate.Flags2 = sparse->Flags2; + itemTemplate.Unk430_1 = sparse->Unk430_1; + itemTemplate.Unk430_2 = sparse->Unk430_2; + itemTemplate.BuyCount = std::max(sparse->BuyCount, 1u); + itemTemplate.BuyPrice = sparse->BuyPrice; + itemTemplate.SellPrice = sparse->SellPrice; + itemTemplate.InventoryType = db2Data->InventoryType; + itemTemplate.AllowableClass = sparse->AllowableClass; + itemTemplate.AllowableRace = sparse->AllowableRace; + itemTemplate.ItemLevel = sparse->ItemLevel; + itemTemplate.RequiredLevel = sparse->RequiredLevel; + itemTemplate.RequiredSkill = sparse->RequiredSkill; + itemTemplate.RequiredSkillRank = sparse->RequiredSkillRank; + itemTemplate.RequiredSpell = sparse->RequiredSpell; + itemTemplate.RequiredHonorRank = sparse->RequiredHonorRank; + itemTemplate.RequiredCityRank = sparse->RequiredCityRank; + itemTemplate.RequiredReputationFaction = sparse->RequiredReputationFaction; + itemTemplate.RequiredReputationRank = sparse->RequiredReputationRank; + itemTemplate.MaxCount = sparse->MaxCount; + itemTemplate.Stackable = sparse->Stackable; + itemTemplate.ContainerSlots = sparse->ContainerSlots; + for (uint32 i = 0; i < MAX_ITEM_PROTO_STATS; ++i) + { + itemTemplate.ItemStat[i].ItemStatType = sparse->ItemStatType[i]; + itemTemplate.ItemStat[i].ItemStatValue = sparse->ItemStatValue[i]; + itemTemplate.ItemStat[i].ItemStatUnk1 = sparse->ItemStatUnk1[i]; + itemTemplate.ItemStat[i].ItemStatUnk2 = sparse->ItemStatUnk2[i]; + } + + itemTemplate.ScalingStatDistribution = sparse->ScalingStatDistribution; + + // cache item damage + FillItemDamageFields(&itemTemplate.DamageMin, &itemTemplate.DamageMax, &itemTemplate.DPS, sparse->ItemLevel, + db2Data->Class, db2Data->SubClass, sparse->Quality, sparse->Delay, sparse->StatScalingFactor, + sparse->InventoryType, sparse->Flags2); + + itemTemplate.DamageType = sparse->DamageType; + itemTemplate.Armor = FillItemArmor(sparse->ItemLevel, db2Data->Class, db2Data->SubClass, sparse->Quality, sparse->InventoryType); + itemTemplate.Delay = sparse->Delay; + itemTemplate.RangedModRange = sparse->RangedModRange; + for (uint32 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) + { + itemTemplate.Spells[i].SpellId = sparse->SpellId[i]; + itemTemplate.Spells[i].SpellTrigger = sparse->SpellTrigger[i]; + itemTemplate.Spells[i].SpellCharges = sparse->SpellCharges[i]; + itemTemplate.Spells[i].SpellCooldown = sparse->SpellCooldown[i]; + itemTemplate.Spells[i].SpellCategory = sparse->SpellCategory[i]; + itemTemplate.Spells[i].SpellCategoryCooldown = sparse->SpellCategoryCooldown[i]; + } + + itemTemplate.SpellPPMRate = 0.0f; + itemTemplate.Bonding = sparse->Bonding; + itemTemplate.Description = sparse->Description; + itemTemplate.PageText = sparse->PageText; + itemTemplate.LanguageID = sparse->LanguageID; + itemTemplate.PageMaterial = sparse->PageMaterial; + itemTemplate.StartQuest = sparse->StartQuest; + itemTemplate.LockID = sparse->LockID; + itemTemplate.Material = sparse->Material; + itemTemplate.Sheath = sparse->Sheath; + itemTemplate.RandomProperty = sparse->RandomProperty; + itemTemplate.RandomSuffix = sparse->RandomSuffix; + itemTemplate.ItemSet = sparse->ItemSet; + itemTemplate.MaxDurability = FillMaxDurability(db2Data->Class, db2Data->SubClass, sparse->InventoryType, sparse->Quality, sparse->ItemLevel); + itemTemplate.Area = sparse->Area; + itemTemplate.Map = sparse->Map; + itemTemplate.BagFamily = sparse->BagFamily; + itemTemplate.TotemCategory = sparse->TotemCategory; + for (uint32 i = 0; i < MAX_ITEM_PROTO_SOCKETS; ++i) + { + itemTemplate.Socket[i].Color = sparse->Color[i]; + itemTemplate.Socket[i].Content = sparse->Content[i]; + } + + itemTemplate.socketBonus = sparse->SocketBonus; + itemTemplate.GemProperties = sparse->GemProperties; + FillDisenchantFields(&itemTemplate.DisenchantID, &itemTemplate.RequiredDisenchantSkill, itemTemplate); + + itemTemplate.ArmorDamageModifier = sparse->ArmorDamageModifier; + itemTemplate.Duration = sparse->Duration; + itemTemplate.ItemLimitCategory = sparse->ItemLimitCategory; + itemTemplate.HolidayId = sparse->HolidayId; + itemTemplate.StatScalingFactor = sparse->StatScalingFactor; + itemTemplate.CurrencySubstitutionId = sparse->CurrencySubstitutionId; + itemTemplate.CurrencySubstitutionCount = sparse->CurrencySubstitutionCount; + itemTemplate.ScriptId = 0; + itemTemplate.FoodType = 0; + itemTemplate.MinMoneyLoot = 0; + itemTemplate.MaxMoneyLoot = 0; + ++sparseCount; + } + + // Load missing items from item_template AND overwrite data from Item-sparse.db2 (item_template is supposed to contain Item-sparse.adb data) + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 + QueryResult result = WorldDatabase.Query("SELECT entry, Class, SubClass, SoundOverrideSubclass, Name, DisplayId, Quality, Flags, FlagsExtra, Unk430_1, Unk430_2, BuyCount, BuyPrice, SellPrice, " + // 14 15 16 17 18 19 20 21 + "InventoryType, AllowableClass, AllowableRace, ItemLevel, RequiredLevel, RequiredSkill, RequiredSkillRank, RequiredSpell, " + // 22 23 24 25 26 27 28 + "RequiredHonorRank, RequiredCityRank, RequiredReputationFaction, RequiredReputationRank, MaxCount, Stackable, ContainerSlots, " + // 29 30 31 32 33 34 35 36 + "stat_type1, stat_value1, stat_unk1_1, stat_unk2_1, stat_type2, stat_value2, stat_unk1_2, stat_unk2_2, " + // 37 38 39 40 41 42 43 44 + "stat_type3, stat_value3, stat_unk1_3, stat_unk2_3, stat_type4, stat_value4, stat_unk1_4, stat_unk2_4, " + // 45 46 47 48 49 50 51 52 + "stat_type5, stat_value5, stat_unk1_5, stat_unk2_5, stat_type6, stat_value6, stat_unk1_6, stat_unk2_6, " + // 53 54 55 56 57 58 59 60 + "stat_type7, stat_value7, stat_unk1_7, stat_unk2_7, stat_type8, stat_value8, stat_unk1_8, stat_unk2_8, " + // 61 62 63 64 65 66 67 68 + "stat_type9, stat_value9, stat_unk1_9, stat_unk2_9, stat_type10, stat_value10, stat_unk1_10, stat_unk2_10, " + // 69 70 71 72 + "ScalingStatDistribution, DamageType, Delay, RangedModRange, " + // 73 74 75 76 77 78 + "spellid_1, spelltrigger_1, spellcharges_1, spellcooldown_1, spellcategory_1, spellcategorycooldown_1, " + // 79 80 81 82 83 84 + "spellid_2, spelltrigger_2, spellcharges_2, spellcooldown_2, spellcategory_2, spellcategorycooldown_2, " + // 85 86 87 88 89 90 + "spellid_3, spelltrigger_3, spellcharges_3, spellcooldown_3, spellcategory_3, spellcategorycooldown_3, " + // 91 92 93 94 95 96 + "spellid_4, spelltrigger_4, spellcharges_4, spellcooldown_4, spellcategory_4, spellcategorycooldown_4, " + // 97 98 99 100 101 102 + "spellid_5, spelltrigger_5, spellcharges_5, spellcooldown_5, spellcategory_5, spellcategorycooldown_5, " + // 103 104 105 106 107 108 109 110 + "Bonding, Description, PageText, LanguageID, PageMaterial, StartQuest, LockID, Material, " + // 111 112 113 114 115 116 117 118 + "Sheath, RandomProperty, RandomSuffix, ItemSet, Area, Map, BagFamily, TotemCategory, " + // 119 120 121 122 123 124 125 + "SocketColor_1, SocketContent_1, SocketColor_2, SocketContent_2, SocketColor_3, SocketContent_3, SocketBonus, " + // 126 127 128 129 130 131 + "GemProperties, ArmorDamageModifier, Duration, ItemLimitCategory, HolidayId, StatScalingFactor, " + // 132 133 + "CurrencySubstitutionId, CurrencySubstitutionCount " + "FROM item_template"); - if (itemTemplate.FlagsCu & ITEM_FLAGS_CU_DURATION_REAL_TIME && !itemTemplate.Duration) + if (result) + { + do { - sLog->outError(LOG_FILTER_SQL, "Item (Entry %u) has flag ITEM_FLAGS_CU_DURATION_REAL_TIME but it does not have duration limit", entry); - itemTemplate.FlagsCu &= ~ITEM_FLAGS_CU_DURATION_REAL_TIME; - } - - ++count; + Field* fields = result->Fetch(); + uint32 itemId = fields[0].GetUInt32(); + if (_itemTemplateStore.find(itemId) != _itemTemplateStore.end()) + --sparseCount; + + ItemTemplate& itemTemplate = _itemTemplateStore[itemId]; + + itemTemplate.ItemId = itemId; + itemTemplate.Class = uint32(fields[1].GetUInt8()); + itemTemplate.SubClass = uint32(fields[2].GetUInt8()); + itemTemplate.SoundOverrideSubclass = fields[3].GetInt32(); + itemTemplate.Name1 = fields[4].GetString(); + itemTemplate.DisplayInfoID = fields[5].GetUInt32(); + itemTemplate.Quality = uint32(fields[6].GetUInt8()); + itemTemplate.Flags = uint32(fields[7].GetInt64()); + itemTemplate.Flags2 = fields[8].GetUInt32(); + itemTemplate.Unk430_1 = fields[9].GetFloat(); + itemTemplate.Unk430_2 = fields[10].GetFloat(); + itemTemplate.BuyCount = uint32(fields[11].GetUInt8()); + itemTemplate.BuyPrice = int32(fields[12].GetInt64()); + itemTemplate.SellPrice = fields[13].GetUInt32(); + + itemTemplate.InventoryType = uint32(fields[14].GetUInt8()); + itemTemplate.AllowableClass = fields[15].GetInt32(); + itemTemplate.AllowableRace = fields[16].GetInt32(); + itemTemplate.ItemLevel = uint32(fields[17].GetUInt16()); + itemTemplate.RequiredLevel = uint32(fields[18].GetUInt8()); + itemTemplate.RequiredSkill = uint32(fields[19].GetUInt16()); + itemTemplate.RequiredSkillRank = uint32(fields[20].GetUInt16()); + itemTemplate.RequiredSpell = fields[21].GetUInt32(); + itemTemplate.RequiredHonorRank = fields[22].GetUInt32(); + itemTemplate.RequiredCityRank = fields[23].GetUInt32(); + itemTemplate.RequiredReputationFaction = uint32(fields[24].GetUInt16()); + itemTemplate.RequiredReputationRank = uint32(fields[25].GetUInt16()); + itemTemplate.MaxCount = fields[26].GetInt32(); + itemTemplate.Stackable = fields[27].GetInt32(); + itemTemplate.ContainerSlots = uint32(fields[28].GetUInt8()); + for (uint32 i = 0; i < MAX_ITEM_PROTO_STATS; ++i) + { + itemTemplate.ItemStat[i].ItemStatType = uint32(fields[29 + i * 4 + 0].GetUInt8()); + itemTemplate.ItemStat[i].ItemStatValue = int32(fields[29 + i * 4 + 1].GetInt16()); + itemTemplate.ItemStat[i].ItemStatUnk1 = fields[29 + i * 4 + 2].GetInt32(); + itemTemplate.ItemStat[i].ItemStatUnk2 = fields[29 + i * 4 + 3].GetInt32(); + } + + itemTemplate.ScalingStatDistribution = uint32(fields[69].GetUInt16()); + + // cache item damage + FillItemDamageFields(&itemTemplate.DamageMin, &itemTemplate.DamageMax, &itemTemplate.DPS, itemTemplate.ItemLevel, + itemTemplate.Class, itemTemplate.SubClass, itemTemplate.Quality, fields[71].GetUInt16(), + fields[131].GetFloat(), itemTemplate.InventoryType, itemTemplate.Flags2); + + itemTemplate.DamageType = fields[70].GetUInt8(); + itemTemplate.Armor = FillItemArmor(itemTemplate.ItemLevel, itemTemplate.Class, + itemTemplate.SubClass, itemTemplate.Quality, + itemTemplate.InventoryType); + + itemTemplate.Delay = fields[71].GetUInt16(); + itemTemplate.RangedModRange = fields[72].GetFloat(); + for (uint32 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) + { + itemTemplate.Spells[i].SpellId = fields[73 + 6 * i + 0].GetInt32(); + itemTemplate.Spells[i].SpellTrigger = uint32(fields[73 + 6 * i + 1].GetUInt8()); + itemTemplate.Spells[i].SpellCharges = int32(fields[73 + 6 * i + 2].GetInt16()); + itemTemplate.Spells[i].SpellCooldown = fields[73 + 6 * i + 3].GetInt32(); + itemTemplate.Spells[i].SpellCategory = uint32(fields[73 + 6 * i + 4].GetUInt16()); + itemTemplate.Spells[i].SpellCategoryCooldown = fields[73 + 6 * i + 5].GetInt32(); + } + + itemTemplate.SpellPPMRate = 0.0f; + itemTemplate.Bonding = uint32(fields[103].GetUInt8()); + itemTemplate.Description = fields[104].GetString(); + itemTemplate.PageText = fields[105].GetUInt32(); + itemTemplate.LanguageID = uint32(fields[106].GetUInt8()); + itemTemplate.PageMaterial = uint32(fields[107].GetUInt8()); + itemTemplate.StartQuest = fields[108].GetUInt32(); + itemTemplate.LockID = fields[109].GetUInt32(); + itemTemplate.Material = int32(fields[110].GetInt8()); + itemTemplate.Sheath = uint32(fields[111].GetUInt8()); + itemTemplate.RandomProperty = fields[112].GetUInt32(); + itemTemplate.RandomSuffix = fields[113].GetInt32(); + itemTemplate.ItemSet = fields[114].GetUInt32(); + itemTemplate.MaxDurability = FillMaxDurability(itemTemplate.Class, itemTemplate.SubClass, + itemTemplate.InventoryType, itemTemplate.Quality, itemTemplate.ItemLevel); + + itemTemplate.Area = fields[115].GetUInt32(); + itemTemplate.Map = uint32(fields[116].GetUInt16()); + itemTemplate.BagFamily = fields[117].GetUInt32(); + itemTemplate.TotemCategory = fields[118].GetUInt32(); + for (uint32 i = 0; i < MAX_ITEM_PROTO_SOCKETS; ++i) + { + itemTemplate.Socket[i].Color = uint32(fields[119 + i*2].GetUInt8()); + itemTemplate.Socket[i].Content = fields[119 + i * 2 + 1].GetUInt32(); + } + + itemTemplate.socketBonus = fields[125].GetUInt32(); + itemTemplate.GemProperties = fields[126].GetUInt32(); + FillDisenchantFields(&itemTemplate.DisenchantID, &itemTemplate.RequiredDisenchantSkill, itemTemplate); + + itemTemplate.ArmorDamageModifier = fields[127].GetFloat(); + itemTemplate.Duration = fields[128].GetUInt32(); + itemTemplate.ItemLimitCategory = uint32(fields[129].GetInt16()); + itemTemplate.HolidayId = fields[130].GetUInt32(); + itemTemplate.StatScalingFactor = fields[131].GetFloat(); + itemTemplate.CurrencySubstitutionId = fields[132].GetInt32(); + itemTemplate.CurrencySubstitutionCount = fields[133].GetInt32(); + itemTemplate.ScriptId = 0; + itemTemplate.FoodType = 0; + itemTemplate.MinMoneyLoot = 0; + itemTemplate.MaxMoneyLoot = 0; + ++dbCount; + } while (result->NextRow()); } - while (result->NextRow()); // Check if item templates for DBC referenced character start outfit are present std::set<uint32> notFoundOutfit; @@ -2723,124 +2670,77 @@ void ObjectMgr::LoadItemTemplates() for (std::set<uint32>::const_iterator itr = notFoundOutfit.begin(); itr != notFoundOutfit.end(); ++itr) sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) does not exist in `item_template` but is referenced in `CharStartOutfit.dbc`", *itr); - sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u item templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); -} - -ItemTemplate const* ObjectMgr::GetItemTemplate(uint32 entry) -{ - ItemTemplateContainer::const_iterator itr = _itemTemplateStore.find(entry); - if (itr != _itemTemplateStore.end()) - return &(itr->second); - - return NULL; + sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u item templates from Item-sparse.db2 and %u from database in %u ms", sparseCount, dbCount, GetMSTimeDiffToNow(oldMSTime)); } -void ObjectMgr::LoadItemSetNameLocales() +void ObjectMgr::LoadItemTemplateAddon() { uint32 oldMSTime = getMSTime(); + uint32 count = 0; - _itemSetNameLocaleStore.clear(); // need for reload case - - QueryResult result = WorldDatabase.Query("SELECT `entry`, `name_loc1`, `name_loc2`, `name_loc3`, `name_loc4`, `name_loc5`, `name_loc6`, `name_loc7`, `name_loc8` FROM `locales_item_set_names`"); - - if (!result) - return; - - do + QueryResult result = WorldDatabase.Query("SELECT Id, FlagsCu, FoodType, MinMoneyLoot, MaxMoneyLoot, SpellPPMChance FROM item_template_addon"); + if (result) { - Field* fields = result->Fetch(); - - uint32 entry = fields[0].GetUInt32(); - - ItemSetNameLocale& data = _itemSetNameLocaleStore[entry]; - - for (uint8 i = 1; i < TOTAL_LOCALES; ++i) - AddLocaleString(fields[i].GetString(), LocaleConstant(i), data.Name); - } while (result->NextRow()); + do + { + Field* fields = result->Fetch(); + uint32 itemId = fields[0].GetUInt32(); + if (!GetItemTemplate(itemId)) + { + sLog->outError(LOG_FILTER_SQL, "Item %u specified in `item_template_addon` does not exist, skipped.", itemId); + continue; + } - sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded " UI64FMTD " Item set name locale strings in %u ms", uint64(_itemSetNameLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime)); + uint32 minMoneyLoot = fields[3].GetUInt32(); + uint32 maxMoneyLoot = fields[4].GetUInt32(); + if (minMoneyLoot > maxMoneyLoot) + { + sLog->outError(LOG_FILTER_SQL, "Minimum money loot specified in `item_template_addon` for item %u was greater than maximum amount, swapping.", itemId); + std::swap(minMoneyLoot, maxMoneyLoot); + } + ItemTemplate& itemTemplate = _itemTemplateStore[itemId]; + itemTemplate.FlagsCu = fields[1].GetUInt32(); + itemTemplate.FoodType = fields[2].GetUInt8(); + itemTemplate.MinMoneyLoot = minMoneyLoot; + itemTemplate.MaxMoneyLoot = maxMoneyLoot; + itemTemplate.SpellPPMRate = fields[5].GetFloat(); + ++count; + } while (result->NextRow()); + } + sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u item addon templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } -void ObjectMgr::LoadItemSetNames() +void ObjectMgr::LoadItemScriptNames() { uint32 oldMSTime = getMSTime(); - - _itemSetNameStore.clear(); // needed for reload case - - std::set<uint32> itemSetItems; - - // fill item set member ids - for (uint32 entryId = 0; entryId < sItemSetStore.GetNumRows(); ++entryId) - { - ItemSetEntry const* setEntry = sItemSetStore.LookupEntry(entryId); - if (!setEntry) - continue; - - for (uint32 i = 0; i < MAX_ITEM_SET_ITEMS; ++i) - if (setEntry->itemId[i]) - itemSetItems.insert(setEntry->itemId[i]); - } - - // 0 1 2 - QueryResult result = WorldDatabase.Query("SELECT `entry`, `name`, `InventoryType` FROM `item_set_names`"); - - if (!result) - { - sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 item set names. DB table `item_set_names` is empty."); - return; - } - - _itemSetNameStore.rehash(result->GetRowCount()); uint32 count = 0; - do - { - Field* fields = result->Fetch(); - - uint32 entry = fields[0].GetUInt32(); - if (itemSetItems.find(entry) == itemSetItems.end()) - { - sLog->outError(LOG_FILTER_SQL, "Item set name (Entry: %u) not found in ItemSet.dbc, data useless.", entry); - continue; - } - - ItemSetNameEntry &data = _itemSetNameStore[entry]; - data.name = fields[1].GetString(); - - uint32 invType = fields[2].GetUInt8(); - if (invType >= MAX_INVTYPE) - { - sLog->outError(LOG_FILTER_SQL, "Item set name (Entry: %u) has wrong InventoryType value (%u)", entry, invType); - invType = INVTYPE_NON_EQUIP; - } - - data.InventoryType = invType; - itemSetItems.erase(entry); - ++count; - } while (result->NextRow()); - - if (!itemSetItems.empty()) + QueryResult result = WorldDatabase.Query("SELECT Id, ScriptName FROM item_script_names"); + if (result) { - ItemTemplate const* pProto; - for (std::set<uint32>::iterator itr = itemSetItems.begin(); itr != itemSetItems.end(); ++itr) + do { - uint32 entry = *itr; - // add data from item_template if available - pProto = sObjectMgr->GetItemTemplate(entry); - if (pProto) + Field* fields = result->Fetch(); + uint32 itemId = fields[0].GetUInt32(); + if (!GetItemTemplate(itemId)) { - sLog->outError(LOG_FILTER_SQL, "Item set part (Entry: %u) does not have entry in `item_set_names`, adding data from `item_template`.", entry); - ItemSetNameEntry &data = _itemSetNameStore[entry]; - data.name = pProto->Name1; - data.InventoryType = pProto->InventoryType; - ++count; + sLog->outError(LOG_FILTER_SQL, "Item %u specified in `item_script_names` does not exist, skipped.", itemId); + continue; } - else - sLog->outError(LOG_FILTER_SQL, "Item set part (Entry: %u) does not have entry in `item_set_names`, set will not display properly.", entry); - } + + _itemTemplateStore[itemId].ScriptId = GetScriptId(fields[1].GetCString()); + ++count; + } while (result->NextRow()); } - sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u item set names in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u item script names in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); +} +ItemTemplate const* ObjectMgr::GetItemTemplate(uint32 entry) +{ + ItemTemplateContainer::const_iterator itr = _itemTemplateStore.find(entry); + if (itr != _itemTemplateStore.end()) + return &(itr->second); + return NULL; } void ObjectMgr::LoadVehicleTemplateAccessories() @@ -3343,88 +3243,6 @@ void ObjectMgr::LoadPlayerInfo() } } - // Loading levels data (class only dependent) - sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Player Create Level HP/Mana Data..."); - { - uint32 oldMSTime = getMSTime(); - - // 0 1 2 3 - QueryResult result = WorldDatabase.Query("SELECT class, level, basehp, basemana FROM player_classlevelstats"); - - if (!result) - { - sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 level health/mana definitions. DB table `game_event_condition` is empty."); - exit(1); - } - - uint32 count = 0; - - do - { - Field* fields = result->Fetch(); - - uint32 current_class = fields[0].GetUInt8(); - if (current_class >= MAX_CLASSES) - { - sLog->outError(LOG_FILTER_SQL, "Wrong class %u in `player_classlevelstats` table, ignoring.", current_class); - continue; - } - - uint8 current_level = fields[1].GetUInt8(); // Can't be > than STRONG_MAX_LEVEL (hardcoded level maximum) due to var type - if (current_level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) - { - sLog->outInfo(LOG_FILTER_GENERAL, "Unused (> MaxPlayerLevel in worldserver.conf) level %u in `player_classlevelstats` table, ignoring.", current_level); - ++count; // make result loading percent "expected" correct in case disabled detail mode for example. - continue; - } - - PlayerClassInfo* info = _playerClassInfo[current_class]; - if (!info) - { - info = new PlayerClassInfo(); - info->levelInfo = new PlayerClassLevelInfo[sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)]; - _playerClassInfo[current_class] = info; - } - - PlayerClassLevelInfo& levelInfo = info->levelInfo[current_level-1]; - - levelInfo.basehealth = fields[2].GetUInt16(); - levelInfo.basemana = fields[3].GetUInt16(); - - ++count; - } - while (result->NextRow()); - - // Fill gaps and check integrity - for (int class_ = 0; class_ < MAX_CLASSES; ++class_) - { - // skip non existed classes - if (!sChrClassesStore.LookupEntry(class_)) - continue; - - PlayerClassInfo* pClassInfo = _playerClassInfo[class_]; - - // fatal error if no level 1 data - if (!pClassInfo->levelInfo || pClassInfo->levelInfo[0].basehealth == 0) - { - sLog->outError(LOG_FILTER_SQL, "Class %i Level 1 does not have health/mana data!", class_); - exit(1); - } - - // fill level gaps - for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level) - { - if (pClassInfo->levelInfo[level].basehealth == 0) - { - sLog->outError(LOG_FILTER_SQL, "Class %i Level %i does not have health/mana data. Using stats data of level %i.", class_, level+1, level); - pClassInfo->levelInfo[level] = pClassInfo->levelInfo[level-1]; - } - } - } - - sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u level health/mana definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); - } - // Loading levels data (class/race dependent) sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Player Create Level Stats Data..."); { @@ -3591,17 +3409,25 @@ void ObjectMgr::LoadPlayerInfo() } } -void ObjectMgr::GetPlayerClassLevelInfo(uint32 class_, uint8 level, PlayerClassLevelInfo* info) const +void ObjectMgr::GetPlayerClassLevelInfo(uint32 class_, uint8 level, uint32& baseHP, uint32& baseMana) const { if (level < 1 || class_ >= MAX_CLASSES) return; - PlayerClassInfo const* pInfo = _playerClassInfo[class_]; - if (level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) level = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); - *info = pInfo->levelInfo[level-1]; + GtOCTBaseHPByClassEntry const* hp = sGtOCTBaseHPByClassStore.LookupEntry((class_-1) * GT_MAX_LEVEL + level-1); + GtOCTBaseMPByClassEntry const* mp = sGtOCTBaseMPByClassStore.LookupEntry((class_-1) * GT_MAX_LEVEL + level-1); + + if (!hp || !mp) + { + sLog->outError(LOG_FILTER_GENERAL, "Tried to get non-existant Class-Level combination data for base hp/mp. Class %u Level %u", class_, level); + return; + } + + baseHP = uint32(hp->ratio); + baseMana = uint32(mp->ratio); } void ObjectMgr::GetPlayerLevelInfo(uint32 race, uint32 class_, uint8 level, PlayerLevelInfo* info) const @@ -3713,33 +3539,37 @@ void ObjectMgr::LoadQuests() "RequiredFactionId1, RequiredFactionId2, RequiredFactionValue1, RequiredFactionValue2, RequiredMinRepFaction, RequiredMaxRepFaction, RequiredMinRepValue, RequiredMaxRepValue, " // 21 22 23 24 25 26 27 28 29 30 31 "PrevQuestId, NextQuestId, ExclusiveGroup, NextQuestIdChain, RewardXPId, RewardOrRequiredMoney, RewardMoneyMaxLevel, RewardSpell, RewardSpellCast, RewardHonor, RewardHonorMultiplier, " - // 32 33 34 35 36 37 38 39 40 41 42 - "RewardMailTemplateId, RewardMailDelay, SourceItemId, SourceItemCount, SourceSpellId, Flags, SpecialFlags, RewardTitleId, RequiredPlayerKills, RewardTalents, RewardArenaPoints, " - // 43 44 45 46 47 48 49 50 - "RewardItemId1, RewardItemId2, RewardItemId3, RewardItemId4, RewardItemCount1, RewardItemCount2, RewardItemCount3, RewardItemCount4, " - // 51 52 53 54 55 56 57 58 59 60 61 62 + // 32 33 34 35 36 37 38 39 40 41 42 43 + "RewardMailTemplateId, RewardMailDelay, SourceItemId, SourceItemCount, SourceSpellId, Flags, SpecialFlags, MinimapTargetMark, RewardTitleId, RequiredPlayerKills, RewardTalents, RewardArenaPoints, " + // 44 45 46 47 48 49 50 51 52 53 54 55 56 + "RewardSkillId, RewardSkillPoints, RewardReputationMask, QuestGiverPortrait, QuestTurnInPortrait, RewardItemId1, RewardItemId2, RewardItemId3, RewardItemId4, RewardItemCount1, RewardItemCount2, RewardItemCount3, RewardItemCount4, " + // 57 58 59 60 61 62 63 64 65 66 67 68 "RewardChoiceItemId1, RewardChoiceItemId2, RewardChoiceItemId3, RewardChoiceItemId4, RewardChoiceItemId5, RewardChoiceItemId6, RewardChoiceItemCount1, RewardChoiceItemCount2, RewardChoiceItemCount3, RewardChoiceItemCount4, RewardChoiceItemCount5, RewardChoiceItemCount6, " - // 63 64 65 66 67 68 69 70 71 72 + // 69 70 71 72 73 74 75 76 77 78 "RewardFactionId1, RewardFactionId2, RewardFactionId3, RewardFactionId4, RewardFactionId5, RewardFactionValueId1, RewardFactionValueId2, RewardFactionValueId3, RewardFactionValueId4, RewardFactionValueId5, " - // 73 74 75 76 77 + // 79 80 81 82 83 "RewardFactionValueIdOverride1, RewardFactionValueIdOverride2, RewardFactionValueIdOverride3, RewardFactionValueIdOverride4, RewardFactionValueIdOverride5, " - // 78 79 80 81 - "PointMapId, PointX, PointY, PointOption, " - // 82 83 84 85 86 87 88 - "Title, Objectives, Details, EndText, OfferRewardText, RequestItemsText, CompletedText, " - // 89 90 91 92 93 94 95 96 + // 84 85 86 87 88 89 90 91 92 93 94 + "PointMapId, PointX, PointY, PointOption, Title, Objectives, Details, EndText, CompletedText, OfferRewardText, RequestItemsText, " + // 95 96 97 98 99 100 101 102 "RequiredNpcOrGo1, RequiredNpcOrGo2, RequiredNpcOrGo3, RequiredNpcOrGo4, RequiredNpcOrGoCount1, RequiredNpcOrGoCount2, RequiredNpcOrGoCount3, RequiredNpcOrGoCount4, " - // 97 98 99 100 101 102 103 104 + // 103 104 105 106 107 108 109 110 "RequiredSourceItemId1, RequiredSourceItemId2, RequiredSourceItemId3, RequiredSourceItemId4, RequiredSourceItemCount1, RequiredSourceItemCount2, RequiredSourceItemCount3, RequiredSourceItemCount4, " - // 105 106 107 108 109 110 111 112 113 114 115 116 + // 111 112 113 114 115 116 117 118 119 120 121 122 "RequiredItemId1, RequiredItemId2, RequiredItemId3, RequiredItemId4, RequiredItemId5, RequiredItemId6, RequiredItemCount1, RequiredItemCount2, RequiredItemCount3, RequiredItemCount4, RequiredItemCount5, RequiredItemCount6, " - // 117 118 119 120 121 122 123 124 125 - "RequiredSpellCast1, RequiredSpellCast2, RequiredSpellCast3, RequiredSpellCast4, Unknown0, ObjectiveText1, ObjectiveText2, ObjectiveText3, ObjectiveText4, " - // 126 127 128 129 130 131 132 133 134 135 + // 123 124 125 126 127 128 129 130 131 + "RequiredSpell, RequiredSpellCast1, RequiredSpellCast2, RequiredSpellCast3, RequiredSpellCast4, ObjectiveText1, ObjectiveText2, ObjectiveText3, ObjectiveText4, " + // 132 133 134 135 136 137 138 139 + "RewardCurrencyId1, RewardCurrencyId2, RewardCurrencyId3, RewardCurrencyId4, RewardCurrencyCount1, RewardCurrencyCount2, RewardCurrencyCount3, RewardCurrencyCount4, " + // 140 141 142 143 144 145 146 147 + "RequiredCurrencyId1, RequiredCurrencyId2, RequiredCurrencyId3, RequiredCurrencyId4, RequiredCurrencyCount1, RequiredCurrencyCount2, RequiredCurrencyCount3, RequiredCurrencyCount4, " + // 148 149 150 151 152 153 + "QuestGiverTextWindow, QuestGiverTargetName, QuestTurnTextWindow, QuestTurnTargetName, SoundAccept, SoundTurnIn, " + // 154 155 156 157 158 159 160 161 162 163 "DetailsEmote1, DetailsEmote2, DetailsEmote3, DetailsEmote4, DetailsEmoteDelay1, DetailsEmoteDelay2, DetailsEmoteDelay3, DetailsEmoteDelay4, EmoteOnIncomplete, EmoteOnComplete, " - // 136 137 138 139 140 141 142 143 + // 164 165 166 167 168 169 170 171 "OfferRewardEmote1, OfferRewardEmote2, OfferRewardEmote3, OfferRewardEmote4, OfferRewardEmoteDelay1, OfferRewardEmoteDelay2, OfferRewardEmoteDelay3, OfferRewardEmoteDelay4, " - // 144 + // 173 "WDBVerified" " FROM quest_template"); if (!result) @@ -3775,11 +3605,11 @@ void ObjectMgr::LoadQuests() if (qinfo->GetQuestMethod() >= 3) sLog->outError(LOG_FILTER_SQL, "Quest %u has `Method` = %u, expected values are 0, 1 or 2.", qinfo->GetQuestId(), qinfo->GetQuestMethod()); - if (qinfo->Flags & ~QUEST_TRINITY_FLAGS_DB_ALLOWED) + if (qinfo->SpecialFlags & ~QUEST_SPECIAL_FLAGS_DB_ALLOWED) { sLog->outError(LOG_FILTER_SQL, "Quest %u has `SpecialFlags` = %u > max allowed value. Correct `SpecialFlags` to value <= %u", - qinfo->GetQuestId(), qinfo->Flags >> 20, QUEST_TRINITY_FLAGS_DB_ALLOWED >> 20); - qinfo->Flags &= QUEST_TRINITY_FLAGS_DB_ALLOWED; + qinfo->GetQuestId(), qinfo->SpecialFlags, QUEST_SPECIAL_FLAGS_DB_ALLOWED); + qinfo->SpecialFlags &= QUEST_SPECIAL_FLAGS_DB_ALLOWED; } if (qinfo->Flags & QUEST_FLAGS_DAILY && qinfo->Flags & QUEST_FLAGS_WEEKLY) @@ -3790,28 +3620,28 @@ void ObjectMgr::LoadQuests() if (qinfo->Flags & QUEST_FLAGS_DAILY) { - if (!(qinfo->Flags & QUEST_TRINITY_FLAGS_REPEATABLE)) + if (!(qinfo->SpecialFlags & QUEST_SPECIAL_FLAGS_REPEATABLE)) { sLog->outError(LOG_FILTER_SQL, "Daily Quest %u not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId()); - qinfo->Flags |= QUEST_TRINITY_FLAGS_REPEATABLE; + qinfo->SpecialFlags |= QUEST_SPECIAL_FLAGS_REPEATABLE; } } if (qinfo->Flags & QUEST_FLAGS_WEEKLY) { - if (!(qinfo->Flags & QUEST_TRINITY_FLAGS_REPEATABLE)) + if (!(qinfo->SpecialFlags & QUEST_SPECIAL_FLAGS_REPEATABLE)) { sLog->outError(LOG_FILTER_SQL, "Weekly Quest %u not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId()); - qinfo->Flags |= QUEST_TRINITY_FLAGS_REPEATABLE; + qinfo->SpecialFlags |= QUEST_SPECIAL_FLAGS_REPEATABLE; } } - if (qinfo->Flags & QUEST_TRINITY_FLAGS_MONTHLY) + if (qinfo->Flags & QUEST_SPECIAL_FLAGS_MONTHLY) { - if (!(qinfo->Flags & QUEST_TRINITY_FLAGS_REPEATABLE)) + if (!(qinfo->Flags & QUEST_SPECIAL_FLAGS_REPEATABLE)) { sLog->outError(LOG_FILTER_SQL, "Monthly quest %u not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId()); - qinfo->Flags |= QUEST_TRINITY_FLAGS_REPEATABLE; + qinfo->Flags |= QUEST_SPECIAL_FLAGS_REPEATABLE; } } @@ -3829,6 +3659,12 @@ void ObjectMgr::LoadQuests() } } + if (qinfo->MinLevel == uint32(-1) || qinfo->MinLevel > DEFAULT_MAX_LEVEL) + { + sLog->outError(LOG_FILTER_SQL, "Quest %u should be disabled because `MinLevel` = %i", qinfo->GetQuestId(), int32(qinfo->MinLevel)); + // no changes needed, sending -1 in SMSG_QUEST_QUERY_RESPONSE is valid + } + // client quest log visual (area case) if (qinfo->ZoneOrSort > 0) { @@ -4029,7 +3865,7 @@ void ObjectMgr::LoadQuests() // no changes, quest can't be done for this requirement } - qinfo->SetFlag(QUEST_TRINITY_FLAGS_DELIVER); + qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAGS_DELIVER); if (!sObjectMgr->GetItemTemplate(id)) { @@ -4038,7 +3874,7 @@ void ObjectMgr::LoadQuests() qinfo->RequiredItemCount[j] = 0; // prevent incorrect work of quest } } - else if (qinfo->RequiredItemCount[j]>0) + else if (qinfo->RequiredItemCount[j] > 0) { sLog->outError(LOG_FILTER_SQL, "Quest %u has `RequiredItemId%d` = 0 but `RequiredItemCount%d` = %u, quest can't be done.", qinfo->GetQuestId(), j+1, j+1, qinfo->RequiredItemCount[j]); @@ -4097,12 +3933,12 @@ void ObjectMgr::LoadQuests() if (found) { - if (!qinfo->HasFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT)) + if (!qinfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT)) { - sLog->outError(LOG_FILTER_SQL, "Spell (id: %u) have SPELL_EFFECT_QUEST_COMPLETE or SPELL_EFFECT_SEND_EVENT for quest %u and RequiredNpcOrGo%d = 0, but quest not have flag QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT. Quest flags or RequiredNpcOrGo%d must be fixed, quest modified to enable objective.", spellInfo->Id, qinfo->Id, j+1, j+1); + sLog->outError(LOG_FILTER_SQL, "Spell (id: %u) have SPELL_EFFECT_QUEST_COMPLETE or SPELL_EFFECT_SEND_EVENT for quest %u and RequiredNpcOrGo%d = 0, but quest not have flag QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT. Quest flags or RequiredNpcOrGo%d must be fixed, quest modified to enable objective.", spellInfo->Id, qinfo->Id, j+1, j+1); // this will prevent quest completing without objective - const_cast<Quest*>(qinfo)->SetFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT); + const_cast<Quest*>(qinfo)->SetSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT); } } else @@ -4136,7 +3972,7 @@ void ObjectMgr::LoadQuests() { // In fact SpeakTo and Kill are quite same: either you can speak to mob:SpeakTo or you can't:Kill/Cast - qinfo->SetFlag(QUEST_TRINITY_FLAGS_KILL_OR_CAST | QUEST_TRINITY_FLAGS_SPEAKTO); + qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAGS_KILL_OR_CAST | QUEST_SPECIAL_FLAGS_SPEAKTO); if (!qinfo->RequiredNpcOrGoCount[j]) { @@ -4316,26 +4152,149 @@ void ObjectMgr::LoadQuests() qNextItr->second->prevChainQuests.push_back(qinfo->GetQuestId()); } + for (uint8 j = 0; j < QUEST_REWARD_CURRENCY_COUNT; ++j) + { + if (qinfo->RewardCurrencyId[j]) + { + if (qinfo->RewardCurrencyCount[j] == 0) + { + sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardCurrencyId%d` = %u but `RewardCurrencyCount%d` = 0, quest can't be done.", + qinfo->GetQuestId(), j+1, qinfo->RewardCurrencyId[j], j+1); + // no changes, quest can't be done for this requirement + } + + if (!sCurrencyTypesStore.LookupEntry(qinfo->RewardCurrencyId[j])) + { + sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardCurrencyId%d` = %u but currency with entry %u does not exist, quest can't be done.", + qinfo->GetQuestId(), j+1, qinfo->RewardCurrencyId[j], qinfo->RewardCurrencyId[j]); + qinfo->RewardCurrencyCount[j] = 0; // prevent incorrect work of quest + } + } + else if (qinfo->RewardCurrencyCount[j] > 0) + { + sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardCurrencyId%d` = 0 but `RewardCurrencyCount%d` = %u, quest can't be done.", + qinfo->GetQuestId(), j+1, j+1, qinfo->RewardCurrencyCount[j]); + qinfo->RewardCurrencyCount[j] = 0; // prevent incorrect work of quest + } + } + + for (uint8 j = 0; j < QUEST_REQUIRED_CURRENCY_COUNT; ++j) + { + if (qinfo->RequiredCurrencyId[j]) + { + if (qinfo->RequiredCurrencyCount[j] == 0) + { + sLog->outError(LOG_FILTER_SQL, "Quest %u has `RequiredCurrencyId%d` = %u but `RequiredCurrencyCount%d` = 0, quest can't be done.", + qinfo->GetQuestId(), j+1, qinfo->RequiredCurrencyId[j], j+1); + // no changes, quest can't be done for this requirement + } + + if (!sCurrencyTypesStore.LookupEntry(qinfo->RequiredCurrencyId[j])) + { + sLog->outError(LOG_FILTER_SQL, "Quest %u has `RequiredCurrencyId%d` = %u but currency with entry %u does not exist, quest can't be done.", + qinfo->GetQuestId(), j+1, qinfo->RequiredCurrencyId[j], qinfo->RequiredCurrencyId[j]); + qinfo->RequiredCurrencyCount[j] = 0; // prevent incorrect work of quest + } + } + else if (qinfo->RequiredCurrencyCount[j] > 0) + { + sLog->outError(LOG_FILTER_SQL, "Quest %u has `RequiredCurrencyId%d` = 0 but `RequiredCurrencyCount%d` = %u, quest can't be done.", + qinfo->GetQuestId(), j+1, j+1, qinfo->RequiredCurrencyCount[j]); + qinfo->RequiredCurrencyCount[j] = 0; // prevent incorrect work of quest + } + } + + if (qinfo->SoundAccept) + { + if (!sSoundEntriesStore.LookupEntry(qinfo->SoundAccept)) + { + sLog->outError(LOG_FILTER_SQL, "Quest %u has `SoundAccept` = %u but sound %u does not exist, set to 0.", + qinfo->GetQuestId(), qinfo->SoundAccept, qinfo->SoundAccept); + qinfo->SoundAccept = 0; // no sound will be played + } + } + + if (qinfo->SoundTurnIn) + { + if (!sSoundEntriesStore.LookupEntry(qinfo->SoundTurnIn)) + { + sLog->outError(LOG_FILTER_SQL, "Quest %u has `SoundTurnIn` = %u but sound %u does not exist, set to 0.", + qinfo->GetQuestId(), qinfo->SoundTurnIn, qinfo->SoundTurnIn); + qinfo->SoundTurnIn = 0; // no sound will be played + } + } + + if (qinfo->RequiredSpell > 0) + { + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(qinfo->RequiredSpell); + + if (!spellInfo) + { + sLog->outError(LOG_FILTER_SQL, "Quest %u has `RequiredSpell` = %u but spell %u does not exist, quest will not require a spell.", + qinfo->GetQuestId(), qinfo->RequiredSpell, qinfo->RequiredSpell); + qinfo->RequiredSpell = 0; // no spell will be required + } + + else if (!SpellMgr::IsSpellValid(spellInfo)) + { + sLog->outError(LOG_FILTER_SQL, "Quest %u has `RequiredSpell` = %u but spell %u is broken, quest will not require a spell.", + qinfo->GetQuestId(), qinfo->RequiredSpell, qinfo->RequiredSpell); + qinfo->RequiredSpell = 0; // no spell will be required + } + + /* Can we require talents? + else if (GetTalentSpellCost(qinfo->RewardSpellCast)) + { + sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardSpell` = %u but spell %u is talent, quest will not have a spell reward.", + qinfo->GetQuestId(), qinfo->RewardSpellCast, qinfo->RewardSpellCast); + qinfo->RewardSpellCast = 0; // no spell will be casted on player + } + }*/ + } + + if (qinfo->RewardSkillId) + { + if (!sSkillLineStore.LookupEntry(qinfo->RewardSkillId)) + { + sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardSkillId` = %u but this skill does not exist", + qinfo->GetQuestId(), qinfo->RewardSkillId); + } + if (!qinfo->RewardSkillPoints) + { + sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardSkillId` = %u but `RewardSkillPoints` is 0", + qinfo->GetQuestId(), qinfo->RewardSkillId); + } + } + + if (qinfo->RewardSkillPoints) + { + if (qinfo->RewardSkillPoints > sWorld->GetConfigMaxSkillValue()) + { + sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardSkillPoints` = %u but max possible skill is %u, quest can't be done.", + qinfo->GetQuestId(), qinfo->RewardSkillPoints, sWorld->GetConfigMaxSkillValue()); + // no changes, quest can't be done for this requirement + } + if (!qinfo->RewardSkillId) + { + sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardSkillPoints` = %u but `RewardSkillId` is 0", + qinfo->GetQuestId(), qinfo->RewardSkillPoints); + } + } + // fill additional data stores if (qinfo->PrevQuestId) { if (_questTemplates.find(abs(qinfo->GetPrevQuestId())) == _questTemplates.end()) - { sLog->outError(LOG_FILTER_SQL, "Quest %d has PrevQuestId %i, but no such quest", qinfo->GetQuestId(), qinfo->GetPrevQuestId()); - } else - { qinfo->prevQuests.push_back(qinfo->PrevQuestId); - } } if (qinfo->NextQuestId) { QuestMap::iterator qNextItr = _questTemplates.find(abs(qinfo->GetNextQuestId())); if (qNextItr == _questTemplates.end()) - { sLog->outError(LOG_FILTER_SQL, "Quest %d has NextQuestId %i, but no such quest", qinfo->GetQuestId(), qinfo->GetNextQuestId()); - } else { int32 signedQuestId = qinfo->NextQuestId < 0 ? -int32(qinfo->GetQuestId()) : int32(qinfo->GetQuestId()); @@ -4346,12 +4305,12 @@ void ObjectMgr::LoadQuests() if (qinfo->ExclusiveGroup) mExclusiveQuestGroups.insert(std::pair<int32, uint32>(qinfo->ExclusiveGroup, qinfo->GetQuestId())); if (qinfo->LimitTime) - qinfo->SetFlag(QUEST_TRINITY_FLAGS_TIMED); + qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAGS_TIMED); if (qinfo->RequiredPlayerKills) - qinfo->SetFlag(QUEST_TRINITY_FLAGS_PLAYER_KILL); + qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAGS_PLAYER_KILL); } - // check QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT for spell with SPELL_EFFECT_QUEST_COMPLETE + // check QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT for spell with SPELL_EFFECT_QUEST_COMPLETE for (uint32 i = 0; i < sSpellMgr->GetSpellInfoStoreSize(); ++i) { SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(i); @@ -4371,12 +4330,12 @@ void ObjectMgr::LoadQuests() if (!quest) continue; - if (!quest->HasFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT)) + if (!quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT)) { - sLog->outError(LOG_FILTER_SQL, "Spell (id: %u) have SPELL_EFFECT_QUEST_COMPLETE for quest %u, but quest not have flag QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT. Quest flags must be fixed, quest modified to enable objective.", spellInfo->Id, quest_id); + sLog->outError(LOG_FILTER_SQL, "Spell (id: %u) have SPELL_EFFECT_QUEST_COMPLETE for quest %u, but quest not have flag QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT. Quest flags must be fixed, quest modified to enable objective.", spellInfo->Id, quest_id); // this will prevent quest completing without objective - const_cast<Quest*>(quest)->SetFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT); + const_cast<Quest*>(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT); } } } @@ -4391,14 +4350,14 @@ void ObjectMgr::LoadQuestLocales() _questLocaleStore.clear(); // need for reload case QueryResult result = WorldDatabase.Query("SELECT Id, " - "Title_loc1, Details_loc1, Objectives_loc1, OfferRewardText_loc1, RequestItemsText_loc1, EndText_loc1, CompletedText_loc1, ObjectiveText1_loc1, ObjectiveText2_loc1, ObjectiveText3_loc1, ObjectiveText4_loc1, " - "Title_loc2, Details_loc2, Objectives_loc2, OfferRewardText_loc2, RequestItemsText_loc2, EndText_loc2, CompletedText_loc2, ObjectiveText1_loc2, ObjectiveText2_loc2, ObjectiveText3_loc2, ObjectiveText4_loc2, " - "Title_loc3, Details_loc3, Objectives_loc3, OfferRewardText_loc3, RequestItemsText_loc3, EndText_loc3, CompletedText_loc3, ObjectiveText1_loc3, ObjectiveText2_loc3, ObjectiveText3_loc3, ObjectiveText4_loc3, " - "Title_loc4, Details_loc4, Objectives_loc4, OfferRewardText_loc4, RequestItemsText_loc4, EndText_loc4, CompletedText_loc4, ObjectiveText1_loc4, ObjectiveText2_loc4, ObjectiveText3_loc4, ObjectiveText4_loc4, " - "Title_loc5, Details_loc5, Objectives_loc5, OfferRewardText_loc5, RequestItemsText_loc5, EndText_loc5, CompletedText_loc5, ObjectiveText1_loc5, ObjectiveText2_loc5, ObjectiveText3_loc5, ObjectiveText4_loc5, " - "Title_loc6, Details_loc6, Objectives_loc6, OfferRewardText_loc6, RequestItemsText_loc6, EndText_loc6, CompletedText_loc6, ObjectiveText1_loc6, ObjectiveText2_loc6, ObjectiveText3_loc6, ObjectiveText4_loc6, " - "Title_loc7, Details_loc7, Objectives_loc7, OfferRewardText_loc7, RequestItemsText_loc7, EndText_loc7, CompletedText_loc7, ObjectiveText1_loc7, ObjectiveText2_loc7, ObjectiveText3_loc7, ObjectiveText4_loc7, " - "Title_loc8, Details_loc8, Objectives_loc8, OfferRewardText_loc8, RequestItemsText_loc8, EndText_loc8, CompletedText_loc8, ObjectiveText1_loc8, ObjectiveText2_loc8, ObjectiveText3_loc8, ObjectiveText4_loc8" + "Title_loc1, Details_loc1, Objectives_loc1, OfferRewardText_loc1, RequestItemsText_loc1, EndText_loc1, CompletedText_loc1, ObjectiveText1_loc1, ObjectiveText2_loc1, ObjectiveText3_loc1, ObjectiveText4_loc1, QuestGiverTextWindow_loc1, QuestGiverTargetName_loc1, QuestTurnTextWindow_loc1, QuestTurnTargetName_loc1," + "Title_loc2, Details_loc2, Objectives_loc2, OfferRewardText_loc2, RequestItemsText_loc2, EndText_loc2, CompletedText_loc2, ObjectiveText1_loc2, ObjectiveText2_loc2, ObjectiveText3_loc2, ObjectiveText4_loc2, QuestGiverTextWindow_loc2, QuestGiverTargetName_loc2, QuestTurnTextWindow_loc2, QuestTurnTargetName_loc2," + "Title_loc3, Details_loc3, Objectives_loc3, OfferRewardText_loc3, RequestItemsText_loc3, EndText_loc3, CompletedText_loc3, ObjectiveText1_loc3, ObjectiveText2_loc3, ObjectiveText3_loc3, ObjectiveText4_loc3, QuestGiverTextWindow_loc3, QuestGiverTargetName_loc3, QuestTurnTextWindow_loc3, QuestTurnTargetName_loc3," + "Title_loc4, Details_loc4, Objectives_loc4, OfferRewardText_loc4, RequestItemsText_loc4, EndText_loc4, CompletedText_loc4, ObjectiveText1_loc4, ObjectiveText2_loc4, ObjectiveText3_loc4, ObjectiveText4_loc4, QuestGiverTextWindow_loc4, QuestGiverTargetName_loc4, QuestTurnTextWindow_loc4, QuestTurnTargetName_loc4," + "Title_loc5, Details_loc5, Objectives_loc5, OfferRewardText_loc5, RequestItemsText_loc5, EndText_loc5, CompletedText_loc5, ObjectiveText1_loc5, ObjectiveText2_loc5, ObjectiveText3_loc5, ObjectiveText4_loc5, QuestGiverTextWindow_loc5, QuestGiverTargetName_loc5, QuestTurnTextWindow_loc5, QuestTurnTargetName_loc5," + "Title_loc6, Details_loc6, Objectives_loc6, OfferRewardText_loc6, RequestItemsText_loc6, EndText_loc6, CompletedText_loc6, ObjectiveText1_loc6, ObjectiveText2_loc6, ObjectiveText3_loc6, ObjectiveText4_loc6, QuestGiverTextWindow_loc6, QuestGiverTargetName_loc6, QuestTurnTextWindow_loc6, QuestTurnTargetName_loc6," + "Title_loc7, Details_loc7, Objectives_loc7, OfferRewardText_loc7, RequestItemsText_loc7, EndText_loc7, CompletedText_loc7, ObjectiveText1_loc7, ObjectiveText2_loc7, ObjectiveText3_loc7, ObjectiveText4_loc7, QuestGiverTextWindow_loc7, QuestGiverTargetName_loc7, QuestTurnTextWindow_loc7, QuestTurnTargetName_loc7," + "Title_loc8, Details_loc8, Objectives_loc8, OfferRewardText_loc8, RequestItemsText_loc8, EndText_loc8, CompletedText_loc8, ObjectiveText1_loc8, ObjectiveText2_loc8, ObjectiveText3_loc8, ObjectiveText4_loc8, QuestGiverTextWindow_loc8, QuestGiverTargetName_loc8, QuestTurnTextWindow_loc8, QuestTurnTargetName_loc8" " FROM locales_quest"); if (!result) @@ -4416,16 +4375,21 @@ void ObjectMgr::LoadQuestLocales() { LocaleConstant locale = (LocaleConstant) i; - AddLocaleString(fields[1 + 11 * (i - 1)].GetString(), locale, data.Title); - AddLocaleString(fields[1 + 11 * (i - 1) + 1].GetString(), locale, data.Details); - AddLocaleString(fields[1 + 11 * (i - 1) + 2].GetString(), locale, data.Objectives); - AddLocaleString(fields[1 + 11 * (i - 1) + 3].GetString(), locale, data.OfferRewardText); - AddLocaleString(fields[1 + 11 * (i - 1) + 4].GetString(), locale, data.RequestItemsText); - AddLocaleString(fields[1 + 11 * (i - 1) + 5].GetString(), locale, data.EndText); - AddLocaleString(fields[1 + 11 * (i - 1) + 6].GetString(), locale, data.CompletedText); + AddLocaleString(fields[1 + 15 * (i - 1)].GetString(), locale, data.Title); + AddLocaleString(fields[1 + 15 * (i - 1) + 1].GetString(), locale, data.Details); + AddLocaleString(fields[1 + 15 * (i - 1) + 2].GetString(), locale, data.Objectives); + AddLocaleString(fields[1 + 15 * (i - 1) + 3].GetString(), locale, data.OfferRewardText); + AddLocaleString(fields[1 + 15 * (i - 1) + 4].GetString(), locale, data.RequestItemsText); + AddLocaleString(fields[1 + 15 * (i - 1) + 5].GetString(), locale, data.EndText); + AddLocaleString(fields[1 + 15 * (i - 1) + 6].GetString(), locale, data.CompletedText); for (uint8 k = 0; k < 4; ++k) - AddLocaleString(fields[1 + 11 * (i - 1) + 7 + k].GetString(), locale, data.ObjectiveText[k]); + AddLocaleString(fields[1 + 15 * (i - 1) + 7 + k].GetString(), locale, data.ObjectiveText[k]); + + AddLocaleString(fields[1 + 15 * (i - 1) + 11].GetString(), locale, data.QuestGiverTextWindow); + AddLocaleString(fields[1 + 15 * (i - 1) + 12].GetString(), locale, data.QuestGiverTargetName); + AddLocaleString(fields[1 + 15 * (i - 1) + 13].GetString(), locale, data.QuestTurnTextWindow); + AddLocaleString(fields[1 + 15 * (i - 1) + 14].GetString(), locale, data.QuestTurnTargetName); } } while (result->NextRow()); @@ -4547,13 +4511,13 @@ void ObjectMgr::LoadScripts(ScriptsType type) continue; } - if (!quest->HasFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT)) + if (!quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT)) { - sLog->outError(LOG_FILTER_SQL, "Table `%s` has quest (ID: %u) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u, but quest not have flag QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT in quest flags. Script command or quest flags wrong. Quest modified to require objective.", + sLog->outError(LOG_FILTER_SQL, "Table `%s` has quest (ID: %u) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u, but quest not have flag QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT in quest flags. Script command or quest flags wrong. Quest modified to require objective.", tableName.c_str(), tmp.QuestExplored.QuestID, tmp.id); // this will prevent quest completing without objective - const_cast<Quest*>(quest)->SetFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT); + const_cast<Quest*>(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT); // continue; - quest objective requirement set and command can be allowed } @@ -5118,7 +5082,7 @@ void ObjectMgr::LoadInstanceEncounters() if (lastEncounterDungeon && !sLFGMgr->GetLFGDungeonEntry(lastEncounterDungeon)) { - sLog->outError(LOG_FILTER_SQL, "Table `instance_encounters` has an encounter %u (%s) marked as final for invalid dungeon id %u, skipped!", entry, dungeonEncounter->encounterName[0], lastEncounterDungeon); + sLog->outError(LOG_FILTER_SQL, "Table `instance_encounters` has an encounter %u (%s) marked as final for invalid dungeon id %u, skipped!", entry, dungeonEncounter->encounterName, lastEncounterDungeon); continue; } @@ -5127,7 +5091,7 @@ void ObjectMgr::LoadInstanceEncounters() { if (itr != dungeonLastBosses.end()) { - sLog->outError(LOG_FILTER_SQL, "Table `instance_encounters` specified encounter %u (%s) as last encounter but %u (%s) is already marked as one, skipped!", entry, dungeonEncounter->encounterName[0], itr->second->id, itr->second->encounterName[0]); + sLog->outError(LOG_FILTER_SQL, "Table `instance_encounters` specified encounter %u (%s) as last encounter but %u (%s) is already marked as one, skipped!", entry, dungeonEncounter->encounterName, itr->second->id, itr->second->encounterName); continue; } @@ -5141,7 +5105,7 @@ void ObjectMgr::LoadInstanceEncounters() CreatureTemplate const* creatureInfo = GetCreatureTemplate(creditEntry); if (!creatureInfo) { - sLog->outError(LOG_FILTER_SQL, "Table `instance_encounters` has an invalid creature (entry %u) linked to the encounter %u (%s), skipped!", creditEntry, entry, dungeonEncounter->encounterName[0]); + sLog->outError(LOG_FILTER_SQL, "Table `instance_encounters` has an invalid creature (entry %u) linked to the encounter %u (%s), skipped!", creditEntry, entry, dungeonEncounter->encounterName); continue; } const_cast<CreatureTemplate*>(creatureInfo)->flags_extra |= CREATURE_FLAG_EXTRA_DUNGEON_BOSS; @@ -5150,12 +5114,12 @@ void ObjectMgr::LoadInstanceEncounters() case ENCOUNTER_CREDIT_CAST_SPELL: if (!sSpellMgr->GetSpellInfo(creditEntry)) { - sLog->outError(LOG_FILTER_SQL, "Table `instance_encounters` has an invalid spell (entry %u) linked to the encounter %u (%s), skipped!", creditEntry, entry, dungeonEncounter->encounterName[0]); + sLog->outError(LOG_FILTER_SQL, "Table `instance_encounters` has an invalid spell (entry %u) linked to the encounter %u (%s), skipped!", creditEntry, entry, dungeonEncounter->encounterName); continue; } break; default: - sLog->outError(LOG_FILTER_SQL, "Table `instance_encounters` has an invalid credit type (%u) for encounter %u (%s), skipped!", creditType, entry, dungeonEncounter->encounterName[0]); + sLog->outError(LOG_FILTER_SQL, "Table `instance_encounters` has an invalid credit type (%u) for encounter %u (%s), skipped!", creditType, entry, dungeonEncounter->encounterName); continue; } @@ -5323,7 +5287,7 @@ void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp) bool has_items = fields[4].GetBool(); m->expire_time = time_t(fields[5].GetUInt32()); m->deliver_time = 0; - m->COD = fields[6].GetUInt32(); + m->COD = fields[6].GetUInt64(); m->checked = fields[7].GetUInt8(); m->mailTemplateId = fields[8].GetInt16(); @@ -5436,12 +5400,12 @@ void ObjectMgr::LoadQuestAreaTriggers() continue; } - if (!quest->HasFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT)) + if (!quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT)) { - sLog->outError(LOG_FILTER_SQL, "Table `areatrigger_involvedrelation` has record (id: %u) for not quest %u, but quest not have flag QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT. Trigger or quest flags must be fixed, quest modified to require objective.", trigger_ID, quest_ID); + sLog->outError(LOG_FILTER_SQL, "Table `areatrigger_involvedrelation` has record (id: %u) for not quest %u, but quest not have flag QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT. Trigger or quest flags must be fixed, quest modified to require objective.", trigger_ID, quest_ID); // this will prevent quest completing without objective - const_cast<Quest*>(quest)->SetFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT); + const_cast<Quest*>(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT); // continue; - quest modified to required objective and trigger can be allowed. } @@ -5539,8 +5503,8 @@ uint32 ObjectMgr::GetNearestTaxiNode(float x, float y, float z, uint32 mapid, ui if (!node || node->map_id != mapid || (!node->MountCreatureID[team == ALLIANCE ? 1 : 0] && node->MountCreatureID[0] != 32981)) // dk flight continue; - uint8 field = (uint8)((i - 1) / 32); - uint32 submask = 1<<((i-1)%32); + uint8 field = (uint8)((i - 1) / 8); + uint32 submask = 1 << ((i-1) % 8); // skip not taxi network nodes if ((sTaxiNodesMask[field] & submask) == 0) @@ -6177,6 +6141,10 @@ void ObjectMgr::SetHighestGuids() result = CharacterDatabase.Query("SELECT MAX(guid) FROM groups"); if (result) sGroupMgr->SetGroupDbStoreSize((*result)[0].GetUInt32()+1); + + result = CharacterDatabase.Query("SELECT MAX(itemId) from character_void_storage"); + if (result) + _voidItemId = (*result)[0].GetUInt64()+1; } uint32 ObjectMgr::GenerateAuctionID() @@ -6324,7 +6292,7 @@ inline void CheckGOSpellId(GameObjectTemplate const* goInfo, uint32 dataN, uint3 goInfo->entry, goInfo->type, N, dataN, dataN); } -inline void CheckAndFixGOChairHeightId(GameObjectTemplate const* goInfo, uint32 const& dataN, uint32 N) +inline void CheckAndFixGOChairHeightId(GameObjectTemplate const* goInfo, uint32& dataN, uint32 N) { if (dataN <= (UNIT_STAND_STATE_SIT_HIGH_CHAIR-UNIT_STAND_STATE_SIT_LOW_CHAIR)) return; @@ -6333,7 +6301,7 @@ inline void CheckAndFixGOChairHeightId(GameObjectTemplate const* goInfo, uint32 goInfo->entry, goInfo->type, N, dataN, UNIT_STAND_STATE_SIT_HIGH_CHAIR-UNIT_STAND_STATE_SIT_LOW_CHAIR); // prevent client and server unexpected work - const_cast<uint32&>(dataN) = 0; + dataN = 0; } inline void CheckGONoDamageImmuneId(GameObjectTemplate* goTemplate, uint32 dataN, uint32 N) @@ -6363,8 +6331,10 @@ void ObjectMgr::LoadGameObjectTemplate() QueryResult result = WorldDatabase.Query("SELECT entry, type, displayId, name, IconName, castBarCaption, unk1, faction, flags, size, questItem1, questItem2, questItem3, " // 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 "questItem4, questItem5, questItem6, data0, data1, data2, data3, data4, data5, data6, data7, data8, data9, data10, data11, data12, " - // 29 30 31 32 33 34 35 36 37 38 39 40 41 - "data13, data14, data15, data16, data17, data18, data19, data20, data21, data22, data23, AIName, ScriptName " + // 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 + "data13, data14, data15, data16, data17, data18, data19, data20, data21, data22, data23, data24, data25, data26, data27, data28, " + // 45 46 47 48 49 50 + "data29, data30, data31, unkInt32, AIName, ScriptName " "FROM gameobject_template"); if (!result) @@ -6398,10 +6368,11 @@ void ObjectMgr::LoadGameObjectTemplate() got.questItems[i] = fields[10 + i].GetUInt32(); for (uint8 i = 0; i < MAX_GAMEOBJECT_DATA; ++i) - got.raw.data[i] = fields[16 + i].GetInt32(); // data1 and data6 can be -1 + got.raw.data[i] = fields[16 + i].GetUInt32(); - got.AIName = fields[40].GetString(); - got.ScriptId = GetScriptId(fields[41].GetCString()); + got.unkInt32 = fields[48].GetInt32(); + got.AIName = fields[49].GetString(); + got.ScriptId = GetScriptId(fields[50].GetCString()); // Checks @@ -6633,7 +6604,7 @@ std::string ObjectMgr::GeneratePetName(uint32 entry) if (list0.empty() || list1.empty()) { CreatureTemplate const* cinfo = GetCreatureTemplate(entry); - char* petname = GetPetName(cinfo->family, sWorld->GetDefaultDbcLocale()); + const char* petname = GetPetName(cinfo->family, sWorld->GetDefaultDbcLocale()); if (!petname) return cinfo->Name; @@ -6648,8 +6619,16 @@ uint32 ObjectMgr::GeneratePetNumber() return ++_hiPetNumber; } +uint64 ObjectMgr::GenerateVoidStorageItemId() +{ + return ++_voidItemId; +} + void ObjectMgr::LoadCorpses() { + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 + // SELECT posX, posY, posZ, orientation, mapId, displayId, itemCache, bytes1, bytes2, flags, dynFlags, time, corpseType, instanceId, phaseMask, corpseGuid, guid FROM corpse WHERE corpseType <> 0 + uint32 oldMSTime = getMSTime(); PreparedQueryResult result = CharacterDatabase.Query(CharacterDatabase.GetPreparedStatement(CHAR_SEL_CORPSES)); @@ -6664,7 +6643,7 @@ void ObjectMgr::LoadCorpses() { Field* fields = result->Fetch(); uint32 guid = fields[16].GetUInt32(); - CorpseType type = CorpseType(fields[13].GetUInt8()); + CorpseType type = CorpseType(fields[12].GetUInt8()); if (type >= MAX_CORPSE_TYPE) { sLog->outError(LOG_FILTER_GENERAL, "Corpse (guid: %u) have wrong corpse type (%u), not loading.", guid, type); @@ -6845,8 +6824,8 @@ void ObjectMgr::LoadReputationSpilloverTemplate() _repSpilloverTemplateStore.clear(); // for reload case - uint32 count = 0; // 0 1 2 3 4 5 6 7 8 9 10 11 12 - QueryResult result = WorldDatabase.Query("SELECT faction, faction1, rate_1, rank_1, faction2, rate_2, rank_2, faction3, rate_3, rank_3, faction4, rate_4, rank_4 FROM reputation_spillover_template"); + uint32 count = 0; // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + QueryResult result = WorldDatabase.Query("SELECT faction, faction1, rate_1, rank_1, faction2, rate_2, rank_2, faction3, rate_3, rank_3, faction4, rate_4, rank_4, faction5, rate_5, rank_5 FROM reputation_spillover_template"); if (!result) { @@ -6874,6 +6853,9 @@ void ObjectMgr::LoadReputationSpilloverTemplate() repTemplate.faction[3] = fields[10].GetUInt16(); repTemplate.faction_rate[3] = fields[11].GetFloat(); repTemplate.faction_rank[3] = fields[12].GetUInt8(); + repTemplate.faction[4] = fields[13].GetUInt16(); + repTemplate.faction_rate[4] = fields[14].GetFloat(); + repTemplate.faction_rank[4] = fields[15].GetUInt8(); FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionId); @@ -6939,6 +6921,12 @@ void ObjectMgr::LoadReputationSpilloverTemplate() sLog->outError(LOG_FILTER_SQL, "Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", repTemplate.faction[3]); continue; } + FactionEntry const* factionEntry4 = sFactionStore.LookupEntry(repTemplate.faction[4]); + if (repTemplate.faction[4] && !factionEntry4) + { + sLog->outError(LOG_FILTER_SQL, "Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", repTemplate.faction[4]); + continue; + } _repSpilloverTemplateStore[factionId] = repTemplate; @@ -7716,12 +7704,10 @@ SkillRangeType GetSkillRangeType(SkillLineEntry const* pSkill, bool racial) { switch (pSkill->categoryId) { - case SKILL_CATEGORY_LANGUAGES: return SKILL_RANGE_LANGUAGE; + case SKILL_CATEGORY_LANGUAGES: + return SKILL_RANGE_LANGUAGE; case SKILL_CATEGORY_WEAPON: - if (pSkill->id != SKILL_FIST_WEAPONS) - return SKILL_RANGE_LEVEL; - else - return SKILL_RANGE_MONO; + return SKILL_RANGE_LEVEL; case SKILL_CATEGORY_ARMOR: case SKILL_CATEGORY_CLASS: if (pSkill->id != SKILL_LOCKPICKING) @@ -8033,7 +8019,7 @@ void ObjectMgr::LoadTrainerSpell() if (!result) { - sLog->outError(LOG_FILTER_SQL, ">> Loaded 0 Trainers. DB table `npc_trainer` is empty!"); + sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 Trainers. DB table `npc_trainer` is empty!"); return; } @@ -8060,11 +8046,12 @@ void ObjectMgr::LoadTrainerSpell() sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %d Trainers in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } -int ObjectMgr::LoadReferenceVendor(int32 vendor, int32 item, std::set<uint32> *skip_vendors) +int ObjectMgr::LoadReferenceVendor(int32 vendor, int32 item, uint8 type, std::set<uint32> *skip_vendors) { // find all items from the reference vendor PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_NPC_VENDOR_REF); stmt->setUInt32(0, uint32(item)); + stmt->setUInt8(1, type); PreparedQueryResult result = WorldDatabase.Query(stmt); if (!result) @@ -8079,19 +8066,20 @@ int ObjectMgr::LoadReferenceVendor(int32 vendor, int32 item, std::set<uint32> *s // if item is a negative, its a reference if (item_id < 0) - count += LoadReferenceVendor(vendor, -item_id, skip_vendors); + count += LoadReferenceVendor(vendor, -item_id, type, skip_vendors); else { - int32 maxcount = fields[1].GetUInt8(); + int32 maxcount = fields[1].GetUInt32(); uint32 incrtime = fields[2].GetUInt32(); uint32 ExtendedCost = fields[3].GetUInt32(); + uint8 type = fields[4].GetUInt8(); - if (!IsVendorItemValid(vendor, item_id, maxcount, incrtime, ExtendedCost, NULL, skip_vendors)) + if (!IsVendorItemValid(vendor, item_id, maxcount, incrtime, ExtendedCost, type, NULL, skip_vendors)) continue; VendorItemData& vList = _cacheVendorItemStore[vendor]; - vList.AddItem(item_id, maxcount, incrtime, ExtendedCost); + vList.AddItem(item_id, maxcount, incrtime, ExtendedCost, type); ++count; } } while (result->NextRow()); @@ -8110,11 +8098,11 @@ void ObjectMgr::LoadVendors() std::set<uint32> skip_vendors; - QueryResult result = WorldDatabase.Query("SELECT entry, item, maxcount, incrtime, ExtendedCost FROM npc_vendor ORDER BY entry, slot ASC"); + QueryResult result = WorldDatabase.Query("SELECT entry, item, maxcount, incrtime, ExtendedCost, type FROM npc_vendor ORDER BY entry, slot ASC"); if (!result) { - sLog->outError(LOG_FILTER_SQL, ">> Loaded 0 Vendors. DB table `npc_vendor` is empty!"); + sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 Vendors. DB table `npc_vendor` is empty!"); return; } @@ -8129,19 +8117,20 @@ void ObjectMgr::LoadVendors() // if item is a negative, its a reference if (item_id < 0) - count += LoadReferenceVendor(entry, -item_id, &skip_vendors); + count += LoadReferenceVendor(entry, -item_id, 0, &skip_vendors); else { - uint32 maxcount = fields[2].GetUInt8(); + uint32 maxcount = fields[2].GetUInt32(); uint32 incrtime = fields[3].GetUInt32(); uint32 ExtendedCost = fields[4].GetUInt32(); + uint8 type = fields[5].GetUInt8(); - if (!IsVendorItemValid(entry, item_id, maxcount, incrtime, ExtendedCost, NULL, &skip_vendors)) + if (!IsVendorItemValid(entry, item_id, maxcount, incrtime, ExtendedCost, type, NULL, &skip_vendors)) continue; VendorItemData& vList = _cacheVendorItemStore[entry]; - vList.AddItem(item_id, maxcount, incrtime, ExtendedCost); + vList.AddItem(item_id, maxcount, incrtime, ExtendedCost, type); ++count; } } @@ -8197,9 +8186,9 @@ void ObjectMgr::LoadGossipMenuItems() _gossipMenuItemsStore.clear(); QueryResult result = WorldDatabase.Query( - // 0 1 2 3 4 + // 0 1 2 3 4 5 "SELECT menu_id, id, option_icon, option_text, option_id, npc_option_npcflag, " - // 5 6 7 8 9 + // 6 7 8 9 10 "action_menu_id, action_poi_id, box_coded, box_money, box_text " "FROM gossip_menu_option ORDER BY menu_id, id"); @@ -8252,10 +8241,10 @@ void ObjectMgr::LoadGossipMenuItems() sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u gossip_menu_option entries in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } -void ObjectMgr::AddVendorItem(uint32 entry, uint32 item, int32 maxcount, uint32 incrtime, uint32 extendedCost, bool persist /*= true*/) +void ObjectMgr::AddVendorItem(uint32 entry, uint32 item, int32 maxcount, uint32 incrtime, uint32 extendedCost, uint8 type, bool persist /*= true*/) { VendorItemData& vList = _cacheVendorItemStore[entry]; - vList.AddItem(item, maxcount, incrtime, extendedCost); + vList.AddItem(item, maxcount, incrtime, extendedCost, type); if (persist) { @@ -8266,18 +8255,19 @@ void ObjectMgr::AddVendorItem(uint32 entry, uint32 item, int32 maxcount, uint32 stmt->setUInt8(2, maxcount); stmt->setUInt32(3, incrtime); stmt->setUInt32(4, extendedCost); + stmt->setUInt8(5, type); WorldDatabase.Execute(stmt); } } -bool ObjectMgr::RemoveVendorItem(uint32 entry, uint32 item, bool persist /*= true*/) +bool ObjectMgr::RemoveVendorItem(uint32 entry, uint32 item, uint8 type, bool persist /*= true*/) { CacheVendorItemContainer::iterator iter = _cacheVendorItemStore.find(entry); if (iter == _cacheVendorItemStore.end()) return false; - if (!iter->second.RemoveItem(item)) + if (!iter->second.RemoveItem(item, type)) return false; if (persist) @@ -8286,6 +8276,7 @@ bool ObjectMgr::RemoveVendorItem(uint32 entry, uint32 item, bool persist /*= tru stmt->setUInt32(0, entry); stmt->setUInt32(1, item); + stmt->setUInt8(2, type); WorldDatabase.Execute(stmt); } @@ -8293,7 +8284,7 @@ bool ObjectMgr::RemoveVendorItem(uint32 entry, uint32 item, bool persist /*= tru return true; } -bool ObjectMgr::IsVendorItemValid(uint32 vendor_entry, uint32 item_id, int32 maxcount, uint32 incrtime, uint32 ExtendedCost, Player* player, std::set<uint32>* skip_vendors, uint32 ORnpcflag) const +bool ObjectMgr::IsVendorItemValid(uint32 vendor_entry, uint32 id, int32 maxcount, uint32 incrtime, uint32 ExtendedCost, uint8 type, Player* player, std::set<uint32>* skip_vendors, uint32 ORnpcflag) const { CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(vendor_entry); if (!cInfo) @@ -8320,12 +8311,13 @@ bool ObjectMgr::IsVendorItemValid(uint32 vendor_entry, uint32 item_id, int32 max return false; } - if (!sObjectMgr->GetItemTemplate(item_id)) + if ((type == ITEM_VENDOR_TYPE_ITEM && !sObjectMgr->GetItemTemplate(id)) || + (type == ITEM_VENDOR_TYPE_CURRENCY && !sCurrencyTypesStore.LookupEntry(id))) { if (player) - ChatHandler(player->GetSession()).PSendSysMessage(LANG_ITEM_NOT_FOUND, item_id); + ChatHandler(player->GetSession()).PSendSysMessage(LANG_ITEM_NOT_FOUND, id, type); else - sLog->outError(LOG_FILTER_SQL, "Table `(game_event_)npc_vendor` for Vendor (Entry: %u) have in item list non-existed item (%u), ignore", vendor_entry, item_id); + sLog->outError(LOG_FILTER_SQL, "Table `(game_event_)npc_vendor` for Vendor (Entry: %u) have in item list non-existed item (%u, type %u), ignore", vendor_entry, id, type); return false; } @@ -8334,41 +8326,44 @@ bool ObjectMgr::IsVendorItemValid(uint32 vendor_entry, uint32 item_id, int32 max if (player) ChatHandler(player->GetSession()).PSendSysMessage(LANG_EXTENDED_COST_NOT_EXIST, ExtendedCost); else - sLog->outError(LOG_FILTER_SQL, "Table `(game_event_)npc_vendor` have Item (Entry: %u) with wrong ExtendedCost (%u) for vendor (%u), ignore", item_id, ExtendedCost, vendor_entry); + sLog->outError(LOG_FILTER_SQL, "Table `(game_event_)npc_vendor` have Item (Entry: %u) with wrong ExtendedCost (%u) for vendor (%u), ignore", id, ExtendedCost, vendor_entry); return false; } - if (maxcount > 0 && incrtime == 0) - { - if (player) - ChatHandler(player->GetSession()).PSendSysMessage("MaxCount != 0 (%u) but IncrTime == 0", maxcount); - else - sLog->outError(LOG_FILTER_SQL, "Table `(game_event_)npc_vendor` has `maxcount` (%u) for item %u of vendor (Entry: %u) but `incrtime`=0, ignore", maxcount, item_id, vendor_entry); - return false; - } - else if (maxcount == 0 && incrtime > 0) + if (type == ITEM_VENDOR_TYPE_ITEM) // not applicable to currencies { - if (player) - ChatHandler(player->GetSession()).PSendSysMessage("MaxCount == 0 but IncrTime<>= 0"); - else - sLog->outError(LOG_FILTER_SQL, "Table `(game_event_)npc_vendor` has `maxcount`=0 for item %u of vendor (Entry: %u) but `incrtime`<>0, ignore", item_id, vendor_entry); - return false; + if (maxcount > 0 && incrtime == 0) + { + if (player) + ChatHandler(player->GetSession()).PSendSysMessage("MaxCount != 0 (%u) but IncrTime == 0", maxcount); + else + sLog->outError(LOG_FILTER_SQL, "Table `(game_event_)npc_vendor` has `maxcount` (%u) for item %u of vendor (Entry: %u) but `incrtime`=0, ignore", maxcount, id, vendor_entry); + return false; + } + else if (maxcount == 0 && incrtime > 0) + { + if (player) + ChatHandler(player->GetSession()).PSendSysMessage("MaxCount == 0 but IncrTime<>= 0"); + else + sLog->outError(LOG_FILTER_SQL, "Table `(game_event_)npc_vendor` has `maxcount`=0 for item %u of vendor (Entry: %u) but `incrtime`<>0, ignore", id, vendor_entry); + return false; + } } VendorItemData const* vItems = GetNpcVendorItemList(vendor_entry); if (!vItems) return true; // later checks for non-empty lists - if (vItems->FindItemCostPair(item_id, ExtendedCost)) + if (vItems->FindItemCostPair(id, ExtendedCost, type)) { if (player) - ChatHandler(player->GetSession()).PSendSysMessage(LANG_ITEM_ALREADY_IN_LIST, item_id, ExtendedCost); + ChatHandler(player->GetSession()).PSendSysMessage(LANG_ITEM_ALREADY_IN_LIST, id, ExtendedCost, type); else - sLog->outError(LOG_FILTER_SQL, "Table `npc_vendor` has duplicate items %u (with extended cost %u) for vendor (Entry: %u), ignoring", item_id, ExtendedCost, vendor_entry); + sLog->outError(LOG_FILTER_SQL, "Table `npc_vendor` has duplicate items %u (with extended cost %u, type %u) for vendor (Entry: %u), ignoring", id, ExtendedCost, type, vendor_entry); return false; } - if (vItems->GetItemCount() >= MAX_VENDOR_ITEMS) + if (vItems->GetItemCount() >= MAX_VENDOR_ITEMS) // FIXME: GetItemCount range 0...255 MAX_VENDOR_ITEMS = 300 { if (player) ChatHandler(player->GetSession()).SendSysMessage(LANG_COMMAND_ADDVENDORITEMITEMS); @@ -8394,7 +8389,7 @@ void ObjectMgr::LoadScriptNames() "UNION " "SELECT DISTINCT(ScriptName) FROM gameobject_template WHERE ScriptName <> '' " "UNION " - "SELECT DISTINCT(ScriptName) FROM item_template WHERE ScriptName <> '' " + "SELECT DISTINCT(ScriptName) FROM item_script_names WHERE ScriptName <> '' " "UNION " "SELECT DISTINCT(ScriptName) FROM areatrigger_scripts WHERE ScriptName <> '' " "UNION " @@ -8524,8 +8519,8 @@ CreatureBaseStats const* ObjectMgr::GetCreatureBaseStats(uint8 level, uint8 unit void ObjectMgr::LoadCreatureClassLevelStats() { uint32 oldMSTime = getMSTime(); - - QueryResult result = WorldDatabase.Query("SELECT level, class, basehp0, basehp1, basehp2, basemana, basearmor FROM creature_classlevelstats"); + // 0 1 2 3 4 5 6 7 + QueryResult result = WorldDatabase.Query("SELECT level, class, basehp0, basehp1, basehp2, basehp3, basemana, basearmor FROM creature_classlevelstats"); if (!result) { @@ -8544,10 +8539,10 @@ void ObjectMgr::LoadCreatureClassLevelStats() CreatureBaseStats stats; for (uint8 i = 0; i < MAX_CREATURE_BASE_HP; ++i) - stats.BaseHealth[i] = fields[i + 2].GetInt16(); + stats.BaseHealth[i] = fields[i + 2].GetUInt32(); - stats.BaseMana = fields[5].GetInt16(); - stats.BaseArmor = fields[6].GetInt16(); + stats.BaseMana = fields[6].GetUInt32(); + stats.BaseArmor = fields[7].GetUInt32(); if (!Class || ((1 << (Class - 1)) & CLASSMASK_ALL_CREATURES) == 0) sLog->outError(LOG_FILTER_SQL, "Creature base stats for level %u has invalid class %u", Level, Class); @@ -8720,6 +8715,39 @@ void ObjectMgr::LoadFactionChangeReputations() sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u faction change reputation pairs in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } +void ObjectMgr::LoadHotfixData() +{ + uint32 oldMSTime = getMSTime(); + + QueryResult result = WorldDatabase.Query("SELECT entry, type, UNIX_TIMESTAMP(hotfixDate) FROM hotfix_data"); + + if (!result) + { + sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 hotfix info entries. DB table `hotfix_data` is empty."); + return; + } + + uint32 count = 0; + + _hotfixData.reserve(result->GetRowCount()); + + do + { + Field* fields = result->Fetch(); + + HotfixInfo info; + info.Entry = fields[0].GetUInt32(); + info.Type = fields[1].GetUInt32(); + info.Timestamp = fields[2].GetUInt64(); + _hotfixData.push_back(info); + + ++count; + } + while (result->NextRow()); + + sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u hotfix info entries in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); +} + void ObjectMgr::LoadFactionChangeSpells() { uint32 oldMSTime = getMSTime(); @@ -8790,6 +8818,99 @@ void ObjectMgr::LoadFactionChangeTitles() sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u faction change title pairs in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } +void ObjectMgr::LoadPhaseDefinitions() +{ + _PhaseDefinitionStore.clear(); + + uint32 oldMSTime = getMSTime(); + + // 0 1 2 3 4 5 + QueryResult result = WorldDatabase.Query("SELECT zoneId, entry, phasemask, phaseId, terrainswapmap, flags FROM `phase_definitions` ORDER BY `entry` ASC"); + + if (!result) + { + sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 phasing definitions. DB table `phase_definitions` is empty."); + return; + } + + uint32 count = 0; + + do + { + Field* fields = result->Fetch(); + + PhaseDefinition PhaseDefinition; + + PhaseDefinition.zoneId = fields[0].GetUInt32(); + PhaseDefinition.entry = fields[1].GetUInt32(); + PhaseDefinition.phasemask = fields[2].GetUInt32(); + PhaseDefinition.phaseId = fields[3].GetUInt32(); + PhaseDefinition.terrainswapmap = fields[4].GetUInt32(); + PhaseDefinition.flags = fields[5].GetUInt32(); + + // Checks + if ((PhaseDefinition.flags & PHASE_FLAG_OVERWRITE_EXISTING) && (PhaseDefinition.flags & PHASE_FLAG_NEGATE_PHASE)) + { + sLog->outError(LOG_FILTER_SQL, "Flags defined in phase_definitions in zoneId %d and entry %u does contain PHASE_FLAG_OVERWRITE_EXISTING and PHASE_FLAG_NEGATE_PHASE. Setting flags to PHASE_FLAG_OVERWRITE_EXISTING", PhaseDefinition.zoneId, PhaseDefinition.entry); + PhaseDefinition.flags &= ~PHASE_FLAG_NEGATE_PHASE; + } + + _PhaseDefinitionStore[PhaseDefinition.zoneId].push_back(PhaseDefinition); + + ++count; + } + while (result->NextRow()); + + sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u phasing definitions in %u ms.", count, GetMSTimeDiffToNow(oldMSTime)); +} + +void ObjectMgr::LoadSpellPhaseInfo() +{ + _SpellPhaseStore.clear(); + + uint32 oldMSTime = getMSTime(); + + // 0 1 2 + QueryResult result = WorldDatabase.Query("SELECT id, phasemask, terrainswapmap FROM `spell_phase`"); + + if (!result) + { + sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 spell dbc infos. DB table `spell_phase` is empty."); + return; + } + + uint32 count = 0; + do + { + Field* fields = result->Fetch(); + + SpellPhaseInfo spellPhaseInfo; + spellPhaseInfo.spellId = fields[0].GetUInt32(); + + SpellInfo const* spell = sSpellMgr->GetSpellInfo(spellPhaseInfo.spellId); + if (!spell) + { + sLog->outError(LOG_FILTER_SQL, "Spell %u defined in `spell_phase` does not exists, skipped.", spellPhaseInfo.spellId); + continue; + } + + if (!spell->HasAura(SPELL_AURA_PHASE)) + { + sLog->outError(LOG_FILTER_SQL, "Spell %u defined in `spell_phase` does not have aura effect type SPELL_AURA_PHASE, useless value.", spellPhaseInfo.spellId); + continue; + } + + spellPhaseInfo.phasemask = fields[1].GetUInt32(); + spellPhaseInfo.terrainswapmap = fields[2].GetUInt32(); + + _SpellPhaseStore[spellPhaseInfo.spellId] = spellPhaseInfo; + + ++count; + } + while (result->NextRow()); + sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u spell dbc infos in %u ms.", count, GetMSTimeDiffToNow(oldMSTime)); +} + GameObjectTemplate const* ObjectMgr::GetGameObjectTemplate(uint32 entry) { GameObjectTemplateContainer::const_iterator itr = _gameObjectTemplateStore.find(entry); |