diff options
Diffstat (limited to 'src/game/ObjectMgr.cpp')
-rw-r--r-- | src/game/ObjectMgr.cpp | 2337 |
1 files changed, 1720 insertions, 617 deletions
diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp index 227b0f90f28..cbf445ebc6b 100644 --- a/src/game/ObjectMgr.cpp +++ b/src/game/ObjectMgr.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,14 +30,13 @@ #include "SpellMgr.h" #include "UpdateMask.h" #include "World.h" -#include "WorldSession.h" #include "Group.h" #include "Guild.h" #include "ArenaTeam.h" #include "Transports.h" #include "ProgressBar.h" #include "Language.h" -#include "GameEvent.h" +#include "GameEventMgr.h" #include "Spell.h" #include "Chat.h" #include "AccountMgr.h" @@ -102,7 +101,7 @@ LanguageDesc lang_description[LANGUAGES_COUNT] = LanguageDesc const* GetLanguageDescByID(uint32 lang) { - for(int i = 0; i < LANGUAGES_COUNT; ++i) + for(uint8 i = 0; i < LANGUAGES_COUNT; ++i) { if(uint32(lang_description[i].lang_id) == lang) return &lang_description[i]; @@ -111,11 +110,31 @@ LanguageDesc const* GetLanguageDescByID(uint32 lang) return NULL; } +bool SpellClickInfo::IsFitToRequirements(Player const* player) const +{ + if(questStart) + { + // not in expected required quest state + if(!player || (!questStartCanActive || !player->IsActiveQuest(questStart)) && !player->GetQuestRewardStatus(questStart)) + return false; + } + + if(questEnd) + { + // not in expected forbidden quest state + if(!player || player->GetQuestRewardStatus(questEnd)) + return false; + } + + return true; +} + ObjectMgr::ObjectMgr() { m_hiCharGuid = 1; m_hiCreatureGuid = 1; m_hiPetGuid = 1; + m_hiVehicleGuid = 1; m_hiItemGuid = 1; m_hiGoGuid = 1; m_hiDoGuid = 1; @@ -123,18 +142,11 @@ ObjectMgr::ObjectMgr() m_hiPetNumber = 1; m_ItemTextId = 1; m_mailid = 1; + m_equipmentSetGuid = 1; m_guildId = 1; m_arenaTeamId = 1; m_auctionid = 1; - mGuildBankTabPrice.resize(GUILD_BANK_MAX_TABS); - mGuildBankTabPrice[0] = 100; - mGuildBankTabPrice[1] = 250; - mGuildBankTabPrice[2] = 500; - mGuildBankTabPrice[3] = 1000; - mGuildBankTabPrice[4] = 2500; - mGuildBankTabPrice[5] = 5000; - // Only zero condition left, others will be added while loading DB tables mConditions.resize(1); } @@ -142,24 +154,10 @@ ObjectMgr::ObjectMgr() ObjectMgr::~ObjectMgr() { for( QuestMap::iterator i = mQuestTemplates.begin( ); i != mQuestTemplates.end( ); ++i ) - { - delete i->second; - } - mQuestTemplates.clear( ); - - for( GossipTextMap::iterator i = mGossipText.begin( ); i != mGossipText.end( ); ++i ) - { delete i->second; - } - mGossipText.clear( ); - - mAreaTriggers.clear(); for(PetLevelInfoMap::iterator i = petInfo.begin( ); i != petInfo.end( ); ++i ) - { delete[] i->second; - } - petInfo.clear(); // free only if loaded for (int class_ = 0; class_ < MAX_CLASSES; ++class_) @@ -189,7 +187,7 @@ ObjectMgr::~ObjectMgr() void ObjectMgr::LoadPlayerInfoInCache() { - QueryResult *result = CharacterDatabase.PQuery("SELECT guid, name, data, class FROM characters"); + QueryResult *result = CharacterDatabase.PQuery("SELECT guid, name, data, class FROM characters"); if(!result) { sLog.outError( "Loading Player Cache failed."); @@ -251,7 +249,7 @@ Group * ObjectMgr::GetGroupByLeader(const uint64 &guid) const return NULL; } -Guild * ObjectMgr::GetGuildById(const uint32 GuildId) const +Guild * ObjectMgr::GetGuildById(uint32 GuildId) const { GuildMap::const_iterator itr = mGuildMap.find(GuildId); if (itr != mGuildMap.end()) @@ -269,7 +267,7 @@ Guild * ObjectMgr::GetGuildByName(const std::string& guildname) const return NULL; } -std::string ObjectMgr::GetGuildNameById(const uint32 GuildId) const +std::string ObjectMgr::GetGuildNameById(uint32 GuildId) const { GuildMap::const_iterator itr = mGuildMap.find(GuildId); if (itr != mGuildMap.end()) @@ -296,7 +294,8 @@ void ObjectMgr::RemoveGuild(uint32 Id) { mGuildMap.erase(Id); } -ArenaTeam* ObjectMgr::GetArenaTeamById(const uint32 arenateamid) const + +ArenaTeam* ObjectMgr::GetArenaTeamById(uint32 arenateamid) const { ArenaTeamMap::const_iterator itr = mArenaTeamMap.find(arenateamid); if (itr != mArenaTeamMap.end()) @@ -350,7 +349,7 @@ void ObjectMgr::LoadCreatureLocales() bar.step(); - sLog.outString(""); + sLog.outString(); sLog.outString(">> Loaded 0 creature locale strings. DB table `locales_creature` is empty."); return; } @@ -366,7 +365,7 @@ void ObjectMgr::LoadCreatureLocales() CreatureLocale& data = mCreatureLocaleMap[entry]; - for(int i = 1; i < MAX_LOCALE; ++i) + for(uint8 i = 1; i < MAX_LOCALE; ++i) { std::string str = fields[1+2*(i-1)].GetCppString(); if(!str.empty()) @@ -398,7 +397,7 @@ void ObjectMgr::LoadCreatureLocales() delete result; sLog.outString(); - sLog.outString( ">> Loaded %u creature locale strings", mCreatureLocaleMap.size() ); + sLog.outString( ">> Loaded %lu creature locale strings", (unsigned long)mCreatureLocaleMap.size() ); } void ObjectMgr::LoadNpcOptionLocales() @@ -418,7 +417,7 @@ void ObjectMgr::LoadNpcOptionLocales() bar.step(); - sLog.outString(""); + sLog.outString(); sLog.outString(">> Loaded 0 npc_option locale strings. DB table `locales_npc_option` is empty."); return; } @@ -434,7 +433,7 @@ void ObjectMgr::LoadNpcOptionLocales() NpcOptionLocale& data = mNpcOptionLocaleMap[entry]; - for(int i = 1; i < MAX_LOCALE; ++i) + for(uint8 i = 1; i < MAX_LOCALE; ++i) { std::string str = fields[1+2*(i-1)].GetCppString(); if(!str.empty()) @@ -466,13 +465,64 @@ void ObjectMgr::LoadNpcOptionLocales() delete result; sLog.outString(); - sLog.outString( ">> Loaded %u npc_option locale strings", mNpcOptionLocaleMap.size() ); + sLog.outString( ">> Loaded %lu npc_option locale strings", (unsigned long)mNpcOptionLocaleMap.size() ); +} + +void ObjectMgr::LoadPointOfInterestLocales() +{ + mPointOfInterestLocaleMap.clear(); // need for reload case + + QueryResult *result = WorldDatabase.Query("SELECT entry,icon_name_loc1,icon_name_loc2,icon_name_loc3,icon_name_loc4,icon_name_loc5,icon_name_loc6,icon_name_loc7,icon_name_loc8 FROM locales_points_of_interest"); + + if(!result) + { + barGoLink bar(1); + + bar.step(); + + sLog.outString(); + sLog.outString(">> Loaded 0 points_of_interest locale strings. DB table `locales_points_of_interest` is empty."); + return; + } + + barGoLink bar(result->GetRowCount()); + + do + { + Field *fields = result->Fetch(); + bar.step(); + + uint32 entry = fields[0].GetUInt32(); + + PointOfInterestLocale& data = mPointOfInterestLocaleMap[entry]; + + for(uint8 i = 1; i < MAX_LOCALE; ++i) + { + std::string str = fields[i].GetCppString(); + if(str.empty()) + continue; + + int idx = GetOrNewIndexForLocale(LocaleConstant(i)); + if(idx >= 0) + { + if(data.IconName.size() <= idx) + data.IconName.resize(idx+1); + + data.IconName[idx] = str; + } + } + } while (result->NextRow()); + + delete result; + + sLog.outString(); + sLog.outString( ">> Loaded %lu points_of_interest locale strings", (unsigned long)mPointOfInterestLocaleMap.size() ); } struct SQLCreatureLoader : public SQLStorageLoaderBase<SQLCreatureLoader> { template<class D> - void convert_from_str(uint32 field_pos, char *src, D &dst) + void convert_from_str(uint32 /*field_pos*/, char *src, D &dst) { dst = D(objmgr.GetScriptId(src)); } @@ -493,100 +543,169 @@ void ObjectMgr::LoadCreatureTemplates() for(uint32 i = 1; i < sCreatureStorage.MaxEntry; ++i) { CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i); - if(!cInfo) + if (!cInfo) continue; - if(cInfo->HeroicEntry) + if (cInfo->HeroicEntry) { CreatureInfo const* heroicInfo = GetCreatureTemplate(cInfo->HeroicEntry); - if(!heroicInfo) + if (!heroicInfo) { sLog.outErrorDb("Creature (Entry: %u) have `heroic_entry`=%u but creature entry %u not exist.",cInfo->HeroicEntry,cInfo->HeroicEntry); continue; } - if(heroicEntries.find(i)!=heroicEntries.end()) + if (heroicEntries.find(i)!=heroicEntries.end()) { sLog.outErrorDb("Creature (Entry: %u) listed as heroic but have value in `heroic_entry`.",i); continue; } - if(heroicEntries.find(cInfo->HeroicEntry)!=heroicEntries.end()) + if (heroicEntries.find(cInfo->HeroicEntry)!=heroicEntries.end()) { sLog.outErrorDb("Creature (Entry: %u) already listed as heroic for another entry.",cInfo->HeroicEntry); continue; } - if(hasHeroicEntries.find(cInfo->HeroicEntry)!=hasHeroicEntries.end()) + if (hasHeroicEntries.find(cInfo->HeroicEntry)!=hasHeroicEntries.end()) { sLog.outErrorDb("Creature (Entry: %u) have `heroic_entry`=%u but creature entry %u have heroic entry also.",i,cInfo->HeroicEntry,cInfo->HeroicEntry); continue; } - if(cInfo->npcflag != heroicInfo->npcflag) + if (cInfo->unit_class != heroicInfo->unit_class) + { + sLog.outErrorDb("Creature (Entry: %u, class %u) has different `unit_class` in heroic mode (Entry: %u, class %u).",i, cInfo->unit_class, cInfo->HeroicEntry, heroicInfo->unit_class); + continue; + } + + if (cInfo->npcflag != heroicInfo->npcflag) { sLog.outErrorDb("Creature (Entry: %u) listed in `creature_template_substitution` has different `npcflag` in heroic mode.",i); continue; } - if(cInfo->classNum != heroicInfo->classNum) + if (cInfo->trainer_class != heroicInfo->trainer_class) { - sLog.outErrorDb("Creature (Entry: %u) listed in `creature_template_substitution` has different `classNum` in heroic mode.",i); + sLog.outErrorDb("Creature (Entry: %u) has different `trainer_class` in heroic mode (Entry: %u).",i,cInfo->HeroicEntry); continue; } - if(cInfo->race != heroicInfo->race) + if (cInfo->trainer_race != heroicInfo->trainer_race) { - sLog.outErrorDb("Creature (Entry: %u) listed in `creature_template_substitution` has different `race` in heroic mode.",i); + sLog.outErrorDb("Creature (Entry: %u) has different `trainer_race` in heroic mode (Entry: %u).",i,cInfo->HeroicEntry); continue; } - if(cInfo->trainer_type != heroicInfo->trainer_type) + if (cInfo->trainer_type != heroicInfo->trainer_type) { sLog.outErrorDb("Creature (Entry: %u) listed in `creature_template_substitution` has different `trainer_type` in heroic mode.",i); continue; } - if(cInfo->trainer_spell != heroicInfo->trainer_spell) + if (cInfo->trainer_spell != heroicInfo->trainer_spell) { sLog.outErrorDb("Creature (Entry: %u) listed in `creature_template_substitution` has different `trainer_spell` in heroic mode.",i); continue; } + if (heroicInfo->AIName && *heroicInfo->AIName) + { + sLog.outErrorDb("Heroic mode creature (Entry: %u) has `AIName`, but in any case will used normal mode creature (Entry: %u) AIName.",cInfo->HeroicEntry,i); + continue; + } + + if (heroicInfo->ScriptID) + { + sLog.outErrorDb("Heroic mode creature (Entry: %u) has `ScriptName`, but in any case will used normal mode creature (Entry: %u) ScriptName.",cInfo->HeroicEntry,i); + continue; + } + hasHeroicEntries.insert(i); heroicEntries.insert(cInfo->HeroicEntry); } FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction_A); - if(!factionTemplate) + if (!factionTemplate) sLog.outErrorDb("Creature (Entry: %u) has non-existing faction_A template (%u)", cInfo->Entry, cInfo->faction_A); factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction_H); - if(!factionTemplate) + if (!factionTemplate) sLog.outErrorDb("Creature (Entry: %u) has non-existing faction_H template (%u)", cInfo->Entry, cInfo->faction_H); - // check model ids, supplying and sending non-existent ids to the client might crash them - if(cInfo->Modelid_A1 && !sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->Modelid_A1)) + // used later for scale + CreatureDisplayInfoEntry const* displayScaleEntry = NULL; + + if (cInfo->DisplayID_A[0]) { - sLog.outErrorDb("Creature (Entry: %u) has non-existing Modelid_A1 (%u), setting it to 0", cInfo->Entry, cInfo->Modelid_A1); - const_cast<CreatureInfo*>(cInfo)->Modelid_A1 = 0; + CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->DisplayID_A[0]); + if(!displayEntry) + { + sLog.outErrorDb("Creature (Entry: %u) has non-existing DisplayID_A id (%u), can crash client", cInfo->Entry, cInfo->DisplayID_A[0]); + const_cast<CreatureInfo*>(cInfo)->DisplayID_A[0] = 0; + } + else if(!displayScaleEntry) + displayScaleEntry = displayEntry; + + CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->DisplayID_A[0]); + if (!minfo) + sLog.outErrorDb("Creature (Entry: %u) not has model data for DisplayID_A (%u)", cInfo->Entry, cInfo->DisplayID_A[0]); } - if(cInfo->Modelid_A2 && !sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->Modelid_A2)) + + if (cInfo->DisplayID_A[1]) { - sLog.outErrorDb("Creature (Entry: %u) has non-existing Modelid_A2 (%u), setting it to 0", cInfo->Entry, cInfo->Modelid_A2); - const_cast<CreatureInfo*>(cInfo)->Modelid_A2 = 0; + CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->DisplayID_A[1]); + if(!displayEntry) + { + sLog.outErrorDb("Creature (Entry: %u) has non-existing DisplayID_A2 id (%u), can crash client", cInfo->Entry, cInfo->DisplayID_A[1]); + const_cast<CreatureInfo*>(cInfo)->DisplayID_A[1] = 0; + } + else if(!displayScaleEntry) + displayScaleEntry = displayEntry; + + CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->DisplayID_A[1]); + if (!minfo) + sLog.outErrorDb("Creature (Entry: %u) not has model data for DisplayID_A2 (%u)", cInfo->Entry, cInfo->DisplayID_A[1]); } - if(cInfo->Modelid_H1 && !sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->Modelid_H1)) + + if (cInfo->DisplayID_H[0]) { - sLog.outErrorDb("Creature (Entry: %u) has non-existing Modelid_H1 (%u), setting it to 0", cInfo->Entry, cInfo->Modelid_H1); - const_cast<CreatureInfo*>(cInfo)->Modelid_H1 = 0; + CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->DisplayID_H[0]); + if(!displayEntry) + { + sLog.outErrorDb("Creature (Entry: %u) has non-existing DisplayID_H id (%u), can crash client", cInfo->Entry, cInfo->DisplayID_H[0]); + const_cast<CreatureInfo*>(cInfo)->DisplayID_H[0] = 0; + } + else if(!displayScaleEntry) + displayScaleEntry = displayEntry; + + CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->DisplayID_H[0]); + if (!minfo) + sLog.outErrorDb("Creature (Entry: %u) not has model data for DisplayID_H (%u)", cInfo->Entry, cInfo->DisplayID_H[0]); } - if(cInfo->Modelid_H2 && !sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->Modelid_H2)) + + if (cInfo->DisplayID_H[1]) { - sLog.outErrorDb("Creature (Entry: %u) has non-existing Modelid_H2 (%u), setting it to 0", cInfo->Entry, cInfo->Modelid_H2); - const_cast<CreatureInfo*>(cInfo)->Modelid_H2 = 0; + CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->DisplayID_H[1]); + if(!displayEntry) + { + sLog.outErrorDb("Creature (Entry: %u) has non-existing DisplayID_H2 id (%u), can crash client", cInfo->Entry, cInfo->DisplayID_H[1]); + const_cast<CreatureInfo*>(cInfo)->DisplayID_H[1] = 0; + } + else if(!displayScaleEntry) + displayScaleEntry = displayEntry; + + CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->DisplayID_H[1]); + if (!minfo) + sLog.outErrorDb("Creature (Entry: %u) not has model data for DisplayID_H2 (%u)", cInfo->Entry, cInfo->DisplayID_H[1]); } + if (!displayScaleEntry) + sLog.outErrorDb("Creature (Entry: %u) not has any existed display id in DisplayID_A/DisplayID_A2/DisplayID_H/DisplayID_H2", cInfo->Entry); + + if (cInfo->unit_class && ((1 << (cInfo->unit_class-1)) & CLASSMASK_ALL_CREATURES) == 0) + sLog.outErrorDb("Creature (Entry: %u) has invalid unit_class(%u) for creature_template", cInfo->Entry, cInfo->unit_class); + if(cInfo->dmgschool >= MAX_SPELL_SCHOOL) { sLog.outErrorDb("Creature (Entry: %u) has invalid spell school value (%u) in `dmgschool`",cInfo->Entry,cInfo->dmgschool); @@ -599,9 +718,28 @@ void ObjectMgr::LoadCreatureTemplates() if(cInfo->rangeattacktime == 0) const_cast<CreatureInfo*>(cInfo)->rangeattacktime = BASE_ATTACK_TIME; + if(cInfo->npcflag & UNIT_NPC_FLAG_SPELLCLICK) + { + sLog.outErrorDb("Creature (Entry: %u) has dynamic flag UNIT_NPC_FLAG_SPELLCLICK (%u) set, it expect to be set by code base at `npc_spellclick_spells` content.",cInfo->Entry,UNIT_NPC_FLAG_SPELLCLICK); + const_cast<CreatureInfo*>(cInfo)->npcflag &= ~UNIT_NPC_FLAG_SPELLCLICK; + } + if((cInfo->npcflag & UNIT_NPC_FLAG_TRAINER) && cInfo->trainer_type >= MAX_TRAINER_TYPE) sLog.outErrorDb("Creature (Entry: %u) has wrong trainer type %u",cInfo->Entry,cInfo->trainer_type); + if(cInfo->type && !sCreatureTypeStore.LookupEntry(cInfo->type)) + { + sLog.outErrorDb("Creature (Entry: %u) has invalid creature type (%u) in `type`",cInfo->Entry,cInfo->type); + const_cast<CreatureInfo*>(cInfo)->type = CREATURE_TYPE_HUMANOID; + } + + // must exist or used hidden but used in data horse case + if(cInfo->family && !sCreatureFamilyStore.LookupEntry(cInfo->family) && cInfo->family != CREATURE_FAMILY_HORSE_CUSTOM ) + { + sLog.outErrorDb("Creature (Entry: %u) has invalid creature family (%u) in `family`",cInfo->Entry,cInfo->family); + const_cast<CreatureInfo*>(cInfo)->family = 0; + } + if(cInfo->InhabitType <= 0 || cInfo->InhabitType > INHABIT_ANYWHERE) { sLog.outErrorDb("Creature (Entry: %u) has wrong value (%u) in `InhabitType`, creature will not correctly walk/swim/fly",cInfo->Entry,cInfo->InhabitType); @@ -615,6 +753,15 @@ void ObjectMgr::LoadCreatureTemplates() sLog.outErrorDb("Creature (Entry: %u) has non-existing PetSpellDataId (%u)", cInfo->Entry, cInfo->PetSpellDataId); } + for(uint8 j = 0; j < CREATURE_MAX_SPELLS; ++j) + { + if(cInfo->spells[j] && !sSpellStore.LookupEntry(cInfo->spells[j])) + { + sLog.outErrorDb("Creature (Entry: %u) has non-existing Spell%d (%u), set to 0", cInfo->Entry, j+1,cInfo->spells[j]); + const_cast<CreatureInfo*>(cInfo)->spells[j] = 0; + } + } + if(cInfo->MovementType >= MAX_DB_MOTION_TYPE) { sLog.outErrorDb("Creature (Entry: %u) has wrong movement generator type (%u), ignore and set to IDLE.",cInfo->Entry,cInfo->MovementType); @@ -633,10 +780,13 @@ void ObjectMgr::LoadCreatureTemplates() /// if not set custom creature scale then load scale from CreatureDisplayInfo.dbc if(cInfo->scale <= 0.0f) { - uint32 modelid = cInfo->GetFirstValidModelId(); - CreatureDisplayInfoEntry const* ScaleEntry = sCreatureDisplayInfoStore.LookupEntry(modelid); - const_cast<CreatureInfo*>(cInfo)->scale = ScaleEntry ? ScaleEntry->scale : 1.0f; + if(displayScaleEntry) + const_cast<CreatureInfo*>(cInfo)->scale = displayScaleEntry->scale; + else + const_cast<CreatureInfo*>(cInfo)->scale = 1.0f; } + + //const_cast<CreatureInfo*>(cInfo)->dmg_multiplier *= Creature::_GetDamageMod(cInfo->rank); } } @@ -683,7 +833,7 @@ void ObjectMgr::ConvertCreatureAddonAuras(CreatureDataAddon* addon, char const* const_cast<CreatureDataAddonAura*&>(addon->auras) = new CreatureDataAddonAura[val.size()/2+1]; int i=0; - for(int j=0;j<val.size()/2;++j) + for(uint32 j=0;j<val.size()/2;++j) { CreatureDataAddonAura& cAura = const_cast<CreatureDataAddonAura&>(addon->auras[i]); cAura.spell_id = (uint32)val[2*j+0]; @@ -729,6 +879,18 @@ void ObjectMgr::LoadCreatureAddons() if(!addon) continue; + if (addon->mount) + { + if (!sCreatureDisplayInfoStore.LookupEntry(addon->mount)) + { + sLog.outErrorDb("Creature (Entry %u) have invalid displayInfoId for mount (%u) defined in `creature_template_addon`.",addon->guidOrEntry, addon->mount); + const_cast<CreatureDataAddon*>(addon)->mount = 0; + } + } + + if (!sEmotesStore.LookupEntry(addon->emote)) + sLog.outErrorDb("Creature (Entry %u) have invalid emote (%u) defined in `creature_template_addon`.",addon->guidOrEntry, addon->emote); + ConvertCreatureAddonAuras(const_cast<CreatureDataAddon*>(addon), "creature_template_addon", "Entry"); if(!sCreatureStorage.LookupEntry<CreatureInfo>(addon->guidOrEntry)) @@ -747,6 +909,18 @@ void ObjectMgr::LoadCreatureAddons() if(!addon) continue; + if (addon->mount) + { + if (!sCreatureDisplayInfoStore.LookupEntry(addon->mount)) + { + sLog.outErrorDb("Creature (GUID %u) have invalid displayInfoId for mount (%u) defined in `creature_addon`.",addon->guidOrEntry, addon->mount); + const_cast<CreatureDataAddon*>(addon)->mount = 0; + } + } + + if (!sEmotesStore.LookupEntry(addon->emote)) + sLog.outErrorDb("Creature (GUID %u) have invalid emote (%u) defined in `creature_addon`.",addon->guidOrEntry, addon->emote); + ConvertCreatureAddonAuras(const_cast<CreatureDataAddon*>(addon), "creature_addon", "GUIDLow"); if(mCreatureDataMap.find(addon->guidOrEntry)==mCreatureDataMap.end()) @@ -763,6 +937,42 @@ void ObjectMgr::LoadEquipmentTemplates() { sEquipmentStorage.Load(); + for(uint32 i=0; i< sEquipmentStorage.MaxEntry; ++i) + { + EquipmentInfo const* eqInfo = sEquipmentStorage.LookupEntry<EquipmentInfo>(i); + + if(!eqInfo) + continue; + + for(uint8 j=0; j<3; j++) + { + if(!eqInfo->equipentry[j]) + continue; + + ItemEntry const *dbcitem = sItemStore.LookupEntry(eqInfo->equipentry[j]); + + if(!dbcitem) + { + sLog.outErrorDb("Unknown item (entry=%u) in creature_equip_template.equipentry%u for entry = %u, forced to 0.", eqInfo->equipentry[j], j+1, i); + const_cast<EquipmentInfo*>(eqInfo)->equipentry[j] = 0; + continue; + } + + if(dbcitem->InventoryType != INVTYPE_WEAPON && + dbcitem->InventoryType != INVTYPE_SHIELD && + dbcitem->InventoryType != INVTYPE_RANGED && + dbcitem->InventoryType != INVTYPE_2HWEAPON && + dbcitem->InventoryType != INVTYPE_WEAPONMAINHAND && + dbcitem->InventoryType != INVTYPE_WEAPONOFFHAND && + dbcitem->InventoryType != INVTYPE_HOLDABLE && + dbcitem->InventoryType != INVTYPE_THROWN && + dbcitem->InventoryType != INVTYPE_RANGEDRIGHT) + { + sLog.outErrorDb("Item (entry=%u) in creature_equip_template.equipentry%u for entry = %u is not equipable in a hand, forced to 0.", eqInfo->equipentry[j], j+1, i); + const_cast<EquipmentInfo*>(eqInfo)->equipentry[j] = 0; + } + } + } sLog.outString( ">> Loaded %u equipment template", sEquipmentStorage.RecordCount ); sLog.outString(); } @@ -772,15 +982,39 @@ CreatureModelInfo const* ObjectMgr::GetCreatureModelInfo(uint32 modelid) return sCreatureModelStorage.LookupEntry<CreatureModelInfo>(modelid); } -uint32 ObjectMgr::ChooseDisplayId(uint32 team, const CreatureInfo *cinfo, const CreatureData *data) +uint32 ObjectMgr::ChooseDisplayId(uint32 team, const CreatureInfo *cinfo, const CreatureData *data /*= NULL*/) { // Load creature model (display id) - uint32 display_id = 0; + if (data && data->displayid) + return data->displayid; + + if(!team) + return cinfo->GetRandomValidModelId(); + + // use defaults from the template + uint32 display_id; + + // DisplayID_A is used if no team is given + if (team == HORDE) + { + if(cinfo->DisplayID_H[0]) + display_id = cinfo->DisplayID_H[1] ? cinfo->DisplayID_H[urand(0,1)] : cinfo->DisplayID_H[0]; + else + display_id = cinfo->DisplayID_H[1]; - if (!data || data->displayid == 0) // use defaults from the template + if(!display_id) + display_id = cinfo->DisplayID_A[0] ? cinfo->DisplayID_A[0] : cinfo->DisplayID_A[1]; + } + else if(team == ALLIANCE) { - display_id = cinfo->GetRandomValidModelId(); - } else display_id = data->displayid; // overwritten from creature data + if(cinfo->DisplayID_A[0]) + display_id = cinfo->DisplayID_A[1] ? cinfo->DisplayID_A[urand(0,1)] : cinfo->DisplayID_A[0]; + else + display_id = cinfo->DisplayID_A[1]; + + if(!display_id) + display_id = cinfo->DisplayID_H[0] ? cinfo->DisplayID_H[0] : cinfo->DisplayID_H[1]; + } return display_id; } @@ -811,6 +1045,29 @@ void ObjectMgr::LoadCreatureModelInfo() { sCreatureModelStorage.Load(); + // post processing + for(uint32 i = 1; i < sCreatureModelStorage.MaxEntry; ++i) + { + CreatureModelInfo const *minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(i); + if (!minfo) + continue; + + if (!sCreatureDisplayInfoStore.LookupEntry(minfo->modelid)) + sLog.outErrorDb("Table `creature_model_info` has model for not existed display id (%u).", minfo->modelid); + + if (minfo->gender > GENDER_NONE) + { + sLog.outErrorDb("Table `creature_model_info` has wrong gender (%u) for display id (%u).", uint32(minfo->gender), minfo->modelid); + const_cast<CreatureModelInfo*>(minfo)->gender = GENDER_MALE; + } + + if (minfo->modelid_other_gender && !sCreatureDisplayInfoStore.LookupEntry(minfo->modelid_other_gender)) + { + sLog.outErrorDb("Table `creature_model_info` has not existed alt.gender model (%u) for existed display id (%u).", minfo->modelid_other_gender, minfo->modelid); + const_cast<CreatureModelInfo*>(minfo)->modelid_other_gender = 0; + } + } + sLog.outString( ">> Loaded %u creature model based info", sCreatureModelStorage.RecordCount ); sLog.outString(); @@ -924,9 +1181,10 @@ void ObjectMgr::LoadCreatures() QueryResult *result = WorldDatabase.Query("SELECT creature.guid, id, map, modelid," // 4 5 6 7 8 9 10 11 "equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, spawndist, currentwaypoint," - // 12 13 14 15 16 17 - "curhealth, curmana, DeathState, MovementType, spawnMask, event " - "FROM creature LEFT OUTER JOIN game_event_creature ON creature.guid = game_event_creature.guid"); + // 12 13 14 15 16 17 18 19 + "curhealth, curmana, DeathState, MovementType, spawnMask, phaseMask, event, pool_entry " + "FROM creature LEFT OUTER JOIN game_event_creature ON creature.guid = game_event_creature.guid " + "LEFT OUTER JOIN pool_creature ON creature.guid = pool_creature.guid"); if(!result) { @@ -934,7 +1192,7 @@ void ObjectMgr::LoadCreatures() bar.step(); - sLog.outString(""); + sLog.outString(); sLog.outErrorDb(">> Loaded 0 creature. DB table `creature` is empty."); return; } @@ -946,6 +1204,9 @@ void ObjectMgr::LoadCreatures() if(cInfo->HeroicEntry) heroicCreatures.insert(cInfo->HeroicEntry); + //TODO: remove this + //gameeventmgr.mGameEventCreatureGuids.resize(52*2-1); + barGoLink bar(result->GetRowCount()); do @@ -953,11 +1214,19 @@ void ObjectMgr::LoadCreatures() Field *fields = result->Fetch(); bar.step(); - uint32 guid = fields[0].GetUInt32(); + uint32 guid = fields[ 0].GetUInt32(); + uint32 entry = fields[ 1].GetUInt32(); + + CreatureInfo const* cInfo = GetCreatureTemplate(entry); + if(!cInfo) + { + sLog.outErrorDb("Table `creature` has creature (GUID: %u) with non existing creature entry %u, skipped.", guid, entry); + continue; + } CreatureData& data = mCreatureDataMap[guid]; - data.id = fields[ 1].GetUInt32(); + data.id = entry; data.mapid = fields[ 2].GetUInt32(); data.displayid = fields[ 3].GetUInt32(); data.equipmentId = fields[ 4].GetUInt32(); @@ -973,20 +1242,25 @@ void ObjectMgr::LoadCreatures() data.is_dead = fields[14].GetBool(); data.movementType = fields[15].GetUInt8(); data.spawnMask = fields[16].GetUInt8(); - int16 gameEvent = fields[17].GetInt16(); + data.phaseMask = fields[17].GetUInt16(); + int16 gameEvent = fields[18].GetInt16(); + int16 PoolId = fields[19].GetInt16(); - CreatureInfo const* cInfo = GetCreatureTemplate(data.id); - if(!cInfo) + if(heroicCreatures.find(data.id)!=heroicCreatures.end()) { - sLog.outErrorDb("Table `creature` have creature (GUID: %u) with not existed creature entry %u, skipped.",guid,data.id ); + sLog.outErrorDb("Table `creature` have creature (GUID: %u) that listed as heroic template in `creature_template_substitution`, skipped.",guid,data.id ); continue; } - if(heroicCreatures.find(data.id)!=heroicCreatures.end()) + // I do not know why but in db most display id are not zero + /*if(data.displayid == 11686 || data.displayid == 24719) { - sLog.outErrorDb("Table `creature` have creature (GUID: %u) that listed as heroic template in `creature_template_substitution`, skipped.",guid,data.id ); - continue; + (const_cast<CreatureInfo*>(cInfo))->flags_extra |= CREATURE_FLAG_EXTRA_TRIGGER; } + else if(data.displayid == cInfo->DisplayID_A || data.displayid == cInfo->DisplayID_A2 + || data.displayid == cInfo->DisplayID_H || data.displayid == cInfo->DisplayID_H2) + data.displayid = 0; + */ if(data.equipmentId > 0) // -1 no equipment, 0 use default { @@ -1003,6 +1277,13 @@ void ObjectMgr::LoadCreatures() data.curhealth = cInfo->minhealth; } + if(cInfo->flags_extra & CREATURE_FLAG_EXTRA_INSTANCE_BIND) + { + MapEntry const* map = sMapStore.LookupEntry(data.mapid); + if(!map || !map->IsDungeon()) + sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `creature_template`.`flags_extra` including CREATURE_FLAG_EXTRA_INSTANCE_BIND but creature are not in instance.",guid,data.id); + } + if(data.curmana < cInfo->minmana) { sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with low current mana (%u), `creature_template`.`minmana`=%u.",guid,data.id,data.curmana, cInfo->minmana ); @@ -1021,6 +1302,8 @@ void ObjectMgr::LoadCreatures() sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `MovementType`=1 (random movement) but with `spawndist`=0, replace by idle movement type (0).",guid,data.id ); data.movementType = IDLE_MOTION_TYPE; } + else if(cInfo->flags_extra & CREATURE_FLAG_EXTRA_TRIGGER) + data.movementType = IDLE_MOTION_TYPE; } else if(data.movementType == IDLE_MOTION_TYPE) { @@ -1031,8 +1314,29 @@ void ObjectMgr::LoadCreatures() } } - if (gameEvent==0) // if not this is to be managed by GameEvent System + if(data.phaseMask==0) + { + sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `phaseMask`=0 (not visible for anyone), set to 1.",guid,data.id ); + data.phaseMask = 1; + } + + //if(entry == 32307 || entry == 32308) + /*if(entry == 30739 || entry == 30740) + { + gameEvent = 51; + uint32 guid2 = objmgr.GenerateLowGuid(HIGHGUID_UNIT); + CreatureData& data2 = mCreatureDataMap[guid2]; + data2 = data; +// data2.id = (entry == 32307 ? 32308 : 32307); + data2.id = (entry == 30739 ? 30740 : 30739); + data2.displayid = 0; + gameeventmgr.mGameEventCreatureGuids[51+51].push_back(guid); + gameeventmgr.mGameEventCreatureGuids[51+50].push_back(guid2); + }*/ + + if (gameEvent==0 && PoolId==0) // if not this is to be managed by GameEvent System or Pool system AddCreatureToGrid(guid, &data); + ++count; } while (result->NextRow()); @@ -1040,7 +1344,7 @@ void ObjectMgr::LoadCreatures() delete result; sLog.outString(); - sLog.outString( ">> Loaded %u creatures", mCreatureDataMap.size() ); + sLog.outString( ">> Loaded %lu creatures", (unsigned long)mCreatureDataMap.size() ); } void ObjectMgr::AddCreatureToGrid(uint32 guid, CreatureData const* data) @@ -1075,15 +1379,112 @@ void ObjectMgr::RemoveCreatureFromGrid(uint32 guid, CreatureData const* data) } } +uint32 ObjectMgr::AddGameObject(uint32 entry, uint32 mapId, float x, float y, float z, float o, uint32 spawntimedelay, float rotation0, float rotation1, float rotation2, float rotation3) +{ + GameObjectInfo const* goinfo = GetGameObjectInfo(entry); + if (!goinfo) + return 0; + + uint32 guid = GenerateLowGuid(HIGHGUID_GAMEOBJECT); + GameObjectData& data = NewGOData(guid); + data.id = entry; + data.mapid = mapId; + data.posX = x; + data.posY = y; + data.posZ = z; + data.orientation = o; + data.rotation0 = rotation0; + data.rotation1 = rotation1; + data.rotation2 = rotation2; + data.rotation3 = rotation3; + data.spawntimesecs = spawntimedelay; + data.animprogress = 100; + data.spawnMask = 1; + data.go_state = GO_STATE_READY; + data.phaseMask = PHASEMASK_NORMAL; + data.dbData = false; + + AddGameobjectToGrid(guid, &data); + + // Spawn if necessary (loaded grids only) + if(Map* map = const_cast<Map*>(MapManager::Instance().CreateBaseMap(mapId))) + { + // We use spawn coords to spawn + if(!map->Instanceable() && !map->IsRemovalGrid(x, y)) + { + GameObject *go = new GameObject; + if(!go->LoadFromDB(guid, map)) + { + sLog.outError("AddGameObject: cannot add gameobject entry %u to map", entry); + delete go; + return 0; + } + map->Add(go); + } + } + + return guid; +} + +uint32 ObjectMgr::AddCreature(uint32 entry, uint32 team, uint32 mapId, float x, float y, float z, float o, uint32 spawntimedelay) +{ + CreatureInfo const *cInfo = GetCreatureTemplate(entry); + if(!cInfo) + return 0; + + uint32 guid = GenerateLowGuid(HIGHGUID_UNIT); + CreatureData& data = NewOrExistCreatureData(guid); + data.id = entry; + data.mapid = mapId; + data.displayid = 0; + data.equipmentId = cInfo->equipmentId; + data.posX = x; + data.posY = y; + data.posZ = z; + data.orientation = o; + data.spawntimesecs = spawntimedelay; + data.spawndist = 0; + data.currentwaypoint = 0; + data.curhealth = cInfo->maxhealth; + data.curmana = cInfo->maxmana; + data.is_dead = false; + data.movementType = cInfo->MovementType; + data.spawnMask = 1; + data.phaseMask = PHASEMASK_NORMAL; + data.dbData = false; + + AddCreatureToGrid(guid, &data); + + // Spawn if necessary (loaded grids only) + if(Map* map = const_cast<Map*>(MapManager::Instance().CreateBaseMap(mapId))) + { + // We use spawn coords to spawn + if(!map->Instanceable() && !map->IsRemovalGrid(x, y)) + { + Creature* creature = new Creature; + if(!creature->LoadFromDB(guid, map)) + { + sLog.outError("AddCreature: cannot add creature entry %u to map", entry); + delete creature; + return 0; + } + map->Add(creature); + } + } + + return guid; +} + void ObjectMgr::LoadGameobjects() { uint32 count = 0; // 0 1 2 3 4 5 6 QueryResult *result = WorldDatabase.Query("SELECT gameobject.guid, id, map, position_x, position_y, position_z, orientation," - // 7 8 9 10 11 12 13 14 15 - "rotation0, rotation1, rotation2, rotation3, spawntimesecs, animprogress, state, spawnMask, event " - "FROM gameobject LEFT OUTER JOIN game_event_gameobject ON gameobject.guid = game_event_gameobject.guid"); + // 7 8 9 10 11 12 13 14 15 16 17 + "rotation0, rotation1, rotation2, rotation3, spawntimesecs, animprogress, state, spawnMask, phaseMask, event, pool_entry " + "FROM gameobject LEFT OUTER JOIN game_event_gameobject ON gameobject.guid = game_event_gameobject.guid " + "LEFT OUTER JOIN pool_gameobject ON gameobject.guid = pool_gameobject.guid"); if(!result) { @@ -1103,11 +1504,25 @@ void ObjectMgr::LoadGameobjects() Field *fields = result->Fetch(); bar.step(); - uint32 guid = fields[0].GetUInt32(); + uint32 guid = fields[ 0].GetUInt32(); + uint32 entry = fields[ 1].GetUInt32(); + + GameObjectInfo const* gInfo = GetGameObjectInfo(entry); + if(!gInfo) + { + sLog.outErrorDb("Table `gameobject` has gameobject (GUID: %u) with non existing gameobject entry %u, skipped.", guid, entry); + continue; + } + + if(gInfo->displayId && !sGameObjectDisplayInfoStore.LookupEntry(gInfo->displayId)) + { + sLog.outErrorDb("Gameobject (GUID: %u Entry %u GoType: %u) have invalid displayId (%u), not loaded.",guid, entry, gInfo->type, gInfo->displayId); + continue; + } GameObjectData& data = mGameObjectDataMap[guid]; - data.id = fields[ 1].GetUInt32(); + data.id = entry; data.mapid = fields[ 2].GetUInt32(); data.posX = fields[ 3].GetFloat(); data.posY = fields[ 4].GetFloat(); @@ -1119,19 +1534,46 @@ void ObjectMgr::LoadGameobjects() data.rotation3 = fields[10].GetFloat(); data.spawntimesecs = fields[11].GetInt32(); data.animprogress = fields[12].GetUInt32(); - data.go_state = fields[13].GetUInt32(); data.ArtKit = 0; + + uint32 go_state = fields[13].GetUInt32(); + if (go_state >= MAX_GO_STATE) + { + sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid `state` (%u) value, skip",guid,data.id,go_state); + continue; + } + data.go_state = GOState(go_state); + data.spawnMask = fields[14].GetUInt8(); - int16 gameEvent = fields[15].GetInt16(); + data.phaseMask = fields[15].GetUInt16(); + int16 gameEvent = fields[16].GetInt16(); + int16 PoolId = fields[17].GetInt16(); - GameObjectInfo const* gInfo = GetGameObjectInfo(data.id); - if(!gInfo) + if(data.rotation2 < -1.0f || data.rotation2 > 1.0f) + { + sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid rotation2 (%f) value, skip",guid,data.id,data.rotation2 ); + continue; + } + + if(data.rotation3 < -1.0f || data.rotation3 > 1.0f) { - sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u) with not existed gameobject entry %u, skipped.",guid,data.id ); + sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid rotation3 (%f) value, skip",guid,data.id,data.rotation3 ); continue; } - if (gameEvent==0) // if not this is to be managed by GameEvent System + if(!MapManager::IsValidMapCoord(data.mapid,data.posX,data.posY,data.posZ,data.orientation)) + { + sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid coordinates, skip",guid,data.id ); + continue; + } + + if(data.phaseMask==0) + { + sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with `phaseMask`=0 (not visible for anyone), set to 1.",guid,data.id ); + data.phaseMask = 1; + } + + if (gameEvent==0 && PoolId==0) // if not this is to be managed by GameEvent System or Pool system AddGameobjectToGrid(guid, &data); ++count; @@ -1140,7 +1582,7 @@ void ObjectMgr::LoadGameobjects() delete result; sLog.outString(); - sLog.outString( ">> Loaded %u gameobjects", mGameObjectDataMap.size()); + sLog.outString( ">> Loaded %lu gameobjects", (unsigned long)mGameObjectDataMap.size()); } void ObjectMgr::AddGameobjectToGrid(uint32 guid, GameObjectData const* data) @@ -1210,7 +1652,7 @@ void ObjectMgr::LoadCreatureRespawnTimes() delete result; - sLog.outString( ">> Loaded %u creature respawn times", mCreatureRespawnTimes.size() ); + sLog.outString( ">> Loaded %lu creature respawn times", (unsigned long)mCreatureRespawnTimes.size() ); sLog.outString(); } @@ -1252,7 +1694,7 @@ void ObjectMgr::LoadGameobjectRespawnTimes() delete result; - sLog.outString( ">> Loaded %u gameobject respawn times", mGORespawnTimes.size() ); + sLog.outString( ">> Loaded %lu gameobject respawn times", (unsigned long)mGORespawnTimes.size() ); sLog.outString(); } @@ -1355,7 +1797,7 @@ void ObjectMgr::LoadItemLocales() bar.step(); - sLog.outString(""); + sLog.outString(); sLog.outString(">> Loaded 0 Item locale strings. DB table `locales_item` is empty."); return; } @@ -1371,7 +1813,7 @@ void ObjectMgr::LoadItemLocales() ItemLocale& data = mItemLocaleMap[entry]; - for(int i = 1; i < MAX_LOCALE; ++i) + for(uint8 i = 1; i < MAX_LOCALE; ++i) { std::string str = fields[1+2*(i-1)].GetCppString(); if(!str.empty()) @@ -1404,13 +1846,13 @@ void ObjectMgr::LoadItemLocales() delete result; sLog.outString(); - sLog.outString( ">> Loaded %u Item locale strings", mItemLocaleMap.size() ); + sLog.outString( ">> Loaded %lu Item locale strings", (unsigned long)mItemLocaleMap.size() ); } struct SQLItemLoader : public SQLStorageLoaderBase<SQLItemLoader> { template<class D> - void convert_from_str(uint32 field_pos, char *src, D &dst) + void convert_from_str(uint32 /*field_pos*/, char *src, D &dst) { dst = D(objmgr.GetScriptId(src)); } @@ -1439,6 +1881,32 @@ void ObjectMgr::LoadItemPrototypes() if(dbcitem) { + if(proto->Class != dbcitem->Class) + { + sLog.outErrorDb("Item (Entry: %u) not correct class %u, must be %u (still using DB value).",i,proto->Class,dbcitem->Class); + // It safe let use Class from DB + } + /* disabled: have some strange wrong cases for Subclass values. + for enable also uncomment Subclass field in ItemEntry structure and in Itemfmt[] + if(proto->SubClass != dbcitem->SubClass) + { + sLog.outErrorDb("Item (Entry: %u) not correct (Class: %u, Sub: %u) pair, must be (Class: %u, Sub: %u) (still using DB value).",i,proto->Class,proto->SubClass,dbcitem->Class,dbcitem->SubClass); + // It safe let use Subclass from DB + } + */ + + if(proto->Unk0 != dbcitem->Unk0) + { + sLog.outErrorDb("Item (Entry: %u) not correct %i Unk0, must be %i (still using DB value).",i,proto->Unk0,dbcitem->Unk0); + // It safe let use Unk0 from DB + } + + if(proto->Material != dbcitem->Material) + { + sLog.outErrorDb("Item (Entry: %u) not correct %i material, must be %i (still using DB value).",i,proto->Material,dbcitem->Material); + // It safe let use Material from DB + } + if(proto->InventoryType != dbcitem->InventoryType) { sLog.outErrorDb("Item (Entry: %u) not correct %u inventory type, must be %u (still using DB value).",i,proto->InventoryType,dbcitem->InventoryType); @@ -1464,7 +1932,7 @@ void ObjectMgr::LoadItemPrototypes() if(proto->Class >= MAX_ITEM_CLASS) { sLog.outErrorDb("Item (Entry: %u) has wrong Class value (%u)",i,proto->Class); - const_cast<ItemPrototype*>(proto)->Class = ITEM_CLASS_JUNK; + const_cast<ItemPrototype*>(proto)->Class = ITEM_CLASS_MISC; } if(proto->SubClass >= MaxItemSubclassValues[proto->Class]) @@ -1497,14 +1965,29 @@ void ObjectMgr::LoadItemPrototypes() const_cast<ItemPrototype*>(proto)->RequiredSkill = 0; } - if(!(proto->AllowableClass & CLASSMASK_ALL_PLAYABLE)) { - sLog.outErrorDb("Item (Entry: %u) not have in `AllowableClass` any playable classes (%u) and can't be equipped.",i,proto->AllowableClass); - } + // can be used in equip slot, as page read use in inventory, or spell casting at use + bool req = proto->InventoryType!=INVTYPE_NON_EQUIP || proto->PageText; + if(!req) + { + for (int j = 0; j < MAX_ITEM_PROTO_SPELLS; ++j) + { + if(proto->Spells[j].SpellId) + { + req = true; + break; + } + } + } - if(!(proto->AllowableRace & RACEMASK_ALL_PLAYABLE)) - { - sLog.outErrorDb("Item (Entry: %u) not have in `AllowableRace` any playable races (%u) and can't be equipped.",i,proto->AllowableRace); + if(req) + { + if(!(proto->AllowableClass & CLASSMASK_ALL_PLAYABLE)) + sLog.outErrorDb("Item (Entry: %u) not have in `AllowableClass` any playable classes (%u) and can't be equipped or use.",i,proto->AllowableClass); + + if(!(proto->AllowableRace & RACEMASK_ALL_PLAYABLE)) + sLog.outErrorDb("Item (Entry: %u) not have in `AllowableRace` any playable races (%u) and can't be equipped or use.",i,proto->AllowableRace); + } } if(proto->RequiredSpell && !sSpellStore.LookupEntry(proto->RequiredSpell)) @@ -1530,18 +2013,35 @@ void ObjectMgr::LoadItemPrototypes() else if(proto->RequiredReputationRank > MIN_REPUTATION_RANK) sLog.outErrorDb("Item (Entry: %u) has RequiredReputationFaction ==0 but RequiredReputationRank > 0, rank setting is useless.",i); - if(proto->Stackable==0) + if(proto->MaxCount < -1) + { + sLog.outErrorDb("Item (Entry: %u) has too large negative in maxcount (%i), replace by value (-1) no storing limits.",i,proto->MaxCount); + const_cast<ItemPrototype*>(proto)->MaxCount = -1; + } + + if(proto->Stackable == 0) { - sLog.outErrorDb("Item (Entry: %u) has wrong value in stackable (%u), replace by default 1.",i,proto->Stackable); + sLog.outErrorDb("Item (Entry: %u) has wrong value in stackable (%i), replace by default 1.",i,proto->Stackable); const_cast<ItemPrototype*>(proto)->Stackable = 1; } - else if(proto->Stackable > 255) + else if(proto->Stackable < -1) + { + sLog.outErrorDb("Item (Entry: %u) has too large negative in stackable (%i), replace by value (-1) no stacking limits.",i,proto->Stackable); + const_cast<ItemPrototype*>(proto)->Stackable = -1; + } + else if(proto->Stackable > 1000) + { + sLog.outErrorDb("Item (Entry: %u) has too large value in stackable (%u), replace by hardcoded upper limit (1000).",i,proto->Stackable); + const_cast<ItemPrototype*>(proto)->Stackable = 1000; + } + + if(proto->StatsCount > MAX_ITEM_PROTO_STATS) { - sLog.outErrorDb("Item (Entry: %u) has too large value in stackable (%u), replace by hardcoded upper limit (255).",i,proto->Stackable); - const_cast<ItemPrototype*>(proto)->Stackable = 255; + sLog.outErrorDb("Item (Entry: %u) has too large value in statscount (%u), replace by hardcoded limit (%u).",i,proto->StatsCount,MAX_ITEM_PROTO_STATS); + const_cast<ItemPrototype*>(proto)->StatsCount = MAX_ITEM_PROTO_STATS; } - for (int j = 0; j < 10; j++) + for (int j = 0; j < MAX_ITEM_PROTO_STATS; ++j) { // for ItemStatValue != 0 if(proto->ItemStat[j].ItemStatValue && proto->ItemStat[j].ItemStatType >= MAX_ITEM_MOD) @@ -1551,7 +2051,7 @@ void ObjectMgr::LoadItemPrototypes() } } - for (int j = 0; j < 5; j++) + for (int j = 0; j < MAX_ITEM_PROTO_DAMAGES; ++j) { if(proto->Damage[j].DamageType >= MAX_SPELL_SCHOOL) { @@ -1561,7 +2061,7 @@ void ObjectMgr::LoadItemPrototypes() } // special format - if(proto->Spells[0].SpellId == SPELL_ID_GENERIC_LEARN) + if((proto->Spells[0].SpellId == SPELL_ID_GENERIC_LEARN) || (proto->Spells[0].SpellId == SPELL_ID_GENERIC_LEARN_PET)) { // spell_1 if(proto->Spells[0].SpellTrigger != ITEM_SPELLTRIGGER_ON_USE) @@ -1598,7 +2098,7 @@ void ObjectMgr::LoadItemPrototypes() const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE; } // allowed only in special format - else if(proto->Spells[1].SpellId==SPELL_ID_GENERIC_LEARN) + else if((proto->Spells[1].SpellId==SPELL_ID_GENERIC_LEARN) || (proto->Spells[1].SpellId==SPELL_ID_GENERIC_LEARN_PET)) { sLog.outErrorDb("Item (Entry: %u) has broken spell in spellid_%d (%u)",i,1+1,proto->Spells[1].SpellId); const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0; @@ -1608,7 +2108,7 @@ void ObjectMgr::LoadItemPrototypes() } // spell_3*,spell_4*,spell_5* is empty - for (int j = 2; j < 5; j++) + for (int j = 2; j < MAX_ITEM_PROTO_SPELLS; ++j) { if(proto->Spells[j].SpellTrigger != ITEM_SPELLTRIGGER_ON_USE) { @@ -1626,7 +2126,7 @@ void ObjectMgr::LoadItemPrototypes() // normal spell list else { - for (int j = 0; j < 5; j++) + for (int j = 0; j < MAX_ITEM_PROTO_SPELLS; ++j) { if(proto->Spells[j].SpellTrigger >= MAX_ITEM_SPELLTRIGGER || proto->Spells[j].SpellTrigger == ITEM_SPELLTRIGGER_LEARN_SPELL_ID) { @@ -1644,7 +2144,7 @@ void ObjectMgr::LoadItemPrototypes() const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0; } // allowed only in special format - else if(proto->Spells[j].SpellId==SPELL_ID_GENERIC_LEARN) + else if((proto->Spells[j].SpellId==SPELL_ID_GENERIC_LEARN) || (proto->Spells[j].SpellId==SPELL_ID_GENERIC_LEARN_PET)) { sLog.outErrorDb("Item (Entry: %u) has broken spell in spellid_%d (%u)",i,j+1,proto->Spells[j].SpellId); const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0; @@ -1692,10 +2192,39 @@ void ObjectMgr::LoadItemPrototypes() if(proto->Map && !sMapStore.LookupEntry(proto->Map)) sLog.outErrorDb("Item (Entry: %u) has wrong Map (%u)",i,proto->Map); + if(proto->BagFamily) + { + // check bits + for(uint32 j = 0; j < sizeof(proto->BagFamily)*8; ++j) + { + uint32 mask = 1 << j; + if((proto->BagFamily & mask)==0) + continue; + + ItemBagFamilyEntry const* bf = sItemBagFamilyStore.LookupEntry(j+1); + if(!bf) + { + sLog.outErrorDb("Item (Entry: %u) has bag family bit set not listed in ItemBagFamily.dbc, remove bit",i); + const_cast<ItemPrototype*>(proto)->BagFamily &= ~mask; + continue; + } + + if(BAG_FAMILY_MASK_CURRENCY_TOKENS & mask) + { + CurrencyTypesEntry const* ctEntry = sCurrencyTypesStore.LookupEntry(proto->ItemId); + if(!ctEntry) + { + sLog.outErrorDb("Item (Entry: %u) has currency bag family bit set in BagFamily but not listed in CurrencyTypes.dbc, remove bit",i); + const_cast<ItemPrototype*>(proto)->BagFamily &= ~mask; + } + } + } + } + if(proto->TotemCategory && !sTotemCategoryStore.LookupEntry(proto->TotemCategory)) sLog.outErrorDb("Item (Entry: %u) has wrong TotemCategory (%u)",i,proto->TotemCategory); - for (int j = 0; j < 3; j++) + for (int j = 0; j < MAX_ITEM_PROTO_SOCKETS; ++j) { if(proto->Socket[j].Color && (proto->Socket[j].Color & SOCKET_COLOR_ALL) != proto->Socket[j].Color) { @@ -1712,10 +2241,124 @@ void ObjectMgr::LoadItemPrototypes() sLog.outErrorDb("Item (Entry: %u) has wrong FoodType value (%u)",i,proto->FoodType); const_cast<ItemPrototype*>(proto)->FoodType = 0; } + + if(proto->ItemLimitCategory && !sItemLimitCategoryStore.LookupEntry(proto->ItemLimitCategory)) + { + sLog.outErrorDb("Item (Entry: %u) has wrong LimitCategory value (%u)",i,proto->ItemLimitCategory); + const_cast<ItemPrototype*>(proto)->ItemLimitCategory = 0; + } + + if(proto->HolidayId && !sHolidaysStore.LookupEntry(proto->HolidayId)) + { + sLog.outErrorDb("Item (Entry: %u) has wrong HolidayId value (%u)", i, proto->HolidayId); + const_cast<ItemPrototype*>(proto)->HolidayId = 0; + } } +} + +void ObjectMgr::LoadItemRequiredTarget() +{ + m_ItemRequiredTarget.clear(); // needed for reload case + + uint32 count = 0; - // this DBC used currently only for check item templates in DB. - sItemStore.Clear(); + QueryResult *result = WorldDatabase.Query("SELECT entry,type,targetEntry FROM item_required_target"); + + if (!result) + { + barGoLink bar(1); + + bar.step(); + + sLog.outString(); + sLog.outErrorDb(">> Loaded 0 ItemRequiredTarget. DB table `item_required_target` is empty."); + return; + } + + barGoLink bar(result->GetRowCount()); + + do + { + Field *fields = result->Fetch(); + bar.step(); + + uint32 uiItemId = fields[0].GetUInt32(); + uint32 uiType = fields[1].GetUInt32(); + uint32 uiTargetEntry = fields[2].GetUInt32(); + + ItemPrototype const* pItemProto = sItemStorage.LookupEntry<ItemPrototype>(uiItemId); + + if (!pItemProto) + { + sLog.outErrorDb("Table `item_required_target`: Entry %u listed for TargetEntry %u does not exist in `item_template`.",uiItemId,uiTargetEntry); + continue; + } + + bool bIsItemSpellValid = false; + + for(uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) + { + if (SpellEntry const* pSpellInfo = sSpellStore.LookupEntry(pItemProto->Spells[i].SpellId)) + { + if (pItemProto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_USE || + pItemProto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_NO_DELAY_USE) + { + SpellScriptTarget::const_iterator lower = spellmgr.GetBeginSpellScriptTarget(pSpellInfo->Id); + SpellScriptTarget::const_iterator upper = spellmgr.GetEndSpellScriptTarget(pSpellInfo->Id); + + if (lower != upper) + break; + + for (int j = 0; j < 3; ++j) + { + if (pSpellInfo->EffectImplicitTargetA[i] == TARGET_UNIT_TARGET_ENEMY || + pSpellInfo->EffectImplicitTargetB[i] == TARGET_UNIT_TARGET_ENEMY || + pSpellInfo->EffectImplicitTargetA[i] == TARGET_UNIT_TARGET_ANY || + pSpellInfo->EffectImplicitTargetB[i] == TARGET_UNIT_TARGET_ANY) + { + bIsItemSpellValid = true; + break; + } + } + if (bIsItemSpellValid) + break; + } + } + } + + if (!bIsItemSpellValid) + { + sLog.outErrorDb("Table `item_required_target`: Spell used by item %u does not have implicit target TARGET_CHAIN_DAMAGE(6), TARGET_DUELVSPLAYER(25), already listed in `spell_script_target` or doesn't have item spelltrigger.",uiItemId); + continue; + } + + if (!uiType || uiType > MAX_ITEM_REQ_TARGET_TYPE) + { + sLog.outErrorDb("Table `item_required_target`: Type %u for TargetEntry %u is incorrect.",uiType,uiTargetEntry); + continue; + } + + if (!uiTargetEntry) + { + sLog.outErrorDb("Table `item_required_target`: TargetEntry == 0 for Type (%u).",uiType); + continue; + } + + if (!sCreatureStorage.LookupEntry<CreatureInfo>(uiTargetEntry)) + { + sLog.outErrorDb("Table `item_required_target`: creature template entry %u does not exist.",uiTargetEntry); + continue; + } + + m_ItemRequiredTarget.insert(ItemRequiredTargetMap::value_type(uiItemId,ItemRequiredTarget(ItemRequiredTargetType(uiType),uiTargetEntry))); + + ++count; + } while (result->NextRow()); + + delete result; + + sLog.outString(); + sLog.outString(">> Loaded %u Item required targets", count); } void ObjectMgr::LoadPetLevelInfo() @@ -1756,7 +2399,10 @@ void ObjectMgr::LoadPetLevelInfo() if(current_level > STRONG_MAX_LEVEL) // hardcoded level maximum sLog.outErrorDb("Wrong (> %u) level %u in `pet_levelstats` table, ignoring.",STRONG_MAX_LEVEL,current_level); else + { sLog.outDetail("Unused (> MaxPlayerLevel in Trinityd.conf) level %u in `pet_levelstats` table, ignoring.",current_level); + ++count; // make result loading percent "expected" correct in case disabled detail mode for example. + } continue; } else if(current_level < 1) @@ -1999,7 +2645,7 @@ void ObjectMgr::LoadPlayerInfo() if(sWorld.getConfig(CONFIG_START_ALL_SPELLS)) result = WorldDatabase.Query("SELECT race, class, Spell, Active FROM playercreateinfo_spell_custom"); else - result = WorldDatabase.Query("SELECT race, class, Spell, Active FROM playercreateinfo_spell"); + result = WorldDatabase.Query("SELECT race, class, Spell FROM playercreateinfo_spell"); uint32 count = 0; @@ -2033,8 +2679,18 @@ void ObjectMgr::LoadPlayerInfo() continue; } - PlayerInfo* pInfo = &playerInfo[current_race][current_class]; - pInfo->spell.push_back(CreateSpellPair(fields[2].GetUInt16(), fields[3].GetUInt8())); + if(!current_race || !current_class) + { + uint32 min_race = current_race ? current_race : 1; + uint32 max_race = current_race ? current_race + 1 : MAX_RACES; + uint32 min_class = current_class ? current_class : 1; + uint32 max_class = current_class ? current_class + 1 : MAX_CLASSES; + for(uint32 r = min_race; r < max_race; ++r) + for(uint32 c = min_class; c < max_class; ++c) + playerInfo[r][c].spell.push_back(fields[2].GetUInt32()); + } + else + playerInfo[current_race][current_class].spell.push_back(fields[2].GetUInt32()); bar.step(); ++count; @@ -2139,7 +2795,10 @@ void ObjectMgr::LoadPlayerInfo() if(current_level > STRONG_MAX_LEVEL) // hardcoded level maximum sLog.outErrorDb("Wrong (> %u) level %u in `player_classlevelstats` table, ignoring.",STRONG_MAX_LEVEL,current_level); else + { sLog.outDetail("Unused (> MaxPlayerLevel in Trinityd.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; } @@ -2234,7 +2893,10 @@ void ObjectMgr::LoadPlayerInfo() if(current_level > STRONG_MAX_LEVEL) // hardcoded level maximum sLog.outErrorDb("Wrong (> %u) level %u in `player_levelstats` table, ignoring.",STRONG_MAX_LEVEL,current_level); else + { sLog.outDetail("Unused (> MaxPlayerLevel in Trinityd.conf) level %u in `player_levelstats` table, ignoring.",current_level); + ++count; // make result loading percent "expected" correct in case disabled detail mode for example. + } continue; } @@ -2306,6 +2968,70 @@ void ObjectMgr::LoadPlayerInfo() } } } + + // Loading xp per level data + { + mPlayerXPperLevel.resize(sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)); + for (uint32 level = 0; level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL); ++level) + mPlayerXPperLevel[level] = 0; + + // 0 1 + QueryResult *result = WorldDatabase.Query("SELECT lvl, xp_for_next_level FROM player_xp_for_level"); + + uint32 count = 0; + + if (!result) + { + barGoLink bar( 1 ); + + sLog.outString(); + sLog.outString( ">> Loaded %u xp for level definitions", count ); + sLog.outErrorDb( "Error loading `player_xp_for_level` table or empty table."); + exit(1); + } + + barGoLink bar( result->GetRowCount() ); + + do + { + Field* fields = result->Fetch(); + + uint32 current_level = fields[0].GetUInt32(); + uint32 current_xp = fields[1].GetUInt32(); + + if(current_level >= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)) + { + if(current_level > STRONG_MAX_LEVEL) // hardcoded level maximum + sLog.outErrorDb("Wrong (> %u) level %u in `player_xp_for_level` table, ignoring.", STRONG_MAX_LEVEL,current_level); + else + { + sLog.outDetail("Unused (> MaxPlayerLevel in mangosd.conf) level %u in `player_xp_for_levels` table, ignoring.",current_level); + ++count; // make result loading percent "expected" correct in case disabled detail mode for example. + } + continue; + } + //PlayerXPperLevel + mPlayerXPperLevel[current_level] = current_xp; + bar.step(); + ++count; + } + while (result->NextRow()); + + delete result; + + sLog.outString(); + sLog.outString( ">> Loaded %u xp for level definitions", count ); + } + + // fill level gaps + for (uint32 level = 1; level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL); ++level) + { + if( mPlayerXPperLevel[level] == 0) + { + sLog.outErrorDb("Level %i does not have XP for level data. Using data of level [%i] + 100.",level+1, level); + mPlayerXPperLevel[level] = mPlayerXPperLevel[level-1]+100; + } + } } void ObjectMgr::GetPlayerClassLevelInfo(uint32 class_, uint32 level, PlayerClassLevelInfo* info) const @@ -2341,7 +3067,8 @@ void ObjectMgr::BuildPlayerLevelInfo(uint8 race, uint8 _class, uint8 level, Play // base data (last known level) *info = playerInfo[race][_class].levelInfo[sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)-1]; - for(int lvl = sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)-1; lvl < level; ++lvl) + // if conversion from uint32 to uint8 causes unexpected behaviour, change lvl to uint32 + for(uint8 lvl = sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)-1; lvl < level; ++lvl) { switch(_class) { @@ -2550,15 +3277,15 @@ void ObjectMgr::LoadGroups() result = CharacterDatabase.Query("SELECT memberGuid, assistant, subgroup, leaderGuid FROM group_member ORDER BY leaderGuid"); if(!result) { - barGoLink bar( 1 ); - bar.step(); + barGoLink bar2( 1 ); + bar2.step(); } else { - barGoLink bar( result->GetRowCount() ); + barGoLink bar2( result->GetRowCount() ); do { - bar.step(); + bar2.step(); Field *fields = result->Fetch(); count++; leaderGuid = MAKE_NEW_GUID(fields[3].GetUInt32(), 0, HIGHGUID_PLAYER); @@ -2610,15 +3337,15 @@ void ObjectMgr::LoadGroups() if(!result) { - barGoLink bar( 1 ); - bar.step(); + barGoLink bar2( 1 ); + bar2.step(); } else { - barGoLink bar( result->GetRowCount() ); + barGoLink bar2( result->GetRowCount() ); do { - bar.step(); + bar2.step(); Field *fields = result->Fetch(); count++; leaderGuid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER); @@ -2632,7 +3359,14 @@ void ObjectMgr::LoadGroups() } } - InstanceSave *save = sInstanceSaveManager.AddInstanceSave(fields[1].GetUInt32(), fields[2].GetUInt32(), fields[4].GetUInt8(), (time_t)fields[5].GetUInt64(), (fields[6].GetUInt32() == 0), true); + MapEntry const* mapEntry = sMapStore.LookupEntry(fields[1].GetUInt32()); + if(!mapEntry || !mapEntry->IsDungeon()) + { + sLog.outErrorDb("Incorrect entry in group_instance table : no dungeon map %d", fields[1].GetUInt32()); + continue; + } + + InstanceSave *save = sInstanceSaveManager.AddInstanceSave(mapEntry->MapID, fields[2].GetUInt32(), fields[4].GetUInt8(), (time_t)fields[5].GetUInt64(), (fields[6].GetUInt32() == 0), true); group->BindToInstance(save, fields[3].GetBool(), true); }while( result->NextRow() ); delete result; @@ -2658,31 +3392,35 @@ void ObjectMgr::LoadQuests() QueryResult *result = WorldDatabase.Query("SELECT entry, Method, ZoneOrSort, SkillOrClass, MinLevel, QuestLevel, Type, RequiredRaces, RequiredSkillValue," // 9 10 11 12 13 14 15 16 "RepObjectiveFaction, RepObjectiveValue, RequiredMinRepFaction, RequiredMinRepValue, RequiredMaxRepFaction, RequiredMaxRepValue, SuggestedPlayers, LimitTime," - // 17 18 19 20 21 22 23 24 25 26 - "QuestFlags, SpecialFlags, CharTitleId, PrevQuestId, NextQuestId, ExclusiveGroup, NextQuestInChain, SrcItemId, SrcItemCount, SrcSpell," - // 27 28 29 30 31 32 33 34 35 36 + // 17 18 19 20 21 22 23 24 25 26 27 28 + "QuestFlags, SpecialFlags, CharTitleId, PlayersSlain, BonusTalents, PrevQuestId, NextQuestId, ExclusiveGroup, NextQuestInChain, SrcItemId, SrcItemCount, SrcSpell," + // 29 30 31 32 33 34 35 36 37 38 "Title, Details, Objectives, OfferRewardText, RequestItemsText, EndText, ObjectiveText1, ObjectiveText2, ObjectiveText3, ObjectiveText4," - // 37 38 39 40 41 42 43 44 + // 39 40 41 42 43 44 45 46 "ReqItemId1, ReqItemId2, ReqItemId3, ReqItemId4, ReqItemCount1, ReqItemCount2, ReqItemCount3, ReqItemCount4," - // 45 46 47 48 49 50 51 52 53 54 54 55 - "ReqSourceId1, ReqSourceId2, ReqSourceId3, ReqSourceId4, ReqSourceCount1, ReqSourceCount2, ReqSourceCount3, ReqSourceCount4, ReqSourceRef1, ReqSourceRef2, ReqSourceRef3, ReqSourceRef4," - // 57 58 59 60 61 62 63 64 + // 47 48 49 50 51 52 53 54 + "ReqSourceId1, ReqSourceId2, ReqSourceId3, ReqSourceId4, ReqSourceCount1, ReqSourceCount2, ReqSourceCount3, ReqSourceCount4," + // 55 56 57 58 59 60 61 62 "ReqCreatureOrGOId1, ReqCreatureOrGOId2, ReqCreatureOrGOId3, ReqCreatureOrGOId4, ReqCreatureOrGOCount1, ReqCreatureOrGOCount2, ReqCreatureOrGOCount3, ReqCreatureOrGOCount4," - // 65 66 67 68 + // 63 64 65 66 "ReqSpellCast1, ReqSpellCast2, ReqSpellCast3, ReqSpellCast4," - // 69 70 71 72 73 74 + // 67 68 69 70 71 72 "RewChoiceItemId1, RewChoiceItemId2, RewChoiceItemId3, RewChoiceItemId4, RewChoiceItemId5, RewChoiceItemId6," - // 75 76 77 78 79 80 + // 73 74 75 76 77 78 "RewChoiceItemCount1, RewChoiceItemCount2, RewChoiceItemCount3, RewChoiceItemCount4, RewChoiceItemCount5, RewChoiceItemCount6," - // 81 82 83 84 85 86 87 88 + // 79 80 81 82 83 84 85 86 "RewItemId1, RewItemId2, RewItemId3, RewItemId4, RewItemCount1, RewItemCount2, RewItemCount3, RewItemCount4," - // 89 90 91 92 93 94 95 96 97 98 + // 87 88 89 90 91 92 93 94 95 96 "RewRepFaction1, RewRepFaction2, RewRepFaction3, RewRepFaction4, RewRepFaction5, RewRepValue1, RewRepValue2, RewRepValue3, RewRepValue4, RewRepValue5," - // 99 100 101 102 103 104 105 106 107 108 109 + // 97 98 99 100 101 102 103 104 105 106 107 "RewHonorableKills, RewOrReqMoney, RewMoneyMaxLevel, RewSpell, RewSpellCast, RewMailTemplateId, RewMailDelaySecs, PointMapId, PointX, PointY, PointOpt," - // 110 111 112 113 114 115 116 117 118 119 - "DetailsEmote1, DetailsEmote2, DetailsEmote3, DetailsEmote4,IncompleteEmote, CompleteEmote, OfferRewardEmote1, OfferRewardEmote2, OfferRewardEmote3, OfferRewardEmote4," - // 120 121 + // 108 109 110 111 112 113 114 115 + "DetailsEmote1, DetailsEmote2, DetailsEmote3, DetailsEmote4, DetailsEmoteDelay1, DetailsEmoteDelay2, DetailsEmoteDelay3, DetailsEmoteDelay4," + // 116 117 118 119 120 121 + "IncompleteEmote, CompleteEmote, OfferRewardEmote1, OfferRewardEmote2, OfferRewardEmote3, OfferRewardEmote4," + // 122 123 124 125 + "OfferRewardEmoteDelay1, OfferRewardEmoteDelay2, OfferRewardEmoteDelay3, OfferRewardEmoteDelay4," + // 126 127 "StartScript, CompleteScript" " FROM quest_template"); if(result == NULL) @@ -2730,7 +3468,7 @@ void ObjectMgr::LoadQuests() qinfo->QuestFlags &= QUEST_TRINITY_FLAGS_DB_ALLOWED; } - if(qinfo->QuestFlags & QUEST_FLAGS_DAILY) + if(qinfo->QuestFlags & (QUEST_FLAGS_DAILY | QUEST_FLAGS_WEEKLY)) { if(!(qinfo->QuestFlags & QUEST_TRINITY_FLAGS_REPEATABLE)) { @@ -2742,7 +3480,7 @@ void ObjectMgr::LoadQuests() if(qinfo->QuestFlags & QUEST_FLAGS_AUTO_REWARDED) { // at auto-reward can be rewarded only RewChoiceItemId[0] - for(int j = 1; j < QUEST_REWARD_CHOICES_COUNT; ++j ) + for(uint8 j = 1; j < QUEST_REWARD_CHOICES_COUNT; ++j ) { if(uint32 id = qinfo->RewChoiceItemId[j]) { @@ -2854,10 +3592,10 @@ void ObjectMgr::LoadQuests() // no changes, quest can't be done for this requirement } - if(qinfo->RequiredMinRepValue && qinfo->RequiredMinRepValue > Player::Reputation_Cap) + if(qinfo->RequiredMinRepValue && qinfo->RequiredMinRepValue > ReputationMgr::Reputation_Cap) { sLog.outErrorDb("Quest %u has `RequiredMinRepValue` = %d but max reputation is %u, quest can't be done.", - qinfo->GetQuestId(),qinfo->RequiredMinRepValue,Player::Reputation_Cap); + qinfo->GetQuestId(),qinfo->RequiredMinRepValue,ReputationMgr::Reputation_Cap); // no changes, quest can't be done for this requirement } @@ -2936,7 +3674,7 @@ void ObjectMgr::LoadQuests() } } - for(int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j ) + for(uint8 j = 0; j < QUEST_OBJECTIVES_COUNT; ++j ) { uint32 id = qinfo->ReqItemId[j]; if(id) @@ -2965,7 +3703,7 @@ void ObjectMgr::LoadQuests() } } - for(int j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; ++j ) + for(uint8 j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; ++j ) { uint32 id = qinfo->ReqSourceId[j]; if(id) @@ -2976,20 +3714,6 @@ void ObjectMgr::LoadQuests() qinfo->GetQuestId(),j+1,id,id); // no changes, quest can't be done for this requirement } - - if(!qinfo->ReqSourceCount[j]) - { - sLog.outErrorDb("Quest %u has `ReqSourceId%d` = %u but `ReqSourceCount%d` = 0, quest can't be done.", - qinfo->GetQuestId(),j+1,id,j+1); - qinfo->ReqSourceId[j] = 0; // prevent incorrect work of quest - } - - if(!qinfo->ReqSourceRef[j]) - { - sLog.outErrorDb("Quest %u has `ReqSourceId%d` = %u but `ReqSourceRef%d` = 0, quest can't be done.", - qinfo->GetQuestId(),j+1,id,j+1); - qinfo->ReqSourceId[j] = 0; // prevent incorrect work of quest - } } else { @@ -2999,45 +3723,10 @@ void ObjectMgr::LoadQuests() qinfo->GetQuestId(),j+1,j+1,qinfo->ReqSourceCount[j]); // no changes, quest ignore this data } - - if(qinfo->ReqSourceRef[j]>0) - { - sLog.outErrorDb("Quest %u has `ReqSourceId%d` = 0 but `ReqSourceRef%d` = %u.", - qinfo->GetQuestId(),j+1,j+1,qinfo->ReqSourceRef[j]); - // no changes, quest ignore this data - } } } - for(int j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; ++j ) - { - uint32 ref = qinfo->ReqSourceRef[j]; - if(ref) - { - if(ref > QUEST_OBJECTIVES_COUNT) - { - sLog.outErrorDb("Quest %u has `ReqSourceRef%d` = %u but max value in `ReqSourceRef%d` is %u, quest can't be done.", - qinfo->GetQuestId(),j+1,ref,j+1,QUEST_OBJECTIVES_COUNT); - // no changes, quest can't be done for this requirement - } - else - if(!qinfo->ReqItemId[ref-1] && !qinfo->ReqSpell[ref-1]) - { - sLog.outErrorDb("Quest %u has `ReqSourceRef%d` = %u but `ReqItemId%u` = 0 and `ReqSpellCast%u` = 0, quest can't be done.", - qinfo->GetQuestId(),j+1,ref,ref,ref); - // no changes, quest can't be done for this requirement - } - else if(qinfo->ReqItemId[ref-1] && qinfo->ReqSpell[ref-1]) - { - sLog.outErrorDb("Quest %u has `ReqItemId%u` = %u and `ReqSpellCast%u` = %u, quest can't have both fields <> 0, then can't be done.", - qinfo->GetQuestId(),ref,qinfo->ReqItemId[ref-1],ref,qinfo->ReqSpell[ref-1]); - // no changes, quest can't be done for this requirement - qinfo->ReqSourceId[j] = 0; // prevent incorrect work of quest - } - } - } - - for(int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j ) + for(uint8 j = 0; j < QUEST_OBJECTIVES_COUNT; ++j ) { uint32 id = qinfo->ReqSpell[j]; if(id) @@ -3047,13 +3736,13 @@ void ObjectMgr::LoadQuests() { sLog.outErrorDb("Quest %u has `ReqSpellCast%d` = %u but spell %u does not exist, quest can't be done.", qinfo->GetQuestId(),j+1,id,id); - // no changes, quest can't be done for this requirement + continue; } if(!qinfo->ReqCreatureOrGOId[j]) { bool found = false; - for(int k = 0; k < 3; ++k) + for(uint8 k = 0; k < 3; ++k) { if( spellInfo->Effect[k]==SPELL_EFFECT_QUEST_COMPLETE && uint32(spellInfo->EffectMiscValue[k])==qinfo->QuestId || spellInfo->Effect[k]==SPELL_EFFECT_SEND_EVENT) @@ -3083,7 +3772,7 @@ void ObjectMgr::LoadQuests() } } - for(int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j ) + for(uint8 j = 0; j < QUEST_OBJECTIVES_COUNT; ++j ) { int32 id = qinfo->ReqCreatureOrGOId[j]; if(id < 0 && !sGOStorage.LookupEntry<GameObjectInfo>(-id)) @@ -3121,7 +3810,7 @@ void ObjectMgr::LoadQuests() } } - for(int j = 0; j < QUEST_REWARD_CHOICES_COUNT; ++j ) + for(uint8 j = 0; j < QUEST_REWARD_CHOICES_COUNT; ++j ) { uint32 id = qinfo->RewChoiceItemId[j]; if(id) @@ -3148,7 +3837,7 @@ void ObjectMgr::LoadQuests() } } - for(int j = 0; j < QUEST_REWARDS_COUNT; ++j ) + for(uint8 j = 0; j < QUEST_REWARDS_COUNT; ++j ) { uint32 id = qinfo->RewItemId[j]; if(id) @@ -3175,7 +3864,7 @@ void ObjectMgr::LoadQuests() } } - for(int j = 0; j < QUEST_REPUTATIONS_COUNT; ++j) + for(uint8 j = 0; j < QUEST_REPUTATIONS_COUNT; ++j) { if(qinfo->RewRepFaction[j]) { @@ -3254,14 +3943,15 @@ void ObjectMgr::LoadQuests() if(qinfo->NextQuestInChain) { - if(mQuestTemplates.find(qinfo->NextQuestInChain) == mQuestTemplates.end()) + QuestMap::iterator qNextItr = mQuestTemplates.find(qinfo->NextQuestInChain); + if(qNextItr == mQuestTemplates.end()) { sLog.outErrorDb("Quest %u has `NextQuestInChain` = %u but quest %u does not exist, quest chain will not work.", qinfo->GetQuestId(),qinfo->NextQuestInChain ,qinfo->NextQuestInChain ); qinfo->NextQuestInChain = 0; } else - mQuestTemplates[qinfo->NextQuestInChain]->prevChainQuests.push_back(qinfo->GetQuestId()); + qNextItr->second->prevChainQuests.push_back(qinfo->GetQuestId()); } // fill additional data stores @@ -3279,14 +3969,15 @@ void ObjectMgr::LoadQuests() if(qinfo->NextQuestId) { - if (mQuestTemplates.find(abs(qinfo->GetNextQuestId())) == mQuestTemplates.end()) + QuestMap::iterator qNextItr = mQuestTemplates.find(abs(qinfo->GetNextQuestId())); + if (qNextItr == mQuestTemplates.end()) { sLog.outErrorDb("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()); - mQuestTemplates[abs(qinfo->GetNextQuestId())]->prevQuests.push_back(signedQuestId); + qNextItr->second->prevQuests.push_back(signedQuestId); } } @@ -3303,7 +3994,7 @@ void ObjectMgr::LoadQuests() if(!spellInfo) continue; - for(int j = 0; j < 3; ++j) + for(uint8 j = 0; j < 3; ++j) { if(spellInfo->Effect[j] != SPELL_EFFECT_QUEST_COMPLETE) continue; @@ -3327,7 +4018,7 @@ void ObjectMgr::LoadQuests() } sLog.outString(); - sLog.outString( ">> Loaded %u quests definitions", mQuestTemplates.size() ); + sLog.outString( ">> Loaded %lu quests definitions", (unsigned long)mQuestTemplates.size() ); } void ObjectMgr::LoadQuestLocales() @@ -3352,7 +4043,7 @@ void ObjectMgr::LoadQuestLocales() bar.step(); - sLog.outString(""); + sLog.outString(); sLog.outString(">> Loaded 0 Quest locale strings. DB table `locales_quest` is empty."); return; } @@ -3368,7 +4059,7 @@ void ObjectMgr::LoadQuestLocales() QuestLocale& data = mQuestLocaleMap[entry]; - for(int i = 1; i < MAX_LOCALE; ++i) + for(uint8 i = 1; i < MAX_LOCALE; ++i) { std::string str = fields[1+10*(i-1)].GetCppString(); if(!str.empty()) @@ -3442,7 +4133,7 @@ void ObjectMgr::LoadQuestLocales() data.EndText[idx] = str; } } - for(int k = 0; k < 4; ++k) + for(uint8 k = 0; k < 4; ++k) { str = fields[1+10*(i-1)+6+k].GetCppString(); if(!str.empty()) @@ -3463,58 +4154,7 @@ void ObjectMgr::LoadQuestLocales() delete result; sLog.outString(); - sLog.outString( ">> Loaded %u Quest locale strings", mQuestLocaleMap.size() ); -} - -void ObjectMgr::LoadPetCreateSpells() -{ - QueryResult *result = WorldDatabase.Query("SELECT entry, Spell1, Spell2, Spell3, Spell4 FROM petcreateinfo_spell"); - if(!result) - { - barGoLink bar( 1 ); - bar.step(); - - sLog.outString(); - sLog.outString( ">> Loaded 0 pet create spells" ); - sLog.outErrorDb("`petcreateinfo_spell` table is empty!"); - return; - } - - uint32 count = 0; - - barGoLink bar( result->GetRowCount() ); - - mPetCreateSpell.clear(); - - do - { - Field *fields = result->Fetch(); - bar.step(); - - uint32 creature_id = fields[0].GetUInt32(); - - if(!creature_id || !sCreatureStorage.LookupEntry<CreatureInfo>(creature_id)) - continue; - - PetCreateSpellEntry PetCreateSpell; - for(int i = 0; i < 4; i++) - { - PetCreateSpell.spellid[i] = fields[i + 1].GetUInt32(); - - if(PetCreateSpell.spellid[i] && !sSpellStore.LookupEntry(PetCreateSpell.spellid[i])) - sLog.outErrorDb("Spell %u listed in `petcreateinfo_spell` does not exist",PetCreateSpell.spellid[i]); - } - - mPetCreateSpell[creature_id] = PetCreateSpell; - - ++count; - } - while (result->NextRow()); - - delete result; - - sLog.outString(); - sLog.outString( ">> Loaded %u pet create spells", count ); + sLog.outString( ">> Loaded %lu Quest locale strings", (unsigned long)mQuestLocaleMap.size() ); } void ObjectMgr::LoadScripts(ScriptMapMap& scripts, char const* tablename) @@ -3584,6 +4224,16 @@ void ObjectMgr::LoadScripts(ScriptMapMap& scripts, char const* tablename) break; } + case SCRIPT_COMMAND_EMOTE: + { + if(!sEmotesStore.LookupEntry(tmp.datalong)) + { + sLog.outErrorDb("Table `%s` has invalid emote id (datalong = %u) in SCRIPT_COMMAND_EMOTE for script id %u",tablename,tmp.datalong,tmp.id); + continue; + } + break; + } + case SCRIPT_COMMAND_TELEPORT_TO: { if(!sMapStore.LookupEntry(tmp.datalong)) @@ -3712,6 +4362,21 @@ void ObjectMgr::LoadScripts(ScriptMapMap& scripts, char const* tablename) } case SCRIPT_COMMAND_REMOVE_AURA: + { + if(!sSpellStore.LookupEntry(tmp.datalong)) + { + sLog.outErrorDb("Table `%s` using non-existent spell (id: %u) in SCRIPT_COMMAND_REMOVE_AURA or SCRIPT_COMMAND_CAST_SPELL for script id %u", + tablename,tmp.datalong,tmp.id); + continue; + } + if(tmp.datalong2 & ~0x1) // 1 bits (0,1) + { + sLog.outErrorDb("Table `%s` using unknown flags in datalong2 (%u)i n SCRIPT_COMMAND_CAST_SPELL for script id %u", + tablename,tmp.datalong2,tmp.id); + continue; + } + break; + } case SCRIPT_COMMAND_CAST_SPELL: { if(!sSpellStore.LookupEntry(tmp.datalong)) @@ -3720,6 +4385,12 @@ void ObjectMgr::LoadScripts(ScriptMapMap& scripts, char const* tablename) tablename,tmp.datalong,tmp.id); continue; } + if(tmp.datalong2 & ~0x3) // 2 bits + { + sLog.outErrorDb("Table `%s` using unknown flags in datalong2 (%u)i n SCRIPT_COMMAND_CAST_SPELL for script id %u", + tablename,tmp.datalong2,tmp.id); + continue; + } break; } } @@ -3793,7 +4464,7 @@ void ObjectMgr::LoadSpellScripts() //check for correct spellEffect bool found = false; - for(int i=0; i<3; ++i) + for(uint8 i=0; i<3; ++i) { // skip empty effects if( !spellInfo->Effect[i] ) @@ -3825,13 +4496,16 @@ void ObjectMgr::LoadEventScripts() switch(goInfo->type) { case GAMEOBJECT_TYPE_GOOBER: - if(goInfo->goober.eventId) + if (goInfo->goober.eventId) evt_scripts.insert(goInfo->goober.eventId); break; case GAMEOBJECT_TYPE_CHEST: - if(goInfo->chest.eventId) + if (goInfo->chest.eventId) evt_scripts.insert(goInfo->chest.eventId); break; + case GAMEOBJECT_TYPE_CAMERA: + if (goInfo->camera.eventID) + evt_scripts.insert(goInfo->camera.eventID); default: break; } @@ -3843,7 +4517,7 @@ void ObjectMgr::LoadEventScripts() SpellEntry const * spell = sSpellStore.LookupEntry(i); if (spell) { - for(int j=0; j<3; ++j) + for(uint8 j=0; j<3; ++j) { if( spell->Effect[j] == SPELL_EFFECT_SEND_EVENT ) { @@ -3858,7 +4532,8 @@ void ObjectMgr::LoadEventScripts() { std::set<uint32>::const_iterator itr2 = evt_scripts.find(itr->first); if (itr2 == evt_scripts.end()) - sLog.outErrorDb("Table `event_scripts` has script (Id: %u) not referring to any gameobject_template type 10 data2 field or type 3 data6 field or any spell effect %u", itr->first, SPELL_EFFECT_SEND_EVENT); + sLog.outErrorDb("Table `event_scripts` has script (Id: %u) not referring to any gameobject_template type 10 data2 field, type 3 data6 field, type 13 data 2 field or any spell effect %u", + itr->first, SPELL_EFFECT_SEND_EVENT); } } @@ -3968,7 +4643,7 @@ void ObjectMgr::LoadPageTextLocales() bar.step(); - sLog.outString(""); + sLog.outString(); sLog.outString(">> Loaded 0 PageText locale strings. DB table `locales_page_text` is empty."); return; } @@ -3984,7 +4659,7 @@ void ObjectMgr::LoadPageTextLocales() PageTextLocale& data = mPageTextLocaleMap[entry]; - for(int i = 1; i < MAX_LOCALE; ++i) + for(uint8 i = 1; i < MAX_LOCALE; ++i) { std::string str = fields[i].GetCppString(); if(str.empty()) @@ -4005,13 +4680,13 @@ void ObjectMgr::LoadPageTextLocales() delete result; sLog.outString(); - sLog.outString( ">> Loaded %u PageText locale strings", mPageTextLocaleMap.size() ); + sLog.outString( ">> Loaded %lu PageText locale strings", (unsigned long)mPageTextLocaleMap.size() ); } struct SQLInstanceLoader : public SQLStorageLoaderBase<SQLInstanceLoader> { template<class D> - void convert_from_str(uint32 field_pos, char *src, D &dst) + void convert_from_str(uint32 /*field_pos*/, char *src, D &dst) { dst = D(objmgr.GetScriptId(src)); } @@ -4035,14 +4710,20 @@ void ObjectMgr::LoadInstanceTemplate() else if(!entry->HasResetTime()) continue; + //FIXME: now exist heroic instance, normal/heroic raid instances + // entry->resetTimeHeroic store reset time for both heroic mode instance (raid and non-raid) + // entry->resetTimeRaid store reset time for normal raid only + // for current state entry->resetTimeRaid == entry->resetTimeHeroic in case raid instances with heroic mode. + // but at some point wee need implement reset time dependent from raid instance mode if(temp->reset_delay == 0) { // use defaults from the DBC - if(entry->SupportsHeroicMode()) + if(entry->resetTimeHeroic) // for both raid and non raids, read above { temp->reset_delay = entry->resetTimeHeroic / DAY; } else if (entry->resetTimeRaid && entry->map_type == MAP_RAID) + // for normal raid only { temp->reset_delay = entry->resetTimeRaid / DAY; } @@ -4056,27 +4737,16 @@ void ObjectMgr::LoadInstanceTemplate() sLog.outString(); } -void ObjectMgr::AddGossipText(GossipText *pGText) -{ - ASSERT( pGText->Text_ID ); - ASSERT( mGossipText.find(pGText->Text_ID) == mGossipText.end() ); - mGossipText[pGText->Text_ID] = pGText; -} - -GossipText *ObjectMgr::GetGossipText(uint32 Text_ID) +GossipText const *ObjectMgr::GetGossipText(uint32 Text_ID) const { - GossipTextMap::const_iterator itr; - for (itr = mGossipText.begin(); itr != mGossipText.end(); ++itr) - { - if(itr->second->Text_ID == Text_ID) - return itr->second; - } + GossipTextMap::const_iterator itr = mGossipText.find(Text_ID); + if(itr != mGossipText.end()) + return &itr->second; return NULL; } void ObjectMgr::LoadGossipText() { - GossipText *pGText; QueryResult *result = WorldDatabase.Query( "SELECT * FROM npc_text" ); int count = 0; @@ -4103,34 +4773,29 @@ void ObjectMgr::LoadGossipText() bar.step(); - pGText = new GossipText; - pGText->Text_ID = fields[cic++].GetUInt32(); - - for (int i=0; i< 8; i++) + uint32 Text_ID = fields[cic++].GetUInt32(); + if(!Text_ID) { - pGText->Options[i].Text_0 = fields[cic++].GetCppString(); - pGText->Options[i].Text_1 = fields[cic++].GetCppString(); - - pGText->Options[i].Language = fields[cic++].GetUInt32(); - pGText->Options[i].Probability = fields[cic++].GetFloat(); + sLog.outErrorDb("Table `npc_text` has record wit reserved id 0, ignore."); + continue; + } - pGText->Options[i].Emotes[0]._Delay = fields[cic++].GetUInt32(); - pGText->Options[i].Emotes[0]._Emote = fields[cic++].GetUInt32(); + GossipText& gText = mGossipText[Text_ID]; - pGText->Options[i].Emotes[1]._Delay = fields[cic++].GetUInt32(); - pGText->Options[i].Emotes[1]._Emote = fields[cic++].GetUInt32(); + for (int i=0; i< 8; i++) + { + gText.Options[i].Text_0 = fields[cic++].GetCppString(); + gText.Options[i].Text_1 = fields[cic++].GetCppString(); - pGText->Options[i].Emotes[2]._Delay = fields[cic++].GetUInt32(); - pGText->Options[i].Emotes[2]._Emote = fields[cic++].GetUInt32(); - } + gText.Options[i].Language = fields[cic++].GetUInt32(); + gText.Options[i].Probability = fields[cic++].GetFloat(); - if ( !pGText->Text_ID ){ - delete pGText; - continue; + for(uint8 j=0; j < 3; ++j) + { + gText.Options[i].Emotes[j]._Delay = fields[cic++].GetUInt32(); + gText.Options[i].Emotes[j]._Emote = fields[cic++].GetUInt32(); + } } - - AddGossipText( pGText ); - } while( result->NextRow() ); sLog.outString(); @@ -4159,8 +4824,8 @@ void ObjectMgr::LoadNpcTextLocales() bar.step(); - sLog.outString(""); - sLog.outString(">> Loaded 0 Quest locale strings. DB table `locales_npc_text` is empty."); + sLog.outString(); + sLog.outString(">> Loaded 0 NpcText locale strings. DB table `locales_npc_text` is empty."); return; } @@ -4175,9 +4840,9 @@ void ObjectMgr::LoadNpcTextLocales() NpcTextLocale& data = mNpcTextLocaleMap[entry]; - for(int i=1; i<MAX_LOCALE; ++i) + for(uint8 i=1; i<MAX_LOCALE; ++i) { - for(int j=0; j<8; ++j) + for(uint8 j=0; j<8; ++j) { std::string str0 = fields[1+8*2*(i-1)+2*j].GetCppString(); if(!str0.empty()) @@ -4210,7 +4875,7 @@ void ObjectMgr::LoadNpcTextLocales() delete result; sLog.outString(); - sLog.outString( ">> Loaded %u NpcText locale strings", mNpcTextLocaleMap.size() ); + sLog.outString( ">> Loaded %lu NpcText locale strings", (unsigned long)mNpcTextLocaleMap.size() ); } //not very fast function but it is called only once a day, or on starting-up @@ -4220,18 +4885,31 @@ void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp) sLog.outDebug("Returning mails current time: hour: %d, minute: %d, second: %d ", localtime(&basetime)->tm_hour, localtime(&basetime)->tm_min, localtime(&basetime)->tm_sec); //delete all old mails without item and without body immediately, if starting server if (!serverUp) - CharacterDatabase.PExecute("DELETE FROM mail WHERE expire_time < '" I64FMTD "' AND has_items = '0' AND itemTextId = 0", (uint64)basetime); + CharacterDatabase.PExecute("DELETE FROM mail WHERE expire_time < '" UI64FMTD "' AND has_items = '0' AND itemTextId = 0", (uint64)basetime); // 0 1 2 3 4 5 6 7 8 9 - QueryResult* result = CharacterDatabase.PQuery("SELECT id,messageType,sender,receiver,itemTextId,has_items,expire_time,cod,checked,mailTemplateId FROM mail WHERE expire_time < '" I64FMTD "'", (uint64)basetime); + QueryResult* result = CharacterDatabase.PQuery("SELECT id,messageType,sender,receiver,itemTextId,has_items,expire_time,cod,checked,mailTemplateId FROM mail WHERE expire_time < '" UI64FMTD "'", (uint64)basetime); if ( !result ) + { + barGoLink bar(1); + bar.step(); + sLog.outString(); + sLog.outString(">> Only expired mails (need to be return or delete) or DB table `mail` is empty."); return; // any mails need to be returned or deleted - Field *fields; + } + //std::ostringstream delitems, delmails; //will be here for optimization //bool deletemail = false, deleteitem = false; //delitems << "DELETE FROM item_instance WHERE guid IN ( "; //delmails << "DELETE FROM mail WHERE id IN ( " + + barGoLink bar( result->GetRowCount() ); + uint32 count = 0; + Field *fields; + do { + bar.step(); + fields = result->Fetch(); Mail *m = new Mail; m->messageID = fields[0].GetUInt32(); @@ -4284,7 +4962,7 @@ void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp) else { //mail will be returned: - CharacterDatabase.PExecute("UPDATE mail SET sender = '%u', receiver = '%u', expire_time = '" I64FMTD "', deliver_time = '" I64FMTD "',cod = '0', checked = '%u' WHERE id = '%u'", m->receiver, m->sender, (uint64)(basetime + 30*DAY), (uint64)basetime, MAIL_CHECK_MASK_RETURNED, m->messageID); + CharacterDatabase.PExecute("UPDATE mail SET sender = '%u', receiver = '%u', expire_time = '" UI64FMTD "', deliver_time = '" UI64FMTD "',cod = '0', checked = '%u' WHERE id = '%u'", m->receiver, m->sender, (uint64)(basetime + 30*DAY), (uint64)basetime, MAIL_CHECK_MASK_RETURNED, m->messageID); delete m; continue; } @@ -4297,8 +4975,12 @@ void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp) //delmails << m->messageID << ", "; CharacterDatabase.PExecute("DELETE FROM mail WHERE id = '%u'", m->messageID); delete m; + ++count; } while (result->NextRow()); delete result; + + sLog.outString(); + sLog.outString( ">> Loaded %u mails", count ); } void ObjectMgr::LoadQuestAreaTriggers() @@ -4455,7 +5137,7 @@ void ObjectMgr::LoadAreaTriggerScripts() sLog.outString( ">> Loaded %u areatrigger scripts", count ); } -uint32 ObjectMgr::GetNearestTaxiNode( float x, float y, float z, uint32 mapid ) +uint32 ObjectMgr::GetNearestTaxiNode( float x, float y, float z, uint32 mapid, uint32 team ) { bool found = false; float dist; @@ -4464,24 +5146,31 @@ uint32 ObjectMgr::GetNearestTaxiNode( float x, float y, float z, uint32 mapid ) for(uint32 i = 1; i < sTaxiNodesStore.GetNumRows(); ++i) { TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(i); - if(node && node->map_id == mapid) + if(!node || node->map_id != mapid || !node->MountCreatureID[team == ALLIANCE ? 1 : 0]) + continue; + + uint8 field = (uint8)((i - 1) / 32); + uint32 submask = 1<<((i-1)%32); + + // skip not taxi network nodes + if((sTaxiNodesMask[field] & submask)==0) + continue; + + float dist2 = (node->x - x)*(node->x - x)+(node->y - y)*(node->y - y)+(node->z - z)*(node->z - z); + if(found) { - float dist2 = (node->x - x)*(node->x - x)+(node->y - y)*(node->y - y)+(node->z - z)*(node->z - z); - if(found) - { - if(dist2 < dist) - { - dist = dist2; - id = i; - } - } - else + if(dist2 < dist) { - found = true; dist = dist2; id = i; } } + else + { + found = true; + dist = dist2; + id = i; + } } return id; @@ -4511,38 +5200,40 @@ void ObjectMgr::GetTaxiPath( uint32 source, uint32 destination, uint32 &path, ui path = dest_i->second.ID; } -uint16 ObjectMgr::GetTaxiMount( uint32 id, uint32 team ) +uint32 ObjectMgr::GetTaxiMountDisplayId( uint32 id, uint32 team, bool allowed_alt_team /* = false */) { uint16 mount_entry = 0; - uint16 mount_id = 0; + // select mount creature id TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(id); if(node) { - if (team == ALLIANCE) mount_entry = node->alliance_mount_type; - else mount_entry = node->horde_mount_type; - - CreatureInfo const *cinfo = GetCreatureTemplate(mount_entry); - if (cinfo) + if (team == ALLIANCE) { - if(! (mount_id = cinfo->GetRandomValidModelId())) - { - sLog.outErrorDb("No displayid found for the taxi mount with the entry %u! Can't load it!", mount_entry); - return false; - } + mount_entry = node->MountCreatureID[1]; + if(!mount_entry && allowed_alt_team) + mount_entry = node->MountCreatureID[0]; + } + else if (team == HORDE) + { + mount_entry = node->MountCreatureID[0]; + + if(!mount_entry && allowed_alt_team) + mount_entry = node->MountCreatureID[1]; } } - CreatureModelInfo const *minfo = GetCreatureModelInfo(mount_id); - if(!minfo) - { - sLog.outErrorDb("Taxi mount (Entry: %u) for taxi node (Id: %u) for team %u has model %u not found in table `creature_model_info`, can't load. ", - mount_entry,id,team,mount_id); + CreatureInfo const *mount_info = GetCreatureTemplate(mount_entry); + if (!mount_info) + return 0; - return false; - } - if(minfo->modelid_other_gender!=0) - mount_id = urand(0,1) ? mount_id : minfo->modelid_other_gender; + uint16 mount_id = objmgr.ChooseDisplayId(team,mount_info); + if (!mount_id) + return 0; + + CreatureModelInfo const *minfo = objmgr.GetCreatureModelRandomGender(mount_id); + if (minfo) + mount_id = minfo->modelid; return mount_id; } @@ -4657,7 +5348,7 @@ void ObjectMgr::LoadGraveyardZones() WorldSafeLocsEntry const *ObjectMgr::GetClosestGraveYard(float x, float y, float z, uint32 MapId, uint32 team) { // search for zone associated closest graveyard - uint32 zoneId = MapManager::Instance().GetZoneId(MapId,x,y); + uint32 zoneId = MapManager::Instance().GetZoneId(MapId,x,y,z); // Simulate std. algorithm: // found some graveyard associated to (ghost_zone,ghost_map) @@ -4668,7 +5359,10 @@ WorldSafeLocsEntry const *ObjectMgr::GetClosestGraveYard(float x, float y, float // then check faction GraveYardMap::const_iterator graveLow = mGraveYardMap.lower_bound(zoneId); GraveYardMap::const_iterator graveUp = mGraveYardMap.upper_bound(zoneId); - if(graveLow==graveUp) + MapEntry const* map = sMapStore.LookupEntry(MapId); + // not need to check validity of map object; MapId _MUST_ be valid here + + if(graveLow==graveUp && !map->IsBattleArena()) { sLog.outErrorDb("Table `game_graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.",zoneId,team); return NULL; @@ -4709,8 +5403,10 @@ WorldSafeLocsEntry const *ObjectMgr::GetClosestGraveYard(float x, float y, float if(MapId != entry->map_id) { // if find graveyard at different map from where entrance placed (or no entrance data), use any first - if (!mapEntry || mapEntry->entrance_map < 0 || mapEntry->entrance_map != entry->map_id || - mapEntry->entrance_x == 0 && mapEntry->entrance_y == 0) + if (!mapEntry || + mapEntry->entrance_map < 0 || + mapEntry->entrance_map != entry->map_id || + (mapEntry->entrance_x == 0 && mapEntry->entrance_y == 0)) { // not have any corrdinates for check distance anyway entryFar = entry; @@ -4855,7 +5551,7 @@ void ObjectMgr::LoadAreaTriggerTeleports() uint32 count = 0; - // 0 1 2 3 4 5 6 + // 0 1 2 3 4 5 6 QueryResult *result = WorldDatabase.Query("SELECT id, access_id, target_map, target_position_x, target_position_y, target_position_z, target_orientation FROM areatrigger_teleport"); if( !result ) { @@ -4896,7 +5592,7 @@ void ObjectMgr::LoadAreaTriggerTeleports() sLog.outErrorDb("Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.",Trigger_ID); continue; } - + MapEntry const* mapEntry = sMapStore.LookupEntry(at.target_mapId); if(!mapEntry) { @@ -4926,8 +5622,8 @@ void ObjectMgr::LoadAccessRequirements() uint32 count = 0; - // 0 1 2 3 4 5 6 7 8 9 10 - QueryResult *result = WorldDatabase.Query("SELECT id, level_min, level_max, item, item2, heroic_key, heroic_key2, quest_done, quest_failed_text, heroic_quest_done, heroic_quest_failed_text FROM access_requirement"); + // 0 1 2 3 4 5 6 7 8 9 10 11 + QueryResult *result = WorldDatabase.Query("SELECT id, level_min, level_max, item, item2, heroic_key, heroic_key2, quest_done, quest_failed_text, heroic_quest_done, heroic_quest_failed_text, heroic_level_min FROM access_requirement"); if( !result ) { @@ -4955,7 +5651,8 @@ void ObjectMgr::LoadAccessRequirements() AccessRequirement ar; ar.levelMin = fields[1].GetUInt8(); - ar.levelMax = fields[2].GetUInt32(); + ar.levelMax = fields[2].GetUInt8(); + ar.heroicLevelMin = fields[11].GetUInt8(); ar.item = fields[3].GetUInt32(); ar.item2 = fields[4].GetUInt32(); ar.heroicKey = fields[5].GetUInt32(); @@ -5007,7 +5704,8 @@ void ObjectMgr::LoadAccessRequirements() if(ar.heroicQuest) { - if(!GetQuestTemplate(ar.heroicQuest)) + QuestMap::iterator qReqItr = mQuestTemplates.find(ar.heroicQuest); + if(qReqItr == mQuestTemplates.end()) { sLog.outErrorDb("Required Heroic Quest %u not exist for trigger %u, remove heroic quest done requirement.",ar.heroicQuest,requiremt_ID); ar.heroicQuest = 0; @@ -5016,7 +5714,8 @@ void ObjectMgr::LoadAccessRequirements() if(ar.quest) { - if(!GetQuestTemplate(ar.quest)) + QuestMap::iterator qReqItr = mQuestTemplates.find(ar.quest); + if(qReqItr == mQuestTemplates.end()) { sLog.outErrorDb("Required Quest %u not exist for trigger %u, remove quest done requirement.",ar.quest,requiremt_ID); ar.quest = 0; @@ -5033,6 +5732,9 @@ void ObjectMgr::LoadAccessRequirements() sLog.outString( ">> Loaded %u access requirement definitions", count ); } +/* + * Searches for the areatrigger which teleports players out of the given map + */ AreaTrigger const* ObjectMgr::GetGoBackTrigger(uint32 Map) const { const MapEntry *mapEntry = sMapStore.LookupEntry(Map); @@ -5049,6 +5751,23 @@ AreaTrigger const* ObjectMgr::GetGoBackTrigger(uint32 Map) const return NULL; } +/** + * Searches for the areatrigger which teleports players to the given map + */ +AreaTrigger const* ObjectMgr::GetMapEntranceTrigger(uint32 Map) const +{ + for (AreaTriggerMap::const_iterator itr = mAreaTriggers.begin(); itr != mAreaTriggers.end(); ++itr) + { + if(itr->second.target_mapId == Map) + { + AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(itr->first); + if(atEntry) + return &itr->second; + } + } + return NULL; +} + void ObjectMgr::SetHighestGuids() { QueryResult *result = CharacterDatabase.Query( "SELECT MAX(guid) FROM characters" ); @@ -5065,9 +5784,6 @@ void ObjectMgr::SetHighestGuids() delete result; } - // pet guids are not saved to DB, set to 0 (pet guid != pet id) - m_hiPetGuid = 0; - result = CharacterDatabase.Query( "SELECT MAX(guid) FROM item_instance" ); if( result ) { @@ -5123,6 +5839,13 @@ void ObjectMgr::SetHighestGuids() delete result; } + result = CharacterDatabase.Query("SELECT MAX(setguid) FROM character_equipmentsets"); + if (result) + { + m_equipmentSetGuid = (*result)[0].GetUInt64()+1; + delete result; + } + result = CharacterDatabase.Query( "SELECT MAX(guildid) FROM guild" ); if (result) { @@ -5141,24 +5864,34 @@ uint32 ObjectMgr::GenerateArenaTeamId() return m_arenaTeamId++; } -uint32 ObjectMgr::GenerateGuildId() +uint32 ObjectMgr::GenerateAuctionID() { - if(m_guildId>=0xFFFFFFFE) + if(m_auctionid>=0xFFFFFFFE) { - sLog.outError("Guild ids overflow!! Can't continue, shutting down server. "); + sLog.outError("Auctions ids overflow!! Can't continue, shutting down server. "); World::StopNow(ERROR_EXIT_CODE); } - return m_guildId++; + return m_auctionid++; } -uint32 ObjectMgr::GenerateAuctionID() +uint64 ObjectMgr::GenerateEquipmentSetGuid() { - if(m_auctionid>=0xFFFFFFFE) + if(m_equipmentSetGuid>=0xFFFFFFFFFFFFFFFEll) { - sLog.outError("Auctions ids overflow!! Can't continue, shutting down server. "); + sLog.outError("EquipmentSet guid overflow!! Can't continue, shutting down server. "); World::StopNow(ERROR_EXIT_CODE); } - return m_auctionid++; + return m_equipmentSetGuid++; +} + +uint32 ObjectMgr::GenerateGuildId() +{ + if(m_guildId>=0xFFFFFFFE) + { + sLog.outError("Guild ids overflow!! Can't continue, shutting down server. "); + World::StopNow(ERROR_EXIT_CODE); + } + return m_guildId++; } uint32 ObjectMgr::GenerateMailID() @@ -5220,6 +5953,13 @@ uint32 ObjectMgr::GenerateLowGuid(HighGuid guidhigh) World::StopNow(ERROR_EXIT_CODE); } return m_hiPetGuid++; + case HIGHGUID_VEHICLE: + if(m_hiVehicleGuid>=0x00FFFFFF) + { + sLog.outError("Vehicle guid overflow!! Can't continue, shutting down server. "); + World::StopNow(ERROR_EXIT_CODE); + } + return m_hiVehicleGuid++; case HIGHGUID_PLAYER: if(m_hiCharGuid>=0xFFFFFFFE) { @@ -5271,7 +6011,7 @@ void ObjectMgr::LoadGameObjectLocales() bar.step(); - sLog.outString(""); + sLog.outString(); sLog.outString(">> Loaded 0 gameobject locale strings. DB table `locales_gameobject` is empty."); return; } @@ -5287,7 +6027,7 @@ void ObjectMgr::LoadGameObjectLocales() GameObjectLocale& data = mGameObjectLocaleMap[entry]; - for(int i = 1; i < MAX_LOCALE; ++i) + for(uint8 i = 1; i < MAX_LOCALE; ++i) { std::string str = fields[i].GetCppString(); if(!str.empty()) @@ -5303,9 +6043,9 @@ void ObjectMgr::LoadGameObjectLocales() } } - for(int i = MAX_LOCALE; i < MAX_LOCALE*2-1; ++i) + for(uint8 i = 1; i < MAX_LOCALE; ++i) { - std::string str = fields[i].GetCppString(); + std::string str = fields[i+(MAX_LOCALE-1)].GetCppString(); if(!str.empty()) { int idx = GetOrNewIndexForLocale(LocaleConstant(i)); @@ -5324,18 +6064,73 @@ void ObjectMgr::LoadGameObjectLocales() delete result; sLog.outString(); - sLog.outString( ">> Loaded %u gameobject locale strings", mGameObjectLocaleMap.size() ); + sLog.outString( ">> Loaded %lu gameobject locale strings", (unsigned long)mGameObjectLocaleMap.size() ); } struct SQLGameObjectLoader : public SQLStorageLoaderBase<SQLGameObjectLoader> { template<class D> - void convert_from_str(uint32 field_pos, char *src, D &dst) + void convert_from_str(uint32 /*field_pos*/, char *src, D &dst) { dst = D(objmgr.GetScriptId(src)); } }; +inline void CheckGOLockId(GameObjectInfo const* goInfo,uint32 dataN,uint32 N) +{ + if (sLockStore.LookupEntry(dataN)) + return; + + sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but lock (Id: %u) not found.", + goInfo->id,goInfo->type,N,goInfo->door.lockId,goInfo->door.lockId); +} + +inline void CheckGOLinkedTrapId(GameObjectInfo const* goInfo,uint32 dataN,uint32 N) +{ + if (GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(dataN)) + { + if (trapInfo->type!=GAMEOBJECT_TYPE_TRAP) + sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but GO (Entry %u) have not GAMEOBJECT_TYPE_TRAP (%u) type.", + goInfo->id,goInfo->type,N,dataN,dataN,GAMEOBJECT_TYPE_TRAP); + } + /* disable check for while (too many error reports baout not existed in trap templates + else + sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but trap GO (Entry %u) not exist in `gameobject_template`.", + goInfo->id,goInfo->type,N,dataN,dataN); + */ +} + +inline void CheckGOSpellId(GameObjectInfo const* goInfo,uint32 dataN,uint32 N) +{ + if (sSpellStore.LookupEntry(dataN)) + return; + + sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but Spell (Entry %u) not exist.", + goInfo->id,goInfo->type,N,dataN,dataN); +} + +inline void CheckAndFixGOChairHeightId(GameObjectInfo const* goInfo,uint32 const& dataN,uint32 N) +{ + if (dataN <= (UNIT_STAND_STATE_SIT_HIGH_CHAIR-UNIT_STAND_STATE_SIT_LOW_CHAIR) ) + return; + + sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but correct chair height in range 0..%i.", + goInfo->id,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; +} + +inline void CheckGONoDamageImmuneId(GameObjectInfo const* goInfo,uint32 dataN,uint32 N) +{ + // 0/1 correct values + if (dataN <= 1) + return; + + sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but expected boolean (0/1) noDamageImmune field value.", + goInfo->id,goInfo->type,N,dataN); +} + void ObjectMgr::LoadGameobjectInfo() { SQLGameObjectLoader loader; @@ -5345,139 +6140,106 @@ void ObjectMgr::LoadGameobjectInfo() for(uint32 id = 1; id < sGOStorage.MaxEntry; id++) { GameObjectInfo const* goInfo = sGOStorage.LookupEntry<GameObjectInfo>(id); - if(!goInfo) + if (!goInfo) continue; + // some GO types have unused go template, check goInfo->displayId at GO spawn data loading or ignore + switch(goInfo->type) { case GAMEOBJECT_TYPE_DOOR: //0 { - if(goInfo->door.lockId) - { - if(!sLockStore.LookupEntry(goInfo->door.lockId)) - sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data1=%u but lock (Id: %u) not found.", - id,goInfo->type,goInfo->door.lockId,goInfo->door.lockId); - } + if (goInfo->door.lockId) + CheckGOLockId(goInfo,goInfo->door.lockId,1); + CheckGONoDamageImmuneId(goInfo,goInfo->door.noDamageImmune,3); break; } case GAMEOBJECT_TYPE_BUTTON: //1 { - if(goInfo->button.lockId) - { - if(!sLockStore.LookupEntry(goInfo->button.lockId)) - sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data1=%u but lock (Id: %u) not found.", - id,goInfo->type,goInfo->button.lockId,goInfo->button.lockId); - } + if (goInfo->button.lockId) + CheckGOLockId(goInfo,goInfo->button.lockId,1); + CheckGONoDamageImmuneId(goInfo,goInfo->button.noDamageImmune,4); + break; + } + case GAMEOBJECT_TYPE_QUESTGIVER: //2 + { + if (goInfo->questgiver.lockId) + CheckGOLockId(goInfo,goInfo->questgiver.lockId,0); + CheckGONoDamageImmuneId(goInfo,goInfo->questgiver.noDamageImmune,5); break; } case GAMEOBJECT_TYPE_CHEST: //3 { - if(goInfo->chest.lockId) - { - if(!sLockStore.LookupEntry(goInfo->chest.lockId)) - sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data0=%u but lock (Id: %u) not found.", - id,goInfo->type,goInfo->chest.lockId,goInfo->chest.lockId); - } - if(goInfo->chest.linkedTrapId) // linked trap - { - if(GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(goInfo->chest.linkedTrapId)) - { - if(trapInfo->type!=GAMEOBJECT_TYPE_TRAP) - sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data7=%u but GO (Entry %u) have not GAMEOBJECT_TYPE_TRAP (%u) type.", - id,goInfo->type,goInfo->chest.linkedTrapId,goInfo->chest.linkedTrapId,GAMEOBJECT_TYPE_TRAP); - } - /* disable check for while - else - sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data2=%u but trap GO (Entry %u) not exist in `gameobject_template`.", - id,goInfo->type,goInfo->chest.linkedTrapId,goInfo->chest.linkedTrapId); - */ - } + if (goInfo->chest.lockId) + CheckGOLockId(goInfo,goInfo->chest.lockId,0); + + if (goInfo->chest.linkedTrapId) // linked trap + CheckGOLinkedTrapId(goInfo,goInfo->chest.linkedTrapId,7); break; } case GAMEOBJECT_TYPE_TRAP: //6 { - /* disable check for while - if(goInfo->trap.spellId) // spell - { - if(!sSpellStore.LookupEntry(goInfo->trap.spellId)) - sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data3=%u but Spell (Entry %u) not exist.", - id,goInfo->type,goInfo->trap.spellId,goInfo->trap.spellId); - } + if (goInfo->trap.lockId) + CheckGOLockId(goInfo,goInfo->trap.lockId,0); + /* disable check for while, too many not existed spells + if (goInfo->trap.spellId) // spell + CheckGOSpellId(goInfo,goInfo->trap.spellId,3); */ break; } case GAMEOBJECT_TYPE_CHAIR: //7 - if(goInfo->chair.height > 2) - { - sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data1=%u but correct chair height in range 0..2.", - id,goInfo->type,goInfo->chair.height); - - // prevent client and server unexpected work - const_cast<GameObjectInfo*>(goInfo)->chair.height = 0; - } + CheckAndFixGOChairHeightId(goInfo,goInfo->chair.height,1); break; case GAMEOBJECT_TYPE_SPELL_FOCUS: //8 { - if(goInfo->spellFocus.focusId) + if (goInfo->spellFocus.focusId) { - if(!sSpellFocusObjectStore.LookupEntry(goInfo->spellFocus.focusId)) + if (!sSpellFocusObjectStore.LookupEntry(goInfo->spellFocus.focusId)) sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data0=%u but SpellFocus (Id: %u) not exist.", id,goInfo->type,goInfo->spellFocus.focusId,goInfo->spellFocus.focusId); } - if(goInfo->spellFocus.linkedTrapId) // linked trap - { - if(GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(goInfo->spellFocus.linkedTrapId)) - { - if(trapInfo->type!=GAMEOBJECT_TYPE_TRAP) - sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data2=%u but GO (Entry %u) have not GAMEOBJECT_TYPE_TRAP (%u) type.", - id,goInfo->type,goInfo->spellFocus.linkedTrapId,goInfo->spellFocus.linkedTrapId,GAMEOBJECT_TYPE_TRAP); - } - /* disable check for while - else - sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data2=%u but trap GO (Entry %u) not exist in `gameobject_template`.", - id,goInfo->type,goInfo->spellFocus.linkedTrapId,goInfo->spellFocus.linkedTrapId); - */ - } + if (goInfo->spellFocus.linkedTrapId) // linked trap + CheckGOLinkedTrapId(goInfo,goInfo->spellFocus.linkedTrapId,2); break; } case GAMEOBJECT_TYPE_GOOBER: //10 { - if(goInfo->goober.pageId) // pageId + if (goInfo->goober.lockId) + CheckGOLockId(goInfo,goInfo->goober.lockId,0); + + if (goInfo->goober.pageId) // pageId { - if(!sPageTextStore.LookupEntry<PageText>(goInfo->goober.pageId)) + if (!sPageTextStore.LookupEntry<PageText>(goInfo->goober.pageId)) sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data7=%u but PageText (Entry %u) not exist.", id,goInfo->type,goInfo->goober.pageId,goInfo->goober.pageId); } - /* disable check for while - if(goInfo->goober.spellId) // spell - { - if(!sSpellStore.LookupEntry(goInfo->goober.spellId)) - sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data2=%u but Spell (Entry %u) not exist.", - id,goInfo->type,goInfo->goober.spellId,goInfo->goober.spellId); - } + /* disable check for while, too many not existed spells + if (goInfo->goober.spellId) // spell + CheckGOSpellId(goInfo,goInfo->goober.spellId,10); */ - if(goInfo->goober.linkedTrapId) // linked trap - { - if(GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(goInfo->goober.linkedTrapId)) - { - if(trapInfo->type!=GAMEOBJECT_TYPE_TRAP) - sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data12=%u but GO (Entry %u) have not GAMEOBJECT_TYPE_TRAP (%u) type.", - id,goInfo->type,goInfo->goober.linkedTrapId,goInfo->goober.linkedTrapId,GAMEOBJECT_TYPE_TRAP); - } - /* disable check for while - else - sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data12=%u but trap GO (Entry %u) not exist in `gameobject_template`.", - id,goInfo->type,goInfo->goober.linkedTrapId,goInfo->goober.linkedTrapId); - */ - } + CheckGONoDamageImmuneId(goInfo,goInfo->goober.noDamageImmune,11); + if (goInfo->goober.linkedTrapId) // linked trap + CheckGOLinkedTrapId(goInfo,goInfo->goober.linkedTrapId,12); + break; + } + case GAMEOBJECT_TYPE_AREADAMAGE: //12 + { + if (goInfo->areadamage.lockId) + CheckGOLockId(goInfo,goInfo->areadamage.lockId,0); + break; + } + case GAMEOBJECT_TYPE_CAMERA: //13 + { + if (goInfo->camera.lockId) + CheckGOLockId(goInfo,goInfo->camera.lockId,0); break; } case GAMEOBJECT_TYPE_MO_TRANSPORT: //15 { - if(goInfo->moTransport.taxiPathId) + if (goInfo->moTransport.taxiPathId) { - if(goInfo->moTransport.taxiPathId >= sTaxiPathNodesByPath.size() || sTaxiPathNodesByPath[goInfo->moTransport.taxiPathId].empty()) + if (goInfo->moTransport.taxiPathId >= sTaxiPathNodesByPath.size() || sTaxiPathNodesByPath[goInfo->moTransport.taxiPathId].empty()) sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data0=%u but TaxiPath (Id: %u) not exist.", id,goInfo->type,goInfo->moTransport.taxiPathId,goInfo->moTransport.taxiPathId); } @@ -5485,26 +6247,41 @@ void ObjectMgr::LoadGameobjectInfo() } case GAMEOBJECT_TYPE_SUMMONING_RITUAL: //18 { - /* disabled - if(goInfo->summoningRitual.spellId) - { - if(!sSpellStore.LookupEntry(goInfo->summoningRitual.spellId)) - sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data1=%u but Spell (Entry %u) not exist.", - id,goInfo->type,goInfo->summoningRitual.spellId,goInfo->summoningRitual.spellId); - } + /* disable check for while, too many not existed spells + // always must have spell + CheckGOSpellId(goInfo,goInfo->summoningRitual.spellId,1); */ break; } case GAMEOBJECT_TYPE_SPELLCASTER: //22 { - if(goInfo->spellcaster.spellId) // spell - { - if(!sSpellStore.LookupEntry(goInfo->spellcaster.spellId)) - sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data3=%u but Spell (Entry %u) not exist.", - id,goInfo->type,goInfo->spellcaster.spellId,goInfo->spellcaster.spellId); - } + // always must have spell + CheckGOSpellId(goInfo,goInfo->spellcaster.spellId,0); + break; + } + case GAMEOBJECT_TYPE_FLAGSTAND: //24 + { + if (goInfo->flagstand.lockId) + CheckGOLockId(goInfo,goInfo->flagstand.lockId,0); + CheckGONoDamageImmuneId(goInfo,goInfo->flagstand.noDamageImmune,5); break; } + case GAMEOBJECT_TYPE_FISHINGHOLE: //25 + { + if (goInfo->fishinghole.lockId) + CheckGOLockId(goInfo,goInfo->fishinghole.lockId,4); + break; + } + case GAMEOBJECT_TYPE_FLAGDROP: //26 + { + if (goInfo->flagdrop.lockId) + CheckGOLockId(goInfo,goInfo->flagdrop.lockId,0); + CheckGONoDamageImmuneId(goInfo,goInfo->flagdrop.noDamageImmune,3); + break; + } + case GAMEOBJECT_TYPE_BARBER_CHAIR: //32 + CheckAndFixGOChairHeightId(goInfo,goInfo->barberChair.chairheight,0); + break; } } @@ -5553,6 +6330,13 @@ uint32 ObjectMgr::GetBaseXP(uint32 level) return mBaseXPTable[level] ? mBaseXPTable[level] : 0; } +uint32 ObjectMgr::GetXPForLevel(uint32 level) +{ + if (level < mPlayerXPperLevel.size()) + return mPlayerXPperLevel[level]; + return 0; +} + void ObjectMgr::LoadPetNames() { uint32 count = 0; @@ -5754,6 +6538,151 @@ void ObjectMgr::LoadReputationOnKill() sLog.outString(">> Loaded %u creature award reputation definitions", count); } +void ObjectMgr::LoadPointsOfInterest() +{ + uint32 count = 0; + + // 0 1 2 3 4 5 + QueryResult *result = WorldDatabase.Query("SELECT entry, x, y, icon, flags, data, icon_name FROM points_of_interest"); + + if(!result) + { + barGoLink bar(1); + + bar.step(); + + sLog.outString(); + sLog.outErrorDb(">> Loaded 0 Points of Interest definitions. DB table `points_of_interest` is empty."); + return; + } + + barGoLink bar(result->GetRowCount()); + + do + { + Field *fields = result->Fetch(); + bar.step(); + + uint32 point_id = fields[0].GetUInt32(); + + PointOfInterest POI; + POI.x = fields[1].GetFloat(); + POI.y = fields[2].GetFloat(); + POI.icon = fields[3].GetUInt32(); + POI.flags = fields[4].GetUInt32(); + POI.data = fields[5].GetUInt32(); + POI.icon_name = fields[6].GetCppString(); + + if(!MaNGOS::IsValidMapCoord(POI.x,POI.y)) + { + sLog.outErrorDb("Table `points_of_interest` (Entry: %u) have invalid coordinates (X: %f Y: %f), ignored.",point_id,POI.x,POI.y); + continue; + } + + mPointsOfInterest[point_id] = POI; + + ++count; + } while (result->NextRow()); + + delete result; + + sLog.outString(); + sLog.outString(">> Loaded %u Points of Interest definitions", count); +} + +void ObjectMgr::LoadNPCSpellClickSpells() +{ + uint32 count = 0; + + mSpellClickInfoMap.clear(); + // 0 1 2 3 4 5 + QueryResult *result = WorldDatabase.Query("SELECT npc_entry, spell_id, quest_start, quest_start_active, quest_end, cast_flags FROM npc_spellclick_spells"); + + if(!result) + { + barGoLink bar(1); + + bar.step(); + + sLog.outString(); + sLog.outErrorDb(">> Loaded 0 spellclick spells. DB table `npc_spellclick_spells` is empty."); + return; + } + + barGoLink bar(result->GetRowCount()); + + do + { + Field *fields = result->Fetch(); + bar.step(); + + uint32 npc_entry = fields[0].GetUInt32(); + CreatureInfo const* cInfo = GetCreatureTemplate(npc_entry); + if (!cInfo) + { + sLog.outErrorDb("Table npc_spellclick_spells references unknown creature_template %u. Skipping entry.", npc_entry); + continue; + } + + if(!(cInfo->npcflag & UNIT_NPC_FLAG_SPELLCLICK)) + const_cast<CreatureInfo*>(cInfo)->npcflag |= UNIT_NPC_FLAG_SPELLCLICK; + + uint32 spellid = fields[1].GetUInt32(); + SpellEntry const *spellinfo = sSpellStore.LookupEntry(spellid); + if (!spellinfo) + { + sLog.outErrorDb("Table npc_spellclick_spells references unknown spellid %u. Skipping entry.", spellid); + continue; + } + + uint32 quest_start = fields[2].GetUInt32(); + + // quest might be 0 to enable spellclick independent of any quest + if (quest_start) + { + if(mQuestTemplates.find(quest_start) == mQuestTemplates.end()) + { + sLog.outErrorDb("Table npc_spellclick_spells references unknown start quest %u. Skipping entry.", quest_start); + continue; + } + + } + + bool quest_start_active = fields[3].GetBool(); + + uint32 quest_end = fields[4].GetUInt32(); + // quest might be 0 to enable spellclick active infinity after start quest + if (quest_end) + { + if(mQuestTemplates.find(quest_end) == mQuestTemplates.end()) + { + sLog.outErrorDb("Table npc_spellclick_spells references unknown end quest %u. Skipping entry.", quest_end); + continue; + } + + } + + uint8 castFlags = fields[5].GetUInt8(); + SpellClickInfo info; + info.spellId = spellid; + info.questStart = quest_start; + info.questStartCanActive = quest_start_active; + info.questEnd = quest_end; + info.castFlags = castFlags; + mSpellClickInfoMap.insert(SpellClickInfoMap::value_type(npc_entry, info)); + + // mark creature template as spell clickable + const_cast<CreatureInfo*>(cInfo)->npcflag |= UNIT_NPC_FLAG_SPELLCLICK; + + ++count; + } while (result->NextRow()); + + delete result; + + sLog.outString(); + sLog.outString(">> Loaded %u spellclick definitions", count); +} + void ObjectMgr::LoadWeatherZoneChances() { uint32 count = 0; @@ -5783,7 +6712,7 @@ void ObjectMgr::LoadWeatherZoneChances() WeatherZoneChances& wzc = mWeatherZoneMap[zone_id]; - for(int season = 0; season < WEATHER_SEASONS; ++season) + for(uint8 season = 0; season < WEATHER_SEASONS; ++season) { wzc.data[season].rainChance = fields[season * (MAX_WEATHER_TYPE-1) + 1].GetUInt32(); wzc.data[season].snowChance = fields[season * (MAX_WEATHER_TYPE-1) + 2].GetUInt32(); @@ -5792,19 +6721,19 @@ void ObjectMgr::LoadWeatherZoneChances() if(wzc.data[season].rainChance > 100) { wzc.data[season].rainChance = 25; - sLog.outErrorDb("Weather for zone %u season %u has wrong rain chance > 100%",zone_id,season); + sLog.outErrorDb("Weather for zone %u season %u has wrong rain chance > 100%%",zone_id,season); } if(wzc.data[season].snowChance > 100) { wzc.data[season].snowChance = 25; - sLog.outErrorDb("Weather for zone %u season %u has wrong snow chance > 100%",zone_id,season); + sLog.outErrorDb("Weather for zone %u season %u has wrong snow chance > 100%%",zone_id,season); } if(wzc.data[season].stormChance > 100) { wzc.data[season].stormChance = 25; - sLog.outErrorDb("Weather for zone %u season %u has wrong storm chance > 100%",zone_id,season); + sLog.outErrorDb("Weather for zone %u season %u has wrong storm chance > 100%%",zone_id,season); } } @@ -5822,7 +6751,7 @@ void ObjectMgr::SaveCreatureRespawnTime(uint32 loguid, uint32 instance, time_t t mCreatureRespawnTimes[MAKE_PAIR64(loguid,instance)] = t; WorldDatabase.PExecute("DELETE FROM creature_respawn WHERE guid = '%u' AND instance = '%u'", loguid, instance); if(t) - WorldDatabase.PExecute("INSERT INTO creature_respawn VALUES ( '%u', '" I64FMTD "', '%u' )", loguid, uint64(t), instance); + WorldDatabase.PExecute("INSERT INTO creature_respawn VALUES ( '%u', '" UI64FMTD "', '%u' )", loguid, uint64(t), instance); } void ObjectMgr::DeleteCreatureData(uint32 guid) @@ -5840,7 +6769,7 @@ void ObjectMgr::SaveGORespawnTime(uint32 loguid, uint32 instance, time_t t) mGORespawnTimes[MAKE_PAIR64(loguid,instance)] = t; WorldDatabase.PExecute("DELETE FROM gameobject_respawn WHERE guid = '%u' AND instance = '%u'", loguid, instance); if(t) - WorldDatabase.PExecute("INSERT INTO gameobject_respawn VALUES ( '%u', '" I64FMTD "', '%u' )", loguid, uint64(t), instance); + WorldDatabase.PExecute("INSERT INTO gameobject_respawn VALUES ( '%u', '" UI64FMTD "', '%u' )", loguid, uint64(t), instance); } void ObjectMgr::DeleteRespawnTimeForInstance(uint32 instance) @@ -6021,11 +6950,18 @@ void ObjectMgr::LoadReservedPlayersNames() bar.step(); fields = result->Fetch(); std::string name= fields[0].GetCppString(); - if(normalizePlayerName(name)) + + std::wstring wstr; + if(!Utf8toWStr (name,wstr)) { - m_ReservedNames.insert(name); - ++count; + sLog.outError("Table `reserved_name` have invalid name: %s", name.c_str() ); + continue; } + + wstrToLower(wstr); + + m_ReservedNames.insert(wstr); + ++count; } while ( result->NextRow() ); delete result; @@ -6034,6 +6970,17 @@ void ObjectMgr::LoadReservedPlayersNames() sLog.outString( ">> Loaded %u reserved player names", count ); } +bool ObjectMgr::IsReservedName( const std::string& name ) const +{ + std::wstring wstr; + if(!Utf8toWStr (name,wstr)) + return false; + + wstrToLower(wstr); + + return m_ReservedNames.find(wstr) != m_ReservedNames.end(); +} + enum LanguageType { LT_BASIC_LATIN = 0x0000, @@ -6182,55 +7129,26 @@ int ObjectMgr::GetOrNewIndexForLocale( LocaleConstant loc ) return m_LocalForIndex.size()-1; } -void ObjectMgr::LoadBattleMastersEntry() +void ObjectMgr::LoadGameObjectForQuests() { - mBattleMastersMap.clear(); // need for reload case - - QueryResult *result = WorldDatabase.Query( "SELECT entry,bg_template FROM battlemaster_entry" ); - - uint32 count = 0; + mGameObjectForQuestSet.clear(); // need for reload case - if( !result ) + if( !sGOStorage.MaxEntry ) { barGoLink bar( 1 ); bar.step(); - sLog.outString(); - sLog.outString( ">> Loaded 0 battlemaster entries - table is empty!" ); + sLog.outString( ">> Loaded 0 GameObjects for quests" ); return; } - barGoLink bar( result->GetRowCount() ); - - do - { - ++count; - bar.step(); - - Field *fields = result->Fetch(); - - uint32 entry = fields[0].GetUInt32(); - uint32 bgTypeId = fields[1].GetUInt32(); - - mBattleMastersMap[entry] = bgTypeId; - - } while( result->NextRow() ); - - delete result; - - sLog.outString(); - sLog.outString( ">> Loaded %u battlemaster entries", count ); -} - -void ObjectMgr::LoadGameObjectForQuests() -{ - mGameObjectForQuestSet.clear(); // need for reload case - + barGoLink bar( sGOStorage.MaxEntry - 1 ); uint32 count = 0; // collect GO entries for GO that must activated for(uint32 go_entry = 1; go_entry < sGOStorage.MaxEntry; ++go_entry) { + bar.step(); GameObjectInfo const* goInfo = sGOStorage.LookupEntry<GameObjectInfo>(go_entry); if(!goInfo) continue; @@ -6265,34 +7183,55 @@ void ObjectMgr::LoadGameObjectForQuests() } sLog.outString(); - sLog.outString( ">> Loaded %u GameObject for quests", count ); + sLog.outString( ">> Loaded %u GameObjects for quests", count ); } bool ObjectMgr::LoadTrinityStrings(DatabaseType& db, char const* table, int32 min_value, int32 max_value) { - // cleanup affected map part for reloading case - for(TrinityStringLocaleMap::iterator itr = mTrinityStringLocaleMap.begin(); itr != mTrinityStringLocaleMap.end();) + int32 start_value = min_value; + int32 end_value = max_value; + // some string can have negative indexes range + if (start_value < 0) { - if(itr->first >= min_value && itr->first <= max_value) + if (end_value >= start_value) { - TrinityStringLocaleMap::iterator itr2 = itr; - ++itr; - mTrinityStringLocaleMap.erase(itr2); + sLog.outErrorDb("Table '%s' attempt loaded with invalid range (%d - %d), strings not loaded.",table,min_value,max_value); + return false; } + + // real range (max+1,min+1) exaple: (-10,-1000) -> -999...-10+1 + std::swap(start_value,end_value); + ++start_value; + ++end_value; + } + else + { + if (start_value >= end_value) + { + sLog.outErrorDb("Table '%s' attempt loaded with invalid range (%d - %d), strings not loaded.",table,min_value,max_value); + return false; + } + } + + // cleanup affected map part for reloading case + for(TrinityStringLocaleMap::iterator itr = mTrinityStringLocaleMap.begin(); itr != mTrinityStringLocaleMap.end();) + { + if (itr->first >= start_value && itr->first < end_value) + mTrinityStringLocaleMap.erase(itr++); else ++itr; } QueryResult *result = db.PQuery("SELECT entry,content_default,content_loc1,content_loc2,content_loc3,content_loc4,content_loc5,content_loc6,content_loc7,content_loc8 FROM %s",table); - if(!result) + if (!result) { barGoLink bar(1); bar.step(); - sLog.outString(""); - if(min_value == MIN_TRINITY_STRING_ID) // error only in case internal strings + sLog.outString(); + if (min_value == MIN_TRINITY_STRING_ID) // error only in case internal strings sLog.outErrorDb(">> Loaded 0 trinity strings. DB table `%s` is empty. Cannot continue.",table); else sLog.outString(">> Loaded 0 string templates. DB table `%s` is empty.",table); @@ -6310,22 +7249,20 @@ bool ObjectMgr::LoadTrinityStrings(DatabaseType& db, char const* table, int32 mi int32 entry = fields[0].GetInt32(); - if(entry==0) + if (entry==0) { sLog.outErrorDb("Table `%s` contain reserved entry 0, ignored.",table); continue; } - else if(entry < min_value || entry > max_value) + else if (entry < start_value || entry >= end_value) { - int32 start = min_value > 0 ? min_value : max_value; - int32 end = min_value > 0 ? max_value : min_value; - sLog.outErrorDb("Table `%s` contain entry %i out of allowed range (%d - %d), ignored.",table,entry,start,end); + sLog.outErrorDb("Table `%s` contain entry %i out of allowed range (%d - %d), ignored.",table,entry,min_value,max_value); continue; } TrinityStringLocale& data = mTrinityStringLocaleMap[entry]; - if(data.Content.size() > 0) + if (data.Content.size() > 0) { sLog.outErrorDb("Table `%s` contain data for already loaded entry %i (from another table?), ignored.",table,entry); continue; @@ -6337,16 +7274,16 @@ bool ObjectMgr::LoadTrinityStrings(DatabaseType& db, char const* table, int32 mi // 0 -> default, idx in to idx+1 data.Content[0] = fields[1].GetCppString(); - for(int i = 1; i < MAX_LOCALE; ++i) + for(uint8 i = 1; i < MAX_LOCALE; ++i) { std::string str = fields[i+1].GetCppString(); - if(!str.empty()) + if (!str.empty()) { int idx = GetOrNewIndexForLocale(LocaleConstant(i)); - if(idx >= 0) + if (idx >= 0) { // 0 -> default, idx in to idx+1 - if(data.Content.size() <= idx+1) + if (data.Content.size() <= idx+1) data.Content.resize(idx+2); data.Content[idx+1] = str; @@ -6358,7 +7295,7 @@ bool ObjectMgr::LoadTrinityStrings(DatabaseType& db, char const* table, int32 mi delete result; sLog.outString(); - if(min_value == MIN_TRINITY_STRING_ID) // internal Trinity strings + if (min_value == MIN_TRINITY_STRING_ID) sLog.outString( ">> Loaded %u Trinity strings from table %s", count,table); else sLog.outString( ">> Loaded %u string templates from %s", count,table); @@ -6503,7 +7440,7 @@ uint16 ObjectMgr::GetConditionId( ConditionType condition, uint32 value1, uint32 bool ObjectMgr::CheckDeclinedNames( std::wstring mainpart, DeclinedName const& names ) { - for(int i =0; i < MAX_DECLINED_NAME_CASES; ++i) + for(uint8 i =0; i < MAX_DECLINED_NAME_CASES; ++i) { std::wstring wname; if(!Utf8toWStr(names.name[i],wname)) @@ -6534,17 +7471,17 @@ bool PlayerCondition::Meets(Player const * player) const case CONDITION_NONE: return true; // empty condition, always met case CONDITION_AURA: - return player->HasAura(value1, value2); + return player->HasAuraEffect(value1, value2); case CONDITION_ITEM: return player->HasItemCount(value1, value2); case CONDITION_ITEM_EQUIPPED: - return player->GetItemOrItemWithGemEquipped(value1) != NULL; + return player->HasItemOrGemWithIdEquipped(value1,1); case CONDITION_ZONEID: return player->GetZoneId() == value1; case CONDITION_REPUTATION_RANK: { FactionEntry const* faction = sFactionStore.LookupEntry(value1); - return faction && player->GetReputationRank(faction) >= value2; + return faction && player->GetReputationMgr().GetRank(faction) >= value2; } case CONDITION_TEAM: return player->GetTeam() == value1; @@ -6561,12 +7498,12 @@ bool PlayerCondition::Meets(Player const * player) const { Unit::AuraMap const& auras = player->GetAuras(); for(Unit::AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) - if((itr->second->GetSpellProto()->Attributes & 0x1000010) && itr->second->GetSpellProto()->SpellVisual==3580) + if((itr->second->GetSpellProto()->Attributes & 0x1000010) && itr->second->GetSpellProto()->SpellVisual[0]==3580) return true; return false; } case CONDITION_NO_AURA: - return !player->HasAura(value1, value2); + return !player->HasAuraEffect(value1, value2); case CONDITION_ACTIVE_EVENT: return gameeventmgr.IsActiveEvent(value1); case CONDITION_INSTANCE_DATA: @@ -6711,7 +7648,7 @@ bool PlayerCondition::IsValid(ConditionType condition, uint32 value1, uint32 val } case CONDITION_ACTIVE_EVENT: { - GameEvent::GameEventDataMap const& events = gameeventmgr.GetEventMap(); + GameEventMgr::GameEventDataMap const& events = gameeventmgr.GetEventMap(); if(value1 >=events.size() || !events[value1].isValid()) { sLog.outErrorDb("Active event condition requires existed event id (%u), skipped", value1); @@ -6722,6 +7659,8 @@ bool PlayerCondition::IsValid(ConditionType condition, uint32 value1, uint32 val case CONDITION_INSTANCE_DATA: //TODO: need some check break; + case CONDITION_NONE: + break; } return true; } @@ -6738,7 +7677,7 @@ SkillRangeType GetSkillRangeType(SkillLineEntry const *pSkill, bool racial) return SKILL_RANGE_MONO; case SKILL_CATEGORY_ARMOR: case SKILL_CATEGORY_CLASS: - if(pSkill->id != SKILL_POISONS && pSkill->id != SKILL_LOCKPICKING) + if(pSkill->id != SKILL_LOCKPICKING) return SKILL_RANGE_MONO; else return SKILL_RANGE_LEVEL; @@ -6753,7 +7692,7 @@ SkillRangeType GetSkillRangeType(SkillLineEntry const *pSkill, bool racial) return SKILL_RANGE_MONO; default: case SKILL_CATEGORY_ATTRIBUTES: //not found in dbc - case SKILL_CATEGORY_NOT_DISPLAYED: //only GENEREC(DND) + case SKILL_CATEGORY_GENERIC: //only GENERIC(DND) return SKILL_RANGE_NONE; } } @@ -6814,11 +7753,10 @@ void ObjectMgr::LoadGameTele() ++count; } while (result->NextRow()); - delete result; sLog.outString(); - sLog.outString( ">> Loaded %u game tele's", count ); + sLog.outString( ">> Loaded %u GameTeleports", count ); } GameTele const* ObjectMgr::GetGameTele(const std::string& name) const @@ -6913,6 +7851,8 @@ void ObjectMgr::LoadTrainerSpell() barGoLink bar( result->GetRowCount() ); + std::set<uint32> talentIds; + uint32 count = 0; do { @@ -6954,30 +7894,51 @@ void ObjectMgr::LoadTrainerSpell() continue; } - TrainerSpell* pTrainerSpell = new TrainerSpell(); - pTrainerSpell->spell = spell; - pTrainerSpell->spellcost = fields[2].GetUInt32(); - pTrainerSpell->reqskill = fields[3].GetUInt32(); - pTrainerSpell->reqskillvalue = fields[4].GetUInt32(); - pTrainerSpell->reqlevel = fields[5].GetUInt32(); + if(GetTalentSpellCost(spell)) + { + if(talentIds.count(spell)==0) + { + sLog.outErrorDb("Table `npc_trainer` has talent as learning spell %u, ignore", spell); + talentIds.insert(spell); + } + continue; + } - if(!pTrainerSpell->reqlevel) - pTrainerSpell->reqlevel = spellinfo->spellLevel; + TrainerSpellData& data = m_mCacheTrainerSpellMap[entry]; + TrainerSpell& trainerSpell = data.spellList[spell]; + trainerSpell.spell = spell; + trainerSpell.spellCost = fields[2].GetUInt32(); + trainerSpell.reqSkill = fields[3].GetUInt32(); + trainerSpell.reqSkillValue = fields[4].GetUInt32(); + trainerSpell.reqLevel = fields[5].GetUInt32(); - TrainerSpellData& data = m_mCacheTrainerSpellMap[entry]; + if(!trainerSpell.reqLevel) + trainerSpell.reqLevel = spellinfo->spellLevel; - if(SpellMgr::IsProfessionSpell(spell)) + // calculate learned spell for profession case when stored cast-spell + trainerSpell.learnedSpell = spell; + for(uint8 i = 0; i <3; ++i) + { + if(spellinfo->Effect[i] != SPELL_EFFECT_LEARN_SPELL) + continue; + if(SpellMgr::IsProfessionOrRidingSpell(spellinfo->EffectTriggerSpell[i])) + { + trainerSpell.learnedSpell = spellinfo->EffectTriggerSpell[i]; + break; + } + } + + if(SpellMgr::IsProfessionSpell(trainerSpell.learnedSpell)) data.trainerType = 2; - data.spellList.push_back(pTrainerSpell); ++count; } while (result->NextRow()); delete result; sLog.outString(); - sLog.outString( ">> Loaded Trainers %d", count ); + sLog.outString( ">> Loaded %d Trainers", count ); } void ObjectMgr::LoadVendors() @@ -7255,16 +8216,30 @@ void ObjectMgr::LoadScriptNames() "SELECT DISTINCT(ScriptName) FROM areatrigger_scripts WHERE ScriptName <> '' " "UNION " "SELECT DISTINCT(script) FROM instance_template WHERE script <> ''"); - if(result) + + if( !result ) { - do - { - m_scriptNames.push_back((*result)[0].GetString()); - } while (result->NextRow()); - delete result; + barGoLink bar( 1 ); + bar.step(); + sLog.outString(); + sLog.outErrorDb(">> Loaded empty set of Script Names!"); + return; } + barGoLink bar( result->GetRowCount() ); + uint32 count = 0; + + do + { + bar.step(); + m_scriptNames.push_back((*result)[0].GetString()); + ++count; + } while (result->NextRow()); + delete result; + std::sort(m_scriptNames.begin(), m_scriptNames.end()); + sLog.outString(); + sLog.outString( ">> Loaded %d Script Names", count ); } uint32 ObjectMgr::GetScriptId(const char *name) @@ -7284,13 +8259,16 @@ void ObjectMgr::CheckScripts(ScriptMapMap const& scripts,std::set<int32>& ids) { for(ScriptMap::const_iterator itrM = itrMM->second.begin(); itrM != itrMM->second.end(); ++itrM) { - if(itrM->second.dataint) + switch(itrM->second.command) { - if(!GetTrinityStringLocale (itrM->second.dataint)) - sLog.outErrorDb( "Table `db_script_string` has not existed string id %u", itrM->first); + case SCRIPT_COMMAND_TALK: + { + if(!GetTrinityStringLocale (itrM->second.dataint)) + sLog.outErrorDb( "Table `db_script_string` not has string id %u used db script (ID: %u)", itrM->second.dataint, itrMM->first); - if(ids.count(itrM->second.dataint)) - ids.erase(itrM->second.dataint); + if(ids.count(itrM->second.dataint)) + ids.erase(itrM->second.dataint); + } } } } @@ -7326,15 +8304,15 @@ uint32 GetAreaTriggerScriptId(uint32 trigger_id) bool LoadTrinityStrings(DatabaseType& db, char const* table,int32 start_value, int32 end_value) { - if(start_value >= 0 || start_value <= end_value) // start/end reversed for negative values + // MAX_DB_SCRIPT_STRING_ID is max allowed negative value for scripts (scrpts can use only more deep negative values + // start/end reversed for negative values + if (start_value > MAX_DB_SCRIPT_STRING_ID || end_value >= start_value) { - sLog.outErrorDb("Table '%s' attempt loaded with invalid range (%d - %d), use (%d - %d) instead.",table,start_value,end_value,-1,std::numeric_limits<int32>::min()); - start_value = -1; - end_value = std::numeric_limits<int32>::min(); + sLog.outErrorDb("Table '%s' attempt loaded with reserved by mangos range (%d - %d), strings not loaded.",table,start_value,end_value+1); + return false; } - // for scripting localized strings allowed use _only_ negative entries - return objmgr.LoadTrinityStrings(db,table,end_value,start_value); + return objmgr.LoadTrinityStrings(db,table,start_value,end_value); } uint32 TRINITY_DLL_SPEC GetScriptId(const char *name) @@ -7357,16 +8335,6 @@ CreatureInfo const *GetCreatureInfo(uint32 id) return objmgr.GetCreatureTemplate(id); } -CreatureInfo const* GetCreatureTemplateStore(uint32 entry) -{ - return sCreatureStorage.LookupEntry<CreatureInfo>(entry); -} - -Quest const* GetQuestTemplateStore(uint32 entry) -{ - return objmgr.GetQuestTemplate(entry); -} - void ObjectMgr::LoadTransportEvents() { @@ -7403,3 +8371,138 @@ void ObjectMgr::LoadTransportEvents() delete result; } +CreatureInfo const* GetCreatureTemplateStore(uint32 entry) +{ + return sCreatureStorage.LookupEntry<CreatureInfo>(entry); +} + +Quest const* GetQuestTemplateStore(uint32 entry) +{ + return objmgr.GetQuestTemplate(entry); +} + +uint64 ObjectMgr::GenerateGMTicketId() +{ + return ++m_GMticketid; +} + +void ObjectMgr::LoadGMTickets() +{ + m_GMTicketList.clear(); + + QueryResult *result = CharacterDatabase.Query( "SELECT `guid`, `playerGuid`, `name`, `message`, `createtime`, `map`, `posX`, `posY`, `posZ`, `timestamp`, `closed`, `assignedto`, `comment` FROM `gm_tickets`" ); + + if(!result) + { + sLog.outString(" \n>> GM Tickets table is empty, no tickets were loaded.\n" ); + return; + } + + uint16 count = 0; + barGoLink bar ((*result).GetRowCount()); + GM_Ticket *ticket; + do + { + Field *fields = result->Fetch(); + ticket = new GM_Ticket; + ticket->guid = fields[0].GetUInt64(); + ticket->playerGuid = fields[1].GetUInt64(); + ticket->name = fields[2].GetCppString(); + ticket->message = fields[3].GetCppString(); + ticket->createtime = fields[4].GetUInt64(); + ticket->map = fields[5].GetUInt32(); + ticket->pos_x = fields[6].GetFloat(); + ticket->pos_y = fields[7].GetFloat(); + ticket->pos_z = fields[8].GetFloat(); + ticket->timestamp = fields[9].GetUInt64(); + ticket->closed = fields[10].GetUInt64(); + ticket->assignedToGM = fields[11].GetUInt64(); + ticket->comment = fields[12].GetCppString(); + ++count; + bar.step(); + + m_GMTicketList.push_back(ticket); + + } while( result->NextRow() ); + + result = CharacterDatabase.PQuery("SELECT MAX(`guid`) from `gm_tickets`"); + m_GMticketid = (*result)[0].GetUInt64(); + + sLog.outString(">>> %u GM Tickets loaded from the database.", count); + delete result; +} + +void ObjectMgr::AddOrUpdateGMTicket(GM_Ticket &ticket, bool create) +{ + if(create) + m_GMTicketList.push_back(&ticket); + + _AddOrUpdateGMTicket(ticket); +} + +void ObjectMgr::_AddOrUpdateGMTicket(GM_Ticket &ticket) +{ + std::string msg(ticket.message), name(ticket.name), comment(ticket.comment); + CharacterDatabase.escape_string(msg); + CharacterDatabase.escape_string(name); + CharacterDatabase.escape_string(comment); + std::ostringstream ss; + ss << "REPLACE INTO `gm_tickets` (`guid`, `playerGuid`, `name`, `message`, `createtime`, `map`, `posX`, `posY`, `posZ`, `timestamp`, `closed`, `assignedto`, `comment`) VALUES('"; + ss << ticket.guid << "', '"; + ss << ticket.playerGuid << "', '"; + ss << name << "', '"; + ss << msg << "', '" ; + ss << ticket.createtime << "', '"; + ss << ticket.map << "', '"; + ss << ticket.pos_x << "', '"; + ss << ticket.pos_y << "', '"; + ss << ticket.pos_z << "', '"; + ss << ticket.timestamp << "', '"; + ss << ticket.closed << "', '"; + ss << ticket.assignedToGM << "', '"; + ss << comment << "');"; + CharacterDatabase.BeginTransaction(); + CharacterDatabase.Execute(ss.str().c_str()); + CharacterDatabase.CommitTransaction(); +} + +void ObjectMgr::RemoveGMTicket(GM_Ticket *ticket, int64 source, bool permanently) +{ + for(GmTicketList::iterator i = m_GMTicketList.begin(); i != m_GMTicketList.end(); ++i) + if((*i)->guid == ticket->guid) + { + if(permanently) + { + CharacterDatabase.PExecute("DELETE FROM `gm_tickets` WHERE `guid` = '%u'", ticket->guid); + i = m_GMTicketList.erase(i); + ticket = NULL; + return; + } + (*i)->closed = source; + _AddOrUpdateGMTicket(*(*i)); + } +} + +void ObjectMgr::RemoveGMTicket(uint64 ticketGuid, int64 source, bool permanently) +{ + GM_Ticket *ticket = GetGMTicket(ticketGuid); + assert( ticket ); + RemoveGMTicket(ticket, source, permanently); +} + +bool ObjectMgr::CheckDB() const +{ + CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(28511); + if(!cInfo || cInfo->spells[4] != 51890) + return false; + + cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(30068); + if(!cInfo || cInfo->faction_A != 21) + return false; + + cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(28768); + if(!cInfo || !cInfo->ScriptID) + return false; + + return true; +} |