diff options
Diffstat (limited to 'src')
86 files changed, 2358 insertions, 2228 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/Main.cpp b/src/server/authserver/Main.cpp index cd58ec2bf68..c57f8254d9c 100644 --- a/src/server/authserver/Main.cpp +++ b/src/server/authserver/Main.cpp @@ -58,7 +58,7 @@ boost::asio::deadline_timer _dbPingTimer(_ioService); uint32 _dbPingInterval; LoginDatabaseWorkerPool LoginDatabase; -int main(int argc, char** argv) +int mainImpl(int argc, char** argv) { std::string configFile = _TRINITY_REALM_CONFIG; auto vm = GetConsoleArguments(argc, argv, configFile); @@ -144,6 +144,24 @@ int main(int argc, char** argv) return 0; } +/// Launch the Trinity server +extern int main(int argc, char** argv) +{ + try + { + return mainImpl(argc, argv); + } + catch (std::exception& ex) + { + std::cerr << "Top-level exception caught:" << ex.what() << "\n"; + +#ifndef NDEBUG // rethrow exception for the debugger + throw; +#else + return 1; +#endif + } +} /// Initialize connection to the database bool StartDB() 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/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/Achievements/AchievementMgr.cpp b/src/server/game/Achievements/AchievementMgr.cpp index c1fde9f53d3..4f4302f823c 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: @@ -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..e8c91479cc2 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp @@ -469,7 +469,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..bd5defe2bc3 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) { - 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; - } - } - } + // 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)) + { + 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) +{ + switch (quality) + { + 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) { - 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_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/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp index d670e0a2949..9b3ccc05280 100644 --- a/src/server/game/DungeonFinding/LFGMgr.cpp +++ b/src/server/game/DungeonFinding/LFGMgr.cpp @@ -755,7 +755,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 +893,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; @@ -1566,7 +1566,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()); diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index b5bcbfd221f..53ec842b053 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -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/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 161fca432b4..4d2b1b85b46 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -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); @@ -19086,7 +19086,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); } @@ -22138,6 +22138,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 +22560,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 +22574,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 +22643,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 +23702,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()) { @@ -25047,6 +25075,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 +25143,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 +25156,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); 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/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index d07ec212d0c..f0d7d039dd5 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -7078,6 +7078,7 @@ void ObjectMgr::LoadReputationSpilloverTemplate() continue; } + bool invalidSpilloverFaction = false; for (uint32 i = 0; i < MAX_SPILLOVER_FACTIONS; ++i) { if (repTemplate.faction[i]) @@ -7087,47 +7088,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]); + if (invalidSpilloverFaction) 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]); - continue; - } _repSpilloverTemplateStore[factionId] = repTemplate; 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..684b4abb2aa 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) { @@ -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/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 49f7bc86081..94f458e6742 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; } } 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..47e5b77a0da 100644 --- a/src/server/game/Movement/MotionMaster.cpp +++ b/src/server/game/Movement/MotionMaster.cpp @@ -647,7 +647,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/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/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp index 5b1d9497953..ab6979a1218 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: diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index e5b8cd111bc..c267e560005 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -5924,7 +5924,7 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const 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 @@ -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); @@ -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; @@ -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); @@ -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/Spell.cpp b/src/server/game/Spells/Spell.cpp index 8e415b06745..8f8295d57d1 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -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->AttributesEx4 & SPELL_ATTR4_CAN_CAST_WHILE_CASTING) + _triggeredCastFlags = TriggerCastFlags(uint32(_triggeredCastFlags) | TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_CAST_DIRECTLY); m_CastItem = NULL; m_castItemGUID.Clear(); diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 651f28d3590..d2f2951aac1 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -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); } diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 5ffb8cf6d5f..dd4453cc4c5 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -3731,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 // diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 555f89aad8f..930bf20b6e7 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); } 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/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/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/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/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..9edde447f32 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,395 @@ 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: + { + Movement::MoveSplineInit init(me); + FillCirclePath(QuelDelarCenterPos, 18.0f, 718.046f, init.Path(), true); + init.SetFly(); + init.SetCyclic(); + init.SetAnimation(Movement::ToFly); + init.Launch(); + + _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: + void FillCirclePath(Position const& centerPos, float radius, float z, Movement::PointsArray& path, bool clockwise) + { + float step = clockwise ? -M_PI / 8.0f : 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; + path.push_back(point); + } + } + + 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 +2858,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 +2873,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/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/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/zone_borean_tundra.cpp b/src/server/scripts/Northrend/zone_borean_tundra.cpp index 9a577516126..9b341180c99 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 ######*/ @@ -2488,7 +2445,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/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/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/Log.cpp b/src/server/shared/Logging/Log.cpp index 4bf4dacb302..2f4d221645c 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); @@ -321,6 +324,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 { @@ -394,6 +400,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..408381620f7 100644 --- a/src/server/shared/Logging/Log.h +++ b/src/server/shared/Logging/Log.h @@ -82,6 +82,7 @@ class Log AppenderMap appenders; LoggerMap loggers; uint8 AppenderId; + LogLevel lowestLogLevel; std::string m_logsDir; std::string m_logsTimestamp; @@ -113,6 +114,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; 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/worldserver/Main.cpp b/src/server/worldserver/Main.cpp index 7b47dba8759..06d8775440a 100644 --- a/src/server/worldserver/Main.cpp +++ b/src/server/worldserver/Main.cpp @@ -92,8 +92,7 @@ void ShutdownCLIThread(std::thread* cliThread); void ShutdownThreadPool(std::vector<std::thread>& threadPool); variables_map GetConsoleArguments(int argc, char** argv, std::string& cfg_file, std::string& cfg_service); -/// Launch the Trinity server -extern int main(int argc, char** argv) +int mainImpl(int argc, char** argv) { std::string configFile = _TRINITY_CORE_CONFIG; std::string configService; @@ -289,6 +288,25 @@ extern int main(int argc, char** argv) return World::GetExitCode(); } +/// Launch the Trinity server +extern int main(int argc, char** argv) +{ + try + { + return mainImpl(argc, argv); + } + catch (std::exception& ex) + { + std::cerr << "Top-level exception caught:" << ex.what() << "\n"; + +#ifndef NDEBUG // rethrow exception for the debugger + throw; +#else + return 1; +#endif + } +} + void ShutdownCLIThread(std::thread* cliThread) { if (cliThread != nullptr) 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 |
