diff options
Diffstat (limited to 'src')
151 files changed, 3118 insertions, 2951 deletions
diff --git a/src/genrev/CMakeLists.txt b/src/genrev/CMakeLists.txt index 8ab3494cd70..e72643c6a6b 100644 --- a/src/genrev/CMakeLists.txt +++ b/src/genrev/CMakeLists.txt @@ -10,6 +10,6 @@ # Need to pass old ${CMAKE_BINARY_DIR} as param because its different at build stage add_custom_target(revision.h ALL - COMMAND ${CMAKE_COMMAND} -DNO_GIT=${WITHOUT_GIT} -DGIT_EXEC=${GIT_EXECUTABLE} -DBUILDDIR=${CMAKE_BINARY_DIR} -P ${CMAKE_SOURCE_DIR}/cmake/genrev.cmake - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMAND "${CMAKE_COMMAND}" -DBUILDDIR="${CMAKE_BINARY_DIR}" -P "${CMAKE_SOURCE_DIR}/cmake/genrev.cmake" "${CMAKE_BINARY_DIR}" + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" ) diff --git a/src/server/authserver/CMakeLists.txt b/src/server/authserver/CMakeLists.txt index edd87a5c347..077cd15404b 100644 --- a/src/server/authserver/CMakeLists.txt +++ b/src/server/authserver/CMakeLists.txt @@ -43,6 +43,7 @@ endif() include_directories( ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/dep/cppformat ${CMAKE_SOURCE_DIR}/src/server/shared ${CMAKE_SOURCE_DIR}/src/server/shared/Configuration ${CMAKE_SOURCE_DIR}/src/server/shared/Database @@ -78,6 +79,7 @@ endif() target_link_libraries(authserver shared + format ${MYSQL_LIBRARY} ${OPENSSL_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} diff --git a/src/server/collision/CMakeLists.txt b/src/server/collision/CMakeLists.txt index e3c77736d5e..150c32a15a3 100644 --- a/src/server/collision/CMakeLists.txt +++ b/src/server/collision/CMakeLists.txt @@ -35,6 +35,7 @@ include_directories( ${CMAKE_SOURCE_DIR}/dep/g3dlite/include ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour/Include + ${CMAKE_SOURCE_DIR}/dep/cppformat ${CMAKE_SOURCE_DIR}/src/server/shared ${CMAKE_SOURCE_DIR}/src/server/shared/Configuration ${CMAKE_SOURCE_DIR}/src/server/shared/Debugging diff --git a/src/server/collision/Management/MMapManager.cpp b/src/server/collision/Management/MMapManager.cpp index 170350d4de4..847b7bbd001 100644 --- a/src/server/collision/Management/MMapManager.cpp +++ b/src/server/collision/Management/MMapManager.cpp @@ -73,7 +73,7 @@ namespace MMAP delete [] fileName; - TC_LOG_INFO("maps", "MMAP:loadMapData: Loaded %03i.mmap", mapId); + TC_LOG_DEBUG("maps", "MMAP:loadMapData: Loaded %03i.mmap", mapId); // store inside our map list MMapData* mmap_data = new MMapData(mesh); @@ -156,7 +156,7 @@ namespace MMAP { mmap->mmapLoadedTiles.insert(std::pair<uint32, dtTileRef>(packedGridPos, tileRef)); ++loadedTiles; - TC_LOG_INFO("maps", "MMAP:loadMap: Loaded mmtile %03i[%02i, %02i] into %03i[%02i, %02i]", mapId, x, y, mapId, header->x, header->y); + TC_LOG_DEBUG("maps", "MMAP:loadMap: Loaded mmtile %03i[%02i, %02i] into %03i[%02i, %02i]", mapId, x, y, mapId, header->x, header->y); return true; } else @@ -205,7 +205,7 @@ namespace MMAP { mmap->mmapLoadedTiles.erase(packedGridPos); --loadedTiles; - TC_LOG_INFO("maps", "MMAP:unloadMap: Unloaded mmtile %03i[%02i, %02i] from %03i", mapId, x, y, mapId); + TC_LOG_DEBUG("maps", "MMAP:unloadMap: Unloaded mmtile %03i[%02i, %02i] from %03i", mapId, x, y, mapId); return true; } @@ -232,13 +232,13 @@ namespace MMAP else { --loadedTiles; - TC_LOG_INFO("maps", "MMAP:unloadMap: Unloaded mmtile %03i[%02i, %02i] from %03i", mapId, x, y, mapId); + TC_LOG_DEBUG("maps", "MMAP:unloadMap: Unloaded mmtile %03i[%02i, %02i] from %03i", mapId, x, y, mapId); } } delete mmap; loadedMMaps.erase(mapId); - TC_LOG_INFO("maps", "MMAP:unloadMap: Unloaded %03i.mmap", mapId); + TC_LOG_DEBUG("maps", "MMAP:unloadMap: Unloaded %03i.mmap", mapId); return true; } @@ -264,7 +264,7 @@ namespace MMAP dtFreeNavMeshQuery(query); mmap->navMeshQueries.erase(instanceId); - TC_LOG_INFO("maps", "MMAP:unloadMapInstance: Unloaded mapId %03u instanceId %u", mapId, instanceId); + TC_LOG_DEBUG("maps", "MMAP:unloadMapInstance: Unloaded mapId %03u instanceId %u", mapId, instanceId); return true; } @@ -295,7 +295,7 @@ namespace MMAP return NULL; } - TC_LOG_INFO("maps", "MMAP:GetNavMeshQuery: created dtNavMeshQuery for mapId %03u instanceId %u", mapId, instanceId); + TC_LOG_DEBUG("maps", "MMAP:GetNavMeshQuery: created dtNavMeshQuery for mapId %03u instanceId %u", mapId, instanceId); mmap->navMeshQueries.insert(std::pair<uint32, dtNavMeshQuery*>(instanceId, query)); } diff --git a/src/server/collision/Maps/MapTree.cpp b/src/server/collision/Maps/MapTree.cpp index 72435594ad0..b493ec18f5f 100644 --- a/src/server/collision/Maps/MapTree.cpp +++ b/src/server/collision/Maps/MapTree.cpp @@ -386,13 +386,12 @@ namespace VMAP { if (!iLoadedSpawns.count(referencedVal)) { -#ifdef VMAP_DEBUG if (referencedVal > iNTreeValues) { - TC_LOG_DEBUG("maps", "StaticMapTree::LoadMapTile() : invalid tree element (%u/%u)", referencedVal, iNTreeValues); + VMAP_ERROR_LOG("maps", "StaticMapTree::LoadMapTile() : invalid tree element (%u/%u) referenced in tile %s", referencedVal, iNTreeValues, tilefile.c_str()); continue; } -#endif + iTreeValues[referencedVal] = ModelInstance(spawn, model); iLoadedSpawns[referencedVal] = 1; } diff --git a/src/server/collision/Maps/TileAssembler.cpp b/src/server/collision/Maps/TileAssembler.cpp index 06540ecdc61..ee978211577 100644 --- a/src/server/collision/Maps/TileAssembler.cpp +++ b/src/server/collision/Maps/TileAssembler.cpp @@ -391,6 +391,18 @@ namespace VMAP } } + if (bounds.isEmpty()) + { + std::cout << "\nModel " << std::string(buff, name_length) << " has empty bounding box" << std::endl; + continue; + } + + if (!bounds.isFinite()) + { + std::cout << "\nModel " << std::string(buff, name_length) << " has invalid bounding box" << std::endl; + continue; + } + fwrite(&displayId, sizeof(uint32), 1, model_list_copy); fwrite(&name_length, sizeof(uint32), 1, model_list_copy); fwrite(&buff, sizeof(char), name_length, model_list_copy); diff --git a/src/server/collision/Models/GameObjectModel.cpp b/src/server/collision/Models/GameObjectModel.cpp index 993c298941c..05bd5d360c6 100644 --- a/src/server/collision/Models/GameObjectModel.cpp +++ b/src/server/collision/Models/GameObjectModel.cpp @@ -78,6 +78,12 @@ void LoadGameObjectModelList() break; } + if (v1.isNaN() || v2.isNaN()) + { + VMAP_ERROR_LOG("misc", "File '%s' Model '%s' has invalid v1%s v2%s values!", VMAP::GAMEOBJECT_MODELS, std::string(buff, name_length).c_str(), v1.toString().c_str(), v2.toString().c_str()); + continue; + } + model_list.insert ( ModelList::value_type( displayId, GameobjectModelData(std::string(buff, name_length), AABox(v1, v2)) ) diff --git a/src/server/game/AI/CoreAI/UnitAI.cpp b/src/server/game/AI/CoreAI/UnitAI.cpp index e7d39d715eb..5aa6ea8ea7a 100644 --- a/src/server/game/AI/CoreAI/UnitAI.cpp +++ b/src/server/game/AI/CoreAI/UnitAI.cpp @@ -141,7 +141,7 @@ void UnitAI::DoCast(uint32 spellId) { if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId)) { - bool playerOnly = (spellInfo->AttributesEx3 & SPELL_ATTR3_ONLY_TARGET_PLAYERS) != 0; + bool playerOnly = spellInfo->HasAttribute(SPELL_ATTR3_ONLY_TARGET_PLAYERS); target = SelectTarget(SELECT_TARGET_RANDOM, 0, spellInfo->GetMaxRange(false), playerOnly); } break; @@ -156,7 +156,7 @@ void UnitAI::DoCast(uint32 spellId) { if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId)) { - bool playerOnly = (spellInfo->AttributesEx3 & SPELL_ATTR3_ONLY_TARGET_PLAYERS) != 0; + bool playerOnly = spellInfo->HasAttribute(SPELL_ATTR3_ONLY_TARGET_PLAYERS); float range = spellInfo->GetMaxRange(false); DefaultTargetSelector targetSelector(me, range, playerOnly, -(int32)spellId); @@ -213,7 +213,7 @@ void UnitAI::FillAISpellInfo() if (!spellInfo) continue; - if (spellInfo->Attributes & SPELL_ATTR0_CASTABLE_WHILE_DEAD) + if (spellInfo->HasAttribute(SPELL_ATTR0_CASTABLE_WHILE_DEAD)) AIInfo->condition = AICOND_DIE; else if (spellInfo->IsPassive() || spellInfo->GetDuration() == -1) AIInfo->condition = AICOND_AGGRO; diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp index b00a24129c3..135a0fcee93 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp @@ -663,3 +663,8 @@ void GetGameObjectListWithEntryInGrid(std::list<GameObject*>& list, WorldObject* { source->GetGameObjectListWithEntryInGrid(list, entry, maxSearchRange); } + +void GetPlayerListInGrid(std::list<Player*>& list, WorldObject* source, float maxSearchRange) +{ + source->GetPlayerListInGrid(list, maxSearchRange); +} diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.h b/src/server/game/AI/ScriptedAI/ScriptedCreature.h index cff628a847e..1a4c3064a59 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.h +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.h @@ -425,5 +425,6 @@ Creature* GetClosestCreatureWithEntry(WorldObject* source, uint32 entry, float m GameObject* GetClosestGameObjectWithEntry(WorldObject* source, uint32 entry, float maxSearchRange); void GetCreatureListWithEntryInGrid(std::list<Creature*>& list, WorldObject* source, uint32 entry, float maxSearchRange); void GetGameObjectListWithEntryInGrid(std::list<GameObject*>& list, WorldObject* source, uint32 entry, float maxSearchRange); +void GetPlayerListInGrid(std::list<Player*>& list, WorldObject* source, float maxSearchRange); #endif // SCRIPTEDCREATURE_H_ diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index e7e5f53f7a2..58b25781035 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -3167,7 +3167,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui std::list<Creature*> list; me->GetCreatureListWithEntryInGrid(list, e.event.distance.entry, (float)e.event.distance.dist); - if (list.size() > 0) + if (!list.empty()) creature = list.front(); } @@ -3198,7 +3198,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui std::list<GameObject*> list; me->GetGameObjectListWithEntryInGrid(list, e.event.distance.entry, (float)e.event.distance.dist); - if (list.size() > 0) + if (!list.empty()) gameobject = list.front(); } diff --git a/src/server/game/Achievements/AchievementMgr.cpp b/src/server/game/Achievements/AchievementMgr.cpp index c1fde9f53d3..51106d7cfe1 100644 --- a/src/server/game/Achievements/AchievementMgr.cpp +++ b/src/server/game/Achievements/AchievementMgr.cpp @@ -55,6 +55,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE: case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG: + case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON: case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING: case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: // only hardcoded list case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: @@ -700,58 +701,100 @@ void AchievementMgr::CheckAllAchievementCriteria() } static const uint32 achievIdByArenaSlot[MAX_ARENA_SLOT] = { 1057, 1107, 1108 }; -static const uint32 achievIdForDungeon[][4] = -{ - // ach_cr_id, is_dungeon, is_raid, is_heroic_dungeon - { 321, true, true, true }, - { 916, false, true, false }, - { 917, false, true, false }, - { 918, true, false, false }, - { 2219, false, false, true }, - { 0, false, false, false } -}; /** * this function will be called whenever the user might have done a criteria relevant action */ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscValue1 /*= 0*/, uint32 miscValue2 /*= 0*/, Unit* unit /*= NULL*/) { - TC_LOG_DEBUG("achievement", "AchievementMgr::UpdateAchievementCriteria(%u, %u, %u)", type, miscValue1, miscValue2); + if (type >= ACHIEVEMENT_CRITERIA_TYPE_TOTAL) + { + TC_LOG_DEBUG("achievement", "UpdateAchievementCriteria: Wrong criteria type %u", type); + return; + } // disable for gamemasters with GM-mode enabled if (m_player->IsGameMaster()) + { + TC_LOG_DEBUG("achievement", "UpdateAchievementCriteria: [Player %s GM mode on] %s, %s (%u), %u, %u" + , m_player->GetName().c_str(), m_player->GetGUID().ToString().c_str(), AchievementGlobalMgr::GetCriteriaTypeString(type), type, miscValue1, miscValue2); return; + } + + TC_LOG_DEBUG("achievement", "UpdateAchievementCriteria: %s, %s (%u), %u, %u" + , m_player->GetGUID().ToString().c_str(), AchievementGlobalMgr::GetCriteriaTypeString(type), type, miscValue1, miscValue2); AchievementCriteriaEntryList const& achievementCriteriaList = sAchievementMgr->GetAchievementCriteriaByType(type); - for (AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i != achievementCriteriaList.end(); ++i) + for (AchievementCriteriaEntry const* achievementCriteria : achievementCriteriaList) { - AchievementCriteriaEntry const* achievementCriteria = (*i); AchievementEntry const* achievement = sAchievementMgr->GetAchievement(achievementCriteria->referredAchievement); if (!achievement) continue; - if (!CanUpdateCriteria(achievementCriteria, achievement)) + if (!CanUpdateCriteria(achievementCriteria, achievement, miscValue1, miscValue2, unit)) continue; switch (type) { + // special cases, db data is checked later + case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: + case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT: + case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT: + break; + default: + if (AchievementCriteriaDataSet const* data = sAchievementMgr->GetCriteriaDataSet(achievementCriteria)) + if (!data->Meets(GetPlayer(), unit, miscValue1)) + continue; + break; + } + + switch (type) + { // std. case: increment at 1 + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST: + case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP: + case ACHIEVEMENT_CRITERIA_TYPE_DEATH: + case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON: + case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE: + case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER: + case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM: + case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET: + case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: + case ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE: + case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA: + case ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA: // This also behaves like ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA + case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL: + case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM: + case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS: + case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE: + case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: + case ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS: case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS: + case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT: + case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2: + case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL: + case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT: + case ACHIEVEMENT_CRITERIA_TYPE_ON_LOGIN: + case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL: case ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION: - case ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS: /* FIXME: for online player only currently */ + case ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS: /* FIXME: for online player only currently */ + case ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM: + case ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM: case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED: case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED: case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED: case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN: + case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS: case ACHIEVEMENT_CRITERIA_TYPE_USE_LFD_TO_GROUP_WITH_PLAYERS: - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscValue1) - continue; SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); break; // std case: increment at miscvalue1 + case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG: + case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE: + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND: + case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE: case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS: case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS: case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD: @@ -759,14 +802,19 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER: case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL: case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY: - case ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS:/* FIXME: for online player only currently */ + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS: /* FIXME: for online player only currently */ case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_DAMAGE_RECEIVED: case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_HEALING_RECEIVED: - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscValue1) - continue; SetCriteriaProgress(achievementCriteria, miscValue1, PROGRESS_ACCUMULATE); break; + // std case: increment at miscvalue2 + case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: + case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM: + case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM: + case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE: + case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE: + SetCriteriaProgress(achievementCriteria, miscValue2, PROGRESS_ACCUMULATE); + break; // std case: high value at miscvalue1 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID: case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD: /* FIXME: for online player only currently */ @@ -774,92 +822,27 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_RECEIVED: case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEAL_CAST: case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALING_RECEIVED: - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscValue1) - continue; SetCriteriaProgress(achievementCriteria, miscValue1, PROGRESS_HIGHEST); break; - - // specialized cases - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST: - { - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscValue1) - continue; - - if (achievement->categoryId == CATEGORY_CHILDRENS_WEEK) - { - AchievementCriteriaDataSet const* data = sAchievementMgr->GetCriteriaDataSet(achievementCriteria); - if (!data || !data->Meets(GetPlayer(), NULL)) - continue; - } - - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG: - { - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscValue1) - continue; - if (achievementCriteria->win_bg.bgMapID != GetPlayer()->GetMapId()) - continue; - - // those requirements couldn't be found in the dbc - AchievementCriteriaDataSet const* data = sAchievementMgr->GetCriteriaDataSet(achievementCriteria); - if (!data || !data->Meets(GetPlayer(), unit)) - continue; - - SetCriteriaProgress(achievementCriteria, miscValue1, PROGRESS_ACCUMULATE); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: - { - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscValue1) - continue; - if (achievementCriteria->kill_creature.creatureID != miscValue1) - continue; - - // those requirements couldn't be found in the dbc - AchievementCriteriaDataSet const* data = sAchievementMgr->GetCriteriaDataSet(achievementCriteria); - if (!data || !data->Meets(GetPlayer(), unit)) - continue; - - SetCriteriaProgress(achievementCriteria, miscValue2, PROGRESS_ACCUMULATE); + // std. case: set at 1 + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: + case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL: + case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA: + case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP: + case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM: + case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM: + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_SET); break; - } - case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE: - { - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscValue2) - continue; - - // those requirements couldn't be found in the dbc - AchievementCriteriaDataSet const* data = sAchievementMgr->GetCriteriaDataSet(achievementCriteria); - if (!data || !data->Meets(GetPlayer(), unit, miscValue1)) - continue; - SetCriteriaProgress(achievementCriteria, miscValue2, PROGRESS_ACCUMULATE); - break; - } + // specialized cases case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL: - if (AchievementCriteriaDataSet const* data = sAchievementMgr->GetCriteriaDataSet(achievementCriteria)) - if (!data->Meets(GetPlayer(), unit)) - continue; SetCriteriaProgress(achievementCriteria, GetPlayer()->getLevel()); break; case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL: - // update at loading or specific skill update - if (miscValue1 && miscValue1 != achievementCriteria->reach_skill_level.skillID) - continue; if (uint32 skillvalue = GetPlayer()->GetBaseSkillValue(achievementCriteria->reach_skill_level.skillID)) SetCriteriaProgress(achievementCriteria, skillvalue); break; case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL: - // update at loading or specific skill update - if (miscValue1 && miscValue1 != achievementCriteria->learn_skill_level.skillID) - continue; if (uint32 maxSkillvalue = GetPlayer()->GetPureMaxSkillValue(achievementCriteria->learn_skill_level.skillID)) SetCriteriaProgress(achievementCriteria, maxSkillvalue); break; @@ -868,14 +851,12 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui SetCriteriaProgress(achievementCriteria, 1); break; case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT: - { SetCriteriaProgress(achievementCriteria, GetPlayer()->GetRewardedQuestCount()); break; - } case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST_DAILY: { time_t nextDailyResetTime = sWorld->GetNextDailyQuestsResetTime(); - CriteriaProgress *progress = GetCriteriaProgress(achievementCriteria); + CriteriaProgress const* progress = GetCriteriaProgress(achievementCriteria); if (!miscValue1) // Login case. { @@ -904,10 +885,6 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui } case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE: { - // speedup for non-login case - if (miscValue1 && miscValue1 != achievementCriteria->complete_quests_in_zone.zoneID) - continue; - uint32 counter = 0; const RewardedQuestSet &rewQuests = GetPlayer()->getRewardedQuests(); @@ -920,231 +897,11 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui SetCriteriaProgress(achievementCriteria, counter); break; } - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND: - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscValue1) - continue; - if (GetPlayer()->GetMapId() != achievementCriteria->complete_battleground.mapID) - continue; - SetCriteriaProgress(achievementCriteria, miscValue1, PROGRESS_ACCUMULATE); - break; - case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP: - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscValue1) - continue; - if (GetPlayer()->GetMapId() != achievementCriteria->death_at_map.mapID) - continue; - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - case ACHIEVEMENT_CRITERIA_TYPE_DEATH: - { - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscValue1) - continue; - // skip wrong arena achievements, if not achievIdByArenaSlot then normal total death counter - bool notfit = false; - for (int j = 0; j < MAX_ARENA_SLOT; ++j) - { - if (achievIdByArenaSlot[j] == achievement->ID) - { - Battleground* bg = GetPlayer()->GetBattleground(); - if (!bg || !bg->isArena() || ArenaTeam::GetSlotByType(bg->GetArenaType()) != j) - notfit = true; - - break; - } - } - if (notfit) - continue; - - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON: - { - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscValue1) - continue; - - Map const* map = GetPlayer()->IsInWorld() ? GetPlayer()->GetMap() : sMapMgr->FindMap(GetPlayer()->GetMapId(), GetPlayer()->GetInstanceId()); - if (!map || !map->IsDungeon()) - continue; - - // search case - bool found = false; - for (int j = 0; achievIdForDungeon[j][0]; ++j) - { - if (achievIdForDungeon[j][0] == achievement->ID) - { - if (map->IsRaid()) - { - // if raid accepted (ignore difficulty) - if (!achievIdForDungeon[j][2]) - break; // for - } - else if (GetPlayer()->GetDungeonDifficulty() == DUNGEON_DIFFICULTY_NORMAL) - { - // dungeon in normal mode accepted - if (!achievIdForDungeon[j][1]) - break; // for - } - else - { - // dungeon in heroic mode accepted - if (!achievIdForDungeon[j][3]) - break; // for - } - - found = true; - break; // for - } - } - if (!found) - continue; - - //FIXME: work only for instances where max == min for players - if (map->ToInstanceMap()->GetMaxPlayers() != achievementCriteria->death_in_dungeon.manLimit) - continue; - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - - } - case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE: - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscValue1) - continue; - if (miscValue1 != achievementCriteria->killed_by_creature.creatureEntry) - continue; - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER: - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscValue1) - continue; - - // if team check required: must kill by opposition faction - if (achievement->ID == 318 && miscValue2 == GetPlayer()->GetTeam()) - continue; - - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING: - { - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscValue1) - continue; - - // those requirements couldn't be found in the dbc - AchievementCriteriaDataSet const* data = sAchievementMgr->GetCriteriaDataSet(achievementCriteria); - if (!data || !data->Meets(GetPlayer(), unit)) - continue; - // miscvalue1 is the ingame fallheight*100 as stored in dbc SetCriteriaProgress(achievementCriteria, miscValue1); break; - } - case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM: - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscValue1) - continue; - if (miscValue2 != achievementCriteria->death_from.type) - continue; - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: - { - // if miscvalues != 0, it contains the questID. - if (miscValue1) - { - if (miscValue1 != achievementCriteria->complete_quest.questID) - continue; - } - else - { - // login case. - if (!GetPlayer()->GetQuestRewardStatus(achievementCriteria->complete_quest.questID)) - continue; - } - - if (AchievementCriteriaDataSet const* data = sAchievementMgr->GetCriteriaDataSet(achievementCriteria)) - if (!data->Meets(GetPlayer(), unit)) - continue; - - SetCriteriaProgress(achievementCriteria, 1); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET: - case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2: - { - if (!miscValue1 || miscValue1 != achievementCriteria->be_spell_target.spellID) - continue; - - // those requirements couldn't be found in the dbc - AchievementCriteriaDataSet const* data = sAchievementMgr->GetCriteriaDataSet(achievementCriteria); - if (!data) - continue; - - if (!data->Meets(GetPlayer(), unit)) - continue; - - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: - case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: - { - if (!miscValue1 || miscValue1 != achievementCriteria->cast_spell.spellID) - continue; - - // those requirements couldn't be found in the dbc - AchievementCriteriaDataSet const* data = sAchievementMgr->GetCriteriaDataSet(achievementCriteria); - if (!data) - continue; - - if (!data->Meets(GetPlayer(), unit)) - continue; - - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL: - if (miscValue1 && miscValue1 != achievementCriteria->learn_spell.spellID) - continue; - - if (GetPlayer()->HasSpell(achievementCriteria->learn_spell.spellID)) - SetCriteriaProgress(achievementCriteria, 1); - break; - case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE: - { - // miscvalue1=loot_type (note: 0 = LOOT_CORPSE and then it ignored) - // miscvalue2=count of item loot - if (!miscValue1 || !miscValue2) - continue; - if (miscValue1 != achievementCriteria->loot_type.lootType) - continue; - - // zone specific - if (achievementCriteria->loot_type.lootTypeCount == 1) - { - // those requirements couldn't be found in the dbc - AchievementCriteriaDataSet const* data = sAchievementMgr->GetCriteriaDataSet(achievementCriteria); - if (!data || !data->Meets(GetPlayer(), unit)) - continue; - } - - SetCriteriaProgress(achievementCriteria, miscValue2, PROGRESS_ACCUMULATE); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM: - // speedup for non-login case - if (miscValue1 && achievementCriteria->own_item.itemID != miscValue1) - continue; - SetCriteriaProgress(achievementCriteria, miscValue2, PROGRESS_ACCUMULATE); - break; case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: - if (!miscValue1) // no update at login - continue; - // additional requirements if (achievementCriteria->additionalRequirements[0].additionalRequirement_type == ACHIEVEMENT_CRITERIA_CONDITION_NO_LOSE) { @@ -1160,198 +917,36 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); break; - case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM: - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscValue1) - continue; - - if (achievementCriteria->use_item.itemID != miscValue1) - continue; - - // Children's Week achievements have extra requirements - if (achievement->categoryId == CATEGORY_CHILDRENS_WEEK) - { - AchievementCriteriaDataSet const* data = sAchievementMgr->GetCriteriaDataSet(achievementCriteria); - if (!data || !data->Meets(GetPlayer(), NULL)) - continue; - } - - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM: - // You _have_ to loot that item, just owning it when logging in does _not_ count! - if (!miscValue1) - continue; - if (miscValue1 != achievementCriteria->own_item.itemID) - continue; - SetCriteriaProgress(achievementCriteria, miscValue2, PROGRESS_ACCUMULATE); - break; - case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA: - { - WorldMapOverlayEntry const* worldOverlayEntry = sWorldMapOverlayStore.LookupEntry(achievementCriteria->explore_area.areaReference); - if (!worldOverlayEntry) - break; - - bool matchFound = false; - for (int j = 0; j < MAX_WORLD_MAP_OVERLAY_AREA_IDX; ++j) - { - uint32 area_id = worldOverlayEntry->areatableID[j]; - if (!area_id) // array have 0 only in empty tail - break; - - int32 exploreFlag = GetAreaFlagByAreaID(area_id); - if (exploreFlag < 0) - continue; - - uint32 playerIndexOffset = uint32(exploreFlag) / 32; - uint32 mask = 1<< (uint32(exploreFlag) % 32); - - if (GetPlayer()->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + playerIndexOffset) & mask) - { - matchFound = true; - break; - } - } - - if (matchFound) - SetCriteriaProgress(achievementCriteria, 1); - break; - } case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT: SetCriteriaProgress(achievementCriteria, GetPlayer()->GetBankBagSlotCount()); break; case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION: { - // skip faction check only at loading - if (miscValue1 && miscValue1 != achievementCriteria->gain_reputation.factionID) - continue; - int32 reputation = GetPlayer()->GetReputationMgr().GetReputation(achievementCriteria->gain_reputation.factionID); if (reputation > 0) SetCriteriaProgress(achievementCriteria, reputation); break; } case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION: - { SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetExaltedFactionCount()); break; - } - case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP: - { - // skip for login case - if (!miscValue1) - continue; - SetCriteriaProgress(achievementCriteria, 1); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM: - { - // miscvalue1 = itemid - // miscvalue2 = itemSlot - if (!miscValue1) - continue; - - if (miscValue2 != achievementCriteria->equip_epic_item.itemSlot) - continue; - - // check item level and quality via achievement_criteria_data - AchievementCriteriaDataSet const* data = sAchievementMgr->GetCriteriaDataSet(achievementCriteria); - if (!data || !data->Meets(GetPlayer(), nullptr, miscValue1)) - continue; - - SetCriteriaProgress(achievementCriteria, 1); - break; - } case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT: case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT: { - // miscvalue1 = itemid - // miscvalue2 = diced value - if (!miscValue1) - continue; - if (miscValue2 != achievementCriteria->roll_greed_on_loot.rollValue) - continue; - - ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(miscValue1); - if (!pProto) + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(miscValue1); + if (!proto) continue; // check item level via achievement_criteria_data AchievementCriteriaDataSet const* data = sAchievementMgr->GetCriteriaDataSet(achievementCriteria); - if (!data || !data->Meets(GetPlayer(), nullptr, pProto->ItemLevel)) - continue; - - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: - { - // miscvalue1 = emote - if (!miscValue1) - continue; - if (miscValue1 != achievementCriteria->do_emote.emoteID) + if (!data || !data->Meets(GetPlayer(), nullptr, proto->ItemLevel)) continue; - if (achievementCriteria->do_emote.count) - { - // those requirements couldn't be found in the dbc - AchievementCriteriaDataSet const* data = sAchievementMgr->GetCriteriaDataSet(achievementCriteria); - if (!data || !data->Meets(GetPlayer(), unit)) - continue; - } SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); break; } - case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE: - case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE: - { - if (!miscValue1) - continue; - - if (achievementCriteria->additionalRequirements[0].additionalRequirement_type == ACHIEVEMENT_CRITERIA_CONDITION_BG_MAP) - { - if (GetPlayer()->GetMapId() != achievementCriteria->additionalRequirements[0].additionalRequirement_value) - continue; - - // map specific case (BG in fact) expected player targeted damage/heal - if (!unit || unit->GetTypeId() != TYPEID_PLAYER) - continue; - } - - SetCriteriaProgress(achievementCriteria, miscValue1, PROGRESS_ACCUMULATE); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM: - // miscvalue1 = item_id - if (!miscValue1) - continue; - if (miscValue1 != achievementCriteria->equip_item.itemID) - continue; - - SetCriteriaProgress(achievementCriteria, 1); - break; - case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT: - // miscvalue1 = go entry - if (!miscValue1) - continue; - if (miscValue1 != achievementCriteria->use_gameobject.goEntry) - continue; - - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT: - if (!miscValue1) - continue; - if (miscValue1 != achievementCriteria->fish_in_gameobject.goEntry) - continue; - - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS: { - if (miscValue1 && miscValue1 != achievementCriteria->learn_skillline_spell.skillLine) - continue; - uint32 spellCount = 0; for (PlayerSpellMap::const_iterator spellIter = GetPlayer()->GetSpellMap().begin(); spellIter != GetPlayer()->GetSpellMap().end(); @@ -1367,24 +962,6 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui SetCriteriaProgress(achievementCriteria, spellCount); break; } - case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscValue1) - continue; - - if (achievementCriteria->win_duel.duelCount) - { - // those requirements couldn't be found in the dbc - AchievementCriteriaDataSet const* data = sAchievementMgr->GetCriteriaDataSet(achievementCriteria); - if (!data) - continue; - - if (!data->Meets(GetPlayer(), unit)) - continue; - } - - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION: SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetReveredFactionCount()); break; @@ -1394,23 +971,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS: SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetVisibleFactionCount()); break; - case ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM: - case ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM: - { - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscValue1) - continue; - ItemTemplate const* proto = sObjectMgr->GetItemTemplate(miscValue1); - if (!proto || proto->Quality < ITEM_QUALITY_EPIC) - continue; - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - } case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE: { - if (miscValue1 && miscValue1 != achievementCriteria->learn_skill_line.skillLine) - continue; - uint32 spellCount = 0; for (PlayerSpellMap::const_iterator spellIter = GetPlayer()->GetSpellMap().begin(); spellIter != GetPlayer()->GetSpellMap().end(); @@ -1427,72 +989,21 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL: SetCriteriaProgress(achievementCriteria, GetPlayer()->GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS)); break; - case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS: - if (!miscValue1 || miscValue1 != achievementCriteria->hk_class.classID) - continue; - - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE: - if (!miscValue1 || miscValue1 != achievementCriteria->hk_race.raceID) - continue; - - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED: SetCriteriaProgress(achievementCriteria, GetPlayer()->GetMoney(), PROGRESS_HIGHEST); break; case ACHIEVEMENT_CRITERIA_TYPE_EARN_ACHIEVEMENT_POINTS: - { if (!miscValue1) { uint32 points = 0; - for (CompletedAchievementMap::iterator itr = m_completedAchievements.begin(); itr != m_completedAchievements.end(); ++itr) - if (AchievementEntry const* pAchievement = sAchievementMgr->GetAchievement(itr->first)) - points += pAchievement->points; + for (CompletedAchievementMap::const_iterator itr = m_completedAchievements.begin(); itr != m_completedAchievements.end(); ++itr) + if (AchievementEntry const* completedAchievements = sAchievementMgr->GetAchievement(itr->first)) + points += completedAchievements->points; SetCriteriaProgress(achievementCriteria, points, PROGRESS_SET); } else SetCriteriaProgress(achievementCriteria, miscValue1, PROGRESS_ACCUMULATE); break; - } - case ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE: - { - if (!miscValue1 || miscValue1 != achievementCriteria->bg_objective.objectiveId) - continue; - - // those requirements couldn't be found in the dbc - AchievementCriteriaDataSet const* data = sAchievementMgr->GetCriteriaDataSet(achievementCriteria); - if (!data || !data->Meets(GetPlayer(), unit)) - continue; - - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL: - case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL: - case ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS: - { - // skip login update - if (!miscValue1) - continue; - - // those requirements couldn't be found in the dbc - AchievementCriteriaDataSet const* data = sAchievementMgr->GetCriteriaDataSet(achievementCriteria); - if (!data || !data->Meets(GetPlayer(), unit)) - continue; - - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA: - { - if (!miscValue1 || miscValue1 != achievementCriteria->honorable_kill_at_area.areaID) - continue; - - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - } case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING: { uint32 reqTeamType = achievementCriteria->highest_team_rating.teamtype; @@ -1504,7 +1015,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui SetCriteriaProgress(achievementCriteria, miscValue1, PROGRESS_HIGHEST); } - else // login case + else // login case { for (uint32 arena_slot = 0; arena_slot < MAX_ARENA_SLOT; ++arena_slot) { @@ -1520,7 +1031,6 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui break; } } - break; } case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_PERSONAL_RATING: @@ -1534,7 +1044,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui SetCriteriaProgress(achievementCriteria, miscValue1, PROGRESS_HIGHEST); } - else // login case + else // login case { for (uint32 arena_slot = 0; arena_slot < MAX_ARENA_SLOT; ++arena_slot) { @@ -1556,27 +1066,6 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui break; } - case ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA: // This also behaves like ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA - { - // Check map id requirement - if (miscValue1 == achievementCriteria->win_arena.mapID) - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_ON_LOGIN: - { - // This criteria is only called directly after login - with expected miscvalue1 == 1 - if (!miscValue1) - continue; - - // They have no proper requirements in dbc - AchievementCriteriaDataSet const* data = sAchievementMgr->GetCriteriaDataSet(achievementCriteria); - if (!data || !data->Meets(GetPlayer(), unit)) - continue; - - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - } // std case: not exist in DBC, not triggered in code as result case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH: case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER: @@ -2019,7 +1508,7 @@ void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement) if (achievement->flags & ACHIEVEMENT_FLAG_COUNTER || HasAchieved(achievement->ID)) return; - TC_LOG_INFO("achievement", "AchievementMgr::CompletedAchievement(%u). Player: %s (%u)", + TC_LOG_DEBUG("achievement", "AchievementMgr::CompletedAchievement(%u). Player: %s (%u)", achievement->ID, m_player->GetName().c_str(), m_player->GetGUIDLow()); SendAchievementEarned(achievement); @@ -2140,18 +1629,53 @@ bool AchievementMgr::HasAchieved(uint32 achievementId) const return m_completedAchievements.find(achievementId) != m_completedAchievements.end(); } -bool AchievementMgr::CanUpdateCriteria(AchievementCriteriaEntry const* criteria, AchievementEntry const* achievement) +bool AchievementMgr::CanUpdateCriteria(AchievementCriteriaEntry const* criteria, AchievementEntry const* achievement, uint32 miscValue1, uint32 miscValue2, Unit const* unit) { - if (DisableMgr::IsDisabledFor(DISABLE_TYPE_ACHIEVEMENT_CRITERIA, criteria->ID, NULL)) + if (DisableMgr::IsDisabledFor(DISABLE_TYPE_ACHIEVEMENT_CRITERIA, criteria->ID, nullptr)) + { + TC_LOG_TRACE("achievement", "CanUpdateCriteria: (Id: %u Type %s) Disabled", + criteria->ID, AchievementGlobalMgr::GetCriteriaTypeString(criteria->requiredType)); return false; + } if (achievement->mapID != -1 && GetPlayer()->GetMapId() != uint32(achievement->mapID)) + { + TC_LOG_TRACE("achievement", "CanUpdateCriteria: (Id: %u Type %s Achievement %u) Wrong map", + criteria->ID, AchievementGlobalMgr::GetCriteriaTypeString(criteria->requiredType), achievement->ID); return false; + } if ((achievement->requiredFaction == ACHIEVEMENT_FACTION_HORDE && GetPlayer()->GetTeam() != HORDE) || (achievement->requiredFaction == ACHIEVEMENT_FACTION_ALLIANCE && GetPlayer()->GetTeam() != ALLIANCE)) + { + TC_LOG_TRACE("achievement", "CanUpdateCriteria: (Id: %u Type %s Achievement %u) Wrong faction", + criteria->ID, AchievementGlobalMgr::GetCriteriaTypeString(criteria->requiredType), achievement->ID); return false; + } + + if (!RequirementsSatisfied(criteria, achievement, miscValue1, miscValue2, unit)) + { + TC_LOG_TRACE("achievement", "CanUpdateCriteria: (Id: %u Type %s) Requirements not satisfied", + criteria->ID, AchievementGlobalMgr::GetCriteriaTypeString(criteria->requiredType)); + return false; + } + if (!ConditionsSatisfied(criteria)) + { + TC_LOG_TRACE("achievement", "CanUpdateCriteria: (Id: %u Type %s) Conditions not satisfied", + criteria->ID, AchievementGlobalMgr::GetCriteriaTypeString(criteria->requiredType)); + return false; + } + + // don't update already completed criteria + if (IsCompletedCriteria(criteria, achievement)) + return false; + + return true; +} + +bool AchievementMgr::ConditionsSatisfied(AchievementCriteriaEntry const* criteria) const +{ for (uint32 i = 0; i < MAX_CRITERIA_REQUIREMENTS; ++i) { if (!criteria->additionalRequirements[i].additionalRequirement_type) @@ -2172,13 +1696,558 @@ bool AchievementMgr::CanUpdateCriteria(AchievementCriteriaEntry const* criteria, } } - // don't update already completed criteria - if (IsCompletedCriteria(criteria, achievement)) - return false; + return true; +} + +bool AchievementMgr::RequirementsSatisfied(AchievementCriteriaEntry const* achievementCriteria, AchievementEntry const* achievement, uint32 miscValue1, uint32 miscValue2, Unit const* unit) const +{ + switch (AchievementCriteriaTypes(achievementCriteria->requiredType)) + { + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST: + case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING: + case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL: + case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: + case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP: + case ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS: + case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS: + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS: + case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS: + case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD: + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING: + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER: + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL: + case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY: + case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL: + case ACHIEVEMENT_CRITERIA_TYPE_ON_LOGIN: + case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: + case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL: + case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE: + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS: + case ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID: + case ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD: + case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED: + case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_DEALT: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_RECEIVED: + case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_DAMAGE_RECEIVED: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEAL_CAST: + case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_HEALING_RECEIVED: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALING_RECEIVED: + case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED: + case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN: + case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS: + case ACHIEVEMENT_CRITERIA_TYPE_USE_LFD_TO_GROUP_WITH_PLAYERS: + if (!miscValue1) + return false; + break; + case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL: + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT: + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT: + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST_DAILY: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_PERSONAL_RATING: + case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT: + case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION: + case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION: + case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION: + case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS: + case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED: + case ACHIEVEMENT_CRITERIA_TYPE_EARN_ACHIEVEMENT_POINTS: + break; + + // specialized cases + case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG: + if (!miscValue1) + return false; + if (achievementCriteria->win_bg.bgMapID != GetPlayer()->GetMapId()) + return false; + break; + case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: + if (!miscValue1) + return false; + if (achievementCriteria->kill_creature.creatureID != miscValue1) + return false; + break; + case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL: + if (miscValue1 && miscValue1 != achievementCriteria->reach_skill_level.skillID) + return false; + break; + case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL: + if (miscValue1 && miscValue1 != achievementCriteria->learn_skill_level.skillID) + return false; + break; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE: + if (miscValue1 && miscValue1 != achievementCriteria->complete_quests_in_zone.zoneID) + return false; + break; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND: + if (!miscValue1) + return false; + if (GetPlayer()->GetMapId() != achievementCriteria->complete_battleground.mapID) + return false; + break; + case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP: + if (!miscValue1) + return false; + if (GetPlayer()->GetMapId() != achievementCriteria->death_at_map.mapID) + return false; + break; + case ACHIEVEMENT_CRITERIA_TYPE_DEATH: + if (!miscValue1) + return false; + for (uint8 j = 0; j < MAX_ARENA_SLOT; ++j) + { + if (achievIdByArenaSlot[j] == achievement->ID) + { + Battleground* bg = GetPlayer()->GetBattleground(); + if (!bg || !bg->isArena() || ArenaTeam::GetSlotByType(bg->GetArenaType()) != j) + return false; + break; + } + } + break; + case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON: + { + if (!miscValue1) + return false; + + Map const* map = GetPlayer()->IsInWorld() ? GetPlayer()->GetMap() : sMapMgr->FindMap(GetPlayer()->GetMapId(), GetPlayer()->GetInstanceId()); + if (!map || !map->IsDungeon()) + return false; + + //FIXME: work only for instances where max == min for players + if (map->ToInstanceMap()->GetMaxPlayers() != achievementCriteria->death_in_dungeon.manLimit) + return false; + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE: + if (!miscValue1) + return false; + if (miscValue1 != achievementCriteria->killed_by_creature.creatureEntry) + return false; + break; + case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER: + if (!miscValue1) + return false; + // if team check required: must kill by opposition faction + if (achievement->ID == 318 && miscValue2 == GetPlayer()->GetTeam()) + return false; + break; + case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM: + if (!miscValue1) + return false; + if (miscValue2 != achievementCriteria->death_from.type) + return false; + break; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: + // if miscvalues != 0, it contains the questID. + if (miscValue1) + { + if (miscValue1 != achievementCriteria->complete_quest.questID) + return false; + } + else + { + // login case. + if (!GetPlayer()->GetQuestRewardStatus(achievementCriteria->complete_quest.questID)) + return false; + } + break; + case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET: + case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2: + if (!miscValue1 || miscValue1 != achievementCriteria->be_spell_target.spellID) + return false; + break; + case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: + case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: + if (!miscValue1 || miscValue1 != achievementCriteria->cast_spell.spellID) + return false; + break; + case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL: + if (miscValue1 && miscValue1 != achievementCriteria->learn_spell.spellID) + return false; + if (!GetPlayer()->HasSpell(achievementCriteria->learn_spell.spellID)) + return false; + break; + case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE: + // miscvalue1=loot_type (note: 0 = LOOT_CORPSE and then it ignored) + // miscvalue2=count of item loot + if (!miscValue1 || !miscValue2) + return false; + if (miscValue1 != achievementCriteria->loot_type.lootType) + return false; + break; + case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM: + if (miscValue1 && achievementCriteria->own_item.itemID != miscValue1) + return false; + break; + case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM: + if (!miscValue1) + return false; + if (achievementCriteria->use_item.itemID != miscValue1) + return false; + break; + case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM: + if (!miscValue1) + return false; + if (miscValue1 != achievementCriteria->own_item.itemID) + return false; + break; + case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA: + { + WorldMapOverlayEntry const* worldOverlayEntry = sWorldMapOverlayStore.LookupEntry(achievementCriteria->explore_area.areaReference); + if (!worldOverlayEntry) + return false; + + bool matchFound = false; + for (int j = 0; j < MAX_WORLD_MAP_OVERLAY_AREA_IDX; ++j) + { + uint32 area_id = worldOverlayEntry->areatableID[j]; + if (!area_id) // array have 0 only in empty tail + break; + + int32 exploreFlag = GetAreaFlagByAreaID(area_id); + if (exploreFlag < 0) + continue; + + uint32 playerIndexOffset = uint32(exploreFlag) / 32; + uint32 mask = 1 << (uint32(exploreFlag) % 32); + + if (GetPlayer()->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + playerIndexOffset) & mask) + { + matchFound = true; + break; + } + } + + if (!matchFound) + return false; + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION: + if (miscValue1 && miscValue1 != achievementCriteria->gain_reputation.factionID) + return false; + break; + case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM: + // miscvalue1 = itemid + // miscvalue2 = itemSlot + if (!miscValue1) + return false; + if (miscValue2 != achievementCriteria->equip_epic_item.itemSlot) + return false; + break; + case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT: + case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT: + // miscvalue1 = itemid + // miscvalue2 = diced value + if (!miscValue1) + return false; + if (miscValue2 != achievementCriteria->roll_greed_on_loot.rollValue) + return false; + break; + case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: + // miscvalue1 = emote + if (!miscValue1) + return false; + if (miscValue1 != achievementCriteria->do_emote.emoteID) + return false; + break; + case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE: + case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE: + if (!miscValue1) + return false; + + if (achievementCriteria->additionalRequirements[0].additionalRequirement_type == ACHIEVEMENT_CRITERIA_CONDITION_BG_MAP) + { + if (GetPlayer()->GetMapId() != achievementCriteria->additionalRequirements[0].additionalRequirement_value) + return false; + + // map specific case (BG in fact) expected player targeted damage/heal + if (!unit || unit->GetTypeId() != TYPEID_PLAYER) + return false; + } + break; + case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM: + if (!miscValue1) + return false; + if (miscValue1 != achievementCriteria->equip_item.itemID) + return false; + break; + case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT: + if (!miscValue1) + return false; + if (miscValue1 != achievementCriteria->use_gameobject.goEntry) + return false; + break; + case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT: + if (!miscValue1) + return false; + if (miscValue1 != achievementCriteria->fish_in_gameobject.goEntry) + return false; + break; + case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS: + if (miscValue1 && miscValue1 != achievementCriteria->learn_skillline_spell.skillLine) + return false; + break; + case ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM: + case ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM: + { + if (!miscValue1) + return false; + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(miscValue1); + if (!proto || proto->Quality < ITEM_QUALITY_EPIC) + return false; + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE: + if (miscValue1 && miscValue1 != achievementCriteria->learn_skill_line.skillLine) + return false; + break; + case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS: + if (!miscValue1 || miscValue1 != achievementCriteria->hk_class.classID) + return false; + break; + case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE: + if (!miscValue1 || miscValue1 != achievementCriteria->hk_race.raceID) + return false; + break; + case ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE: + if (!miscValue1 || miscValue1 != achievementCriteria->bg_objective.objectiveId) + return false; + break; + case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA: + if (!miscValue1 || miscValue1 != achievementCriteria->honorable_kill_at_area.areaID) + return false; + break; + case ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA: + if (miscValue1 != achievementCriteria->win_arena.mapID) + return false; + break; + default: + break; + } return true; } +char const* AchievementGlobalMgr::GetCriteriaTypeString(uint32 type) +{ + return GetCriteriaTypeString(AchievementCriteriaTypes(type)); +} + +char const* AchievementGlobalMgr::GetCriteriaTypeString(AchievementCriteriaTypes type) +{ + switch (type) + { + case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: + return "KILL_CREATURE"; + case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG: + return "TYPE_WIN_BG"; + case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL: + return "REACH_LEVEL"; + case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL: + return "REACH_SKILL_LEVEL"; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT: + return "COMPLETE_ACHIEVEMENT"; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT: + return "COMPLETE_QUEST_COUNT"; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST_DAILY: + return "COMPLETE_DAILY_QUEST_DAILY"; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE: + return "COMPLETE_QUESTS_IN_ZONE"; + case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE: + return "DAMAGE_DONE"; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST: + return "COMPLETE_DAILY_QUEST"; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND: + return "COMPLETE_BATTLEGROUND"; + case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP: + return "DEATH_AT_MAP"; + case ACHIEVEMENT_CRITERIA_TYPE_DEATH: + return "DEATH"; + case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON: + return "DEATH_IN_DUNGEON"; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_RAID: + return "COMPLETE_RAID"; + case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE: + return "KILLED_BY_CREATURE"; + case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER: + return "KILLED_BY_PLAYER"; + case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING: + return "FALL_WITHOUT_DYING"; + case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM: + return "DEATHS_FROM"; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: + return "COMPLETE_QUEST"; + case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET: + return "BE_SPELL_TARGET"; + case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: + return "CAST_SPELL"; + case ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE: + return "BG_OBJECTIVE_CAPTURE"; + case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA: + return "HONORABLE_KILL_AT_AREA"; + case ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA: + return "WIN_ARENA"; + case ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA: + return "PLAY_ARENA"; + case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL: + return "LEARN_SPELL"; + case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL: + return "HONORABLE_KILL"; + case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM: + return "OWN_ITEM"; + case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: + return "WIN_RATED_ARENA"; + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING: + return "HIGHEST_TEAM_RATING"; + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_PERSONAL_RATING: + return "HIGHEST_PERSONAL_RATING"; + case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL: + return "LEARN_SKILL_LEVEL"; + case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM: + return "USE_ITEM"; + case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM: + return "LOOT_ITEM"; + case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA: + return "EXPLORE_AREA"; + case ACHIEVEMENT_CRITERIA_TYPE_OWN_RANK: + return "OWN_RANK"; + case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT: + return "BUY_BANK_SLOT"; + case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION: + return "GAIN_REPUTATION"; + case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION: + return "GAIN_EXALTED_REPUTATION"; + case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP: + return "VISIT_BARBER_SHOP"; + case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM: + return "EQUIP_EPIC_ITEM"; + case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT: + return "ROLL_NEED_ON_LOOT"; + case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT: + return "GREED_ON_LOOT"; + case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS: + return "HK_CLASS"; + case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE: + return "HK_RACE"; + case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: + return "DO_EMOTE"; + case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE: + return "HEALING_DONE"; + case ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS: + return "GET_KILLING_BLOWS"; + case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM: + return "EQUIP_ITEM"; + case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS: + return "MONEY_FROM_VENDORS"; + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS: + return "GOLD_SPENT_FOR_TALENTS"; + case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS: + return "NUMBER_OF_TALENT_RESETS"; + case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD: + return "MONEY_FROM_QUEST_REWARD"; + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING: + return "GOLD_SPENT_FOR_TRAVELLING"; + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER: + return "GOLD_SPENT_AT_BARBER"; + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL: + return "GOLD_SPENT_FOR_MAIL"; + case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY: + return "LOOT_MONEY"; + case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT: + return "USE_GAMEOBJECT"; + case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2: + return "BE_SPELL_TARGET2"; + case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL: + return "SPECIAL_PVP_KILL"; + case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT: + return "FISH_IN_GAMEOBJECT"; + case ACHIEVEMENT_CRITERIA_TYPE_ON_LOGIN: + return "ON_LOGIN"; + case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS: + return "LEARN_SKILLLINE_SPELLS"; + case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: + return "WIN_DUEL"; + case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL: + return "LOSE_DUEL"; + case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE: + return "KILL_CREATURE_TYPE"; + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS: + return "GOLD_EARNED_BY_AUCTIONS"; + case ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION: + return "CREATE_AUCTION"; + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID: + return "HIGHEST_AUCTION_BID"; + case ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS: + return "WON_AUCTIONS"; + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD: + return "HIGHEST_AUCTION_SOLD"; + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED: + return "HIGHEST_GOLD_VALUE_OWNED"; + case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION: + return "GAIN_REVERED_REPUTATION"; + case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION: + return "GAIN_HONORED_REPUTATION"; + case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS: + return "KNOWN_FACTIONS"; + case ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM: + return "LOOT_EPIC_ITEM"; + case ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM: + return "RECEIVE_EPIC_ITEM"; + case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED: + return "ROLL_NEED"; + case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED: + return "ROLL_GREED"; + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH: + return "HIGHEST_HEALTH"; + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_POWER: + return "HIGHEST_POWER"; + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_STAT: + return "HIGHEST_STAT"; + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER: + return "HIGHEST_SPELLPOWER"; + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_ARMOR: + return "HIGHEST_ARMOR"; + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_RATING: + return "HIGHEST_RATING"; + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_DEALT: + return "HIT_DEALT"; + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_RECEIVED: + return "HIT_RECEIVED"; + case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_DAMAGE_RECEIVED: + return "TOTAL_DAMAGE_RECEIVED"; + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEAL_CAST: + return "HIGHEST_HEAL_CAST"; + case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_HEALING_RECEIVED: + return "TOTAL_HEALING_RECEIVED"; + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALING_RECEIVED: + return "HIGHEST_HEALING_RECEIVED"; + case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED: + return "QUEST_ABANDONED"; + case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN: + return "FLIGHT_PATHS_TAKEN"; + case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE: + return "LOOT_TYPE"; + case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: + return "CAST_SPELL2"; + case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE: + return "LEARN_SKILL_LINE"; + case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL: + return "EARN_HONORABLE_KILL"; + case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS: + return "ACCEPTED_SUMMONINGS"; + case ACHIEVEMENT_CRITERIA_TYPE_EARN_ACHIEVEMENT_POINTS: + return "EARN_ACHIEVEMENT_POINTS"; + case ACHIEVEMENT_CRITERIA_TYPE_USE_LFD_TO_GROUP_WITH_PLAYERS: + return "USE_LFD_TO_GROUP_WITH_PLAYERS"; + } + return "MISSING_TYPE"; +} + //========================================================== void AchievementGlobalMgr::LoadAchievementCriteriaList() { @@ -2357,7 +2426,7 @@ void AchievementGlobalMgr::LoadAchievementCriteriaData() AchievementEntry const* achievement = sAchievementMgr->GetAchievement(criteria->referredAchievement); if (!achievement) continue; - if (achievement->categoryId != CATEGORY_CHILDRENS_WEEK) + if (achievement->categoryId != CATEGORY_CHILDRENS_WEEK && achievement->ID != 1785) continue; break; } diff --git a/src/server/game/Achievements/AchievementMgr.h b/src/server/game/Achievements/AchievementMgr.h index f8869757e50..078229ea4c8 100644 --- a/src/server/game/Achievements/AchievementMgr.h +++ b/src/server/game/Achievements/AchievementMgr.h @@ -293,9 +293,12 @@ class AchievementMgr void CompletedCriteriaFor(AchievementEntry const* achievement); bool IsCompletedCriteria(AchievementCriteriaEntry const* achievementCriteria, AchievementEntry const* achievement); bool IsCompletedAchievement(AchievementEntry const* entry); - bool CanUpdateCriteria(AchievementCriteriaEntry const* criteria, AchievementEntry const* achievement); + bool CanUpdateCriteria(AchievementCriteriaEntry const* criteria, AchievementEntry const* achievement, uint32 miscValue1, uint32 miscValue2, Unit const* unit); void BuildAllDataPacket(WorldPacket* data) const; + bool ConditionsSatisfied(AchievementCriteriaEntry const* criteria) const; + bool RequirementsSatisfied(AchievementCriteriaEntry const* criteria, AchievementEntry const* achievement, uint32 miscValue1, uint32 miscValue2, Unit const* unit) const; + Player* m_player; CriteriaProgressMap m_criteriaProgress; CompletedAchievementMap m_completedAchievements; @@ -309,6 +312,9 @@ class AchievementGlobalMgr ~AchievementGlobalMgr() { } public: + static char const* GetCriteriaTypeString(AchievementCriteriaTypes type); + static char const* GetCriteriaTypeString(uint32 type); + static AchievementGlobalMgr* instance() { static AchievementGlobalMgr instance; diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp index ac2ceb78346..0235aa7e8b6 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp @@ -440,8 +440,6 @@ bool AuctionHouseObject::RemoveAuction(AuctionEntry* auction) // we need to delete the entry, it is not referenced any more delete auction; - auction = NULL; - return wasInMap; } @@ -469,7 +467,7 @@ void AuctionHouseObject::Update() continue; ///- Either cancel the auction if there was no bidder - if (auction->bidder == 0) + if (auction->bidder == 0 && auction->bid == 0) { sAuctionMgr->SendAuctionExpiredMail(auction, trans); sScriptMgr->OnAuctionExpire(this, auction); diff --git a/src/server/game/AuctionHouseBot/AuctionHouseBot.cpp b/src/server/game/AuctionHouseBot/AuctionHouseBot.cpp index 7e8ccb4bb23..c0c753100bc 100644 --- a/src/server/game/AuctionHouseBot/AuctionHouseBot.cpp +++ b/src/server/game/AuctionHouseBot/AuctionHouseBot.cpp @@ -204,14 +204,24 @@ void AuctionBotConfig::GetConfigFromFile() SetConfig(CONFIG_AHBOT_MINTIME, "AuctionHouseBot.MinTime", 1); SetConfig(CONFIG_AHBOT_MAXTIME, "AuctionHouseBot.MaxTime", 72); - SetConfigMinMax(CONFIG_AHBOT_BUYER_CHANCE_RATIO_ALLIANCE, "AuctionHouseBot.Buyer.Alliance.Chance.Ratio", 3, 1, 100); - SetConfigMinMax(CONFIG_AHBOT_BUYER_CHANCE_RATIO_HORDE, "AuctionHouseBot.Buyer.Horde.Chance.Ratio", 3, 1, 100); - SetConfigMinMax(CONFIG_AHBOT_BUYER_CHANCE_RATIO_NEUTRAL, "AuctionHouseBot.Buyer.Neutral.Chance.Ratio", 3, 1, 100); SetConfigMinMax(CONFIG_AHBOT_BUYER_RECHECK_INTERVAL, "AuctionHouseBot.Buyer.Recheck.Interval", 20, 1, DAY / MINUTE); + SetConfig(CONFIG_AHBOT_BUYER_BASEPRICE_GRAY, "AuctionHouseBot.Buyer.Baseprice.Gray", 3504); + SetConfig(CONFIG_AHBOT_BUYER_BASEPRICE_WHITE, "AuctionHouseBot.Buyer.Baseprice.White", 5429); + SetConfig(CONFIG_AHBOT_BUYER_BASEPRICE_GREEN, "AuctionHouseBot.Buyer.Baseprice.Green", 21752); + SetConfig(CONFIG_AHBOT_BUYER_BASEPRICE_BLUE, "AuctionHouseBot.Buyer.Baseprice.Blue", 36463); + SetConfig(CONFIG_AHBOT_BUYER_BASEPRICE_PURPLE, "AuctionHouseBot.Buyer.Baseprice.Purple", 87124); + SetConfig(CONFIG_AHBOT_BUYER_BASEPRICE_ORANGE, "AuctionHouseBot.Buyer.Baseprice.Orange", 214347); + SetConfig(CONFIG_AHBOT_BUYER_BASEPRICE_YELLOW, "AuctionHouseBot.Buyer.Baseprice.Yellow", 407406); + SetConfig(CONFIG_AHBOT_BUYER_CHANCEMULTIPLIER_GRAY, "AuctionHouseBot.Buyer.ChanceMultiplier.Gray", 100); + SetConfig(CONFIG_AHBOT_BUYER_CHANCEMULTIPLIER_WHITE, "AuctionHouseBot.Buyer.ChanceMultiplier.White", 100); + SetConfig(CONFIG_AHBOT_BUYER_CHANCEMULTIPLIER_GREEN, "AuctionHouseBot.Buyer.ChanceMultiplier.Green", 100); + SetConfig(CONFIG_AHBOT_BUYER_CHANCEMULTIPLIER_BLUE, "AuctionHouseBot.Buyer.ChanceMultiplier.Blue", 100); + SetConfig(CONFIG_AHBOT_BUYER_CHANCEMULTIPLIER_PURPLE, "AuctionHouseBot.Buyer.ChanceMultiplier.Purple", 100); + SetConfig(CONFIG_AHBOT_BUYER_CHANCEMULTIPLIER_ORANGE, "AuctionHouseBot.Buyer.ChanceMultiplier.Orange", 100); + SetConfig(CONFIG_AHBOT_BUYER_CHANCEMULTIPLIER_YELLOW, "AuctionHouseBot.Buyer.ChanceMultiplier.Yellow", 100); SetConfig(CONFIG_AHBOT_SELLER_ENABLED, "AuctionHouseBot.Seller.Enabled", false); SetConfig(CONFIG_AHBOT_BUYER_ENABLED, "AuctionHouseBot.Buyer.Enabled", false); - SetConfig(CONFIG_AHBOT_BUYPRICE_BUYER, "AuctionHouseBot.Buyer.Buyprice", true); SetConfig(CONFIG_AHBOT_CLASS_MISC_MOUNT_MIN_REQ_LEVEL, "AuctionHouseBot.Class.Misc.Mount.ReqLevel.Min", 0); SetConfig(CONFIG_AHBOT_CLASS_MISC_MOUNT_MAX_REQ_LEVEL, "AuctionHouseBot.Class.Misc.Mount.ReqLevel.Max", 0); @@ -229,7 +239,7 @@ void AuctionBotConfig::GetConfigFromFile() char const* AuctionBotConfig::GetHouseTypeName(AuctionHouseType houseType) { - static char const* names[MAX_AUCTION_HOUSE_TYPE] = { "Alliance", "Horde", "Neutral" }; + static char const* names[MAX_AUCTION_HOUSE_TYPE] = { "Neutral", "Alliance", "Horde" }; return names[houseType]; } diff --git a/src/server/game/AuctionHouseBot/AuctionHouseBot.h b/src/server/game/AuctionHouseBot/AuctionHouseBot.h index 964262579e6..d7570c37d44 100644 --- a/src/server/game/AuctionHouseBot/AuctionHouseBot.h +++ b/src/server/game/AuctionHouseBot/AuctionHouseBot.h @@ -39,9 +39,9 @@ enum AuctionQuality enum AuctionHouseType { - AUCTION_HOUSE_ALLIANCE = 0, - AUCTION_HOUSE_HORDE = 1, - AUCTION_HOUSE_NEUTRAL = 2 + AUCTION_HOUSE_NEUTRAL = 0, + AUCTION_HOUSE_ALLIANCE = 1, + AUCTION_HOUSE_HORDE = 2 }; #define MAX_AUCTION_HOUSE_TYPE 3 @@ -110,10 +110,21 @@ enum AuctionBotConfigUInt32Values CONFIG_AHBOT_CLASS_PERMANENT_PRICE_RATIO, CONFIG_AHBOT_CLASS_MISC_PRICE_RATIO, CONFIG_AHBOT_CLASS_GLYPH_PRICE_RATIO, - CONFIG_AHBOT_BUYER_CHANCE_RATIO_ALLIANCE, - CONFIG_AHBOT_BUYER_CHANCE_RATIO_HORDE, - CONFIG_AHBOT_BUYER_CHANCE_RATIO_NEUTRAL, CONFIG_AHBOT_BUYER_RECHECK_INTERVAL, + CONFIG_AHBOT_BUYER_BASEPRICE_GRAY, + CONFIG_AHBOT_BUYER_BASEPRICE_WHITE, + CONFIG_AHBOT_BUYER_BASEPRICE_GREEN, + CONFIG_AHBOT_BUYER_BASEPRICE_BLUE, + CONFIG_AHBOT_BUYER_BASEPRICE_PURPLE, + CONFIG_AHBOT_BUYER_BASEPRICE_ORANGE, + CONFIG_AHBOT_BUYER_BASEPRICE_YELLOW, + CONFIG_AHBOT_BUYER_CHANCEMULTIPLIER_GRAY, + CONFIG_AHBOT_BUYER_CHANCEMULTIPLIER_WHITE, + CONFIG_AHBOT_BUYER_CHANCEMULTIPLIER_GREEN, + CONFIG_AHBOT_BUYER_CHANCEMULTIPLIER_BLUE, + CONFIG_AHBOT_BUYER_CHANCEMULTIPLIER_PURPLE, + CONFIG_AHBOT_BUYER_CHANCEMULTIPLIER_ORANGE, + CONFIG_AHBOT_BUYER_CHANCEMULTIPLIER_YELLOW, CONFIG_AHBOT_CLASS_MISC_MOUNT_MIN_REQ_LEVEL, CONFIG_AHBOT_CLASS_MISC_MOUNT_MAX_REQ_LEVEL, CONFIG_AHBOT_CLASS_MISC_MOUNT_MIN_SKILL_RANK, @@ -143,7 +154,6 @@ enum AuctionBotConfigBoolValues CONFIG_AHBOT_BIND_USE, CONFIG_AHBOT_BIND_QUEST, CONFIG_AHBOT_BUYPRICE_SELLER, - CONFIG_AHBOT_BUYPRICE_BUYER, CONFIG_AHBOT_SELLER_ENABLED, CONFIG_AHBOT_BUYER_ENABLED, CONFIG_AHBOT_LOCKBOX_ENABLED, diff --git a/src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.cpp b/src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.cpp index af60316cbec..64463948574 100644 --- a/src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.cpp +++ b/src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.cpp @@ -20,7 +20,7 @@ #include "ItemPrototype.h" #include "AuctionHouseBotBuyer.h" -AuctionBotBuyer::AuctionBotBuyer(): _checkInterval(20) +AuctionBotBuyer::AuctionBotBuyer() : _checkInterval(20 * MINUTE) { // Define faction for our main data class. for (int i = 0; i < MAX_AUCTION_HOUSE_TYPE; ++i) @@ -48,391 +48,394 @@ bool AuctionBotBuyer::Initialize() if (!activeHouse) return false; - //load Check interval + // load Check interval _checkInterval = sAuctionBotConfig->GetConfig(CONFIG_AHBOT_BUYER_RECHECK_INTERVAL) * MINUTE; - TC_LOG_DEBUG("ahbot", "AHBot buyer interval between 2 check = %u", _checkInterval); + TC_LOG_DEBUG("ahbot", "AHBot buyer interval is %u minutes", _checkInterval / MINUTE); return true; } -void AuctionBotBuyer::LoadBuyerValues(BuyerConfiguration& config) +void AuctionBotBuyer::LoadConfig() { - uint32 factionChance; - - switch (config.GetHouseType()) + for (int i = 0; i < MAX_AUCTION_HOUSE_TYPE; ++i) { - case AUCTION_HOUSE_ALLIANCE: - config.BuyerPriceRatio = sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ALLIANCE_PRICE_RATIO) + 50; - factionChance = sAuctionBotConfig->GetConfig(CONFIG_AHBOT_BUYER_CHANCE_RATIO_ALLIANCE); - break; - case AUCTION_HOUSE_HORDE: - config.BuyerPriceRatio = sAuctionBotConfig->GetConfig(CONFIG_AHBOT_HORDE_PRICE_RATIO) + 50; - factionChance = sAuctionBotConfig->GetConfig(CONFIG_AHBOT_BUYER_CHANCE_RATIO_HORDE); - break; - default: - config.BuyerPriceRatio = sAuctionBotConfig->GetConfig(CONFIG_AHBOT_NEUTRAL_PRICE_RATIO) + 50; - factionChance = sAuctionBotConfig->GetConfig(CONFIG_AHBOT_BUYER_CHANCE_RATIO_NEUTRAL); - break; + _houseConfig[i].BuyerEnabled = sAuctionBotConfig->GetConfigBuyerEnabled(AuctionHouseType(i)); + if (_houseConfig[i].BuyerEnabled) + LoadBuyerValues(_houseConfig[i]); } +} + +void AuctionBotBuyer::LoadBuyerValues(BuyerConfiguration& /* config */) +{ - config.FactionChance = 5000 * factionChance; } -void AuctionBotBuyer::LoadConfig() +// Makes an AHbot buyer cycle for AH type if necessary +bool AuctionBotBuyer::Update(AuctionHouseType houseType) { - for (int i = 0; i < MAX_AUCTION_HOUSE_TYPE; ++i) + if (!sAuctionBotConfig->GetConfigBuyerEnabled(houseType)) + return false; + + TC_LOG_DEBUG("ahbot", "AHBot: %s buying ...", AuctionBotConfig::GetHouseTypeName(houseType)); + + BuyerConfiguration& config = _houseConfig[houseType]; + uint32 eligibleItems = GetItemInformation(config); + if (eligibleItems) { - _houseConfig[i].BuyerEnabled = sAuctionBotConfig->GetConfigBuyerEnabled(AuctionHouseType(i)); - if (_houseConfig[i].BuyerEnabled) - LoadBuyerValues(_houseConfig[i]); + // Prepare list of items to bid or buy - remove old items + PrepareListOfEntry(config); + // Process buying and bidding items + BuyAndBidItems(config); } + + return true; } -uint32 AuctionBotBuyer::GetBuyableEntry(BuyerConfiguration& config) +// Collects information about item counts and minimum prices to SameItemInfo and updates EligibleItems - a list with new items eligible for bot to buy and bid +// Returns count of items in AH that were eligible for being bought or bidded on by ahbot buyer (EligibleItems size) +uint32 AuctionBotBuyer::GetItemInformation(BuyerConfiguration& config) { config.SameItemInfo.clear(); - uint32 count = 0; time_t now = time(nullptr); + uint32 count = 0; AuctionHouseObject* house = sAuctionMgr->GetAuctionsMap(config.GetHouseType()); for (AuctionHouseObject::AuctionEntryMap::const_iterator itr = house->GetAuctionsBegin(); itr != house->GetAuctionsEnd(); ++itr) { AuctionEntry* entry = itr->second; Item* item = sAuctionMgr->GetAItem(entry->itemGUIDLow); - if (item) + if (!item) + continue; + + BuyerItemInfo& itemInfo = config.SameItemInfo[item->GetEntry()]; + + // Update item entry's count and total bid prices + // This can be used later to determine the prices and chances to bid + uint32 itemBidPrice = entry->startbid / item->GetCount(); + itemInfo.TotalBidPrice = itemInfo.TotalBidPrice + itemBidPrice; + itemInfo.BidItemCount++; + + // Set minimum bid price + if (!itemInfo.MinBidPrice) + itemInfo.MinBidPrice = itemBidPrice; + else + itemBidPrice = std::min(itemInfo.MinBidPrice, itemBidPrice); + + // Set minimum buyout price if item has buyout + if (entry->buyout) + { + // Update item entry's count and total buyout prices + // This can be used later to determine the prices and chances to buyout + uint32 itemBuyPrice = entry->buyout / item->GetCount(); + itemInfo.TotalBuyPrice = itemInfo.TotalBuyPrice + itemBuyPrice; + itemInfo.BuyItemCount++; + + if (!itemInfo.MinBuyPrice) + itemInfo.MinBuyPrice = itemBuyPrice; + else + itemInfo.MinBuyPrice = std::min(itemInfo.MinBuyPrice, itemBuyPrice); + } + + // Add/update to EligibleItems if: + // has a bid by player or + // has no bids and not owned by bot + if ((entry->bid && entry->bidder) || (entry->owner && !entry->bid)) { - ItemTemplate const * prototype = item->GetTemplate(); - if (prototype) - { - ++config.SameItemInfo[item->GetEntry()].ItemCount; // Structure constructor will make sure Element are correctly initialized if entry is created here. - config.SameItemInfo[item->GetEntry()].BuyPrice = config.SameItemInfo[item->GetEntry()].BuyPrice + (itr->second->buyout / item->GetCount()); - config.SameItemInfo[item->GetEntry()].BidPrice = config.SameItemInfo[item->GetEntry()].BidPrice + (itr->second->startbid / item->GetCount()); - if (itr->second->buyout != 0) - { - if (itr->second->buyout / item->GetCount() < config.SameItemInfo[item->GetEntry()].MinBuyPrice) - config.SameItemInfo[item->GetEntry()].MinBuyPrice = itr->second->buyout / item->GetCount(); - else if (config.SameItemInfo[item->GetEntry()].MinBuyPrice == 0) - config.SameItemInfo[item->GetEntry()].MinBuyPrice = itr->second->buyout / item->GetCount(); - } - if (itr->second->startbid / item->GetCount() < config.SameItemInfo[item->GetEntry()].MinBidPrice) - config.SameItemInfo[item->GetEntry()].MinBidPrice = itr->second->startbid / item->GetCount(); - else if (config.SameItemInfo[item->GetEntry()].MinBidPrice == 0) - config.SameItemInfo[item->GetEntry()].MinBidPrice = itr->second->startbid / item->GetCount(); - - if (!entry->owner) - { - - if (entry->bid != 0 && entry->bidder) // Add bid by player - { - config.CheckedEntry[entry->Id].LastExist = now; - config.CheckedEntry[entry->Id].AuctionId = entry->Id; - ++count; - } - } - else - { - if (entry->bid != 0) - { - if (entry->bidder) - { - config.CheckedEntry[entry->Id].LastExist = now; - config.CheckedEntry[entry->Id].AuctionId = entry->Id; - ++count; - } - } - else - { - config.CheckedEntry[entry->Id].LastExist = now; - config.CheckedEntry[entry->Id].AuctionId = entry->Id; - ++count; - } - } - } + config.EligibleItems[entry->Id].LastExist = now; + config.EligibleItems[entry->Id].AuctionId = entry->Id; + ++count; } } - TC_LOG_DEBUG("ahbot", "AHBot: %u items added to buyable vector for ah type: %u", count, config.GetHouseType()); + TC_LOG_DEBUG("ahbot", "AHBot: %u items added to buyable/biddable vector for ah type: %u", count, config.GetHouseType()); TC_LOG_DEBUG("ahbot", "AHBot: SameItemInfo size = %u", (uint32)config.SameItemInfo.size()); return count; } -void AuctionBotBuyer::PrepareListOfEntry(BuyerConfiguration& config) +// ahInfo can be NULL +bool AuctionBotBuyer::RollBuyChance(const BuyerItemInfo* ahInfo, const Item* item, const AuctionEntry* auction, uint32 /*bidPrice*/) { - time_t now = time(nullptr) - 5; + if (!auction->buyout) + return false; - for (CheckEntryMap::iterator itr = config.CheckedEntry.begin(); itr != config.CheckedEntry.end();) - { - if (itr->second.LastExist < (now - 5)) - config.CheckedEntry.erase(itr++); - else - ++itr; - } + uint32 itemBuyPrice = auction->buyout / item->GetCount(); + uint32 itemPrice = item->GetTemplate()->SellPrice ? item->GetTemplate()->SellPrice : GetVendorPrice(item->GetTemplate()->Quality); + // The AH cut needs to be added to the price, but we dont want a 100% chance to buy if the price is exactly AH default + itemPrice *= 1.4f; - TC_LOG_DEBUG("ahbot", "AHBot: CheckedEntry size = %u", (uint32)config.CheckedEntry.size()); -} + // This value is between 0 and 100 and is used directly as the chance to buy or bid + // Value equal or above 100 means 100% chance and value below 0 means 0% chance + float chance = 100 / sqrt(itemBuyPrice / float(itemPrice)); -bool AuctionBotBuyer::IsBuyableEntry(uint32 buyoutPrice, double inGameBuyPrice, uint32 maxBuyablePrice, uint32 minBuyPrice, uint32 maxChance, uint32 chanceRatio) -{ - double ratio = 0; - uint32 chance = 0; + // If a player has bidded on item, have fifth of normal chance + if (auction->bidder) + chance = chance / 5; - if (buyoutPrice <= minBuyPrice) + if (ahInfo) { - if (buyoutPrice <= maxBuyablePrice) - chance = maxChance; - else - { + float avgBuyPrice = ahInfo->TotalBuyPrice / float(ahInfo->BuyItemCount); - if (buyoutPrice > 0 && maxBuyablePrice > 0) - { - ratio = double(buyoutPrice) / double(maxBuyablePrice); - if (ratio < 10) - chance = maxChance - (ratio * maxChance / 10); - else - chance = 1; - } - } - } - else if (buyoutPrice <= inGameBuyPrice) - { - if (buyoutPrice <= maxBuyablePrice) - chance = maxChance / 5; - else - { + TC_LOG_DEBUG("ahbot", "AHBot: buyout average: %.1f items with buyout: %u", avgBuyPrice, ahInfo->BuyItemCount); - if (buyoutPrice > 0 && maxBuyablePrice > 0) - { - ratio = double(buyoutPrice) / double(maxBuyablePrice); - if (ratio < 10) - chance = (maxChance / 5) - (ratio * maxChance / 50); - else - chance = 1; - } - } - } - else if (buyoutPrice <= maxBuyablePrice) - chance = maxChance / 10; - else - { - if (buyoutPrice > 0 && maxBuyablePrice > 0) + // If there are more than 5 items on AH of this entry, try weigh in the average buyout price + if (ahInfo->BuyItemCount > 5) { - ratio = double(buyoutPrice) / double(maxBuyablePrice); - if (ratio < 10) - chance = (maxChance / 5) - (ratio* maxChance / 50); - else - chance = 0; + chance *= 1 / sqrt(itemBuyPrice / avgBuyPrice); } - else - chance = 0; } - if (urand(1, chanceRatio) <= chance) - { - TC_LOG_DEBUG("ahbot", "AHBot: WIN BUY! Chance = %u, num = %u.", chance, chanceRatio); - return true; - } - else - { - TC_LOG_DEBUG("ahbot", "AHBot: LOOSE BUY! Chance = %u, num = %u.", chance, chanceRatio); - return false; - } + // Add config weigh in for quality + chance *= GetChanceMultiplier(item->GetTemplate()->Quality) / 100.0f; + + float rand = frand(0, 100); + bool win = rand <= chance; + TC_LOG_DEBUG("ahbot", "AHBot: %s BUY! chance = %.2f, price = %u, buyprice = %u.", win ? "WIN" : "LOSE", chance, itemPrice, itemBuyPrice); + return win; } -bool AuctionBotBuyer::IsBidableEntry(uint32 bidPrice, double inGameBuyPrice, double maxBidablePrice, uint32 minBidPrice, uint32 maxChance, uint32 chanceRatio) +// ahInfo can be NULL +bool AuctionBotBuyer::RollBidChance(const BuyerItemInfo* ahInfo, const Item* item, const AuctionEntry* auction, uint32 bidPrice) { - double ratio = 0; - uint32 chance = 0; + uint32 itemBidPrice = bidPrice / item->GetCount(); + uint32 itemPrice = item->GetTemplate()->SellPrice ? item->GetTemplate()->SellPrice : GetVendorPrice(item->GetTemplate()->Quality); + // The AH cut needs to be added to the price, but we dont want a 100% chance to buy if the price is exactly AH default + itemPrice *= 1.4f; - if (bidPrice <= minBidPrice) - { - if (inGameBuyPrice != 0 && bidPrice < inGameBuyPrice - (inGameBuyPrice / 30)) - chance = maxChance; - else - { - if (bidPrice < maxBidablePrice) - { - ratio = maxBidablePrice / bidPrice; - if (ratio < 3) - chance = maxChance / 500 * ratio; - else - chance = maxChance / 500; - } - } - } - else if (bidPrice < (inGameBuyPrice - (inGameBuyPrice / 30))) - chance = (maxChance / 10); - else + // This value is between 0 and 100 and is used directly as the chance to buy or bid + // Value equal or above 100 means 100% chance and value below 0 means 0% chance + float chance = 100 / sqrt(itemBidPrice / float(itemPrice)); + + if (ahInfo) { - if (bidPrice < maxBidablePrice) + float avgBidPrice = ahInfo->TotalBidPrice / float(ahInfo->BidItemCount); + + TC_LOG_DEBUG("ahbot", "AHBot: Bid average: %.1f biddable item count: %u", avgBidPrice, ahInfo->BidItemCount); + + // If there are more than 5 items on AH of this entry, try weigh in the average bid price + if (ahInfo->BidItemCount >= 5) { - ratio = maxBidablePrice / bidPrice; - if (ratio < 4) - chance = maxChance / 1000 * ratio; - else - chance = maxChance / 1000; + chance *= 1 / sqrt(itemBidPrice / avgBidPrice); } } - if (urand(1, chanceRatio) <= chance) - { - TC_LOG_DEBUG("ahbot", "AHBot: WIN BID! Chance = %u, num = %u.", chance, chanceRatio); - return true; - } - else - { - TC_LOG_DEBUG("ahbot", "AHBot: LOOSE BID! Chance = %u, num = %u.", chance, chanceRatio); - return false; - } -} + // If a player has bidded on item, have fifth of normal chance + if (auction->bidder) + chance = chance / 5; -void AuctionBotBuyer::PlaceBidToEntry(AuctionEntry* auction, uint32 bidPrice) -{ - TC_LOG_DEBUG("ahbot", "AHBot: Bid placed to entry %u, %.2fg", auction->Id, float(bidPrice) / 10000.0f); - auction->bid = bidPrice; + // Add config weigh in for quality + chance *= GetChanceMultiplier(item->GetTemplate()->Quality) / 100.0f; + + float rand = frand(0, 100); + bool win = rand <= chance; + TC_LOG_DEBUG("ahbot", "AHBot: %s BID! chance = %.2f, price = %u, bidprice = %u.", win ? "WIN" : "LOSE", chance, itemPrice, itemBidPrice); + return win; } -void AuctionBotBuyer::BuyEntry(AuctionEntry* auction) +// Removes items from EligibleItems that we shouldnt buy or bid on +// The last existed time on them should be older than now +void AuctionBotBuyer::PrepareListOfEntry(BuyerConfiguration& config) { - TC_LOG_DEBUG("ahbot", "AHBot: Entry %u bought at %.2fg", auction->Id, float(auction->buyout) / 10000.0f); - auction->bid = auction->buyout; + // now - 5 seconds to leave out all old entries but keep the ones just updated a moment ago + time_t now = time(nullptr) - 5; + + for (CheckEntryMap::iterator itr = config.EligibleItems.begin(); itr != config.EligibleItems.end();) + { + if (itr->second.LastExist < now) + config.EligibleItems.erase(itr++); + else + ++itr; + } + + TC_LOG_DEBUG("ahbot", "AHBot: EligibleItems size = %u", (uint32)config.EligibleItems.size()); } -void AuctionBotBuyer::AddNewAuctionBuyerBotBid(BuyerConfiguration& config) +// Tries to bid and buy items based on their prices and chances set in configs +void AuctionBotBuyer::BuyAndBidItems(BuyerConfiguration& config) { + time_t now = time(nullptr); AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(config.GetHouseType()); + CheckEntryMap& items = config.EligibleItems; - PrepareListOfEntry(config); - - time_t now = time(nullptr); - uint32 buyCycles; - if (config.CheckedEntry.size() > sAuctionBotConfig->GetItemPerCycleBoost()) + // Max amount of items to buy or bid + uint32 cycles = sAuctionBotConfig->GetItemPerCycleNormal(); + if (items.size() > sAuctionBotConfig->GetItemPerCycleBoost()) { - buyCycles = sAuctionBotConfig->GetItemPerCycleBoost(); + // set more cycles if there is a huge influx of items + cycles = sAuctionBotConfig->GetItemPerCycleBoost(); TC_LOG_DEBUG("ahbot", "AHBot: Boost value used for Buyer! (if this happens often adjust both ItemsPerCycle in worldserver.conf)"); } - else - buyCycles = sAuctionBotConfig->GetItemPerCycleNormal(); - for (CheckEntryMap::iterator itr = config.CheckedEntry.begin(); itr != config.CheckedEntry.end();) + // Process items eligible to be bidded or bought + CheckEntryMap::iterator itr = items.begin(); + while (cycles && itr != items.end()) { AuctionEntry* auction = auctionHouse->GetAuction(itr->second.AuctionId); - if (!auction) // is auction not active now + if (!auction) { - TC_LOG_DEBUG("ahbot", "AHBot: Entry %u doesn't exists, perhaps bought already?", - itr->second.AuctionId); - - config.CheckedEntry.erase(itr++); + TC_LOG_DEBUG("ahbot", "AHBot: Entry %u doesn't exists, perhaps bought already?", itr->second.AuctionId); + items.erase(itr++); continue; } - if (itr->second.LastChecked != 0 && (now - itr->second.LastChecked) <= _checkInterval) + // Check if the item has been checked once before + // If it has been checked and it was recently, skip it + if (itr->second.LastChecked && (now - itr->second.LastChecked) <= _checkInterval) { TC_LOG_DEBUG("ahbot", "AHBot: In time interval wait for entry %u!", auction->Id); ++itr; continue; } - if (buyCycles == 0) - break; - - uint32 maxChance = 5000; - Item* item = sAuctionMgr->GetAItem(auction->itemGUIDLow); - if (!item) // auction item not accessible, possible auction in payment pending mode + if (!item) { - config.CheckedEntry.erase(itr++); + // auction item not accessible, possible auction in payment pending mode + items.erase(itr++); continue; } - ItemTemplate const* prototype = item->GetTemplate(); - - uint32 basePrice = sAuctionBotConfig->GetConfig(CONFIG_AHBOT_BUYPRICE_BUYER) ? prototype->BuyPrice : prototype->SellPrice; - basePrice *= item->GetCount(); - - uint32 maxBuyablePrice = (basePrice * config.BuyerPriceRatio) / 100; - BuyerItemInfoMap::iterator sameItemItr = config.SameItemInfo.find(item->GetEntry()); - uint32 buyoutPrice = auction->buyout / item->GetCount(); - + // price to bid if bidding uint32 bidPrice; - uint32 bidPriceByItem; - uint32 minBidPrice; - uint32 minBuyPrice; if (auction->bid >= auction->startbid) { - bidPrice = auction->GetAuctionOutBid(); - bidPriceByItem = auction->bid / item->GetCount(); + // get bid price to outbid previous bidder + bidPrice = auction->bid + auction->GetAuctionOutBid(); } else { + // no previous bidders - use starting bid bidPrice = auction->startbid; - bidPriceByItem = auction->startbid / item->GetCount(); } - double inGameBuyPrice; - double inGameBidPrice; - if (sameItemItr == config.SameItemInfo.end()) - { - inGameBuyPrice = 0; - inGameBidPrice = 0; - minBidPrice = 0; - minBuyPrice = 0; - } - else - { - if (sameItemItr->second.ItemCount == 1) - maxBuyablePrice = maxBuyablePrice * 5; // if only one item exist can be bought if the price is high too. - inGameBuyPrice = sameItemItr->second.BuyPrice / sameItemItr->second.ItemCount; - inGameBidPrice = sameItemItr->second.BidPrice / sameItemItr->second.ItemCount; - minBidPrice = sameItemItr->second.MinBidPrice; - minBuyPrice = sameItemItr->second.MinBuyPrice; - } + const BuyerItemInfo* ahInfo = nullptr; + BuyerItemInfoMap::const_iterator sameItemItr = config.SameItemInfo.find(item->GetEntry()); + if (sameItemItr != config.SameItemInfo.end()) + ahInfo = &sameItemItr->second; - uint32 maxBidablePrice = maxBuyablePrice - (maxBuyablePrice / 30); // Max Bidable price defined to 70% of max buyable price + TC_LOG_DEBUG("ahbot", "AHBot: Rolling for AHentry %u:", auction->Id); - TC_LOG_DEBUG("ahbot", "AHBot: Auction added with data:"); - TC_LOG_DEBUG("ahbot", "AHBot: MaxPrice of Entry %u is %.1fg.", itr->second.AuctionId, double(maxBuyablePrice) / 10000.0); - TC_LOG_DEBUG("ahbot", "AHBot: GamePrice buy=%.1fg, bid=%.1fg.", inGameBuyPrice / 10000, inGameBidPrice / 10000); - TC_LOG_DEBUG("ahbot", "AHBot: Minimal price see in AH Buy=%ug, Bid=%ug.", - minBuyPrice / 10000, minBidPrice / 10000); - TC_LOG_DEBUG("ahbot", "AHBot: Actual Entry price, Buy=%ug, Bid=%ug.", buyoutPrice / 10000, bidPrice / 10000); + // Roll buy and bid chances + bool successBuy = RollBuyChance(ahInfo, item, auction, bidPrice); + bool successBid = RollBidChance(ahInfo, item, auction, bidPrice); - if (!auction->owner) // Original auction owner - maxChance = maxChance / 5; // if Owner is AHBot this mean player placed bid on this auction. We divide by 5 chance for AhBuyer to place bid on it. (This make more challenge than ignore entry) - if (auction->buyout != 0) // Is the item directly buyable? - { - if (IsBuyableEntry(buyoutPrice, inGameBuyPrice, maxBuyablePrice, minBuyPrice, maxChance, config.FactionChance)) - { - if (IsBidableEntry(bidPriceByItem, inGameBuyPrice, maxBidablePrice, minBidPrice, maxChance / 2, config.FactionChance)) - { - if (urand(0, 5) == 0) - PlaceBidToEntry(auction, bidPrice); - else - BuyEntry(auction); - } - else - BuyEntry(auction); - } - else if (IsBidableEntry(bidPriceByItem, inGameBuyPrice, maxBidablePrice, minBidPrice, maxChance / 2, config.FactionChance)) - PlaceBidToEntry(auction, bidPrice); - } - else if (IsBidableEntry(bidPriceByItem, inGameBuyPrice, maxBidablePrice, minBidPrice, maxChance, config.FactionChance)) - PlaceBidToEntry(auction, bidPrice); + // If roll bidding succesfully and bid price is above buyout -> buyout + // If roll for buying was successful but not for bid, buyout directly + // If roll bidding was also successful, buy the entry with 20% chance + // - Better bid than buy since the item is bought by bot if no player bids after + // Otherwise bid if roll for bid was successful + if ((auction->buyout && successBid && bidPrice >= auction->buyout) || + (successBuy && (!successBid || urand(1, 5) == 1))) + BuyEntry(auction, auctionHouse); // buyout + else if (successBid) + PlaceBidToEntry(auction, bidPrice); // bid itr->second.LastChecked = now; - --buyCycles; - + --cycles; ++itr; } + + // Clear not needed entries + config.SameItemInfo.clear(); } -bool AuctionBotBuyer::Update(AuctionHouseType houseType) +uint32 AuctionBotBuyer::GetVendorPrice(uint32 quality) { - if (sAuctionBotConfig->GetConfigBuyerEnabled(houseType)) + switch (quality) { - TC_LOG_DEBUG("ahbot", "AHBot: %s buying ...", AuctionBotConfig::GetHouseTypeName(houseType)); - if (GetBuyableEntry(_houseConfig[houseType]) > 0) - AddNewAuctionBuyerBotBid(_houseConfig[houseType]); - return true; + case ITEM_QUALITY_POOR: + return sAuctionBotConfig->GetConfig(CONFIG_AHBOT_BUYER_BASEPRICE_GRAY); + case ITEM_QUALITY_NORMAL: + return sAuctionBotConfig->GetConfig(CONFIG_AHBOT_BUYER_BASEPRICE_WHITE); + case ITEM_QUALITY_UNCOMMON: + return sAuctionBotConfig->GetConfig(CONFIG_AHBOT_BUYER_BASEPRICE_GREEN); + case ITEM_QUALITY_RARE: + return sAuctionBotConfig->GetConfig(CONFIG_AHBOT_BUYER_BASEPRICE_BLUE); + case ITEM_QUALITY_EPIC: + return sAuctionBotConfig->GetConfig(CONFIG_AHBOT_BUYER_BASEPRICE_PURPLE); + case ITEM_QUALITY_LEGENDARY: + return sAuctionBotConfig->GetConfig(CONFIG_AHBOT_BUYER_BASEPRICE_ORANGE); + case ITEM_QUALITY_ARTIFACT: + return sAuctionBotConfig->GetConfig(CONFIG_AHBOT_BUYER_BASEPRICE_YELLOW); + default: + return 1 * SILVER; } +} + +uint32 AuctionBotBuyer::GetChanceMultiplier(uint32 quality) +{ + switch (quality) + { + case ITEM_QUALITY_POOR: + return sAuctionBotConfig->GetConfig(CONFIG_AHBOT_BUYER_CHANCEMULTIPLIER_GRAY); + case ITEM_QUALITY_NORMAL: + return sAuctionBotConfig->GetConfig(CONFIG_AHBOT_BUYER_CHANCEMULTIPLIER_WHITE); + case ITEM_QUALITY_UNCOMMON: + return sAuctionBotConfig->GetConfig(CONFIG_AHBOT_BUYER_CHANCEMULTIPLIER_GREEN); + case ITEM_QUALITY_RARE: + return sAuctionBotConfig->GetConfig(CONFIG_AHBOT_BUYER_CHANCEMULTIPLIER_BLUE); + case ITEM_QUALITY_EPIC: + return sAuctionBotConfig->GetConfig(CONFIG_AHBOT_BUYER_CHANCEMULTIPLIER_PURPLE); + case ITEM_QUALITY_LEGENDARY: + return sAuctionBotConfig->GetConfig(CONFIG_AHBOT_BUYER_CHANCEMULTIPLIER_ORANGE); + case ITEM_QUALITY_ARTIFACT: + return sAuctionBotConfig->GetConfig(CONFIG_AHBOT_BUYER_CHANCEMULTIPLIER_YELLOW); + default: + return 100; + } +} + +// Buys the auction and does necessary actions to complete the buyout +void AuctionBotBuyer::BuyEntry(AuctionEntry* auction, AuctionHouseObject* auctionHouse) +{ + TC_LOG_DEBUG("ahbot", "AHBot: Entry %u bought at %.2fg", auction->Id, float(auction->buyout) / GOLD); + + SQLTransaction trans = CharacterDatabase.BeginTransaction(); + + // Send mail to previous bidder if any + if (auction->bidder) + sAuctionMgr->SendAuctionOutbiddedMail(auction, auction->buyout, NULL, trans); + + // Set bot as bidder and set new bid amount + auction->bidder = 0; + auction->bid = auction->buyout; + + // Mails must be under transaction control too to prevent data loss + sAuctionMgr->SendAuctionSalePendingMail(auction, trans); + sAuctionMgr->SendAuctionSuccessfulMail(auction, trans); + sAuctionMgr->SendAuctionWonMail(auction, trans); + + // Delete auction from DB + auction->DeleteFromDB(trans); + + // Remove auction item and auction from memory + sAuctionMgr->RemoveAItem(auction->itemGUIDLow); + auctionHouse->RemoveAuction(auction); + + // Run SQLs + CharacterDatabase.CommitTransaction(trans); +} + +// Bids on the auction and does the necessary actions for bidding +void AuctionBotBuyer::PlaceBidToEntry(AuctionEntry* auction, uint32 bidPrice) +{ + TC_LOG_DEBUG("ahbot", "AHBot: Bid placed to entry %u, %.2fg", auction->Id, float(bidPrice) / GOLD); + + SQLTransaction trans = CharacterDatabase.BeginTransaction(); + + // Send mail to previous bidder if any + if (auction->bidder) + sAuctionMgr->SendAuctionOutbiddedMail(auction, bidPrice, NULL, trans); + + // Set bot as bidder and set new bid amount + auction->bidder = 0; + auction->bid = bidPrice; + + // Update auction to DB + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_AUCTION_BID); + stmt->setUInt32(0, auction->bidder); + stmt->setUInt32(1, auction->bid); + stmt->setUInt32(2, auction->Id); + trans->Append(stmt); - return false; + // Run SQLs + CharacterDatabase.CommitTransaction(trans); } diff --git a/src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.h b/src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.h index bd8d6e46f0e..c030731cb40 100644 --- a/src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.h +++ b/src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.h @@ -24,7 +24,7 @@ struct BuyerAuctionEval { - BuyerAuctionEval(): AuctionId(0), LastChecked(0), LastExist(0) {} + BuyerAuctionEval() : AuctionId(0), LastChecked(0), LastExist(0) { } uint32 AuctionId; time_t LastChecked; @@ -33,13 +33,14 @@ struct BuyerAuctionEval struct BuyerItemInfo { - BuyerItemInfo(): ItemCount(0), BuyPrice(0), BidPrice(0), MinBuyPrice(0), MinBidPrice(0) {} + BuyerItemInfo() : BidItemCount(0), BuyItemCount(0), MinBuyPrice(0), MinBidPrice(0), TotalBuyPrice(0), TotalBidPrice(0) { } - uint32 ItemCount; - double BuyPrice; - double BidPrice; + uint32 BidItemCount; + uint32 BuyItemCount; uint32 MinBuyPrice; uint32 MinBidPrice; + double TotalBuyPrice; + double TotalBidPrice; }; typedef std::map<uint32, BuyerItemInfo> BuyerItemInfoMap; @@ -47,7 +48,7 @@ typedef std::map<uint32, BuyerAuctionEval> CheckEntryMap; struct BuyerConfiguration { - BuyerConfiguration(): FactionChance(3), BuyerEnabled(false), BuyerPriceRatio(100), _houseType(AUCTION_HOUSE_NEUTRAL) {} + BuyerConfiguration() : BuyerEnabled(false), _houseType(AUCTION_HOUSE_NEUTRAL) { } void Initialize(AuctionHouseType houseType) { @@ -57,10 +58,8 @@ struct BuyerConfiguration AuctionHouseType GetHouseType() const { return _houseType; } BuyerItemInfoMap SameItemInfo; - CheckEntryMap CheckedEntry; - uint32 FactionChance; + CheckEntryMap EligibleItems; bool BuyerEnabled; - uint32 BuyerPriceRatio; private: AuctionHouseType _houseType; @@ -78,19 +77,23 @@ public: bool Update(AuctionHouseType houseType) override; void LoadConfig(); - void AddNewAuctionBuyerBotBid(BuyerConfiguration& config); + void BuyAndBidItems(BuyerConfiguration& config); private: uint32 _checkInterval; BuyerConfiguration _houseConfig[MAX_AUCTION_HOUSE_TYPE]; void LoadBuyerValues(BuyerConfiguration& config); - bool IsBuyableEntry(uint32 buyoutPrice, double inGameBuyPrice, uint32 maxBuyablePrice, uint32 minBuyPrice, uint32 maxChance, uint32 chanceRatio); - bool IsBidableEntry(uint32 bidPrice, double inGameBuyPrice, double maxBidablePrice, uint32 minBidPrice, uint32 maxChance, uint32 chanceRatio); + + // ahInfo can be NULL + bool RollBuyChance(const BuyerItemInfo* ahInfo, const Item* item, const AuctionEntry* auction, uint32 bidPrice); + bool RollBidChance(const BuyerItemInfo* ahInfo, const Item* item, const AuctionEntry* auction, uint32 bidPrice); void PlaceBidToEntry(AuctionEntry* auction, uint32 bidPrice); - void BuyEntry(AuctionEntry* auction); + void BuyEntry(AuctionEntry* auction, AuctionHouseObject* auctionHouse); void PrepareListOfEntry(BuyerConfiguration& config); - uint32 GetBuyableEntry(BuyerConfiguration& config); + uint32 GetItemInformation(BuyerConfiguration& config); + uint32 GetVendorPrice(uint32 quality); + uint32 GetChanceMultiplier(uint32 quality); }; #endif diff --git a/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp b/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp index dff4077d569..733bd5e9ec8 100644 --- a/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp +++ b/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp @@ -44,14 +44,14 @@ bool AuctionBotSeller::Initialize() { std::stringstream includeStream(sAuctionBotConfig->GetAHBotIncludes()); std::string temp; - while (getline(includeStream, temp, ',')) + while (std::getline(includeStream, temp, ',')) includeItems.push_back(atoi(temp.c_str())); } { std::stringstream excludeStream(sAuctionBotConfig->GetAHBotExcludes()); std::string temp; - while (getline(excludeStream, temp, ',')) + while (std::getline(excludeStream, temp, ',')) excludeItems.push_back(atoi(temp.c_str())); } diff --git a/src/server/game/Battlegrounds/ArenaTeam.cpp b/src/server/game/Battlegrounds/ArenaTeam.cpp index 7ed05205f39..3b7e9ac3f33 100644 --- a/src/server/game/Battlegrounds/ArenaTeam.cpp +++ b/src/server/game/Battlegrounds/ArenaTeam.cpp @@ -179,7 +179,7 @@ bool ArenaTeam::AddMember(ObjectGuid playerGuid) player->SetArenaTeamInfoField(GetSlot(), ARENA_TEAM_MEMBER, 1); } - TC_LOG_INFO("bg.arena", "Player: %s [%s] joined arena team type: %u [Id: %u, Name: %s].", playerName.c_str(), playerGuid.ToString().c_str(), GetType(), GetId(), GetName().c_str()); + TC_LOG_DEBUG("bg.arena", "Player: %s [%s] joined arena team type: %u [Id: %u, Name: %s].", playerName.c_str(), playerGuid.ToString().c_str(), GetType(), GetId(), GetName().c_str()); return true; } diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp index e091df160a3..03b4fe3dea6 100644 --- a/src/server/game/Battlegrounds/Battleground.cpp +++ b/src/server/game/Battlegrounds/Battleground.cpp @@ -510,7 +510,7 @@ inline void Battleground::_ProcessJoin(uint32 diff) if (!aura->IsPermanent() && aura->GetDuration() <= 30*IN_MILLISECONDS && aurApp->IsPositive() - && (!(aura->GetSpellInfo()->Attributes & SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY)) + && (!aura->GetSpellInfo()->HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY)) && (!aura->HasEffectType(SPELL_AURA_MOD_INVISIBILITY))) player->RemoveAura(iter); else diff --git a/src/server/game/CMakeLists.txt b/src/server/game/CMakeLists.txt index eb8627efa44..2f7bc4a9c69 100644 --- a/src/server/game/CMakeLists.txt +++ b/src/server/game/CMakeLists.txt @@ -110,6 +110,7 @@ include_directories( ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Recast/Include ${CMAKE_SOURCE_DIR}/dep/g3dlite/include ${CMAKE_SOURCE_DIR}/dep/SFMT + ${CMAKE_SOURCE_DIR}/dep/cppformat ${CMAKE_SOURCE_DIR}/dep/zlib ${CMAKE_SOURCE_DIR}/src/server/collision ${CMAKE_SOURCE_DIR}/src/server/collision/Management diff --git a/src/server/game/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp index 44df1097dd4..3195de9b6bd 100644 --- a/src/server/game/Chat/Chat.cpp +++ b/src/server/game/Chat/Chat.cpp @@ -104,17 +104,6 @@ ChatCommand* ChatHandler::getCommandTable() return commandTableCache; } -std::string ChatHandler::PGetParseString(uint32 entry, ...) const -{ - const char *format = GetTrinityString(entry); - char str[1024]; - va_list ap; - va_start(ap, entry); - vsnprintf(str, 1024, format, ap); - va_end(ap); - return std::string(str); -} - char const* ChatHandler::GetTrinityString(uint32 entry) const { return m_session->GetTrinityString(entry); @@ -258,27 +247,6 @@ void ChatHandler::SendSysMessage(uint32 entry) SendSysMessage(GetTrinityString(entry)); } -void ChatHandler::PSendSysMessage(uint32 entry, ...) -{ - const char *format = GetTrinityString(entry); - va_list ap; - char str [2048]; - va_start(ap, entry); - vsnprintf(str, 2048, format, ap); - va_end(ap); - SendSysMessage(str); -} - -void ChatHandler::PSendSysMessage(const char *format, ...) -{ - va_list ap; - char str [2048]; - va_start(ap, format); - vsnprintf(str, 2048, format, ap); - va_end(ap); - SendSysMessage(str); -} - bool ChatHandler::ExecuteCommandInTable(ChatCommand* table, const char* text, std::string const& fullcmd) { char const* oldtext = text; diff --git a/src/server/game/Chat/Chat.h b/src/server/game/Chat/Chat.h index d712740bd7d..ba59245cdaa 100644 --- a/src/server/game/Chat/Chat.h +++ b/src/server/game/Chat/Chat.h @@ -20,6 +20,7 @@ #define TRINITYCORE_CHAT_H #include "SharedDefines.h" +#include "StringFormat.h" #include "WorldSession.h" #include "RBAC.h" @@ -68,9 +69,24 @@ class ChatHandler virtual void SendSysMessage(char const* str); void SendSysMessage(uint32 entry); - void PSendSysMessage(char const* format, ...) ATTR_PRINTF(2, 3); - void PSendSysMessage(uint32 entry, ...); - std::string PGetParseString(uint32 entry, ...) const; + + template<typename... Args> + void PSendSysMessage(const char* fmt, Args const&... args) + { + SendSysMessage(Trinity::StringFormat(fmt, args...).c_str()); + } + + template<typename... Args> + void PSendSysMessage(uint32 entry, Args const&... args) + { + SendSysMessage(PGetParseString(entry, args...).c_str()); + } + + template<typename... Args> + std::string PGetParseString(uint32 entry, Args const&... args) const + { + return Trinity::StringFormat(GetTrinityString(entry), args...); + } bool ParseCommands(const char* text); diff --git a/src/server/game/Chat/ChatLink.cpp b/src/server/game/Chat/ChatLink.cpp index 1eb330cc9e1..732a770645b 100644 --- a/src/server/game/Chat/ChatLink.cpp +++ b/src/server/game/Chat/ChatLink.cpp @@ -285,7 +285,7 @@ bool SpellChatLink::ValidateName(char* buffer, const char* context) ChatLink::ValidateName(buffer, context); // spells with that flag have a prefix of "$PROFESSION: " - if (_spell->Attributes & SPELL_ATTR0_TRADESPELL) + if (_spell->HasAttribute(SPELL_ATTR0_TRADESPELL)) { SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(_spell->Id); if (bounds.first == bounds.second) diff --git a/src/server/game/Combat/ThreatManager.cpp b/src/server/game/Combat/ThreatManager.cpp index 43057409b9b..cd108e54b9f 100644 --- a/src/server/game/Combat/ThreatManager.cpp +++ b/src/server/game/Combat/ThreatManager.cpp @@ -79,7 +79,7 @@ bool ThreatCalcHelper::isValidProcess(Unit* hatedUnit, Unit* hatingUnit, SpellIn return false; // spell not causing threat - if (threatSpell && threatSpell->AttributesEx & SPELL_ATTR1_NO_THREAT) + if (threatSpell && threatSpell->HasAttribute(SPELL_ATTR1_NO_THREAT)) return false; ASSERT(hatingUnit->GetTypeId() == TYPEID_UNIT); diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp index eb3d1708675..8400e4cee00 100644 --- a/src/server/game/Conditions/ConditionMgr.cpp +++ b/src/server/game/Conditions/ConditionMgr.cpp @@ -99,7 +99,8 @@ ConditionMgr::ConditionTypeInfo const ConditionMgr::StaticConditionTypeData[COND { "Distance", true, true, true }, { "Alive", false, false, false }, { "Health Value", true, true, false }, - { "Health Pct", true, true, false } + { "Health Pct", true, true, false }, + { "Realm Achievement", true, false, false } }; // Checks if object meets the condition @@ -416,6 +417,13 @@ bool Condition::Meets(ConditionSourceInfo& sourceInfo) condMeets = creature->GetCreatureTemplate()->type == ConditionValue1; break; } + case CONDITION_REALM_ACHIEVEMENT: + { + AchievementEntry const* achievement = sAchievementMgr->GetAchievement(ConditionValue1); + if (achievement && sAchievementMgr->IsRealmCompleted(achievement, std::numeric_limits<uint32>::max())) + condMeets = true; + break; + } default: condMeets = false; break; @@ -580,6 +588,9 @@ uint32 Condition::GetSearcherTypeMaskForCondition() case CONDITION_CREATURE_TYPE: mask |= GRID_MAP_TYPE_MASK_CREATURE; break; + case CONDITION_REALM_ACHIEVEMENT: + mask |= GRID_MAP_TYPE_MASK_ALL; + break; default: ASSERT(false && "Condition::GetSearcherTypeMaskForCondition - missing condition handling!"); break; @@ -2056,6 +2067,16 @@ bool ConditionMgr::isConditionTypeValid(Condition* cond) case CONDITION_PHASEMASK: case CONDITION_ALIVE: break; + case CONDITION_REALM_ACHIEVEMENT: + { + AchievementEntry const* achievement = sAchievementMgr->GetAchievement(cond->ConditionValue1); + if (!achievement) + { + TC_LOG_ERROR("sql.sql", "%s has non existing realm first achivement id (%u), skipped.", cond->ToString(true).c_str(), cond->ConditionValue1); + return false; + } + break; + } default: break; } diff --git a/src/server/game/Conditions/ConditionMgr.h b/src/server/game/Conditions/ConditionMgr.h index 4ca27f0d986..1fdd011633e 100644 --- a/src/server/game/Conditions/ConditionMgr.h +++ b/src/server/game/Conditions/ConditionMgr.h @@ -72,7 +72,8 @@ enum ConditionTypes CONDITION_ALIVE = 36, // 0 0 0 true if unit is alive CONDITION_HP_VAL = 37, // hpVal ComparisonType 0 true if unit's hp matches given value CONDITION_HP_PCT = 38, // hpPct ComparisonType 0 true if unit's hp matches given pct - CONDITION_MAX = 39 // MAX + CONDITION_REALM_ACHIEVEMENT = 39, // achievement_id 0 0 true if realm achievement is complete + CONDITION_MAX = 40 // MAX }; /*! Documentation on implementing a new ConditionSourceType: diff --git a/src/server/game/DungeonFinding/LFG.cpp b/src/server/game/DungeonFinding/LFG.cpp index ab06eff7148..8ec638128d8 100644 --- a/src/server/game/DungeonFinding/LFG.cpp +++ b/src/server/game/DungeonFinding/LFG.cpp @@ -91,9 +91,6 @@ std::string GetStateString(LfgState state) case LFG_STATE_DUNGEON: entry = LANG_LFG_STATE_DUNGEON; break; - case LFG_STATE_BOOT: - entry = LANG_LFG_STATE_BOOT; - break; case LFG_STATE_FINISHED_DUNGEON: entry = LANG_LFG_STATE_FINISHED_DUNGEON; break; diff --git a/src/server/game/DungeonFinding/LFG.h b/src/server/game/DungeonFinding/LFG.h index 6145a2324b1..84a25ee49ad 100644 --- a/src/server/game/DungeonFinding/LFG.h +++ b/src/server/game/DungeonFinding/LFG.h @@ -64,8 +64,8 @@ enum LfgState LFG_STATE_ROLECHECK, // Rolecheck active LFG_STATE_QUEUED, // Queued LFG_STATE_PROPOSAL, // Proposal active - LFG_STATE_BOOT, // Vote kick active - LFG_STATE_DUNGEON, // In LFG Group, in a Dungeon + //LFG_STATE_BOOT, // Vote kick active + LFG_STATE_DUNGEON = 5, // In LFG Group, in a Dungeon LFG_STATE_FINISHED_DUNGEON, // In LFG Group, in a finished Dungeon LFG_STATE_RAIDBROWSER // Using Raid finder }; diff --git a/src/server/game/DungeonFinding/LFGGroupData.cpp b/src/server/game/DungeonFinding/LFGGroupData.cpp index 2ab1e0b1e7e..aa6916a39a4 100644 --- a/src/server/game/DungeonFinding/LFGGroupData.cpp +++ b/src/server/game/DungeonFinding/LFGGroupData.cpp @@ -22,7 +22,7 @@ namespace lfg { LfgGroupData::LfgGroupData(): m_State(LFG_STATE_NONE), m_OldState(LFG_STATE_NONE), - m_Leader(), m_Dungeon(0), m_KicksLeft(LFG_GROUP_MAX_KICKS) + m_Leader(), m_Dungeon(0), m_KicksLeft(LFG_GROUP_MAX_KICKS), m_VoteKickActive(false) { } LfgGroupData::~LfgGroupData() @@ -126,4 +126,14 @@ uint8 LfgGroupData::GetKicksLeft() const return m_KicksLeft; } +void LfgGroupData::SetVoteKick(bool active) +{ + m_VoteKickActive = active; +} + +bool LfgGroupData::IsVoteKickActive() const +{ + return m_VoteKickActive; +} + } // namespace lfg diff --git a/src/server/game/DungeonFinding/LFGGroupData.h b/src/server/game/DungeonFinding/LFGGroupData.h index 8d8f1dc0f3d..b573e7c309e 100644 --- a/src/server/game/DungeonFinding/LFGGroupData.h +++ b/src/server/game/DungeonFinding/LFGGroupData.h @@ -66,6 +66,9 @@ class LfgGroupData // VoteKick uint8 GetKicksLeft() const; + void SetVoteKick(bool active); + bool IsVoteKickActive() const; + private: // General LfgState m_State; ///< State if group in LFG @@ -76,6 +79,7 @@ class LfgGroupData uint32 m_Dungeon; ///< Dungeon entry // Vote Kick uint8 m_KicksLeft; ///< Number of kicks left + bool m_VoteKickActive; }; } // namespace lfg diff --git a/src/server/game/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp index d670e0a2949..93ebeca9780 100644 --- a/src/server/game/DungeonFinding/LFGMgr.cpp +++ b/src/server/game/DungeonFinding/LFGMgr.cpp @@ -311,9 +311,8 @@ void LFGMgr::Update(uint32 diff) ObjectGuid pguid = itVotes->first; if (pguid != boot.victim) SendLfgBootProposalUpdate(pguid, boot); - SetState(pguid, LFG_STATE_DUNGEON); } - SetState(itBoot->first, LFG_STATE_DUNGEON); + SetVoteKick(itBoot->first, false); BootsStore.erase(itBoot); } } @@ -643,7 +642,6 @@ void LFGMgr::LeaveLfg(ObjectGuid guid) break; case LFG_STATE_DUNGEON: case LFG_STATE_FINISHED_DUNGEON: - case LFG_STATE_BOOT: if (guid != gguid) // Player SetState(guid, LFG_STATE_NONE); break; @@ -755,7 +753,7 @@ void LFGMgr::GetCompatibleDungeons(LfgDungeonSet& dungeons, GuidSet const& playe { ObjectGuid guid = (*it); LfgLockMap const& cachedLockMap = GetLockedDungeons(guid); - Player* player = ObjectAccessor::FindPlayer(guid); + Player* player = ObjectAccessor::FindConnectedPlayer(guid); for (LfgLockMap::const_iterator it2 = cachedLockMap.begin(); it2 != cachedLockMap.end() && !dungeons.empty(); ++it2) { uint32 dungeonId = (it2->first & 0x00FFFFFF); // Compare dungeon ids @@ -893,7 +891,7 @@ void LFGMgr::MakeNewGroup(LfgProposal const& proposal) for (GuidList::const_iterator it = players.begin(); it != players.end(); ++it) { ObjectGuid pguid = (*it); - Player* player = ObjectAccessor::FindPlayer(pguid); + Player* player = ObjectAccessor::FindConnectedPlayer(pguid); if (!player) continue; @@ -1156,7 +1154,7 @@ void LFGMgr::RemoveProposal(LfgProposalContainer::iterator itProposal, LfgUpdate */ void LFGMgr::InitBoot(ObjectGuid gguid, ObjectGuid kicker, ObjectGuid victim, std::string const& reason) { - SetState(gguid, LFG_STATE_BOOT); + SetVoteKick(gguid, true); LfgPlayerBoot& boot = BootsStore[gguid]; boot.inProgress = true; @@ -1170,7 +1168,6 @@ void LFGMgr::InitBoot(ObjectGuid gguid, ObjectGuid kicker, ObjectGuid victim, st for (GuidSet::const_iterator itr = players.begin(); itr != players.end(); ++itr) { ObjectGuid guid = (*itr); - SetState(guid, LFG_STATE_BOOT); boot.votes[guid] = LFG_ANSWER_PENDING; } @@ -1227,13 +1224,10 @@ void LFGMgr::UpdateBoot(ObjectGuid guid, bool accept) { ObjectGuid pguid = itVotes->first; if (pguid != boot.victim) - { - SetState(pguid, LFG_STATE_DUNGEON); SendLfgBootProposalUpdate(pguid, boot); - } } - SetState(gguid, LFG_STATE_DUNGEON); + SetVoteKick(gguid, false); if (agreeNum == LFG_GROUP_KICK_VOTES_NEEDED) // Vote passed - Kick player { if (Group* group = sGroupMgr->GetGroupByGUID(gguid.GetCounter())) @@ -1495,12 +1489,12 @@ LfgState LFGMgr::GetState(ObjectGuid guid) if (guid.IsGroup()) { state = GroupsStore[guid].GetState(); - TC_LOG_TRACE("lfg.data.group.state.get", "Group: %s, State: %u", guid.ToString().c_str(), state); + TC_LOG_TRACE("lfg.data.group.state.get", "Group: %s, State: %s", guid.ToString().c_str(), GetStateString(state).c_str()); } else { state = PlayersStore[guid].GetState(); - TC_LOG_TRACE("lfg.data.player.state.get", "Player: %s, State: %u", guid.ToString().c_str(), state); + TC_LOG_TRACE("lfg.data.player.state.get", "Player: %s, State: %s", guid.ToString().c_str(), GetStateString(state).c_str()); } return state; @@ -1523,6 +1517,16 @@ LfgState LFGMgr::GetOldState(ObjectGuid guid) return state; } +bool LFGMgr::IsVoteKickActive(ObjectGuid gguid) +{ + ASSERT(gguid.IsGroup()); + + bool active = GroupsStore[gguid].IsVoteKickActive(); + TC_LOG_TRACE("lfg.data.group.votekick.get", "Group: %s, Active: %d", gguid.ToString().c_str(), active); + + return active; +} + uint32 LFGMgr::GetDungeon(ObjectGuid guid, bool asId /*= true */) { uint32 dungeon = GroupsStore[guid].GetDungeon(asId); @@ -1566,7 +1570,7 @@ LfgLockMap const LFGMgr::GetLockedDungeons(ObjectGuid guid) { TC_LOG_TRACE("lfg.data.player.dungeons.locked.get", "Player: %s, LockedDungeons.", guid.ToString().c_str()); LfgLockMap lock; - Player* player = ObjectAccessor::FindPlayer(guid); + Player* player = ObjectAccessor::FindConnectedPlayer(guid); if (!player) { TC_LOG_WARN("lfg.data.player.dungeons.locked.get", "Player: %s not ingame while retrieving his LockedDungeons.", guid.ToString().c_str()); @@ -1683,6 +1687,17 @@ void LFGMgr::SetState(ObjectGuid guid, LfgState state) } } +void LFGMgr::SetVoteKick(ObjectGuid gguid, bool active) +{ + ASSERT(gguid.IsGroup()); + + LfgGroupData& data = GroupsStore[gguid]; + TC_LOG_TRACE("lfg.data.group.votekick.set", "Group: %s, New state: %d, Previous: %d", + gguid.ToString().c_str(), active, data.IsVoteKickActive()); + + data.SetVoteKick(active); +} + void LFGMgr::SetDungeon(ObjectGuid guid, uint32 dungeon) { TC_LOG_TRACE("lfg.data.group.dungeon.set", "Group: %s, Dungeon: %u", guid.ToString().c_str(), dungeon); diff --git a/src/server/game/DungeonFinding/LFGMgr.h b/src/server/game/DungeonFinding/LFGMgr.h index 0e5fadc85cc..fda9a8b7ce9 100644 --- a/src/server/game/DungeonFinding/LFGMgr.h +++ b/src/server/game/DungeonFinding/LFGMgr.h @@ -322,6 +322,8 @@ class LFGMgr LfgDungeonSet const& GetSelectedDungeons(ObjectGuid guid); /// Get current lfg state LfgState GetState(ObjectGuid guid); + /// Get current vote kick state + bool IsVoteKickActive(ObjectGuid gguid); /// Get current dungeon uint32 GetDungeon(ObjectGuid guid, bool asId = true); /// Get the map id of the current dungeon @@ -425,6 +427,7 @@ class LFGMgr void SetSelectedDungeons(ObjectGuid guid, LfgDungeonSet const& dungeons); void DecreaseKicksLeft(ObjectGuid guid); void SetState(ObjectGuid guid, LfgState state); + void SetVoteKick(ObjectGuid gguid, bool active); void RemovePlayerData(ObjectGuid guid); void GetCompatibleDungeons(LfgDungeonSet& dungeons, GuidSet const& players, LfgLockPartyMap& lockMap, bool isContinue); void _SaveToDB(ObjectGuid guid, uint32 db_guid); diff --git a/src/server/game/DungeonFinding/LFGQueue.cpp b/src/server/game/DungeonFinding/LFGQueue.cpp index 683590cebe8..a7c0a1cfa67 100644 --- a/src/server/game/DungeonFinding/LFGQueue.cpp +++ b/src/server/game/DungeonFinding/LFGQueue.cpp @@ -377,7 +377,7 @@ LfgCompatibility LFGQueue::CheckCompatibility(GuidList check) // Group with less that MAXGROUPSIZE members always compatible if (check.size() == 1 && numPlayers != MAXGROUPSIZE) { - TC_LOG_DEBUG("lfg.queue.match.compatibility.check", "Guids: (%s) sigle group. Compatibles", strGuids.c_str()); + TC_LOG_DEBUG("lfg.queue.match.compatibility.check", "Guids: (%s) single group. Compatibles", strGuids.c_str()); LfgQueueDataContainer::iterator itQueue = QueueDataStore.find(check.front()); LfgCompatibilityData data(LFG_COMPATIBLES_WITH_LESS_PLAYERS); diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index a44ca68f79b..a3ce6d919c9 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -2697,7 +2697,7 @@ void Creature::FocusTarget(Spell const* focusSpell, WorldObject const* target) _focusSpell = focusSpell; SetGuidValue(UNIT_FIELD_TARGET, target->GetGUID()); - if (focusSpell->GetSpellInfo()->AttributesEx5 & SPELL_ATTR5_DONT_TURN_DURING_CAST) + if (focusSpell->GetSpellInfo()->HasAttribute(SPELL_ATTR5_DONT_TURN_DURING_CAST)) AddUnitState(UNIT_STATE_ROTATING); // Set serverside orientation if needed (needs to be after attribute check) @@ -2716,7 +2716,7 @@ void Creature::ReleaseFocus(Spell const* focusSpell) else SetGuidValue(UNIT_FIELD_TARGET, ObjectGuid::Empty); - if (focusSpell->GetSpellInfo()->AttributesEx5 & SPELL_ATTR5_DONT_TURN_DURING_CAST) + if (focusSpell->GetSpellInfo()->HasAttribute(SPELL_ATTR5_DONT_TURN_DURING_CAST)) ClearUnitState(UNIT_STATE_ROTATING); } diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index b5bcbfd221f..0b97499fc05 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -1072,32 +1072,32 @@ ByteBuffer& operator<<(ByteBuffer& buf, Position::PositionXYZOStreamer const& st void MovementInfo::OutDebug() { - TC_LOG_INFO("misc", "MOVEMENT INFO"); - TC_LOG_INFO("misc", "%s", guid.ToString().c_str()); - TC_LOG_INFO("misc", "flags %u", flags); - TC_LOG_INFO("misc", "flags2 %u", flags2); - TC_LOG_INFO("misc", "time %u current time " UI64FMTD "", flags2, uint64(::time(NULL))); - TC_LOG_INFO("misc", "position: `%s`", pos.ToString().c_str()); + TC_LOG_DEBUG("misc", "MOVEMENT INFO"); + TC_LOG_DEBUG("misc", "%s", guid.ToString().c_str()); + TC_LOG_DEBUG("misc", "flags %u", flags); + TC_LOG_DEBUG("misc", "flags2 %u", flags2); + TC_LOG_DEBUG("misc", "time %u current time " UI64FMTD "", flags2, uint64(::time(NULL))); + TC_LOG_DEBUG("misc", "position: `%s`", pos.ToString().c_str()); if (flags & MOVEMENTFLAG_ONTRANSPORT) { - TC_LOG_INFO("misc", "TRANSPORT:"); - TC_LOG_INFO("misc", "%s", transport.guid.ToString().c_str()); - TC_LOG_INFO("misc", "position: `%s`", transport.pos.ToString().c_str()); - TC_LOG_INFO("misc", "seat: %i", transport.seat); - TC_LOG_INFO("misc", "time: %u", transport.time); + TC_LOG_DEBUG("misc", "TRANSPORT:"); + TC_LOG_DEBUG("misc", "%s", transport.guid.ToString().c_str()); + TC_LOG_DEBUG("misc", "position: `%s`", transport.pos.ToString().c_str()); + TC_LOG_DEBUG("misc", "seat: %i", transport.seat); + TC_LOG_DEBUG("misc", "time: %u", transport.time); if (flags2 & MOVEMENTFLAG2_INTERPOLATED_MOVEMENT) - TC_LOG_INFO("misc", "time2: %u", transport.time2); + TC_LOG_DEBUG("misc", "time2: %u", transport.time2); } if ((flags & (MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_FLYING)) || (flags2 & MOVEMENTFLAG2_ALWAYS_ALLOW_PITCHING)) - TC_LOG_INFO("misc", "pitch: %f", pitch); + TC_LOG_DEBUG("misc", "pitch: %f", pitch); - TC_LOG_INFO("misc", "fallTime: %u", fallTime); + TC_LOG_DEBUG("misc", "fallTime: %u", fallTime); if (flags & MOVEMENTFLAG_FALLING) - TC_LOG_INFO("misc", "j_zspeed: %f j_sinAngle: %f j_cosAngle: %f j_xyspeed: %f", jump.zspeed, jump.sinAngle, jump.cosAngle, jump.xyspeed); + TC_LOG_DEBUG("misc", "j_zspeed: %f j_sinAngle: %f j_cosAngle: %f j_xyspeed: %f", jump.zspeed, jump.sinAngle, jump.cosAngle, jump.xyspeed); if (flags & MOVEMENTFLAG_SPLINE_ELEVATION) - TC_LOG_INFO("misc", "splineElevation: %f", splineElevation); + TC_LOG_DEBUG("misc", "splineElevation: %f", splineElevation); } WorldObject::WorldObject(bool isWorldObject) : WorldLocation(), LastUsedScriptID(0), @@ -2255,6 +2255,13 @@ void WorldObject::GetCreatureListWithEntryInGrid(std::list<Creature*>& creatureL cell.Visit(pair, visitor, *(this->GetMap()), *this, maxSearchRange); } +void WorldObject::GetPlayerListInGrid(std::list<Player*>& playerList, float maxSearchRange) const +{ + Trinity::AnyPlayerInObjectRangeCheck checker(this, maxSearchRange); + Trinity::PlayerListSearcher<Trinity::AnyPlayerInObjectRangeCheck> searcher(this, playerList, checker); + this->VisitNearbyWorldObject(maxSearchRange, searcher); +} + /* namespace Trinity { diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index 2aa1704f7d2..c48d8ff4d18 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -683,6 +683,7 @@ class WorldObject : public Object, public WorldLocation void GetGameObjectListWithEntryInGrid(std::list<GameObject*>& lList, uint32 uiEntry, float fMaxSearchRange) const; void GetCreatureListWithEntryInGrid(std::list<Creature*>& lList, uint32 uiEntry, float fMaxSearchRange) const; + void GetPlayerListInGrid(std::list<Player*>& lList, float fMaxSearchRange) const; void DestroyForNearbyPlayers(); virtual void UpdateObjectVisibility(bool forced = true); diff --git a/src/server/game/Entities/Object/ObjectGuid.h b/src/server/game/Entities/Object/ObjectGuid.h index be292751811..36dbdd72069 100644 --- a/src/server/game/Entities/Object/ObjectGuid.h +++ b/src/server/game/Entities/Object/ObjectGuid.h @@ -23,6 +23,7 @@ #include "ByteBuffer.h" #include <functional> +#include <unordered_set> enum TypeID { @@ -202,6 +203,7 @@ typedef std::set<ObjectGuid> GuidSet; typedef std::list<ObjectGuid> GuidList; typedef std::deque<ObjectGuid> GuidDeque; typedef std::vector<ObjectGuid> GuidVector; +typedef std::unordered_set<ObjectGuid> GuidUnorderedSet; // minimum buffer size for packed guid is 9 bytes #define PACKED_GUID_MIN_BUFFER_SIZE 9 diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index 5084031880d..fdfd1fa3d4d 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -1314,7 +1314,7 @@ void Pet::_LoadAuras(uint32 timediff) } aura->SetLoadedState(maxduration, remaintime, remaincharges, stackcount, recalculatemask, &damage[0]); aura->ApplyForTargets(); - TC_LOG_INFO("entities.pet", "Added aura spellid %u, effectmask %u", spellInfo->Id, effmask); + TC_LOG_DEBUG("entities.pet", "Added aura spellid %u, effectmask %u", spellInfo->Id, effmask); } } while (result->NextRow()); diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 161fca432b4..e46448df133 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -3902,7 +3902,7 @@ bool Player::IsNeedCastPassiveSpellAtLearn(SpellInfo const* spellInfo) const // talent dependent passives activated at form apply have proper stance data ShapeshiftForm form = GetShapeshiftForm(); bool need_cast = (!spellInfo->Stances || (form && (spellInfo->Stances & (1 << (form - 1)))) || - (!form && (spellInfo->AttributesEx2 & SPELL_ATTR2_NOT_NEED_SHAPESHIFT))); + (!form && spellInfo->HasAttribute(SPELL_ATTR2_NOT_NEED_SHAPESHIFT))); //Check CasterAuraStates return need_cast && (!spellInfo->CasterAuraState || HasAuraState(AuraStateType(spellInfo->CasterAuraState))); @@ -4218,7 +4218,7 @@ void Player::RemoveSpellCategoryCooldown(uint32 cat, bool update /* = false */) void Player::RemoveArenaSpellCooldowns(bool removeActivePetCooldowns) { - // remove cooldowns on spells that have <= 10 min CD + // remove cooldowns on spells that have < 10 min CD SpellCooldowns::iterator itr, next; for (itr = m_spellCooldowns.begin(); itr != m_spellCooldowns.end(); itr = next) @@ -4226,10 +4226,10 @@ void Player::RemoveArenaSpellCooldowns(bool removeActivePetCooldowns) next = itr; ++next; SpellInfo const* entry = sSpellMgr->GetSpellInfo(itr->first); - // check if spellentry is present and if the cooldown is less or equal to 10 min + // check if spellentry is present and if the cooldown is less than 10 min if (entry && - entry->RecoveryTime <= 10 * MINUTE * IN_MILLISECONDS && - entry->CategoryRecoveryTime <= 10 * MINUTE * IN_MILLISECONDS) + entry->RecoveryTime < 10 * MINUTE * IN_MILLISECONDS && + entry->CategoryRecoveryTime < 10 * MINUTE * IN_MILLISECONDS) { // remove & notify RemoveSpellCooldown(itr->first, true); @@ -4830,10 +4830,22 @@ void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRe stmt->setUInt32(0, guid); trans->Append(stmt); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_ARENA_STATS); + stmt->setUInt32(0, guid); + trans->Append(stmt); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_AURA); stmt->setUInt32(0, guid); trans->Append(stmt); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PLAYER_BGDATA); + stmt->setUInt32(0, guid); + trans->Append(stmt); + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_BATTLEGROUND_RANDOM); + stmt->setUInt32(0, guid); + trans->Append(stmt); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_GIFT); stmt->setUInt32(0, guid); trans->Append(stmt); @@ -4923,10 +4935,6 @@ void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRe stmt->setUInt32(0, guid); trans->Append(stmt); - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PLAYER_BGDATA); - stmt->setUInt32(0, guid); - trans->Append(stmt); - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_GLYPHS); stmt->setUInt32(0, guid); trans->Append(stmt); @@ -4955,6 +4963,10 @@ void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRe stmt->setUInt32(0, guid); trans->Append(stmt); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_STATS); + stmt->setUInt32(0, guid); + trans->Append(stmt); + CharacterDatabase.CommitTransaction(trans); break; } @@ -5002,7 +5014,7 @@ void Player::DeleteOldCharacters() */ void Player::DeleteOldCharacters(uint32 keepDays) { - TC_LOG_INFO("entities.player", "Player::DeleteOldChars: Deleting all characters which have been deleted %u days before...", keepDays); + TC_LOG_INFO("entities.player", "Player::DeleteOldChars: Removing characters older than %u day(s)", keepDays); PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_OLD_CHARS); stmt->setUInt32(0, uint32(time(NULL) - time_t(keepDays * DAY))); @@ -5010,7 +5022,7 @@ void Player::DeleteOldCharacters(uint32 keepDays) if (result) { - TC_LOG_DEBUG("entities.player", "Player::DeleteOldChars: Found " UI64FMTD " character(s) to delete", result->GetRowCount()); + TC_LOG_DEBUG("entities.player", "Player::DeleteOldChars: " UI64FMTD " character(s) to remove", result->GetRowCount()); do { Field* fields = result->Fetch(); @@ -6636,20 +6648,20 @@ void Player::SendActionButtons(uint32 state) const } GetSession()->SendPacket(&data); - TC_LOG_INFO("network", "SMSG_ACTION_BUTTONS sent '%u' spec '%u' Sent", GetGUIDLow(), m_activeSpec); + TC_LOG_DEBUG("network", "SMSG_ACTION_BUTTONS sent '%u' spec '%u' Sent", GetGUIDLow(), m_activeSpec); } bool Player::IsActionButtonDataValid(uint8 button, uint32 action, uint8 type) { if (button >= MAX_ACTION_BUTTONS) { - TC_LOG_ERROR("entities.player", "Action %u not added into button %u for player %s (GUID: %u): button must be < %u", action, button, GetName().c_str(), GetGUIDLow(), MAX_ACTION_BUTTONS ); + TC_LOG_DEBUG("entities.player", "Action %u not added into button %u for player %s (GUID: %u): button must be < %u", action, button, GetName().c_str(), GetGUIDLow(), MAX_ACTION_BUTTONS ); return false; } if (action >= MAX_ACTION_BUTTON_ACTION_VALUE) { - TC_LOG_ERROR("entities.player", "Action %u not added into button %u for player %s (GUID: %u): action must be < %u", action, button, GetName().c_str(), GetGUIDLow(), MAX_ACTION_BUTTON_ACTION_VALUE); + TC_LOG_DEBUG("entities.player", "Action %u not added into button %u for player %s (GUID: %u): action must be < %u", action, button, GetName().c_str(), GetGUIDLow(), MAX_ACTION_BUTTON_ACTION_VALUE); return false; } @@ -6658,20 +6670,20 @@ bool Player::IsActionButtonDataValid(uint8 button, uint32 action, uint8 type) case ACTION_BUTTON_SPELL: if (!sSpellMgr->GetSpellInfo(action)) { - TC_LOG_ERROR("entities.player", "Spell action %u not added into button %u for player %s (GUID: %u): spell not exist", action, button, GetName().c_str(), GetGUIDLow()); + TC_LOG_DEBUG("entities.player", "Spell action %u not added into button %u for player %s (GUID: %u): spell not exist", action, button, GetName().c_str(), GetGUIDLow()); return false; } if (!HasSpell(action)) { - TC_LOG_ERROR("entities.player", "Spell action %u not added into button %u for player %s (GUID: %u): player don't known this spell", action, button, GetName().c_str(), GetGUIDLow()); + TC_LOG_DEBUG("entities.player", "Spell action %u not added into button %u for player %s (GUID: %u): player don't known this spell", action, button, GetName().c_str(), GetGUIDLow()); return false; } break; case ACTION_BUTTON_ITEM: if (!sObjectMgr->GetItemTemplate(action)) { - TC_LOG_ERROR("entities.player", "Item action %u not added into button %u for player %s (GUID: %u): item not exist", action, button, GetName().c_str(), GetGUIDLow()); + TC_LOG_DEBUG("entities.player", "Item action %u not added into button %u for player %s (GUID: %u): item not exist", action, button, GetName().c_str(), GetGUIDLow()); return false; } break; @@ -6681,7 +6693,7 @@ bool Player::IsActionButtonDataValid(uint8 button, uint32 action, uint8 type) case ACTION_BUTTON_EQSET: break; default: - TC_LOG_ERROR("entities.player", "Unknown action type %u", type); + TC_LOG_DEBUG("entities.player", "Unknown action type %u", type); return false; // other cases not checked at this moment } @@ -6883,7 +6895,7 @@ void Player::CheckAreaExploreAndOutdoor() GiveXP(XP, NULL); SendExplorationExperience(area, XP); } - TC_LOG_INFO("entities.player", "Player %u discovered a new area: %u", GetGUIDLow(), area); + TC_LOG_DEBUG("entities.player", "Player %u discovered a new area: %u", GetGUIDLow(), area); } } } @@ -7763,7 +7775,7 @@ void Player::_ApplyItemMods(Item* item, uint8 slot, bool apply) if (item->IsBroken()) return; - TC_LOG_INFO("entities.player.items", "applying mods for item %u ", item->GetGUIDLow()); + TC_LOG_DEBUG("entities.player.items", "applying mods for item %u ", item->GetGUIDLow()); uint8 attacktype = Player::GetAttackBySlot(slot); @@ -9774,7 +9786,7 @@ uint32 Player::GetXPRestBonus(uint32 xp) SetRestBonus(GetRestBonus() - rested_bonus); - TC_LOG_INFO("entities.player", "Player gain %u xp (+ %u Rested Bonus). Rested points=%f", xp+rested_bonus, rested_bonus, GetRestBonus()); + TC_LOG_DEBUG("entities.player", "Player gain %u xp (+ %u Rested Bonus). Rested points=%f", xp+rested_bonus, rested_bonus, GetRestBonus()); return rested_bonus; } @@ -17999,7 +18011,7 @@ void Player::_LoadAuras(PreparedQueryResult result, uint32 timediff) aura->SetLoadedState(maxduration, remaintime, remaincharges, stackcount, recalculatemask, &damage[0]); aura->ApplyForTargets(); - TC_LOG_INFO("entities.player", "Added aura spellid %u, effectmask %u", spellInfo->Id, effmask); + TC_LOG_DEBUG("entities.player", "Added aura spellid %u, effectmask %u", spellInfo->Id, effmask); } } while (result->NextRow()); @@ -19086,7 +19098,7 @@ bool Player::Satisfy(AccessRequirement const* ar, uint32 target_map, bool report else if (mapDiff->hasErrorMessage) // if (missingAchievement) covered by this case SendTransferAborted(target_map, TRANSFER_ABORT_DIFFICULTY, target_difficulty); else if (missingItem) - GetSession()->SendAreaTriggerMessage(GetSession()->GetTrinityString(LANG_LEVEL_MINREQUIRED_AND_ITEM), LevelMin, sObjectMgr->GetItemTemplate(missingItem)->Name1.c_str()); + GetSession()->SendAreaTriggerMessage(GetSession()->GetTrinityString(LANG_LEVEL_MINREQUIRED_AND_ITEM), LevelMin, ASSERT_NOTNULL(sObjectMgr->GetItemTemplate(missingItem))->Name1.c_str()); else if (LevelMin) GetSession()->SendAreaTriggerMessage(GetSession()->GetTrinityString(LANG_LEVEL_MINREQUIRED), LevelMin); } @@ -19732,6 +19744,7 @@ void Player::_SaveInventory(SQLTransaction& trans) m_itemUpdateQueue.clear(); } + void Player::_SaveMail(SQLTransaction& trans) { if (!m_mailsLoaded) @@ -20670,8 +20683,6 @@ void Player::PetSpellInitialize() if (!pet) return; - TC_LOG_DEBUG("entities.pet", "Pet Spells Groups"); - CharmInfo* charmInfo = pet->GetCharmInfo(); WorldPacket data(SMSG_PET_SPELLS, 8+2+4+4+4*MAX_UNIT_ACTION_BAR_INDEX+1+1); @@ -20682,6 +20693,8 @@ void Player::PetSpellInitialize() data << uint8(charmInfo->GetCommandState()); data << uint16(0); // Flags, mostly unknown + TC_LOG_DEBUG("entities.pet", "Player::PetspellInitialize: Creating spellgroups for summoned pet"); + // action bar loop charmInfo->BuildActionBar(&data); @@ -22043,7 +22056,7 @@ void Player::AddSpellAndCategoryCooldowns(SpellInfo const* spellInfo, uint32 ite if (rec > 0) ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, rec, spell); - if (catrec > 0 && !(spellInfo->AttributesEx6 & SPELL_ATTR6_IGNORE_CATEGORY_COOLDOWN_MODS)) + if (catrec > 0 && !spellInfo->HasAttribute(SPELL_ATTR6_IGNORE_CATEGORY_COOLDOWN_MODS)) ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, catrec, spell); if (int32 cooldownMod = GetTotalAuraModifier(SPELL_AURA_MOD_COOLDOWN)) @@ -22138,6 +22151,34 @@ void Player::SendCooldownEvent(SpellInfo const* spellInfo, uint32 itemId /*= 0*/ data << uint32(spellInfo->Id); data << uint64(GetGUID()); SendDirectMessage(&data); + + uint32 cat = spellInfo->GetCategory(); + if (cat && spellInfo->CategoryRecoveryTime) + { + SpellCategoryStore::const_iterator ct = sSpellsByCategoryStore.find(cat); + if (ct != sSpellsByCategoryStore.end()) + { + SpellCategorySet const& catSet = ct->second; + for (SpellCooldowns::const_iterator i = m_spellCooldowns.begin(); i != m_spellCooldowns.end(); ++i) + { + if (i->first == spellInfo->Id) // skip main spell, already handled above + continue; + + SpellInfo const* spellInfo2 = sSpellMgr->GetSpellInfo(i->first); + if (!spellInfo2 || !spellInfo2->IsCooldownStartedOnEvent()) + continue; + + if (catSet.find(i->first) != catSet.end()) + { + // Send activate cooldown timer (possible 0) at client side + WorldPacket data(SMSG_COOLDOWN_EVENT, 4 + 8); + data << uint32(i->first); + data << uint64(GetGUID()); + SendDirectMessage(&data); + } + } + } + } } void Player::UpdatePotionCooldown(Spell* spell) @@ -22532,13 +22573,13 @@ bool Player::IsVisibleGloballyFor(Player const* u) const } template<class T> -inline void UpdateVisibilityOf_helper(GuidSet& s64, T* target, std::set<Unit*>& /*v*/) +inline void UpdateVisibilityOf_helper(GuidUnorderedSet& s64, T* target, std::set<Unit*>& /*v*/) { s64.insert(target->GetGUID()); } template<> -inline void UpdateVisibilityOf_helper(GuidSet& s64, GameObject* target, std::set<Unit*>& /*v*/) +inline void UpdateVisibilityOf_helper(GuidUnorderedSet& s64, GameObject* target, std::set<Unit*>& /*v*/) { // @HACK: This is to prevent objects like deeprun tram from disappearing when player moves far from its spawn point while riding it if ((target->GetGOInfo()->type != GAMEOBJECT_TYPE_TRANSPORT)) @@ -22546,14 +22587,14 @@ inline void UpdateVisibilityOf_helper(GuidSet& s64, GameObject* target, std::set } template<> -inline void UpdateVisibilityOf_helper(GuidSet& s64, Creature* target, std::set<Unit*>& v) +inline void UpdateVisibilityOf_helper(GuidUnorderedSet& s64, Creature* target, std::set<Unit*>& v) { s64.insert(target->GetGUID()); v.insert(target); } template<> -inline void UpdateVisibilityOf_helper(GuidSet& s64, Player* target, std::set<Unit*>& v) +inline void UpdateVisibilityOf_helper(GuidUnorderedSet& s64, Player* target, std::set<Unit*>& v) { s64.insert(target->GetGUID()); v.insert(target); @@ -22615,7 +22656,7 @@ void Player::UpdateTriggerVisibility() UpdateData udata; WorldPacket packet; - for (GuidSet::iterator itr = m_clientGUIDs.begin(); itr != m_clientGUIDs.end(); ++itr) + for (auto itr = m_clientGUIDs.begin(); itr != m_clientGUIDs.end(); ++itr) { if (itr->IsCreature()) { @@ -23674,7 +23715,7 @@ void Player::UpdateForQuestWorldObjects() UpdateData udata; WorldPacket packet; - for (GuidSet::iterator itr = m_clientGUIDs.begin(); itr != m_clientGUIDs.end(); ++itr) + for (auto itr = m_clientGUIDs.begin(); itr != m_clientGUIDs.end(); ++itr) { if (itr->IsGameObject()) { @@ -23863,7 +23904,7 @@ bool Player::HasItemFitToSpellRequirements(SpellInfo const* spellInfo, Item cons bool Player::CanNoReagentCast(SpellInfo const* spellInfo) const { // don't take reagents for spells with SPELL_ATTR5_NO_REAGENT_WHILE_PREP - if (spellInfo->AttributesEx5 & SPELL_ATTR5_NO_REAGENT_WHILE_PREP && + if (spellInfo->HasAttribute(SPELL_ATTR5_NO_REAGENT_WHILE_PREP) && HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PREPARATION)) return true; @@ -24166,18 +24207,6 @@ void Player::UpdateAreaDependentAuras(uint32 newArea) if (itr->second->autocast && itr->second->IsFitToRequirements(this, m_zoneUpdateId, newArea)) if (!HasAura(itr->second->spellId)) CastSpell(this, itr->second->spellId, true); - - if (newArea == 4273 && GetVehicleCreatureBase() && GetPositionX() > 400) // Ulduar - { - switch (GetVehicleBase()->GetEntry()) - { - case 33062: - case 33109: - case 33060: - GetVehicleCreatureBase()->DespawnOrUnsummon(); - break; - } - } } uint32 Player::GetCorpseReclaimDelay(bool pvp) const @@ -24308,7 +24337,7 @@ PartyResult Player::CanUninviteFromGroup(ObjectGuid guidMember) const return ERR_PARTY_LFG_BOOT_LIMIT; lfg::LfgState state = sLFGMgr->GetState(gguid); - if (state == lfg::LFG_STATE_BOOT) + if (sLFGMgr->IsVoteKickActive(gguid)) return ERR_PARTY_LFG_BOOT_IN_PROGRESS; if (grp->GetMembersCount() <= lfg::LFG_GROUP_KICK_VOTES_NEEDED) @@ -24815,7 +24844,7 @@ void Player::RestoreBaseRune(uint8 index) { AuraEffect const* aura = m_runes->runes[index].ConvertAura; // If rune was converted by a non-pasive aura that still active we should keep it converted - if (aura && !(aura->GetSpellInfo()->Attributes & SPELL_ATTR0_PASSIVE)) + if (aura && !aura->GetSpellInfo()->HasAttribute(SPELL_ATTR0_PASSIVE)) return; ConvertRune(index, GetBaseRune(index)); SetRuneConvertAura(index, NULL); @@ -25047,6 +25076,7 @@ void Player::_LoadSkills(PreparedQueryResult result) // SetPQuery(PLAYER_LOGIN_QUERY_LOADSKILLS, "SELECT skill, value, max FROM character_skills WHERE guid = '%u'", GUID_LOPART(m_guid)); uint32 count = 0; + std::unordered_map<uint32, uint32> loadedSkillValues; if (result) { do @@ -25114,8 +25144,7 @@ void Player::_LoadSkills(PreparedQueryResult result) SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(count), 0); mSkillStatus.insert(SkillStatusMap::value_type(skill, SkillStatusData(count, SKILL_UNCHANGED))); - - LearnSkillRewardedSpells(skill, value); + loadedSkillValues[skill] = value; ++count; @@ -25128,6 +25157,10 @@ void Player::_LoadSkills(PreparedQueryResult result) while (result->NextRow()); } + // Learn skill rewarded spells after all skills have been loaded to prevent learning a skill from them before its loaded with proper value from DB + for (auto& skill : loadedSkillValues) + LearnSkillRewardedSpells(skill.first, skill.second); + for (; count < PLAYER_MAX_SKILLS; ++count) { SetUInt32Value(PLAYER_SKILL_INDEX(count), 0); @@ -25429,7 +25462,7 @@ void Player::LearnTalent(uint32 talentId, uint32 talentRank) LearnSpell(spellid, false); AddTalent(spellid, m_activeSpec, true); - TC_LOG_INFO("entities.player", "TalentID: %u Rank: %u Spell: %u Spec: %u\n", talentId, talentRank, spellid, m_activeSpec); + TC_LOG_DEBUG("entities.player", "TalentID: %u Rank: %u Spell: %u Spec: %u\n", talentId, talentRank, spellid, m_activeSpec); // update free talent points SetFreeTalentPoints(CurTalentPoints - (talentRank - curtalent_maxrank + 1)); @@ -25564,7 +25597,7 @@ void Player::LearnPetTalent(ObjectGuid petGuid, uint32 talentId, uint32 talentRa // learn! (other talent ranks will unlearned at learning) pet->learnSpell(spellid); - TC_LOG_INFO("entities.player", "PetTalentID: %u Rank: %u Spell: %u\n", talentId, talentRank, spellid); + TC_LOG_DEBUG("entities.player", "PetTalentID: %u Rank: %u Spell: %u\n", talentId, talentRank, spellid); // update free talent points pet->SetFreeTalentPoints(CurTalentPoints - (talentRank - curtalent_maxrank + 1)); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 61141192cd1..1e928c80f3c 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -2170,7 +2170,7 @@ class Player : public Unit, public GridObject<Player> WorldLocation GetStartPosition() const; // currently visible objects at player client - GuidSet m_clientGUIDs; + GuidUnorderedSet m_clientGUIDs; bool HaveAtClient(WorldObject const* u) const; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index fdd91368704..f44c14a12dd 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -589,7 +589,7 @@ uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDam // interrupting auras with AURA_INTERRUPT_FLAG_DAMAGE before checking !damage (absorbed damage breaks that type of auras) if (spellProto) { - if (!(spellProto->AttributesEx4 & SPELL_ATTR4_DAMAGE_DOESNT_BREAK_AURAS)) + if (!spellProto->HasAttribute(SPELL_ATTR4_DAMAGE_DOESNT_BREAK_AURAS)) victim->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TAKE_DAMAGE, spellProto->Id); } else @@ -777,7 +777,7 @@ uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDam if (damagetype != NODAMAGE && damage) { if (victim != this && victim->GetTypeId() == TYPEID_PLAYER && // does not support creature push_back - (!spellProto || !(spellProto->AttributesEx7 & SPELL_ATTR7_NO_PUSHBACK_ON_DAMAGE))) + (!spellProto || !spellProto->HasAttribute(SPELL_ATTR7_NO_PUSHBACK_ON_DAMAGE))) { if (damagetype != DOT) if (Spell* spell = victim->m_currentSpells[CURRENT_GENERIC_SPELL]) @@ -981,7 +981,7 @@ void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage* damageInfo, int32 dama uint32 crTypeMask = victim->GetCreatureTypeMask(); // Spells with SPELL_ATTR4_FIXED_DAMAGE ignore resilience because their damage is based off another spell's damage. - if (!(spellInfo->AttributesEx4 & SPELL_ATTR4_FIXED_DAMAGE)) + if (!spellInfo->HasAttribute(SPELL_ATTR4_FIXED_DAMAGE)) { if (IsDamageReducedByArmor(damageSchoolMask, spellInfo)) damage = CalcArmorReducedDamage(victim, damage, spellInfo, attackType); @@ -1459,7 +1459,7 @@ bool Unit::IsDamageReducedByArmor(SpellSchoolMask schoolMask, SpellInfo const* s if (spellInfo) { // there are spells with no specific attribute but they have "ignores armor" in tooltip - if (spellInfo->AttributesCu & SPELL_ATTR0_CU_IGNORE_ARMOR) + if (spellInfo->HasAttribute(SPELL_ATTR0_CU_IGNORE_ARMOR)) return false; // bleeding effects are not reduced by armor @@ -1560,7 +1560,7 @@ uint32 Unit::CalcSpellResistance(Unit* victim, SpellSchoolMask schoolMask, Spell return 0; // Ignore spells that can't be resisted - if (spellInfo && spellInfo->AttributesEx4 & SPELL_ATTR4_IGNORE_RESISTANCES) + if (spellInfo && spellInfo->HasAttribute(SPELL_ATTR4_IGNORE_RESISTANCES)) return 0; uint32 const BOSS_LEVEL = 83; @@ -2234,15 +2234,15 @@ void Unit::SendMeleeAttackStop(Unit* victim) TC_LOG_DEBUG("entities.unit", "WORLD: Sent SMSG_ATTACKSTOP"); if (victim) - TC_LOG_INFO("entities.unit", "%s %u stopped attacking %s %u", (GetTypeId() == TYPEID_PLAYER ? "Player" : "Creature"), GetGUIDLow(), (victim->GetTypeId() == TYPEID_PLAYER ? "player" : "creature"), victim->GetGUIDLow()); + TC_LOG_DEBUG("entities.unit", "%s %u stopped attacking %s %u", (GetTypeId() == TYPEID_PLAYER ? "Player" : "Creature"), GetGUIDLow(), (victim->GetTypeId() == TYPEID_PLAYER ? "player" : "creature"), victim->GetGUIDLow()); else - TC_LOG_INFO("entities.unit", "%s %u stopped attacking", (GetTypeId() == TYPEID_PLAYER ? "Player" : "Creature"), GetGUIDLow()); + TC_LOG_DEBUG("entities.unit", "%s %u stopped attacking", (GetTypeId() == TYPEID_PLAYER ? "Player" : "Creature"), GetGUIDLow()); } bool Unit::isSpellBlocked(Unit* victim, SpellInfo const* spellProto, WeaponAttackType attackType) { // These spells can't be blocked - if (spellProto && spellProto->Attributes & SPELL_ATTR0_IMPOSSIBLE_DODGE_PARRY_BLOCK) + if (spellProto && spellProto->HasAttribute(SPELL_ATTR0_IMPOSSIBLE_DODGE_PARRY_BLOCK)) return false; if (victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION) || victim->HasInArc(float(M_PI), this)) @@ -2309,7 +2309,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo { // Spells with SPELL_ATTR3_IGNORE_HIT_RESULT will additionally fully ignore // resist and deflect chances - if (spellInfo->AttributesEx3 & SPELL_ATTR3_IGNORE_HIT_RESULT) + if (spellInfo->HasAttribute(SPELL_ATTR3_IGNORE_HIT_RESULT)) return SPELL_MISS_NONE; WeaponAttackType attType = BASE_ATTACK; @@ -2357,10 +2357,10 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo bool canDodge = true; bool canParry = true; - bool canBlock = (spellInfo->AttributesEx3 & SPELL_ATTR3_BLOCKABLE_SPELL) != 0; + bool canBlock = spellInfo->HasAttribute(SPELL_ATTR3_BLOCKABLE_SPELL); // Same spells cannot be parry/dodge - if (spellInfo->Attributes & SPELL_ATTR0_IMPOSSIBLE_DODGE_PARRY_BLOCK) + if (spellInfo->HasAttribute(SPELL_ATTR0_IMPOSSIBLE_DODGE_PARRY_BLOCK)) return SPELL_MISS_NONE; // Chance resist mechanic @@ -2400,7 +2400,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo } else // Only deterrence as of 3.3.5 { - if (spellInfo->AttributesCu & SPELL_ATTR0_CU_REQ_CASTER_BEHIND_TARGET) + if (spellInfo->HasAttribute(SPELL_ATTR0_CU_REQ_CASTER_BEHIND_TARGET)) canParry = false; } } @@ -2491,7 +2491,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo) { // Can`t miss on dead target (on skinning for example) - if ((!victim->IsAlive() && victim->GetTypeId() != TYPEID_PLAYER) || spellInfo->AttributesEx3 & SPELL_ATTR3_IGNORE_HIT_RESULT) + if ((!victim->IsAlive() && victim->GetTypeId() != TYPEID_PLAYER) || spellInfo->HasAttribute(SPELL_ATTR3_IGNORE_HIT_RESULT)) return SPELL_MISS_NONE; SpellSchoolMask schoolMask = spellInfo->GetSchoolMask(); @@ -3089,7 +3089,7 @@ bool Unit::IsNonMeleeSpellCast(bool withDelayed, bool skipChanneled, bool skipAu { if (!skipInstant || m_currentSpells[CURRENT_GENERIC_SPELL]->GetCastTime()) { - if (!isAutoshoot || !(m_currentSpells[CURRENT_GENERIC_SPELL]->m_spellInfo->AttributesEx2 & SPELL_ATTR2_NOT_RESET_AUTO_ACTIONS)) + if (!isAutoshoot || !(m_currentSpells[CURRENT_GENERIC_SPELL]->m_spellInfo->HasAttribute(SPELL_ATTR2_NOT_RESET_AUTO_ACTIONS))) return true; } } @@ -3097,7 +3097,7 @@ bool Unit::IsNonMeleeSpellCast(bool withDelayed, bool skipChanneled, bool skipAu if (!skipChanneled && m_currentSpells[CURRENT_CHANNELED_SPELL] && (m_currentSpells[CURRENT_CHANNELED_SPELL]->getState() != SPELL_STATE_FINISHED)) { - if (!isAutoshoot || !(m_currentSpells[CURRENT_CHANNELED_SPELL]->m_spellInfo->AttributesEx2 & SPELL_ATTR2_NOT_RESET_AUTO_ACTIONS)) + if (!isAutoshoot || !(m_currentSpells[CURRENT_CHANNELED_SPELL]->m_spellInfo->HasAttribute(SPELL_ATTR2_NOT_RESET_AUTO_ACTIONS))) return true; } // autorepeat spells may be finished or delayed, but they are still considered cast @@ -3232,7 +3232,7 @@ Aura* Unit::_TryStackingOrRefreshingExistingAura(SpellInfo const* newAura, uint8 castItemGUID = castItem->GetGUID(); // find current aura from spell and change it's stackamount, or refresh it's duration - if (Aura* foundAura = GetOwnedAura(newAura->Id, casterGUID, (newAura->AttributesCu & SPELL_ATTR0_CU_ENCHANT_PROC) ? castItemGUID : ObjectGuid::Empty, 0)) + if (Aura* foundAura = GetOwnedAura(newAura->Id, casterGUID, newAura->HasAttribute(SPELL_ATTR0_CU_ENCHANT_PROC) ? castItemGUID : ObjectGuid::Empty, 0)) { // effect masks do not match // extremely rare case @@ -3743,7 +3743,7 @@ void Unit::RemoveAurasDueToSpellByDispel(uint32 spellId, uint32 dispellerSpellId // Call OnDispel hook on AuraScript aura->CallScriptDispel(&dispelInfo); - if (aura->GetSpellInfo()->AttributesEx7 & SPELL_ATTR7_DISPEL_CHARGES) + if (aura->GetSpellInfo()->HasAttribute(SPELL_ATTR7_DISPEL_CHARGES)) aura->ModCharges(-dispelInfo.GetRemovedCharges(), AURA_REMOVE_BY_ENEMY_SPELL); else aura->ModStackAmount(-dispelInfo.GetRemovedCharges(), AURA_REMOVE_BY_ENEMY_SPELL); @@ -3788,7 +3788,7 @@ void Unit::RemoveAurasDueToSpellBySteal(uint32 spellId, ObjectGuid casterGUID, U } } - bool stealCharge = (aura->GetSpellInfo()->AttributesEx7 & SPELL_ATTR7_DISPEL_CHARGES) != 0; + bool stealCharge = aura->GetSpellInfo()->HasAttribute(SPELL_ATTR7_DISPEL_CHARGES); // Cast duration to unsigned to prevent permanent aura's such as Righteous Fury being permanently added to caster uint32 dur = std::min(2u * MINUTE * IN_MILLISECONDS, uint32(aura->GetDuration())); @@ -4046,9 +4046,9 @@ void Unit::RemoveArenaAuras() { AuraApplication const* aurApp = iter->second; Aura const* aura = aurApp->GetBase(); - if (!(aura->GetSpellInfo()->AttributesEx4 & SPELL_ATTR4_UNK21) // don't remove stances, shadowform, pally/hunter auras + if (!aura->GetSpellInfo()->HasAttribute(SPELL_ATTR4_UNK21) // don't remove stances, shadowform, pally/hunter auras && !aura->IsPassive() // don't remove passive auras - && (aurApp->IsPositive() || !(aura->GetSpellInfo()->AttributesEx3 & SPELL_ATTR3_DEATH_PERSISTENT))) // not negative death persistent auras + && (aurApp->IsPositive() || !aura->GetSpellInfo()->HasAttribute(SPELL_ATTR3_DEATH_PERSISTENT))) // not negative death persistent auras RemoveAura(iter); else ++iter; @@ -4308,7 +4308,7 @@ void Unit::GetDispellableAuraList(Unit* caster, uint32 dispelMask, DispelCharges // The charges / stack amounts don't count towards the total number of auras that can be dispelled. // Ie: A dispel on a target with 5 stacks of Winters Chill and a Polymorph has 1 / (1 + 1) -> 50% chance to dispell // Polymorph instead of 1 / (5 + 1) -> 16%. - bool dispel_charges = (aura->GetSpellInfo()->AttributesEx7 & SPELL_ATTR7_DISPEL_CHARGES) != 0; + bool dispel_charges = aura->GetSpellInfo()->HasAttribute(SPELL_ATTR7_DISPEL_CHARGES); uint8 charges = dispel_charges ? aura->GetCharges() : aura->GetStackAmount(); if (charges > 0) dispelList.push_back(std::make_pair(aura, charges)); @@ -9602,7 +9602,7 @@ bool Unit::IsMagnet() const Unit* Unit::GetMagicHitRedirectTarget(Unit* victim, SpellInfo const* spellInfo) { // Patch 1.2 notes: Spell Reflection no longer reflects abilities - if (spellInfo->Attributes & SPELL_ATTR0_ABILITY || spellInfo->AttributesEx & SPELL_ATTR1_CANT_BE_REDIRECTED || spellInfo->Attributes & SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY) + if (spellInfo->HasAttribute(SPELL_ATTR0_ABILITY) || spellInfo->HasAttribute(SPELL_ATTR1_CANT_BE_REDIRECTED) || spellInfo->HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY)) return victim; Unit::AuraEffectList const& magnetAuras = victim->GetAuraEffectsByType(SPELL_AURA_SPELL_MAGNET); @@ -9854,7 +9854,7 @@ uint32 Unit::SpellDamageBonusDone(Unit* victim, SpellInfo const* spellProto, uin return pdamage; // Some spells don't benefit from done mods - if (spellProto->AttributesEx3 & SPELL_ATTR3_NO_DONE_BONUS) + if (spellProto->HasAttribute(SPELL_ATTR3_NO_DONE_BONUS)) return pdamage; // For totems get damage bonus from owner @@ -9985,7 +9985,7 @@ float Unit::SpellDamagePctDone(Unit* victim, SpellInfo const* spellProto, Damage return 1.0f; // Some spells don't benefit from pct done mods - if (spellProto->AttributesEx6 & SPELL_ATTR6_NO_DONE_PCT_DAMAGE_MODS) + if (spellProto->HasAttribute(SPELL_ATTR6_NO_DONE_PCT_DAMAGE_MODS)) return 1.0f; // For totems pct done mods are calculated when its calculation is run on the player in SpellDamageBonusDone. @@ -10009,7 +10009,7 @@ float Unit::SpellDamagePctDone(Unit* victim, SpellInfo const* spellProto, Damage { if ((*i)->GetSpellInfo()->EquippedItemClass == -1) AddPct(DoneTotalMod, (*i)->GetAmount()); - else if (!((*i)->GetSpellInfo()->AttributesEx5 & SPELL_ATTR5_SPECIAL_ITEM_CLASS_CHECK) && ((*i)->GetSpellInfo()->EquippedItemSubClassMask == 0)) + else if (!(*i)->GetSpellInfo()->HasAttribute(SPELL_ATTR5_SPECIAL_ITEM_CLASS_CHECK) && ((*i)->GetSpellInfo()->EquippedItemSubClassMask == 0)) AddPct(DoneTotalMod, (*i)->GetAmount()); else if (ToPlayer() && ToPlayer()->HasItemFitToSpellRequirements((*i)->GetSpellInfo())) AddPct(DoneTotalMod, (*i)->GetAmount()); @@ -10324,7 +10324,7 @@ uint32 Unit::SpellDamageBonusTaken(Unit* caster, SpellInfo const* spellProto, ui } } // Spells with SPELL_ATTR4_FIXED_DAMAGE should only benefit from mechanic damage mod auras. - if (!(spellProto->AttributesEx4 & SPELL_ATTR4_FIXED_DAMAGE)) + if (!spellProto->HasAttribute(SPELL_ATTR4_FIXED_DAMAGE)) { // get all auras from caster that allow the spell to ignore resistance (sanctified wrath) AuraEffectList const& IgnoreResistAuras = caster->GetAuraEffectsByType(SPELL_AURA_MOD_IGNORE_TARGET_RESIST); @@ -10454,7 +10454,7 @@ float Unit::GetUnitSpellCriticalChance(Unit* victim, SpellInfo const* spellProto return 0.0f; // not critting spell - if ((spellProto->AttributesEx2 & SPELL_ATTR2_CANT_CRIT)) + if (spellProto->HasAttribute(SPELL_ATTR2_CANT_CRIT)) return 0.0f; float crit_chance = 0.0f; @@ -11063,7 +11063,7 @@ bool Unit::IsImmunedToDamage(SpellSchoolMask shoolMask) const bool Unit::IsImmunedToDamage(SpellInfo const* spellInfo) const { - if (spellInfo->Attributes & SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY) + if (spellInfo->HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY)) return false; uint32 shoolMask = spellInfo->GetSchoolMask(); @@ -11096,7 +11096,7 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo) const if (itr->type == spellInfo->Id) return true; - if (spellInfo->Attributes & SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY) + if (spellInfo->HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY)) return false; if (spellInfo->Dispel) @@ -11154,7 +11154,7 @@ bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) cons if (!spellInfo || !spellInfo->Effects[index].IsEffect()) return false; - if (spellInfo->Attributes & SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY) + if (spellInfo->HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY)) return false; // If m_immuneToEffect type contain this effect type, IMMUNE effect. @@ -11177,7 +11177,7 @@ bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) cons SpellImmuneList const& list = m_spellImmune[IMMUNITY_STATE]; for (SpellImmuneList::const_iterator itr = list.begin(); itr != list.end(); ++itr) if (itr->type == aura) - if (!(spellInfo->AttributesEx3 & SPELL_ATTR3_IGNORE_HIT_RESULT)) + if (!spellInfo->HasAttribute(SPELL_ATTR3_IGNORE_HIT_RESULT)) return true; // Check for immune to application of harmful magical effects @@ -11252,7 +11252,7 @@ uint32 Unit::MeleeDamageBonusDone(Unit* victim, uint32 pdamage, WeaponAttackType // Some spells don't benefit from pct done mods if (spellProto) - if (!(spellProto->AttributesEx6 & SPELL_ATTR6_NO_DONE_PCT_DAMAGE_MODS)) + if (!spellProto->HasAttribute(SPELL_ATTR6_NO_DONE_PCT_DAMAGE_MODS)) { AuraEffectList const& mModDamagePercentDone = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE); for (AuraEffectList::const_iterator i = mModDamagePercentDone.begin(); i != mModDamagePercentDone.end(); ++i) @@ -11261,7 +11261,7 @@ uint32 Unit::MeleeDamageBonusDone(Unit* victim, uint32 pdamage, WeaponAttackType { if ((*i)->GetSpellInfo()->EquippedItemClass == -1) AddPct(DoneTotalMod, (*i)->GetAmount()); - else if (!((*i)->GetSpellInfo()->AttributesEx5 & SPELL_ATTR5_SPECIAL_ITEM_CLASS_CHECK) && ((*i)->GetSpellInfo()->EquippedItemSubClassMask == 0)) + else if (!(*i)->GetSpellInfo()->HasAttribute(SPELL_ATTR5_SPECIAL_ITEM_CLASS_CHECK) && ((*i)->GetSpellInfo()->EquippedItemSubClassMask == 0)) AddPct(DoneTotalMod, (*i)->GetAmount()); else if (ToPlayer() && ToPlayer()->HasItemFitToSpellRequirements((*i)->GetSpellInfo())) AddPct(DoneTotalMod, (*i)->GetAmount()); @@ -11523,7 +11523,7 @@ void Unit::ApplySpellDispelImmunity(const SpellInfo* spellProto, DispelType type { ApplySpellImmune(spellProto->Id, IMMUNITY_DISPEL, type, apply); - if (apply && spellProto->AttributesEx & SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY) + if (apply && spellProto->HasAttribute(SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY)) { // Create dispel mask by dispel type uint32 dispelMask = SpellInfo::GetDispelMask(type); @@ -11864,7 +11864,7 @@ bool Unit::_IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell, Wo return false; // can't attack invisible (ignore stealth for aoe spells) also if the area being looked at is from a spell use the dynamic object created instead of the casting unit. - if ((!bySpell || !(bySpell->AttributesEx6 & SPELL_ATTR6_CAN_TARGET_INVISIBLE)) && (obj ? !obj->CanSeeOrDetect(target, bySpell && bySpell->IsAffectingArea()) : !CanSeeOrDetect(target, bySpell && bySpell->IsAffectingArea()))) + if ((!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_INVISIBLE)) && (obj ? !obj->CanSeeOrDetect(target, bySpell && bySpell->IsAffectingArea()) : !CanSeeOrDetect(target, bySpell && bySpell->IsAffectingArea()))) return false; // can't attack dead @@ -11872,7 +11872,7 @@ bool Unit::_IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell, Wo return false; // can't attack untargetable - if ((!bySpell || !(bySpell->AttributesEx6 & SPELL_ATTR6_CAN_TARGET_UNTARGETABLE)) + if ((!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_UNTARGETABLE)) && target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) return false; @@ -11981,7 +11981,7 @@ bool Unit::_IsValidAssistTarget(Unit const* target, SpellInfo const* bySpell) co return false; // can't assist invisible - if ((!bySpell || !(bySpell->AttributesEx6 & SPELL_ATTR6_CAN_TARGET_INVISIBLE)) && !CanSeeOrDetect(target, bySpell && bySpell->IsAffectingArea())) + if ((!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_INVISIBLE)) && !CanSeeOrDetect(target, bySpell && bySpell->IsAffectingArea())) return false; // can't assist dead @@ -11989,11 +11989,11 @@ bool Unit::_IsValidAssistTarget(Unit const* target, SpellInfo const* bySpell) co return false; // can't assist untargetable - if ((!bySpell || !(bySpell->AttributesEx6 & SPELL_ATTR6_CAN_TARGET_UNTARGETABLE)) + if ((!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_UNTARGETABLE)) && target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) return false; - if (!bySpell || !(bySpell->AttributesEx6 & SPELL_ATTR6_ASSIST_IGNORE_IMMUNE_FLAG)) + if (!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_ASSIST_IGNORE_IMMUNE_FLAG)) { if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE)) { @@ -12040,7 +12040,7 @@ bool Unit::_IsValidAssistTarget(Unit const* target, SpellInfo const* bySpell) co // PvC case - player can assist creature only if has specific type flags // !target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) && else if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) - && (!bySpell || !(bySpell->AttributesEx6 & SPELL_ATTR6_ASSIST_IGNORE_IMMUNE_FLAG)) + && (!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_ASSIST_IGNORE_IMMUNE_FLAG)) && !((target->GetByteValue(UNIT_FIELD_BYTES_2, 1) & UNIT_BYTE2_FLAG_PVP))) { if (Creature const* creatureTarget = target->ToCreature()) @@ -12822,7 +12822,7 @@ int32 Unit::ModSpellDuration(SpellInfo const* spellProto, Unit const* target, in return duration; // some auras are not affected by duration modifiers - if (spellProto->AttributesEx7 & SPELL_ATTR7_IGNORE_DURATION_MODS) + if (spellProto->HasAttribute(SPELL_ATTR7_IGNORE_DURATION_MODS)) return duration; // cut duration only of negative effects @@ -12926,17 +12926,17 @@ void Unit::ModSpellCastTime(SpellInfo const* spellInfo, int32 & castTime, Spell* if (!spellInfo || castTime < 0) return; - if (spellInfo->IsChanneled() && !(spellInfo->AttributesEx5 & SPELL_ATTR5_HASTE_AFFECT_DURATION)) + if (spellInfo->IsChanneled() && !spellInfo->HasAttribute(SPELL_ATTR5_HASTE_AFFECT_DURATION)) return; // called from caster if (Player* modOwner = GetSpellModOwner()) modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CASTING_TIME, castTime, spell); - if (!((spellInfo->Attributes & (SPELL_ATTR0_ABILITY | SPELL_ATTR0_TRADESPELL)) || (spellInfo->AttributesEx3 & SPELL_ATTR3_NO_DONE_BONUS)) && + if (!(spellInfo->HasAttribute(SPELL_ATTR0_ABILITY) || spellInfo->HasAttribute(SPELL_ATTR0_TRADESPELL) || spellInfo->HasAttribute(SPELL_ATTR0_TRADESPELL) || spellInfo->HasAttribute(SPELL_ATTR3_NO_DONE_BONUS)) && ((GetTypeId() == TYPEID_PLAYER && spellInfo->SpellFamilyName) || GetTypeId() == TYPEID_UNIT)) castTime = int32(float(castTime) * GetFloatValue(UNIT_MOD_CAST_SPEED)); - else if (spellInfo->Attributes & SPELL_ATTR0_REQ_AMMO && !(spellInfo->AttributesEx2 & SPELL_ATTR2_AUTOREPEAT_FLAG)) + else if (spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) && !spellInfo->HasAttribute(SPELL_ATTR2_AUTOREPEAT_FLAG)) castTime = int32(float(castTime) * m_modAttackSpeedPct[RANGED_ATTACK]); else if (spellInfo->SpellVisual[0] == 3881 && HasAura(67556)) // cooking with Chef Hat. castTime = 500; @@ -13711,7 +13711,7 @@ void CharmInfo::InitPossessCreateSpells() { uint32 spellId = _unit->ToCreature()->m_spells[i]; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); - if (spellInfo && !(spellInfo->Attributes & SPELL_ATTR0_CASTABLE_WHILE_DEAD)) + if (spellInfo && !spellInfo->HasAttribute(SPELL_ATTR0_CASTABLE_WHILE_DEAD)) { if (spellInfo->IsPassive()) _unit->CastSpell(_unit, spellInfo, true); @@ -13739,7 +13739,7 @@ void CharmInfo::InitCharmCreateSpells() uint32 spellId = _unit->ToCreature()->m_spells[x]; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); - if (!spellInfo || spellInfo->Attributes & SPELL_ATTR0_CASTABLE_WHILE_DEAD) + if (!spellInfo || spellInfo->HasAttribute(SPELL_ATTR0_CASTABLE_WHILE_DEAD)) { _charmspells[x].SetActionAndType(spellId, ACT_DISABLED); continue; @@ -14126,7 +14126,7 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u continue; // Triggered spells not triggering additional spells - bool triggered = !(spellProto->AttributesEx3 & SPELL_ATTR3_CAN_PROC_WITH_TRIGGERED) ? + bool triggered = !spellProto->HasAttribute(SPELL_ATTR3_CAN_PROC_WITH_TRIGGERED) ? (procExtra & PROC_EX_INTERNAL_TRIGGERED && !(procFlag & PROC_FLAG_DONE_TRAP_ACTIVATION)) : false; for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) @@ -14183,7 +14183,7 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u cooldown = i->spellProcEvent->cooldown; // Note: must SetCantProc(false) before return - if (spellInfo->AttributesEx3 & SPELL_ATTR3_DISABLE_PROC) + if (spellInfo->HasAttribute(SPELL_ATTR3_DISABLE_PROC)) SetCantProc(true); bool handled = i->aura->CallScriptProcHandlers(aurApp, eventInfo); @@ -14378,7 +14378,7 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u i->aura->CallScriptAfterProcHandlers(aurApp, eventInfo); - if (spellInfo->AttributesEx3 & SPELL_ATTR3_DISABLE_PROC) + if (spellInfo->HasAttribute(SPELL_ATTR3_DISABLE_PROC)) SetCantProc(false); } @@ -15053,7 +15053,7 @@ bool Unit::IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const // Additional checks for triggered spells (ignore trap casts) if (procExtra & PROC_EX_INTERNAL_TRIGGERED && !(procFlag & PROC_FLAG_DONE_TRAP_ACTIVATION)) { - if (!(spellProto->AttributesEx3 & SPELL_ATTR3_CAN_PROC_WITH_TRIGGERED)) + if (!spellProto->HasAttribute(SPELL_ATTR3_CAN_PROC_WITH_TRIGGERED)) return false; } @@ -16213,7 +16213,7 @@ Aura* Unit::AddAura(uint32 spellId, Unit* target) if (!spellInfo) return NULL; - if (!target->IsAlive() && !(spellInfo->Attributes & SPELL_ATTR0_PASSIVE) && !(spellInfo->AttributesEx2 & SPELL_ATTR2_CAN_TARGET_DEAD)) + if (!target->IsAlive() && !spellInfo->HasAttribute(SPELL_ATTR0_PASSIVE) && !spellInfo->HasAttribute(SPELL_ATTR2_CAN_TARGET_DEAD)) return NULL; return AddAura(spellInfo, MAX_EFFECT_MASK, target); @@ -17316,30 +17316,30 @@ void Unit::StopAttackFaction(uint32 faction_id) void Unit::OutDebugInfo() const { TC_LOG_ERROR("entities.unit", "Unit::OutDebugInfo"); - TC_LOG_INFO("entities.unit", "%s name %s", GetGUID().ToString().c_str(), GetName().c_str()); - TC_LOG_INFO("entities.unit", "Owner %s, Minion %s, Charmer %s, Charmed %s", GetOwnerGUID().ToString().c_str(), GetMinionGUID().ToString().c_str(), GetCharmerGUID().ToString().c_str(), GetCharmGUID().ToString().c_str()); - TC_LOG_INFO("entities.unit", "In world %u, unit type mask %u", (uint32)(IsInWorld() ? 1 : 0), m_unitTypeMask); + TC_LOG_DEBUG("entities.unit", "%s name %s", GetGUID().ToString().c_str(), GetName().c_str()); + TC_LOG_DEBUG("entities.unit", "Owner %s, Minion %s, Charmer %s, Charmed %s", GetOwnerGUID().ToString().c_str(), GetMinionGUID().ToString().c_str(), GetCharmerGUID().ToString().c_str(), GetCharmGUID().ToString().c_str()); + TC_LOG_DEBUG("entities.unit", "In world %u, unit type mask %u", (uint32)(IsInWorld() ? 1 : 0), m_unitTypeMask); if (IsInWorld()) - TC_LOG_INFO("entities.unit", "Mapid %u", GetMapId()); + TC_LOG_DEBUG("entities.unit", "Mapid %u", GetMapId()); std::ostringstream o; o << "Summon Slot: "; for (uint32 i = 0; i < MAX_SUMMON_SLOT; ++i) o << m_SummonSlot[i].ToString() << ", "; - TC_LOG_INFO("entities.unit", "%s", o.str().c_str()); + TC_LOG_DEBUG("entities.unit", "%s", o.str().c_str()); o.str(""); o << "Controlled List: "; for (ControlList::const_iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr) o << (*itr)->GetGUID().ToString() << ", "; - TC_LOG_INFO("entities.unit", "%s", o.str().c_str()); + TC_LOG_DEBUG("entities.unit", "%s", o.str().c_str()); o.str(""); o << "Aura List: "; for (AuraApplicationMap::const_iterator itr = m_appliedAuras.begin(); itr != m_appliedAuras.end(); ++itr) o << itr->first << ", "; - TC_LOG_INFO("entities.unit", "%s", o.str().c_str()); + TC_LOG_DEBUG("entities.unit", "%s", o.str().c_str()); o.str(""); if (IsVehicle()) @@ -17348,11 +17348,11 @@ void Unit::OutDebugInfo() const for (SeatMap::iterator itr = GetVehicleKit()->Seats.begin(); itr != GetVehicleKit()->Seats.end(); ++itr) if (Unit* passenger = ObjectAccessor::GetUnit(*GetVehicleBase(), itr->second.Passenger.Guid)) o << passenger->GetGUID().ToString() << ", "; - TC_LOG_INFO("entities.unit", "%s", o.str().c_str()); + TC_LOG_DEBUG("entities.unit", "%s", o.str().c_str()); } if (GetVehicle()) - TC_LOG_INFO("entities.unit", "On vehicle %u.", GetVehicleBase()->GetEntry()); + TC_LOG_DEBUG("entities.unit", "On vehicle %u.", GetVehicleBase()->GetEntry()); } uint32 Unit::GetRemainingPeriodicAmount(ObjectGuid caster, uint32 spellId, AuraType auraType, uint8 effectIndex) const diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index d07ec212d0c..1db9183fbe4 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -3893,6 +3893,8 @@ void ObjectMgr::BuildPlayerLevelInfo(uint8 race, uint8 _class, uint8 level, Play } } +int32 const ReputationMgr::Reputation_Cap; + void ObjectMgr::LoadQuests() { uint32 oldMSTime = getMSTime(); @@ -7078,6 +7080,7 @@ void ObjectMgr::LoadReputationSpilloverTemplate() continue; } + bool invalidSpilloverFaction = false; for (uint32 i = 0; i < MAX_SPILLOVER_FACTIONS; ++i) { if (repTemplate.faction[i]) @@ -7087,47 +7090,28 @@ void ObjectMgr::LoadReputationSpilloverTemplate() if (!factionSpillover) { TC_LOG_ERROR("sql.sql", "Spillover faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template` for faction %u, skipping", repTemplate.faction[i], factionId); - continue; + invalidSpilloverFaction = true; + break; } if (factionSpillover->reputationListID < 0) { TC_LOG_ERROR("sql.sql", "Spillover faction (faction.dbc) %u for faction %u in `reputation_spillover_template` can not be listed for client, and then useless, skipping", repTemplate.faction[i], factionId); - continue; + invalidSpilloverFaction = true; + break; } if (repTemplate.faction_rank[i] >= MAX_REPUTATION_RANK) { TC_LOG_ERROR("sql.sql", "Rank %u used in `reputation_spillover_template` for spillover faction %u is not valid, skipping", repTemplate.faction_rank[i], repTemplate.faction[i]); - continue; + invalidSpilloverFaction = true; + break; } } } - FactionEntry const* factionEntry0 = sFactionStore.LookupEntry(repTemplate.faction[0]); - if (repTemplate.faction[0] && !factionEntry0) - { - TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", repTemplate.faction[0]); - continue; - } - FactionEntry const* factionEntry1 = sFactionStore.LookupEntry(repTemplate.faction[1]); - if (repTemplate.faction[1] && !factionEntry1) - { - TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", repTemplate.faction[1]); - continue; - } - FactionEntry const* factionEntry2 = sFactionStore.LookupEntry(repTemplate.faction[2]); - if (repTemplate.faction[2] && !factionEntry2) - { - TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", repTemplate.faction[2]); - continue; - } - FactionEntry const* factionEntry3 = sFactionStore.LookupEntry(repTemplate.faction[3]); - if (repTemplate.faction[3] && !factionEntry3) - { - TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", repTemplate.faction[3]); + if (invalidSpilloverFaction) continue; - } _repSpilloverTemplateStore[factionId] = repTemplate; @@ -8619,7 +8603,6 @@ void ObjectMgr::LoadBroadcastTexts() } _broadcastTextStore.rehash(result->GetRowCount()); - uint32 count = 0; do { @@ -8645,26 +8628,23 @@ void ObjectMgr::LoadBroadcastTexts() { if (!sSoundEntriesStore.LookupEntry(bct.SoundId)) { - TC_LOG_INFO("sql.sql", "BroadcastText (Id: %u) in table `broadcast_text` has SoundId %u but sound does not exist. Skipped.", bct.Id, bct.SoundId); - // don't load bct of higher expansions - continue; + TC_LOG_DEBUG("broadcasttext", "BroadcastText (Id: %u) in table `broadcast_text` has SoundId %u but sound does not exist.", bct.Id, bct.SoundId); + bct.SoundId = 0; } } if (!GetLanguageDescByID(bct.Language)) { - TC_LOG_INFO("sql.sql", "BroadcastText (Id: %u) in table `broadcast_text` using Language %u but Language does not exist. Skipped.", bct.Id, bct.Language); - // don't load bct of higher expansions - continue; + TC_LOG_DEBUG("broadcasttext", "BroadcastText (Id: %u) in table `broadcast_text` using Language %u but Language does not exist.", bct.Id, bct.Language); + bct.Language = LANG_UNIVERSAL; } if (bct.EmoteId0) { if (!sEmotesStore.LookupEntry(bct.EmoteId0)) { - TC_LOG_INFO("sql.sql", "BroadcastText (Id: %u) in table `broadcast_text` has EmoteId0 %u but emote does not exist. Skipped.", bct.Id, bct.EmoteId0); - // don't load bct of higher expansions - continue; + TC_LOG_DEBUG("broadcasttext", "BroadcastText (Id: %u) in table `broadcast_text` has EmoteId0 %u but emote does not exist.", bct.Id, bct.EmoteId0); + bct.EmoteId0 = 0; } } @@ -8672,9 +8652,8 @@ void ObjectMgr::LoadBroadcastTexts() { if (!sEmotesStore.LookupEntry(bct.EmoteId1)) { - TC_LOG_INFO("sql.sql", "BroadcastText (Id: %u) in table `broadcast_text` has EmoteId1 %u but emote does not exist. Skipped.", bct.Id, bct.EmoteId1); - // don't load bct of higher expansions - continue; + TC_LOG_DEBUG("broadcasttext", "BroadcastText (Id: %u) in table `broadcast_text` has EmoteId1 %u but emote does not exist.", bct.Id, bct.EmoteId1); + bct.EmoteId1 = 0; } } @@ -8682,19 +8661,16 @@ void ObjectMgr::LoadBroadcastTexts() { if (!sEmotesStore.LookupEntry(bct.EmoteId2)) { - TC_LOG_INFO("sql.sql", "BroadcastText (Id: %u) in table `broadcast_text` has EmoteId2 %u but emote does not exist. Skipped.", bct.Id, bct.EmoteId2); - // don't load bct of higher expansions - continue; + TC_LOG_DEBUG("broadcasttext", "BroadcastText (Id: %u) in table `broadcast_text` has EmoteId2 %u but emote does not exist.", bct.Id, bct.EmoteId2); + bct.EmoteId2 = 0; } } _broadcastTextStore[bct.Id] = bct; - - ++count; } while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Loaded %u broadcast texts in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " broadcast texts in %u ms", _broadcastTextStore.size(), GetMSTimeDiffToNow(oldMSTime)); } void ObjectMgr::LoadBroadcastTextLocales() @@ -8720,8 +8696,7 @@ void ObjectMgr::LoadBroadcastTextLocales() BroadcastTextContainer::iterator bct = _broadcastTextStore.find(id); if (bct == _broadcastTextStore.end()) { - TC_LOG_INFO("sql.sql", "BroadcastText (Id: %u) in table `locales_broadcast_text` does not exist or is incompatible. Skipped!", id); - // don't load bct of higher expansions + TC_LOG_ERROR("sql.sql", "BroadcastText (Id: %u) in table `locales_broadcast_text` does not exist. Skipped!", id); continue; } diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.cpp b/src/server/game/Grids/Notifiers/GridNotifiers.cpp index e14910bdf5e..abe302d2d3d 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.cpp +++ b/src/server/game/Grids/Notifiers/GridNotifiers.cpp @@ -65,7 +65,7 @@ void VisibleNotifier::SendToSelf() } } - for (GuidSet::const_iterator it = vis_guids.begin(); it != vis_guids.end(); ++it) + for (auto it = vis_guids.begin(); it != vis_guids.end(); ++it) { i_player.m_clientGUIDs.erase(*it); i_data.AddOutOfRangeGUID(*it); diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.h b/src/server/game/Grids/Notifiers/GridNotifiers.h index 5eb726084b3..ccf9748e67a 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.h +++ b/src/server/game/Grids/Notifiers/GridNotifiers.h @@ -43,7 +43,7 @@ namespace Trinity Player &i_player; UpdateData i_data; std::set<Unit*> i_visibleNow; - GuidSet vis_guids; + GuidUnorderedSet vis_guids; VisibleNotifier(Player &player) : i_player(player), vis_guids(player.m_clientGUIDs) { } template<class T> void Visit(GridRefManager<T> &m); diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp index d9124551c63..5664cbb6e4b 100644 --- a/src/server/game/Groups/Group.cpp +++ b/src/server/game/Groups/Group.cpp @@ -1808,7 +1808,7 @@ GroupJoinBattlegroundResult Group::CanJoinBattlegroundQueue(Battleground const* return ERR_BATTLEGROUND_NONE; // ERR_GROUP_JOIN_BATTLEGROUND_TOO_MANY handled on client side // get a player as reference, to compare other players' stats to (arena team id, queue id based on level, etc.) - Player* reference = GetFirstMember()->GetSource(); + Player* reference = ASSERT_NOTNULL(GetFirstMember())->GetSource(); // no reference found, can't join this way if (!reference) return ERR_BATTLEGROUND_JOIN_FAILED; diff --git a/src/server/game/Handlers/PetHandler.cpp b/src/server/game/Handlers/PetHandler.cpp index 0fcaa47a5e2..9ff2041ee92 100644 --- a/src/server/game/Handlers/PetHandler.cpp +++ b/src/server/game/Handlers/PetHandler.cpp @@ -70,7 +70,7 @@ void WorldSession::HandlePetAction(WorldPacket& recvData) // used also for charmed creature Unit* pet = ObjectAccessor::GetUnit(*_player, guid1); - TC_LOG_INFO("network", "HandlePetAction: %s - flag: %u, spellid: %u, target: %s.", guid1.ToString().c_str(), uint32(flag), spellid, guid2.ToString().c_str()); + TC_LOG_DEBUG("network", "HandlePetAction: %s - flag: %u, spellid: %u, target: %s.", guid1.ToString().c_str(), uint32(flag), spellid, guid2.ToString().c_str()); if (!pet) { @@ -89,7 +89,7 @@ void WorldSession::HandlePetAction(WorldPacket& recvData) SpellInfo const* spell = (flag == ACT_ENABLED || flag == ACT_PASSIVE) ? sSpellMgr->GetSpellInfo(spellid) : NULL; if (!spell) return; - if (!(spell->Attributes & SPELL_ATTR0_CASTABLE_WHILE_DEAD)) + if (!spell->HasAttribute(SPELL_ATTR0_CASTABLE_WHILE_DEAD)) return; } @@ -398,7 +398,7 @@ void WorldSession::HandlePetActionHelper(Unit* pet, ObjectGuid guid1, uint32 spe void WorldSession::HandlePetNameQuery(WorldPacket& recvData) { - TC_LOG_INFO("network", "HandlePetNameQuery. CMSG_PET_NAME_QUERY"); + TC_LOG_DEBUG("network", "WORLD: Received CMSG_PET_NAME_QUERY"); uint32 petnumber; ObjectGuid petguid; @@ -465,7 +465,7 @@ bool WorldSession::CheckStableMaster(ObjectGuid guid) void WorldSession::HandlePetSetAction(WorldPacket& recvData) { - TC_LOG_INFO("network", "HandlePetSetAction. CMSG_PET_SET_ACTION"); + TC_LOG_DEBUG("network", "WORLD: Received CMSG_PET_SET_ACTION"); ObjectGuid petguid; uint8 count; @@ -545,7 +545,7 @@ void WorldSession::HandlePetSetAction(WorldPacket& recvData) uint32 spell_id = UNIT_ACTION_BUTTON_ACTION(data[i]); uint8 act_state = UNIT_ACTION_BUTTON_TYPE(data[i]); - TC_LOG_INFO("network", "Player %s has changed pet spell action. Position: %u, Spell: %u, State: 0x%X", + TC_LOG_DEBUG("network", "Player %s has changed pet spell action. Position: %u, Spell: %u, State: 0x%X", _player->GetName().c_str(), position[i], spell_id, uint32(act_state)); //if it's act for spell (en/disable/cast) and there is a spell given (0 = remove spell) which pet doesn't know, don't add @@ -582,7 +582,7 @@ void WorldSession::HandlePetSetAction(WorldPacket& recvData) void WorldSession::HandlePetRename(WorldPacket& recvData) { - TC_LOG_INFO("network", "HandlePetRename. CMSG_PET_RENAME"); + TC_LOG_DEBUG("network", "WORLD: Received CMSG_PET_RENAME"); ObjectGuid petguid; uint8 isdeclined; @@ -670,7 +670,7 @@ void WorldSession::HandlePetAbandon(WorldPacket& recvData) { ObjectGuid guid; recvData >> guid; //pet guid - TC_LOG_INFO("network", "HandlePetAbandon. CMSG_PET_ABANDON %s", guid.ToString().c_str()); + TC_LOG_DEBUG("network", "WORLD: Received CMSG_PET_ABANDON %s", guid.ToString().c_str()); if (!_player->IsInWorld()) return; @@ -696,7 +696,7 @@ void WorldSession::HandlePetAbandon(WorldPacket& recvData) void WorldSession::HandlePetSpellAutocastOpcode(WorldPacket& recvPacket) { - TC_LOG_INFO("network", "CMSG_PET_SPELL_AUTOCAST"); + TC_LOG_DEBUG("network", "WORLD: Received CMSG_PET_SPELL_AUTOCAST"); ObjectGuid guid; uint32 spellid; uint8 state; //1 for on, 0 for off @@ -744,7 +744,7 @@ void WorldSession::HandlePetSpellAutocastOpcode(WorldPacket& recvPacket) void WorldSession::HandlePetCastSpellOpcode(WorldPacket& recvPacket) { - TC_LOG_DEBUG("network", "WORLD: CMSG_PET_CAST_SPELL"); + TC_LOG_DEBUG("network", "WORLD: Received CMSG_PET_CAST_SPELL"); ObjectGuid guid; uint8 castCount; @@ -846,7 +846,7 @@ void WorldSession::SendPetNameInvalid(uint32 error, const std::string& name, Dec void WorldSession::HandlePetLearnTalent(WorldPacket& recvData) { - TC_LOG_DEBUG("network", "WORLD: CMSG_PET_LEARN_TALENT"); + TC_LOG_DEBUG("network", "WORLD: Received CMSG_PET_LEARN_TALENT"); ObjectGuid guid; uint32 talentId, requestedRank; @@ -858,7 +858,7 @@ void WorldSession::HandlePetLearnTalent(WorldPacket& recvData) void WorldSession::HandleLearnPreviewTalentsPet(WorldPacket& recvData) { - TC_LOG_DEBUG("network", "CMSG_LEARN_PREVIEW_TALENTS_PET"); + TC_LOG_DEBUG("network", "WORLD: Received CMSG_LEARN_PREVIEW_TALENTS_PET"); ObjectGuid guid; recvData >> guid; diff --git a/src/server/game/Handlers/QuestHandler.cpp b/src/server/game/Handlers/QuestHandler.cpp index d6d7e3b9876..3c6258b2d7c 100644 --- a/src/server/game/Handlers/QuestHandler.cpp +++ b/src/server/game/Handlers/QuestHandler.cpp @@ -458,6 +458,12 @@ void WorldSession::HandleQuestConfirmAccept(WorldPacket& recvData) if (!_player->IsInSameRaidWith(originalPlayer)) return; + if (!originalPlayer->CanShareQuest(questId)) + return; + + if (!_player->CanTakeQuest(quest, true)) + return; + if (_player->CanAddQuest(quest, true)) _player->AddQuestAndCheckCompletion(quest, NULL); // NULL, this prevent DB script from duplicate running @@ -621,7 +627,7 @@ void WorldSession::HandleQuestgiverStatusMultipleQuery(WorldPacket& /*recvPacket WorldPacket data(SMSG_QUESTGIVER_STATUS_MULTIPLE, 4); data << uint32(count); // placeholder - for (GuidSet::const_iterator itr = _player->m_clientGUIDs.begin(); itr != _player->m_clientGUIDs.end(); ++itr) + for (auto itr = _player->m_clientGUIDs.begin(); itr != _player->m_clientGUIDs.end(); ++itr) { uint32 questStatus = DIALOG_STATUS_NONE; diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp index c4bd892d3cf..fede3e30211 100644 --- a/src/server/game/Handlers/SpellHandler.cpp +++ b/src/server/game/Handlers/SpellHandler.cpp @@ -417,7 +417,7 @@ void WorldSession::HandleCancelAuraOpcode(WorldPacket& recvPacket) return; // not allow remove spells with attr SPELL_ATTR0_CANT_CANCEL - if (spellInfo->Attributes & SPELL_ATTR0_CANT_CANCEL) + if (spellInfo->HasAttribute(SPELL_ATTR0_CANT_CANCEL)) return; // channeled spell case (it currently cast then) diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp index db6a2c98379..52b866850a6 100644 --- a/src/server/game/Loot/LootMgr.cpp +++ b/src/server/game/Loot/LootMgr.cpp @@ -1832,7 +1832,7 @@ void LoadLootTemplates_Spell() { // not report about not trainable spells (optionally supported by DB) // ignore 61756 (Northrend Inscription Research (FAST QA VERSION) for example - if (!(spellInfo->Attributes & SPELL_ATTR0_NOT_SHAPESHIFT) || (spellInfo->Attributes & SPELL_ATTR0_TRADESPELL)) + if (!spellInfo->HasAttribute(SPELL_ATTR0_NOT_SHAPESHIFT) || spellInfo->HasAttribute(SPELL_ATTR0_TRADESPELL)) { LootTemplates_Spell.ReportNonExistingId(spell_id, "Spell", spellInfo->Id); } diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 49f7bc86081..b4066b2257e 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -80,7 +80,10 @@ bool Map::ExistMap(uint32 mapid, int gx, int gy) FILE* pf = fopen(fileName, "rb"); if (!pf) - TC_LOG_ERROR("maps", "Map file '%s': does not exist!", fileName); + { + TC_LOG_ERROR("maps", "Map file '%s' does not exist!", fileName); + TC_LOG_ERROR("maps", "Please place MAP-files (*.map) in the appropriate directory (%s), or correct the DataDir setting in your worldserver.conf file.", (sWorld->GetDataPath()+"maps/").c_str()); + } else { map_fileheader header; @@ -109,7 +112,8 @@ bool Map::ExistVMap(uint32 mapid, int gx, int gy) if (!exists) { std::string name = vmgr->getDirFileName(mapid, gx, gy); - TC_LOG_ERROR("maps", "VMap file '%s' is missing or points to wrong version of vmap file. Redo vmaps with latest version of vmap_assembler.exe.", (sWorld->GetDataPath()+"vmaps/"+name).c_str()); + TC_LOG_ERROR("maps", "VMap file '%s' does not exist", (sWorld->GetDataPath()+"vmaps/"+name).c_str()); + TC_LOG_ERROR("maps", "Please place VMAP-files (*.vmtree and *.vmtile) in the vmap-directory (%s), or correct the DataDir setting in your worldserver.conf file.", (sWorld->GetDataPath()+"vmaps/").c_str()); return false; } } @@ -126,9 +130,9 @@ void Map::LoadMMap(int gx, int gy) bool mmapLoadResult = MMAP::MMapFactory::createOrGetMMapManager()->loadMap((sWorld->GetDataPath() + "mmaps").c_str(), GetId(), gx, gy); if (mmapLoadResult) - TC_LOG_INFO("maps", "MMAP loaded name:%s, id:%d, x:%d, y:%d (mmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy); + TC_LOG_DEBUG("maps", "MMAP loaded name:%s, id:%d, x:%d, y:%d (mmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy); else - TC_LOG_INFO("maps", "Could not load MMAP name:%s, id:%d, x:%d, y:%d (mmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy); + TC_LOG_ERROR("maps", "Could not load MMAP name:%s, id:%d, x:%d, y:%d (mmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy); } void Map::LoadVMap(int gx, int gy) @@ -140,10 +144,10 @@ void Map::LoadVMap(int gx, int gy) switch (vmapLoadResult) { case VMAP::VMAP_LOAD_RESULT_OK: - TC_LOG_INFO("maps", "VMAP loaded name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy); + TC_LOG_DEBUG("maps", "VMAP loaded name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy); break; case VMAP::VMAP_LOAD_RESULT_ERROR: - TC_LOG_INFO("maps", "Could not load VMAP name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy); + TC_LOG_ERROR("maps", "Could not load VMAP name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy); break; case VMAP::VMAP_LOAD_RESULT_IGNORED: TC_LOG_DEBUG("maps", "Ignored VMAP name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy); @@ -173,7 +177,7 @@ void Map::LoadMap(int gx, int gy, bool reload) //map already load, delete it before reloading (Is it necessary? Do we really need the ability the reload maps during runtime?) if (GridMaps[gx][gy]) { - TC_LOG_INFO("maps", "Unloading previously loaded map %u before reloading.", GetId()); + TC_LOG_DEBUG("maps", "Unloading previously loaded map %u before reloading.", GetId()); sScriptMgr->OnUnloadGridMap(this, GridMaps[gx][gy], gx, gy); delete (GridMaps[gx][gy]); @@ -185,7 +189,7 @@ void Map::LoadMap(int gx, int gy, bool reload) int len = sWorld->GetDataPath().length() + strlen("maps/%03u%02u%02u.map") + 1; tmp = new char[len]; snprintf(tmp, len, (char *)(sWorld->GetDataPath() + "maps/%03u%02u%02u.map").c_str(), GetId(), gx, gy); - TC_LOG_INFO("maps", "Loading map %s", tmp); + TC_LOG_DEBUG("maps", "Loading map %s", tmp); // loading data GridMaps[gx][gy] = new GridMap(); if (!GridMaps[gx][gy]->loadData(tmp)) @@ -2497,7 +2501,7 @@ void Map::UpdateObjectsVisibilityFor(Player* player, Cell cell, CellCoord cellpa void Map::SendInitSelf(Player* player) { - TC_LOG_INFO("maps", "Creating player data for himself %u", player->GetGUIDLow()); + TC_LOG_DEBUG("maps", "Creating player data for himself %u", player->GetGUIDLow()); UpdateData data; @@ -2849,7 +2853,7 @@ bool InstanceMap::CanEnter(Player* player) uint32 maxPlayers = GetMaxPlayers(); if (GetPlayersCountExceptGMs() >= maxPlayers) { - TC_LOG_INFO("maps", "MAP: Instance '%u' of map '%s' cannot have more than '%u' players. Player '%s' rejected", GetInstanceId(), GetMapName(), maxPlayers, player->GetName().c_str()); + TC_LOG_WARN("maps", "MAP: Instance '%u' of map '%s' cannot have more than '%u' players. Player '%s' rejected", GetInstanceId(), GetMapName(), maxPlayers, player->GetName().c_str()); player->SendTransferAborted(GetId(), TRANSFER_ABORT_MAX_PLAYERS); return false; } @@ -2918,7 +2922,7 @@ bool InstanceMap::AddPlayerToMap(Player* player) InstanceSave* mapSave = sInstanceSaveMgr->GetInstanceSave(GetInstanceId()); if (!mapSave) { - TC_LOG_INFO("maps", "InstanceMap::Add: creating instance save for map %d spawnmode %d with instance id %d", GetId(), GetSpawnMode(), GetInstanceId()); + TC_LOG_DEBUG("maps", "InstanceMap::Add: creating instance save for map %d spawnmode %d with instance id %d", GetId(), GetSpawnMode(), GetInstanceId()); mapSave = sInstanceSaveMgr->AddInstanceSave(GetId(), GetInstanceId(), Difficulty(GetSpawnMode()), 0, true); } @@ -2994,7 +2998,7 @@ bool InstanceMap::AddPlayerToMap(Player* player) // first player enters (no players yet) SetResetSchedule(false); - TC_LOG_INFO("maps", "MAP: Player '%s' entered instance '%u' of map '%s'", player->GetName().c_str(), GetInstanceId(), GetMapName()); + TC_LOG_DEBUG("maps", "MAP: Player '%s' entered instance '%u' of map '%s'", player->GetName().c_str(), GetInstanceId(), GetMapName()); // initialize unload state m_unloadTimer = 0; m_resetAfterUnload = false; @@ -3020,7 +3024,7 @@ void InstanceMap::Update(const uint32 t_diff) void InstanceMap::RemovePlayerFromMap(Player* player, bool remove) { - TC_LOG_INFO("maps", "MAP: Removing player '%s' from instance '%u' of map '%s' before relocating to another map", player->GetName().c_str(), GetInstanceId(), GetMapName()); + TC_LOG_DEBUG("maps", "MAP: Removing player '%s' from instance '%u' of map '%s' before relocating to another map", player->GetName().c_str(), GetInstanceId(), GetMapName()); //if last player set unload timer if (!m_unloadTimer && m_mapRefManager.getSize() == 1) m_unloadTimer = m_unloadWhenEmpty ? MIN_UNLOAD_DELAY : std::max(sWorld->getIntConfig(CONFIG_INSTANCE_UNLOAD_DELAY), (uint32)MIN_UNLOAD_DELAY); @@ -3253,7 +3257,7 @@ bool BattlegroundMap::AddPlayerToMap(Player* player) void BattlegroundMap::RemovePlayerFromMap(Player* player, bool remove) { - TC_LOG_INFO("maps", "MAP: Removing player '%s' from bg '%u' of map '%s' before relocating to another map", player->GetName().c_str(), GetInstanceId(), GetMapName()); + TC_LOG_DEBUG("maps", "MAP: Removing player '%s' from bg '%u' of map '%s' before relocating to another map", player->GetName().c_str(), GetInstanceId(), GetMapName()); Map::RemovePlayerFromMap(player, remove); } diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h index 8131984224f..9bae8fbdfae 100644 --- a/src/server/game/Miscellaneous/Language.h +++ b/src/server/game/Miscellaneous/Language.h @@ -207,7 +207,8 @@ enum TrinityStrings LANG_LIQUID_STATUS = 175, LANG_INVALID_GAMEOBJECT_TYPE = 176, LANG_GAMEOBJECT_DAMAGED = 177, - // Room for more level 1 178-199 not used + LANG_GRID_POSITION = 178, + // Room for more level 1 179-199 not used // level 2 chat LANG_NO_SELECTION = 200, diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index 0775a9a299a..685bcd338a9 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -427,7 +427,7 @@ enum SpellAttr4 SPELL_ATTR4_UNK4 = 0x00000010, // 4 This will no longer cause guards to attack on use?? SPELL_ATTR4_UNK5 = 0x00000020, // 5 SPELL_ATTR4_NOT_STEALABLE = 0x00000040, // 6 although such auras might be dispellable, they cannot be stolen - SPELL_ATTR4_TRIGGERED = 0x00000080, // 7 spells forced to be triggered + SPELL_ATTR4_CAN_CAST_WHILE_CASTING = 0x00000080, // 7 Can be cast while another cast is in progress - see CanCastWhileCasting(SpellRec const*,CGUnit_C *,int &) SPELL_ATTR4_FIXED_DAMAGE = 0x00000100, // 8 Ignores resilience and any (except mechanic related) damage or % damage taken auras on target. SPELL_ATTR4_TRIGGER_ACTIVATE = 0x00000200, // 9 initially disabled / trigger activate from event (Execute, Riposte, Deep Freeze end other) SPELL_ATTR4_SPELL_VS_EXTEND_COST = 0x00000400, // 10 Rogue Shiv have this flag diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp index 298e534d58e..bda3ad851c8 100644 --- a/src/server/game/Movement/MotionMaster.cpp +++ b/src/server/game/Movement/MotionMaster.cpp @@ -387,6 +387,43 @@ void MotionMaster::MoveJump(float x, float y, float z, float speedXY, float spee Mutate(new EffectMovementGenerator(id), MOTION_SLOT_CONTROLLED); } +void MotionMaster::MoveCirclePath(float x, float y, float z, float radius, bool clockwise, uint8 stepCount) +{ + float step = 2 * float(M_PI) / stepCount * (clockwise ? -1.0f : 1.0f); + Position const& pos = { x, y, z, 0.0f }; + float angle = pos.GetAngle(_owner->GetPositionX(), _owner->GetPositionY()); + + Movement::MoveSplineInit init(_owner); + + for (uint8 i = 0; i < stepCount; angle += step, ++i) + { + G3D::Vector3 point; + point.x = x + radius * cosf(angle); + point.y = y + radius * sinf(angle); + + if (_owner->IsFlying()) + point.z = z; + else + point.z = _owner->GetMap()->GetHeight(_owner->GetPhaseMask(), point.x, point.y, z); + + init.Path().push_back(point); + } + + if (_owner->IsFlying()) + { + init.SetFly(); + init.SetCyclic(); + init.SetAnimation(Movement::ToFly); + } + else + { + init.SetWalk(true); + init.SetCyclic(); + } + + init.Launch(); +} + void MotionMaster::MoveFall(uint32 id /*=0*/) { // use larger distance for vmap height search than in most other cases @@ -647,7 +684,7 @@ void MotionMaster::DirectDelete(_Ty curr) void MotionMaster::DelayedDelete(_Ty curr) { - TC_LOG_FATAL("misc", "Unit (Entry %u) is trying to delete its updating MG (Type %u)!", _owner->GetEntry(), curr->GetMovementGeneratorType()); + TC_LOG_FATAL("misc", "Unit (Entry %u) is trying to delete its updating Movement Generator (Type %u)!", _owner->GetEntry(), curr->GetMovementGeneratorType()); if (isStatic(curr)) return; if (!_expList) diff --git a/src/server/game/Movement/MotionMaster.h b/src/server/game/Movement/MotionMaster.h index 2821cd5a59b..0b547d96e7f 100644 --- a/src/server/game/Movement/MotionMaster.h +++ b/src/server/game/Movement/MotionMaster.h @@ -184,6 +184,7 @@ class MotionMaster //: private std::stack<MovementGenerator *> void MoveJump(Position const& pos, float speedXY, float speedZ, uint32 id = EVENT_JUMP) { MoveJump(pos.m_positionX, pos.m_positionY, pos.m_positionZ, speedXY, speedZ, id); }; void MoveJump(float x, float y, float z, float speedXY, float speedZ, uint32 id = EVENT_JUMP); + void MoveCirclePath(float x, float y, float z, float radius, bool clockwise, uint8 stepCount); void MoveFall(uint32 id = 0); void MoveSeekAssistance(float x, float y, float z); diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp index ed1ea7f4b17..a382bf9547e 100755 --- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp @@ -379,9 +379,9 @@ void FlightPathMovementGenerator::PreloadEndGrid() // Load the grid if (endMap) { - TC_LOG_INFO("misc", "Preloading rid (%f, %f) for map %u at node index %u/%u", _endGridX, _endGridY, _endMapId, _preloadTargetNode, (uint32)(i_path->size()-1)); + TC_LOG_DEBUG("misc", "Preloading rid (%f, %f) for map %u at node index %u/%u", _endGridX, _endGridY, _endMapId, _preloadTargetNode, (uint32)(i_path->size()-1)); endMap->LoadGrid(_endGridX, _endGridY); } else - TC_LOG_INFO("misc", "Unable to determine map to preload flightmaster grid"); + TC_LOG_DEBUG("misc", "Unable to determine map to preload flightmaster grid"); } diff --git a/src/server/game/Movement/Spline/MoveSpline.cpp b/src/server/game/Movement/Spline/MoveSpline.cpp index d3fa03c1238..1beeebbbad3 100644 --- a/src/server/game/Movement/Spline/MoveSpline.cpp +++ b/src/server/game/Movement/Spline/MoveSpline.cpp @@ -147,7 +147,7 @@ void MoveSpline::init_spline(const MoveSplineInitArgs& args) /// @todo what to do in such cases? problem is in input data (all points are at same coords) if (spline.length() < minimal_duration) { - TC_LOG_ERROR("misc", "MoveSpline::init_spline: zero length spline, wrong input data?"); + TC_LOG_DEBUG("misc", "MoveSpline::init_spline: zero length spline, wrong input data?"); spline.set_length(spline.last(), spline.isCyclic() ? 1000 : 1); } point_Idx = spline.first(); diff --git a/src/server/game/Reputation/ReputationMgr.cpp b/src/server/game/Reputation/ReputationMgr.cpp index ad71381a1de..b99cb677ba0 100644 --- a/src/server/game/Reputation/ReputationMgr.cpp +++ b/src/server/game/Reputation/ReputationMgr.cpp @@ -297,7 +297,7 @@ bool ReputationMgr::SetReputation(FactionEntry const* factionEntry, int32 standi { // bonuses are already given, so just modify standing by rate int32 spilloverRep = int32(standing * repTemplate->faction_rate[i]); - SetOneFactionReputation(sFactionStore.LookupEntry(repTemplate->faction[i]), spilloverRep, incremental); + SetOneFactionReputation(sFactionStore.AssertEntry(repTemplate->faction[i]), spilloverRep, incremental); } } } diff --git a/src/server/game/Scripting/ScriptLoader.cpp b/src/server/game/Scripting/ScriptLoader.cpp index 025184ff6ef..8be5da91c32 100644 --- a/src/server/game/Scripting/ScriptLoader.cpp +++ b/src/server/game/Scripting/ScriptLoader.cpp @@ -356,7 +356,6 @@ void AddSC_dustwallow_marsh(); void AddSC_felwood(); void AddSC_feralas(); void AddSC_moonglade(); -void AddSC_mulgore(); void AddSC_orgrimmar(); void AddSC_silithus(); void AddSC_stonetalon_mountains(); @@ -426,7 +425,8 @@ void AddSC_boss_heigan(); void AddSC_boss_gothik(); void AddSC_boss_thaddius(); void AddSC_instance_naxxramas(); -void AddSC_boss_magus_telestra(); //The Nexus Nexus +void AddSC_boss_nexus_commanders(); // The Nexus Nexus +void AddSC_boss_magus_telestra(); void AddSC_boss_anomalus(); void AddSC_boss_ormorok(); void AddSC_boss_keristrasza(); @@ -1061,7 +1061,6 @@ void AddKalimdorScripts() AddSC_felwood(); AddSC_feralas(); AddSC_moonglade(); - AddSC_mulgore(); AddSC_orgrimmar(); AddSC_silithus(); AddSC_stonetalon_mountains(); @@ -1255,7 +1254,8 @@ void AddNorthrendScripts() AddSC_boss_gothik(); AddSC_boss_thaddius(); AddSC_instance_naxxramas(); - AddSC_boss_magus_telestra(); //The Nexus Nexus + AddSC_boss_nexus_commanders(); // The Nexus Nexus + AddSC_boss_magus_telestra(); AddSC_boss_anomalus(); AddSC_boss_ormorok(); AddSC_boss_keristrasza(); diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp index 628452d120c..aed829a7b57 100644 --- a/src/server/game/Scripting/ScriptMgr.cpp +++ b/src/server/game/Scripting/ScriptMgr.cpp @@ -182,9 +182,8 @@ struct TSpellSummary uint8 Effects; // set of enum SelectEffect } *SpellSummary; -ScriptMgr::ScriptMgr() : _scriptCount(0) +ScriptMgr::ScriptMgr() : _scriptCount(0), _scheduledScripts(0) { - _scheduledScripts = 0; } ScriptMgr::~ScriptMgr() { } diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index e290b13c635..a226f1b7ed2 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -1142,7 +1142,7 @@ class ScriptMgr uint32 _scriptCount; //atomic op counter for active scripts amount - std::atomic_long _scheduledScripts; + std::atomic<uint32> _scheduledScripts; }; #endif diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index b6b1a4992af..fa50fd56576 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -218,8 +218,8 @@ void WorldSession::SendPacket(WorldPacket* packet) { uint64 minTime = uint64(cur_time - lastTime); uint64 fullTime = uint64(lastTime - firstTime); - TC_LOG_INFO("misc", "Send all time packets count: " UI64FMTD " bytes: " UI64FMTD " avr.count/sec: %f avr.bytes/sec: %f time: %u", sendPacketCount, sendPacketBytes, float(sendPacketCount)/fullTime, float(sendPacketBytes)/fullTime, uint32(fullTime)); - TC_LOG_INFO("misc", "Send last min packets count: " UI64FMTD " bytes: " UI64FMTD " avr.count/sec: %f avr.bytes/sec: %f", sendLastPacketCount, sendLastPacketBytes, float(sendLastPacketCount)/minTime, float(sendLastPacketBytes)/minTime); + TC_LOG_DEBUG("misc", "Send all time packets count: " UI64FMTD " bytes: " UI64FMTD " avr.count/sec: %f avr.bytes/sec: %f time: %u", sendPacketCount, sendPacketBytes, float(sendPacketCount)/fullTime, float(sendPacketBytes)/fullTime, uint32(fullTime)); + TC_LOG_DEBUG("misc", "Send last min packets count: " UI64FMTD " bytes: " UI64FMTD " avr.count/sec: %f avr.bytes/sec: %f", sendLastPacketCount, sendLastPacketBytes, float(sendLastPacketCount)/minTime, float(sendLastPacketBytes)/minTime); lastTime = cur_time; sendLastPacketCount = 1; @@ -981,7 +981,7 @@ void WorldSession::ReadAddonsInfo(WorldPacket &data) addonInfo >> enabled >> crc >> unk1; - TC_LOG_INFO("misc", "ADDON: Name: %s, Enabled: 0x%x, CRC: 0x%x, Unknown2: 0x%x", addonName.c_str(), enabled, crc, unk1); + TC_LOG_DEBUG("misc", "ADDON: Name: %s, Enabled: 0x%x, CRC: 0x%x, Unknown2: 0x%x", addonName.c_str(), enabled, crc, unk1); AddonInfo addon(addonName, enabled, crc, 2, true); @@ -989,15 +989,15 @@ void WorldSession::ReadAddonsInfo(WorldPacket &data) if (savedAddon) { if (addon.CRC != savedAddon->CRC) - TC_LOG_INFO("misc", "ADDON: %s was known, but didn't match known CRC (0x%x)!", addon.Name.c_str(), savedAddon->CRC); + TC_LOG_ERROR("misc", "ADDON: %s was known, but didn't match known CRC (0x%x)!", addon.Name.c_str(), savedAddon->CRC); else - TC_LOG_INFO("misc", "ADDON: %s was known, CRC is correct (0x%x)", addon.Name.c_str(), savedAddon->CRC); + TC_LOG_DEBUG("misc", "ADDON: %s was known, CRC is correct (0x%x)", addon.Name.c_str(), savedAddon->CRC); } else { AddonMgr::SaveAddon(addon); - TC_LOG_INFO("misc", "ADDON: %s (0x%x) was not known, saving...", addon.Name.c_str(), addon.CRC); + TC_LOG_DEBUG("misc", "ADDON: %s (0x%x) was not known, saving...", addon.Name.c_str(), addon.CRC); } /// @todo Find out when to not use CRC/pubkey, and other possible states. @@ -1048,7 +1048,7 @@ void WorldSession::SendAddonsInfo() data << uint8(usepk); if (usepk) // if CRC is wrong, add public key (client need it) { - TC_LOG_INFO("misc", "ADDON: CRC (0x%x) for addon %s is wrong (does not match expected 0x%x), sending pubkey", + TC_LOG_DEBUG("misc", "ADDON: CRC (0x%x) for addon %s is wrong (does not match expected 0x%x), sending pubkey", itr->CRC, itr->Name.c_str(), STANDARD_ADDON_CRC); data.append(addonPublicKey, sizeof(addonPublicKey)); @@ -1262,7 +1262,7 @@ bool WorldSession::DosProtection::EvaluateOpcode(WorldPacket& p, time_t time) co return true; case POLICY_KICK: { - TC_LOG_INFO("network", "AntiDOS: Player kicked!"); + TC_LOG_WARN("network", "AntiDOS: Player kicked!"); Session->KickPlayer(); return false; } @@ -1278,7 +1278,7 @@ bool WorldSession::DosProtection::EvaluateOpcode(WorldPacket& p, time_t time) co case BAN_IP: nameOrIp = Session->GetRemoteAddress(); break; } sWorld->BanAccount(bm, nameOrIp, duration, "DOS (Packet Flooding/Spoofing", "Server: AutoDOS"); - TC_LOG_INFO("network", "AntiDOS: Player automatically banned for %u seconds.", duration); + TC_LOG_WARN("network", "AntiDOS: Player automatically banned for %u seconds.", duration); Session->KickPlayer(); return false; } diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp index 5b1d9497953..0bb864de0ed 100644 --- a/src/server/game/Server/WorldSocket.cpp +++ b/src/server/game/Server/WorldSocket.cpp @@ -146,14 +146,12 @@ bool WorldSocket::ReadDataHandler() uint16 opcode = uint16(header->cmd); - std::string opcodeName = GetOpcodeNameForLogging(opcode); - WorldPacket packet(opcode, std::move(_packetBuffer)); if (sPacketLog->CanLogPacket()) sPacketLog->LogPacket(packet, CLIENT_TO_SERVER, GetRemoteIpAddress(), GetRemotePort()); - TC_LOG_TRACE("network.opcode", "C->S: %s %s", (_worldSession ? _worldSession->GetPlayerInfo() : GetRemoteIpAddress().to_string()).c_str(), opcodeName.c_str()); + TC_LOG_TRACE("network.opcode", "C->S: %s %s", (_worldSession ? _worldSession->GetPlayerInfo() : GetRemoteIpAddress().to_string()).c_str(), GetOpcodeNameForLogging(opcode).c_str()); switch (opcode) { @@ -170,7 +168,7 @@ bool WorldSocket::ReadDataHandler() HandleAuthSession(packet); break; case CMSG_KEEP_ALIVE: - TC_LOG_DEBUG("network", "%s", opcodeName.c_str()); + TC_LOG_DEBUG("network", "%s", GetOpcodeNameForLogging(opcode).c_str()); sScriptMgr->OnPacketReceive(_worldSession, packet); break; default: @@ -257,7 +255,7 @@ void WorldSocket::HandleAuthSession(WorldPacket& recvPacket) recvPacket >> unk4; recvPacket.read(digest, 20); - TC_LOG_INFO("network", "WorldSocket::HandleAuthSession: client %u, serverId %u, account %s, loginServerType %u, clientseed %u, realmIndex %u", + TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: client %u, serverId %u, account %s, loginServerType %u, clientseed %u, realmIndex %u", clientBuild, serverId, account.c_str(), @@ -429,7 +427,7 @@ void WorldSocket::HandleAuthSession(WorldPacket& recvPacket) if (allowedAccountType > SEC_PLAYER && AccountTypes(security) < allowedAccountType) { SendAuthResponseError(AUTH_UNAVAILABLE); - TC_LOG_INFO("network", "WorldSocket::HandleAuthSession: User tries to login but his security level is not enough"); + TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: User tries to login but his security level is not enough"); sScriptMgr->OnFailedAccountLogin(id); DelayedCloseSocket(); return; diff --git a/src/server/game/Server/WorldSocketMgr.cpp b/src/server/game/Server/WorldSocketMgr.cpp index 947bd62860c..e83d39cb922 100644 --- a/src/server/game/Server/WorldSocketMgr.cpp +++ b/src/server/game/Server/WorldSocketMgr.cpp @@ -46,6 +46,8 @@ WorldSocketMgr::WorldSocketMgr() : BaseSocketMgr(), _socketSendBufferSize(-1), m { } +int const boost::asio::socket_base::max_connections; + bool WorldSocketMgr::StartNetwork(boost::asio::io_service& service, std::string const& bindIp, uint16 port) { _tcpNoDelay = sConfigMgr->GetBoolDefault("Network.TcpNodelay", true); diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index e5b8cd111bc..5366326a022 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -542,11 +542,11 @@ void AuraEffect::CalculatePeriodic(Unit* caster, bool create, bool load) // Haste modifies periodic time of channeled spells if (m_spellInfo->IsChanneled()) { - if (m_spellInfo->AttributesEx5 & SPELL_ATTR5_HASTE_AFFECT_DURATION) + if (m_spellInfo->HasAttribute(SPELL_ATTR5_HASTE_AFFECT_DURATION)) caster->ModSpellCastTime(m_spellInfo, m_amplitude); } // and periodic time of auras affected by SPELL_AURA_PERIODIC_HASTE - else if (caster->HasAuraTypeWithAffectMask(SPELL_AURA_PERIODIC_HASTE, m_spellInfo) || m_spellInfo->AttributesEx5 & SPELL_ATTR5_HASTE_AFFECT_DURATION) + else if (caster->HasAuraTypeWithAffectMask(SPELL_AURA_PERIODIC_HASTE, m_spellInfo) || m_spellInfo->HasAttribute(SPELL_ATTR5_HASTE_AFFECT_DURATION)) m_amplitude = int32(m_amplitude * caster->GetFloatValue(UNIT_MOD_CAST_SPEED)); } } @@ -555,7 +555,7 @@ void AuraEffect::CalculatePeriodic(Unit* caster, bool create, bool load) { m_tickNumber = m_amplitude ? GetBase()->GetDuration() / m_amplitude : 0; m_periodicTimer = m_amplitude ? GetBase()->GetDuration() % m_amplitude : 0; - if (m_spellInfo->AttributesEx5 & SPELL_ATTR5_START_PERIODIC_AT_APPLY) + if (m_spellInfo->HasAttribute(SPELL_ATTR5_START_PERIODIC_AT_APPLY)) ++m_tickNumber; } else // aura just created or reapplied @@ -571,7 +571,7 @@ void AuraEffect::CalculatePeriodic(Unit* caster, bool create, bool load) { m_periodicTimer = 0; // Start periodic on next tick or at aura apply - if (m_amplitude && !(m_spellInfo->AttributesEx5 & SPELL_ATTR5_START_PERIODIC_AT_APPLY)) + if (m_amplitude && !m_spellInfo->HasAttribute(SPELL_ATTR5_START_PERIODIC_AT_APPLY)) m_periodicTimer += m_amplitude; } } @@ -1133,7 +1133,7 @@ void AuraEffect::HandleShapeshiftBoosts(Unit* target, bool apply) const continue; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first); - if (!spellInfo || !(spellInfo->Attributes & (SPELL_ATTR0_PASSIVE | SPELL_ATTR0_HIDDEN_CLIENTSIDE))) + if (!spellInfo || !(spellInfo->HasAttribute(SPELL_ATTR0_PASSIVE) || spellInfo->HasAttribute(SPELL_ATTR0_HIDDEN_CLIENTSIDE))) continue; if (spellInfo->Stances & (1<<(GetMiscValue()-1))) @@ -1148,7 +1148,7 @@ void AuraEffect::HandleShapeshiftBoosts(Unit* target, bool apply) const if (GlyphPropertiesEntry const* glyph = sGlyphPropertiesStore.LookupEntry(glyphId)) { SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(glyph->SpellId); - if (!spellInfo || !(spellInfo->Attributes & (SPELL_ATTR0_PASSIVE | SPELL_ATTR0_HIDDEN_CLIENTSIDE))) + if (!spellInfo || !(spellInfo->HasAttribute(SPELL_ATTR0_PASSIVE) || spellInfo->HasAttribute(SPELL_ATTR0_HIDDEN_CLIENTSIDE))) continue; if (spellInfo->Stances & (1<<(GetMiscValue()-1))) target->CastSpell(target, glyph->SpellId, true, NULL, this); @@ -3243,7 +3243,7 @@ void AuraEffect::HandleModStateImmunityMask(AuraApplication const* aurApp, uint8 for (std::list <AuraType>::iterator iter = aura_immunity_list.begin(); iter != aura_immunity_list.end(); ++iter) target->ApplySpellImmune(GetId(), IMMUNITY_STATE, *iter, apply); - if (apply && GetSpellInfo()->AttributesEx & SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY) + if (apply && GetSpellInfo()->HasAttribute(SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY)) { target->RemoveAurasWithMechanic(mechanic_immunity_list, AURA_REMOVE_BY_DEFAULT, GetId()); for (std::list <AuraType>::iterator iter = aura_immunity_list.begin(); iter != aura_immunity_list.end(); ++iter) @@ -3300,7 +3300,7 @@ void AuraEffect::HandleModMechanicImmunity(AuraApplication const* aurApp, uint8 break; } - if (apply && GetSpellInfo()->AttributesEx & SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY) + if (apply && GetSpellInfo()->HasAttribute(SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY)) target->RemoveAurasWithMechanic(mechanic, AURA_REMOVE_BY_DEFAULT, GetId()); } @@ -3336,7 +3336,7 @@ void AuraEffect::HandleAuraModStateImmunity(AuraApplication const* aurApp, uint8 target->ApplySpellImmune(GetId(), IMMUNITY_STATE, GetMiscValue(), apply); - if (apply && GetSpellInfo()->AttributesEx & SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY) + if (apply && GetSpellInfo()->HasAttribute(SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY)) target->RemoveAurasByType(AuraType(GetMiscValue()), ObjectGuid::Empty, GetBase()); } @@ -3372,13 +3372,13 @@ void AuraEffect::HandleAuraModSchoolImmunity(AuraApplication const* aurApp, uint target->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_IMMUNE_OR_LOST_SELECTION); // remove all flag auras (they are positive, but they must be removed when you are immune) - if (GetSpellInfo()->AttributesEx & SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY - && GetSpellInfo()->AttributesEx2 & SPELL_ATTR2_DAMAGE_REDUCED_SHIELD) + if (GetSpellInfo()->HasAttribute(SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY) + && GetSpellInfo()->HasAttribute(SPELL_ATTR2_DAMAGE_REDUCED_SHIELD)) target->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_IMMUNE_OR_LOST_SELECTION); /// @todo optimalize this cycle - use RemoveAurasWithInterruptFlags call or something else if ((apply) - && GetSpellInfo()->AttributesEx & SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY + && GetSpellInfo()->HasAttribute(SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY) && GetSpellInfo()->IsPositive()) //Only positive immunity removes auras { uint32 school_mask = GetMiscValue(); @@ -3759,7 +3759,7 @@ void AuraEffect::HandleModTotalPercentStat(AuraApplication const* aurApp, uint8 // recalculate current HP/MP after applying aura modifications (only for spells with SPELL_ATTR0_UNK4 0x00000010 flag) // this check is total bullshit i think - if (GetMiscValue() == STAT_STAMINA && (m_spellInfo->Attributes & SPELL_ATTR0_ABILITY)) + if (GetMiscValue() == STAT_STAMINA && m_spellInfo->HasAttribute(SPELL_ATTR0_ABILITY)) target->SetHealth(std::max<uint32>(uint32(healthPct * target->GetMaxHealth() * 0.01f), (alive ? 1 : 0))); } @@ -5901,7 +5901,7 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const else damage = uint32(target->CountPctFromMaxHealth(damage)); - if (!(m_spellInfo->AttributesEx4 & SPELL_ATTR4_FIXED_DAMAGE)) + if (!m_spellInfo->HasAttribute(SPELL_ATTR4_FIXED_DAMAGE)) if (m_spellInfo->Effects[m_effIndex].IsTargetingArea() || isAreaAura) { damage = int32(float(damage) * target->GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_AOE_DAMAGE_AVOIDANCE, m_spellInfo->SchoolMask)); @@ -5918,13 +5918,13 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const damage = caster->SpellCriticalDamageBonus(m_spellInfo, damage, target); int32 dmg = damage; - if (!(GetSpellInfo()->AttributesEx4 & SPELL_ATTR4_FIXED_DAMAGE)) + if (!GetSpellInfo()->HasAttribute(SPELL_ATTR4_FIXED_DAMAGE)) caster->ApplyResilience(target, NULL, &dmg, crit, CR_CRIT_TAKEN_SPELL); damage = dmg; caster->CalcAbsorbResist(target, GetSpellInfo()->GetSchoolMask(), DOT, damage, &absorb, &resist, GetSpellInfo()); - TC_LOG_INFO("spells", "PeriodicTick: %s attacked %s for %u dmg inflicted by %u absorb is %u", + TC_LOG_DEBUG("spells.periodic", "PeriodicTick: %s attacked %s for %u dmg inflicted by %u absorb is %u", GetCasterGUID().ToString().c_str(), target->GetGUID().ToString().c_str(), damage, GetId(), absorb); caster->DealDamageMods(target, damage, &absorb); @@ -5979,7 +5979,7 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c damage = caster->SpellDamageBonusDone(target, GetSpellInfo(), damage, DOT, GetBase()->GetStackAmount()) * caster->SpellDamagePctDone(target, m_spellInfo, DOT); else damage = std::max(int32(damage * GetDonePct()), 0); - + damage = target->SpellDamageBonusTaken(caster, GetSpellInfo(), damage, DOT, GetBase()->GetStackAmount()); // Calculate armor mitigation @@ -5990,7 +5990,7 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c damage = damageReductedArmor; } - if (!(m_spellInfo->AttributesEx4 & SPELL_ATTR4_FIXED_DAMAGE)) + if (!m_spellInfo->HasAttribute(SPELL_ATTR4_FIXED_DAMAGE)) if (m_spellInfo->Effects[m_effIndex].IsTargetingArea() || isAreaAura) { damage = uint32(float(damage) * target->GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_AOE_DAMAGE_AVOIDANCE, m_spellInfo->SchoolMask)); @@ -6007,7 +6007,7 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c damage = caster->SpellCriticalDamageBonus(m_spellInfo, damage, target); int32 dmg = damage; - if (!(GetSpellInfo()->AttributesEx4 & SPELL_ATTR4_FIXED_DAMAGE)) + if (!GetSpellInfo()->HasAttribute(SPELL_ATTR4_FIXED_DAMAGE)) caster->ApplyResilience(target, NULL, &dmg, crit, CR_CRIT_TAKEN_SPELL); damage = dmg; @@ -6016,7 +6016,7 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c if (target->GetHealth() < damage) damage = uint32(target->GetHealth()); - TC_LOG_INFO("spells", "PeriodicTick: %s health leech of %s for %u dmg inflicted by %u abs is %u", + TC_LOG_DEBUG("spells.periodic", "PeriodicTick: %s health leech of %s for %u dmg inflicted by %u abs is %u", GetCasterGUID().ToString().c_str(), target->GetGUID().ToString().c_str(), damage, GetId(), absorb); caster->SendSpellNonMeleeDamageLog(target, GetId(), damage, GetSpellInfo()->GetSchoolMask(), absorb, resist, false, 0, crit); @@ -6083,7 +6083,7 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const } // heal for caster damage (must be alive) - if (target != caster && GetSpellInfo()->AttributesEx2 & SPELL_ATTR2_HEALTH_FUNNEL && !caster->IsAlive()) + if (target != caster && GetSpellInfo()->HasAttribute(SPELL_ATTR2_HEALTH_FUNNEL) && !caster->IsAlive()) return; // don't regen when permanent aura target has full power @@ -6160,7 +6160,7 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const if (crit) damage = caster->SpellCriticalHealingBonus(m_spellInfo, damage, target); - TC_LOG_INFO("spells", "PeriodicTick: %s heal of %s for %u health inflicted by %u", + TC_LOG_DEBUG("spells.periodic", "PeriodicTick: %s heal of %s for %u health inflicted by %u", GetCasterGUID().ToString().c_str(), target->GetGUID().ToString().c_str(), damage, GetId()); uint32 absorb = 0; @@ -6177,7 +6177,7 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const // Health Funnel // damage caster for heal amount - if (target != caster && GetSpellInfo()->AttributesEx2 & SPELL_ATTR2_HEALTH_FUNNEL) + if (target != caster && GetSpellInfo()->HasAttribute(SPELL_ATTR2_HEALTH_FUNNEL)) { uint32 funnelDamage = GetSpellInfo()->ManaPerSecond; // damage is not affected by spell power @@ -6231,7 +6231,7 @@ void AuraEffect::HandlePeriodicManaLeechAuraTick(Unit* target, Unit* caster) con drainAmount = maxmana; } - TC_LOG_INFO("spells", "PeriodicTick: %s power leech of %s for %u dmg inflicted by %u", + TC_LOG_DEBUG("spells.periodic", "PeriodicTick: %s power leech of %s for %u dmg inflicted by %u", GetCasterGUID().ToString().c_str(), target->GetGUID().ToString().c_str(), drainAmount, GetId()); // resilience reduce mana draining effect at spell crit damage reduction (added in 2.4) @@ -6292,7 +6292,7 @@ void AuraEffect::HandleObsModPowerAuraTick(Unit* target, Unit* caster) const // ignore negative values (can be result apply spellmods to aura damage uint32 amount = std::max(m_amount, 0) * target->GetMaxPower(powerType) /100; - TC_LOG_INFO("spells", "PeriodicTick: %s energize %s for %u dmg inflicted by %u", + TC_LOG_DEBUG("spells.periodic", "PeriodicTick: %s energize %s for %u dmg inflicted by %u", GetCasterGUID().ToString().c_str(), target->GetGUID().ToString().c_str(), amount, GetId()); SpellPeriodicAuraLogInfo pInfo(this, amount, 0, 0, 0, 0.0f, false); @@ -6308,7 +6308,7 @@ void AuraEffect::HandlePeriodicEnergizeAuraTick(Unit* target, Unit* caster) cons { Powers powerType = Powers(GetMiscValue()); - if (target->GetTypeId() == TYPEID_PLAYER && target->getPowerType() != powerType && !(m_spellInfo->AttributesEx7 & SPELL_ATTR7_CAN_RESTORE_SECONDARY_POWER)) + if (target->GetTypeId() == TYPEID_PLAYER && target->getPowerType() != powerType && !m_spellInfo->HasAttribute(SPELL_ATTR7_CAN_RESTORE_SECONDARY_POWER)) return; if (!target->IsAlive() || !target->GetMaxPower(powerType)) @@ -6330,7 +6330,7 @@ void AuraEffect::HandlePeriodicEnergizeAuraTick(Unit* target, Unit* caster) cons SpellPeriodicAuraLogInfo pInfo(this, amount, 0, 0, 0, 0.0f, false); target->SendPeriodicAuraLog(&pInfo); - TC_LOG_INFO("spells", "PeriodicTick: %s energize %s for %u dmg inflicted by %u", + TC_LOG_DEBUG("spells.periodic", "PeriodicTick: %s energize %s for %u dmg inflicted by %u", GetCasterGUID().ToString().c_str(), target->GetGUID().ToString().c_str(), amount, GetId()); int32 gain = target->ModifyPower(powerType, amount); diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index 93a4c96b92f..0ba059b1952 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -191,7 +191,7 @@ void AuraApplication::BuildUpdatePacket(ByteBuffer& data, bool remove) const Aura const* aura = GetBase(); data << uint32(aura->GetId()); uint32 flags = _flags; - if (aura->GetMaxDuration() > 0 && !(aura->GetSpellInfo()->AttributesEx5 & SPELL_ATTR5_HIDE_DURATION)) + if (aura->GetMaxDuration() > 0 && !aura->GetSpellInfo()->HasAttribute(SPELL_ATTR5_HIDE_DURATION)) flags |= AFLAG_DURATION; data << uint8(flags); data << uint8(aura->GetCasterLevel()); @@ -749,12 +749,13 @@ void Aura::SetDuration(int32 duration, bool withMods) void Aura::RefreshDuration(bool withMods) { - if (withMods) + Unit* caster = GetCaster(); + if (withMods && caster) { int32 duration = m_spellInfo->GetMaxDuration(); // Calculate duration of periodics affected by haste. - if (GetCaster()->HasAuraTypeWithAffectMask(SPELL_AURA_PERIODIC_HASTE, m_spellInfo) || m_spellInfo->AttributesEx5 & SPELL_ATTR5_HASTE_AFFECT_DURATION) - duration = int32(duration * GetCaster()->GetFloatValue(UNIT_MOD_CAST_SPEED)); + if (caster->HasAuraTypeWithAffectMask(SPELL_AURA_PERIODIC_HASTE, m_spellInfo) || m_spellInfo->HasAttribute(SPELL_ATTR5_HASTE_AFFECT_DURATION)) + duration = int32(duration * caster->GetFloatValue(UNIT_MOD_CAST_SPEED)); SetMaxDuration(duration); SetDuration(duration); @@ -1813,7 +1814,7 @@ bool Aura::CanStackWith(Aura const* existingAura) const if (existingAura->GetSpellInfo()->IsChanneled()) return true; - if (m_spellInfo->AttributesEx3 & SPELL_ATTR3_STACK_FOR_DIFF_CASTERS) + if (m_spellInfo->HasAttribute(SPELL_ATTR3_STACK_FOR_DIFF_CASTERS)) return true; // check same periodic auras @@ -1865,7 +1866,7 @@ bool Aura::CanStackWith(Aura const* existingAura) const if (m_spellInfo->IsMultiSlotAura() && !IsArea()) return true; if (GetCastItemGUID() && existingAura->GetCastItemGUID()) - if (GetCastItemGUID() != existingAura->GetCastItemGUID() && (m_spellInfo->AttributesCu & SPELL_ATTR0_CU_ENCHANT_PROC)) + if (GetCastItemGUID() != existingAura->GetCastItemGUID() && m_spellInfo->HasAttribute(SPELL_ATTR0_CU_ENCHANT_PROC)) return true; // same spell with same caster should not stack return false; diff --git a/src/server/game/Spells/Auras/SpellAuras.h b/src/server/game/Spells/Auras/SpellAuras.h index ac45461b333..6abcd578411 100644 --- a/src/server/game/Spells/Auras/SpellAuras.h +++ b/src/server/game/Spells/Auras/SpellAuras.h @@ -161,8 +161,8 @@ class Aura { return GetCasterGUID() == target->GetGUID() && m_spellInfo->Stances - && !(m_spellInfo->AttributesEx2 & SPELL_ATTR2_NOT_NEED_SHAPESHIFT) - && !(m_spellInfo->Attributes & SPELL_ATTR0_NOT_SHAPESHIFT); + && !m_spellInfo->HasAttribute(SPELL_ATTR2_NOT_NEED_SHAPESHIFT) + && !m_spellInfo->HasAttribute(SPELL_ATTR0_NOT_SHAPESHIFT); } bool CanBeSaved() const; diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 8e415b06745..c31386cd496 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -482,23 +482,23 @@ void SpellCastTargets::Update(Unit* caster) void SpellCastTargets::OutDebug() const { if (!m_targetMask) - TC_LOG_INFO("spells", "No targets"); + TC_LOG_DEBUG("spells", "No targets"); - TC_LOG_INFO("spells", "target mask: %u", m_targetMask); + TC_LOG_DEBUG("spells", "target mask: %u", m_targetMask); if (m_targetMask & (TARGET_FLAG_UNIT_MASK | TARGET_FLAG_CORPSE_MASK | TARGET_FLAG_GAMEOBJECT_MASK)) - TC_LOG_INFO("spells", "Object target: %s", m_objectTargetGUID.ToString().c_str()); + TC_LOG_DEBUG("spells", "Object target: %s", m_objectTargetGUID.ToString().c_str()); if (m_targetMask & TARGET_FLAG_ITEM) - TC_LOG_INFO("spells", "Item target: %s", m_itemTargetGUID.ToString().c_str()); + TC_LOG_DEBUG("spells", "Item target: %s", m_itemTargetGUID.ToString().c_str()); if (m_targetMask & TARGET_FLAG_TRADE_ITEM) - TC_LOG_INFO("spells", "Trade item target: %s", m_itemTargetGUID.ToString().c_str()); + TC_LOG_DEBUG("spells", "Trade item target: %s", m_itemTargetGUID.ToString().c_str()); if (m_targetMask & TARGET_FLAG_SOURCE_LOCATION) - TC_LOG_INFO("spells", "Source location: transport guid:%s trans offset: %s position: %s", m_src._transportGUID.ToString().c_str(), m_src._transportOffset.ToString().c_str(), m_src._position.ToString().c_str()); + TC_LOG_DEBUG("spells", "Source location: transport guid:%s trans offset: %s position: %s", m_src._transportGUID.ToString().c_str(), m_src._transportOffset.ToString().c_str(), m_src._position.ToString().c_str()); if (m_targetMask & TARGET_FLAG_DEST_LOCATION) - TC_LOG_INFO("spells", "Destination location: transport guid:%s trans offset: %s position: %s", m_dst._transportGUID.ToString().c_str(), m_dst._transportOffset.ToString().c_str(), m_dst._position.ToString().c_str()); + TC_LOG_DEBUG("spells", "Destination location: transport guid:%s trans offset: %s position: %s", m_dst._transportGUID.ToString().c_str(), m_dst._transportOffset.ToString().c_str(), m_dst._position.ToString().c_str()); if (m_targetMask & TARGET_FLAG_STRING) - TC_LOG_INFO("spells", "String: %s", m_strTarget.c_str()); - TC_LOG_INFO("spells", "speed: %f", m_speed); - TC_LOG_INFO("spells", "elevation: %f", m_elevation); + TC_LOG_DEBUG("spells", "String: %s", m_strTarget.c_str()); + TC_LOG_DEBUG("spells", "speed: %f", m_speed); + TC_LOG_DEBUG("spells", "elevation: %f", m_elevation); } SpellValue::SpellValue(SpellInfo const* proto) @@ -512,7 +512,7 @@ SpellValue::SpellValue(SpellInfo const* proto) Spell::Spell(Unit* caster, SpellInfo const* info, TriggerCastFlags triggerFlags, ObjectGuid originalCasterGUID, bool skipCheck) : m_spellInfo(sSpellMgr->GetSpellForDifficultyFromSpell(info, caster)), -m_caster((info->AttributesEx6 & SPELL_ATTR6_CAST_BY_CHARMER && caster->GetCharmerOrOwner()) ? caster->GetCharmerOrOwner() : caster) +m_caster((info->HasAttribute(SPELL_ATTR6_CAST_BY_CHARMER) && caster->GetCharmerOrOwner()) ? caster->GetCharmerOrOwner() : caster) , m_spellValue(new SpellValue(m_spellInfo)), m_preGeneratedPath(PathGenerator(m_caster)) { m_customError = SPELL_CUSTOM_ERROR_NONE; @@ -533,7 +533,7 @@ m_caster((info->AttributesEx6 & SPELL_ATTR6_CAST_BY_CHARMER && caster->GetCharme switch (m_spellInfo->DmgClass) { case SPELL_DAMAGE_CLASS_MELEE: - if (m_spellInfo->AttributesEx3 & SPELL_ATTR3_REQ_OFFHAND) + if (m_spellInfo->HasAttribute(SPELL_ATTR3_REQ_OFFHAND)) m_attackType = OFF_ATTACK; else m_attackType = BASE_ATTACK; @@ -543,7 +543,7 @@ m_caster((info->AttributesEx6 & SPELL_ATTR6_CAST_BY_CHARMER && caster->GetCharme break; default: // Wands - if (m_spellInfo->AttributesEx2 & SPELL_ATTR2_AUTOREPEAT_FLAG) + if (m_spellInfo->HasAttribute(SPELL_ATTR2_AUTOREPEAT_FLAG)) m_attackType = RANGED_ATTACK; else m_attackType = BASE_ATTACK; @@ -574,8 +574,8 @@ m_caster((info->AttributesEx6 & SPELL_ATTR6_CAST_BY_CHARMER && caster->GetCharme m_spellState = SPELL_STATE_NULL; _triggeredCastFlags = triggerFlags; - if (info->AttributesEx4 & SPELL_ATTR4_TRIGGERED) - _triggeredCastFlags = TriggerCastFlags(TRIGGERED_FULL_MASK & ~TRIGGERED_IGNORE_EQUIPPED_ITEM_REQUIREMENT); + if (info->HasAttribute(SPELL_ATTR4_CAN_CAST_WHILE_CASTING)) + _triggeredCastFlags = TriggerCastFlags(uint32(_triggeredCastFlags) | TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_CAST_DIRECTLY); m_CastItem = NULL; m_castItemGUID.Clear(); @@ -615,8 +615,8 @@ m_caster((info->AttributesEx6 & SPELL_ATTR6_CAST_BY_CHARMER && caster->GetCharme // Determine if spell can be reflected back to the caster // Patch 1.2 notes: Spell Reflection no longer reflects abilities - m_canReflect = m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC && !(m_spellInfo->Attributes & SPELL_ATTR0_ABILITY) - && !(m_spellInfo->AttributesEx & SPELL_ATTR1_CANT_BE_REFLECTED) && !(m_spellInfo->Attributes & SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY) + m_canReflect = m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC && !m_spellInfo->HasAttribute(SPELL_ATTR0_ABILITY) + && !m_spellInfo->HasAttribute(SPELL_ATTR1_CANT_BE_REFLECTED) && !m_spellInfo->HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY) && !m_spellInfo->IsPassive() && !m_spellInfo->IsPositive(); CleanupTargetList(); @@ -1749,11 +1749,11 @@ uint32 Spell::GetSearcherTypeMask(SpellTargetObjectTypes objType, ConditionList* default: break; } - if (!(m_spellInfo->AttributesEx2 & SPELL_ATTR2_CAN_TARGET_DEAD)) + if (!m_spellInfo->HasAttribute(SPELL_ATTR2_CAN_TARGET_DEAD)) retMask &= ~GRID_MAP_TYPE_MASK_CORPSE; - if (m_spellInfo->AttributesEx3 & SPELL_ATTR3_ONLY_TARGET_PLAYERS) + if (m_spellInfo->HasAttribute(SPELL_ATTR3_ONLY_TARGET_PLAYERS)) retMask &= GRID_MAP_TYPE_MASK_CORPSE | GRID_MAP_TYPE_MASK_PLAYER; - if (m_spellInfo->AttributesEx3 & SPELL_ATTR3_ONLY_TARGET_GHOSTS) + if (m_spellInfo->HasAttribute(SPELL_ATTR3_ONLY_TARGET_GHOSTS)) retMask &= GRID_MAP_TYPE_MASK_PLAYER; if (condList) @@ -1843,7 +1843,7 @@ void Spell::SearchChainTargets(std::list<WorldObject*>& targets, uint32 chainTar } // chain lightning/heal spells and similar - allow to jump at larger distance and go out of los - bool isBouncingFar = (m_spellInfo->AttributesEx4 & SPELL_ATTR4_AREA_TARGET_CHAIN + bool isBouncingFar = (m_spellInfo->HasAttribute(SPELL_ATTR4_AREA_TARGET_CHAIN) || m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_NONE || m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC); @@ -1944,7 +1944,7 @@ void Spell::prepareDataForTriggerSystem(AuraEffect const* /*triggeredByAura*/) break; case SPELL_DAMAGE_CLASS_RANGED: // Auto attack - if (m_spellInfo->AttributesEx2 & SPELL_ATTR2_AUTOREPEAT_FLAG) + if (m_spellInfo->HasAttribute(SPELL_ATTR2_AUTOREPEAT_FLAG)) { m_procAttacker = PROC_FLAG_DONE_RANGED_AUTO_ATTACK; m_procVictim = PROC_FLAG_TAKEN_RANGED_AUTO_ATTACK; @@ -1958,7 +1958,7 @@ void Spell::prepareDataForTriggerSystem(AuraEffect const* /*triggeredByAura*/) default: if (m_spellInfo->EquippedItemClass == ITEM_CLASS_WEAPON && m_spellInfo->EquippedItemSubClassMask & (1<<ITEM_SUBCLASS_WEAPON_WAND) - && m_spellInfo->AttributesEx2 & SPELL_ATTR2_AUTOREPEAT_FLAG) // Wands auto attack + && m_spellInfo->HasAttribute(SPELL_ATTR2_AUTOREPEAT_FLAG)) // Wands auto attack { m_procAttacker = PROC_FLAG_DONE_RANGED_AUTO_ATTACK; m_procVictim = PROC_FLAG_TAKEN_RANGED_AUTO_ATTACK; @@ -1990,8 +1990,8 @@ void Spell::prepareDataForTriggerSystem(AuraEffect const* /*triggeredByAura*/) if (!(m_procAttacker & PROC_FLAG_DONE_RANGED_AUTO_ATTACK)) { if (_triggeredCastFlags & TRIGGERED_DISALLOW_PROC_EVENTS && - (m_spellInfo->AttributesEx2 & SPELL_ATTR2_TRIGGERED_CAN_TRIGGER_PROC || - m_spellInfo->AttributesEx3 & SPELL_ATTR3_TRIGGERED_CAN_TRIGGER_PROC_2)) + (m_spellInfo->HasAttribute(SPELL_ATTR2_TRIGGERED_CAN_TRIGGER_PROC) || + m_spellInfo->HasAttribute(SPELL_ATTR3_TRIGGERED_CAN_TRIGGER_PROC_2))) m_procEx |= PROC_EX_INTERNAL_CANT_PROC; else if (_triggeredCastFlags & TRIGGERED_DISALLOW_PROC_EVENTS) m_procEx |= PROC_EX_INTERNAL_TRIGGERED; @@ -2275,7 +2275,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) m_spellAura = NULL; // Set aura to null for every target-make sure that pointer is not used for unit without aura applied //Spells with this flag cannot trigger if effect is cast on self - bool canEffectTrigger = !(m_spellInfo->AttributesEx3 & SPELL_ATTR3_CANT_TRIGGER_PROC) && unitTarget->CanProc() && (CanExecuteTriggersOnHit(mask) || missInfo == SPELL_MISS_IMMUNE || missInfo == SPELL_MISS_IMMUNE2); + bool canEffectTrigger = !m_spellInfo->HasAttribute(SPELL_ATTR3_CANT_TRIGGER_PROC) && unitTarget->CanProc() && (CanExecuteTriggersOnHit(mask) || missInfo == SPELL_MISS_IMMUNE || missInfo == SPELL_MISS_IMMUNE2); Unit* spellHitTarget = NULL; if (missInfo == SPELL_MISS_NONE) // In case spell hit target, do all effect on that target @@ -2395,7 +2395,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) if (canEffectTrigger && missInfo != SPELL_MISS_REFLECT) { caster->ProcDamageAndSpell(unitTarget, procAttacker, procVictim, procEx, damageInfo.damage, m_attackType, m_spellInfo, m_triggeredByAuraSpell); - if (caster->GetTypeId() == TYPEID_PLAYER && (m_spellInfo->Attributes & SPELL_ATTR0_STOP_ATTACK_TARGET) == 0 && + if (caster->GetTypeId() == TYPEID_PLAYER && m_spellInfo->HasAttribute(SPELL_ATTR0_STOP_ATTACK_TARGET) == 0 && (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE || m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED)) caster->ToPlayer()->CastItemCombatSpell(unitTarget, m_attackType, procVictim, procEx); } @@ -2415,7 +2415,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) caster->ProcDamageAndSpell(unit, procAttacker, procVictim, procEx, 0, m_attackType, m_spellInfo, m_triggeredByAuraSpell); // Failed Pickpocket, reveal rogue - if (missInfo == SPELL_MISS_RESIST && m_spellInfo->AttributesCu & SPELL_ATTR0_CU_PICKPOCKET && unitTarget->GetTypeId() == TYPEID_UNIT) + if (missInfo == SPELL_MISS_RESIST && m_spellInfo->HasAttribute(SPELL_ATTR0_CU_PICKPOCKET) && unitTarget->GetTypeId() == TYPEID_UNIT) { m_caster->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TALK); if (unitTarget->ToCreature()->IsAIEnabled) @@ -2425,9 +2425,9 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) if (missInfo != SPELL_MISS_EVADE && !m_caster->IsFriendlyTo(unit) && (!m_spellInfo->IsPositive() || m_spellInfo->HasEffect(SPELL_EFFECT_DISPEL))) { - m_caster->CombatStart(unit, !(m_spellInfo->AttributesEx3 & SPELL_ATTR3_NO_INITIAL_AGGRO)); + m_caster->CombatStart(unit, !m_spellInfo->HasAttribute(SPELL_ATTR3_NO_INITIAL_AGGRO)); - if (m_spellInfo->AttributesCu & SPELL_ATTR0_CU_AURA_CC) + if (m_spellInfo->HasAttribute(SPELL_ATTR0_CU_AURA_CC)) if (!unit->IsStandState()) unit->SetStandState(UNIT_STAND_STATE_STAND); } @@ -2514,7 +2514,7 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA { unit->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_HITBYSPELL); /// @todo This is a hack. But we do not know what types of stealth should be interrupted by CC - if ((m_spellInfo->AttributesCu & SPELL_ATTR0_CU_AURA_CC) && unit->IsControlledByPlayer()) + if (m_spellInfo->HasAttribute(SPELL_ATTR0_CU_AURA_CC) && unit->IsControlledByPlayer()) unit->RemoveAurasByType(SPELL_AURA_MOD_STEALTH); } else if (m_caster->IsFriendlyTo(unit)) @@ -2532,7 +2532,7 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA if (m_caster->GetTypeId() == TYPEID_PLAYER) m_caster->ToPlayer()->UpdatePvP(true); } - if (unit->IsInCombat() && !(m_spellInfo->AttributesEx3 & SPELL_ATTR3_NO_INITIAL_AGGRO)) + if (unit->IsInCombat() && !m_spellInfo->HasAttribute(SPELL_ATTR3_NO_INITIAL_AGGRO)) { m_caster->SetInCombatState(unit->GetCombatTimer() > 0, unit); unit->getHostileRefManager().threatAssist(m_caster, 0.0f); @@ -2625,7 +2625,7 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA if (m_spellInfo->IsChanneled()) m_originalCaster->ModSpellCastTime(aurSpellInfo, duration, this); // and duration of auras affected by SPELL_AURA_PERIODIC_HASTE - else if (m_originalCaster->HasAuraTypeWithAffectMask(SPELL_AURA_PERIODIC_HASTE, aurSpellInfo) || m_spellInfo->AttributesEx5 & SPELL_ATTR5_HASTE_AFFECT_DURATION) + else if (m_originalCaster->HasAuraTypeWithAffectMask(SPELL_AURA_PERIODIC_HASTE, aurSpellInfo) || m_spellInfo->HasAttribute(SPELL_ATTR5_HASTE_AFFECT_DURATION)) duration = int32(duration * m_originalCaster->GetFloatValue(UNIT_MOD_CAST_SPEED)); if (duration != m_spellAura->GetMaxDuration()) @@ -3192,7 +3192,7 @@ void Spell::cast(bool skipCheck) SendSpellGo(); // Okay, everything is prepared. Now we need to distinguish between immediate and evented delayed spells - if ((m_spellInfo->Speed > 0.0f && !m_spellInfo->IsChanneled()) || m_spellInfo->AttributesEx4 & SPELL_ATTR4_UNK4) + if ((m_spellInfo->Speed > 0.0f && !m_spellInfo->IsChanneled()) || m_spellInfo->HasAttribute(SPELL_ATTR4_UNK4)) { // Remove used for cast item if need (it can be already NULL after TakeReagents call // in case delayed spell remove item at cast delay start @@ -3592,7 +3592,7 @@ void Spell::finish(bool ok) break; } } - if (!found && !(m_spellInfo->AttributesEx2 & SPELL_ATTR2_NOT_RESET_AUTO_ACTIONS)) + if (!found && !m_spellInfo->HasAttribute(SPELL_ATTR2_NOT_RESET_AUTO_ACTIONS)) { m_caster->resetAttackTimer(BASE_ATTACK); if (m_caster->haveOffhandWeapon()) @@ -3618,7 +3618,7 @@ void Spell::finish(bool ok) } // Stop Attack for some spells - if (m_spellInfo->Attributes & SPELL_ATTR0_STOP_ATTACK_TARGET) + if (m_spellInfo->HasAttribute(SPELL_ATTR0_STOP_ATTACK_TARGET)) m_caster->AttackStop(); } @@ -3783,7 +3783,7 @@ void Spell::SendSpellStart() if ((IsTriggered() && !m_spellInfo->IsAutoRepeatRangedSpell()) || m_triggeredByAuraSpell) castFlags |= CAST_FLAG_PENDING; - if (m_spellInfo->Attributes & SPELL_ATTR0_REQ_AMMO) + if (m_spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO)) castFlags |= CAST_FLAG_AMMO; if ((m_caster->GetTypeId() == TYPEID_PLAYER || (m_caster->GetTypeId() == TYPEID_UNIT && m_caster->ToCreature()->IsPet())) @@ -3836,7 +3836,7 @@ void Spell::SendSpellGo() if ((IsTriggered() && !m_spellInfo->IsAutoRepeatRangedSpell()) || m_triggeredByAuraSpell) castFlags |= CAST_FLAG_PENDING; - if (m_spellInfo->Attributes & SPELL_ATTR0_REQ_AMMO) + if (m_spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO)) castFlags |= CAST_FLAG_AMMO; // arrows/bullets visual if ((m_caster->GetTypeId() == TYPEID_PLAYER || @@ -4230,7 +4230,7 @@ void Spell::SendResurrectRequest(Player* target) data << uint8(m_caster->GetTypeId() == TYPEID_PLAYER ? 0 : 1); // "you'll be afflicted with resurrection sickness" // override delay sent with SMSG_CORPSE_RECLAIM_DELAY, set instant resurrection for spells with this attribute - if (m_spellInfo->AttributesEx3 & SPELL_ATTR3_IGNORE_RESURRECTION_TIMER) + if (m_spellInfo->HasAttribute(SPELL_ATTR3_IGNORE_RESURRECTION_TIMER)) data << uint32(0); target->GetSession()->SendPacket(&data); } @@ -4561,8 +4561,8 @@ void Spell::HandleThreatSpells() if (m_UniqueTargetInfo.empty()) return; - if ((m_spellInfo->AttributesEx & SPELL_ATTR1_NO_THREAT) || - (m_spellInfo->AttributesEx3 & SPELL_ATTR3_NO_INITIAL_AGGRO)) + if (m_spellInfo->HasAttribute(SPELL_ATTR1_NO_THREAT) || + m_spellInfo->HasAttribute(SPELL_ATTR3_NO_INITIAL_AGGRO)) return; float threat = 0.0f; @@ -4573,7 +4573,7 @@ void Spell::HandleThreatSpells() threat += threatEntry->flatMod; } - else if ((m_spellInfo->AttributesCu & SPELL_ATTR0_CU_NO_INITIAL_THREAT) == 0) + else if (m_spellInfo->HasAttribute(SPELL_ATTR0_CU_NO_INITIAL_THREAT) == 0) threat += m_spellInfo->SpellLevel; // past this point only multiplicative effects occur @@ -4634,11 +4634,11 @@ void Spell::HandleEffects(Unit* pUnitTarget, Item* pItemTarget, GameObject* pGOT SpellCastResult Spell::CheckCast(bool strict) { // check death state - if (!m_caster->IsAlive() && !(m_spellInfo->Attributes & SPELL_ATTR0_PASSIVE) && !((m_spellInfo->Attributes & SPELL_ATTR0_CASTABLE_WHILE_DEAD) || (IsTriggered() && !m_triggeredByAuraSpell))) + if (!m_caster->IsAlive() && !m_spellInfo->HasAttribute(SPELL_ATTR0_PASSIVE) && !(m_spellInfo->HasAttribute(SPELL_ATTR0_CASTABLE_WHILE_DEAD) || (IsTriggered() && !m_triggeredByAuraSpell))) return SPELL_FAILED_CASTER_DEAD; // check cooldowns to prevent cheating - if (m_caster->GetTypeId() == TYPEID_PLAYER && !(m_spellInfo->Attributes & SPELL_ATTR0_PASSIVE)) + if (m_caster->GetTypeId() == TYPEID_PLAYER && !m_spellInfo->HasAttribute(SPELL_ATTR0_PASSIVE)) { //can cast triggered (by aura only?) spells while have this flag if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_AURASTATE) && m_caster->ToPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_ALLOW_ONLY_ABILITY)) @@ -4657,7 +4657,7 @@ SpellCastResult Spell::CheckCast(bool strict) return SPELL_FAILED_NOT_READY; } - if (m_spellInfo->AttributesEx7 & SPELL_ATTR7_IS_CHEAT_SPELL && !m_caster->HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_ALLOW_CHEAT_SPELLS)) + if (m_spellInfo->HasAttribute(SPELL_ATTR7_IS_CHEAT_SPELL) && !m_caster->HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_ALLOW_CHEAT_SPELLS)) { m_customError = SPELL_CUSTOM_ERROR_GM_ONLY; return SPELL_FAILED_CUSTOM_ERROR; @@ -4675,11 +4675,11 @@ SpellCastResult Spell::CheckCast(bool strict) if (m_caster->GetTypeId() == TYPEID_PLAYER && VMAP::VMapFactory::createOrGetVMapManager()->isLineOfSightCalcEnabled()) { - if (m_spellInfo->Attributes & SPELL_ATTR0_OUTDOORS_ONLY && + if (m_spellInfo->HasAttribute(SPELL_ATTR0_OUTDOORS_ONLY) && !m_caster->GetMap()->IsOutdoors(m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ())) return SPELL_FAILED_ONLY_OUTDOORS; - if (m_spellInfo->Attributes & SPELL_ATTR0_INDOORS_ONLY && + if (m_spellInfo->HasAttribute(SPELL_ATTR0_INDOORS_ONLY) && m_caster->GetMap()->IsOutdoors(m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ())) return SPELL_FAILED_ONLY_INDOORS; } @@ -4705,7 +4705,7 @@ SpellCastResult Spell::CheckCast(bool strict) if (shapeError != SPELL_CAST_OK) return shapeError; - if ((m_spellInfo->Attributes & SPELL_ATTR0_ONLY_STEALTHED) && !(m_caster->HasStealthAura())) + if (m_spellInfo->HasAttribute(SPELL_ATTR0_ONLY_STEALTHED) && !(m_caster->HasStealthAura())) return SPELL_FAILED_ONLY_STEALTHED; } } @@ -4791,7 +4791,7 @@ SpellCastResult Spell::CheckCast(bool strict) // those spells may have incorrect target entries or not filled at all (for example 15332) // such spells when learned are not targeting anyone using targeting system, they should apply directly to caster instead // also, such casts shouldn't be sent to client - if (!((m_spellInfo->Attributes & SPELL_ATTR0_PASSIVE) && (!m_targets.GetUnitTarget() || m_targets.GetUnitTarget() == m_caster))) + if (!(m_spellInfo->HasAttribute(SPELL_ATTR0_PASSIVE) && (!m_targets.GetUnitTarget() || m_targets.GetUnitTarget() == m_caster))) { // Check explicit target for m_originalCaster - todo: get rid of such workarounds SpellCastResult castResult = m_spellInfo->CheckExplicitTarget(m_originalCaster ? m_originalCaster : m_caster, m_targets.GetObjectTarget(), m_targets.GetItemTarget()); @@ -4808,11 +4808,11 @@ SpellCastResult Spell::CheckCast(bool strict) if (target != m_caster) { // Must be behind the target - if ((m_spellInfo->AttributesCu & SPELL_ATTR0_CU_REQ_CASTER_BEHIND_TARGET) && target->HasInArc(static_cast<float>(M_PI), m_caster)) + if (m_spellInfo->HasAttribute(SPELL_ATTR0_CU_REQ_CASTER_BEHIND_TARGET) && target->HasInArc(static_cast<float>(M_PI), m_caster)) return SPELL_FAILED_NOT_BEHIND; // Target must be facing you - if ((m_spellInfo->AttributesCu & SPELL_ATTR0_CU_REQ_TARGET_FACING_CASTER) && !target->HasInArc(static_cast<float>(M_PI), m_caster)) + if (m_spellInfo->HasAttribute(SPELL_ATTR0_CU_REQ_TARGET_FACING_CASTER) && !target->HasInArc(static_cast<float>(M_PI), m_caster)) return SPELL_FAILED_NOT_INFRONT; if (m_caster->GetEntry() != WORLD_TRIGGER) // Ignore LOS for gameobjects casts (wrongly cast by a trigger) @@ -4822,7 +4822,7 @@ SpellCastResult Spell::CheckCast(bool strict) if (DynamicObject* dynObj = m_caster->GetDynObject(m_triggeredByAuraSpell->Id)) losTarget = dynObj; - if (!(m_spellInfo->AttributesEx2 & SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS) && !DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, NULL, SPELL_DISABLE_LOS) && !target->IsWithinLOSInMap(losTarget)) + if (!m_spellInfo->HasAttribute(SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS) && !DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, NULL, SPELL_DISABLE_LOS) && !target->IsWithinLOSInMap(losTarget)) return SPELL_FAILED_LINE_OF_SIGHT; } } @@ -4834,7 +4834,7 @@ SpellCastResult Spell::CheckCast(bool strict) float x, y, z; m_targets.GetDstPos()->GetPosition(x, y, z); - if (!(m_spellInfo->AttributesEx2 & SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS) && !DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, NULL, SPELL_DISABLE_LOS) && !m_caster->IsWithinLOS(x, y, z)) + if (!m_spellInfo->HasAttribute(SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS) && !DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, NULL, SPELL_DISABLE_LOS) && !m_caster->IsWithinLOS(x, y, z)) return SPELL_FAILED_LINE_OF_SIGHT; } @@ -4855,15 +4855,15 @@ SpellCastResult Spell::CheckCast(bool strict) } // Spell cast only in battleground - if ((m_spellInfo->AttributesEx3 & SPELL_ATTR3_BATTLEGROUND) && m_caster->GetTypeId() == TYPEID_PLAYER) + if (m_spellInfo->HasAttribute(SPELL_ATTR3_BATTLEGROUND) && m_caster->GetTypeId() == TYPEID_PLAYER) if (!m_caster->ToPlayer()->InBattleground()) return SPELL_FAILED_ONLY_BATTLEGROUNDS; // do not allow spells to be cast in arenas // - with greater than 10 min CD without SPELL_ATTR4_USABLE_IN_ARENA flag // - with SPELL_ATTR4_NOT_USABLE_IN_ARENA flag - if ((m_spellInfo->AttributesEx4 & SPELL_ATTR4_NOT_USABLE_IN_ARENA) || - (m_spellInfo->GetRecoveryTime() > 10 * MINUTE * IN_MILLISECONDS && !(m_spellInfo->AttributesEx4 & SPELL_ATTR4_USABLE_IN_ARENA))) + if (m_spellInfo->HasAttribute(SPELL_ATTR4_NOT_USABLE_IN_ARENA) || + (m_spellInfo->GetRecoveryTime() > 10 * MINUTE * IN_MILLISECONDS && !m_spellInfo->HasAttribute(SPELL_ATTR4_USABLE_IN_ARENA))) if (MapEntry const* mapEntry = sMapStore.LookupEntry(m_caster->GetMapId())) if (mapEntry->IsBattleArena()) return SPELL_FAILED_NOT_IN_ARENA; @@ -4881,7 +4881,7 @@ SpellCastResult Spell::CheckCast(bool strict) // not let players cast spells at mount (and let do it to creatures) if (m_caster->IsMounted() && m_caster->GetTypeId() == TYPEID_PLAYER && !(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_MOUNTED_OR_ON_VEHICLE) && - !m_spellInfo->IsPassive() && !(m_spellInfo->Attributes & SPELL_ATTR0_CASTABLE_WHILE_MOUNTED)) + !m_spellInfo->IsPassive() && !m_spellInfo->HasAttribute(SPELL_ATTR0_CASTABLE_WHILE_MOUNTED)) { if (m_caster->IsInFlight()) return SPELL_FAILED_NOT_ON_TAXI; @@ -4938,7 +4938,7 @@ SpellCastResult Spell::CheckCast(bool strict) for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) if (m_spellInfo->Effects[i].Effect == SPELL_EFFECT_DISPEL) { - if (m_spellInfo->Effects[i].IsTargetingArea() || m_spellInfo->AttributesEx & SPELL_ATTR1_MELEE_COMBAT_START) + if (m_spellInfo->Effects[i].IsTargetingArea() || m_spellInfo->HasAttribute(SPELL_ATTR1_MELEE_COMBAT_START)) { hasDispellableAura = true; break; @@ -5522,7 +5522,7 @@ SpellCastResult Spell::CheckPetCast(Unit* target) SpellCastResult Spell::CheckCasterAuras() const { // spells totally immuned to caster auras (wsg flag drop, give marks etc) - if (m_spellInfo->AttributesEx6 & SPELL_ATTR6_IGNORE_CASTER_AURAS) + if (m_spellInfo->HasAttribute(SPELL_ATTR6_IGNORE_CASTER_AURAS)) return SPELL_CAST_OK; uint8 school_immune = 0; @@ -5531,7 +5531,7 @@ SpellCastResult Spell::CheckCasterAuras() const // Check if the spell grants school or mechanic immunity. // We use bitmasks so the loop is done only once and not on every aura check below. - if (m_spellInfo->AttributesEx & SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY) + if (m_spellInfo->HasAttribute(SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY)) { for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) { @@ -5547,7 +5547,7 @@ SpellCastResult Spell::CheckCasterAuras() const mechanic_immune = IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK; } - bool usableInStun = (m_spellInfo->AttributesEx5 & SPELL_ATTR5_USABLE_WHILE_STUNNED) != 0; + bool usableInStun = m_spellInfo->HasAttribute(SPELL_ATTR5_USABLE_WHILE_STUNNED); // Glyph of Pain Suppression // there is no other way to handle it @@ -5579,9 +5579,9 @@ SpellCastResult Spell::CheckCasterAuras() const else prevented_reason = SPELL_FAILED_STUNNED; } - else if (unitflag & UNIT_FLAG_CONFUSED && !(m_spellInfo->AttributesEx5 & SPELL_ATTR5_USABLE_WHILE_CONFUSED)) + else if (unitflag & UNIT_FLAG_CONFUSED && !m_spellInfo->HasAttribute(SPELL_ATTR5_USABLE_WHILE_CONFUSED)) prevented_reason = SPELL_FAILED_CONFUSED; - else if (unitflag & UNIT_FLAG_FLEEING && !(m_spellInfo->AttributesEx5 & SPELL_ATTR5_USABLE_WHILE_FEARED)) + else if (unitflag & UNIT_FLAG_FLEEING && !m_spellInfo->HasAttribute(SPELL_ATTR5_USABLE_WHILE_FEARED)) prevented_reason = SPELL_FAILED_FLEEING; else if (unitflag & UNIT_FLAG_SILENCED && m_spellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE) prevented_reason = SPELL_FAILED_SILENCED; @@ -5601,7 +5601,7 @@ SpellCastResult Spell::CheckCasterAuras() const SpellInfo const* auraInfo = aura->GetSpellInfo(); if (auraInfo->GetAllEffectsMechanicMask() & mechanic_immune) continue; - if (auraInfo->GetSchoolMask() & school_immune && !(auraInfo->AttributesEx & SPELL_ATTR1_UNAFFECTED_BY_SCHOOL_IMMUNE)) + if (auraInfo->GetSchoolMask() & school_immune && !auraInfo->HasAttribute(SPELL_ATTR1_UNAFFECTED_BY_SCHOOL_IMMUNE)) continue; if (auraInfo->GetDispelMask() & dispel_immune) continue; @@ -5619,11 +5619,11 @@ SpellCastResult Spell::CheckCasterAuras() const return SPELL_FAILED_STUNNED; break; case SPELL_AURA_MOD_CONFUSE: - if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR5_USABLE_WHILE_CONFUSED)) + if (!m_spellInfo->HasAttribute(SPELL_ATTR5_USABLE_WHILE_CONFUSED)) return SPELL_FAILED_CONFUSED; break; case SPELL_AURA_MOD_FEAR: - if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR5_USABLE_WHILE_FEARED)) + if (!m_spellInfo->HasAttribute(SPELL_ATTR5_USABLE_WHILE_FEARED)) return SPELL_FAILED_FLEEING; break; case SPELL_AURA_MOD_SILENCE: @@ -6260,7 +6260,7 @@ SpellCastResult Spell::CheckItems() if (!(_triggeredCastFlags & TRIGGERED_IGNORE_EQUIPPED_ITEM_REQUIREMENT) && m_spellInfo->EquippedItemClass >=0) { // main hand weapon required - if (m_spellInfo->AttributesEx3 & SPELL_ATTR3_MAIN_HAND) + if (m_spellInfo->HasAttribute(SPELL_ATTR3_MAIN_HAND)) { Item* item = m_caster->ToPlayer()->GetWeaponForAttack(BASE_ATTACK); @@ -6274,7 +6274,7 @@ SpellCastResult Spell::CheckItems() } // offhand hand weapon required - if (m_spellInfo->AttributesEx3 & SPELL_ATTR3_REQ_OFFHAND) + if (m_spellInfo->HasAttribute(SPELL_ATTR3_REQ_OFFHAND)) { Item* item = m_caster->ToPlayer()->GetWeaponForAttack(OFF_ATTACK); @@ -6324,7 +6324,7 @@ void Spell::Delayed() // only called in DealDamage() else m_timer += delaytime; - TC_LOG_INFO("spells", "Spell %u partially interrupted for (%d) ms at damage", m_spellInfo->Id, delaytime); + TC_LOG_DEBUG("spells", "Spell %u partially interrupted for (%d) ms at damage", m_spellInfo->Id, delaytime); WorldPacket data(SMSG_SPELL_DELAYED, 8+4); data << m_caster->GetPackGUID(); @@ -6463,11 +6463,11 @@ bool Spell::CheckEffectTarget(Unit const* target, uint32 eff, Position const* lo } // check for ignore LOS on the effect itself - if (m_spellInfo->AttributesEx2 & SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS || DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, NULL, SPELL_DISABLE_LOS)) + if (m_spellInfo->HasAttribute(SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS) || DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, NULL, SPELL_DISABLE_LOS)) return true; // if spell is triggered, need to check for LOS disable on the aura triggering it and inherit that behaviour - if (IsTriggered() && m_triggeredByAuraSpell && (m_triggeredByAuraSpell->AttributesEx2 & SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS || DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_triggeredByAuraSpell->Id, NULL, SPELL_DISABLE_LOS))) + if (IsTriggered() && m_triggeredByAuraSpell && (m_triggeredByAuraSpell->HasAttribute(SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS) || DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_triggeredByAuraSpell->Id, NULL, SPELL_DISABLE_LOS))) return true; /// @todo shit below shouldn't be here, but it's temporary @@ -6518,7 +6518,7 @@ bool Spell::CheckEffectTarget(Unit const* target, uint32 eff, Position const* lo bool Spell::IsNextMeleeSwingSpell() const { - return (m_spellInfo->Attributes & SPELL_ATTR0_ON_NEXT_SWING) != 0; + return m_spellInfo->HasAttribute(SPELL_ATTR0_ON_NEXT_SWING); } bool Spell::IsAutoActionResetSpell() const @@ -6697,7 +6697,7 @@ void Spell::HandleLaunchPhase() if (m_applyMultiplierMask & (1 << i)) multiplier[i] = m_spellInfo->Effects[i].CalcDamageMultiplier(m_originalCaster, this); - bool usesAmmo = (m_spellInfo->AttributesCu & SPELL_ATTR0_CU_DIRECT_DAMAGE) != 0; + bool usesAmmo = m_spellInfo->HasAttribute(SPELL_ATTR0_CU_DIRECT_DAMAGE); Unit::AuraEffectList const& Auras = m_caster->GetAuraEffectsByType(SPELL_AURA_ABILITY_CONSUME_NO_AMMO); for (Unit::AuraEffectList::const_iterator j = Auras.begin(); j != Auras.end(); ++j) { @@ -7140,7 +7140,7 @@ void Spell::CallScriptDestinationTargetSelectHandlers(SpellDestination& target, bool Spell::CheckScriptEffectImplicitTargets(uint32 effIndex, uint32 effIndexToCheck) { // Skip if there are not any script - if (!m_loadedScripts.size()) + if (m_loadedScripts.empty()) return true; for (std::list<SpellScript*>::iterator itr = m_loadedScripts.begin(); itr != m_loadedScripts.end(); ++itr) @@ -7162,7 +7162,7 @@ bool Spell::CheckScriptEffectImplicitTargets(uint32 effIndex, uint32 effIndexToC bool Spell::CanExecuteTriggersOnHit(uint8 effMask, SpellInfo const* triggeredByAura) const { - bool only_on_caster = (triggeredByAura && (triggeredByAura->AttributesEx4 & SPELL_ATTR4_PROC_ONLY_ON_CASTER)); + bool only_on_caster = (triggeredByAura && triggeredByAura->HasAttribute(SPELL_ATTR4_PROC_ONLY_ON_CASTER)); // If triggeredByAura has SPELL_ATTR4_PROC_ONLY_ON_CASTER then it can only proc on a cast spell with TARGET_UNIT_CASTER for (uint8 i = 0;i < MAX_SPELL_EFFECTS; ++i) { @@ -7401,12 +7401,12 @@ WorldObjectSpellConeTargetCheck::WorldObjectSpellConeTargetCheck(float coneAngle bool WorldObjectSpellConeTargetCheck::operator()(WorldObject* target) { - if (_spellInfo->AttributesCu & SPELL_ATTR0_CU_CONE_BACK) + if (_spellInfo->HasAttribute(SPELL_ATTR0_CU_CONE_BACK)) { if (!_caster->isInBack(target, _coneAngle)) return false; } - else if (_spellInfo->AttributesCu & SPELL_ATTR0_CU_CONE_LINE) + else if (_spellInfo->HasAttribute(SPELL_ATTR0_CU_CONE_LINE)) { if (!_caster->HasInLine(target, _caster->GetObjectSize())) return false; diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 651f28d3590..7a2f15b3370 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -324,7 +324,7 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex) case SPELLFAMILY_GENERIC: { // Meteor like spells (divided damage to targets) - if (m_spellInfo->AttributesCu & SPELL_ATTR0_CU_SHARE_DAMAGE) + if (m_spellInfo->HasAttribute(SPELL_ATTR0_CU_SHARE_DAMAGE)) { uint32 count = 0; for (std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) @@ -1755,7 +1755,7 @@ void Spell::EffectEnergize(SpellEffIndex effIndex) Powers power = Powers(m_spellInfo->Effects[effIndex].MiscValue); - if (unitTarget->GetTypeId() == TYPEID_PLAYER && unitTarget->getPowerType() != power && !(m_spellInfo->AttributesEx7 & SPELL_ATTR7_CAN_RESTORE_SECONDARY_POWER)) + if (unitTarget->GetTypeId() == TYPEID_PLAYER && unitTarget->getPowerType() != power && !m_spellInfo->HasAttribute(SPELL_ATTR7_CAN_RESTORE_SECONDARY_POWER)) return; if (unitTarget->GetMaxPower(power) == 0) @@ -1870,7 +1870,7 @@ void Spell::EffectEnergizePct(SpellEffIndex effIndex) Powers power = Powers(m_spellInfo->Effects[effIndex].MiscValue); - if (unitTarget->GetTypeId() == TYPEID_PLAYER && unitTarget->getPowerType() != power && !(m_spellInfo->AttributesEx7 & SPELL_ATTR7_CAN_RESTORE_SECONDARY_POWER)) + if (unitTarget->GetTypeId() == TYPEID_PLAYER && unitTarget->getPowerType() != power && !m_spellInfo->HasAttribute(SPELL_ATTR7_CAN_RESTORE_SECONDARY_POWER)) return; uint32 maxPower = unitTarget->GetMaxPower(power); @@ -4226,7 +4226,7 @@ void Spell::EffectStuck(SpellEffIndex /*effIndex*/) return; TC_LOG_DEBUG("spells", "Spell Effect: Stuck"); - TC_LOG_INFO("spells", "Player %s (guid %u) used auto-unstuck future at map %u (%f, %f, %f)", player->GetName().c_str(), player->GetGUIDLow(), player->GetMapId(), player->GetPositionX(), player->GetPositionY(), player->GetPositionZ()); + TC_LOG_DEBUG("spells", "Player %s (guid %u) used auto-unstuck future at map %u (%f, %f, %f)", player->GetName().c_str(), player->GetGUIDLow(), player->GetMapId(), player->GetPositionX(), player->GetPositionY(), player->GetPositionZ()); if (player->IsInFlight()) return; @@ -4819,8 +4819,8 @@ void Spell::EffectLeapBack(SpellEffIndex effIndex) if (!unitTarget) return; - float speedxy = float(m_spellInfo->Effects[effIndex].MiscValue)/10; - float speedz = float(damage/10); + float speedxy = m_spellInfo->Effects[effIndex].MiscValue / 10.f; + float speedz = damage/ 10.f; //1891: Disengage m_caster->JumpTo(speedxy, speedz, m_spellInfo->SpellIconID != 1891); } @@ -5344,13 +5344,13 @@ void Spell::EffectStealBeneficialBuff(SpellEffIndex effIndex) if ((aura->GetSpellInfo()->GetDispelMask()) & dispelMask) { // Need check for passive? this - if (!aurApp->IsPositive() || aura->IsPassive() || aura->GetSpellInfo()->AttributesEx4 & SPELL_ATTR4_NOT_STEALABLE) + if (!aurApp->IsPositive() || aura->IsPassive() || aura->GetSpellInfo()->HasAttribute(SPELL_ATTR4_NOT_STEALABLE)) continue; // The charges / stack amounts don't count towards the total number of auras that can be dispelled. // Ie: A dispel on a target with 5 stacks of Winters Chill and a Polymorph has 1 / (1 + 1) -> 50% chance to dispell // Polymorph instead of 1 / (5 + 1) -> 16%. - bool dispel_charges = (aura->GetSpellInfo()->AttributesEx7 & SPELL_ATTR7_DISPEL_CHARGES) != 0; + bool dispel_charges = aura->GetSpellInfo()->HasAttribute(SPELL_ATTR7_DISPEL_CHARGES); uint8 charges = dispel_charges ? aura->GetCharges() : aura->GetStackAmount(); if (charges > 0) steal_list.push_back(std::make_pair(aura, charges)); @@ -5852,7 +5852,7 @@ void Spell::EffectCastButtons(SpellEffIndex effIndex) if (!p_caster->HasSpell(spell_id) || p_caster->HasSpellCooldown(spell_id)) continue; - if (!(spellInfo->AttributesEx7 & SPELL_ATTR7_SUMMON_PLAYER_TOTEM)) + if (!spellInfo->HasAttribute(SPELL_ATTR7_SUMMON_PLAYER_TOTEM)) continue; uint32 cost = spellInfo->CalcPowerCost(m_caster, spellInfo->GetSchoolMask()); diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 6215f8cfa0a..862521a068c 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -453,7 +453,7 @@ int32 SpellEffectInfo::CalcValue(Unit const* caster, int32 const* bp, Unit const // amount multiplication based on caster's level if (!caster->IsControlledByPlayer() && _spellInfo->SpellLevel && _spellInfo->SpellLevel != caster->getLevel() && - !basePointsPerLevel && (_spellInfo->Attributes & SPELL_ATTR0_LEVEL_DAMAGE_CALCULATION)) + !basePointsPerLevel && _spellInfo->HasAttribute(SPELL_ATTR0_LEVEL_DAMAGE_CALCULATION)) { bool canEffectScale = false; switch (Effect) @@ -1049,14 +1049,14 @@ bool SpellInfo::NeedsToBeTriggeredByCaster(SpellInfo const* triggeringSpell) con bool SpellInfo::IsPassive() const { - return (Attributes & SPELL_ATTR0_PASSIVE) != 0; + return HasAttribute(SPELL_ATTR0_PASSIVE); } bool SpellInfo::IsAutocastable() const { - if (Attributes & SPELL_ATTR0_PASSIVE) + if (HasAttribute(SPELL_ATTR0_PASSIVE)) return false; - if (AttributesEx & SPELL_ATTR1_UNAUTOCASTABLE_BY_PET) + if (HasAttribute(SPELL_ATTR1_UNAUTOCASTABLE_BY_PET)) return false; return true; } @@ -1107,37 +1107,37 @@ bool SpellInfo::IsMultiSlotAura() const bool SpellInfo::IsStackableOnOneSlotWithDifferentCasters() const { /// TODO: Re-verify meaning of SPELL_ATTR3_STACK_FOR_DIFF_CASTERS and update conditions here - return StackAmount > 1 && !IsChanneled() && !(AttributesEx3 & SPELL_ATTR3_STACK_FOR_DIFF_CASTERS); + return StackAmount > 1 && !IsChanneled() && !HasAttribute(SPELL_ATTR3_STACK_FOR_DIFF_CASTERS); } bool SpellInfo::IsCooldownStartedOnEvent() const { - return Attributes & SPELL_ATTR0_DISABLED_WHILE_ACTIVE || (CategoryEntry && CategoryEntry->Flags & SPELL_CATEGORY_FLAG_COOLDOWN_STARTS_ON_EVENT); + return HasAttribute(SPELL_ATTR0_DISABLED_WHILE_ACTIVE) || (CategoryEntry && CategoryEntry->Flags & SPELL_CATEGORY_FLAG_COOLDOWN_STARTS_ON_EVENT); } bool SpellInfo::IsDeathPersistent() const { - return (AttributesEx3 & SPELL_ATTR3_DEATH_PERSISTENT) != 0; + return HasAttribute(SPELL_ATTR3_DEATH_PERSISTENT); } bool SpellInfo::IsRequiringDeadTarget() const { - return (AttributesEx3 & SPELL_ATTR3_ONLY_TARGET_GHOSTS) != 0; + return HasAttribute(SPELL_ATTR3_ONLY_TARGET_GHOSTS); } bool SpellInfo::IsAllowingDeadTarget() const { - return AttributesEx2 & SPELL_ATTR2_CAN_TARGET_DEAD || Targets & (TARGET_FLAG_CORPSE_ALLY | TARGET_FLAG_CORPSE_ENEMY | TARGET_FLAG_UNIT_DEAD); + return HasAttribute(SPELL_ATTR2_CAN_TARGET_DEAD) || Targets & (TARGET_FLAG_CORPSE_ALLY | TARGET_FLAG_CORPSE_ENEMY | TARGET_FLAG_UNIT_DEAD); } bool SpellInfo::CanBeUsedInCombat() const { - return !(Attributes & SPELL_ATTR0_CANT_USED_IN_COMBAT); + return !HasAttribute(SPELL_ATTR0_CANT_USED_IN_COMBAT); } bool SpellInfo::IsPositive() const { - return !(AttributesCu & SPELL_ATTR0_CU_NEGATIVE); + return !HasAttribute(SPELL_ATTR0_CU_NEGATIVE); } bool SpellInfo::IsPositiveEffect(uint8 effIndex) const @@ -1146,27 +1146,27 @@ bool SpellInfo::IsPositiveEffect(uint8 effIndex) const { default: case 0: - return !(AttributesCu & SPELL_ATTR0_CU_NEGATIVE_EFF0); + return !HasAttribute(SPELL_ATTR0_CU_NEGATIVE_EFF0); case 1: - return !(AttributesCu & SPELL_ATTR0_CU_NEGATIVE_EFF1); + return !HasAttribute(SPELL_ATTR0_CU_NEGATIVE_EFF1); case 2: - return !(AttributesCu & SPELL_ATTR0_CU_NEGATIVE_EFF2); + return !HasAttribute(SPELL_ATTR0_CU_NEGATIVE_EFF2); } } bool SpellInfo::IsChanneled() const { - return (AttributesEx & (SPELL_ATTR1_CHANNELED_1 | SPELL_ATTR1_CHANNELED_2)) != 0; + return HasAttribute(SPELL_ATTR1_CHANNELED_1) || HasAttribute(SPELL_ATTR1_CHANNELED_2); } bool SpellInfo::NeedsComboPoints() const { - return (AttributesEx & (SPELL_ATTR1_REQ_COMBO_POINTS1 | SPELL_ATTR1_REQ_COMBO_POINTS2)) != 0; + return HasAttribute(SPELL_ATTR1_REQ_COMBO_POINTS1) || HasAttribute(SPELL_ATTR1_REQ_COMBO_POINTS2); } bool SpellInfo::IsBreakingStealth() const { - return !(AttributesEx & SPELL_ATTR1_NOT_BREAK_STEALTH); + return !HasAttribute(SPELL_ATTR1_NOT_BREAK_STEALTH); } bool SpellInfo::IsRangedWeaponSpell() const @@ -1177,12 +1177,12 @@ bool SpellInfo::IsRangedWeaponSpell() const bool SpellInfo::IsAutoRepeatRangedSpell() const { - return (AttributesEx2 & SPELL_ATTR2_AUTOREPEAT_FLAG) != 0; + return HasAttribute(SPELL_ATTR2_AUTOREPEAT_FLAG); } bool SpellInfo::IsAffectedBySpellMods() const { - return !(AttributesEx3 & SPELL_ATTR3_NO_DONE_BONUS); + return !HasAttribute(SPELL_ATTR3_NO_DONE_BONUS); } bool SpellInfo::IsAffectedBySpellMod(SpellModifier const* mod) const @@ -1205,11 +1205,11 @@ bool SpellInfo::IsAffectedBySpellMod(SpellModifier const* mod) const bool SpellInfo::CanPierceImmuneAura(SpellInfo const* aura) const { // these spells pierce all avalible spells (Resurrection Sickness for example) - if (Attributes & SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY) + if (HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY)) return true; // these spells (Cyclone for example) can pierce all... // ...but not these (Divine shield, Ice block, Cyclone and Banish for example) - if ((AttributesEx & SPELL_ATTR1_UNAFFECTED_BY_SCHOOL_IMMUNE) && !(aura && (aura->Mechanic == MECHANIC_IMMUNE_SHIELD || aura->Mechanic == MECHANIC_INVULNERABILITY || aura->Mechanic == MECHANIC_BANISH))) + if ((HasAttribute(SPELL_ATTR1_UNAFFECTED_BY_SCHOOL_IMMUNE)) && !(aura && (aura->Mechanic == MECHANIC_IMMUNE_SHIELD || aura->Mechanic == MECHANIC_INVULNERABILITY || aura->Mechanic == MECHANIC_BANISH))) return true; return false; @@ -1218,15 +1218,15 @@ bool SpellInfo::CanPierceImmuneAura(SpellInfo const* aura) const bool SpellInfo::CanDispelAura(SpellInfo const* aura) const { // These spells (like Mass Dispel) can dispell all auras, except death persistent ones (like Dungeon and Battleground Deserter) - if (Attributes & SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY && !aura->IsDeathPersistent()) + if (HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY) && !aura->IsDeathPersistent()) return true; // These auras (like Divine Shield) can't be dispelled - if (aura->Attributes & SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY) + if (aura->HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY)) return false; // These auras (Cyclone for example) are not dispelable - if (aura->AttributesEx & SPELL_ATTR1_UNAFFECTED_BY_SCHOOL_IMMUNE) + if (aura->HasAttribute(SPELL_ATTR1_UNAFFECTED_BY_SCHOOL_IMMUNE)) return false; return true; @@ -1235,7 +1235,7 @@ bool SpellInfo::CanDispelAura(SpellInfo const* aura) const bool SpellInfo::IsSingleTarget() const { // all other single target spells have if it has AttributesEx5 - if (AttributesEx5 & SPELL_ATTR5_SINGLE_TARGET_SPELL) + if (HasAttribute(SPELL_ATTR5_SINGLE_TARGET_SPELL)) return true; switch (GetSpellSpecific()) @@ -1332,7 +1332,7 @@ SpellCastResult SpellInfo::CheckShapeshift(uint32 form) const if (actAsShifted) { - if (Attributes & SPELL_ATTR0_NOT_SHAPESHIFT) // not while shapeshifted + if (HasAttribute(SPELL_ATTR0_NOT_SHAPESHIFT)) // not while shapeshifted return SPELL_FAILED_NOT_SHAPESHIFT; else if (Stances != 0) // needs other shapeshift return SPELL_FAILED_ONLY_SHAPESHIFT; @@ -1340,7 +1340,7 @@ SpellCastResult SpellInfo::CheckShapeshift(uint32 form) const else { // needs shapeshift - if (!(AttributesEx2 & SPELL_ATTR2_NOT_NEED_SHAPESHIFT) && Stances != 0) + if (!HasAttribute(SPELL_ATTR2_NOT_NEED_SHAPESHIFT) && Stances != 0) return SPELL_FAILED_ONLY_SHAPESHIFT; } @@ -1379,7 +1379,7 @@ SpellCastResult SpellInfo::CheckLocation(uint32 map_id, uint32 zone_id, uint32 a } // continent limitation (virtual continent) - if (AttributesEx4 & SPELL_ATTR4_CAST_ONLY_IN_OUTLAND) + if (HasAttribute(SPELL_ATTR4_CAST_ONLY_IN_OUTLAND)) { uint32 v_map = GetVirtualMapForMapAndZone(map_id, zone_id); MapEntry const* mapEntry = sMapStore.LookupEntry(v_map); @@ -1388,7 +1388,7 @@ SpellCastResult SpellInfo::CheckLocation(uint32 map_id, uint32 zone_id, uint32 a } // raid instance limitation - if (AttributesEx6 & SPELL_ATTR6_NOT_IN_RAID_INSTANCE) + if (HasAttribute(SPELL_ATTR6_NOT_IN_RAID_INSTANCE)) { MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); if (!mapEntry || mapEntry->IsRaid()) @@ -1493,11 +1493,11 @@ SpellCastResult SpellInfo::CheckLocation(uint32 map_id, uint32 zone_id, uint32 a SpellCastResult SpellInfo::CheckTarget(Unit const* caster, WorldObject const* target, bool implicit) const { - if (AttributesEx & SPELL_ATTR1_CANT_TARGET_SELF && caster == target) + if (HasAttribute(SPELL_ATTR1_CANT_TARGET_SELF) && caster == target) return SPELL_FAILED_BAD_TARGETS; // check visibility - ignore stealth for implicit (area) targets - if (!(AttributesEx6 & SPELL_ATTR6_CAN_TARGET_INVISIBLE) && !caster->CanSeeOrDetect(target, implicit)) + if (!HasAttribute(SPELL_ATTR6_CAN_TARGET_INVISIBLE) && !caster->CanSeeOrDetect(target, implicit)) return SPELL_FAILED_BAD_TARGETS; Unit const* unitTarget = target->ToUnit(); @@ -1505,7 +1505,7 @@ SpellCastResult SpellInfo::CheckTarget(Unit const* caster, WorldObject const* ta // creature/player specific target checks if (unitTarget) { - if (AttributesEx & SPELL_ATTR1_CANT_TARGET_IN_COMBAT) + if (HasAttribute(SPELL_ATTR1_CANT_TARGET_IN_COMBAT)) { if (unitTarget->IsInCombat()) return SPELL_FAILED_TARGET_AFFECTING_COMBAT; @@ -1517,9 +1517,9 @@ SpellCastResult SpellInfo::CheckTarget(Unit const* caster, WorldObject const* ta } // only spells with SPELL_ATTR3_ONLY_TARGET_GHOSTS can target ghosts - if (((AttributesEx3 & SPELL_ATTR3_ONLY_TARGET_GHOSTS) != 0) != unitTarget->HasAuraType(SPELL_AURA_GHOST)) + if (HasAttribute(SPELL_ATTR3_ONLY_TARGET_GHOSTS) != unitTarget->HasAuraType(SPELL_AURA_GHOST)) { - if (AttributesEx3 & SPELL_ATTR3_ONLY_TARGET_GHOSTS) + if (HasAttribute(SPELL_ATTR3_ONLY_TARGET_GHOSTS)) return SPELL_FAILED_TARGET_NOT_GHOST; else return SPELL_FAILED_BAD_TARGETS; @@ -1530,12 +1530,12 @@ SpellCastResult SpellInfo::CheckTarget(Unit const* caster, WorldObject const* ta if (caster->GetTypeId() == TYPEID_PLAYER) { // Do not allow these spells to target creatures not tapped by us (Banish, Polymorph, many quest spells) - if (AttributesEx2 & SPELL_ATTR2_CANT_TARGET_TAPPED) + if (HasAttribute(SPELL_ATTR2_CANT_TARGET_TAPPED)) if (Creature const* targetCreature = unitTarget->ToCreature()) if (targetCreature->hasLootRecipient() && !targetCreature->isTappedBy(caster->ToPlayer())) return SPELL_FAILED_CANT_CAST_ON_TAPPED; - if (AttributesCu & SPELL_ATTR0_CU_PICKPOCKET) + if (HasAttribute(SPELL_ATTR0_CU_PICKPOCKET)) { if (unitTarget->GetTypeId() == TYPEID_PLAYER) return SPELL_FAILED_BAD_TARGETS; @@ -1575,21 +1575,21 @@ SpellCastResult SpellInfo::CheckTarget(Unit const* caster, WorldObject const* ta else return SPELL_CAST_OK; // corpseOwner and unit specific target checks - if (AttributesEx3 & SPELL_ATTR3_ONLY_TARGET_PLAYERS && !unitTarget->ToPlayer()) + if (HasAttribute(SPELL_ATTR3_ONLY_TARGET_PLAYERS) && !unitTarget->ToPlayer()) return SPELL_FAILED_TARGET_NOT_PLAYER; if (!IsAllowingDeadTarget() && !unitTarget->IsAlive()) return SPELL_FAILED_TARGETS_DEAD; // check this flag only for implicit targets (chain and area), allow to explicitly target units for spells like Shield of Righteousness - if (implicit && AttributesEx6 & SPELL_ATTR6_CANT_TARGET_CROWD_CONTROLLED && !unitTarget->CanFreeMove()) + if (implicit && HasAttribute(SPELL_ATTR6_CANT_TARGET_CROWD_CONTROLLED) && !unitTarget->CanFreeMove()) return SPELL_FAILED_BAD_TARGETS; // checked in Unit::IsValidAttack/AssistTarget, shouldn't be checked for ENTRY targets - //if (!(AttributesEx6 & SPELL_ATTR6_CAN_TARGET_UNTARGETABLE) && target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) + //if (!HasAttribute(SPELL_ATTR6_CAN_TARGET_UNTARGETABLE) && target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) // return SPELL_FAILED_BAD_TARGETS; - //if (!(AttributesEx6 & SPELL_ATTR6_CAN_TARGET_POSSESSED_FRIENDS) + //if (!HasAttribute(SPELL_ATTR6_CAN_TARGET_POSSESSED_FRIENDS) if (!CheckTargetCreatureType(unitTarget)) { @@ -1705,7 +1705,7 @@ SpellCastResult SpellInfo::CheckVehicle(Unit const* caster) const checkMask = VEHICLE_SEAT_FLAG_CAN_ATTACK; VehicleSeatEntry const* vehicleSeat = vehicle->GetSeatForPassenger(caster); - if (!(AttributesEx6 & SPELL_ATTR6_CASTABLE_WHILE_ON_VEHICLE) && !(Attributes & SPELL_ATTR0_CASTABLE_WHILE_MOUNTED) + if (!HasAttribute(SPELL_ATTR6_CASTABLE_WHILE_ON_VEHICLE) && !HasAttribute(SPELL_ATTR0_CASTABLE_WHILE_MOUNTED) && (vehicleSeat->m_flags & checkMask) != checkMask) return SPELL_FAILED_CANT_DO_THAT_RIGHT_NOW; @@ -2106,7 +2106,7 @@ uint32 SpellInfo::CalcCastTime(Spell* spell /*= NULL*/) const if (spell) spell->GetCaster()->ModSpellCastTime(this, castTime, spell); - if (Attributes & SPELL_ATTR0_REQ_AMMO && (!IsAutoRepeatRangedSpell())) + if (HasAttribute(SPELL_ATTR0_REQ_AMMO) && (!IsAutoRepeatRangedSpell())) castTime += 500; return (castTime > 0) ? uint32(castTime) : 0; @@ -2147,7 +2147,7 @@ uint32 SpellInfo::GetRecoveryTime() const int32 SpellInfo::CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask) const { // Spell drain all exist power on cast (Only paladin lay of Hands) - if (AttributesEx & SPELL_ATTR1_DRAIN_ALL_POWER) + if (HasAttribute(SPELL_ATTR1_DRAIN_ALL_POWER)) { // If power type - health drain all if (PowerType == POWER_HEALTH) @@ -2193,7 +2193,7 @@ int32 SpellInfo::CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask) c powerCost += caster->GetInt32Value(UNIT_FIELD_POWER_COST_MODIFIER + school); // Shiv - costs 20 + weaponSpeed*10 energy (apply only to non-triggered spell with energy cost) - if (AttributesEx4 & SPELL_ATTR4_SPELL_VS_EXTEND_COST) + if (HasAttribute(SPELL_ATTR4_SPELL_VS_EXTEND_COST)) { uint32 speed = 0; if (SpellShapeshiftEntry const* ss = sSpellShapeshiftStore.LookupEntry(caster->GetShapeshiftForm())) @@ -2201,7 +2201,7 @@ int32 SpellInfo::CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask) c else { WeaponAttackType slot = BASE_ATTACK; - if (AttributesEx3 & SPELL_ATTR3_REQ_OFFHAND) + if (HasAttribute(SPELL_ATTR3_REQ_OFFHAND)) slot = OFF_ATTACK; speed = caster->GetAttackTime(slot); @@ -2216,7 +2216,7 @@ int32 SpellInfo::CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask) c if (!caster->IsControlledByPlayer()) { - if (Attributes & SPELL_ATTR0_LEVEL_DAMAGE_CALCULATION) + if (HasAttribute(SPELL_ATTR0_LEVEL_DAMAGE_CALCULATION)) { GtNPCManaCostScalerEntry const* spellScaler = sGtNPCManaCostScalerStore.LookupEntry(SpellLevel - 1); GtNPCManaCostScalerEntry const* casterScaler = sGtNPCManaCostScalerStore.LookupEntry(caster->getLevel() - 1); @@ -2361,7 +2361,7 @@ void SpellInfo::_InitializeExplicitTargetMask() bool SpellInfo::_IsPositiveEffect(uint8 effIndex, bool deep) const { // not found a single positive spell with this attribute - if (Attributes & SPELL_ATTR0_NEGATIVE_1) + if (HasAttribute(SPELL_ATTR0_NEGATIVE_1)) return false; switch (SpellFamilyName) @@ -2542,7 +2542,7 @@ bool SpellInfo::_IsPositiveEffect(uint8 effIndex, bool deep) const if (Effects[effIndex].TargetA.GetTarget() != TARGET_UNIT_CASTER) return false; // but not this if this first effect (didn't find better check) - if (Attributes & SPELL_ATTR0_NEGATIVE_1 && effIndex == 0) + if (HasAttribute(SPELL_ATTR0_NEGATIVE_1) && effIndex == 0) return false; break; case SPELL_AURA_MECHANIC_IMMUNITY: diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h index d9c062e77fd..f2808a089e1 100644 --- a/src/server/game/Spells/SpellInfo.h +++ b/src/server/game/Spells/SpellInfo.h @@ -376,6 +376,16 @@ public: bool HasAura(AuraType aura) const; bool HasAreaAuraEffect() const; + inline bool HasAttribute(SpellAttr0 attribute) const { return !!(Attributes & attribute); } + inline bool HasAttribute(SpellAttr1 attribute) const { return !!(AttributesEx & attribute); } + inline bool HasAttribute(SpellAttr2 attribute) const { return !!(AttributesEx2 & attribute); } + inline bool HasAttribute(SpellAttr3 attribute) const { return !!(AttributesEx3 & attribute); } + inline bool HasAttribute(SpellAttr4 attribute) const { return !!(AttributesEx4 & attribute); } + inline bool HasAttribute(SpellAttr5 attribute) const { return !!(AttributesEx5 & attribute); } + inline bool HasAttribute(SpellAttr6 attribute) const { return !!(AttributesEx6 & attribute); } + inline bool HasAttribute(SpellAttr7 attribute) const { return !!(AttributesEx7 & attribute); } + inline bool HasAttribute(SpellCustomAttributes customAttribute) const { return !!(AttributesCu & customAttribute); } + bool IsExplicitDiscovery() const; bool IsLootCrafting() const; bool IsQuestTame() const; diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index db16a7312ea..b9f046670cc 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -2238,7 +2238,7 @@ void SpellMgr::LoadEnchantCustomAttr() continue; /// @todo find a better check - if (!(spellInfo->AttributesEx2 & SPELL_ATTR2_PRESERVE_ENCHANT_IN_ARENA) || !(spellInfo->Attributes & SPELL_ATTR0_NOT_SHAPESHIFT)) + if (!spellInfo->HasAttribute(SPELL_ATTR2_PRESERVE_ENCHANT_IN_ARENA) || !spellInfo->HasAttribute(SPELL_ATTR0_NOT_SHAPESHIFT)) continue; for (uint32 j = 0; j < MAX_SPELL_EFFECTS; ++j) @@ -2959,6 +2959,8 @@ void SpellMgr::LoadSpellInfoCorrections() switch (spellInfo->Id) { case 53096: // Quetz'lun's Judgment + case 70743: // AoD Special + case 70614: // AoD Special - Vegard spellInfo->MaxAffectedTargets = 1; break; case 42436: // Drink! (Brewfest) @@ -3729,7 +3731,7 @@ void SpellMgr::LoadSpellInfoCorrections() case 45440: // Steam Tonk Controller case 60256: // Collect Sample // Crashes client on pressing ESC - spellInfo->AttributesEx4 &= ~SPELL_ATTR4_TRIGGERED; + spellInfo->AttributesEx4 &= ~SPELL_ATTR4_CAN_CAST_WHILE_CASTING; break; // ISLE OF CONQUEST SPELLS // @@ -3746,7 +3748,7 @@ void SpellMgr::LoadSpellInfoCorrections() { case SPELLFAMILY_PALADIN: // Seals of the Pure should affect Seal of Righteousness - if (spellInfo->SpellIconID == 25 && spellInfo->Attributes & SPELL_ATTR0_PASSIVE) + if (spellInfo->SpellIconID == 25 && spellInfo->HasAttribute(SPELL_ATTR0_PASSIVE)) spellInfo->Effects[EFFECT_0].SpellClassMask[1] |= 0x20000000; break; case SPELLFAMILY_DEATHKNIGHT: diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 555f89aad8f..84f22f2f1a9 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -940,6 +940,12 @@ void World::LoadConfigSettings(bool reload) } m_bool_configs[CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY] = sConfigMgr->GetBoolDefault("SaveRespawnTimeImmediately", true); + if (!m_bool_configs[CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY]) + { + TC_LOG_WARN("server.loading", "SaveRespawnTimeImmediately triggers assertions when disabled, overridden to Enabled"); + m_bool_configs[CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY] = true; + } + m_bool_configs[CONFIG_WEATHER] = sConfigMgr->GetBoolDefault("ActivateWeather", true); m_int_configs[CONFIG_DISABLE_BREATHING] = sConfigMgr->GetIntDefault("DisableWaterBreath", SEC_CONSOLE); @@ -1334,7 +1340,7 @@ void World::SetInitialWorldSettings() !MapManager::ExistMapAndVMap(530, 10349.6f, -6357.29f) || !MapManager::ExistMapAndVMap(530, -3961.64f, -13931.2f)))) { - TC_LOG_ERROR("server.loading", "Correct *.map files not found in path '%smaps' or *.vmtree/*.vmtile files in '%svmaps'. Please place *.map/*.vmtree/*.vmtile files in appropriate directories or correct the DataDir value in the worldserver.conf file.", m_dataPath.c_str(), m_dataPath.c_str()); + TC_LOG_FATAL("server.loading", "Unable to load critical files - server shutting down !!!"); exit(1); } @@ -3018,7 +3024,7 @@ void World::ResetRandomBG() { TC_LOG_INFO("misc", "Random BG status reset for all characters."); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_BATTLEGROUND_RANDOM); + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_BATTLEGROUND_RANDOM_ALL); CharacterDatabase.Execute(stmt); for (SessionMap::const_iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr) diff --git a/src/server/scripts/CMakeLists.txt b/src/server/scripts/CMakeLists.txt index e7d08d69805..8bf84c32447 100644 --- a/src/server/scripts/CMakeLists.txt +++ b/src/server/scripts/CMakeLists.txt @@ -50,6 +50,7 @@ include_directories( ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Recast/Include ${CMAKE_SOURCE_DIR}/dep/g3dlite/include ${CMAKE_SOURCE_DIR}/dep/SFMT + ${CMAKE_SOURCE_DIR}/dep/cppformat ${CMAKE_SOURCE_DIR}/dep/zlib ${CMAKE_SOURCE_DIR}/src/server/shared ${CMAKE_SOURCE_DIR}/src/server/shared/Configuration diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp index 5d6cdb3fb63..ccd82aa3ef9 100644 --- a/src/server/scripts/Commands/cs_debug.cpp +++ b/src/server/scripts/Commands/cs_debug.cpp @@ -299,7 +299,7 @@ public: else if (commentToken[1] == '/') { std::string str; - getline(ifs, str); + std::getline(ifs, str); continue; } // regular data diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 0e86823aa7c..dea2a5216e7 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -237,7 +237,8 @@ public: zoneId, (zoneEntry ? zoneEntry->area_name[handler->GetSessionDbcLocale()] : unknown), areaId, (areaEntry ? areaEntry->area_name[handler->GetSessionDbcLocale()] : unknown), object->GetPhaseMask(), - object->GetPositionX(), object->GetPositionY(), object->GetPositionZ(), object->GetOrientation(), + object->GetPositionX(), object->GetPositionY(), object->GetPositionZ(), object->GetOrientation()); + handler->PSendSysMessage(LANG_GRID_POSITION, cell.GridX(), cell.GridY(), cell.CellX(), cell.CellY(), object->GetInstanceId(), zoneX, zoneY, groundZ, floorZ, haveMap, haveVMap, haveMMap); diff --git a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp index 59f2ce1a2c9..92c3f83034d 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp @@ -360,17 +360,17 @@ public: case GOSSIP_ACTION_INFO_DEF+3: player->CLOSE_GOSSIP_MENU(); pBarnesAI->m_uiEventId = EVENT_OZ; - TC_LOG_INFO("scripts", "player (%s) manually set Opera event to EVENT_OZ", player->GetGUID().ToString().c_str()); + TC_LOG_DEBUG("scripts", "player (%s) manually set Opera event to EVENT_OZ", player->GetGUID().ToString().c_str()); break; case GOSSIP_ACTION_INFO_DEF+4: player->CLOSE_GOSSIP_MENU(); pBarnesAI->m_uiEventId = EVENT_HOOD; - TC_LOG_INFO("scripts", "player (%s) manually set Opera event to EVENT_HOOD", player->GetGUID().ToString().c_str()); + TC_LOG_DEBUG("scripts", "player (%s) manually set Opera event to EVENT_HOOD", player->GetGUID().ToString().c_str()); break; case GOSSIP_ACTION_INFO_DEF+5: player->CLOSE_GOSSIP_MENU(); pBarnesAI->m_uiEventId = EVENT_RAJ; - TC_LOG_INFO("scripts", "player (%s) manually set Opera event to EVENT_RAJ", player->GetGUID().ToString().c_str()); + TC_LOG_DEBUG("scripts", "player (%s) manually set Opera event to EVENT_RAJ", player->GetGUID().ToString().c_str()); break; } diff --git a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp index 3db0e1092fd..326360428d2 100644 --- a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp @@ -174,7 +174,7 @@ public: anchor->GetContactPoint(me, anchorX, anchorY, z, 1.0f); playerGUID = target->GetGUID(); - Talk(SAY_EVENT_START); + Talk(SAY_EVENT_START, target); } void UpdateAI(uint32 diff) override diff --git a/src/server/scripts/EasternKingdoms/SunkenTemple/sunken_temple.cpp b/src/server/scripts/EasternKingdoms/SunkenTemple/sunken_temple.cpp index 3ced85f09a2..97be139cefe 100644 --- a/src/server/scripts/EasternKingdoms/SunkenTemple/sunken_temple.cpp +++ b/src/server/scripts/EasternKingdoms/SunkenTemple/sunken_temple.cpp @@ -36,19 +36,27 @@ EndContentData */ # at_malfurion_Stormrage_trigger #####*/ -class at_malfurion_stormrage : public AreaTriggerScript +enum MalfurionMisc { -public: - at_malfurion_stormrage() : AreaTriggerScript("at_malfurion_stormrage") { } + NPC_MALFURION_STORMRAGE = 15362, + QUEST_ERANIKUS_TYRANT_OF_DREAMS = 8733, + QUEST_THE_CHARGE_OF_DRAGONFLIGHTS = 8555, +}; - bool OnTrigger(Player* player, const AreaTriggerEntry* /*at*/) override - { - if (player->GetInstanceScript() && !player->FindNearestCreature(15362, 15)) - player->SummonCreature(15362, player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), -1.52f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 100000); - return false; - } +class at_malfurion_stormrage : public AreaTriggerScript +{ + public: + at_malfurion_stormrage() : AreaTriggerScript("at_malfurion_stormrage") { } + bool OnTrigger(Player* player, const AreaTriggerEntry* /*at*/) override + { + if (player->GetInstanceScript() && !player->FindNearestCreature(NPC_MALFURION_STORMRAGE, 15.0f) && + player->GetQuestStatus(QUEST_THE_CHARGE_OF_DRAGONFLIGHTS) == QUEST_STATUS_REWARDED && player->GetQuestStatus(QUEST_ERANIKUS_TYRANT_OF_DREAMS) != QUEST_STATUS_REWARDED) + player->SummonCreature(NPC_MALFURION_STORMRAGE, player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), -1.52f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 100000); + return false; + } }; + /*##### # go_atalai_statue #####*/ diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_muru.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_muru.cpp index ea56494f0f9..421f3d771a0 100644 --- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_muru.cpp +++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_muru.cpp @@ -207,7 +207,7 @@ public: void DamageTaken(Unit* /*done_by*/, uint32 &damage) override { - if (damage > me->GetHealth() && events.IsInPhase(PHASE_ONE)) + if (damage >= me->GetHealth() && events.IsInPhase(PHASE_ONE)) { damage = 0; me->RemoveAllAuras(); diff --git a/src/server/scripts/EasternKingdoms/ZulAman/boss_hexlord.cpp b/src/server/scripts/EasternKingdoms/ZulAman/boss_hexlord.cpp index 01ac3920082..1a98c081570 100644 --- a/src/server/scripts/EasternKingdoms/ZulAman/boss_hexlord.cpp +++ b/src/server/scripts/EasternKingdoms/ZulAman/boss_hexlord.cpp @@ -689,54 +689,57 @@ class boss_alyson_antille : public CreatureScript } }; -struct boss_gazakrothAI : public boss_hexlord_addAI +class boss_gazakroth : public CreatureScript { - boss_gazakrothAI(Creature* creature) : boss_hexlord_addAI(creature) - { - Initialize(); - } - - void Initialize() - { - firebolt_timer = 2000; - } + public: - uint32 firebolt_timer; + boss_gazakroth() : CreatureScript("boss_gazakroth") { } + + struct boss_gazakrothAI : public boss_hexlord_addAI + { + boss_gazakrothAI(Creature* creature) : boss_hexlord_addAI(creature) + { + Initialize(); + } - void Reset() override - { - Initialize(); - boss_hexlord_addAI::Reset(); - } + void Initialize() + { + firebolt_timer = 2 * IN_MILLISECONDS; + } + + void Reset() override + { + Initialize(); + boss_hexlord_addAI::Reset(); + } - void AttackStart(Unit* who) override - { - if (!who) - return; + void AttackStart(Unit* who) override + { + AttackStartCaster(who, 20.0f); + } - if (who->isTargetableForAttack()) - { - if (me->Attack(who, false)) + void UpdateAI(uint32 diff) override { - me->GetMotionMaster()->MoveChase(who, 20); - me->AddThreat(who, 0.0f); + if (!UpdateVictim()) + return; + + if (firebolt_timer <= diff) + { + DoCastVictim(SPELL_FIREBOLT, false); + firebolt_timer = 0.7 * IN_MILLISECONDS; + } else firebolt_timer -= diff; + + boss_hexlord_addAI::UpdateAI(diff); } - } - } - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; + private: + uint32 firebolt_timer; + }; - if (firebolt_timer <= diff) + CreatureAI* GetAI(Creature* creature) const override { - DoCastVictim(SPELL_FIREBOLT, false); - firebolt_timer = 700; - } else firebolt_timer -= diff; - - boss_hexlord_addAI::UpdateAI(diff); - } + return GetInstanceAI<boss_gazakrothAI>(creature); + } }; class boss_lord_raadan : public CreatureScript @@ -1062,7 +1065,7 @@ void AddSC_boss_hex_lord_malacrass() { new boss_hexlord_malacrass(); new boss_thurg(); - // new boss_gazakroth(); + new boss_gazakroth(); new boss_lord_raadan(); new boss_darkheart(); new boss_slither(); diff --git a/src/server/scripts/EasternKingdoms/zone_redridge_mountains.cpp b/src/server/scripts/EasternKingdoms/zone_redridge_mountains.cpp index 898e3f9a2cb..ecfd705cf4d 100644 --- a/src/server/scripts/EasternKingdoms/zone_redridge_mountains.cpp +++ b/src/server/scripts/EasternKingdoms/zone_redridge_mountains.cpp @@ -92,7 +92,7 @@ public: me->SetWalk(false); break; case 115: - player->AreaExploredOrEventHappens(QUEST_MISSING_IN_ACTION); + player->GroupEventHappens(QUEST_MISSING_IN_ACTION, me); timer = 2000; phase = 4; break; diff --git a/src/server/scripts/EasternKingdoms/zone_undercity.cpp b/src/server/scripts/EasternKingdoms/zone_undercity.cpp index 9ce694fb76c..ca281bab60a 100644 --- a/src/server/scripts/EasternKingdoms/zone_undercity.cpp +++ b/src/server/scripts/EasternKingdoms/zone_undercity.cpp @@ -40,25 +40,44 @@ EndContentData */ enum Sylvanas { - QUEST_JOURNEY_TO_UNDERCITY = 9180, - EMOTE_LAMENT_END = 0, - SAY_LAMENT_END = 1, + QUEST_JOURNEY_TO_UNDERCITY = 9180, - SOUND_CREDIT = 10896, - ENTRY_HIGHBORNE_LAMENTER = 21628, - ENTRY_HIGHBORNE_BUNNY = 21641, + EMOTE_LAMENT_END = 0, + SAY_LAMENT_END = 1, + EMOTE_LAMENT = 2, - SPELL_HIGHBORNE_AURA = 37090, - SPELL_SYLVANAS_CAST = 36568, - SPELL_RIBBON_OF_SOULS = 34432, // the real one to use might be 37099 + // Ambassador Sunsorrow + SAY_SUNSORROW_WHISPER = 0, + + SOUND_CREDIT = 10896, + + NPC_HIGHBORNE_LAMENTER = 21628, + NPC_HIGHBORNE_BUNNY = 21641, + NPC_AMBASSADOR_SUNSORROW = 16287, + + SPELL_HIGHBORNE_AURA = 37090, + SPELL_SYLVANAS_CAST = 36568, + //SPELL_RIBBON_OF_SOULS = 34432, the real one to use might be 37099 + SPELL_RIBBON_OF_SOULS = 37099, // Combat spells - SPELL_BLACK_ARROW = 59712, - SPELL_FADE = 20672, - SPELL_FADE_BLINK = 29211, - SPELL_MULTI_SHOT = 59713, - SPELL_SHOT = 59710, - SPELL_SUMMON_SKELETON = 59711 + SPELL_BLACK_ARROW = 59712, + SPELL_FADE = 20672, + SPELL_FADE_BLINK = 29211, + SPELL_MULTI_SHOT = 59713, + SPELL_SHOT = 59710, + SPELL_SUMMON_SKELETON = 59711, + + // Events + EVENT_FADE = 1, + EVENT_SUMMON_SKELETON = 2, + EVENT_BLACK_ARROW = 3, + EVENT_SHOOT = 4, + EVENT_MULTI_SHOT = 5, + EVENT_LAMENT_OF_THE_HIGHBORN = 6, + EVENT_SUNSORROW_WHISPER = 7, + + GUID_EVENT_INVOKER = 1, }; float HighborneLoc[4][3]= @@ -77,26 +96,14 @@ class npc_lady_sylvanas_windrunner : public CreatureScript public: npc_lady_sylvanas_windrunner() : CreatureScript("npc_lady_sylvanas_windrunner") { } - bool OnQuestReward(Player* /*player*/, Creature* creature, const Quest *_Quest, uint32 /*slot*/) override + bool OnQuestReward(Player* player, Creature* creature, const Quest *_Quest, uint32 /*slot*/) override { if (_Quest->GetQuestId() == QUEST_JOURNEY_TO_UNDERCITY) - { - ENSURE_AI(npc_lady_sylvanas_windrunner::npc_lady_sylvanas_windrunnerAI, creature->AI())->LamentEvent = true; - ENSURE_AI(npc_lady_sylvanas_windrunner::npc_lady_sylvanas_windrunnerAI, creature->AI())->DoPlaySoundToSet(creature, SOUND_CREDIT); - creature->CastSpell(creature, SPELL_SYLVANAS_CAST, false); - - for (uint8 i = 0; i < 4; ++i) - creature->SummonCreature(ENTRY_HIGHBORNE_LAMENTER, HighborneLoc[i][0], HighborneLoc[i][1], HIGHBORNE_LOC_Y, HighborneLoc[i][2], TEMPSUMMON_TIMED_DESPAWN, 160000); - } + creature->AI()->SetGUID(player->GetGUID(), GUID_EVENT_INVOKER); return true; } - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_lady_sylvanas_windrunnerAI(creature); - } - struct npc_lady_sylvanas_windrunnerAI : public ScriptedAI { npc_lady_sylvanas_windrunnerAI(Creature* creature) : ScriptedAI(creature) @@ -106,41 +113,51 @@ public: void Initialize() { - LamentEventTimer = 5000; LamentEvent = false; targetGUID.Clear(); - - FadeTimer = 30000; - SummonSkeletonTimer = 20000; - BlackArrowTimer = 15000; - ShotTimer = 8000; - MultiShotTimer = 10000; + playerGUID.Clear(); } - uint32 LamentEventTimer; - bool LamentEvent; - ObjectGuid targetGUID; - - uint32 FadeTimer; - uint32 SummonSkeletonTimer; - uint32 BlackArrowTimer; - uint32 ShotTimer; - uint32 MultiShotTimer; - void Reset() override { Initialize(); + _events.Reset(); } - void EnterCombat(Unit* /*who*/) override { } + void EnterCombat(Unit* /*who*/) override + { + _events.ScheduleEvent(EVENT_FADE, 30000); + _events.ScheduleEvent(EVENT_SUMMON_SKELETON, 20000); + _events.ScheduleEvent(EVENT_BLACK_ARROW, 15000); + _events.ScheduleEvent(EVENT_SHOOT, 8000); + _events.ScheduleEvent(EVENT_MULTI_SHOT, 10000); + } + + void SetGUID(ObjectGuid guid, int32 type) override + { + if (type == GUID_EVENT_INVOKER) + { + Talk(EMOTE_LAMENT); + DoPlaySoundToSet(me, SOUND_CREDIT); + DoCast(me, SPELL_SYLVANAS_CAST, false); + playerGUID = guid; + LamentEvent = true; + + for (uint8 i = 0; i < 4; ++i) + me->SummonCreature(NPC_HIGHBORNE_LAMENTER, HighborneLoc[i][0], HighborneLoc[i][1], HIGHBORNE_LOC_Y, HighborneLoc[i][2], TEMPSUMMON_TIMED_DESPAWN, 160000); + + _events.ScheduleEvent(EVENT_LAMENT_OF_THE_HIGHBORN, 2000); + _events.ScheduleEvent(EVENT_SUNSORROW_WHISPER, 10000); + } + } void JustSummoned(Creature* summoned) override { - if (summoned->GetEntry() == ENTRY_HIGHBORNE_BUNNY) + if (summoned->GetEntry() == NPC_HIGHBORNE_BUNNY) { if (Creature* target = ObjectAccessor::GetCreature(*summoned, targetGUID)) { - target->MonsterMoveWithSpeed(target->GetPositionX(), target->GetPositionY(), me->GetPositionZ()+15.0f, 0); + target->GetMotionMaster()->MoveJump(target->GetPositionX(), target->GetPositionY(), me->GetPositionZ() + 15.0f, 0); target->SetPosition(target->GetPositionX(), target->GetPositionY(), me->GetPositionZ()+15.0f, 0.0f); summoned->CastSpell(target, SPELL_RIBBON_OF_SOULS, false); } @@ -152,75 +169,86 @@ public: void UpdateAI(uint32 diff) override { - if (LamentEvent) - { - if (LamentEventTimer <= diff) - { - DoSummon(ENTRY_HIGHBORNE_BUNNY, me, 10.0f, 3000, TEMPSUMMON_TIMED_DESPAWN); - - LamentEventTimer = 2000; - if (!me->HasAura(SPELL_SYLVANAS_CAST)) - { - Talk(SAY_LAMENT_END); - Talk(EMOTE_LAMENT_END); - LamentEvent = false; - } - } else LamentEventTimer -= diff; - } - - if (!UpdateVictim()) + if (!UpdateVictim() && !LamentEvent) return; - // Combat spells - - if (FadeTimer <= diff) - { - DoCast(me, SPELL_FADE); - // add a blink to simulate a stealthed movement and reappearing elsewhere - DoCast(me, SPELL_FADE_BLINK); - FadeTimer = 30000 + rand32() % 5000; - // if the victim is out of melee range she cast multi shot - if (Unit* victim = me->GetVictim()) - if (me->GetDistance(victim) > 10.0f) - DoCast(victim, SPELL_MULTI_SHOT); - } else FadeTimer -= diff; - - if (SummonSkeletonTimer <= diff) - { - DoCast(me, SPELL_SUMMON_SKELETON); - SummonSkeletonTimer = 20000 + rand32() % 10000; - } else SummonSkeletonTimer -= diff; - - if (BlackArrowTimer <= diff) - { - if (Unit* victim = me->GetVictim()) - { - DoCast(victim, SPELL_BLACK_ARROW); - BlackArrowTimer = 15000 + rand32() % 5000; - } - } else BlackArrowTimer -= diff; + _events.Update(diff); - if (ShotTimer <= diff) - { - if (Unit* victim = me->GetVictim()) - { - DoCast(victim, SPELL_SHOT); - ShotTimer = 8000 + rand32() % 2000; - } - } else ShotTimer -= diff; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - if (MultiShotTimer <= diff) + while (uint32 eventId = _events.ExecuteEvent()) { - if (Unit* victim = me->GetVictim()) + switch (eventId) { - DoCast(victim, SPELL_MULTI_SHOT); - MultiShotTimer = 10000 + rand32() % 3000; + case EVENT_FADE: + DoCast(me, SPELL_FADE); + // add a blink to simulate a stealthed movement and reappearing elsewhere + DoCast(me, SPELL_FADE_BLINK); + // if the victim is out of melee range she cast multi shot + if (Unit* victim = me->GetVictim()) + if (me->GetDistance(victim) > 10.0f) + DoCast(victim, SPELL_MULTI_SHOT); + _events.ScheduleEvent(EVENT_FADE, urand(30000, 35000)); + break; + case EVENT_SUMMON_SKELETON: + DoCast(me, SPELL_SUMMON_SKELETON); + _events.ScheduleEvent(EVENT_SUMMON_SKELETON, urand(20000, 30000)); + break; + case EVENT_BLACK_ARROW: + if (Unit* victim = me->GetVictim()) + DoCast(victim, SPELL_BLACK_ARROW); + _events.ScheduleEvent(EVENT_BLACK_ARROW, urand(15000, 20000)); + break; + case EVENT_SHOOT: + if (Unit* victim = me->GetVictim()) + DoCast(victim, SPELL_SHOT); + _events.ScheduleEvent(EVENT_SHOOT, urand(8000, 10000)); + break; + case EVENT_MULTI_SHOT: + if (Unit* victim = me->GetVictim()) + DoCast(victim, SPELL_MULTI_SHOT); + _events.ScheduleEvent(EVENT_MULTI_SHOT, urand(10000, 13000)); + break; + case EVENT_LAMENT_OF_THE_HIGHBORN: + if (!me->HasAura(SPELL_SYLVANAS_CAST)) + { + Talk(SAY_LAMENT_END); + Talk(EMOTE_LAMENT_END); + LamentEvent = false; + me->HandleEmoteCommand(EMOTE_ONESHOT_KNEEL); + Reset(); + } + else + { + DoSummon(NPC_HIGHBORNE_BUNNY, me, 10.0f, 3000, TEMPSUMMON_TIMED_DESPAWN); + _events.ScheduleEvent(EVENT_LAMENT_OF_THE_HIGHBORN, 2000); + } + break; + case EVENT_SUNSORROW_WHISPER: + if (Creature* ambassador = me->FindNearestCreature(NPC_AMBASSADOR_SUNSORROW, 20.0f)) + if (Player* player = ObjectAccessor::GetPlayer(*me, playerGUID)) + ambassador->AI()->Talk(SAY_SUNSORROW_WHISPER, player); + break; + default: + break; } - } else MultiShotTimer -= diff; + } DoMeleeAttackIfReady(); } + + private: + EventMap _events; + bool LamentEvent; + ObjectGuid targetGUID; + ObjectGuid playerGUID; }; + + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_lady_sylvanas_windrunnerAI(creature); + } }; /*###### diff --git a/src/server/scripts/Kalimdor/CMakeLists.txt b/src/server/scripts/Kalimdor/CMakeLists.txt index 9a68973b8d1..ce8d0cfbcfd 100644 --- a/src/server/scripts/Kalimdor/CMakeLists.txt +++ b/src/server/scripts/Kalimdor/CMakeLists.txt @@ -92,7 +92,6 @@ set(scripts_STAT_SRCS Kalimdor/RuinsOfAhnQiraj/boss_moam.cpp Kalimdor/RuinsOfAhnQiraj/ruins_of_ahnqiraj.h Kalimdor/RuinsOfAhnQiraj/boss_kurinnaxx.cpp - Kalimdor/zone_mulgore.cpp Kalimdor/zone_bloodmyst_isle.cpp Kalimdor/zone_thunder_bluff.cpp Kalimdor/zone_azshara.cpp diff --git a/src/server/scripts/Kalimdor/RazorfenKraul/razorfen_kraul.cpp b/src/server/scripts/Kalimdor/RazorfenKraul/razorfen_kraul.cpp index a7c17a9df3e..f4a7e1f4185 100644 --- a/src/server/scripts/Kalimdor/RazorfenKraul/razorfen_kraul.cpp +++ b/src/server/scripts/Kalimdor/RazorfenKraul/razorfen_kraul.cpp @@ -136,131 +136,7 @@ public: } }; -enum SnufflenoseGopher -{ - NPC_SNUFFLENOSE_GOPHER = 4781, - GO_BLUELEAF_TUBBER = 20920, - ACTION_FIND_NEW_TUBBER = 0, - POINT_TUBBER = 0 -}; - -struct npc_snufflenose_gopher : public CreatureScript -{ -public: - npc_snufflenose_gopher() : CreatureScript("npc_snufflenose_gopher") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_snufflenose_gopherAI(creature); - } - - struct npc_snufflenose_gopherAI : public PetAI - { - npc_snufflenose_gopherAI(Creature* creature) : PetAI(creature) - { - IsMovementActive = false; - } - - void Reset() override - { - IsMovementActive = false; - TargetTubberGUID.Clear(); - } - - void MovementInform(uint32 type, uint32 id) override - { - if (type == POINT_MOTION_TYPE && id == POINT_TUBBER) - { - if (GameObject* go = me->GetMap()->GetGameObject(TargetTubberGUID)) - { - go->SetRespawnTime(5 * MINUTE); - go->Refresh(); - go->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_INTERACT_COND); - } - - IsMovementActive = false; - } - else - PetAI::MovementInform(type, id); - } - - void DoFindNewTubber() - { - std::list<GameObject*> tubbersInRange; - GetGameObjectListWithEntryInGrid(tubbersInRange, me, GO_BLUELEAF_TUBBER, 40.0f); - - if (tubbersInRange.empty()) - return; - - tubbersInRange.remove_if([](GameObject* go) - { - return go->isSpawned() || !go->HasFlag(GAMEOBJECT_FLAGS, GO_FLAG_INTERACT_COND); - }); - - tubbersInRange.sort(Trinity::ObjectDistanceOrderPred(me)); - - GameObject* nearestTubber = tubbersInRange.front(); - if (!nearestTubber) - return; - - TargetTubberGUID = nearestTubber->GetGUID(); - - // XFurry was wrong... - me->GetMotionMaster()->MovePoint(POINT_TUBBER, nearestTubber->GetPositionX(), - nearestTubber->GetPositionY(), - nearestTubber->GetPositionZ()); - IsMovementActive = true; - } - - void UpdateAI(uint32 diff) override - { - if (!IsMovementActive) - PetAI::UpdateAI(diff); - } - - void DoAction(int32 action) override - { - if (action == ACTION_FIND_NEW_TUBBER) - DoFindNewTubber(); - } - - - bool IsMovementActive; - ObjectGuid TargetTubberGUID; - }; -}; - -class spell_snufflenose_command : public SpellScriptLoader -{ - public: - spell_snufflenose_command() : SpellScriptLoader("spell_snufflenose_command") { } - - class spell_snufflenose_commandSpellScript : public SpellScript - { - PrepareSpellScript(spell_snufflenose_commandSpellScript); - - void HandleEffect(SpellEffIndex /*effIndex*/) - { - if (Creature* target = GetHitCreature()) - if (target->GetEntry() == NPC_SNUFFLENOSE_GOPHER) - target->AI()->DoAction(ACTION_FIND_NEW_TUBBER); - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_snufflenose_commandSpellScript::HandleEffect, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_snufflenose_commandSpellScript(); - } -}; - void AddSC_razorfen_kraul() { - new npc_snufflenose_gopher(); new npc_willix(); - new spell_snufflenose_command(); } diff --git a/src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp b/src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp index 390fd3e529f..ca5e4697c35 100644 --- a/src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp +++ b/src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp @@ -29,6 +29,7 @@ EndContentData */ #include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "PassiveAI.h" #include "Player.h" /*###### @@ -48,14 +49,18 @@ class npc_webbed_creature : public CreatureScript public: npc_webbed_creature() : CreatureScript("npc_webbed_creature") { } - struct npc_webbed_creatureAI : public ScriptedAI + struct npc_webbed_creatureAI : public NullCreatureAI { - npc_webbed_creatureAI(Creature* creature) : ScriptedAI(creature) { } + npc_webbed_creatureAI(Creature* creature) : NullCreatureAI(creature) { } void Reset() override { } void EnterCombat(Unit* /*who*/) override { } + void AttackStart(Unit* /*who*/) override { } + + void MoveInLineOfSight(Unit* /*who*/) override { } + void JustDied(Unit* killer) override { uint32 spawnCreatureID = 0; diff --git a/src/server/scripts/Kalimdor/zone_felwood.cpp b/src/server/scripts/Kalimdor/zone_felwood.cpp index cdf04b1d4cc..91b9ce08010 100644 --- a/src/server/scripts/Kalimdor/zone_felwood.cpp +++ b/src/server/scripts/Kalimdor/zone_felwood.cpp @@ -100,7 +100,44 @@ public: } }; +/*###### +## at_ancient_leaf +######*/ + +enum AncientMisc +{ + QUEST_ANCIENT_LEAF = 7632, + NPC_VARTRUS = 14524, + NPC_STOMA = 14525, + NPC_HASTAT = 14526, + CREATURE_GROUP_ANCIENTS = 1 +}; + +class at_ancient_leaf : public AreaTriggerScript +{ + public: + at_ancient_leaf() : AreaTriggerScript("at_ancient_leaf") { } + + bool OnTrigger(Player* player, AreaTriggerEntry const* /*trigger*/) override + { + if (player->IsGameMaster() || !player->IsAlive()) + return false; + + // Handle Call Ancients event start - The area trigger summons 3 ancients + if ((player->GetQuestStatus(QUEST_ANCIENT_LEAF) == QUEST_STATUS_COMPLETE) || (player->GetQuestStatus(QUEST_ANCIENT_LEAF) == QUEST_STATUS_REWARDED)) + { + // If ancients are already spawned, skip the rest + if (GetClosestCreatureWithEntry(player, NPC_VARTRUS, 50.0f) || GetClosestCreatureWithEntry(player, NPC_STOMA, 50.0f) || GetClosestCreatureWithEntry(player, NPC_HASTAT, 50.0f)) + return true; + + player->GetMap()->SummonCreatureGroup(CREATURE_GROUP_ANCIENTS); + } + return false; + } +}; + void AddSC_felwood() { new npcs_riverbreeze_and_silversky(); + new at_ancient_leaf(); } diff --git a/src/server/scripts/Kalimdor/zone_mulgore.cpp b/src/server/scripts/Kalimdor/zone_mulgore.cpp deleted file mode 100644 index 0d0ef218885..00000000000 --- a/src/server/scripts/Kalimdor/zone_mulgore.cpp +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/> - * Copyright (C) 2006-2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/> - * - * 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 the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* ScriptData -SDName: Mulgore -SD%Complete: 100 -SDComment: Support for quest: 11129, 772 -SDCategory: Mulgore -EndScriptData */ - -/* ContentData -npc_kyle_frenzied -npc_plains_vision -EndContentData */ - -#include "ScriptMgr.h" -#include "ScriptedCreature.h" -#include "ScriptedGossip.h" -#include "Player.h" -#include "SpellInfo.h" - -/*##### -# npc_kyle_frenzied -######*/ - -enum KyleFrenzied -{ - EMOTE_SEE_LUNCH = 0, - EMOTE_EAT_LUNCH = 1, - EMOTE_DANCE = 2, - - SPELL_LUNCH = 42222, - NPC_KYLE_FRENZIED = 23616, - NPC_KYLE_FRIENDLY = 23622, - POINT_ID = 1 -}; - -class npc_kyle_frenzied : public CreatureScript -{ -public: - npc_kyle_frenzied() : CreatureScript("npc_kyle_frenzied") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_kyle_frenziedAI(creature); - } - - struct npc_kyle_frenziedAI : public ScriptedAI - { - npc_kyle_frenziedAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - } - - void Initialize() - { - EventActive = false; - IsMovingToLunch = false; - PlayerGUID.Clear(); - EventTimer = 5000; - EventPhase = 0; - } - - bool EventActive; - bool IsMovingToLunch; - ObjectGuid PlayerGUID; - uint32 EventTimer; - uint8 EventPhase; - - void Reset() override - { - Initialize(); - - if (me->GetEntry() == NPC_KYLE_FRIENDLY) - me->UpdateEntry(NPC_KYLE_FRENZIED); - } - - void SpellHit(Unit* Caster, SpellInfo const* Spell) override - { - if (!me->GetVictim() && !EventActive && Spell->Id == SPELL_LUNCH) - { - if (Caster->GetTypeId() == TYPEID_PLAYER) - PlayerGUID = Caster->GetGUID(); - - if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE) - { - me->GetMotionMaster()->MovementExpired(); - me->GetMotionMaster()->MoveIdle(); - me->StopMoving(); - } - - EventActive = true; - Talk(EMOTE_SEE_LUNCH); - me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_CREATURE_SPECIAL); - } - } - - void MovementInform(uint32 Type, uint32 PointId) override - { - if (Type != POINT_MOTION_TYPE || !EventActive) - return; - - if (PointId == POINT_ID) - IsMovingToLunch = false; - } - - void UpdateAI(uint32 diff) override - { - if (EventActive) - { - if (IsMovingToLunch) - return; - - if (EventTimer <= diff) - { - EventTimer = 5000; - ++EventPhase; - - switch (EventPhase) - { - case 1: - if (Unit* unit = ObjectAccessor::GetUnit(*me, PlayerGUID)) - { - if (GameObject* go = unit->GetGameObject(SPELL_LUNCH)) - { - IsMovingToLunch = true; - me->GetMotionMaster()->MovePoint(POINT_ID, go->GetPositionX(), go->GetPositionY(), go->GetPositionZ()); - } - } - break; - case 2: - Talk(EMOTE_EAT_LUNCH); - me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_USE_STANDING); - break; - case 3: - if (Player* unit = ObjectAccessor::GetPlayer(*me, PlayerGUID)) - unit->TalkedToCreature(me->GetEntry(), me->GetGUID()); - - me->UpdateEntry(NPC_KYLE_FRIENDLY); - break; - case 4: - EventTimer = 30000; - Talk(EMOTE_DANCE); - me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_DANCESPECIAL); - break; - case 5: - me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_NONE); - Reset(); - me->GetMotionMaster()->Clear(); - break; - } - } - else - EventTimer -= diff; - } - } - }; - -}; - -/*##### -# npc_plains_vision -######*/ - -Position const wpPlainVision[50] = -{ - {-2226.32f, -408.095f, -9.36235f, 0.0f}, - {-2203.04f, -437.212f, -5.72498f, 0.0f}, - {-2163.91f, -457.851f, -7.09049f, 0.0f}, - {-2123.87f, -448.137f, -9.29591f, 0.0f}, - {-2104.66f, -427.166f, -6.49513f, 0.0f}, - {-2101.48f, -422.826f, -5.3567f, 0.0f}, - {-2097.56f, -417.083f, -7.16716f, 0.0f}, - {-2084.87f, -398.626f, -9.88973f, 0.0f}, - {-2072.71f, -382.324f, -10.2488f, 0.0f}, - {-2054.05f, -356.728f, -6.22468f, 0.0f}, - {-2051.8f, -353.645f, -5.35791f, 0.0f}, - {-2049.08f, -349.912f, -6.15723f, 0.0f}, - {-2030.6f, -310.724f, -9.59302f, 0.0f}, - {-2002.15f, -249.308f, -10.8124f, 0.0f}, - {-1972.85f, -195.811f, -10.6316f, 0.0f}, - {-1940.93f, -147.652f, -11.7055f, 0.0f}, - {-1888.06f, -81.943f, -11.4404f, 0.0f}, - {-1837.05f, -34.0109f, -12.258f, 0.0f}, - {-1796.12f, -14.6462f, -10.3581f, 0.0f}, - {-1732.61f, -4.27746f, -10.0213f, 0.0f}, - {-1688.94f, -0.829945f, -11.7103f, 0.0f}, - {-1681.32f, 13.0313f, -9.48056f, 0.0f}, - {-1677.04f, 36.8349f, -7.10318f, 0.0f}, - {-1675.2f, 68.559f, -8.95384f, 0.0f}, - {-1676.57f, 89.023f, -9.65104f, 0.0f}, - {-1678.16f, 110.939f, -10.1782f, 0.0f}, - {-1677.86f, 128.681f, -5.73869f, 0.0f}, - {-1675.27f, 144.324f, -3.47916f, 0.0f}, - {-1671.7f, 163.169f, -1.23098f, 0.0f}, - {-1666.61f, 181.584f, 5.26145f, 0.0f}, - {-1661.51f, 196.154f, 8.95252f, 0.0f}, - {-1655.47f, 210.811f, 8.38727f, 0.0f}, - {-1647.07f, 226.947f, 5.27755f, 0.0f}, - {-1621.65f, 232.91f, 2.69579f, 0.0f}, - {-1600.23f, 237.641f, 2.98539f, 0.0f}, - {-1576.07f, 242.546f, 4.66541f, 0.0f}, - {-1554.57f, 248.494f, 6.60377f, 0.0f}, - {-1547.53f, 259.302f, 10.6741f, 0.0f}, - {-1541.7f, 269.847f, 16.4418f, 0.0f}, - {-1539.83f, 278.989f, 21.0597f, 0.0f}, - {-1540.16f, 290.219f, 27.8247f, 0.0f}, - {-1538.99f, 298.983f, 34.0032f, 0.0f}, - {-1540.38f, 307.337f, 41.3557f, 0.0f}, - {-1536.61f, 314.884f, 48.0179f, 0.0f}, - {-1532.42f, 323.277f, 55.6667f, 0.0f}, - {-1528.77f, 329.774f, 61.1525f, 0.0f}, - {-1525.65f, 333.18f, 63.2161f, 0.0f}, - {-1517.01f, 350.713f, 62.4286f, 0.0f}, - {-1511.39f, 362.537f, 62.4539f, 0.0f}, - {-1508.68f, 366.822f, 62.733f, 0.0f} -}; - -class npc_plains_vision : public CreatureScript -{ -public: - npc_plains_vision() : CreatureScript("npc_plains_vision") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_plains_visionAI(creature); - } - - struct npc_plains_visionAI : public ScriptedAI - { - npc_plains_visionAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - } - - void Initialize() - { - WayPointId = 0; - newWaypoint = true; - amountWP = 49; - } - - bool newWaypoint; - uint8 WayPointId; - uint8 amountWP; - - void Reset() override - { - Initialize(); - } - - void EnterCombat(Unit* /*who*/) override { } - - void MovementInform(uint32 type, uint32 id) override - { - if (type != POINT_MOTION_TYPE) - return; - - if (id < amountWP) - { - ++WayPointId; - newWaypoint = true; - } - else - { - me->setDeathState(JUST_DIED); - me->RemoveCorpse(); - } - } - - void UpdateAI(uint32 /*diff*/) override - { - if (newWaypoint) - { - me->GetMotionMaster()->MovePoint(WayPointId, wpPlainVision[WayPointId]); - newWaypoint = false; - } - } - }; - -}; - -/*##### -# -######*/ - -void AddSC_mulgore() -{ - new npc_kyle_frenzied(); - new npc_plains_vision(); -} diff --git a/src/server/scripts/Kalimdor/zone_the_barrens.cpp b/src/server/scripts/Kalimdor/zone_the_barrens.cpp index c167bc700f7..eab104d8df2 100644 --- a/src/server/scripts/Kalimdor/zone_the_barrens.cpp +++ b/src/server/scripts/Kalimdor/zone_the_barrens.cpp @@ -659,6 +659,7 @@ public: if (quest->GetQuestId() == QUEST_ESCAPE) { creature->setFaction(FACTION_RATCHET); + creature->AI()->Talk(SAY_START); if (npc_escortAI* pEscortAI = CAST_AI(npc_wizzlecrank_shredder::npc_wizzlecrank_shredderAI, creature->AI())) pEscortAI->Start(true, false, player->GetGUID()); } diff --git a/src/server/scripts/Northrend/CMakeLists.txt b/src/server/scripts/Northrend/CMakeLists.txt index e04dcf0ec96..66d9021223f 100644 --- a/src/server/scripts/Northrend/CMakeLists.txt +++ b/src/server/scripts/Northrend/CMakeLists.txt @@ -78,8 +78,7 @@ set(scripts_STAT_SRCS Northrend/Nexus/Oculus/boss_urom.cpp Northrend/Nexus/Oculus/oculus.cpp Northrend/Nexus/Oculus/instance_oculus.cpp - Northrend/Nexus/Nexus/boss_commander_kolurg.cpp - Northrend/Nexus/Nexus/boss_commander_stoutbeard.cpp + Northrend/Nexus/Nexus/boss_nexus_commanders.cpp Northrend/Nexus/Nexus/boss_ormorok.cpp Northrend/Nexus/Nexus/boss_magus_telestra.cpp Northrend/Nexus/Nexus/instance_nexus.cpp diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/instance_trial_of_the_crusader.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/instance_trial_of_the_crusader.cpp index 4f883fd7e90..23bfd773a9b 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/instance_trial_of_the_crusader.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/instance_trial_of_the_crusader.cpp @@ -255,12 +255,6 @@ class instance_trial_of_the_crusader : public InstanceMapScript if (GetBossState(BOSS_VALKIRIES) == SPECIAL) state = DONE; break; - case DONE: - if (instance->GetPlayers().getFirst()->GetSource()->GetTeam() == ALLIANCE) - EventStage = 4020; - else - EventStage = 4030; - break; default: break; } @@ -336,7 +330,7 @@ class instance_trial_of_the_crusader : public InstanceMapScript if (type < MAX_ENCOUNTERS) { - TC_LOG_INFO("scripts", "[ToCr] BossState(type %u) %u = state %u;", type, GetBossState(type), state); + TC_LOG_DEBUG("scripts", "[ToCr] BossState(type %u) %u = state %u;", type, GetBossState(type), state); if (state == FAIL) { if (instance->IsHeroic()) diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.cpp index 16526b694e9..974bd4672f1 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.cpp @@ -133,7 +133,7 @@ class npc_announcer_toc10 : public CreatureScript char const* _message = "We are ready!"; - if (player->IsInCombat() || instance->IsEncounterInProgress() || instance->GetData(TYPE_EVENT)) + if (player->IsInCombat() || instance->IsEncounterInProgress()) return true; uint8 i = 0; @@ -199,17 +199,11 @@ class npc_announcer_toc10 : public CreatureScript } else if (instance->GetBossState(BOSS_LICH_KING) != DONE) { - if (GameObject* floor = ObjectAccessor::GetGameObject(*player, instance->GetGuidData(GO_ARGENT_COLISEUM_FLOOR))) - floor->SetDestructibleState(GO_DESTRUCTIBLE_DAMAGED); - - creature->CastSpell(creature, SPELL_CORPSE_TELEPORT, false); - creature->CastSpell(creature, SPELL_DESTROY_FLOOR_KNOCKUP, false); - - if (!ObjectAccessor::GetCreature(*creature, instance->GetGuidData(NPC_ANUBARAK))) - creature->SummonCreature(NPC_ANUBARAK, AnubarakLoc[0], TEMPSUMMON_CORPSE_TIMED_DESPAWN, DESPAWN_TIME); - - if (creature->IsVisible()) - creature->SetVisible(false); + if (creature->GetMap()->GetPlayers().getFirst()->GetSource()->GetTeam() == ALLIANCE) + instance->SetData(TYPE_EVENT, 4020); + else + instance->SetData(TYPE_EVENT, 4030); + instance->SetBossState(BOSS_LICH_KING, NOT_STARTED); } creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); return true; diff --git a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_bronjahm.cpp b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_bronjahm.cpp index 97c4372f0e6..0b829c2c6dd 100644 --- a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_bronjahm.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_bronjahm.cpp @@ -27,7 +27,7 @@ enum Yells SAY_SLAY = 1, SAY_DEATH = 2, SAY_SOUL_STORM = 3, - SAY_CORRUPT_SOUL = 4, + SAY_CORRUPT_SOUL = 4 }; enum Spells @@ -39,9 +39,9 @@ enum Spells SPELL_TELEPORT = 68988, SPELL_FEAR = 68950, SPELL_SOULSTORM = 68872, - SPELL_SOULSTORM_CHANNEL = 69008, // pre-fight - SPELL_SOULSTORM_VISUAL = 68870, // pre-cast soulstorm - SPELL_PURPLE_BANISH_VISUAL = 68862 // Used by Soul Fragment (Aura) + SPELL_SOULSTORM_CHANNEL = 69008, // Pre-fight + SPELL_SOULSTORM_VISUAL = 68870, // Pre-cast Soulstorm + SPELL_PURPLE_BANISH_VISUAL = 68862 // Used by Soul Fragment (Aura) }; enum Events @@ -50,7 +50,7 @@ enum Events EVENT_SHADOW_BOLT = 2, EVENT_CORRUPT_SOUL = 3, EVENT_SOULSTORM = 4, - EVENT_FEAR = 5, + EVENT_FEAR = 5 }; enum CombatPhases @@ -59,6 +59,11 @@ enum CombatPhases PHASE_2 = 2 }; +enum Misc +{ + DATA_SOUL_POWER = 1 +}; + class boss_bronjahm : public CreatureScript { public: @@ -73,33 +78,30 @@ class boss_bronjahm : public CreatureScript void Reset() override { - events.Reset(); + _Reset(); events.SetPhase(PHASE_1); events.ScheduleEvent(EVENT_SHADOW_BOLT, 2000); events.ScheduleEvent(EVENT_MAGIC_BANE, urand(8000, 20000)); events.ScheduleEvent(EVENT_CORRUPT_SOUL, urand(25000, 35000), 0, PHASE_1); - - instance->SetBossState(DATA_BRONJAHM, NOT_STARTED); } - void JustReachedHome() override - { - DoCast(me, SPELL_SOULSTORM_CHANNEL, true); - } + void JustReachedHome() override + { + _JustReachedHome(); + DoCast(me, SPELL_SOULSTORM_CHANNEL, true); + } void EnterCombat(Unit* /*who*/) override { + _EnterCombat(); Talk(SAY_AGGRO); me->RemoveAurasDueToSpell(SPELL_SOULSTORM_CHANNEL); - - instance->SetBossState(DATA_BRONJAHM, IN_PROGRESS); } void JustDied(Unit* /*killer*/) override { + _JustDied(); Talk(SAY_DEATH); - - instance->SetBossState(DATA_BRONJAHM, DONE); } void KilledUnit(Unit* who) override @@ -128,6 +130,23 @@ class boss_bronjahm : public CreatureScript summon->CastSpell(summon, SPELL_PURPLE_BANISH_VISUAL, true); } + uint32 GetData(uint32 type) const override + { + if (type == DATA_SOUL_POWER) + { + uint32 count = 0; + for (ObjectGuid const& guid : summons) + { + if (Creature* summon = ObjectAccessor::GetCreature(*me, guid)) + if (summon->GetEntry() == NPC_CORRUPTED_SOUL_FRAGMENT && summon->IsAlive()) + ++count; + } + return count; + } + + return 0; + } + void UpdateAI(uint32 diff) override { if (!UpdateVictim()) @@ -143,13 +162,21 @@ class boss_bronjahm : public CreatureScript switch (eventId) { case EVENT_MAGIC_BANE: - DoCastVictim(SPELL_MAGIC_S_BANE); + DoCastAOE(SPELL_MAGIC_S_BANE); events.ScheduleEvent(EVENT_MAGIC_BANE, urand(8000, 20000)); break; case EVENT_SHADOW_BOLT: - if (!me->IsWithinMeleeRange(me->GetVictim())) + if (events.IsInPhase(PHASE_2)) + { DoCastVictim(SPELL_SHADOW_BOLT); - events.ScheduleEvent(EVENT_SHADOW_BOLT, 2000); + events.ScheduleEvent(EVENT_SHADOW_BOLT, urand(1, 2) * IN_MILLISECONDS); + } + else + { + if (!me->IsWithinMeleeRange(me->GetVictim())) + DoCastVictim(SPELL_SHADOW_BOLT); + events.ScheduleEvent(EVENT_SHADOW_BOLT, 2 * IN_MILLISECONDS); + } break; case EVENT_CORRUPT_SOUL: if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true)) @@ -165,8 +192,7 @@ class boss_bronjahm : public CreatureScript me->CastSpell(me, SPELL_SOULSTORM, false); break; case EVENT_FEAR: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true)) - me->CastCustomSpell(SPELL_FEAR, SPELLVALUE_MAX_TARGETS, 1, target, false); + me->CastCustomSpell(SPELL_FEAR, SPELLVALUE_MAX_TARGETS, 1, nullptr, false); events.ScheduleEvent(EVENT_FEAR, urand(8000, 12000), 0, PHASE_2); break; default: @@ -174,7 +200,8 @@ class boss_bronjahm : public CreatureScript } } - DoMeleeAttackIfReady(); + if (!events.IsInPhase(PHASE_2)) + DoMeleeAttackIfReady(); } }; @@ -201,17 +228,11 @@ class npc_corrupted_soul_fragment : public CreatureScript if (type != CHASE_MOTION_TYPE) return; - if (TempSummon* summ = me->ToTempSummon()) - { - ObjectGuid BronjahmGUID(instance->GetGuidData(DATA_BRONJAHM)); - if (BronjahmGUID.GetCounter() != id) - return; - - if (Creature* bronjahm = ObjectAccessor::GetCreature(*me, BronjahmGUID)) - me->CastSpell(bronjahm, SPELL_CONSUME_SOUL, true); + if (instance->GetGuidData(DATA_BRONJAHM).GetCounter() != id) + return; - summ->UnSummon(); - } + me->CastSpell((Unit*)nullptr, SPELL_CONSUME_SOUL, true); + me->DespawnOrUnsummon(); } private: @@ -220,7 +241,7 @@ class npc_corrupted_soul_fragment : public CreatureScript CreatureAI* GetAI(Creature* creature) const override { - return GetInstanceAI<npc_corrupted_soul_fragmentAI>(creature); + return GetInstanceAI<npc_corrupted_soul_fragmentAI>(creature, FoSScriptName); } }; @@ -238,12 +259,10 @@ class spell_bronjahm_magic_bane : public SpellScriptLoader if (GetHitUnit()->getPowerType() != POWER_MANA) return; - const int32 maxDamage = GetCaster()->GetMap()->GetSpawnMode() == 1 ? 15000 : 10000; - int32 newDamage = GetHitDamage(); - newDamage += GetHitUnit()->GetMaxPower(POWER_MANA)/2; - newDamage = std::min<int32>(maxDamage, newDamage); + int32 const maxDamage = GetCaster()->GetMap()->IsHeroic() ? 15000 : 10000; + int32 newDamage = GetHitDamage() + (GetHitUnit()->GetMaxPower(POWER_MANA) / 2); - SetHitDamage(newDamage); + SetHitDamage(std::min<int32>(maxDamage, newDamage)); } void Register() override @@ -261,7 +280,7 @@ class spell_bronjahm_magic_bane : public SpellScriptLoader class spell_bronjahm_consume_soul : public SpellScriptLoader { public: - spell_bronjahm_consume_soul() : SpellScriptLoader("spell_bronjahm_consume_soul") { } + spell_bronjahm_consume_soul() : SpellScriptLoader("spell_bronjahm_consume_soul") { } class spell_bronjahm_consume_soul_SpellScript : public SpellScript { @@ -285,38 +304,22 @@ class spell_bronjahm_consume_soul : public SpellScriptLoader } }; -class spell_bronjahm_soulstorm_channel : public SpellScriptLoader +static uint32 const SoulstormVisualSpells[] = { - public: - spell_bronjahm_soulstorm_channel() : SpellScriptLoader("spell_bronjahm_soulstorm_channel") { } - - class spell_bronjahm_soulstorm_channel_AuraScript : public AuraScript - { - PrepareAuraScript(spell_bronjahm_soulstorm_channel_AuraScript); - - void HandlePeriodicTick(AuraEffect const* /*aurEff*/) - { - PreventDefaultAction(); - for (uint32 i = 68904; i <= 68907; ++i) - GetTarget()->CastSpell(GetTarget(), i, true); - } - - void Register() override - { - OnEffectPeriodic += AuraEffectPeriodicFn(spell_bronjahm_soulstorm_channel_AuraScript::HandlePeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); - } - }; - - AuraScript* GetAuraScript() const override - { - return new spell_bronjahm_soulstorm_channel_AuraScript(); - } + 68904, + 68886, + 68905, + 68896, + 68906, + 68897, + 68907, + 68898 }; class spell_bronjahm_soulstorm_visual : public SpellScriptLoader { public: - spell_bronjahm_soulstorm_visual() : SpellScriptLoader("spell_bronjahm_soulstorm_visual") { } + spell_bronjahm_soulstorm_visual(char const* scriptName) : SpellScriptLoader(scriptName) { } class spell_bronjahm_soulstorm_visual_AuraScript : public AuraScript { @@ -325,11 +328,7 @@ class spell_bronjahm_soulstorm_visual : public SpellScriptLoader void HandlePeriodicTick(AuraEffect const* aurEff) { PreventDefaultAction(); - if (aurEff->GetTickNumber()%5) - return; - GetTarget()->CastSpell(GetTarget(), 68886, true); - for (uint32 i = 68896; i <= 68898; ++i) - GetTarget()->CastSpell(GetTarget(), i, true); + GetTarget()->CastSpell(GetTarget(), SoulstormVisualSpells[aurEff->GetTickNumber() % 8], true); } void Register() override @@ -344,21 +343,6 @@ class spell_bronjahm_soulstorm_visual : public SpellScriptLoader } }; -class DistanceCheck -{ - public: - explicit DistanceCheck(Unit* _caster) : caster(_caster) { } - - bool operator() (WorldObject* unit) const - { - if (caster->GetExactDist2d(unit) <= 10.0f) - return true; - return false; - } - - Unit* caster; -}; - class spell_bronjahm_soulstorm_targeting : public SpellScriptLoader { public: @@ -368,25 +352,19 @@ class spell_bronjahm_soulstorm_targeting : public SpellScriptLoader { PrepareSpellScript(spell_bronjahm_soulstorm_targeting_SpellScript); - void FilterTargetsInitial(std::list<WorldObject*>& targets) - { - targets.remove_if(DistanceCheck(GetCaster())); - sharedTargets = targets; - } - - // use the same target for first and second effect - void FilterTargetsSubsequent(std::list<WorldObject*>& targets) + void FilterTargets(std::list<WorldObject*>& targets) { - targets = sharedTargets; + Unit* caster = GetCaster(); + targets.remove_if([caster](WorldObject* target) + { + return caster->GetExactDist2d(target) <= 10.0f; + }); } void Register() override { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_bronjahm_soulstorm_targeting_SpellScript::FilterTargetsInitial, EFFECT_1, TARGET_UNIT_DEST_AREA_ENEMY); - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_bronjahm_soulstorm_targeting_SpellScript::FilterTargetsSubsequent, EFFECT_2, TARGET_UNIT_DEST_AREA_ENEMY); + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_bronjahm_soulstorm_targeting_SpellScript::FilterTargets, EFFECT_ALL, TARGET_UNIT_DEST_AREA_ENEMY); } - - std::list<WorldObject*> sharedTargets; }; SpellScript* GetSpellScript() const override @@ -395,13 +373,25 @@ class spell_bronjahm_soulstorm_targeting : public SpellScriptLoader } }; +class achievement_bronjahm_soul_power : public AchievementCriteriaScript +{ + public: + achievement_bronjahm_soul_power() : AchievementCriteriaScript("achievement_bronjahm_soul_power") { } + + bool OnCheck(Player* /*source*/, Unit* target) override + { + return target && target->GetAI()->GetData(DATA_SOUL_POWER) >= 4; + } +}; + void AddSC_boss_bronjahm() { new boss_bronjahm(); new npc_corrupted_soul_fragment(); new spell_bronjahm_magic_bane(); new spell_bronjahm_consume_soul(); - new spell_bronjahm_soulstorm_channel(); - new spell_bronjahm_soulstorm_visual(); + new spell_bronjahm_soulstorm_visual("spell_bronjahm_soulstorm_channel"); + new spell_bronjahm_soulstorm_visual("spell_bronjahm_soulstorm_visual"); new spell_bronjahm_soulstorm_targeting(); + new achievement_bronjahm_soul_power(); } diff --git a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp index 2f1d4943019..d5f00757039 100644 --- a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp @@ -128,7 +128,7 @@ class boss_devourer_of_souls : public CreatureScript struct boss_devourer_of_soulsAI : public BossAI { - boss_devourer_of_soulsAI(Creature* creature) : BossAI(creature, DATA_DEVOURER_EVENT) + boss_devourer_of_soulsAI(Creature* creature) : BossAI(creature, DATA_DEVOURER_OF_SOULS) { Initialize(); beamAngle = 0.f; @@ -143,20 +143,17 @@ class boss_devourer_of_souls : public CreatureScript void Reset() override { + _Reset(); me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); me->SetDisplayId(DISPLAY_ANGER); me->SetReactState(REACT_AGGRESSIVE); - events.Reset(); - summons.DespawnAll(); - Initialize(); - - instance->SetData(DATA_DEVOURER_EVENT, NOT_STARTED); } void EnterCombat(Unit* /*who*/) override { + _EnterCombat(); Talk(SAY_FACE_AGGRO); if (!me->FindNearestCreature(NPC_CRUCIBLE_OF_SOULS, 60)) // Prevent double spawn @@ -166,8 +163,6 @@ class boss_devourer_of_souls : public CreatureScript events.ScheduleEvent(EVENT_WELL_OF_SOULS, 30000); events.ScheduleEvent(EVENT_UNLEASHED_SOULS, 20000); events.ScheduleEvent(EVENT_WAILING_SOULS, urand(60000, 70000)); - - instance->SetData(DATA_DEVOURER_EVENT, IN_PROGRESS); } void KilledUnit(Unit* victim) override @@ -175,7 +170,7 @@ class boss_devourer_of_souls : public CreatureScript if (victim->GetTypeId() != TYPEID_PLAYER) return; - int32 textId = 0; + uint8 textId = 0; switch (me->GetDisplayId()) { case DISPLAY_ANGER: @@ -197,14 +192,12 @@ class boss_devourer_of_souls : public CreatureScript void JustDied(Unit* /*killer*/) override { - summons.DespawnAll(); + _JustDied(); Position spawnPoint = {5618.139f, 2451.873f, 705.854f, 0}; Talk(SAY_FACE_DEATH); - instance->SetData(DATA_DEVOURER_EVENT, DONE); - int32 entryIndex; if (instance->GetData(DATA_TEAM_IN_INSTANCE) == ALLIANCE) entryIndex = 0; @@ -402,11 +395,6 @@ class spell_devourer_of_souls_mirrored_soul_proc : public SpellScriptLoader return true; } - bool Load() override - { - return true; - } - bool CheckProc(ProcEventInfo& /*eventInfo*/) { return GetCaster() && GetCaster()->IsAlive(); diff --git a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/forge_of_souls.h b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/forge_of_souls.h index 054e5d5a7bd..ddbc117e7fd 100644 --- a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/forge_of_souls.h +++ b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/forge_of_souls.h @@ -15,38 +15,44 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef DEF_FORGE_OF_SOULS_H -#define DEF_FORGE_OF_SOULS_H +#ifndef FORGE_OF_SOULS_H_ +#define FORGE_OF_SOULS_H_ #define FoSScriptName "instance_forge_of_souls" #define DataHeader "FOS" +uint32 const EncounterCount = 2; + enum Data { - DATA_BRONJAHM = 0, - DATA_DEVOURER = 1, - DATA_DEVOURER_EVENT = 2, - DATA_TEAM_IN_INSTANCE = 3, + // Encounter states and GUIDs + DATA_BRONJAHM = 0, + DATA_DEVOURER_OF_SOULS = 1, + + // Additional Data + DATA_TEAM_IN_INSTANCE = 2 }; enum Creatures { - CREATURE_BRONJAHM = 36497, - CREATURE_DEVOURER = 36502, - - NPC_SYLVANAS_PART1 = 37596, - NPC_SYLVANAS_PART2 = 38161, - NPC_JAINA_PART1 = 37597, - NPC_JAINA_PART2 = 38160, - NPC_KALIRA = 37583, - NPC_ELANDRA = 37774, - NPC_LORALEN = 37779, - NPC_KORELN = 37582, - NPC_CHAMPION_1_HORDE = 37584, - NPC_CHAMPION_2_HORDE = 37587, - NPC_CHAMPION_3_HORDE = 37588, - NPC_CHAMPION_1_ALLIANCE = 37496, - NPC_CHAMPION_2_ALLIANCE = 37497, - NPC_CRUCIBLE_OF_SOULS = 37094, + NPC_BRONJAHM = 36497, + NPC_DEVOURER = 36502, + NPC_CORRUPTED_SOUL_FRAGMENT = 36535, + + NPC_SYLVANAS_PART1 = 37596, + NPC_SYLVANAS_PART2 = 38161, + NPC_JAINA_PART1 = 37597, + NPC_JAINA_PART2 = 38160, + NPC_KALIRA = 37583, + NPC_ELANDRA = 37774, + NPC_LORALEN = 37779, + NPC_KORELN = 37582, + NPC_CHAMPION_1_HORDE = 37584, + NPC_CHAMPION_2_HORDE = 37587, + NPC_CHAMPION_3_HORDE = 37588, + NPC_CHAMPION_1_ALLIANCE = 37496, + NPC_CHAMPION_2_ALLIANCE = 37497, + NPC_CRUCIBLE_OF_SOULS = 37094 }; -#endif + +#endif // FORGE_OF_SOULS_H_ diff --git a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/instance_forge_of_souls.cpp b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/instance_forge_of_souls.cpp index 8b586ea7fc2..db7628ebada 100644 --- a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/instance_forge_of_souls.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/instance_forge_of_souls.cpp @@ -20,13 +20,6 @@ #include "forge_of_souls.h" #include "Player.h" -#define MAX_ENCOUNTER 2 - -/* Forge of Souls encounters: -0- Bronjahm, The Godfather of Souls -1- The Devourer of Souls -*/ - class instance_forge_of_souls : public InstanceMapScript { public: @@ -37,7 +30,7 @@ class instance_forge_of_souls : public InstanceMapScript instance_forge_of_souls_InstanceScript(Map* map) : InstanceScript(map) { SetHeaders(DataHeader); - SetBossNumber(MAX_ENCOUNTER); + SetBossNumber(EncounterCount); teamInInstance = 0; } @@ -60,10 +53,10 @@ class instance_forge_of_souls : public InstanceMapScript switch (creature->GetEntry()) { - case CREATURE_BRONJAHM: + case NPC_BRONJAHM: bronjahm = creature->GetGUID(); break; - case CREATURE_DEVOURER: + case NPC_DEVOURER: devourerOfSouls = creature->GetGUID(); break; case NPC_SYLVANAS_PART1: @@ -100,7 +93,7 @@ class instance_forge_of_souls : public InstanceMapScript { case DATA_BRONJAHM: return bronjahm; - case DATA_DEVOURER: + case DATA_DEVOURER_OF_SOULS: return devourerOfSouls; default: break; diff --git a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp index 7b80db7dd39..927ebfbc55c 100644 --- a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp @@ -21,6 +21,7 @@ #include "SpellScript.h" #include "Transport.h" #include "Player.h" +#include "MoveSplineInit.h" #include "halls_of_reflection.h" enum Text @@ -342,6 +343,20 @@ class npc_jaina_or_sylvanas_intro_hor : public CreatureScript public: npc_jaina_or_sylvanas_intro_hor() : CreatureScript("npc_jaina_or_sylvanas_intro_hor") { } + bool OnGossipHello(Player* player, Creature* creature) override + { + // override default gossip + if (InstanceScript* instance = creature->GetInstanceScript()) + if (instance->GetData(DATA_QUEL_DELAR_EVENT) == IN_PROGRESS || instance->GetData(DATA_QUEL_DELAR_EVENT) == SPECIAL) + { + player->PlayerTalkClass->ClearMenus(); + return true; + } + + // load default gossip + return false; + } + struct npc_jaina_or_sylvanas_intro_horAI : public ScriptedAI { npc_jaina_or_sylvanas_intro_horAI(Creature* creature) : ScriptedAI(creature) @@ -1998,6 +2013,13 @@ class at_hor_intro_start : public AreaTriggerScript if (_instance->GetData(DATA_INTRO_EVENT) == NOT_STARTED) _instance->SetData(DATA_INTRO_EVENT, IN_PROGRESS); + if (player->HasAura(SPELL_QUEL_DELAR_COMPULSION) && (player->GetQuestStatus(QUEST_HALLS_OF_REFLECTION_ALLIANCE) == QUEST_STATUS_INCOMPLETE || + player->GetQuestStatus(QUEST_HALLS_OF_REFLECTION_HORDE) == QUEST_STATUS_INCOMPLETE) && _instance->GetData(DATA_QUEL_DELAR_EVENT) == NOT_STARTED) + { + _instance->SetData(DATA_QUEL_DELAR_EVENT, IN_PROGRESS); + _instance->SetGuidData(DATA_QUEL_DELAR_INVOKER, player->GetGUID()); + } + return true; } }; @@ -2330,6 +2352,374 @@ class npc_lumbering_abomination : public CreatureScript } }; +enum QuelDelarUther +{ + ACTION_UTHER_START_SCREAM = 1, + ACTION_UTHER_OUTRO = 2, + + EVENT_UTHER_1 = 1, + EVENT_UTHER_2 = 2, + EVENT_UTHER_3 = 3, + EVENT_UTHER_4 = 4, + EVENT_UTHER_5 = 5, + EVENT_UTHER_6 = 6, + EVENT_UTHER_7 = 7, + EVENT_UTHER_8 = 8, + EVENT_UTHER_9 = 9, + EVENT_UTHER_10 = 10, + EVENT_UTHER_11 = 11, + EVENT_UTHER_FACING = 12, + EVENT_UTHER_KNEEL = 13, + + SAY_UTHER_QUEL_DELAR_1 = 16, + SAY_UTHER_QUEL_DELAR_2 = 17, + SAY_UTHER_QUEL_DELAR_3 = 18, + SAY_UTHER_QUEL_DELAR_4 = 19, + SAY_UTHER_QUEL_DELAR_5 = 20, + SAY_UTHER_QUEL_DELAR_6 = 21, + + SPELL_ESSENCE_OF_CAPTURED_1 = 73036 +}; + +enum QuelDelarSword +{ + SPELL_WHIRLWIND_VISUAL = 70300, + SPELL_HEROIC_STRIKE = 29426, + SPELL_WHIRLWIND = 67716, + SPELL_BLADESTORM = 67541, + + NPC_QUEL_DELAR = 37158, + POINT_TAKE_OFF = 1, + + EVENT_QUEL_DELAR_INIT = 1, + EVENT_QUEL_DELAR_FLIGHT_INIT = 2, + EVENT_QUEL_DELAR_FLIGHT = 3, + EVENT_QUEL_DELAR_LAND = 4, + EVENT_QUEL_DELAR_FIGHT = 5, + EVENT_QUEL_DELAR_BLADESTORM = 6, + EVENT_QUEL_DELAR_HEROIC_STRIKE = 7, + EVENT_QUEL_DELAR_WHIRLWIND = 8, + + SAY_QUEL_DELAR_SWORD = 0 +}; + +enum QuelDelarMisc +{ + SAY_FROSTMOURNE_BUNNY = 0, + SPELL_QUEL_DELAR_WILL = 70698 +}; + +Position const QuelDelarCenterPos = { 5309.259f, 2006.390f, 718.046f, 0.0f }; +Position const QuelDelarSummonPos = { 5298.473f, 1994.852f, 709.424f, 3.979351f }; +Position const QuelDelarMovement[] = +{ + { 5292.870f, 1998.950f, 718.046f, 0.0f }, + { 5295.819f, 1991.912f, 707.707f, 0.0f }, + { 5295.301f, 1989.782f, 708.696f, 0.0f } +}; + +Position const UtherQuelDelarMovement[] = +{ + { 5336.830f, 1981.700f, 709.319f, 0.0f }, + { 5314.350f, 1993.440f, 707.726f, 0.0f } +}; + +class npc_uther_quel_delar : public CreatureScript +{ + public: + npc_uther_quel_delar() : CreatureScript("npc_uther_quel_delar") { } + + struct npc_uther_quel_delarAI : public ScriptedAI + { + npc_uther_quel_delarAI(Creature* creature) : ScriptedAI(creature) + { + _instance = me->GetInstanceScript(); + } + + void Reset() override + { + // Prevent to break Uther in intro event during instance encounter + if (_instance->GetData(DATA_QUEL_DELAR_EVENT) != IN_PROGRESS && _instance->GetData(DATA_QUEL_DELAR_EVENT) != SPECIAL) + return; + + _events.Reset(); + _events.ScheduleEvent(EVENT_UTHER_1, 1); + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage) override + { + if (damage >= me->GetHealth()) + damage = me->GetHealth() - 1; + } + + void DoAction(int32 action) override + { + switch (action) + { + case ACTION_UTHER_START_SCREAM: + _instance->SetData(DATA_QUEL_DELAR_EVENT, SPECIAL); + _events.ScheduleEvent(EVENT_UTHER_2, 0); + break; + case ACTION_UTHER_OUTRO: + _events.ScheduleEvent(EVENT_UTHER_6, 0); + break; + default: + break; + } + } + + void MovementInform(uint32 /*type*/, uint32 pointId) override + { + switch (pointId) + { + case 1: + _events.ScheduleEvent(EVENT_UTHER_FACING, 1000); + break; + default: + break; + } + } + + void UpdateAI(uint32 diff) override + { + // Prevent to break Uther in intro event during instance encounter + if (_instance->GetData(DATA_QUEL_DELAR_EVENT) != IN_PROGRESS && _instance->GetData(DATA_QUEL_DELAR_EVENT) != SPECIAL) + return; + + _events.Update(diff); + + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_UTHER_1: + Talk(SAY_UTHER_QUEL_DELAR_1); + break; + case EVENT_UTHER_2: + if (Creature* bunny = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_FROSTMOURNE_ALTAR_BUNNY))) + if (Unit* target = ObjectAccessor::GetPlayer(*me, _instance->GetGuidData(DATA_QUEL_DELAR_INVOKER))) + bunny->CastSpell(target, SPELL_QUEL_DELAR_WILL, true); + _events.ScheduleEvent(EVENT_UTHER_3, 2000); + break; + case EVENT_UTHER_3: + me->SummonCreature(NPC_QUEL_DELAR, QuelDelarSummonPos); + _events.ScheduleEvent(EVENT_UTHER_4, 2000); + break; + case EVENT_UTHER_4: + Talk(SAY_UTHER_QUEL_DELAR_2); + _events.ScheduleEvent(EVENT_UTHER_5, 8000); + break; + case EVENT_UTHER_5: + me->GetMotionMaster()->MovePoint(1, UtherQuelDelarMovement[0]); + break; + case EVENT_UTHER_6: + me->SetWalk(true); + me->GetMotionMaster()->MovePoint(0, UtherQuelDelarMovement[1]); + _events.ScheduleEvent(EVENT_UTHER_7, 5000); + break; + case EVENT_UTHER_7: + Talk(SAY_UTHER_QUEL_DELAR_3); + _events.ScheduleEvent(EVENT_UTHER_8, 12000); + break; + case EVENT_UTHER_8: + Talk(SAY_UTHER_QUEL_DELAR_4); + _events.ScheduleEvent(EVENT_UTHER_9, 7000); + break; + case EVENT_UTHER_9: + Talk(SAY_UTHER_QUEL_DELAR_5); + _events.ScheduleEvent(EVENT_UTHER_10, 10000); + break; + case EVENT_UTHER_10: + Talk(SAY_UTHER_QUEL_DELAR_6); + _events.ScheduleEvent(EVENT_UTHER_11, 5000); + break; + case EVENT_UTHER_11: + DoCast(me, SPELL_ESSENCE_OF_CAPTURED_1, true); + me->DespawnOrUnsummon(3000); + _instance->SetData(DATA_QUEL_DELAR_EVENT, DONE); + break; + case EVENT_UTHER_FACING: + if (Creature* bunny = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_FROSTMOURNE_ALTAR_BUNNY))) + me->SetFacingToObject(bunny); + _events.ScheduleEvent(EVENT_UTHER_KNEEL, 1000); + break; + case EVENT_UTHER_KNEEL: + me->HandleEmoteCommand(EMOTE_STATE_KNEEL); + break; + default: + break; + } + } + } + + private: + EventMap _events; + InstanceScript* _instance; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetHallsOfReflectionAI<npc_uther_quel_delarAI>(creature); + } +}; + +class npc_quel_delar_sword : public CreatureScript +{ + public: + npc_quel_delar_sword() : CreatureScript("npc_quel_delar_sword") { } + + struct npc_quel_delar_swordAI : public ScriptedAI + { + npc_quel_delar_swordAI(Creature* creature) : ScriptedAI(creature) + { + _instance = me->GetInstanceScript(); + me->SetDisplayId(me->GetCreatureTemplate()->Modelid2); + _intro = true; + } + + void Reset() override + { + _events.Reset(); + me->SetSpeed(MOVE_FLIGHT, 4.5f, true); + DoCast(SPELL_WHIRLWIND_VISUAL); + if (_intro) + _events.ScheduleEvent(EVENT_QUEL_DELAR_INIT, 0); + else + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC); + } + + void EnterCombat(Unit* /*victim*/) override + { + _events.ScheduleEvent(EVENT_QUEL_DELAR_HEROIC_STRIKE, 4000); + _events.ScheduleEvent(EVENT_QUEL_DELAR_BLADESTORM, 6000); + _events.ScheduleEvent(EVENT_QUEL_DELAR_WHIRLWIND, 6000); + } + + void JustDied(Unit* /*killer*/) override + { + if (Creature* uther = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_UTHER_QUEL_DELAR))) + uther->AI()->DoAction(ACTION_UTHER_OUTRO); + } + + void MovementInform(uint32 type, uint32 pointId) override + { + if (type != EFFECT_MOTION_TYPE) + return; + + switch (pointId) + { + case POINT_TAKE_OFF: + _events.ScheduleEvent(EVENT_QUEL_DELAR_FLIGHT, 0); + break; + default: + break; + } + } + + void UpdateAI(uint32 diff) override + { + _events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + if (!UpdateVictim()) + { + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_QUEL_DELAR_INIT: + if (Creature* bunny = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_FROSTMOURNE_ALTAR_BUNNY))) + bunny->AI()->Talk(SAY_FROSTMOURNE_BUNNY); + _intro = false; + _events.ScheduleEvent(EVENT_QUEL_DELAR_FLIGHT_INIT, 2500); + break; + case EVENT_QUEL_DELAR_FLIGHT_INIT: + me->GetMotionMaster()->MoveTakeoff(POINT_TAKE_OFF, QuelDelarMovement[0]); + break; + case EVENT_QUEL_DELAR_FLIGHT: + { + me->GetMotionMaster()->MoveCirclePath(QuelDelarCenterPos.GetPositionX(), QuelDelarCenterPos.GetPositionY(), 718.046f, 18.0f, true, 16); + _events.ScheduleEvent(EVENT_QUEL_DELAR_LAND, 15000); + break; + } + case EVENT_QUEL_DELAR_LAND: + me->StopMoving(); + me->GetMotionMaster()->Clear(); + me->GetMotionMaster()->MoveLand(0, QuelDelarMovement[1]); + _events.ScheduleEvent(EVENT_QUEL_DELAR_FIGHT, 6000); + break; + case EVENT_QUEL_DELAR_FIGHT: + Talk(SAY_QUEL_DELAR_SWORD); + me->GetMotionMaster()->MovePoint(0, QuelDelarMovement[2]); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC); + break; + default: + break; + } + } + } + else + { + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_QUEL_DELAR_BLADESTORM: + DoCast(me, SPELL_BLADESTORM); + _events.ScheduleEvent(EVENT_QUEL_DELAR_BLADESTORM, 10000); + break; + case EVENT_QUEL_DELAR_HEROIC_STRIKE: + DoCastVictim(SPELL_HEROIC_STRIKE); + _events.ScheduleEvent(EVENT_QUEL_DELAR_HEROIC_STRIKE, 6000); + break; + case EVENT_QUEL_DELAR_WHIRLWIND: + DoCastAOE(SPELL_WHIRLWIND); + _events.ScheduleEvent(EVENT_QUEL_DELAR_WHIRLWIND, 1000); + break; + default: + break; + } + } + + DoMeleeAttackIfReady(); + } + } + + private: + EventMap _events; + InstanceScript* _instance; + bool _intro; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetHallsOfReflectionAI<npc_quel_delar_swordAI>(creature); + } +}; + +// 5660 +class at_hor_uther_quel_delar_start : public AreaTriggerScript +{ + public: + at_hor_uther_quel_delar_start() : AreaTriggerScript("at_hor_uther_quel_delar_start") { } + + bool OnTrigger(Player* player, AreaTriggerEntry const* /*trigger*/) override + { + if (player->IsGameMaster()) + return true; + + InstanceScript* _instance = player->GetInstanceScript(); + + if (_instance->GetData(DATA_QUEL_DELAR_EVENT) == IN_PROGRESS) + if (Creature* uther = ObjectAccessor::GetCreature(*player, _instance->GetGuidData(DATA_UTHER_QUEL_DELAR))) + uther->AI()->DoAction(ACTION_UTHER_START_SCREAM); + + return true; + } +}; + // 72900 - Start Halls of Reflection Quest AE class spell_hor_start_halls_of_reflection_quest_ae : public SpellScriptLoader { @@ -2447,6 +2837,7 @@ void AddSC_halls_of_reflection() new at_hor_waves_restarter(); new at_hor_impenetrable_door(); new at_hor_shadow_throne(); + new at_hor_uther_quel_delar_start(); new npc_jaina_or_sylvanas_intro_hor(); new npc_jaina_or_sylvanas_escape_hor(); new npc_the_lich_king_escape_hor(); @@ -2461,6 +2852,8 @@ void AddSC_halls_of_reflection() new npc_raging_ghoul(); new npc_risen_witch_doctor(); new npc_lumbering_abomination(); + new npc_uther_quel_delar(); + new npc_quel_delar_sword(); new spell_hor_start_halls_of_reflection_quest_ae(); new spell_hor_evasion(); new spell_hor_gunship_cannon_fire(); diff --git a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.h b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.h index 9594617fa9a..d2f9ab5d262 100644 --- a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.h +++ b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.h @@ -46,7 +46,13 @@ enum DataTypes DATA_ESCAPE_LEADER = 10, DATA_ICEWALL = 11, DATA_ICEWALL_TARGET = 12, - DATA_GUNSHIP = 13 + DATA_GUNSHIP = 13, + + // Quest stuff + DATA_QUEL_DELAR_EVENT = 14, + DATA_FROSTMOURNE_ALTAR_BUNNY = 15, + DATA_UTHER_QUEL_DELAR = 16, + DATA_QUEL_DELAR_INVOKER = 17 }; enum CreatureIds @@ -131,7 +137,8 @@ enum InstanceEvents EVENT_NEXT_WAVE = 2, EVENT_DO_WIPE = 3, EVENT_ADD_WAVE = 4, - EVENT_SPAWN_ESCAPE_EVENT = 5 + EVENT_SPAWN_ESCAPE_EVENT = 5, + EVENT_QUEL_DELAR_SUMMON_UTHER = 6 }; enum InstanceEventIds @@ -160,7 +167,17 @@ enum InstanceSpells // Gunship SPELL_GUNSHIP_CANNON_FIRE = 70017, SPELL_GUNSHIP_CANNON_FIRE_MISSILE_ALLIANCE = 70021, - SPELL_GUNSHIP_CANNON_FIRE_MISSILE_HORDE = 70246 + SPELL_GUNSHIP_CANNON_FIRE_MISSILE_HORDE = 70246, + + // Halls of Reflection quest + SPELL_QUEL_DELAR_COMPULSION = 70013, + SPELL_ESSENCE_OF_CAPTURED = 70720 +}; + +enum InstanceQuests +{ + QUEST_HALLS_OF_REFLECTION_ALLIANCE = 24480, + QUEST_HALLS_OF_REFLECTION_HORDE = 24561 }; enum InstanceWorldStates diff --git a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp index ea2a1539f1c..660a639487f 100644 --- a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp @@ -73,6 +73,8 @@ Position const SpawnPos[] = { 5299.250f, 2035.998f, 707.7781f, 5.026548f } }; +Position const UtherQuelDalarPos = { 5302.001f, 1988.698f, 707.7781f, 3.700098f }; + class instance_halls_of_reflection : public InstanceMapScript { public: @@ -89,6 +91,7 @@ class instance_halls_of_reflection : public InstanceMapScript _waveCount = 0; _introState = NOT_STARTED; _frostswornGeneralState = NOT_STARTED; + _quelDelarState = NOT_STARTED; events.Reset(); } @@ -159,6 +162,9 @@ class instance_halls_of_reflection : public InstanceMapScript case NPC_ICE_WALL_TARGET: IcewallTargetGUID = creature->GetGUID(); break; + case NPC_UTHER: + UtherGUID = creature->GetGUID(); + break; default: break; } @@ -439,6 +445,18 @@ class instance_halls_of_reflection : public InstanceMapScript HandleGameObject(ShadowThroneDoorGUID, true); _frostswornGeneralState = data; break; + case DATA_QUEL_DELAR_EVENT: + if (data == IN_PROGRESS) + { + if (_quelDelarState == NOT_STARTED) + { + if (Creature* bunny = instance->GetCreature(FrostmourneAltarBunnyGUID)) + bunny->CastSpell((Unit*)NULL, SPELL_ESSENCE_OF_CAPTURED); + events.ScheduleEvent(EVENT_QUEL_DELAR_SUMMON_UTHER, 2000); + } + } + _quelDelarState = data; + break; default: break; } @@ -446,6 +464,18 @@ class instance_halls_of_reflection : public InstanceMapScript SaveToDB(); } + void SetGuidData(uint32 type, ObjectGuid data) override + { + switch (type) + { + case DATA_QUEL_DELAR_INVOKER: + QuelDelarInvokerGUID = data; + break; + default: + break; + } + } + // wave scheduling, checked when wave npcs die void OnUnitDeath(Unit* unit) override { @@ -491,6 +521,9 @@ class instance_halls_of_reflection : public InstanceMapScript case EVENT_SPAWN_ESCAPE_EVENT: SpawnEscapeEvent(); break; + case EVENT_QUEL_DELAR_SUMMON_UTHER: + instance->SummonCreature(NPC_UTHER, UtherQuelDalarPos); + break; } } @@ -649,6 +682,8 @@ class instance_halls_of_reflection : public InstanceMapScript return _introState; case DATA_FROSTSWORN_GENERAL: return _frostswornGeneralState; + case DATA_QUEL_DELAR_EVENT: + return _quelDelarState; default: break; } @@ -682,6 +717,12 @@ class instance_halls_of_reflection : public InstanceMapScript return IcewallGUID; case DATA_ICEWALL_TARGET: return IcewallTargetGUID; + case DATA_FROSTMOURNE_ALTAR_BUNNY: + return FrostmourneAltarBunnyGUID; + case DATA_UTHER_QUEL_DELAR: + return UtherGUID; + case DATA_QUEL_DELAR_INVOKER: + return QuelDelarInvokerGUID; default: break; } @@ -691,7 +732,7 @@ class instance_halls_of_reflection : public InstanceMapScript void WriteSaveDataMore(std::ostringstream& data) override { - data << _introState << ' ' << _frostswornGeneralState; + data << _introState << ' ' << _frostswornGeneralState << ' ' << _quelDelarState; } void ReadSaveDataMore(std::istringstream& data) override @@ -708,6 +749,12 @@ class instance_halls_of_reflection : public InstanceMapScript SetData(DATA_FROSTSWORN_GENERAL, DONE); else SetData(DATA_FROSTSWORN_GENERAL, NOT_STARTED); + + data >> temp; + if (temp == DONE) + SetData(DATA_QUEL_DELAR_EVENT, DONE); + else + SetData(DATA_QUEL_DELAR_EVENT, NOT_STARTED); } private: @@ -731,6 +778,7 @@ class instance_halls_of_reflection : public InstanceMapScript uint32 _waveCount; uint32 _introState; uint32 _frostswornGeneralState; + uint32 _quelDelarState; EventMap events; GuidSet waveGuidList[8]; @@ -740,6 +788,8 @@ class instance_halls_of_reflection : public InstanceMapScript ObjectGuid CaptainGUID; ObjectGuid IcewallGUID; ObjectGuid IcewallTargetGUID; + ObjectGuid QuelDelarInvokerGUID; + ObjectGuid UtherGUID; GuidSet GunshipCannonGUIDs; GuidSet GunshipStairGUIDs; diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp index 4c6fd0f2fcc..63082808e03 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp @@ -1259,7 +1259,7 @@ class spell_putricide_mutated_plague : public SpellScriptLoader return; uint32 triggerSpell = GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell; - SpellInfo const* spell = sSpellMgr->GetSpellInfo(triggerSpell); + SpellInfo const* spell = sSpellMgr->EnsureSpellInfo(triggerSpell); spell = sSpellMgr->GetSpellForDifficultyFromSpell(spell, caster); int32 damage = spell->Effects[EFFECT_0].CalcValue(caster); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_rotface.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_rotface.cpp index 4d7b9ec42b4..d56a3f80f75 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_rotface.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_rotface.cpp @@ -18,7 +18,7 @@ #include "ObjectMgr.h" #include "ScriptMgr.h" #include "ScriptedCreature.h" -#include "SpellAuras.h" +#include "SpellAuraEffects.h" #include "GridNotifiers.h" #include "icecrown_citadel.h" @@ -213,7 +213,7 @@ class boss_rotface : public CreatureScript } break; case EVENT_MUTATED_INFECTION: - me->CastCustomSpell(SPELL_MUTATED_INFECTION, SPELLVALUE_MAX_TARGETS, 1, NULL, false); + DoCastAOE(SPELL_MUTATED_INFECTION); events.ScheduleEvent(EVENT_MUTATED_INFECTION, infectionCooldown); break; case EVENT_VILE_GAS: @@ -509,13 +509,6 @@ class spell_rotface_mutated_infection : public SpellScriptLoader { PrepareSpellScript(spell_rotface_mutated_infection_SpellScript); - public: - spell_rotface_mutated_infection_SpellScript() - { - _target = nullptr; - } - - private: void FilterTargets(std::list<WorldObject*>& targets) { // remove targets with this aura already @@ -527,14 +520,6 @@ class spell_rotface_mutated_infection : public SpellScriptLoader WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets); targets.clear(); targets.push_back(target); - _target = target; - } - - void ReplaceTargets(std::list<WorldObject*>& targets) - { - targets.clear(); - if (_target) - targets.push_back(_target); } void NotifyTargets() @@ -546,19 +531,43 @@ class spell_rotface_mutated_infection : public SpellScriptLoader void Register() override { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_rotface_mutated_infection_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_rotface_mutated_infection_SpellScript::ReplaceTargets, EFFECT_1, TARGET_UNIT_SRC_AREA_ENEMY); - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_rotface_mutated_infection_SpellScript::ReplaceTargets, EFFECT_2, TARGET_UNIT_SRC_AREA_ENEMY); + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_rotface_mutated_infection_SpellScript::FilterTargets, EFFECT_ALL, TARGET_UNIT_SRC_AREA_ENEMY); AfterHit += SpellHitFn(spell_rotface_mutated_infection_SpellScript::NotifyTargets); } + }; + + class spell_rotface_mutated_infection_AuraScript : public AuraScript + { + PrepareAuraScript(spell_rotface_mutated_infection_AuraScript); - WorldObject* _target; + bool Validate(SpellInfo const* spellInfo) override + { + if (!sSpellMgr->GetSpellInfo(uint32(spellInfo->Effects[EFFECT_2].CalcValue()))) + return false; + return true; + } + + void HandleEffectRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) + { + Unit* target = GetTarget(); + target->CastSpell(target, uint32(GetSpellInfo()->Effects[EFFECT_2].CalcValue()), true, nullptr, aurEff, GetCasterGUID()); + } + + void Register() override + { + AfterEffectRemove += AuraEffectRemoveFn(spell_rotface_mutated_infection_AuraScript::HandleEffectRemove, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL); + } }; SpellScript* GetSpellScript() const override { return new spell_rotface_mutated_infection_SpellScript(); } + + AuraScript* GetAuraScript() const override + { + return new spell_rotface_mutated_infection_AuraScript(); + } }; class spell_rotface_little_ooze_combine : public SpellScriptLoader diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp index ae0e00cb849..07816ebbe39 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp @@ -2417,6 +2417,7 @@ class spell_the_lich_king_defile : public SpellScriptLoader void CorrectRange(std::list<WorldObject*>& targets) { targets.remove_if(ExactDistanceCheck(GetCaster(), 10.0f * GetCaster()->GetObjectScale())); + targets.remove_if(Trinity::UnitAuraCheck(true, SPELL_HARVEST_SOUL_VALKYR)); } void ChangeDamageAndGrow() diff --git a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp index 43069f10464..6c3b2187325 100644 --- a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp +++ b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp @@ -520,11 +520,7 @@ public: _despawned = false; break; case ACTION_CYCLIC_MOVEMENT: - Movement::MoveSplineInit init(me); - FillCirclePath(MalygosPositions[3], 120.0f, 283.2763f, init.Path(), true); - init.SetFly(); - init.SetCyclic(); - init.Launch(); + me->GetMotionMaster()->MoveCirclePath(MalygosPositions[3].GetPositionX(), MalygosPositions[3].GetPositionY(), 283.2763f, 120.0f, true, 16); break; } } @@ -1020,22 +1016,6 @@ public: } private: - // Used to generate perfect cyclic movements (Enter Circle). - void FillCirclePath(Position const& centerPos, float radius, float z, Movement::PointsArray& path, bool clockwise) - { - float step = clockwise ? float(-M_PI) / 8.0f : float(M_PI) / 8.0f; - float angle = centerPos.GetAngle(me->GetPositionX(), me->GetPositionY()); - - for (uint8 i = 0; i < 16; angle += step, ++i) - { - G3D::Vector3 point; - point.x = centerPos.GetPositionX() + radius * cosf(angle); - point.y = centerPos.GetPositionY() + radius * sinf(angle); - point.z = z; // Don't use any height getters unless all bugs are fixed. - path.push_back(point); - } - } - uint8 _phase; // Counter for phases used with a getter. uint8 _summonDeaths; // Keeps count of arcane trash. uint8 _preparingPulsesChecker; // In retail they use 2 preparing pulses with 7 sec CD, after they pass 2 seconds. @@ -1326,11 +1306,7 @@ public: { if (action < ACTION_DELAYED_DESPAWN) { - Movement::MoveSplineInit init(me); - FillCirclePath(MalygosPositions[3], 35.0f, 282.3402f, init.Path(), true); - init.SetFly(); - init.SetCyclic(); - init.Launch(); + me->GetMotionMaster()->MoveCirclePath(MalygosPositions[3].GetPositionX(), MalygosPositions[3].GetPositionY(), 282.3402f, 35.0f, true, 16); } else { @@ -1339,21 +1315,6 @@ public: } private: - void FillCirclePath(Position const& centerPos, float radius, float z, Movement::PointsArray& path, bool clockwise) - { - float step = clockwise ? float(-M_PI) / 9.0f : float(M_PI) / 9.0f; - float angle = centerPos.GetAngle(me->GetPositionX(), me->GetPositionY()); - - for (uint8 i = 0; i < 18; angle += step, ++i) - { - G3D::Vector3 point; - point.x = centerPos.GetPositionX() + radius * cosf(angle); - point.y = centerPos.GetPositionY() + radius * sinf(angle); - point.z = z; // Don't use any height getters unless all bugs are fixed. - path.push_back(point); - } - } - InstanceScript* _instance; }; diff --git a/src/server/scripts/Northrend/Nexus/Nexus/boss_commander_kolurg.cpp b/src/server/scripts/Northrend/Nexus/Nexus/boss_commander_kolurg.cpp deleted file mode 100644 index fc56f541a4b..00000000000 --- a/src/server/scripts/Northrend/Nexus/Nexus/boss_commander_kolurg.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2008-2015 TrinityCore <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 the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* Script Data Start -SDName: Boss Commander Kolurg -SDAuthor: LordVanMartin -SD%Complete: -SDComment: Only Alliance Heroic -SDCategory: -Script Data End */ - -#include "ScriptMgr.h" -#include "ScriptedCreature.h" - -enum Spells -{ - SPELL_BATTLE_SHOUT = 31403, - SPELL_CHARGE = 60067, - SPELL_FRIGHTENING_SHOUT = 19134, - SPELL_WHIRLWIND_1 = 38619, - SPELL_WHIRLWIND_2 = 38618 - -}; - -//not used -//Yell -#define SAY_AGGRO -1576024 -#define SAY_KILL -1576025 -#define SAY_DEATH -1576026 - -class boss_commander_kolurg : public CreatureScript -{ -public: - boss_commander_kolurg() : CreatureScript("boss_commander_kolurg") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return new boss_commander_kolurgAI(creature); - } - - struct boss_commander_kolurgAI : public ScriptedAI - { - boss_commander_kolurgAI(Creature* creature) : ScriptedAI(creature) { } - - void Reset() override { } - void EnterCombat(Unit* /*who*/) override { } - void AttackStart(Unit* /*who*/) override { } - void MoveInLineOfSight(Unit* /*who*/) override { } - - void UpdateAI(uint32 /*diff*/) override - { - //Return since we have no target - if (!UpdateVictim()) - return; - - DoMeleeAttackIfReady(); - } - void JustDied(Unit* /*killer*/) override { } - }; - -}; - -void AddSC_boss_commander_kolurg() -{ - new boss_commander_kolurg(); -} diff --git a/src/server/scripts/Northrend/Nexus/Nexus/boss_commander_stoutbeard.cpp b/src/server/scripts/Northrend/Nexus/Nexus/boss_commander_stoutbeard.cpp deleted file mode 100644 index ff08038d898..00000000000 --- a/src/server/scripts/Northrend/Nexus/Nexus/boss_commander_stoutbeard.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2008-2015 TrinityCore <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 the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* Script Data Start -SDName: Boss Commander Stoutbeard -SDAuthor: LordVanMartin -SD%Complete: -SDComment: Only Horde Heroic -SDCategory: -Script Data End */ - -#include "ScriptMgr.h" -#include "ScriptedCreature.h" - -enum CommanderStoutbeard -{ - SPELL_BATTLE_SHOUT = 31403, - SPELL_CHARGE = 60067, - SPELL_FRIGHTENING_SHOUT = 19134, - SPELL_WHIRLWIND_1 = 38619, - SPELL_WHIRLWIND_2 = 38618 -}; - - -class boss_commander_stoutbeard : public CreatureScript -{ -public: - boss_commander_stoutbeard() : CreatureScript("boss_commander_stoutbeard") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return new boss_commander_stoutbeardAI(creature); - } - - struct boss_commander_stoutbeardAI : public ScriptedAI - { - boss_commander_stoutbeardAI(Creature* creature) : ScriptedAI(creature) { } - - void Reset() override { } - void AttackStart(Unit* /*who*/) override { } - void MoveInLineOfSight(Unit* /*who*/) override { } - - void UpdateAI(uint32 /*diff*/) override - { - //Return since we have no target - if (!UpdateVictim()) - return; - - DoMeleeAttackIfReady(); - } - }; - -}; - -void AddSC_boss_commander_stoutbeard() -{ - new boss_commander_stoutbeard(); -} diff --git a/src/server/scripts/Northrend/Nexus/Nexus/boss_nexus_commanders.cpp b/src/server/scripts/Northrend/Nexus/Nexus/boss_nexus_commanders.cpp new file mode 100644 index 00000000000..d40f9fecc6a --- /dev/null +++ b/src/server/scripts/Northrend/Nexus/Nexus/boss_nexus_commanders.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2008-2015 TrinityCore <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 the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "ScriptMgr.h" +#include "ScriptedCreature.h" +#include "nexus.h" + +enum Spells +{ + SPELL_BATTLE_SHOUT = 31403, + SPELL_CHARGE = 60067, + SPELL_FRIGHTENING_SHOUT = 19134, + SPELL_WHIRLWIND = 38618, + SPELL_FROZEN_PRISON = 47543 +}; + +enum Yells +{ + SAY_AGGRO = 0, + SAY_KILL = 1, + SAY_DEATH = 2 +}; + +enum Events +{ + EVENT_CHARGE_COMMANDER = 1, + EVENT_WHIRLWIND, + EVENT_FRIGHTENING_SHOUT +}; + +class boss_nexus_commanders : public CreatureScript +{ + public: + boss_nexus_commanders() : CreatureScript("boss_nexus_commanders") { } + + struct boss_nexus_commandersAI : public BossAI + { + boss_nexus_commandersAI(Creature* creature) : BossAI(creature, DATA_COMMANDER) { } + + void EnterCombat(Unit* /*who*/) override + { + _EnterCombat(); + Talk(SAY_AGGRO); + me->RemoveAurasDueToSpell(SPELL_FROZEN_PRISON); + DoCast(me, SPELL_BATTLE_SHOUT); + + events.ScheduleEvent(EVENT_CHARGE_COMMANDER, urand(3000, 4000)); + events.ScheduleEvent(EVENT_WHIRLWIND, urand(6000, 8000)); + events.ScheduleEvent(EVENT_FRIGHTENING_SHOUT, urand(13000, 15000)); + } + + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) + { + case EVENT_CHARGE_COMMANDER: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true)) + DoCast(target, SPELL_CHARGE); + events.ScheduleEvent(EVENT_CHARGE_COMMANDER, urand(11000, 15000)); + break; + case EVENT_WHIRLWIND: + DoCast(me, SPELL_WHIRLWIND); + events.ScheduleEvent(EVENT_WHIRLWIND, urand(19500, 25000)); + break; + case EVENT_FRIGHTENING_SHOUT: + DoCastAOE(SPELL_FRIGHTENING_SHOUT); + events.ScheduleEvent(EVENT_FRIGHTENING_SHOUT, urand(45000, 55000)); + break; + default: + break; + } + } + + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + Talk(SAY_DEATH); + } + + void KilledUnit(Unit* who) override + { + if (who->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_KILL); + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<boss_nexus_commandersAI>(creature); + } +}; + +void AddSC_boss_nexus_commanders() +{ + new boss_nexus_commanders(); +} diff --git a/src/server/scripts/Northrend/Nexus/Nexus/nexus.h b/src/server/scripts/Northrend/Nexus/Nexus/nexus.h index 90866ea83f8..c48c7323301 100644 --- a/src/server/scripts/Northrend/Nexus/Nexus/nexus.h +++ b/src/server/scripts/Northrend/Nexus/Nexus/nexus.h @@ -21,32 +21,33 @@ #define NexusScriptName "instance_nexus" #define DataHeader "NEX" -uint32 const EncounterCount = 4; +uint32 const EncounterCount = 5; enum DataTypes { - DATA_MAGUS_TELESTRA = 0, - DATA_ANOMALUS = 1, - DATA_ORMOROK = 2, - DATA_KERISTRASZA = 3, + DATA_COMMANDER = 0, + DATA_MAGUS_TELESTRA = 1, + DATA_ANOMALUS = 2, + DATA_ORMOROK = 3, + DATA_KERISTRASZA = 4, - ANOMALUS_CONTAINMET_SPHERE = 4, - ORMOROKS_CONTAINMET_SPHERE = 5, - TELESTRAS_CONTAINMET_SPHERE = 6 + ANOMALUS_CONTAINMET_SPHERE = 5, + ORMOROKS_CONTAINMET_SPHERE = 6, + TELESTRAS_CONTAINMET_SPHERE = 7 }; enum CreatureIds { NPC_ANOMALUS = 26763, NPC_KERISTRASZA = 26723, - + // Alliance NPC_ALLIANCE_BERSERKER = 26800, NPC_ALLIANCE_RANGER = 26802, NPC_ALLIANCE_CLERIC = 26805, NPC_ALLIANCE_COMMANDER = 27949, NPC_COMMANDER_STOUTBEARD = 26796, - + // Horde NPC_HORDE_BERSERKER = 26799, NPC_HORDE_RANGER = 26801, @@ -59,7 +60,7 @@ enum GameObjectIds { GO_ANOMALUS_CONTAINMET_SPHERE = 188527, GO_ORMOROKS_CONTAINMET_SPHERE = 188528, - GO_TELESTRAS_CONTAINMET_SPHERE = 188526 + GO_TELESTRAS_CONTAINMET_SPHERE = 188526 }; #endif diff --git a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_volkhan.cpp b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_volkhan.cpp index 7bc8a5300f4..536cae08483 100644 --- a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_volkhan.cpp +++ b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_volkhan.cpp @@ -29,7 +29,7 @@ EndScriptData */ #include "Player.h" #include "SpellInfo.h" -enum Enums +enum Texts { SAY_AGGRO = 0, SAY_FORGE = 1, @@ -38,29 +38,50 @@ enum Enums SAY_DEATH = 4, EMOTE_TO_ANVIL = 5, EMOTE_SHATTER = 6, +}; +enum Spells +{ SPELL_HEAT = 52387, SPELL_SHATTERING_STOMP = 52237, - SPELL_TEMPER = 52238, SPELL_TEMPER_DUMMY = 52654, - SPELL_SUMMON_MOLTEN_GOLEM = 52405, + SPELL_FORGE_VISUAL = 52654, // Molten Golem SPELL_BLAST_WAVE = 23113, SPELL_IMMOLATION_STRIKE = 52433, SPELL_SHATTER = 52429, +}; +enum Events +{ + EVENT_PAUSE = 1, + EVENT_SHATTERING_STOMP = 2, + EVENT_SHATTER = 3, + EVENT_FORGE_CAST = 4, + + // Molten Golem + EVENT_BLAST = 5, + EVENT_IMMOLATION = 6 +}; + +enum Npcs +{ NPC_VOLKHAN_ANVIL = 28823, NPC_MOLTEN_GOLEM = 28695, NPC_BRITTLE_GOLEM = 28681, - MAX_GOLEM = 2, - DATA_SHATTER_RESISTANT = 2042 }; +enum Phases +{ + PHASE_INTRO = 1, + PHASE_NORMAL +}; + /*###### ## Boss Volkhan ######*/ @@ -68,68 +89,44 @@ class boss_volkhan : public CreatureScript { public: boss_volkhan() : CreatureScript("boss_volkhan") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<boss_volkhanAI>(creature); - } - - struct boss_volkhanAI : public ScriptedAI + + struct boss_volkhanAI : public BossAI { - boss_volkhanAI(Creature* creature) : ScriptedAI(creature) + boss_volkhanAI(Creature* creature) : BossAI(creature, DATA_VOLKHAN) { Initialize(); - instance = creature->GetInstanceScript(); } void Initialize() { - m_bIsStriking = false; - m_bHasTemper = false; + m_bIsStriking = false; + m_bHasTemper = false; m_bCanShatterGolem = false; - - m_uiPause_Timer = 3500; - m_uiShatteringStomp_Timer = 0; - m_uiShatter_Timer = 5000; - m_uiDelay_Timer = 1000; - m_uiSummonPhase = 0; - GolemsShattered = 0; + m_uiDelay_Timer = 1000; + m_uiSummonPhase = 0; + GolemsShattered = 0; m_uiHealthAmountModifier = 1; } - InstanceScript* instance; - - GuidList m_lGolemGUIDList; - - bool m_bHasTemper; - bool m_bIsStriking; - bool m_bCanShatterGolem; - - uint8 GolemsShattered; - uint32 m_uiPause_Timer; - uint32 m_uiShatteringStomp_Timer; - uint32 m_uiShatter_Timer; - uint32 m_uiDelay_Timer; - uint32 m_uiSummonPhase; - - uint32 m_uiHealthAmountModifier; - void Reset() override { Initialize(); - + _Reset(); DespawnGolem(); m_lGolemGUIDList.clear(); - - instance->SetBossState(DATA_VOLKHAN, NOT_STARTED); + events.SetPhase(PHASE_INTRO); + events.ScheduleEvent(EVENT_FORGE_CAST, 2 * IN_MILLISECONDS, 0, PHASE_INTRO); } void EnterCombat(Unit* /*who*/) override { Talk(SAY_AGGRO); - - instance->SetBossState(DATA_VOLKHAN, IN_PROGRESS); + events.SetPhase(PHASE_NORMAL); + events.ScheduleEvent(EVENT_PAUSE, 3.5 * IN_MILLISECONDS, 0, PHASE_NORMAL); + events.ScheduleEvent(EVENT_SHATTERING_STOMP, 0 * IN_MILLISECONDS, 0, PHASE_NORMAL); + events.ScheduleEvent(EVENT_SHATTER, 5 * IN_MILLISECONDS, 0, PHASE_NORMAL); + _EnterCombat(); } void AttackStart(Unit* who) override @@ -150,7 +147,7 @@ public: Talk(SAY_DEATH); DespawnGolem(); - instance->SetBossState(DATA_VOLKHAN, DONE); + _JustDied(); } void KilledUnit(Unit* who) override @@ -224,59 +221,61 @@ public: return 0; } - void UpdateAI(uint32 uiDiff) override + void UpdateAI(uint32 diff) override { - if (!UpdateVictim()) + // Return since we have no target and are in CombatPhase + if (events.IsInPhase(PHASE_NORMAL) && !UpdateVictim()) return; - if (m_bIsStriking) - { - if (m_uiPause_Timer <= uiDiff) - { - if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() != CHASE_MOTION_TYPE) - if (me->GetVictim()) - me->GetMotionMaster()->MoveChase(me->GetVictim()); - - m_bHasTemper = false; - m_bIsStriking = false; - m_uiPause_Timer = 3500; - } - else - m_uiPause_Timer -= uiDiff; + events.Update(diff); + if (me->HasUnitState(UNIT_STATE_CASTING)) return; - } - // When to start shatter? After 60, 40 or 20% hp? - if (!m_bHasTemper && m_uiHealthAmountModifier >= 3) + while (uint32 eventId = events.ExecuteEvent()) { - if (m_uiShatteringStomp_Timer <= uiDiff) + switch (eventId) { - // Should he stomp even if he has no brittle golem to shatter? - Talk(SAY_STOMP); - - DoCast(me, SPELL_SHATTERING_STOMP); + case EVENT_PAUSE: + if (m_bIsStriking) + { + if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() != CHASE_MOTION_TYPE) + if (me->GetVictim()) + me->GetMotionMaster()->MoveChase(me->GetVictim()); - Talk(EMOTE_SHATTER); + m_bHasTemper = false; + m_bIsStriking = false; + events.ScheduleEvent(EVENT_PAUSE, 3.5 * IN_MILLISECONDS, 0, PHASE_NORMAL); + } + break; + case EVENT_SHATTERING_STOMP: + if (!m_bHasTemper && m_uiHealthAmountModifier >= 3) + { + // Should he stomp even if he has no brittle golem to shatter? + Talk(SAY_STOMP); - m_uiShatteringStomp_Timer = 30000; - m_bCanShatterGolem = true; - } - else - m_uiShatteringStomp_Timer -= uiDiff; - } + DoCast(me, SPELL_SHATTERING_STOMP); - // Shatter Golems 3 seconds after Shattering Stomp - if (m_bCanShatterGolem) - { - if (m_uiShatter_Timer <= uiDiff) - { - ShatterGolem(); - m_uiShatter_Timer = 3000; - m_bCanShatterGolem = false; + Talk(EMOTE_SHATTER); + events.ScheduleEvent(EVENT_SHATTERING_STOMP, 30 * IN_MILLISECONDS, 0, PHASE_NORMAL); + m_bCanShatterGolem = true; + } + break; + case EVENT_SHATTER: + if (m_bCanShatterGolem) + { + ShatterGolem(); + events.ScheduleEvent(EVENT_SHATTER, 3 * IN_MILLISECONDS, 0, PHASE_NORMAL); + m_bCanShatterGolem = false; + } + break; + case EVENT_FORGE_CAST: + DoCast(me, SPELL_FORGE_VISUAL); + events.ScheduleEvent(EVENT_FORGE_CAST, 15 * IN_MILLISECONDS, 0, PHASE_INTRO); + break; + default: + break; } - else - m_uiShatter_Timer -= uiDiff; } // Health check @@ -302,12 +301,10 @@ public: me->GetMotionMaster()->MoveTargetedHome(); m_uiSummonPhase = 2; // Set Next Phase break; - case 2: // 2 - Check if reached Anvil // This is handled in: void JustReachedHome() override break; - case 3: // 3 - Cast Temper on the Anvil if (Unit* target = GetClosestCreatureWithEntry(me, NPC_VOLKHAN_ANVIL, 1000.0f, true)) @@ -319,10 +316,9 @@ public: m_uiDelay_Timer = 1000; // Delay 2 seconds before next phase can begin m_uiSummonPhase = 4; // Set Next Phase break; - case 4: // 4 - Wait for delay to expire - if (m_uiDelay_Timer <= uiDiff) + if (m_uiDelay_Timer <= diff) { if (Unit* target = SelectTarget(SELECT_TARGET_TOPAGGRO, 0)) { @@ -333,9 +329,8 @@ public: m_uiSummonPhase = 5; } else - m_uiDelay_Timer -= uiDiff; + m_uiDelay_Timer -= diff; break; - case 5: // 5 - Spawn the Golems if (Creature* creatureTarget = GetClosestCreatureWithEntry(me, NPC_VOLKHAN_ANVIL, 1000.0f, true)) @@ -349,13 +344,30 @@ public: DoMeleeAttackIfReady(); } + + private: + GuidList m_lGolemGUIDList; + uint32 m_uiHealthAmountModifier; + uint8 GolemsShattered; + uint32 m_uiDelay_Timer; + uint32 m_uiSummonPhase; + + bool m_bHasTemper; + bool m_bIsStriking; + bool m_bCanShatterGolem; }; + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<boss_volkhanAI>(creature); + } + }; /*###### ## npc_molten_golem ######*/ + class npc_molten_golem : public CreatureScript { public: @@ -376,18 +388,12 @@ public: void Initialize() { m_bIsFrozen = false; - - m_uiBlast_Timer = 20000; - m_uiDeathDelay_Timer = 0; - m_uiImmolation_Timer = 5000; + events.ScheduleEvent(EVENT_BLAST, 20 * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_IMMOLATION, 5 * IN_MILLISECONDS); } bool m_bIsFrozen; - uint32 m_uiBlast_Timer; - uint32 m_uiDeathDelay_Timer; - uint32 m_uiImmolation_Timer; - void Reset() override { Initialize(); @@ -433,30 +439,39 @@ public: me->DespawnOrUnsummon(); } - void UpdateAI(uint32 uiDiff) override + void UpdateAI(uint32 diff) override { // Return since we have no target or if we are frozen if (!UpdateVictim() || m_bIsFrozen) return; - if (m_uiBlast_Timer <= uiDiff) - { - DoCast(me, SPELL_BLAST_WAVE); - m_uiBlast_Timer = 20000; - } - else - m_uiBlast_Timer -= uiDiff; + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - if (m_uiImmolation_Timer <= uiDiff) + while (uint32 eventId = events.ExecuteEvent()) { - DoCastVictim(SPELL_IMMOLATION_STRIKE); - m_uiImmolation_Timer = 5000; + switch (eventId) + { + case EVENT_BLAST: + DoCast(me, SPELL_BLAST_WAVE); + events.ScheduleEvent(EVENT_BLAST, 20 * IN_MILLISECONDS); + break; + case EVENT_IMMOLATION: + DoCastVictim(SPELL_IMMOLATION_STRIKE); + events.ScheduleEvent(EVENT_BLAST, 5 * IN_MILLISECONDS); + break; + default: + break; + } } - else - m_uiImmolation_Timer -= uiDiff; DoMeleeAttackIfReady(); } + + private: + EventMap events; }; }; diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp index a682e405d08..a7a9f8dde88 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp @@ -1126,30 +1126,11 @@ class npc_ominous_cloud : public CreatureScript DoCast(me, SPELL_OMINOUS_CLOUD_VISUAL); } - void FillCirclePath(Position const& centerPos, float radius, float z, Movement::PointsArray& path, bool clockwise) - { - float step = clockwise ? float(-M_PI) / 8.0f : float(M_PI) / 8.0f; - float angle = centerPos.GetAngle(me->GetPositionX(), me->GetPositionY()); - - for (uint8 i = 0; i < 16; angle += step, ++i) - { - G3D::Vector3 point; - point.x = centerPos.GetPositionX() + radius * cosf(angle); - point.y = centerPos.GetPositionY() + radius * sinf(angle); - point.z = me->GetMap()->GetHeight(me->GetPhaseMask(), point.x, point.y, z + 5.0f); - path.push_back(point); - } - } - void UpdateAI(uint32 /*diff*/) override { } void DoAction(int32 action) override { - Movement::MoveSplineInit init(me); - FillCirclePath(YoggSaronSpawnPos, me->GetDistance2d(YoggSaronSpawnPos.GetPositionX(), YoggSaronSpawnPos.GetPositionY()), me->GetPositionZ(), init.Path(), action != 0); - init.SetWalk(true); - init.SetCyclic(); - init.Launch(); + me->GetMotionMaster()->MoveCirclePath(YoggSaronSpawnPos.GetPositionX(), YoggSaronSpawnPos.GetPositionY(), me->GetPositionZ() + 5.0f, me->GetDistance2d(YoggSaronSpawnPos.GetPositionX(), YoggSaronSpawnPos.GetPositionY()), true, 16); } }; diff --git a/src/server/scripts/Northrend/VioletHold/boss_zuramat.cpp b/src/server/scripts/Northrend/VioletHold/boss_zuramat.cpp index d1df244b733..90fd936e853 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_zuramat.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_zuramat.cpp @@ -26,9 +26,6 @@ enum Spells SPELL_SUMMON_VOID_SENTRY = 54369, SPELL_VOID_SHIFT = 54361, H_SPELL_VOID_SHIFT = 59743, - - SPELL_ZURAMAT_ADD_2 = 54342, - H_SPELL_ZURAMAT_ADD_2 = 59747 }; enum Creatures @@ -192,12 +189,6 @@ public: Talk(SAY_SLAY); } - void JustSummoned(Creature* summon) override - { - summon->AI()->AttackStart(me->GetVictim()); - summon->CastSpell((Unit*)NULL, SPELL_ZURAMAT_ADD_2); - summon->SetPhaseMask(17, true); - } }; }; diff --git a/src/server/scripts/Northrend/VioletHold/instance_violet_hold.cpp b/src/server/scripts/Northrend/VioletHold/instance_violet_hold.cpp index 3e3ce5cde75..a462c68e084 100644 --- a/src/server/scripts/Northrend/VioletHold/instance_violet_hold.cpp +++ b/src/server/scripts/Northrend/VioletHold/instance_violet_hold.cpp @@ -133,6 +133,7 @@ public: uiCyanigosaEventTimer = 3 * IN_MILLISECONDS; bActive = false; + bWiped = false; bIsDoorSpellCast = false; bCrystalActivated = false; defenseless = true; diff --git a/src/server/scripts/Northrend/zone_borean_tundra.cpp b/src/server/scripts/Northrend/zone_borean_tundra.cpp index 9a577516126..4aea36e3fe7 100644 --- a/src/server/scripts/Northrend/zone_borean_tundra.cpp +++ b/src/server/scripts/Northrend/zone_borean_tundra.cpp @@ -29,7 +29,6 @@ npc_corastrasza npc_sinkhole_kill_credit npc_khunok_the_behemoth npc_nerubar_victim -npc_keristrasza npc_nesingwary_trapper npc_lurgglbr npc_nexus_drake_hatchling @@ -223,48 +222,6 @@ public: }; /*###### -## npc_keristrasza -######*/ - -enum Keristrasza -{ - SPELL_TELEPORT_TO_SARAGOSA = 46772 -}; - -#define GOSSIP_HELLO_KERI "I am prepared to face Saragosa!" - -class npc_keristrasza : public CreatureScript -{ -public: - npc_keristrasza() : CreatureScript("npc_keristrasza") { } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (creature->IsQuestGiver()) - player->PrepareQuestMenu(creature->GetGUID()); - - if (player->GetQuestStatus(11957) == QUEST_STATUS_INCOMPLETE) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HELLO_KERI, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - - player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); - - return true; - } - - bool OnGossipSelect(Player* player, Creature* /*creature*/, uint32 /*sender*/, uint32 action) override - { - player->PlayerTalkClass->ClearMenus(); - if (action == GOSSIP_ACTION_INFO_DEF + 1) - { - player->CLOSE_GOSSIP_MENU(); - player->CastSpell(player, SPELL_TELEPORT_TO_SARAGOSA, true); - } - - return true; - } -}; - -/*###### ## npc_corastrasza ######*/ @@ -438,6 +395,9 @@ enum NesingwaryTrapper GO_CARIBOU_TRAP_15 = 188008, SPELL_TRAPPED = 46104, + + // Texts + SAY_NESINGWARY_1 = 0 }; #define CaribouTrapsNum 15 @@ -514,7 +474,7 @@ public: phase = 3; break; case 3: - //Talk(SAY_NESINGWARY_1); + Talk(SAY_NESINGWARY_1); phaseTimer = 2000; phase = 4; break; @@ -2488,7 +2448,6 @@ void AddSC_borean_tundra() { new npc_sinkhole_kill_credit(); new npc_khunok_the_behemoth(); - new npc_keristrasza(); new npc_corastrasza(); new npc_iruk(); new npc_nerubar_victim(); diff --git a/src/server/scripts/Northrend/zone_storm_peaks.cpp b/src/server/scripts/Northrend/zone_storm_peaks.cpp index bfdea4a1435..bb1e88ce158 100644 --- a/src/server/scripts/Northrend/zone_storm_peaks.cpp +++ b/src/server/scripts/Northrend/zone_storm_peaks.cpp @@ -641,97 +641,6 @@ enum JokkumScriptcast EVENT_KROLMIR_9 = 24, }; -class npc_king_jokkum_vehicle : public CreatureScript -{ -public: - npc_king_jokkum_vehicle() : CreatureScript("npc_king_jokkum_vehicle") { } - - struct npc_king_jokkum_vehicleAI : public VehicleAI - { - npc_king_jokkum_vehicleAI(Creature* creature) : VehicleAI(creature) - { - pathEnd = false; - } - - void Reset() override - { - playerGUID.Clear(); - pathEnd = false; - } - - void OnCharmed(bool /*apply*/) override { } - - void PassengerBoarded(Unit* who, int8 /*seat*/, bool apply) override - { - if (apply) - { - playerGUID = who->GetGUID(); - Talk(SAY_HOLD_ON, who); - me->CastSpell(who, SPELL_JOKKUM_KILL_CREDIT, true); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC); - me->GetMotionMaster()->MovePath(PATH_JOKKUM, false); - } - } - - void MovementInform(uint32 type, uint32 id) override - { - if (type != WAYPOINT_MOTION_TYPE) - return; - - if (pathEnd) - { - if (id == 4) - { - - } - } - else - { - if (id == 19) - { - pathEnd = true; - me->SetFacingTo(0.418879f); - Talk(SAY_JOKKUM_1); - if (Player* player = ObjectAccessor::GetPlayer(*me, playerGUID)) - me->CastSpell(player, SPELL_PLAYER_CAST_VERANUS_SUMMON); - me->CastSpell(me, SPELL_EJECT_ALL_PASSENGERS); - - } - } - } - - void UpdateAI(uint32 diff) override - { - if (!pathEnd) - return; - - events.Update(diff); - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_KROLMIR_1: - Talk(SAY_JOKKUM_2); - events.ScheduleEvent(EVENT_KROLMIR_2, 4000); - break; - } - } - } - - private: - EventMap events; - ObjectGuid playerGUID; - bool pathEnd; - - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_king_jokkum_vehicleAI(creature); - } -}; - class spell_jokkum_scriptcast : public SpellScriptLoader { public: spell_jokkum_scriptcast() : SpellScriptLoader("spell_jokkum_scriptcast") { } @@ -855,7 +764,6 @@ void AddSC_storm_peaks() new npc_icefang(); new npc_hyldsmeet_protodrake(); new npc_brann_bronzebeard_keystone(); - new npc_king_jokkum_vehicle(); new spell_jokkum_scriptcast(); new spell_veranus_summon(); new spell_close_rift(); diff --git a/src/server/scripts/Northrend/zone_zuldrak.cpp b/src/server/scripts/Northrend/zone_zuldrak.cpp index f73930181f0..91c796a6e69 100644 --- a/src/server/scripts/Northrend/zone_zuldrak.cpp +++ b/src/server/scripts/Northrend/zone_zuldrak.cpp @@ -313,7 +313,7 @@ public: { player->KilledMonsterCredit(gymerDummy->GetEntry(), gymerDummy->GetGUID()); gymerDummy->CastSpell(gymerDummy, SPELL_GYMER_LOCK_EXPLOSION, true); - gymerDummy->DespawnOrUnsummon(); + gymerDummy->DespawnOrUnsummon(4 * IN_MILLISECONDS); } } return true; diff --git a/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp b/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp index 728b164cc04..e4369f0348d 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp @@ -93,15 +93,12 @@ enum Factions FACTION_COMBAT = 1868 }; -enum SetData +enum Actions { - SETDATA_DATA = 1, - SETDATA_RESET = 1, - SETDATA_CHANNELER_DIED = 2, - SETDATA_START_SPAWNING = 3, - SETDATA_STOP_SPAWNING = 4, - SETDATA_DESPAWN_ALL_SPAWNS = 5, - SETDATA_START_ATTACK_AKAMA = 6 + ACTION_CHANNELER_DIED = 1, + ACTION_START_SPAWNING = 2, + ACTION_STOP_SPAWNING = 3, + ACTION_DESPAWN_ALL_SPAWNS = 4, }; enum Events @@ -158,12 +155,11 @@ class boss_shade_of_akama : public CreatureScript public: boss_shade_of_akama() : CreatureScript("boss_shade_of_akama") { } - struct boss_shade_of_akamaAI : public ScriptedAI + struct boss_shade_of_akamaAI : public BossAI { - boss_shade_of_akamaAI(Creature* creature) : ScriptedAI(creature) + boss_shade_of_akamaAI(Creature* creature) : BossAI(creature, DATA_SHADE_OF_AKAMA) { Initialize(); - instance = creature->GetInstanceScript(); me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); } @@ -177,6 +173,7 @@ public: void Reset() override { + _Reset(); if (!HasKilledAkamaAndReseting) { for (GuidList::const_iterator itr = Channelers.begin(); itr != Channelers.end(); ++itr) @@ -185,7 +182,7 @@ public: for (GuidList::const_iterator itr = Spawners.begin(); itr != Spawners.end(); ++itr) if (Creature* Spawner = ObjectAccessor::GetCreature(*me, *itr)) - Spawner->AI()->SetData(SETDATA_DATA, SETDATA_DESPAWN_ALL_SPAWNS); + Spawner->AI()->DoAction(ACTION_DESPAWN_ALL_SPAWNS); events.ScheduleEvent(EVENT_FIND_CHANNELERS_SPAWNERS, 3000); events.ScheduleEvent(EVENT_RESET_ENCOUNTER, 5000); @@ -197,11 +194,6 @@ public: Initialize(); } - void JustDied(Unit* /*killer*/) override - { - instance->SetBossState(DATA_SHADE_OF_AKAMA, DONE); - } - void EnterCombat(Unit* /*who*/) override { } void AttackStart(Unit* who) override @@ -216,9 +208,9 @@ public: ScriptedAI::AttackStart(who); } - void SetData(uint32 data, uint32 value) override + void DoAction(int32 actionId) override { - if (data == SETDATA_DATA && value == SETDATA_CHANNELER_DIED) + if (actionId == ACTION_CHANNELER_DIED) me->RemoveAuraFromStack(SPELL_SHADE_SOUL_CHANNEL_2); UpdateSpeed(); @@ -314,7 +306,7 @@ public: for (GuidList::const_iterator itr = Spawners.begin(); itr != Spawners.end(); ++itr) if (Creature* Spawner = ObjectAccessor::GetCreature(*me, *itr)) - Spawner->AI()->SetData(SETDATA_DATA, SETDATA_START_SPAWNING); + Spawner->AI()->DoAction(ACTION_START_SPAWNING); break; } case EVENT_START_ATTACK_AKAMA: @@ -353,7 +345,7 @@ public: for (GuidList::const_iterator itr = Spawners.begin(); itr != Spawners.end(); ++itr) if (Creature* Spawner = ObjectAccessor::GetCreature(*me, *itr)) - Spawner->AI()->SetData(SETDATA_DATA, SETDATA_DESPAWN_ALL_SPAWNS); + Spawner->AI()->DoAction(ACTION_DESPAWN_ALL_SPAWNS); events.ScheduleEvent(EVENT_FIND_CHANNELERS_SPAWNERS, 10000); events.ScheduleEvent(EVENT_RESET_ENCOUNTER, 20000); @@ -378,7 +370,7 @@ public: for (GuidList::const_iterator itr = Spawners.begin(); itr != Spawners.end(); ++itr) if (Creature* Spawner = ObjectAccessor::GetCreature(*me, *itr)) - Spawner->AI()->SetData(SETDATA_DATA, SETDATA_STOP_SPAWNING); + Spawner->AI()->DoAction(ACTION_STOP_SPAWNING); } } } @@ -390,8 +382,6 @@ public: public: bool HasKilledAkama; private: - InstanceScript* instance; - EventMap events; GuidList Channelers; GuidList Spawners; bool akamaReached; @@ -433,9 +423,11 @@ public: void Reset() override { me->setFaction(FACTION_FRIENDLY); - me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); DoCast(me, SPELL_STEALTH); Initialize(); + + if (instance->GetBossState(DATA_SHADE_OF_AKAMA) != DONE) + me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); } void JustDied(Unit* /*killer*/) override @@ -581,7 +573,7 @@ public: void JustDied(Unit* /*killer*/) override { if (Creature* Shade = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SHADE_OF_AKAMA))) - Shade->AI()->SetData(SETDATA_DATA, SETDATA_CHANNELER_DIED); + Shade->AI()->DoAction(ACTION_CHANNELER_DIED); } void EnterCombat(Unit* /*who*/) override { } @@ -603,7 +595,7 @@ public: else { me->InterruptSpell(CURRENT_CHANNELED_SPELL); - Shade->AI()->SetData(SETDATA_DATA, SETDATA_CHANNELER_DIED); + Shade->AI()->DoAction(ACTION_CHANNELER_DIED); } } events.ScheduleEvent(EVENT_CHANNEL, 2000); @@ -663,36 +655,33 @@ public: Summons.Summon(summon); } - void SetData(uint32 data, uint32 value) override + void DoAction(int32 actionId) override { - if (data == SETDATA_DATA) - { - doSpawning = true; + doSpawning = true; - switch (value) - { - case SETDATA_START_SPAWNING: - if (leftSide) - { - events.ScheduleEvent(EVENT_SPAWN_WAVE_B, 100); - events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_SORCERER, urand(2000, 5000)); - } - else - { - events.ScheduleEvent(EVENT_SPAWN_WAVE_B, 10000); - events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_DEFENDER, urand(2000, 5000)); - } - break; - case SETDATA_STOP_SPAWNING: - doSpawning = false; - break; - case SETDATA_DESPAWN_ALL_SPAWNS: - doSpawning = false; - Summons.DespawnAll(); - break; - default: - break; - } + switch (actionId) + { + case ACTION_START_SPAWNING: + if (leftSide) + { + events.ScheduleEvent(EVENT_SPAWN_WAVE_B, 100); + events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_SORCERER, urand(2000, 5000)); + } + else + { + events.ScheduleEvent(EVENT_SPAWN_WAVE_B, 10000); + events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_DEFENDER, urand(2000, 5000)); + } + break; + case ACTION_STOP_SPAWNING: + doSpawning = false; + break; + case ACTION_DESPAWN_ALL_SPAWNS: + doSpawning = false; + Summons.DespawnAll(); + break; + default: + break; } } @@ -778,23 +767,16 @@ public: } } - summonerGuid.Clear(); Initialize(); } void JustDied(Unit* /*killer*/) override { if (Creature* Shade = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SHADE_OF_AKAMA))) - Shade->AI()->SetData(SETDATA_DATA, SETDATA_CHANNELER_DIED); + Shade->AI()->DoAction(ACTION_CHANNELER_DIED); me->DespawnOrUnsummon(5000); } - void IsSummonedBy(Unit* /*summoner*/) override - { - if (Creature* summoner = (ObjectAccessor::GetCreature((*me), summonerGuid))) - ENSURE_AI(npc_creature_generator_akama::npc_creature_generator_akamaAI, summoner->AI())->JustSummoned(me); - } - void EnterCombat(Unit* /*who*/) override { } void AttackStart(Unit* who) override @@ -824,7 +806,7 @@ public: else { me->InterruptSpell(CURRENT_CHANNELED_SPELL); - Shade->AI()->SetData(SETDATA_DATA, SETDATA_CHANNELER_DIED); + Shade->AI()->DoAction(ACTION_CHANNELER_DIED); switchToCombat = true; if (Unit* target = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) AttackStart(target); @@ -855,7 +837,6 @@ public: private: InstanceScript* instance; EventMap events; - ObjectGuid summonerGuid; bool startedBanishing; bool switchToCombat; }; @@ -884,8 +865,6 @@ public: void Reset() override { - summonerGuid.Clear(); - if (Unit* target = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) AttackStart(target); } @@ -895,12 +874,6 @@ public: me->DespawnOrUnsummon(5000); } - void IsSummonedBy(Unit* /*summoner*/) override - { - if (Creature* summoner = (ObjectAccessor::GetCreature((*me), summonerGuid))) - ENSURE_AI(npc_creature_generator_akama::npc_creature_generator_akamaAI, summoner->AI())->JustSummoned(me); - } - void EnterCombat(Unit* /*who*/) override { events.ScheduleEvent(EVENT_HEROIC_STRIKE, 5000); @@ -947,7 +920,6 @@ public: private: InstanceScript* instance; EventMap events; - ObjectGuid summonerGuid; }; CreatureAI* GetAI(Creature* creature) const override @@ -974,8 +946,6 @@ public: void Reset() override { - summonerGuid.Clear(); - if (Unit* target = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) AttackStart(target); } @@ -985,12 +955,6 @@ public: me->DespawnOrUnsummon(5000); } - void IsSummonedBy(Unit* /*summoner*/) override - { - if (Creature* summoner = (ObjectAccessor::GetCreature((*me), summonerGuid))) - ENSURE_AI(npc_creature_generator_akama::npc_creature_generator_akamaAI, summoner->AI())->JustSummoned(me); - } - void EnterCombat(Unit* /*who*/) override { events.ScheduleEvent(EVENT_DEBILITATING_POISON, urand(500, 2000)); @@ -1027,7 +991,6 @@ public: private: InstanceScript* instance; EventMap events; - ObjectGuid summonerGuid; }; CreatureAI* GetAI(Creature* creature) const override @@ -1054,8 +1017,6 @@ public: void Reset() override { - summonerGuid.Clear(); - if (Unit* target = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) AttackStart(target); } @@ -1065,12 +1026,6 @@ public: me->DespawnOrUnsummon(5000); } - void IsSummonedBy(Unit* /*summoner*/) override - { - if (Creature* summoner = (ObjectAccessor::GetCreature((*me), summonerGuid))) - ENSURE_AI(npc_creature_generator_akama::npc_creature_generator_akamaAI, summoner->AI())->JustSummoned(me); - } - void EnterCombat(Unit* /*who*/) override { events.ScheduleEvent(EVENT_RAIN_OF_FIRE, 18000); @@ -1107,7 +1062,6 @@ public: private: InstanceScript* instance; EventMap events; - ObjectGuid summonerGuid; }; CreatureAI* GetAI(Creature* creature) const override @@ -1137,7 +1091,6 @@ public: { spiritMend = false; chainHeal = false; - summonerGuid.Clear(); } void Reset() override @@ -1153,12 +1106,6 @@ public: me->DespawnOrUnsummon(5000); } - void IsSummonedBy(Unit* /*summoner*/) override - { - if (Creature* summoner = (ObjectAccessor::GetCreature((*me), summonerGuid))) - ENSURE_AI(npc_creature_generator_akama::npc_creature_generator_akamaAI, summoner->AI())->JustSummoned(me); - } - void EnterCombat(Unit* /*who*/) override { events.ScheduleEvent(EVENT_SPIRIT_HEAL, urand (5000, 6000)); @@ -1208,7 +1155,6 @@ public: private: InstanceScript* instance; EventMap events; - ObjectGuid summonerGuid; bool spiritMend; bool chainHeal; }; diff --git a/src/server/scripts/Outland/TempestKeep/arcatraz/arcatraz.cpp b/src/server/scripts/Outland/TempestKeep/arcatraz/arcatraz.cpp index 2407f3980de..9ffc151b3b9 100644 --- a/src/server/scripts/Outland/TempestKeep/arcatraz/arcatraz.cpp +++ b/src/server/scripts/Outland/TempestKeep/arcatraz/arcatraz.cpp @@ -338,6 +338,13 @@ class npc_warden_mellichar : public CreatureScript IsRunning = true; } + void JustSummoned(Creature* summon) override + { + DoZoneInCombat(summon); + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true)) + summon->AI()->AttackStart(target); + } + bool CanProgress() { if (Phase == 7 && instance->GetData(DATA_WARDEN_4) == DONE) diff --git a/src/server/scripts/Outland/TempestKeep/arcatraz/boss_harbinger_skyriss.cpp b/src/server/scripts/Outland/TempestKeep/arcatraz/boss_harbinger_skyriss.cpp index 50b77aac410..213f31b280f 100644 --- a/src/server/scripts/Outland/TempestKeep/arcatraz/boss_harbinger_skyriss.cpp +++ b/src/server/scripts/Outland/TempestKeep/arcatraz/boss_harbinger_skyriss.cpp @@ -283,7 +283,10 @@ class boss_harbinger_skyriss_illusion : public CreatureScript { boss_harbinger_skyriss_illusionAI(Creature* creature) : ScriptedAI(creature) { } - void Reset() override { } + void Reset() override + { + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + } void EnterCombat(Unit* /*who*/) override { } }; diff --git a/src/server/shared/CMakeLists.txt b/src/server/shared/CMakeLists.txt index fb86ac7db18..5f319191c14 100644 --- a/src/server/shared/CMakeLists.txt +++ b/src/server/shared/CMakeLists.txt @@ -59,7 +59,7 @@ include_directories( ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour ${CMAKE_SOURCE_DIR}/dep/SFMT - ${CMAKE_SOURCE_DIR}/dep/sockets/include + ${CMAKE_SOURCE_DIR}/dep/cppformat ${CMAKE_SOURCE_DIR}/dep/utf8cpp ${CMAKE_SOURCE_DIR}/src/server ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/src/server/shared/Cryptography/BigNumber.cpp b/src/server/shared/Cryptography/BigNumber.cpp index 434268097fe..7b353eb5616 100644 --- a/src/server/shared/Cryptography/BigNumber.cpp +++ b/src/server/shared/Cryptography/BigNumber.cpp @@ -171,19 +171,20 @@ bool BigNumber::isZero() const std::unique_ptr<uint8[]> BigNumber::AsByteArray(int32 minSize, bool littleEndian) { - int length = (minSize >= GetNumBytes()) ? minSize : GetNumBytes(); + int numBytes = GetNumBytes(); + int length = (minSize >= numBytes) ? minSize : numBytes; uint8* array = new uint8[length]; // If we need more bytes than length of BigNumber set the rest to 0 - if (length > GetNumBytes()) + if (length > numBytes) memset((void*)array, 0, length); BN_bn2bin(_bn, (unsigned char *)array); // openssl's BN stores data internally in big endian format, reverse if little endian desired if (littleEndian) - std::reverse(array, array + length); + std::reverse(array, array + numBytes); std::unique_ptr<uint8[]> ret(array); return ret; diff --git a/src/server/shared/DataStores/DBCStore.h b/src/server/shared/DataStores/DBCStore.h index 10d4ff1bec9..1cb67a4235e 100644 --- a/src/server/shared/DataStores/DBCStore.h +++ b/src/server/shared/DataStores/DBCStore.h @@ -87,6 +87,13 @@ class DBCStorage return (id >= nCount) ? NULL : indexTable.asT[id]; } + T const* AssertEntry(uint32 id) const + { + T const* entry = LookupEntry(id); + ASSERT(entry); + return entry; + } + uint32 GetNumRows() const { return nCount; } char const* GetFormat() const { return fmt; } uint32 GetFieldCount() const { return fieldCount; } diff --git a/src/server/shared/Database/DatabaseWorkerPool.h b/src/server/shared/Database/DatabaseWorkerPool.h index 5a06ad69a1d..f0ddbe91ad8 100644 --- a/src/server/shared/Database/DatabaseWorkerPool.h +++ b/src/server/shared/Database/DatabaseWorkerPool.h @@ -29,6 +29,8 @@ #include "QueryHolder.h" #include "AdhocStatement.h" +#include <mysqld_error.h> + #define MIN_MYSQL_SERVER_VERSION 50100u #define MIN_MYSQL_CLIENT_VERSION 50100u @@ -368,7 +370,8 @@ class DatabaseWorkerPool void DirectCommitTransaction(SQLTransaction& transaction) { T* con = GetFreeConnection(); - if (con->ExecuteTransaction(transaction)) + int errorCode = con->ExecuteTransaction(transaction); + if (!errorCode) { con->Unlock(); // OK, operation succesful return; @@ -376,12 +379,12 @@ class DatabaseWorkerPool //! Handle MySQL Errno 1213 without extending deadlock to the core itself /// @todo More elegant way - if (con->GetLastError() == 1213) + if (errorCode == ER_LOCK_DEADLOCK) { uint8 loopBreaker = 5; for (uint8 i = 0; i < loopBreaker; ++i) { - if (con->ExecuteTransaction(transaction)) + if (!con->ExecuteTransaction(transaction)) break; } } diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.cpp b/src/server/shared/Database/Implementation/CharacterDatabase.cpp index 265b0578841..c8632b8a3c2 100644 --- a/src/server/shared/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/shared/Database/Implementation/CharacterDatabase.cpp @@ -60,7 +60,8 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_SEL_CHAR_POSITION_XYZ, "SELECT map, position_x, position_y, position_z FROM characters WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_POSITION, "SELECT position_x, position_y, position_z, orientation, map, taxi_path FROM characters WHERE guid = ?", CONNECTION_SYNCH); - PrepareStatement(CHAR_DEL_BATTLEGROUND_RANDOM, "DELETE FROM character_battleground_random", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_BATTLEGROUND_RANDOM_ALL, "DELETE FROM character_battleground_random", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_BATTLEGROUND_RANDOM, "DELETE FROM character_battleground_random WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_BATTLEGROUND_RANDOM, "INSERT INTO character_battleground_random (guid) VALUES (?)", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER, "SELECT guid, account, name, race, class, gender, level, xp, money, playerBytes, playerBytes2, playerFlags, " @@ -300,6 +301,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_DEL_ARENA_TEAM_MEMBER, "DELETE FROM arena_team_member WHERE arenaTeamId = ? AND guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_ARENA_TEAM_STATS, "UPDATE arena_team SET rating = ?, weekGames = ?, weekWins = ?, seasonGames = ?, seasonWins = ?, rank = ? WHERE arenaTeamId = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_ARENA_TEAM_MEMBER, "UPDATE arena_team_member SET personalRating = ?, weekGames = ?, weekWins = ?, seasonGames = ?, seasonWins = ? WHERE arenaTeamId = ? AND guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_CHARACTER_ARENA_STATS, "DELETE FROM character_arena_stats WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_REP_CHARACTER_ARENA_STATS, "REPLACE INTO character_arena_stats (guid, slot, matchMakerRating) VALUES (?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_PLAYER_ARENA_TEAMS, "SELECT arena_team_member.arenaTeamId FROM arena_team_member JOIN arena_team ON arena_team_member.arenaTeamId = arena_team.arenaTeamId WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_UPD_ARENA_TEAM_NAME, "UPDATE arena_team SET name = ? WHERE arenaTeamId = ?", CONNECTION_ASYNC); diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.h b/src/server/shared/Database/Implementation/CharacterDatabase.h index f7ff5b9186e..e56a24d6865 100644 --- a/src/server/shared/Database/Implementation/CharacterDatabase.h +++ b/src/server/shared/Database/Implementation/CharacterDatabase.h @@ -72,6 +72,7 @@ enum CharacterDatabaseStatements CHAR_SEL_CHAR_POSITION_XYZ, CHAR_SEL_CHAR_POSITION, + CHAR_DEL_BATTLEGROUND_RANDOM_ALL, CHAR_DEL_BATTLEGROUND_RANDOM, CHAR_INS_BATTLEGROUND_RANDOM, @@ -253,6 +254,7 @@ enum CharacterDatabaseStatements CHAR_DEL_ARENA_TEAM_MEMBER, CHAR_UPD_ARENA_TEAM_STATS, CHAR_UPD_ARENA_TEAM_MEMBER, + CHAR_DEL_CHARACTER_ARENA_STATS, CHAR_REP_CHARACTER_ARENA_STATS, CHAR_SEL_PLAYER_ARENA_TEAMS, CHAR_UPD_ARENA_TEAM_NAME, diff --git a/src/server/shared/Database/MySQLConnection.cpp b/src/server/shared/Database/MySQLConnection.cpp index bea229df184..1a9f973d47b 100644 --- a/src/server/shared/Database/MySQLConnection.cpp +++ b/src/server/shared/Database/MySQLConnection.cpp @@ -359,11 +359,11 @@ void MySQLConnection::CommitTransaction() Execute("COMMIT"); } -bool MySQLConnection::ExecuteTransaction(SQLTransaction& transaction) +int MySQLConnection::ExecuteTransaction(SQLTransaction& transaction) { std::list<SQLElementData> const& queries = transaction->m_queries; if (queries.empty()) - return false; + return -1; BeginTransaction(); @@ -380,8 +380,9 @@ bool MySQLConnection::ExecuteTransaction(SQLTransaction& transaction) if (!Execute(stmt)) { TC_LOG_WARN("sql.sql", "Transaction aborted. %u queries not executed.", (uint32)queries.size()); + int errorCode = GetLastError(); RollbackTransaction(); - return false; + return errorCode; } } break; @@ -392,8 +393,9 @@ bool MySQLConnection::ExecuteTransaction(SQLTransaction& transaction) if (!Execute(sql)) { TC_LOG_WARN("sql.sql", "Transaction aborted. %u queries not executed.", (uint32)queries.size()); + int errorCode = GetLastError(); RollbackTransaction(); - return false; + return errorCode; } } break; @@ -406,7 +408,7 @@ bool MySQLConnection::ExecuteTransaction(SQLTransaction& transaction) // and not while iterating over every element. CommitTransaction(); - return true; + return 0; } MySQLPreparedStatement* MySQLConnection::GetPreparedStatement(uint32 index) diff --git a/src/server/shared/Database/MySQLConnection.h b/src/server/shared/Database/MySQLConnection.h index 33f17d02228..d486f5b4679 100644 --- a/src/server/shared/Database/MySQLConnection.h +++ b/src/server/shared/Database/MySQLConnection.h @@ -86,7 +86,7 @@ class MySQLConnection void BeginTransaction(); void RollbackTransaction(); void CommitTransaction(); - bool ExecuteTransaction(SQLTransaction& transaction); + int ExecuteTransaction(SQLTransaction& transaction); operator bool () const { return m_Mysql != NULL; } void Ping() { mysql_ping(m_Mysql); } diff --git a/src/server/shared/Database/Transaction.cpp b/src/server/shared/Database/Transaction.cpp index 3dee865267b..9f36d198bde 100644 --- a/src/server/shared/Database/Transaction.cpp +++ b/src/server/shared/Database/Transaction.cpp @@ -17,6 +17,9 @@ #include "DatabaseEnv.h" #include "Transaction.h" +#include <mysqld_error.h> + +std::mutex TransactionTask::_deadlockLock; //- Append a raw ad-hoc query to the transaction void Transaction::Append(const char* sql) @@ -74,14 +77,17 @@ void Transaction::Cleanup() bool TransactionTask::Execute() { - if (m_conn->ExecuteTransaction(m_trans)) + int errorCode = m_conn->ExecuteTransaction(m_trans); + if (!errorCode) return true; - if (m_conn->GetLastError() == 1213) + if (errorCode == ER_LOCK_DEADLOCK) { + // Make sure only 1 async thread retries a transaction so they don't keep dead-locking each other + std::lock_guard<std::mutex> lock(_deadlockLock); uint8 loopBreaker = 5; // Handle MySQL Errno 1213 without extending deadlock to the core itself for (uint8 i = 0; i < loopBreaker; ++i) - if (m_conn->ExecuteTransaction(m_trans)) + if (!m_conn->ExecuteTransaction(m_trans)) return true; } diff --git a/src/server/shared/Database/Transaction.h b/src/server/shared/Database/Transaction.h index cf6aa98b386..83d59006ddc 100644 --- a/src/server/shared/Database/Transaction.h +++ b/src/server/shared/Database/Transaction.h @@ -66,6 +66,7 @@ class TransactionTask : public SQLOperation bool Execute() override; SQLTransaction m_trans; + static std::mutex _deadlockLock; }; #endif diff --git a/src/server/shared/Debugging/Errors.h b/src/server/shared/Debugging/Errors.h index df770c2aa47..4d4624b63dd 100644 --- a/src/server/shared/Debugging/Errors.h +++ b/src/server/shared/Debugging/Errors.h @@ -49,4 +49,10 @@ namespace Trinity #define ASSERT WPAssert +template <typename T> inline T* ASSERT_NOTNULL(T* pointer) +{ + ASSERT(pointer); + return pointer; +} + #endif diff --git a/src/server/shared/Debugging/WheatyExceptionReport.cpp b/src/server/shared/Debugging/WheatyExceptionReport.cpp index e9f4f9ca9ac..f8f641a9ea7 100644 --- a/src/server/shared/Debugging/WheatyExceptionReport.cpp +++ b/src/server/shared/Debugging/WheatyExceptionReport.cpp @@ -1068,7 +1068,7 @@ bool logChildren) { case btChar: case btStdString: - FormatOutputValue(buffer, basicType, length, (PVOID)offset, sizeof(buffer)); + FormatOutputValue(buffer, basicType, length, (PVOID)offset, sizeof(buffer), elementsCount); symbolDetails.top().Value = buffer; break; default: @@ -1196,7 +1196,8 @@ void WheatyExceptionReport::FormatOutputValue(char * pszCurrBuffer, BasicType basicType, DWORD64 length, PVOID pAddress, -size_t bufferSize) +size_t bufferSize, +size_t countOverride) { __try { @@ -1204,10 +1205,15 @@ size_t bufferSize) { case btChar: { - if (strlen((char*)pAddress) > bufferSize - 6) + // Special case handling for char[] type + if (countOverride != 0) + length = countOverride; + else + length = strlen((char*)pAddress); + if (length > bufferSize - 6) pszCurrBuffer += sprintf(pszCurrBuffer, "\"%.*s...\"", bufferSize - 6, (char*)pAddress); else - pszCurrBuffer += sprintf(pszCurrBuffer, "\"%s\"", (char*)pAddress); + pszCurrBuffer += sprintf(pszCurrBuffer, "\"%.*s\"", length, (char*)pAddress); break; } case btStdString: diff --git a/src/server/shared/Debugging/WheatyExceptionReport.h b/src/server/shared/Debugging/WheatyExceptionReport.h index 9137b91aac9..b7731daaa2b 100644 --- a/src/server/shared/Debugging/WheatyExceptionReport.h +++ b/src/server/shared/Debugging/WheatyExceptionReport.h @@ -172,7 +172,7 @@ class WheatyExceptionReport static char * DumpTypeIndex(char *, DWORD64, DWORD, unsigned, DWORD_PTR, bool &, const char*, char*, bool, bool); - static void FormatOutputValue(char * pszCurrBuffer, BasicType basicType, DWORD64 length, PVOID pAddress, size_t bufferSize); + static void FormatOutputValue(char * pszCurrBuffer, BasicType basicType, DWORD64 length, PVOID pAddress, size_t bufferSize, size_t countOverride = 0); static BasicType GetBasicType(DWORD typeIndex, DWORD64 modBase); static DWORD_PTR DereferenceUnsafePointer(DWORD_PTR address); diff --git a/src/server/shared/Logging/Appender.cpp b/src/server/shared/Logging/Appender.cpp index dff931e3da8..ca40a857419 100644 --- a/src/server/shared/Logging/Appender.cpp +++ b/src/server/shared/Logging/Appender.cpp @@ -18,6 +18,10 @@ #include "Appender.h" #include "Common.h" #include "Util.h" +#include "StringFormat.h" + +#include <utility> +#include <sstream> std::string LogMessage::getTimeStr(time_t time) { @@ -68,38 +72,36 @@ void Appender::setLogLevel(LogLevel _level) level = _level; } -void Appender::write(LogMessage& message) +void Appender::write(LogMessage* message) { - if (!level || level > message.level) + if (!level || level > message->level) return; - message.prefix.clear(); + std::ostringstream ss; + if (flags & APPENDER_FLAGS_PREFIX_TIMESTAMP) - message.prefix.append(message.getTimeStr()); + ss << message->getTimeStr(); if (flags & APPENDER_FLAGS_PREFIX_LOGLEVEL) { - if (!message.prefix.empty()) - message.prefix.push_back(' '); + if (ss.rdbuf()->in_avail() == 0) + ss << ' '; - char text[MAX_QUERY_LEN]; - snprintf(text, MAX_QUERY_LEN, "%-5s", Appender::getLogLevelString(message.level)); - message.prefix.append(text); + ss << Trinity::StringFormat("%-5s", Appender::getLogLevelString(message->level)); } if (flags & APPENDER_FLAGS_PREFIX_LOGFILTERTYPE) { - if (!message.prefix.empty()) - message.prefix.push_back(' '); + if (ss.rdbuf()->in_avail() == 0) + ss << ' '; - message.prefix.push_back('['); - message.prefix.append(message.type); - message.prefix.push_back(']'); + ss << '[' << message->type << ']'; } - if (!message.prefix.empty()) - message.prefix.push_back(' '); + if (ss.rdbuf()->in_avail() == 0) + ss << ' '; + message->prefix = std::move(ss.str()); _write(message); } diff --git a/src/server/shared/Logging/Appender.h b/src/server/shared/Logging/Appender.h index 6f38eb6aaf7..38c45b3bcf1 100644 --- a/src/server/shared/Logging/Appender.h +++ b/src/server/shared/Logging/Appender.h @@ -21,6 +21,7 @@ #include <unordered_map> #include <string> #include <time.h> +#include <type_traits> #include "Define.h" // Values assigned have their equivalent in enum ACE_Log_Priority @@ -57,16 +58,16 @@ enum AppenderFlags struct LogMessage { - LogMessage(LogLevel _level, std::string const& _type, std::string const& _text) - : level(_level), type(_type), text(_text), mtime(time(NULL)) + LogMessage(LogLevel _level, std::string const& _type, std::string&& _text) + : level(_level), type(_type), text(std::forward<std::string>(_text)), mtime(time(NULL)) { } static std::string getTimeStr(time_t time); std::string getTimeStr(); - LogLevel level; - std::string type; - std::string text; + LogLevel const level; + std::string const type; + std::string const text; std::string prefix; std::string param1; time_t mtime; @@ -91,11 +92,11 @@ class Appender AppenderFlags getFlags() const; void setLogLevel(LogLevel); - void write(LogMessage& message); + void write(LogMessage* message); static const char* getLogLevelString(LogLevel level); private: - virtual void _write(LogMessage const& /*message*/) = 0; + virtual void _write(LogMessage const* /*message*/) = 0; uint8 id; std::string name; diff --git a/src/server/shared/Logging/AppenderConsole.cpp b/src/server/shared/Logging/AppenderConsole.cpp index ae27337fb9a..2efa4db4d2e 100644 --- a/src/server/shared/Logging/AppenderConsole.cpp +++ b/src/server/shared/Logging/AppenderConsole.cpp @@ -158,14 +158,14 @@ void AppenderConsole::ResetColor(bool stdout_stream) #endif } -void AppenderConsole::_write(LogMessage const& message) +void AppenderConsole::_write(LogMessage const* message) { - bool stdout_stream = !(message.level == LOG_LEVEL_ERROR || message.level == LOG_LEVEL_FATAL); + bool stdout_stream = !(message->level == LOG_LEVEL_ERROR || message->level == LOG_LEVEL_FATAL); if (_colored) { uint8 index; - switch (message.level) + switch (message->level) { case LOG_LEVEL_TRACE: index = 5; @@ -189,9 +189,9 @@ void AppenderConsole::_write(LogMessage const& message) } SetColor(stdout_stream, _colors[index]); - utf8printf(stdout_stream ? stdout : stderr, "%s%s", message.prefix.c_str(), message.text.c_str()); + utf8printf(stdout_stream ? stdout : stderr, "%s%s\n", message->prefix.c_str(), message->text.c_str()); ResetColor(stdout_stream); } else - utf8printf(stdout_stream ? stdout : stderr, "%s%s", message.prefix.c_str(), message.text.c_str()); + utf8printf(stdout_stream ? stdout : stderr, "%s%s\n", message->prefix.c_str(), message->text.c_str()); } diff --git a/src/server/shared/Logging/AppenderConsole.h b/src/server/shared/Logging/AppenderConsole.h index 0f9536b3111..0acf7636e35 100644 --- a/src/server/shared/Logging/AppenderConsole.h +++ b/src/server/shared/Logging/AppenderConsole.h @@ -51,7 +51,7 @@ class AppenderConsole: public Appender private: void SetColor(bool stdout_stream, ColorTypes color); void ResetColor(bool stdout_stream); - void _write(LogMessage const& message) override; + void _write(LogMessage const* message) override; bool _colored; ColorTypes _colors[MaxLogLevels]; }; diff --git a/src/server/shared/Logging/AppenderDB.cpp b/src/server/shared/Logging/AppenderDB.cpp index d297d6d08d3..8a329ea3a0f 100644 --- a/src/server/shared/Logging/AppenderDB.cpp +++ b/src/server/shared/Logging/AppenderDB.cpp @@ -23,18 +23,18 @@ AppenderDB::AppenderDB(uint8 id, std::string const& name, LogLevel level) AppenderDB::~AppenderDB() { } -void AppenderDB::_write(LogMessage const& message) +void AppenderDB::_write(LogMessage const* message) { // Avoid infinite loop, PExecute triggers Logging with "sql.sql" type - if (!enabled || !message.type.find("sql")) + if (!enabled || (message->type.find("sql") != std::string::npos)) return; PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_LOG); - stmt->setUInt64(0, message.mtime); + stmt->setUInt64(0, message->mtime); stmt->setUInt32(1, realmId); - stmt->setString(2, message.type); - stmt->setUInt8(3, uint8(message.level)); - stmt->setString(4, message.text); + stmt->setString(2, message->type); + stmt->setUInt8(3, uint8(message->level)); + stmt->setString(4, message->text); LoginDatabase.Execute(stmt); } diff --git a/src/server/shared/Logging/AppenderDB.h b/src/server/shared/Logging/AppenderDB.h index e20ceaf77b4..09affdb46f1 100644 --- a/src/server/shared/Logging/AppenderDB.h +++ b/src/server/shared/Logging/AppenderDB.h @@ -31,7 +31,7 @@ class AppenderDB: public Appender private: uint32 realmId; bool enabled; - void _write(LogMessage const& message) override; + void _write(LogMessage const* message) override; }; #endif diff --git a/src/server/shared/Logging/AppenderFile.cpp b/src/server/shared/Logging/AppenderFile.cpp index 07a88a367ae..3892adbe3be 100644 --- a/src/server/shared/Logging/AppenderFile.cpp +++ b/src/server/shared/Logging/AppenderFile.cpp @@ -43,21 +43,21 @@ AppenderFile::~AppenderFile() CloseFile(); } -void AppenderFile::_write(LogMessage const& message) +void AppenderFile::_write(LogMessage const* message) { - bool exceedMaxSize = maxFileSize > 0 && (fileSize.load() + message.Size()) > maxFileSize; + bool exceedMaxSize = maxFileSize > 0 && (fileSize.load() + message->Size()) > maxFileSize; if (dynamicName) { char namebuf[TRINITY_PATH_MAX]; - snprintf(namebuf, TRINITY_PATH_MAX, filename.c_str(), message.param1.c_str()); + snprintf(namebuf, TRINITY_PATH_MAX, filename.c_str(), message->param1.c_str()); // always use "a" with dynamic name otherwise it could delete the log we wrote in last _write() call FILE* file = OpenFile(namebuf, "a", backup || exceedMaxSize); if (!file) return; - fprintf(file, "%s%s", message.prefix.c_str(), message.text.c_str()); + fprintf(file, "%s%s", message->prefix.c_str(), message->text.c_str()); fflush(file); - fileSize += uint64(message.Size()); + fileSize += uint64(message->Size()); fclose(file); return; } @@ -67,9 +67,9 @@ void AppenderFile::_write(LogMessage const& message) if (!logfile) return; - fprintf(logfile, "%s%s", message.prefix.c_str(), message.text.c_str()); + fprintf(logfile, "%s%s\n", message->prefix.c_str(), message->text.c_str()); fflush(logfile); - fileSize += uint64(message.Size()); + fileSize += uint64(message->Size()); } FILE* AppenderFile::OpenFile(std::string const &filename, std::string const &mode, bool backup) diff --git a/src/server/shared/Logging/AppenderFile.h b/src/server/shared/Logging/AppenderFile.h index 23651fc1129..36afdd23ad1 100644 --- a/src/server/shared/Logging/AppenderFile.h +++ b/src/server/shared/Logging/AppenderFile.h @@ -30,7 +30,7 @@ class AppenderFile: public Appender private: void CloseFile(); - void _write(LogMessage const& message) override; + void _write(LogMessage const* message) override; FILE* logfile; std::string filename; std::string logDir; diff --git a/src/server/shared/Logging/Log.cpp b/src/server/shared/Logging/Log.cpp index 4bf4dacb302..c9a4432039f 100644 --- a/src/server/shared/Logging/Log.cpp +++ b/src/server/shared/Logging/Log.cpp @@ -199,6 +199,9 @@ void Log::CreateLoggerFromConfig(std::string const& appenderName) return; } + if (level < lowestLogLevel) + lowestLogLevel = level; + logger.Create(name, level); //fprintf(stdout, "Log::CreateLoggerFromConfig: Created Logger %s, Level %u\n", name.c_str(), level); @@ -261,30 +264,18 @@ void Log::ReadLoggersFromConfig() } } -void Log::vlog(std::string const& filter, LogLevel level, char const* str, va_list argptr) -{ - char text[MAX_QUERY_LEN]; - vsnprintf(text, MAX_QUERY_LEN, str, argptr); - write(new LogMessage(level, filter, text)); -} - -void Log::write(LogMessage* msg) const +void Log::write(std::unique_ptr<LogMessage>&& msg) const { Logger const* logger = GetLoggerByType(msg->type); - msg->text.append("\n"); if (_ioService) { - auto logOperation = std::shared_ptr<LogOperation>(new LogOperation(logger, msg)); + auto logOperation = std::shared_ptr<LogOperation>(new LogOperation(logger, std::forward<std::unique_ptr<LogMessage>>(msg))); _ioService->post(_strand->wrap([logOperation](){ logOperation->call(); })); - } else - { - logger->write(*msg); - delete msg; - } + logger->write(msg.get()); } std::string Log::GetTimestampStr() @@ -321,6 +312,9 @@ bool Log::SetLogLevel(std::string const& name, const char* newLevelc, bool isLog return false; it->second.setLogLevel(newLevel); + + if (newLevel != LOG_LEVEL_DISABLED && newLevel < lowestLogLevel) + lowestLogLevel = newLevel; } else { @@ -343,40 +337,20 @@ void Log::outCharDump(char const* str, uint32 accountId, uint64 guid, char const ss << "== START DUMP == (account: " << accountId << " guid: " << guid << " name: " << name << ")\n" << str << "\n== END DUMP ==\n"; - LogMessage* msg = new LogMessage(LOG_LEVEL_INFO, "entities.player.dump", ss.str()); + std::unique_ptr<LogMessage> msg(new LogMessage(LOG_LEVEL_INFO, "entities.player.dump", ss.str())); std::ostringstream param; param << guid << '_' << name; msg->param1 = param.str(); - write(msg); -} - -void Log::outCommand(uint32 account, const char * str, ...) -{ - if (!str || !ShouldLog("commands.gm", LOG_LEVEL_INFO)) - return; - - va_list ap; - va_start(ap, str); - char text[MAX_QUERY_LEN]; - vsnprintf(text, MAX_QUERY_LEN, str, ap); - va_end(ap); - - LogMessage* msg = new LogMessage(LOG_LEVEL_INFO, "commands.gm", text); - - std::ostringstream ss; - ss << account; - msg->param1 = ss.str(); - - write(msg); + write(std::move(msg)); } void Log::SetRealmId(uint32 id) { for (AppenderMap::iterator it = appenders.begin(); it != appenders.end(); ++it) if (it->second && it->second->getType() == APPENDER_DB) - ((AppenderDB *)it->second)->setRealmId(id); + static_cast<AppenderDB*>(it->second)->setRealmId(id); } void Log::Close() @@ -394,6 +368,7 @@ void Log::LoadFromConfig() { Close(); + lowestLogLevel = LOG_LEVEL_FATAL; AppenderId = 0; m_logsDir = sConfigMgr->GetStringDefault("LogsDir", ""); if (!m_logsDir.empty()) diff --git a/src/server/shared/Logging/Log.h b/src/server/shared/Logging/Log.h index 1d67ff87f76..20d83d2dcf0 100644 --- a/src/server/shared/Logging/Log.h +++ b/src/server/shared/Logging/Log.h @@ -22,12 +22,14 @@ #include "Define.h" #include "Appender.h" #include "Logger.h" -#include <stdarg.h> +#include "StringFormat.h" #include <boost/asio/io_service.hpp> #include <boost/asio/strand.hpp> +#include <stdarg.h> #include <unordered_map> #include <string> +#include <memory> #define LOGGER_ROOT "root" @@ -59,17 +61,34 @@ class Log bool ShouldLog(std::string const& type, LogLevel level) const; bool SetLogLevel(std::string const& name, char const* level, bool isLogger = true); - void outMessage(std::string const& f, LogLevel level, char const* str, ...) ATTR_PRINTF(4, 5); + template<typename... Args> + inline void outMessage(std::string const& filter, LogLevel const level, const char* fmt, Args const&... args) + { + write(std::move(std::unique_ptr<LogMessage>(new LogMessage(level, filter, std::move(Trinity::StringFormat(fmt, args...)))))); + } + + template<typename... Args> + void outCommand(uint32 account, const char* fmt, Args const&... args) + { + if (!ShouldLog("commands.gm", LOG_LEVEL_INFO)) + return; + + std::unique_ptr<LogMessage> msg(new LogMessage(LOG_LEVEL_INFO, "commands.gm", std::move(Trinity::StringFormat(fmt, args...)))); + + std::ostringstream ss; + ss << account; + msg->param1 = ss.str(); + + write(std::move(msg)); + } - void outCommand(uint32 account, const char * str, ...) ATTR_PRINTF(3, 4); void outCharDump(char const* str, uint32 account_id, uint64 guid, char const* name); void SetRealmId(uint32 id); private: static std::string GetTimestampStr(); - void vlog(std::string const& f, LogLevel level, char const* str, va_list argptr); - void write(LogMessage* msg) const; + void write(std::unique_ptr<LogMessage>&& msg) const; Logger const* GetLoggerByType(std::string const& type) const; Appender* GetAppenderByName(std::string const& name); @@ -82,6 +101,7 @@ class Log AppenderMap appenders; LoggerMap loggers; uint8 AppenderId; + LogLevel lowestLogLevel; std::string m_logsDir; std::string m_logsTimestamp; @@ -113,6 +133,10 @@ inline bool Log::ShouldLog(std::string const& type, LogLevel level) const // Speed up in cases where requesting "Type.sub1.sub2" but only configured // Logger "Type" + // Don't even look for a logger if the LogLevel is lower than lowest log levels across all loggers + if (level < lowestLogLevel) + return false; + Logger const* logger = GetLoggerByType(type); if (!logger) return false; @@ -121,23 +145,34 @@ inline bool Log::ShouldLog(std::string const& type, LogLevel level) const return logLevel != LOG_LEVEL_DISABLED && logLevel <= level; } -inline void Log::outMessage(std::string const& filter, LogLevel level, const char * str, ...) -{ - va_list ap; - va_start(ap, str); - - vlog(filter, level, str, ap); - - va_end(ap); -} - #define sLog Log::instance() +#define LOG_EXCEPTION_FREE(filterType__, level__, ...) \ + { \ + try \ + { \ + sLog->outMessage(filterType__, level__, __VA_ARGS__); \ + } \ + catch (std::exception& e) \ + { \ + sLog->outMessage("server", LOG_LEVEL_ERROR, "Wrong format occurred (%s) at %s:%u.", \ + e.what(), __FILE__, __LINE__); \ + } \ + } + #if PLATFORM != PLATFORM_WINDOWS +void check_args(const char* format, ...) ATTR_PRINTF(1, 2); + +// This will catch format errors on build time #define TC_LOG_MESSAGE_BODY(filterType__, level__, ...) \ do { \ if (sLog->ShouldLog(filterType__, level__)) \ - sLog->outMessage(filterType__, level__, __VA_ARGS__); \ + { \ + if (false) \ + check_args(__VA_ARGS__); \ + \ + LOG_EXCEPTION_FREE(filterType__, level__, __VA_ARGS__); \ + } \ } while (0) #else #define TC_LOG_MESSAGE_BODY(filterType__, level__, ...) \ @@ -145,7 +180,7 @@ inline void Log::outMessage(std::string const& filter, LogLevel level, const cha __pragma(warning(disable:4127)) \ do { \ if (sLog->ShouldLog(filterType__, level__)) \ - sLog->outMessage(filterType__, level__, __VA_ARGS__); \ + LOG_EXCEPTION_FREE(filterType__, level__, __VA_ARGS__); \ } while (0) \ __pragma(warning(pop)) #endif diff --git a/src/server/shared/Logging/LogOperation.cpp b/src/server/shared/Logging/LogOperation.cpp index 9afb28a49c2..bcd923c705e 100644 --- a/src/server/shared/Logging/LogOperation.cpp +++ b/src/server/shared/Logging/LogOperation.cpp @@ -18,14 +18,8 @@ #include "LogOperation.h" #include "Logger.h" -LogOperation::~LogOperation() -{ - delete msg; -} - int LogOperation::call() { - if (logger && msg) - logger->write(*msg); + logger->write(msg.get()); return 0; } diff --git a/src/server/shared/Logging/LogOperation.h b/src/server/shared/Logging/LogOperation.h index b8655413273..ffdd35c3c09 100644 --- a/src/server/shared/Logging/LogOperation.h +++ b/src/server/shared/Logging/LogOperation.h @@ -18,23 +18,25 @@ #ifndef LOGOPERATION_H #define LOGOPERATION_H +#include <memory> + class Logger; struct LogMessage; class LogOperation { public: - LogOperation(Logger const* _logger, LogMessage* _msg) - : logger(_logger), msg(_msg) + LogOperation(Logger const* _logger, std::unique_ptr<LogMessage>&& _msg) + : logger(_logger), msg(std::forward<std::unique_ptr<LogMessage>>(_msg)) { } - ~LogOperation(); + ~LogOperation() { } int call(); protected: Logger const* logger; - LogMessage* msg; + std::unique_ptr<LogMessage> msg; }; #endif diff --git a/src/server/shared/Logging/Logger.cpp b/src/server/shared/Logging/Logger.cpp index 615732deb30..3b02eb47575 100644 --- a/src/server/shared/Logging/Logger.cpp +++ b/src/server/shared/Logging/Logger.cpp @@ -50,9 +50,9 @@ void Logger::setLogLevel(LogLevel _level) level = _level; } -void Logger::write(LogMessage& message) const +void Logger::write(LogMessage* message) const { - if (!level || level > message.level || message.text.empty()) + if (!level || level > message->level || message->text.empty()) { //fprintf(stderr, "Logger::write: Logger %s, Level %u. Msg %s Level %u WRONG LEVEL MASK OR EMPTY MSG\n", getName().c_str(), getLogLevel(), message.text.c_str(), message.level); return; diff --git a/src/server/shared/Logging/Logger.h b/src/server/shared/Logging/Logger.h index a81ee8d7bd2..1aee75c5d72 100644 --- a/src/server/shared/Logging/Logger.h +++ b/src/server/shared/Logging/Logger.h @@ -32,7 +32,7 @@ class Logger std::string const& getName() const; LogLevel getLogLevel() const; void setLogLevel(LogLevel level); - void write(LogMessage& message) const; + void write(LogMessage* message) const; private: std::string name; diff --git a/src/server/shared/Networking/Socket.h b/src/server/shared/Networking/Socket.h index f6bf3976b85..f7a1b954cb0 100644 --- a/src/server/shared/Networking/Socket.h +++ b/src/server/shared/Networking/Socket.h @@ -34,7 +34,9 @@ using boost::asio::ip::tcp; #define READ_BLOCK_SIZE 4096 -#define TC_SOCKET_USE_IOCP BOOST_ASIO_HAS_IOCP +#ifdef BOOST_ASIO_HAS_IOCP +#define TC_SOCKET_USE_IOCP +#endif template<class T> class Socket : public std::enable_shared_from_this<T> diff --git a/src/server/shared/Networking/SocketMgr.h b/src/server/shared/Networking/SocketMgr.h index df57baf257e..2078ae90c62 100644 --- a/src/server/shared/Networking/SocketMgr.h +++ b/src/server/shared/Networking/SocketMgr.h @@ -99,7 +99,7 @@ public: } catch (boost::system::system_error const& err) { - TC_LOG_INFO("network", "Failed to retrieve client's remote address %s", err.what()); + TC_LOG_WARN("network", "Failed to retrieve client's remote address %s", err.what()); } } diff --git a/src/server/shared/Utilities/StringFormat.h b/src/server/shared/Utilities/StringFormat.h new file mode 100644 index 00000000000..70d9aefb14d --- /dev/null +++ b/src/server/shared/Utilities/StringFormat.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * 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 the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef TRINITYCORE_STRING_FORMAT_H +#define TRINITYCORE_STRING_FORMAT_H + +#include <format.h> + +namespace Trinity +{ + //! Default TC string format function + template<typename... Args> + inline std::string StringFormat(const char* fmt, Args const&... args) + { + return fmt::sprintf(fmt, args...); + } +} + +#endif diff --git a/src/server/worldserver/CMakeLists.txt b/src/server/worldserver/CMakeLists.txt index 97180fb36a0..ce9ae8d5219 100644 --- a/src/server/worldserver/CMakeLists.txt +++ b/src/server/worldserver/CMakeLists.txt @@ -47,6 +47,7 @@ include_directories( ${CMAKE_SOURCE_DIR}/dep/gsoap ${CMAKE_SOURCE_DIR}/dep/sockets/include ${CMAKE_SOURCE_DIR}/dep/SFMT + ${CMAKE_SOURCE_DIR}/dep/cppformat ${CMAKE_SOURCE_DIR}/src/server/collision ${CMAKE_SOURCE_DIR}/src/server/collision/Management ${CMAKE_SOURCE_DIR}/src/server/collision/Models @@ -168,6 +169,7 @@ target_link_libraries(worldserver g3dlib gsoap Detour + format ${JEMALLOC_LIBRARY} ${READLINE_LIBRARY} ${TERMCAP_LIBRARY} diff --git a/src/server/worldserver/RemoteAccess/RASession.h b/src/server/worldserver/RemoteAccess/RASession.h index d272f323c6a..efd2106fdd1 100644 --- a/src/server/worldserver/RemoteAccess/RASession.h +++ b/src/server/worldserver/RemoteAccess/RASession.h @@ -30,8 +30,6 @@ using boost::asio::ip::tcp; const size_t bufferSize = 4096; -#define BUFFER_SIZE 4096 - class RASession : public std::enable_shared_from_this <RASession> { public: diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index eaef902addc..ecb5824b1c5 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -3029,33 +3029,54 @@ AuctionHouseBot.Buyer.Horde.Enabled = 0 AuctionHouseBot.Buyer.Neutral.Enabled = 0 # -# AuctionHouseBot.BuyPrice.Buyer -# Description: Should the Buyer use BuyPrice or SellPrice to determine Bid Prices -# Default: 1 - (use BuyPrice) -# 0 - (use SellPrice) - -AuctionHouseBot.Buyer.Buyprice = 1 +# AuctionHouseBot.Buyer.Baseprice.QUALITY +# Description: Base sellprices in copper for non priced items for each quality. +# The default values are based on average item prices of each quality. +# Defaults: Gray 3504 +# White 5429 +# Green 21752 +# Blue 36463 +# Purple 87124 +# Orange 214347 +# Yellow 407406 + +AuctionHouseBot.Buyer.Baseprice.Gray = 3504 +AuctionHouseBot.Buyer.Baseprice.White = 5429 +AuctionHouseBot.Buyer.Baseprice.Green = 21752 +AuctionHouseBot.Buyer.Baseprice.Blue = 36463 +AuctionHouseBot.Buyer.Baseprice.Purple = 87124 +AuctionHouseBot.Buyer.Baseprice.Orange = 214347 +AuctionHouseBot.Buyer.Baseprice.Yellow = 407406 + +# +# AuctionHouseBot.Buyer.ChanceMultiplier.QUALITY +# Description: Multipliers for the buy/bid chances for each quality. 100 means the chance is 100% of the original, +# 1 would mean 1 % of the original and 200 would mean 200% of the original chance. +# Defaults: Gray 100 +# White 100 +# Green 100 +# Blue 100 +# Purple 100 +# Orange 100 +# Yellow 100 + +AuctionHouseBot.Buyer.ChanceMultiplier.Gray = 100 +AuctionHouseBot.Buyer.ChanceMultiplier.White = 100 +AuctionHouseBot.Buyer.ChanceMultiplier.Green = 100 +AuctionHouseBot.Buyer.ChanceMultiplier.Blue = 100 +AuctionHouseBot.Buyer.ChanceMultiplier.Purple = 100 +AuctionHouseBot.Buyer.ChanceMultiplier.Orange = 100 +AuctionHouseBot.Buyer.ChanceMultiplier.Yellow = 100 # # AuctionHouseBot.Buyer.Recheck.Interval -# Description: This specifies the time interval (in minutes) between two evaluations of the same selled item. -# The lesser this value is, the more chances you give for item to be bought by ahbot. +# Description: This specifies the time interval (in minutes) between two evaluations of the same sold item. +# The smaller this value is, the more chances you give for an item to be bought by ahbot. # Default: 20 (20min.) AuctionHouseBot.Buyer.Recheck.Interval = 20 # -# AuctionHouseBot.Buyer.Alliance.Chance.Ratio -# Description: Chance ratio for the buyer to buy an item. Higher the value, lesser the probability -# Example: 3 (1 out of 3 change, that is, 33%). -# Default: 3 -# - -AuctionHouseBot.Buyer.Alliance.Chance.Ratio = 3 -AuctionHouseBot.Buyer.Horde.Chance.Ratio = 3 -AuctionHouseBot.Buyer.Neutral.Chance.Ratio = 3 - -# ################################################################################################### ################################################################################################### @@ -3192,6 +3213,7 @@ Logger.sql.sql=5,Console DBErrors #Logger.scripts.ai=3,Console Server #Logger.server.authserver=3,Console Server #Logger.spells=3,Console Server +#Logger.spells.periodic=3,Console Server #Logger.sql.dev=3,Console Server #Logger.sql.driver=3,Console Server #Logger.warden=3,Console Server |