From ac576a2398ba4cda3d7425464333bb8d662a8790 Mon Sep 17 00:00:00 2001 From: hater Date: Wed, 18 Oct 2023 22:05:25 -0400 Subject: [PATCH] fix stalling and rebase without ac pulls --- modules/mod-Forge/src/ForgeCache.cpp | 83 ++- modules/mod-Forge/src/ForgeCommonMessage.cpp | 36 +- .../src/ForgePlayerMessageHandler.cpp | 4 + .../src/TopicHandlers/GetAffixesHandler.cpp | 49 ++ .../src/TopicHandlers/LearnTalentHandler.cpp | 21 +- .../src/TopicHandlers/StartMythicHandler.cpp | 99 ++++ .../src/ForgedInstanceManager.cpp | 342 ------------- .../src/ForgedInstanceManager.h | 68 --- .../src/ForgedInstance_scripts.cpp | 281 ++--------- .../ChallengeMode/ChallengeModeCriteria.cpp | 86 ++++ .../ChallengeMode/ChallengeModeCriteria.h | 59 +++ .../game/ChallengeMode/ChallengeModeMgr.cpp | 104 ++++ .../game/ChallengeMode/ChallengeModeMgr.h | 17 + .../game/Entities/Creature/Creature.cpp | 19 +- src/server/game/Entities/Creature/Creature.h | 2 + .../game/Entities/Player/KillRewarder.cpp | 22 +- src/server/game/Entities/Player/Player.cpp | 44 ++ src/server/game/Entities/Player/Player.h | 6 + .../game/Entities/Player/PlayerStorage.cpp | 5 + src/server/game/Entities/Unit/Unit.cpp | 5 + src/server/game/Globals/ObjectMgr.cpp | 205 ++++++++ src/server/game/Globals/ObjectMgr.h | 137 +++++ src/server/game/Instances/InstanceSaveMgr.h | 1 + src/server/game/Instances/InstanceScript.cpp | 476 +++++++++++++++--- src/server/game/Instances/InstanceScript.h | 93 +++- src/server/game/Maps/Map.cpp | 20 +- src/server/game/Maps/ZoneScript.h | 5 + .../game/Spells/Auras/SpellAuraEffects.cpp | 50 +- .../game/Spells/Auras/SpellAuraEffects.h | 2 +- src/server/game/World/World.cpp | 4 + src/server/scripts/Spells/spell_druid.cpp | 9 +- src/server/scripts/Spells/spell_generic.cpp | 13 +- src/server/scripts/Spells/spell_hunter.cpp | 3 +- src/server/scripts/Spells/spell_mage.cpp | 6 +- src/server/scripts/Spells/spell_priest.cpp | 3 +- src/server/scripts/Spells/spell_shaman.cpp | 5 +- src/server/shared/SharedDefines.h | 13 +- 37 files changed, 1550 insertions(+), 847 deletions(-) create mode 100644 modules/mod-Forge/src/TopicHandlers/GetAffixesHandler.cpp create mode 100644 modules/mod-Forge/src/TopicHandlers/StartMythicHandler.cpp delete mode 100644 modules/mod-ForgedInstance/src/ForgedInstanceManager.cpp delete mode 100644 modules/mod-ForgedInstance/src/ForgedInstanceManager.h create mode 100644 src/server/game/ChallengeMode/ChallengeModeCriteria.cpp create mode 100644 src/server/game/ChallengeMode/ChallengeModeCriteria.h create mode 100644 src/server/game/ChallengeMode/ChallengeModeMgr.cpp create mode 100644 src/server/game/ChallengeMode/ChallengeModeMgr.h diff --git a/modules/mod-Forge/src/ForgeCache.cpp b/modules/mod-Forge/src/ForgeCache.cpp index 6a296b6b412ef6..ae620f2b5f64a6 100644 --- a/modules/mod-Forge/src/ForgeCache.cpp +++ b/modules/mod-Forge/src/ForgeCache.cpp @@ -25,6 +25,13 @@ enum CharacterPointType LEVEL_10_TAB = 7 }; +enum NodeType +{ + AURA = 0, + SPELL = 1, + CHOICE = 2 +}; + enum ForgeSettingIndex { FORGE_SETTING_SPEC_SLOTS = 0 @@ -92,6 +99,13 @@ struct ForgeCharacterSpec std::unordered_map> Talents; // tabId std::unordered_map PointsSpent; + std::unordered_map ChoiceNodesChosen; +}; + +struct ForgeTalentChoice +{ + uint32 spellId; + bool active; }; struct ForgeTalent @@ -104,10 +118,11 @@ struct ForgeTalent uint16 TabPointReq; uint8 RequiredLevel; CharacterPointType TalentType; + NodeType nodeType; uint8 NumberOfRanks; PereqReqirementType PreReqType; std::list Prereqs; - std::list ExclusiveWith; + std::list Choices; std::list UnlearnSpells; // rank number, spellId std::unordered_map Ranks; @@ -853,6 +868,9 @@ class ForgeCache : public DatabaseScript // tabId std::unordered_map TalentTabs; + + // choiceNodeId is the id of the node in forge_talents + std::unordered_map> _choiceNodes; private: std::unordered_map CharacterActiveSpecs; std::unordered_map CONFIG; @@ -919,16 +937,29 @@ class ForgeCache : public DatabaseScript AddTalentTrees(); AddTalentsToTrees(); AddTalentPrereqs(); - AddTalentExclusiveness(); + AddTalentChoiceNodes(); AddTalentRanks(); AddTalentUnlearn(); AddCharacterSpecs(); AddTalentSpent(); AddCharacterTalents(); + AddCharacterChoiceNodes(); + LOG_INFO("server.load", "Loading characters points..."); AddCharacterPointsFromDB(); AddCharacterClassSpecs(); AddCharacterXmogSets(); + + LOG_INFO("server.load", "Loading m+ difficulty multipliers..."); + sObjectMgr->LoadInstanceDifficultyMultiplier(); + LOG_INFO("server.load", "Loading m+ difficulty level scales..."); + sObjectMgr->LoadMythicLevelScale(); + LOG_INFO("server.load", "Loading m+ minion values..."); + sObjectMgr->LoadMythicMinionValue(); + LOG_INFO("server.load", "Loading m+ keys..."); + sObjectMgr->LoadMythicDungeonKeyMap(); + LOG_INFO("server.load", "Loading m+ affixes..."); + sObjectMgr->LoadMythicAffixes(); } void GetCharacters() @@ -1194,6 +1225,7 @@ class ForgeCache : public DatabaseScript newTalent->NumberOfRanks = talentFields[7].Get(); newTalent->PreReqType = (PereqReqirementType)talentFields[8].Get(); newTalent->TabPointReq = talentFields[9].Get(); + newTalent->nodeType = NodeType(talentFields[10].Get()); auto tabItt = TalentTabs.find(newTalent->TalentTabId); @@ -1237,9 +1269,11 @@ class ForgeCache : public DatabaseScript } while (preReqTalents->NextRow()); } - void AddTalentExclusiveness() + void AddTalentChoiceNodes() { - QueryResult exclTalents = WorldDatabase.Query("SELECT * FROM forge_talent_exclusive"); + QueryResult exclTalents = WorldDatabase.Query("SELECT * FROM forge_talent_choice_nodes"); + + _choiceNodes.clear(); if (!exclTalents) return; @@ -1247,23 +1281,54 @@ class ForgeCache : public DatabaseScript do { Field* talentFields = exclTalents->Fetch(); - uint32 spellId = talentFields[0].Get(); + uint32 choiceNodeId = talentFields[0].Get(); uint32 talentTabId = talentFields[1].Get(); - uint32 exclusiveSpellId = talentFields[2].Get(); + uint32 spellChoice = talentFields[2].Get(); - ForgeTalent* lt = TalentTabs[talentTabId]->Talents[spellId]; + ForgeTalentChoice* choice = new ForgeTalentChoice(); + choice->active = false; + choice->spellId = spellChoice; + + _choiceNodes[choiceNodeId].push_back(spellChoice); + + ForgeTalent* lt = TalentTabs[talentTabId]->Talents[choiceNodeId]; if (lt != nullptr) { - lt->ExclusiveWith.push_back(exclusiveSpellId); + lt->Choices.push_back(choice); } else { - LOG_ERROR("FORGE.ForgeCache", "Error loading AddTalentExclusiveness, invaild exclusiveSpellId id: " + std::to_string(exclusiveSpellId)); + LOG_ERROR("FORGE.ForgeCache", "Error loading AddTalentChoiceNodes, invaild choiceNodeId id: " + std::to_string(choiceNodeId)); } } while (exclTalents->NextRow()); } + void AddCharacterChoiceNodes() { + QueryResult choiceQuery = CharacterDatabase.Query("SELECT * FROM forge_character_node_choices"); + + if (!choiceQuery) + return; + + do + { + Field* fields = choiceQuery->Fetch(); + uint32 id = fields[0].Get(); + ObjectGuid characterGuid = ObjectGuid::Create(id); + uint32 specId = fields[1].Get(); + uint32 TabId = fields[2].Get(); + uint32 nodeId = fields[3].Get(); + uint32 chosenSpell = fields[4].Get(); + + ForgeTalent* ft = TalentTabs[TabId]->Talents[nodeId]; + if (ft->nodeType == NodeType::CHOICE) { + ForgeCharacterSpec* spec = CharacterSpecs[characterGuid][specId]; + spec->ChoiceNodesChosen[nodeId] = chosenSpell; + } + + } while (choiceQuery->NextRow()); + } + void AddTalentRanks() { QueryResult talentRanks = WorldDatabase.Query("SELECT * FROM forge_talent_ranks"); diff --git a/modules/mod-Forge/src/ForgeCommonMessage.cpp b/modules/mod-Forge/src/ForgeCommonMessage.cpp index bcf77d3f37215f..9aea1f7da5d251 100644 --- a/modules/mod-Forge/src/ForgeCommonMessage.cpp +++ b/modules/mod-Forge/src/ForgeCommonMessage.cpp @@ -94,14 +94,15 @@ std::string ForgeCommonMessage::BuildTree(Player* player, CharacterPointType poi j = 0; - for (auto& preReq : talentKvp.second->ExclusiveWith) + for (auto& preReq : talentKvp.second->Ranks) { - std::string reqDel = "!"; + std::string reqDel = "%"; if (j == 0) reqDel = ""; - msg = msg + reqDel + std::to_string(preReq); + msg = msg + reqDel + std::to_string(preReq.first) + "~" + + std::to_string(preReq.second); j++; } @@ -110,32 +111,29 @@ std::string ForgeCommonMessage::BuildTree(Player* player, CharacterPointType poi j = 0; - for (auto& preReq : talentKvp.second->Ranks) + for (auto& preReq : talentKvp.second->UnlearnSpells) { - std::string reqDel = "%"; + std::string reqDel = "`"; if (j == 0) reqDel = ""; - msg = msg + reqDel + std::to_string(preReq.first) + "~" + - std::to_string(preReq.second); + msg = msg + reqDel + std::to_string(preReq); j++; } - msg = msg + "&"; // delimit the field + msg = msg + "&" + std::to_string(talentKvp.second->nodeType) + "&"; - j = 0; - - for (auto& preReq : talentKvp.second->UnlearnSpells) + // TODO: SET THIS TO BE CHOICE NODE + for (auto& choice : talentKvp.second->Choices) { - std::string reqDel = "`"; + std::string choiceDel = "!"; if (j == 0) - reqDel = ""; - - msg = msg + reqDel + std::to_string(preReq); + choiceDel = ""; + msg = msg + choiceDel + std::to_string(choice->spellId); j++; } @@ -313,14 +311,6 @@ bool ForgeCommonMessage::CanLearnTalent(Player* player, uint32 tabId, uint32 spe } } - for (auto& exclu : ft->ExclusiveWith) - { - auto typeItt = skillTabs.find(exclu); - - if (typeItt != skillTabs.end() && (typeItt->second->CurrentRank > 0)) - return false; - } - return true; } diff --git a/modules/mod-Forge/src/ForgePlayerMessageHandler.cpp b/modules/mod-Forge/src/ForgePlayerMessageHandler.cpp index 186bb77cdc8ac3..eada16cfcd85e1 100644 --- a/modules/mod-Forge/src/ForgePlayerMessageHandler.cpp +++ b/modules/mod-Forge/src/ForgePlayerMessageHandler.cpp @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include // Add player scripts @@ -282,6 +284,8 @@ void AddForgePlayerMessageHandler() sTopicRouter->AddHandler(new SaveTransmogSetHandler(cache, cm)); sTopicRouter->AddHandler(new GetTransmogSetsHandler(cache, cm)); sTopicRouter->AddHandler(new GetTransmogHandler(cache, cm)); + sTopicRouter->AddHandler(new StartMythicHandler(cache, cm)); + sTopicRouter->AddHandler(new GetAffixesHandler(cache, cm)); new UseSkillBook(); new ForgeCacheCommands(); diff --git a/modules/mod-Forge/src/TopicHandlers/GetAffixesHandler.cpp b/modules/mod-Forge/src/TopicHandlers/GetAffixesHandler.cpp new file mode 100644 index 00000000000000..6259adf7f7ca72 --- /dev/null +++ b/modules/mod-Forge/src/TopicHandlers/GetAffixesHandler.cpp @@ -0,0 +1,49 @@ +#include "ScriptMgr.h" +#include "Player.h" +#include "Config.h" +#include "Chat.h" +#include "WorldPacket.h" +#include "TopicRouter.h" +#include "ForgeCommonMessage.h" +#include + +class GetAffixesHandler : public ForgeTopicHandler +{ +public: + GetAffixesHandler(ForgeCache* cache, ForgeCommonMessage* messageCommon) : ForgeTopicHandler(ForgeTopic::MYTHIC_GET_AFFIXES_LIST) + { + fc = cache; + mc = messageCommon; + } + + void HandleMessage(ForgeAddonMessage& iam) override + { + auto affixes = sObjectMgr->GetForgeAffixesByTierMap(); + + std::string out = ""; + std::string delim = ""; + + for (auto tier : affixes) { + if (tier.first > 1) + delim = ";"; + + out += delim + std::to_string(tier.first) + "K"; + + std::string sep = ""; + for (int i = 0; i < tier.second.size(); i++) { + if (i > 0) + sep = "*"; + + out += sep + std::to_string(tier.second[i]); + } + } + + iam.player->SendForgeUIMsg(ForgeTopic::MYTHIC_GET_AFFIXES_LIST, out); + } + + +private: + + ForgeCache* fc; + ForgeCommonMessage* mc; +}; diff --git a/modules/mod-Forge/src/TopicHandlers/LearnTalentHandler.cpp b/modules/mod-Forge/src/TopicHandlers/LearnTalentHandler.cpp index 8ab95f5dd86285..4a5870d469e2be 100644 --- a/modules/mod-Forge/src/TopicHandlers/LearnTalentHandler.cpp +++ b/modules/mod-Forge/src/TopicHandlers/LearnTalentHandler.cpp @@ -184,16 +184,17 @@ class LearnTalentHandler : public ForgeTopicHandler } } - for (auto& exclu : ft->ExclusiveWith) - { - auto typeItt = skillTabs.find(exclu); - - if (typeItt != skillTabs.end() && typeItt->second->CurrentRank > 0) - { - RequirementsNotMet(iam); - return; - } - } + // TODO: FIX + //for (auto& exclu : ft->Choices) + //{ + // auto typeItt = skillTabs.find(exclu); + + // if (typeItt != skillTabs.end() && typeItt->second->CurrentRank > 0) + // { + // RequirementsNotMet(iam); + // return; + // } + //} auto spellItter = skillTabs.find(ft->SpellId); ForgeCharacterTalent* ct = new ForgeCharacterTalent(); diff --git a/modules/mod-Forge/src/TopicHandlers/StartMythicHandler.cpp b/modules/mod-Forge/src/TopicHandlers/StartMythicHandler.cpp new file mode 100644 index 00000000000000..c61787db4788f5 --- /dev/null +++ b/modules/mod-Forge/src/TopicHandlers/StartMythicHandler.cpp @@ -0,0 +1,99 @@ +#include "ScriptMgr.h" +#include "Player.h" +#include "Config.h" +#include "Chat.h" +#include "WorldPacket.h" +#include "TopicRouter.h" +#include "ForgeCommonMessage.h" +#include +#include + +const auto GAMEOBJECT_TYPE_KEYSTONE_RECEPTACLE = 1000000; + +class StartMythicHandler : public ForgeTopicHandler +{ +public: + StartMythicHandler(ForgeCache* cache, ForgeCommonMessage* messageCommon) : ForgeTopicHandler(ForgeTopic::MYTHIC_SET_AFFIXES_AND_START) + { + fc = cache; + mc = messageCommon; + } + + void HandleMessage(ForgeAddonMessage& iam) override + { + if (iam.message == "ping") { + if (auto script = iam.player->GetInstanceScript()) + if (script->IsChallengeModeStarted()) + script->SendChallengeModeStart(iam.player); + + return; + } + else { + + if (!iam.player->mythicStartCheck) + return; + + if (iam.message.empty()) + return; + + std::vector results; + boost::algorithm::split(results, iam.message, boost::is_any_of("~")); + + auto affixesRcv = results.size(); + + if (results.empty() || !fc->isNumber(results[0])) + return; + else if (affixesRcv > 1) + if (!fc->isNumber(results[1])) + return; + else if (affixesRcv > 2) + if (!fc->isNumber(results[2])) + return; + + auto map = iam.player->GetMap()->GetId(); + auto keyMap = sObjectMgr->GetDungeonKeyMap(); + auto item = keyMap[map]; + + if (!iam.player->HasItemCount(item->itemId)) + return; + + auto difficulty = -(iam.player->GetItemByEntry(item->itemId)->GetItemRandomPropertyId() + 100); + auto affixesIntended = GetAffixCountForLevel(difficulty); + + if (affixesRcv != affixesIntended) + return; + + uint32 tierOne = static_cast(std::stoul(results[0])); + if (!sObjectMgr->AffixExistsAndMatchesTier(tierOne, 1)) + return; + + uint32 tierTwo = 0; + uint32 tierThree = 0; + if (affixesIntended > 1) { + tierTwo = static_cast(std::stoul(results[1])); + if (!sObjectMgr->AffixExistsAndMatchesTier(tierTwo, 2)) + return; + else if (affixesIntended > 2) { + tierThree = static_cast(std::stoul(results[2])); + if (!sObjectMgr->AffixExistsAndMatchesTier(tierThree, 3)) + return; + } + } + + iam.player->GetInstanceScript()->StartChallengeMode(iam.player, item, difficulty, tierOne, tierTwo, tierThree); + } + } + +private: + uint8 GetAffixCountForLevel(uint8 difficulty) { + auto out = 1; + if (difficulty > 3) + out = 2; + if (difficulty > 6) + out = 3; + return out; + } + + ForgeCache* fc; + ForgeCommonMessage* mc; +}; diff --git a/modules/mod-ForgedInstance/src/ForgedInstanceManager.cpp b/modules/mod-ForgedInstance/src/ForgedInstanceManager.cpp deleted file mode 100644 index e8a58d3bf759d3..00000000000000 --- a/modules/mod-ForgedInstance/src/ForgedInstanceManager.cpp +++ /dev/null @@ -1,342 +0,0 @@ -#include "ForgedInstanceManager.h" - -std::map /* possibleLoot*/> ForgedInstanceManager::loots = {}; -std::vector ForgedInstanceManager::completions = {}; -std::map ForgedInstanceManager::requierements = {}; -std::map ForgedInstanceManager::encounters = {}; -std::map ForgedInstanceManager::creaturesModifiers = {}; - -void ForgedInstanceManager::CreateLoot(uint32 bossId, Creature* boss) -{ - if (!boss) - return; - - uint32 instanceId = boss->GetMap()->GetInstanceId(); - - if (!IsInForgedInstance(instanceId)) - return; - - - ForgedInstance ForgedInstance = GetForgedInstanceEncounter(instanceId); - - if (ForgedInstance.isRaid) { - boss->loot.items.clear(); - std::vector loots = GenerateForgedInstanceLoot(bossId, boss->GetLootMode()); - for (auto& item : loots) { - boss->loot.AddItem(item); - } - } - RewardEmblemsPlayers(boss->GetMap()->GetPlayers(), ForgedInstance.isRaid); -} - -void ForgedInstanceManager::CreateLoot(uint32 bossId, GameObject* go) -{ - if (!go) - return; - - uint32 instanceId = go->GetMap()->GetInstanceId(); - - if (!IsInForgedInstance(instanceId)) - return; - - ForgedInstance ForgedInstance = GetForgedInstanceEncounter(instanceId); - - if (ForgedInstance.isRaid) { - go->loot.clear(); - std::vector loots = GenerateForgedInstanceLoot(bossId, go->GetLootMode()); - for (auto& item : loots) { - go->loot.AddItem(item); - } - } - RewardEmblemsPlayers(go->GetMap()->GetPlayers(), ForgedInstance.isRaid); -} - -void ForgedInstanceManager::StartForgedInstance(Player* player, uint8 level) -{ - Group* group = player->GetGroup(); - - if (!group) { - ChatHandler(player->GetSession()).PSendSysMessage("You need to be in group to start this dungeon on Forged Difficulty."); - return; - } - - if (group->GetLeaderGUID() != player->GetGUID()) { - ChatHandler(player->GetSession()).PSendSysMessage("You need to be the group leader to start this dungeon on Forged Difficulty."); - return; - } - - Map* map = player->GetMap(); - - Group::MemberSlotList const& members = group->GetMemberSlots(); - uint32 instanceId = map->GetInstanceId(); - std::vector playerGuidsDoNotMeetRequierement = {}; - - /* for (auto itr = members.begin(); itr != members.end(); ++itr) { - Player* GroupMember = ObjectAccessor::GetPlayer(map, itr->guid); - if (!GroupMember) { - ChatHandler(player->GetSession()).PSendSysMessage("Someone in your group is disconnected."); - return; - } - } - - for (auto itr = members.begin(); itr != members.end(); ++itr) { - if (Player* GroupMember = ObjectAccessor::GetPlayer(map, itr->guid)) { - if (!CanDoForgedInstance(GroupMember, map->GetId())) - playerGuidsDoNotMeetRequierement.push_back(GroupMember->GetGUID().GetCounter()); - } - } - - if (playerGuidsDoNotMeetRequierement.size() > 0) { - std::string names = ""; - for (auto guid : playerGuidsDoNotMeetRequierement) { - if (Player* GroupMember = ObjectAccessor::GetPlayer(map, ObjectGuid(guid))) - names += GroupMember->GetName() + (playerGuidsDoNotMeetRequierement.size() > 1 ? ", " : "."); - } - - ChatHandler(player->GetSession()).PSendSysMessage("Theses player(s) in your group do not meet the requirements to start : %s", names); - return; - } */ - - for (auto itr = members.begin(); itr != members.end(); ++itr) { - Player* GroupMember = ObjectAccessor::FindPlayer(itr->guid); - if (GroupMember) { - ChatHandler(GroupMember->GetSession()).PSendSysMessage("Your group leader started %s on ForgedInstance difficulty.", map->GetMapName()); - } - } - - bool isRaid = map->IsRaid(); - ForgedInstanceManager::encounters[map->GetInstanceId()] = { map, group->GetGUID().GetCounter(), player, isRaid, {}, level}; -} - -void ForgedInstanceManager::AddKillCreditBoss(Player* player, uint32 bossId) -{ - auto completion = std::find_if(ForgedInstanceManager::completions.begin(), ForgedInstanceManager::completions.end(), [&](Completion const& completion) - { - return completion.bossId == bossId; - }); - - if (completion == ForgedInstanceManager::completions.end()) { - completions.push_back({ player->GetGUID().GetCounter(), bossId }); - CharacterDatabase.Query("INSERT ForgedInstance_completions (guid, bossId) VALUES ({}, {})", player->GetGUID().GetCounter(), bossId); - } -} - -void ForgedInstanceManager::RemoveGuidCalculated(Creature* creature) -{ - if (!IsInForgedInstance(creature->GetInstanceId())) - return; - - ForgedInstanceManager::encounters[creature->GetInstanceId()].creatureGuids.erase(std::remove( - ForgedInstanceManager::encounters[creature->GetInstanceId()].creatureGuids.begin(), - ForgedInstanceManager::encounters[creature->GetInstanceId()].creatureGuids.end(), creature->GetGUID().GetCounter()), - ForgedInstanceManager::encounters[creature->GetInstanceId()].creatureGuids.end()); -} - -void ForgedInstanceManager::PreloadAllLoot() -{ - QueryResult result = WorldDatabase.Query("SELECT * FROM ForgedInstance_loot"); - - if (result) { - do - { - Field* fields = result->Fetch(); - uint32 bossId = fields[0].Get(); - uint32 itemId = fields[1].Get(); - uint32 quantity = fields[2].Get(); - ForgedInstanceManager::loots[bossId].push_back({ itemId, quantity }); - } while (result->NextRow()); - } -} - -void ForgedInstanceManager::PreloadAllCompletions() -{ - QueryResult result = CharacterDatabase.Query("SELECT * FROM ForgedInstance_completions"); - - if (result) { - do - { - Field* fields = result->Fetch(); - uint64 guid = fields[0].Get(); - uint32 bossId = fields[1].Get(); - ForgedInstanceManager::completions.push_back({ guid, bossId }); - } while (result->NextRow()); - } -} - -void ForgedInstanceManager::PreloadAllRequierements() -{ - QueryResult result = WorldDatabase.Query("SELECT * FROM ForgedInstance_requierements"); - - if (result) { - do - { - Field* fields = result->Fetch(); - uint32 mapId = fields[0].Get(); - uint32 bossId = fields[1].Get(); - ForgedInstanceManager::requierements[mapId] = bossId; - } while (result->NextRow()); - } -} - -void ForgedInstanceManager::PreloadAllCreaturesIds() -{ - QueryResult result = WorldDatabase.Query("SELECT * FROM ForgedInstance_difficulty"); - if (result) { - do - { - Field* fields = result->Fetch(); - uint32 mapId = fields[0].Get(); - float spellMultiplier = fields[1].Get(); - float meleeMultiplier = fields[2].Get(); - float healthMultiplier = fields[3].Get(); - float healthCoefficient = fields[4].Get(); - ForgedInstanceManager::creaturesModifiers[mapId] = { spellMultiplier, meleeMultiplier, healthMultiplier, healthCoefficient }; - } while (result->NextRow()); - } -} - -bool ForgedInstanceManager::CanDoForgedInstance(Player* player, uint32 mapId) -{ - auto it = ForgedInstanceManager::requierements.find(mapId); - if (it == ForgedInstanceManager::requierements.end()) - return false; - - uint32 bossId = it->second; - - auto completion = std::find_if(ForgedInstanceManager::completions.begin(), ForgedInstanceManager::completions.end(), [&](Completion const& completion) - { - return completion.bossId == bossId; - }); - - if (completion == ForgedInstanceManager::completions.end()) - return false; - - return true; -} - -ForgedInstanceManager::ForgedInstance ForgedInstanceManager::GetForgedInstanceEncounter(uint32 instanceId) -{ - auto itr = ForgedInstanceManager::encounters.find(instanceId); - if (itr != ForgedInstanceManager::encounters.end()) - return itr->second; - - return {}; -} - -void ForgedInstanceManager::ResetForgedInstance(Group* group, bool remove) -{ - for (auto it = ForgedInstanceManager::encounters.begin(); it != ForgedInstanceManager::encounters.end(); ++it) - { - if (it->second.groupGuid == group->GetGUID().GetCounter()) { - it->second.creatureGuids.clear(); - if (remove) { - ForgedInstanceManager::encounters.erase(it); - return; - } - } - } -} - -void ForgedInstanceManager::AddCreatureCalculated(Map* map, uint64 guid) -{ - ForgedInstanceManager::encounters[map->GetInstanceId()].creatureGuids.push_back(guid); -} - -void ForgedInstanceManager::RewardEmblemsPlayers(Map::PlayerList const& playerList, bool isRaid) -{ - if (playerList.IsEmpty()) - return; - - for (Map::PlayerList::const_iterator playerIteration = playerList.begin(); playerIteration != playerList.end(); ++playerIteration) - if (playerIteration->GetSource()) { - Player* p = playerIteration->GetSource(); - Group* group = p->GetGroup(); - - if (!group) - return; - - uint32 emblemId = sConfigMgr->GetOption("RaidEmblemId", 40753); - - Group::MemberSlotList const& members = group->GetMemberSlots(); - for (auto itr = members.begin(); itr != members.end(); ++itr) { - Player* GroupMember = ObjectAccessor::FindPlayer(itr->guid); - if (GroupMember) { - GroupMember->AddItem(emblemId, isRaid ? 10 : 3); - GroupMember->AddItem(400506, isRaid ? 3 : 1); - } - } - break; - } - - - -} - -std::vector ForgedInstanceManager::GenerateForgedInstanceLoot(uint32 bossId, uint8 lootMode) -{ - std::vector loots = {}; - std::vector possibleLoots = {}; - - auto it = ForgedInstanceManager::loots.find(bossId); - if (it != ForgedInstanceManager::loots.end()) - possibleLoots = it->second; - - if (possibleLoots.size() == 0) - return loots; - - uint32 itemCount = 5; - - for (size_t i = 1; i <= itemCount; i++) - { - int random = rand() % possibleLoots.size(); - uint32 itemId = possibleLoots[random].ItemId; - if (itemId) { - auto founded = std::find_if(possibleLoots.begin(), possibleLoots.end(), [&](PossibleLoot const& loot) - { - return loot.ItemId == itemId; - }); - possibleLoots.erase(founded); - LootStoreItem storeItem = LootStoreItem(itemId, 0, 100, 0, lootMode, 0, 1, 1); - loots.push_back(storeItem); - } - } - return loots; -} - -Modifier ForgedInstanceManager::GetModifier(uint32 mapId) -{ - auto it = ForgedInstanceManager::creaturesModifiers.find(mapId); - if (it != ForgedInstanceManager::creaturesModifiers.end()) - return it->second; - - return {}; -} - -bool ForgedInstanceManager::IsInForgedInstance(uint32 instanceId) -{ - auto it = ForgedInstanceManager::encounters.find(instanceId); - if (it != ForgedInstanceManager::encounters.end()) - return true; - - return false; -} - -bool ForgedInstanceManager::IsInForgedInstance(Player* player) -{ - const uint32 instanceId = player->GetMap()->GetInstanceId(); - auto itr = ForgedInstanceManager::encounters.find(player->GetMap()->GetInstanceId()); - if (itr != ForgedInstanceManager::encounters.end()) - return true; - return false; -} - -bool ForgedInstanceManager::creatureAlreadyCalculated(uint32 instanceId, uint64 guid) -{ - auto itr = ForgedInstanceManager::encounters.find(instanceId); - if (itr != ForgedInstanceManager::encounters.end()) - if (std::find(ForgedInstanceManager::encounters[instanceId].creatureGuids.begin(), ForgedInstanceManager::encounters[instanceId].creatureGuids.end(), guid) - != ForgedInstanceManager::encounters[instanceId].creatureGuids.end()) - return true; - - return false; -} diff --git a/modules/mod-ForgedInstance/src/ForgedInstanceManager.h b/modules/mod-ForgedInstance/src/ForgedInstanceManager.h deleted file mode 100644 index cf69f6f90169b8..00000000000000 --- a/modules/mod-ForgedInstance/src/ForgedInstanceManager.h +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once -#include "Player.h" -#include "DatabaseEnv.h" -#include "Log.h" -#include "Chat.h" -#include "Group.h" -#include "Config.h" - - -struct PossibleLoot { - uint32 ItemId; - uint32 Quantity; -}; - -struct Completion { - uint64 guid; - uint32 bossId; -}; - -struct Modifier { - float spellMultiplier; - float meleeMultiplier; - float healthMultiplier; - float healthCofficient; -}; - - -class ForgedInstanceManager { -public: - - struct ForgedInstance { - Map* map; - uint64 groupGuid; - Player* leader; - bool isRaid; - std::vector creatureGuids; - uint8 level; - }; - static void CreateLoot(uint32 bossId, Creature* boss); - static void CreateLoot(uint32 bossId, GameObject* go); - static void StartForgedInstance(Player* player, uint8 level); - static void AddKillCreditBoss(Player* player, uint32 bossId); - static void RemoveGuidCalculated(Creature* creature); - static bool IsInForgedInstance(uint32 instanceId); - static bool IsInForgedInstance(Player* player); - static bool creatureAlreadyCalculated(uint32 instanceId, uint64 guid); - static Modifier GetModifier(uint32 creatureId); - static void PreloadAllLoot(); - static void PreloadAllCompletions(); - static void PreloadAllRequierements(); - static void PreloadAllCreaturesIds(); - static ForgedInstance GetForgedInstanceEncounter(uint32 instanceId); - static void ResetForgedInstance(Group* group, bool remove); - static void AddCreatureCalculated(Map* map, uint64 guid); - -private: - - static void RewardEmblemsPlayers(Map::PlayerList const& playerList, bool isRaid); - static std::map /* possibleLoot*/> loots; - static std::vector completions; - static std::map requierements; - static std::map creaturesModifiers; - static std::map encounters; - - static bool CanDoForgedInstance(Player* player, uint32 mapId); - static std::vector GenerateForgedInstanceLoot(uint32 bossId, uint8 lootMode); - -}; diff --git a/modules/mod-ForgedInstance/src/ForgedInstance_scripts.cpp b/modules/mod-ForgedInstance/src/ForgedInstance_scripts.cpp index 09f5f100d12691..3a13d4b3738819 100644 --- a/modules/mod-ForgedInstance/src/ForgedInstance_scripts.cpp +++ b/modules/mod-ForgedInstance/src/ForgedInstance_scripts.cpp @@ -6,7 +6,6 @@ #include "Player.h" #include "Config.h" #include "Chat.h" -#include "ForgedInstanceManager.h" #include "ScriptedAI/ScriptedCreature.h" #include "GossipDef.h" #include "ScriptedGossip.h" @@ -14,271 +13,61 @@ #include "Chat.h" #include "ScriptMgr.h" -class go_ForgedInstance : public GameObjectScript +struct KeystoneRangeCheck : public BasicEvent { -public: - go_ForgedInstance() : GameObjectScript("go_ForgedInstance") {} - - bool OnGossipHello(Player* player, GameObject* go) override - { - Map* map = player->GetMap(); - std::string mapName = map->GetMapName(); - std::string gossip = "Start " + mapName + " on ForgedInstance difficulty."; - if (!ForgedInstanceManager::IsInForgedInstance(player)) { - AddGossipItemFor(player, 0, gossip, 0, 0); - SendGossipMenuFor(player, DEFAULT_GOSSIP_MESSAGE, go->GetGUID()); - } - return true; - } - - bool OnGossipSelect(Player* player, GameObject* go, uint32 sender, uint32 action) override - { - if (action == 0) - ForgedInstanceManager::StartForgedInstance(player, 0); - - CloseGossipMenuFor(player); - return true; - } -}; - -class AutoBalance_AllCreatureScript : public AllCreatureScript -{ -public: - AutoBalance_AllCreatureScript() - : AllCreatureScript("AutoBalance_AllCreatureScript") - { - } - - void Creature_SelectLevel(const CreatureTemplate* /*creatureTemplate*/, Creature* creature) override - { - ModifyCreatureAttributes(creature, true); - } - - void OnAllCreatureUpdate(Creature* creature, uint32 /*diff*/) override - { - ModifyCreatureAttributes(creature); - } - - void ModifyCreatureAttributes(Creature* creature, bool resetSelLevel = false) - { - if (!creature || !creature->GetMap()) - return; - - if ((creature->IsHunterPet() || creature->IsPet() || creature->IsSummon())) - return; - - if (!creature->GetMap()->IsDungeon()) - return; - - if (creature->GetMap()->IsBattleground()) - return; - - if (!creature->IsAlive()) - return; - - if (creature->IsInCombat()) - return; - - if (!ForgedInstanceManager::IsInForgedInstance(creature->GetMap()->GetInstanceId())) - return; - - CreatureBaseStats const* origCreatureStats = sObjectMgr->GetCreatureBaseStats(creature->getLevel(), creature->GetCreatureTemplate()->unit_class); - uint32 baseHealth = origCreatureStats->GenerateHealth(creature->GetCreatureTemplate()); - uint32 baseMana = origCreatureStats->GenerateMana(creature->GetCreatureTemplate()); - - if (creature->GetMaxHealth() == baseHealth) - ForgedInstanceManager::RemoveGuidCalculated(creature); - - if (ForgedInstanceManager::creatureAlreadyCalculated(creature->GetMap()->GetInstanceId(), creature->GetGUID().GetCounter())) - return; - - ForgedInstanceManager::ForgedInstance ForgedInstance = ForgedInstanceManager::GetForgedInstanceEncounter(creature->GetMap()->GetInstanceId()); - uint32 playersCounts = creature->GetMap()->GetPlayersCountExceptGMs() < 10 ? 10 : creature->GetMap()->GetPlayersCountExceptGMs(); - - float scaleWithPlayers = 0.0f; - float scaledHealth = 0.0f; - float scaledMana = 0.0f; - - Modifier modifier = ForgedInstanceManager::GetModifier(creature->GetMapId()); - - if (ForgedInstance.isRaid) { - scaleWithPlayers = modifier.healthCofficient * playersCounts; - scaledHealth = round((baseHealth) * scaleWithPlayers); - scaledMana = round((baseMana) * scaleWithPlayers); - } - else { - float multiplier = sConfigMgr->GetOption("HpDungeonMultiplier", 4.0f); - scaledHealth = round(((float)baseHealth * multiplier)); - scaledMana = round(((float)baseMana * multiplier)); - } - - creature->SetMaxHealth(scaledHealth); - creature->SetHealth(scaledHealth); - creature->SetMaxPower(POWER_MANA, scaledMana); - creature->SetPower(POWER_MANA, scaledMana); - ForgedInstanceManager::AddCreatureCalculated(creature->GetMap(), creature->GetGUID().GetCounter()); - }; -}; - - -class AutoBalance_UnitScript : public UnitScript -{ -public: - AutoBalance_UnitScript() - : UnitScript("AutoBalance_UnitScript", true) - { - } - - uint32 DealDamage(Unit* AttackerUnit, Unit* playerVictim, uint32 damage, DamageEffectType /*damagetype*/) override - { - if (playerVictim->IsControlledByPlayer()) - return damage; - - return _Modifer_DealDamage(playerVictim, AttackerUnit, damage); - } - - void ModifyMeleeDamage(Unit* target, Unit* attacker, uint32& damage) override + KeystoneRangeCheck(Player* player, GameObject* go) : guid(go->GetGUID()), player(player) { - if (target->IsControlledByPlayer()) - return; - - damage = _Modifer_DealDamage(target, attacker, damage); - } - - uint32 _Modifer_DealDamage(Unit* target, Unit* attacker, uint32 damage) - { - if (!attacker || !attacker->GetMap()) - return damage; - - if (attacker->GetMap()->IsBattleground()) - return damage; - - if (!ForgedInstanceManager::IsInForgedInstance(target->GetMap()->GetInstanceId())) - return damage; - - if (!attacker->ToCreature()) - return damage; - - if (!attacker->ToCreature()->isWorldBoss() && !attacker->ToCreature()->IsDungeonBoss()) - return damage; - - if (!attacker || attacker->GetTypeId() == TYPEID_PLAYER || !attacker->IsInWorld()) - return damage; - - ForgedInstanceManager::ForgedInstance ForgedInstance = ForgedInstanceManager::GetForgedInstanceEncounter(attacker->GetMap()->GetInstanceId()); - - float raidMultiplier = sConfigMgr->GetOption("RaidMultiplier", 1.5f); - float dungeonMultiplier = sConfigMgr->GetOption("DungeonMultiplier", 3.0f); - float damageMultiplier = ForgedInstance.isRaid ? raidMultiplier : dungeonMultiplier; // default in ForgedInstance - - Modifier modifier = ForgedInstanceManager::GetModifier(attacker->GetMapId()); - - if (modifier.meleeMultiplier) - damageMultiplier = modifier.meleeMultiplier; - - if ((attacker->IsHunterPet() || attacker->IsPet() || attacker->IsSummon()) && attacker->IsControlledByPlayer()) - return damage; - - return damage * damageMultiplier; - } -}; - - -class PS_ForgedInstance : public PlayerScript -{ -public: - PS_ForgedInstance() : PlayerScript("PS_ForgedInstance") { } - - void OnMapChanged(Player* player) { - - if (!player) - return; - - if (Group* group = player->GetGroup()) - ForgedInstanceManager::ResetForgedInstance(group, false); + ASSERT(player->mythicStartCheck == nullptr, "Only one KeystoneRangeCheck should be active at one time"); + player->mythicStartCheck = this; + player->m_Events.AddEvent(this, player->m_Events.CalculateTime(500)); } -}; - -class GS_ForgedInstance : public GroupScript -{ -public: - GS_ForgedInstance() : GroupScript("GS_ForgedInstance") { } - - void OnDisband(Group* group) { - - if (!group) - return; - - ForgedInstanceManager::ResetForgedInstance(group, true); - } -}; - - -class CS_ForgedInstance : public CommandScript -{ -public: - CS_ForgedInstance() : CommandScript("CS_ForgedInstance") { } - std::vector GetCommands() const + bool Execute(uint64, uint32) override { - static Acore::ChatCommands::ChatCommandTable commandTable = + GameObject const* go = ObjectAccessor::GetGameObject(*player, guid); + if (go && go->IsWithinDistInMap(player, go->GetCombatReach() + 5.0f)) { - { "reloadallForgedInstance", HandleResetModifier, SEC_ADMINISTRATOR, Acore::ChatCommands::Console::No }, - }; - return commandTable; - } + player->m_Events.AddEvent(this, player->m_Events.CalculateTime(500)); + return false; + } - static bool HandleResetModifier(ChatHandler* handler, const char* args) - { - ForgedInstanceManager::PreloadAllCompletions(); - ForgedInstanceManager::PreloadAllCreaturesIds(); - ForgedInstanceManager::PreloadAllLoot(); - ForgedInstanceManager::PreloadAllRequierements(); + player->mythicStartCheck = nullptr; + player->SendForgeUIMsg(ForgeTopic::MYTHIC_OPEN_WINDOW, "0"); return true; } -}; - -class US_ForgedInstance : public UnitScript -{ -public: - US_ForgedInstance() : UnitScript("CS_ForgedInstance") { } - void OnBossCompleted(Unit* unit, Creature* creature, GameObject* go, uint32 bossId) { - if (go) - ForgedInstanceManager::CreateLoot(bossId, go); - else - ForgedInstanceManager::CreateLoot(bossId, creature); - } - - void OnBossEnterCombat(Creature* creature, uint32 bossId) { - ForgedInstanceManager::RemoveGuidCalculated(creature); - } - + ObjectGuid guid; + Player* player; + uint32 lastNotSavedAlert = 0; }; -class WS_ForgedInstance : public WorldScript +class go_mythic_init : public GameObjectScript { public: - WS_ForgedInstance() : WorldScript("WS_ForgedInstance") { } + go_mythic_init() : GameObjectScript("go_mythic_init") {} - void OnAfterConfigLoad(bool reload) override + bool OnGossipHello(Player* player, GameObject* go) override { - ForgedInstanceManager::PreloadAllCompletions(); - ForgedInstanceManager::PreloadAllCreaturesIds(); - ForgedInstanceManager::PreloadAllLoot(); - ForgedInstanceManager::PreloadAllRequierements(); + WorldSession* session = player->GetSession(); + auto map = go->GetMap()->GetId(); + auto keyMap = sObjectMgr->GetDungeonKeyMap(); + + if (auto item = keyMap[map]) { + if (player->HasItemCount(item->itemId)) { + if (!player->mythicStartCheck) + new KeystoneRangeCheck(player, go); + + auto difficulty = -(player->GetItemByEntry(item->itemId)->GetItemRandomPropertyId() + 100); + player->SendForgeUIMsg(ForgeTopic::MYTHIC_OPEN_WINDOW, std::to_string(difficulty) + ";" + go->GetMap()->GetMapName()); + return true; + } + } + return true; } }; // Add all scripts in one void AddSC_ForgedInstance() { - //new PS_ForgedInstance(); - //new CS_ForgedInstance(); - //new GS_ForgedInstance(); - //new US_ForgedInstance(); - //new WS_ForgedInstance(); - //new go_ForgedInstance(); - //new AutoBalance_UnitScript(); - //new AutoBalance_AllCreatureScript(); + new go_mythic_init(); } diff --git a/src/server/game/ChallengeMode/ChallengeModeCriteria.cpp b/src/server/game/ChallengeMode/ChallengeModeCriteria.cpp new file mode 100644 index 00000000000000..55e765cd35f4d0 --- /dev/null +++ b/src/server/game/ChallengeMode/ChallengeModeCriteria.cpp @@ -0,0 +1,86 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * 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 . + */ + +#include "ChallengeModeCriteria.h" +#include "ChallengeModeMgr.h" +#include "DatabaseEnv.h" +#include "InstanceSaveMgr.h" +#include "InstanceScript.h" +#include "Log.h" +#include "Map.h" +#include "ObjectMgr.h" +#include "Player.h" + +enum CriteriaType { + BOSS = 0, + MINIONS = 1, + TIMER = 3 +}; + +ChallengeModeCriteria::ChallengeModeCriteria(Map* map) : _map(map) +{ + ASSERT(_map); + LoadInstanceData(); +} + +void ChallengeModeCriteria::LoadInstanceData() +{ + _criteria.clear(); + + QueryResult result = WorldDatabase.Query("SELECT * FROM `forge_mythic_criteria` where `instance` = {}", _map->GetId()); + + if (result) + { + do + { + Field* fields = result->Fetch(); + uint32 instance = fields[1].Get(); + uint32 type = fields[2].Get(); + uint32 value = fields[3].Get(); + + switch (type) { + case BOSS: + _criteria[value] = 0; + continue; + case MINIONS: + _maxMinionCount = value; + _criteria[0] = 0; + continue; + case TIMER: + + continue; + default: + continue; + } + } + while (result->NextRow()); + } +} + +void ChallengeModeCriteria::CompleteCriteria(Unit* victim) +{ + if (InstanceMap* iMap = const_cast(_map)->ToInstanceMap()) + { + if (InstanceScript* instance = iMap->GetInstanceScript()) + { + if (instance->IsChallengeModeStarted()) + { + instance->CompleteChallengeMode(victim->GetPosition()); + } + } + } +} diff --git a/src/server/game/ChallengeMode/ChallengeModeCriteria.h b/src/server/game/ChallengeMode/ChallengeModeCriteria.h new file mode 100644 index 00000000000000..9a7e927c59c890 --- /dev/null +++ b/src/server/game/ChallengeMode/ChallengeModeCriteria.h @@ -0,0 +1,59 @@ +#ifndef ChallengeModeCriteria_h__ +#define ChallengeModeCriteria_h__ + +#include "ObjectMgr.h" + +class Map; + +class ChallengeModeCriteria +{ + public: + ChallengeModeCriteria(Map* map); + + void SaveToDB(); + void LoadInstanceData(); + + bool CheckCriteria() { + return _criteria[0] >= _maxMinionCount && bossesCompleted(); + } + void CompleteCriteria(Unit*); + + void UpdateMinionCount(uint32 unit) + { + auto count = sObjectMgr->GetMythicMinionValue(_map->GetId(), unit); + _criteria[0] += count; + } + + void UpdateBossState(uint32 unit) + { + auto boss = _criteria.find(unit); + if (boss != _criteria.end()) { + _criteria[unit] = 1; + } + } + + Map* GetMap() const { return _map; } + + float _maxMinionCount; + std::unordered_map _criteria; + uint32 maxTimer; + // 0 key is minion count, others are boss id as key + protected: + Map* _map; + std::unordered_map _minionCountMap; + + private: + bool bossesCompleted() { + bool out = true; + for (auto boss : _criteria) { + if (boss.first > 0) { + out = boss.second == 1 && out; + if (!out) + return out; + } + } + return out; + } +}; + +#endif diff --git a/src/server/game/ChallengeMode/ChallengeModeMgr.cpp b/src/server/game/ChallengeMode/ChallengeModeMgr.cpp new file mode 100644 index 00000000000000..82807abac8fb0f --- /dev/null +++ b/src/server/game/ChallengeMode/ChallengeModeMgr.cpp @@ -0,0 +1,104 @@ +#include "ChallengeModeMgr.h" +#include "Containers.h" +#include "Item.h" + +ChallengeModeMgr* ChallengeModeMgr::instance() +{ + static ChallengeModeMgr instance; + return &instance; +} + +uint32 ChallengeModeMgr::GetRandomChallengeId(uint32 flags/* = 4*/) +{ + return sObjectMgr->GetRandomMythicKey(); +} + +std::vector ChallengeModeMgr::GetBonusListIdsForRewards(uint32 baseItemIlevel, uint8 challengeLevel) +{ +// if (challengeLevel < 2) + return {}; +// +// std::vector> bonusDescriptionByChallengeLevel = +// { +// { 3410, 5 }, // Mythic 2 +// { 3411, 5 }, // Mythic 3 +// { 3412, 10 }, // Mythic 4 +// { 3413, 15 }, // Mythic 5 +// { 3414, 20 }, // Mythic 6 +// { 3415, 20 }, // Mythic 7 +// { 3416, 25 }, // Mythic 8 +// { 3417, 25 }, // Mythic 9 +// { 3418, 30 }, // Mythic 10 +// { 3509, 35 }, // Mythic 11 +// { 3510, 40 }, // Mythic 12 +// { 3534, 45 }, // Mythic 13 +// { 3535, 50 }, // Mythic 14 +// { 3535, 55 }, // Mythic 15 +// }; +// +// const uint32 baseMythicIlevel = 885; +// std::pair bonusAndDeltaPair = bonusDescriptionByChallengeLevel[challengeLevel < 15 ? (challengeLevel - 2): 13]; +// return { bonusAndDeltaPair.first, (int32)sDB2Manager.GetItemBonusListForItemLevelDelta(baseMythicIlevel - baseItemIlevel + bonusAndDeltaPair.second) }; +} + +void ChallengeModeMgr::Reward(Player* player, uint8 challengeLevel) +{ + //if (!GetMapChallengeModeEntry(player->GetMapId())) + // return; + + //JournalInstanceEntry const* journalInstance = sDB2Manager.GetJournalInstanceByMapId(player->GetMapId()); + //if (!journalInstance) + // return; + + //auto encounters = sDB2Manager.GetJournalEncounterByJournalInstanceId(journalInstance->ID); + //if (!encounters) + // return; + + //std::vector items; + //for (auto encounter : *encounters) + // if (std::vector const* journalItems = sDB2Manager.GetJournalItemsByEncounter(encounter->ID)) + // items.insert(items.end(), journalItems->begin(), journalItems->end()); + + //if (items.empty()) + // return; + + //std::vector stuffLoots; + //for (JournalEncounterItemEntry const* journalEncounterItem : items) + //{ + // ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(journalEncounterItem->ItemID); + // if (!itemTemplate) + // continue; + + // if (!itemTemplate->IsUsableByLootSpecialization(player, false)) + // continue; + + // if (itemTemplate->GetInventoryType() != INVTYPE_NON_EQUIP) + // stuffLoots.push_back(itemTemplate); + //} + + //ItemTemplate const* randomStuffItem = Trinity::Containers::SelectRandomContainerElement(stuffLoots); + //if (!randomStuffItem) + // return; + + //uint32 itemId = randomStuffItem->GetId(); + //ItemPosCountVec dest; + //InventoryResult msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemId, 1); + //if (msg != EQUIP_ERR_OK) + //{ + // player->SendEquipError(msg, nullptr, nullptr, itemId); + // return; + //} + + //std::vector bonusListIds = GetBonusListIdsForRewards(randomStuffItem->GetBaseItemLevel(), challengeLevel); + //Item* pItem = player->StoreNewItem(dest, itemId, true, GenerateItemRandomBonusListId(itemId), GuidSet(), ItemContext(0), bonusListIds); + //player->SendNewItem(pItem, 1, true, false, true); + + //WorldPackets::Loot::DisplayToast displayToast; + //displayToast.EntityId = itemId; + //displayToast.ToastType = TOAST_ITEM; + //displayToast.Quantity = 1; + //displayToast.RandomPropertiesID = pItem->GetItemRandomBonusListId(); + //displayToast.ToastMethod = TOAST_METHOD_POPUP; + //displayToast.bonusListIDs = pItem->m_itemData->BonusListIDs; + //player->SendDirectMessage(displayToast.Write()); +} diff --git a/src/server/game/ChallengeMode/ChallengeModeMgr.h b/src/server/game/ChallengeMode/ChallengeModeMgr.h new file mode 100644 index 00000000000000..9663c1c75d7f43 --- /dev/null +++ b/src/server/game/ChallengeMode/ChallengeModeMgr.h @@ -0,0 +1,17 @@ +#ifndef CHALLENGEMODE_MGR_H +#define CHALLENGEMODE_MGR_H + +class ChallengeModeMgr +{ +public: + static ChallengeModeMgr* instance(); + + uint32 GetRandomChallengeId(uint32 flags = 4); + + std::vector GetBonusListIdsForRewards(uint32 baseItemIlevel, uint8 challengeLevel); + void Reward(Player* player, uint8 challengeLevel); +}; + +#define sChallengeModeMgr ChallengeModeMgr::instance() + +#endif diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 7d3ddd2c2f5fe3..faeacdf3f32dba 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -358,12 +358,21 @@ void Creature::RemoveCorpse(bool setSpawnTime, bool skipVisibility) if (IsAIEnabled) AI()->CorpseRemoved(respawnDelay); + bool mplus = false; + // hater: disable respawn in m+ + if (auto script = GetInstanceScript()) + if (script->IsChallengeModeStarted()) + mplus = true; + // Should get removed later, just keep "compatibility" with scripts - if (setSpawnTime) + if (setSpawnTime && !mplus) { m_respawnTime = GameTime::GetGameTime().count() + respawnDelay; //SaveRespawnTime(); } + else if (mplus) { + m_respawnTime = GameTime::GetGameTime().count() + WEEK; + } float x, y, z, o; GetRespawnPosition(x, y, z, &o); @@ -1795,6 +1804,14 @@ void Creature::LoadEquipment(int8 id, bool force /*= false*/) SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + i, einfo->ItemEntry[i]); } +void Creature::SetBaseHealth(uint64 health) +{ + SetMaxHealth(health); + SetModifierValue(UNIT_MOD_HEALTH, BASE_VALUE, (float)health); + SetCreateHealth((uint32)health); + SetFullHealth(); +} + bool Creature::hasQuest(uint32 quest_id) const { QuestRelationBounds qr = sObjectMgr->GetCreatureQuestRelationBounds(GetEntry()); diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 572c5c97d502f5..eb8fa2e6e2b114 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -304,6 +304,8 @@ class Creature : public Unit, public GridObject, public MovableMapObje void SetInCombatWithZone(); + void SetBaseHealth(uint64 health); + [[nodiscard]] bool hasQuest(uint32 quest_id) const override; [[nodiscard]] bool hasInvolvedQuest(uint32 quest_id) const override; diff --git a/src/server/game/Entities/Player/KillRewarder.cpp b/src/server/game/Entities/Player/KillRewarder.cpp index dfeb8ed6d61021..3551523bd3037c 100644 --- a/src/server/game/Entities/Player/KillRewarder.cpp +++ b/src/server/game/Entities/Player/KillRewarder.cpp @@ -285,7 +285,23 @@ void KillRewarder::Reward() // 5. Credit instance encounter. if (Creature* victim = _victim->ToCreature()) - if (victim->IsDungeonBoss()) - if (Map* map = _victim->FindMap()) - map->UpdateEncounterState(ENCOUNTER_CREDIT_KILL_CREATURE, _victim->GetEntry(), _victim); + if (Map* map = _victim->FindMap()) { + InstanceScript* script = victim->GetInstanceScript(); + if (auto criteria = script->GetCriteria()) { + if (victim->IsDungeonBoss()) { + map->UpdateEncounterState(ENCOUNTER_CREDIT_KILL_CREATURE, _victim->GetEntry(), _victim); + criteria->UpdateBossState(_victim->GetEntry()); + } + else + criteria->UpdateMinionCount(_victim->GetEntry()); + + if (criteria->CheckCriteria()) + criteria->CompleteCriteria(_victim); + else { + Map::PlayerList const& players = map->GetPlayers(); + for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) + itr->GetSource()->GetInstanceScript()->SendChallengeModeCriteria(itr->GetSource()); + } + } + } } diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index b91e74813d2215..59ba57fbabdac9 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -5118,6 +5118,14 @@ void Player::RepopAtGraveyard() SpawnCorpseBones(); } + if (auto script = GetInstanceScript()) + if (GetMap()->IsDungeon()) { + ResurrectPlayer(0.5f); + SpawnCorpseBones(); + script->DoNearTeleportPlayers(script->_challengeEntranceLoc); // TODO: lol + return; + } + GraveyardStruct const* ClosestGrave = nullptr; // Special handle for battleground maps @@ -15981,6 +15989,42 @@ bool Player::AddItem(uint32 itemId, uint32 count) return true; } +bool Player::AddChallengeKey(uint32 challengeId, uint32 challengeLevel/* = 2*/) +{ + uint32 itemId = challengeId; + + uint8 count = 1; + uint32 noSpaceForCount = 0; + ItemPosCountVec dest; + + bool existingKey = false; + auto keyMap = sObjectMgr->GetDungeonKeyMap(); + for (auto key : keyMap) + existingKey = existingKey || HasItemCount(key.second->itemId, 1, true); + + InventoryResult msg = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemId, count, &noSpaceForCount); + + if (msg != EQUIP_ERR_OK) + count -= noSpaceForCount; + + if (count == 0 || dest.empty() || existingKey) + { + /// @todo Send to mailbox if no space + ChatHandler(GetSession()).PSendSysMessage("You don't have any space in your bags."); + return false; + } + + Item* item = StoreNewItem(dest, itemId, true); + if (item) + { + item->SetItemRandomProperties(-(challengeLevel + 100)); + SendNewItem(item, count, true, false); + } + else + return false; + return true; +} + PetStable& Player::GetOrInitPetStable() { if (!m_petStable) diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index b8dde489614ca4..7511327ea79a2f 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1423,6 +1423,9 @@ class Player : public Unit, public GridObject bool AddItem(uint32 itemId, uint32 count); + // hater: m+ + bool AddChallengeKey(uint32 challengeId, uint32 challengeLevel/* = 2*/); + /*********************************************************/ /*** GOSSIP SYSTEM ***/ /*********************************************************/ @@ -2666,6 +2669,9 @@ class Player : public Unit, public GridObject PresetMapType presetMap; std::string GetDebugInfo() const override; + // hater: m+ + BasicEvent* mythicStartCheck = nullptr; + protected: // Gamemaster whisper whitelist WhisperListContainer WhisperList; diff --git a/src/server/game/Entities/Player/PlayerStorage.cpp b/src/server/game/Entities/Player/PlayerStorage.cpp index f538ff00d37f35..39efde4d7e523e 100644 --- a/src/server/game/Entities/Player/PlayerStorage.cpp +++ b/src/server/game/Entities/Player/PlayerStorage.cpp @@ -5699,6 +5699,11 @@ bool Player::isAllowedToLoot(Creature const* creature) if (HasPendingBind()) return false; + if (auto script = GetInstanceScript()) + if (script->IsChallengeModeStarted()) { + return false; + } + const Loot* loot = &creature->loot; if (loot->isLooted()) // nothing to loot or everything looted. return false; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index d12077fe43b93b..917592eb359d7a 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -14835,7 +14835,12 @@ void Unit::setDeathState(DeathState s, bool despawn) // players in instance don't have ZoneScript, but they have InstanceScript if (ZoneScript* zoneScript = GetZoneScript() ? GetZoneScript() : (ZoneScript*)GetInstanceScript()) + { zoneScript->OnUnitDeath(this); + + if (IsPlayer()) + zoneScript->OnPlayerDeath(ToPlayer()); + } } else if (s == JUST_RESPAWNED) { diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index f9695cb3ed0a24..2ad9c85b1059ca 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -9062,6 +9062,211 @@ void ObjectMgr::LoadTrainerSpell() LOG_INFO("server.loading", " "); } +void ObjectMgr::LoadWorldSafeLocs() +{ + uint32 oldMSTime = getMSTime(); + + // 0 1 2 3 4 5 + if (QueryResult result = WorldDatabase.Query("SELECT ID, MapID, LocX, LocY, LocZ, Facing FROM world_safe_locs")) + { + do + { + Field* fields = result->Fetch(); + uint32 id = fields[0].Get(); + WorldLocation loc(fields[1].Get(), fields[2].Get(), fields[3].Get(), fields[4].Get(), fields[5].Get()); + if (!MapMgr::IsValidMapCoord(loc)) + { + LOG_ERROR("sql.sql", "World location (ID: %u) has a invalid position MapID: %u %s, skipped", id, loc.GetMapId(), loc.ToString().c_str()); + continue; + } + + WorldSafeLocsEntry* worldSafeLocs = _worldSafeLocs[id]; + worldSafeLocs->ID = id; + worldSafeLocs->Loc.WorldRelocate(loc); + + } while (result->NextRow()); + + LOG_INFO("server.loading", ">> Loaded {} world locations %u ms", _worldSafeLocs.size(), GetMSTimeDiffToNow(oldMSTime)); + } + else + LOG_INFO("server.loading", ">> Loaded 0 world locations. DB table `world_safe_locs` is empty."); +} + +MapChallengeModeEntry* ObjectMgr::GetChallengeMode(uint32 id) const +{ + auto out = _cacheChallengeMode.find(id); + if (out != _cacheChallengeMode.end()) + return out->second; + else + return nullptr; +} + +WorldSafeLocsEntry* ObjectMgr::GetWorldSafeLoc(uint32 id) const +{ + auto out = _worldSafeLocs.find(id); + if (out != _worldSafeLocs.end()) + return out->second; + else + return nullptr; +} + +void ObjectMgr::LoadInstanceDifficultyMultiplier() +{ + uint32 oldMSTime = getMSTime(); + + _instanceDifficultyMultipliers.clear(); + + QueryResult result = WorldDatabase.Query("SELECT mapId, difficultyId, healthMultiplier, damageMultiplier FROM instance_difficulty_multiplier"); + + if (!result) + { + LOG_ERROR("sql.sql", ">> Loaded 0 instance difficulty multiplier. DB table `instance_difficulty_multiplier` is empty."); + LOG_INFO("server.loading", " "); + return; + } + + do + { + Field* fields = result->Fetch(); + + uint32 mapId = fields[0].Get(); + uint32 difficultyId = fields[1].Get(); + + InstanceDifficultyMultiplier* multipliers = new InstanceDifficultyMultiplier(); + multipliers->healthMultiplier = fields[2].Get(); + multipliers->damageMultiplier = fields[3].Get(); + _instanceDifficultyMultipliers[mapId][difficultyId] = multipliers; + } while (result->NextRow()); + + LOG_INFO("server.loading", ">> Loaded {} instance difficulty multipliers in {} ms", _instanceDifficultyMultipliers.size(), GetMSTimeDiffToNow(oldMSTime)); +} + +void ObjectMgr::LoadMythicLevelScale() +{ + uint32 oldMSTime = getMSTime(); + + _mythicLevelScales.clear(); + + QueryResult result = WorldDatabase.Query("SELECT * FROM forge_mythic_level_scale"); + + if (!result) + { + LOG_ERROR("sql.sql", ">> Loaded 0 challenge level scales. DB table `forge_mythic_level_scale` is empty."); + LOG_INFO("server.loading", " "); + return; + } + + do + { + Field* fields = result->Fetch(); + + uint32 difficulty = fields[0].Get(); + + MythicDifficultyScale* scale = new MythicDifficultyScale(); + scale->mod = fields[1].Get(); + _mythicLevelScales[difficulty] = scale; + } while (result->NextRow()); + + LOG_INFO("server.loading", ">> Loaded {} forge_mythic_level_scale in {} ms", _mythicLevelScales.size(), GetMSTimeDiffToNow(oldMSTime)); +} + +void ObjectMgr::LoadMythicMinionValue() +{ + uint32 oldMSTime = getMSTime(); + + _cacheMythicMinionValues.clear(); + + QueryResult result = WorldDatabase.Query("SELECT * FROM forge_mythic_minion_value"); + + if (!result) + { + LOG_ERROR("sql.sql", ">> Loaded 0 minion values. DB table `forge_mythic_minion_value` is empty."); + LOG_INFO("server.loading", " "); + return; + } + + do + { + Field* fields = result->Fetch(); + + uint32 instance = fields[0].Get(); + uint32 unit = fields[1].Get(); + float value = fields[2].Get(); + + _cacheMythicMinionValues[instance][unit] = value; + } while (result->NextRow()); + + LOG_INFO("server.loading", ">> Loaded {} instances from forge_mythic_minion_value in {} ms", _cacheMythicMinionValues.size(), GetMSTimeDiffToNow(oldMSTime)); +} + +void ObjectMgr::LoadMythicDungeonKeyMap() +{ + uint32 oldMSTime = getMSTime(); + + _forgeMythicKeys.clear(); + _forgeMythicMaps.clear(); + + QueryResult result = WorldDatabase.Query("SELECT * FROM forge_mythic_instance_key"); + + if (!result) + { + LOG_ERROR("sql.sql", ">> Loaded 0 instance keys. DB table `forge_mythic_instance_key` is empty."); + LOG_INFO("server.loading", " "); + return; + } + + do + { + Field* fields = result->Fetch(); + + uint32 map = fields[0].Get(); + uint32 key = fields[1].Get(); + int32 baseTimer = fields[2].Get(); + + KeyInfo* info = new KeyInfo(); + info->baseTimer = baseTimer; + info->itemId = key; + _forgeMythicKeys[map] = info; + _forgeMythicMaps.push_back(map); + } while (result->NextRow()); + + LOG_INFO("server.loading", ">> Loaded {} instances from forge_mythic_instance_key in {} ms", _forgeMythicMaps.size(), GetMSTimeDiffToNow(oldMSTime)); +} + +void ObjectMgr::LoadMythicAffixes() +{ + uint32 oldMSTime = getMSTime(); + + _forgeAffixes.clear(); + + QueryResult result = WorldDatabase.Query("SELECT * FROM forge_mythic_affixes"); + + if (!result) + { + LOG_ERROR("sql.sql", ">> Loaded 0 affixes. DB table `forge_mythic_affixes` is empty."); + LOG_INFO("server.loading", " "); + return; + } + + do + { + Field* fields = result->Fetch(); + + uint32 spell = fields[0].Get(); + uint32 tier = fields[1].Get(); + int32 timerDiff = fields[2].Get(); + + AffixInfo* affix = new AffixInfo(); + affix->tier = tier; + affix->timerDiff = timerDiff; + + _forgeAffixes[spell] = affix; + _forgeAffixTiers[tier].push_back(spell); + } while (result->NextRow()); + + LOG_INFO("server.loading", ">> Loaded {} instances from forge_mythic_affixes in {} ms", _forgeMythicMaps.size(), GetMSTimeDiffToNow(oldMSTime)); +} + int ObjectMgr::LoadReferenceVendor(int32 vendor, int32 item, std::set* skip_vendors) { // find all items from the reference vendor diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index e48a059fef5da0..36225760e99dd1 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -41,6 +41,7 @@ #include #include #include +#include class Item; struct DungeonProgressionRequirements; @@ -431,6 +432,58 @@ struct AreaTrigger float orientation; }; +// hater: m+ +struct InstanceDifficultyMultiplier +{ + float healthMultiplier; + float damageMultiplier; +}; + +struct MythicDifficultyScale +{ + float mod; +}; + +struct MapChallengeModeEntry +{ + std::string Name; + uint32 ID; + uint16 MapID; + uint8 Flags; + uint32 ExpansionLevel; + int16 CriteriaCount[3]; +}; + +struct WorldSafeLocsEntry +{ + uint32 ID = 0; + WorldLocation Loc; +}; + +struct SpellScalingEntry +{ + //uint32 Id; // 0 m_ID + int32 CastTimeMin; // 1 + int32 CastTimeMax; // 2 + int32 CastTimeMaxLevel; // 3 + int32 ScalingClass; // 4 (index * 100) + charLevel - 1 => gtSpellScaling.dbc + float Multiplier[3]; // 5-7 + float RandomMultiplier[3]; // 8-10 + float OtherMultiplier[3]; // 11-13 + float CoefBase; // 14 some coefficient, mostly 1.0f + int32 CoefLevelBase; // 15 some level +}; + +struct KeyInfo { + uint32 itemId; + int32 baseTimer; +}; + +struct AffixInfo { + uint8 tier; + int32 timerDiff; +}; + struct BroadcastText { BroadcastText() @@ -1453,6 +1506,76 @@ class ObjectMgr uint8 GetInstanceSavedGameobjectState(uint32 id, uint32 guid); void SetInstanceSavedGameobjectState(uint32 id, uint32 guid, uint8 state); void NewInstanceSavedGameobjectState(uint32 id, uint32 guid, uint8 state); + + // hater: m+ + WorldSafeLocsEntry* GetWorldSafeLoc(uint32 id) const; + MapChallengeModeEntry* GetChallengeMode(uint32 id) const; + void LoadWorldSafeLocs(); + void LoadMythicLevelScale(); + void LoadInstanceDifficultyMultiplier(); + void LoadMythicMinionValue(); + void LoadMythicDungeonKeyMap(); + void LoadMythicAffixes(); + + [[nodiscard]] InstanceDifficultyMultiplier const* GetInstanceDifficultyMultiplier(uint32 mapId, uint32 difficultyId) const + { + auto itr = _instanceDifficultyMultipliers.find(mapId); + if (itr == _instanceDifficultyMultipliers.end()) return nullptr; + auto sub = itr->second.find(difficultyId); + if (sub == itr->second.end()) return nullptr; + return sub->second; + } + + [[nodiscard]] MythicDifficultyScale const* GetMythicDifficultyScale(uint32 difficulty) const + { + auto itr = _mythicLevelScales.find(difficulty); + if (itr == _mythicLevelScales.end()) return nullptr; + return itr->second; + } + + [[nodiscard]] float GetMythicMinionValue(uint32 mapId, uint32 unit) const + { + auto itr = _cacheMythicMinionValues.find(mapId); + if (itr == _cacheMythicMinionValues.end()) return 0; + auto sub = itr->second.find(unit); + if (sub == itr->second.end()) return 0; + return sub->second; + } + + [[nodiscard]] uint32 GetRandomMythicKey() const + { + std::random_device rd; + std::mt19937 eng(rd()); + std::uniform_int_distribution<> distr(0, _forgeMythicMaps.size() - 1); + auto index = _forgeMythicMaps[distr(eng)]; + + return _forgeMythicKeys.at(index)->itemId; + } + + std::unordered_map GetDungeonKeyMap() { + return _forgeMythicKeys; + } + + boolean AffixExistsAndMatchesTier(uint32 affix, uint8 tier) { + auto find = _forgeAffixes.find(affix); + if (find != _forgeAffixes.end()) + return find->second->tier == tier; + + return false; + } + + AffixInfo* GetAffix(uint32 affix) { + auto find = _forgeAffixes.find(affix); + if (find != _forgeAffixes.end()) + return find->second; + + return nullptr; + } + + std::unordered_map> GetForgeAffixesByTierMap() { + return _forgeAffixTiers; + } + private: // first free id for selected id type uint32 _auctionId; // pussywizard: accessed by a single thread @@ -1619,6 +1742,20 @@ class ObjectMgr std::set _difficultyEntries[MAX_DIFFICULTY - 1]; // already loaded difficulty 1 value in creatures, used in CheckCreatureTemplate std::set _hasDifficultyEntries[MAX_DIFFICULTY - 1]; // already loaded creatures with difficulty 1 values, used in CheckCreatureTemplate + // hater: m+ + std::unordered_map _worldSafeLocs; + std::unordered_map _cacheChallengeMode; + std::unordered_map> _cacheMythicMinionValues; + typedef std::unordered_map> InstanceDifficultyMultiplierContainer; + InstanceDifficultyMultiplierContainer _instanceDifficultyMultipliers; + typedef std::unordered_map MythicDifficultyScaleContainer; + + std::unordered_map _forgeMythicKeys; + MythicDifficultyScaleContainer _mythicLevelScales; + std::vector _forgeMythicMaps; + std::unordered_map _forgeAffixes; + std::unordered_map> _forgeAffixTiers; + enum CreatureLinkedRespawnType { CREATURE_TO_CREATURE, diff --git a/src/server/game/Instances/InstanceSaveMgr.h b/src/server/game/Instances/InstanceSaveMgr.h index 7c6f18371e739d..d76b438aca790b 100644 --- a/src/server/game/Instances/InstanceSaveMgr.h +++ b/src/server/game/Instances/InstanceSaveMgr.h @@ -94,6 +94,7 @@ class InstanceSave uint32 m_instanceid; uint32 m_mapid; Difficulty m_difficulty; + uint32 m_entranceId; bool m_canReset; std::string m_instanceData; uint32 m_completedEncounterMask; diff --git a/src/server/game/Instances/InstanceScript.cpp b/src/server/game/Instances/InstanceScript.cpp index 600edae4a2534c..b48d72c4fd742b 100644 --- a/src/server/game/Instances/InstanceScript.cpp +++ b/src/server/game/Instances/InstanceScript.cpp @@ -30,6 +30,13 @@ #include "ScriptMgr.h" #include "Spell.h" #include "WorldSession.h" +#include "ChallengeModeMgr.h" +#include "ObjectMgr.h" + +struct MapChallengeModeEntry; + +uint32 GO_REWARD_CHEST = 1000001; +uint64 GO_KEY_START = 1000000; BossBoundaryData::~BossBoundaryData() { @@ -58,6 +65,18 @@ void InstanceScript::OnCreatureCreate(Creature* creature) { AddObject(creature, true); AddMinion(creature, true); + + Difficulty difficulty = instance->GetDifficulty(); + if (difficulty != Difficulty::REGULAR_DIFFICULTY) { + if (InstanceDifficultyMultiplier const* multiplier = sObjectMgr->GetInstanceDifficultyMultiplier(instance->GetId(), difficulty)) + creature->SetBaseHealth(creature->GetMaxHealth() * multiplier->healthMultiplier); + + creature->SetLevel(DEFAULT_MAX_LEVEL); + } + + if (IsChallengeModeStarted()) + if (!creature->IsPet()) + CastChallengeCreatureSpell(creature); } void InstanceScript::OnCreatureRemove(Creature* creature) @@ -117,6 +136,73 @@ void InstanceScript::HandleGameObject(ObjectGuid GUID, bool open, GameObject* go } } +void InstanceScript::OnPlayerEnter(Player* player) +{ + if (IsChallengeModeStarted()) + { + SendChallengeModeStart(player); + SendChallengeModeElapsedTimer(player); + SendChallengeModeDeathCount(player); + + CastChallengePlayerSpell(player); + } +} + +void InstanceScript::OnPlayerExit(Player* player) +{ + player->RemoveAurasDueToSpell(SPELL_CHALLENGER_BURDEN); +} + +void InstanceScript::OnPlayerDeath(Player* /*player*/) +{ + if (IsChallengeModeStarted()) + { + _challengeModeDeathCount++; + + DoOnPlayers([this](Player* player) + { + SendChallengeModeElapsedTimer(player); + SendChallengeModeDeathCount(player); + }); + } +} + +void InstanceScript::UpdateOperations(uint32 const diff) +{ + for (auto itr = timedDelayedOperations.begin(); itr != timedDelayedOperations.end(); itr++) + { + itr->first -= diff; + + if (itr->first < 0) + { + itr->second(); + itr->second = nullptr; + } + } + + uint32 timedDelayedOperationCountToRemove = std::count_if(std::begin(timedDelayedOperations), std::end(timedDelayedOperations), [](const std::pair>& pair) -> bool + { + return pair.second == nullptr; + }); + + for (uint32 i = 0; i < timedDelayedOperationCountToRemove; i++) + { + auto itr = std::find_if(std::begin(timedDelayedOperations), std::end(timedDelayedOperations), [](const std::pair>& p_Pair) -> bool + { + return p_Pair.second == nullptr; + }); + + if (itr != std::end(timedDelayedOperations)) + timedDelayedOperations.erase(itr); + } + + if (timedDelayedOperations.empty() && !emptyWarned) + { + emptyWarned = true; + LastOperationCalled(); + } +} + bool InstanceScript::IsEncounterInProgress() const { for (std::vector::const_iterator itr = bosses.begin(); itr != bosses.end(); ++itr) @@ -526,6 +612,74 @@ void InstanceScript::DoRespawnGameObject(ObjectGuid uiGuid, uint32 uiTimeToDespa LOG_DEBUG("scripts", "InstanceScript: DoRespawnGameObject failed"); } +void InstanceScript::DoRemoveAurasDueToSpellOnPlayers(uint32 spell) +{ + DoOnPlayers([spell](Player* player) + { + player->RemoveAurasDueToSpell(spell); + + if (Pet* pet = player->GetPet()) + pet->RemoveAurasDueToSpell(spell); + }); +} + +void InstanceScript::DoCastSpellOnPlayer(Player* player, uint32 spell, bool includePets /*= false*/, bool includeControlled /*= false*/) +{ + if (!player) + return; + + player->CastSpell(player, spell, true); + + if (!includePets) + return; + + for (uint8 itr2 = 0; itr2 < MAX_SUMMON_SLOT; ++itr2) + { + ObjectGuid summonGUID = player->m_SummonSlot[itr2]; + if (!summonGUID.IsEmpty()) + if (Creature* summon = instance->GetCreature(summonGUID)) + summon->CastSpell(player, spell, true); + } + + if (!includeControlled) + return; + + for (auto itr2 = player->m_Controlled.begin(); itr2 != player->m_Controlled.end(); ++itr2) + { + if (Unit* controlled = *itr2) + if (controlled->IsInWorld() && controlled->GetTypeId() == TYPEID_UNIT) + controlled->CastSpell(player, spell, true); + } +} + + +void InstanceScript::DoCastSpellOnPlayers(uint32 spell, Unit* caster /*= nullptr*/, bool triggered /*= true*/) +{ + DoOnPlayers([spell, caster, triggered](Player* player) + { + Unit* spellCaster = caster ? caster : player; + spellCaster->CastSpell(player, spell, triggered); + }); +} + +void InstanceScript::DoOnPlayers(std::function&& function) +{ + Map::PlayerList const& plrList = instance->GetPlayers(); + + if (!plrList.IsEmpty()) + for (Map::PlayerList::const_iterator i = plrList.begin(); i != plrList.end(); ++i) + if (Player* player = i->GetSource()) + function(player); +} + +void InstanceScript::DoNearTeleportPlayers(const Position pos, bool casting /*=false*/) +{ + DoOnPlayers([pos, casting](Player* player) + { + player->NearTeleportTo(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), casting); + }); +} + void InstanceScript::DoRespawnCreature(ObjectGuid guid, bool force) { if (Creature* creature = instance->GetCreature(guid)) @@ -609,64 +763,6 @@ void InstanceScript::DoStopTimedAchievement(AchievementCriteriaTimedTypes type, player->RemoveTimedAchievement(type, entry); } -// Remove Auras due to Spell on all players in instance -void InstanceScript::DoRemoveAurasDueToSpellOnPlayers(uint32 spell) -{ - Map::PlayerList const& PlayerList = instance->GetPlayers(); - if (!PlayerList.IsEmpty()) - { - for (Map::PlayerList::const_iterator itr = PlayerList.begin(); itr != PlayerList.end(); ++itr) - { - if (Player* player = itr->GetSource()) - { - player->RemoveAurasDueToSpell(spell); - if (Pet* pet = player->GetPet()) - pet->RemoveAurasDueToSpell(spell); - } - } - } -} - -// Cast spell on all players in instance -void InstanceScript::DoCastSpellOnPlayers(uint32 spell) -{ - Map::PlayerList const& PlayerList = instance->GetPlayers(); - - if (!PlayerList.IsEmpty()) - for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) - if (Player* player = i->GetSource()) - player->CastSpell(player, spell, true); -} - -void InstanceScript::DoCastSpellOnPlayer(Player* player, uint32 spell, bool includePets /*= false*/, bool includeControlled /*= false*/) -{ - if (!player) - return; - - player->CastSpell(player, spell, true); - - if (!includePets) - return; - - for (uint8 itr2 = 0; itr2 < MAX_SUMMON_SLOT; ++itr2) - { - ObjectGuid summonGUID = player->m_SummonSlot[itr2]; - if (!summonGUID.IsEmpty()) - if (Creature* summon = instance->GetCreature(summonGUID)) - summon->CastSpell(player, spell, true); - } - - if (!includeControlled) - return; - - for (auto itr2 = player->m_Controlled.begin(); itr2 != player->m_Controlled.end(); ++itr2) - { - if (Unit* controlled = *itr2) - if (controlled->IsInWorld() && controlled->GetTypeId() == TYPEID_UNIT) - controlled->CastSpell(player, spell, true); - } -} - bool InstanceScript::CheckAchievementCriteriaMeet(uint32 criteria_id, Player const* /*source*/, Unit const* /*target*/ /*= nullptr*/, uint32 /*miscvalue1*/ /*= 0*/) { LOG_ERROR("scripts.ai", "Achievement system call InstanceScript::CheckAchievementCriteriaMeet but instance script for map {} not have implementation for achievement criteria {}", @@ -693,6 +789,13 @@ void InstanceScript::SetCompletedEncountersMask(uint32 newMask, bool save) } } +void InstanceScript::SetEntranceLocation(uint32 worldSafeLocationId) +{ + _entranceId = worldSafeLocationId; + if (_temporaryEntranceId) + _temporaryEntranceId = 0; +} + void InstanceScript::SendEncounterUnit(uint32 type, Unit* unit /*= nullptr*/, uint8 param1 /*= 0*/, uint8 param2 /*= 0*/) { // size of this packet is at most 15 (usually less) @@ -746,6 +849,263 @@ std::string InstanceScript::GetBossStateName(uint8 state) } } +class ChallengeModeWorker +{ +public: + ChallengeModeWorker(InstanceScript* instance) : _instance(instance) { } + + void Visit(std::unordered_map& creatureMap) + { + for (auto const& p : creatureMap) + { + if (p.second->IsInWorld() && !p.second->IsPet()) + { + if (!p.second->IsAlive()) + p.second->Respawn(); + + Difficulty difficulty = _instance->instance->GetDifficulty(); + if (difficulty != Difficulty::REGULAR_DIFFICULTY) { + if (InstanceDifficultyMultiplier const* multiplier = sObjectMgr->GetInstanceDifficultyMultiplier(_instance->instance->GetId(), difficulty)) + p.second->SetBaseHealth(p.second->GetMaxHealth() * multiplier->healthMultiplier); + + p.second->SetLevel(DEFAULT_MAX_LEVEL); + } + + _instance->CastChallengeCreatureSpell(p.second); + } + } + } + + template + void Visit(std::unordered_map&) { } + +private: + InstanceScript* _instance; +}; + +void InstanceScript::StartChallengeMode(Player* player, KeyInfo* key, uint8 level, uint32 affixOne, uint32 affixTwo, uint32 affixThree) +{ + if (IsChallengeModeStarted()) + return; + + if (GetCompletedEncounterMask() != 0) + return; + + //GameObject const* go = ObjectAccessor::GetGameObject(*player, ObjectGuid(GO_KEY_START)); + //if (go && go->IsWithinDistInMap(player, go->GetCombatReach() + 5.0f)) + //{ + // instance->RemoveFromMap(go, false); + //} + + _challengeModeStarted = true; + _challengeModeLevel = level; + _challengeOwner = player; + _challengeEntranceLoc; + + tierOneAffix = affixOne; + tierTwoAffix = affixTwo; + tierThreeAffix = affixThree; + + _challengeKey = key; + _challengeModeTimerMax = key->baseTimer; + if (auto affix = sObjectMgr->GetAffix(tierOneAffix)) + _challengeModeTimerMax += affix->timerDiff; + if (auto affix = sObjectMgr->GetAffix(tierTwoAffix)) + _challengeModeTimerMax += affix->timerDiff; + if (auto affix = sObjectMgr->GetAffix(tierThreeAffix)) + _challengeModeTimerMax += affix->timerDiff; + + _challengeModeCriteria = new ChallengeModeCriteria(instance); + + // Add the health/dmg modifier aura to all creatures + ChallengeModeWorker worker(this); + TypeContainerVisitor visitor(worker); + visitor.Visit(instance->GetObjectsStore()); + + // Tp back all players to begin + if (WorldSafeLocsEntry const* entranceSafeLocEntry = sObjectMgr->GetWorldSafeLoc(GetEntranceLocation())) + _challengeEntranceLoc.Relocate(entranceSafeLocEntry->Loc); + else if (AreaTriggerTeleport const* areaTrigger = sObjectMgr->GetMapEntranceTrigger(instance->GetId())) + _challengeEntranceLoc.Relocate(areaTrigger->target_X, areaTrigger->target_Y, areaTrigger->target_Z, areaTrigger->target_Orientation); + DoNearTeleportPlayers(_challengeEntranceLoc); + + if (_challengeModeDoorPosition.has_value()) + instance->SummonGameObject(GOB_CHALLENGER_DOOR, *_challengeModeDoorPosition, 0, 0, 0, 1.0f, WEEK); + + DoOnPlayers([this](Player* player) + { + CastChallengePlayerSpell(player); + }); + + AddTimedDelayedOperation(10000, [this]() + { + _challengeModeStartTime = getMSTime(); + DoOnPlayers([this](Player* player) + { + SendChallengeModeStart(player); + }); + + if (GameObject* door = GetGameObject(GOB_CHALLENGER_DOOR)) + DoUseDoorOrButton(door->GetGUID(), WEEK); + }); +} + +void InstanceScript::CompleteChallengeMode(Position pos) +{ + if (!IsChallengeModeStarted()) + return; + + uint32 totalDuration = GetChallengeModeCurrentDuration(); + + uint8 mythicIncrement = 0; + + if (totalDuration < _challengeModeTimerMax) + ++mythicIncrement; + if (totalDuration < _challengeModeTimerMax * .8) + ++mythicIncrement; + if (totalDuration < _challengeModeTimerMax * .6) + ++mythicIncrement; + + _challengeOwner->DestroyItemCount(_challengeKey->itemId, 1, true); + DoOnPlayers([this, mythicIncrement](Player* player) + { + player->SendForgeUIMsg(ForgeTopic::MYTHIC_KEY_COMPLETED, "done"); + player->AddChallengeKey(sChallengeModeMgr->GetRandomChallengeId(), std::max(_challengeModeLevel + mythicIncrement, 10)); + }); + + // Achievements only if timer respected + if (mythicIncrement) + { + // Potential for m+ achievements + /*if (_challengeModeLevel >= 2) + DoCompleteAchievement(11183); + if (_challengeModeLevel >= 5) + DoCompleteAchievement(11184); + if (_challengeModeLevel >= 10) + DoCompleteAchievement(11185); + if (_challengeModeLevel >= 15) + { + DoCompleteAchievement(11162); + DoCompleteAchievement(11224); + }*/ + } + _challengeModeStarted = false; + SpawnChallengeModeRewardChest(pos); + + DoOnPlayers([this](Player* player) + { + sChallengeModeMgr->Reward(player, _challengeModeLevel); + }); + +} + +uint32 InstanceScript::GetChallengeModeCurrentDuration() +{ + return uint32(GetMSTimeDiffToNow(_challengeModeStartTime) / 1000) + (5 * _challengeModeDeathCount); +} + +void InstanceScript::SendChallengeModeStart(Player* player) +{ + auto challengeMod = sObjectMgr->GetMythicDifficultyScale(_challengeModeLevel); + if (!challengeMod) + return; + + std::string initKey = (std::string)instance->GetMapName() + "*" + std::to_string(_challengeModeLevel) + "*" + std::to_string(_challengeModeTimerMax); + player->SendForgeUIMsg(ForgeTopic::MYTHIC_SET_AFFIXES_AND_START, initKey); + + SendChallengeModeDeathCount(player); + SendChallengeModeElapsedTimer(player); + SendChallengeModeCriteria(player); +} + +void InstanceScript::SendChallengeModeCriteria(Player* player) +{ + if (IsChallengeModeStarted()) { + std::string out = ""; + int i = 0; + for (auto criterion : _challengeModeCriteria->_criteria) { + auto delim = ""; + if (i > 0) + delim = "*"; + + std::string name = std::to_string(criterion.first); + auto value = std::to_string(criterion.second); + if (criterion.first > 0) { + if (auto ct = sObjectMgr->GetCreatureTemplate(criterion.first)) { + name = ct->Name; + value = value.substr(0, 1); + } + } + else + value = value.substr(0, value.find(".") + 2); + + // crit id to status + out += delim + name + "~" + value; + i++; + } + + player->SendForgeUIMsg(ForgeTopic::MYTHIC_UPDATE_CRITERIA, out); + } +} + +void InstanceScript::SendChallengeModeDeathCount(Player* player/* = nullptr*/) +{ + if (IsChallengeModeStarted()) + player->SendForgeUIMsg(ForgeTopic::MYTHIC_UPDATE_DEATHS, std::to_string(_challengeModeDeathCount)); +} + +void InstanceScript::SendChallengeModeElapsedTimer(Player* player/* = nullptr*/) +{ + if (IsChallengeModeStarted()) { + auto duration = GetChallengeModeCurrentDuration(); + player->SendForgeUIMsg(ForgeTopic::MYTHIC_UPDATE_TIMER, std::to_string(duration)); + } +} + +void InstanceScript::CastChallengeCreatureSpell(Creature* creature) +{ + if (auto challengeMod = sObjectMgr->GetMythicDifficultyScale(_challengeModeLevel)) { + float mod = challengeMod->mod; + CustomSpellValues values; + values.AddSpellMod(SPELLVALUE_BASE_POINT0, mod); + values.AddSpellMod(SPELLVALUE_BASE_POINT1, mod); + + // Affixes + //values.AddSpellMod(SPELLVALUE_BASE_POINT2, 0); // 6 Raging + //values.AddSpellMod(SPELLVALUE_BASE_POINT3, 0); // 7 Bolstering + //values.AddSpellMod(SPELLVALUE_BASE_POINT4, 0); // 9 Tyrannical + //values.AddSpellMod(SPELLVALUE_BASE_POINT5, 0); // + //values.AddSpellMod(SPELLVALUE_BASE_POINT6, 0); // + //values.AddSpellMod(SPELLVALUE_BASE_POINT7, 0); // 3 Volcanic + //values.AddSpellMod(SPELLVALUE_BASE_POINT8, 0); // 4 Necrotic + //values.AddSpellMod(SPELLVALUE_BASE_POINT9, 0); // 10 Fortified + //values.AddSpellMod(SPELLVALUE_BASE_POINT10, 0); // 8 Sanguine + //values.AddSpellMod(SPELLVALUE_BASE_POINT11, 0); // 14 Quaking + //values.AddSpellMod(SPELLVALUE_BASE_POINT12, 0); // 13 Explosive + //values.AddSpellMod(SPELLVALUE_BASE_POINT13, 0); // 11 Bursting + + creature->CastCustomSpell(SPELL_CHALLENGER_MIGHT, values, creature, TRIGGERED_FULL_MASK); + } +} + +void InstanceScript::CastChallengePlayerSpell(Player* player) +{ + CustomSpellValues values; + + // Affixes + values.AddSpellMod(SPELLVALUE_BASE_POINT0, 0); // 12 Grievous + values.AddSpellMod(SPELLVALUE_BASE_POINT1, 0); // 2 Skittish + values.AddSpellMod(SPELLVALUE_BASE_POINT2, 0); // + + player->CastCustomSpell(SPELL_CHALLENGER_BURDEN, values, player, TRIGGERED_FULL_MASK); +} + +void InstanceScript::SpawnChallengeModeRewardChest(Position p) { + if (IsChallengeModeStarted()) { + GameObject* chest = _challengeOwner->SummonGameObject(GO_REWARD_CHEST, p.GetPositionX(), p.GetPositionY(), p.GetPositionZ(), 0, 0, 0, 0, 0, 0); + } +} + bool InstanceHasScript(WorldObject const* obj, char const* scriptName) { if (InstanceMap* instance = obj->GetMap()->ToInstanceMap()) diff --git a/src/server/game/Instances/InstanceScript.h b/src/server/game/Instances/InstanceScript.h index 4f3a2d8c9bd8d2..25b7da2de7d5c0 100644 --- a/src/server/game/Instances/InstanceScript.h +++ b/src/server/game/Instances/InstanceScript.h @@ -22,6 +22,7 @@ #include "ObjectMgr.h" #include "World.h" #include "ZoneScript.h" +#include "ChallengeModeCriteria.h" #include #define OUT_SAVE_INST_DATA LOG_DEBUG("scripts.ai", "Saving Instance Data for Instance {} (Map {}, Instance Id {})", instance->GetMapName(), instance->GetId(), instance->GetInstanceId()) @@ -75,6 +76,14 @@ struct DoorData DoorType type; }; +enum ChallengeMode +{ + GOB_CHALLENGER_DOOR = 239408, + + SPELL_CHALLENGER_MIGHT = 100101, + SPELL_CHALLENGER_BURDEN = 100102 +}; + struct BossBoundaryEntry { uint32 const bossId; @@ -139,11 +148,13 @@ typedef std::map ObjectInfoMap; class InstanceScript : public ZoneScript { public: - explicit InstanceScript(Map* map) : instance(map), completedEncounters(0) {} + InstanceScript(Map* map) : instance(map), completedEncounters(0), _entranceId(0), _temporaryEntranceId(0), + _challengeModeStarted(false), _challengeModeLevel(0), _challengeModeStartTime(0), _challengeModeDeathCount(0) {} ~InstanceScript() override {} Map* instance; + Position _challengeEntranceLoc; //On creation, NOT load. virtual void Initialize() {} @@ -180,7 +191,9 @@ class InstanceScript : public ZoneScript GameObject* GetGameObject(uint32 type); //Called when a player successfully enters the instance. - virtual void OnPlayerEnter(Player* /*player*/) {} + void OnPlayerEnter(Player* /*player*/) override; + void OnPlayerExit(Player*) override; + void OnPlayerDeath(Player*) override; virtual void OnPlayerAreaUpdate(Player* /*player*/, uint32 /*oldArea*/, uint32 /*newArea*/) {} @@ -189,6 +202,8 @@ class InstanceScript : public ZoneScript //use HandleGameObject(GUID, boolen, nullptr); in any other script void HandleGameObject(ObjectGuid guid, bool open, GameObject* go = nullptr); + void UpdateOperations(uint32 const diff); + //change active state of doors or buttons void DoUseDoorOrButton(ObjectGuid guid, uint32 withRestoreTime = 0, bool useAlternativeState = false); @@ -217,11 +232,15 @@ class InstanceScript : public ZoneScript // Remove Auras due to Spell on all players in instance void DoRemoveAurasDueToSpellOnPlayers(uint32 spell); - // Cast spell on all players in instance - void DoCastSpellOnPlayers(uint32 spell); + // hater: m+ + void DoOnPlayers(std::function&& function); + void DoNearTeleportPlayers(const Position pos, bool casting = false); // Cast spell on player - void DoCastSpellOnPlayer(Player* player, uint32 spell, bool includePets /*= false*/, bool includeControlled /*= false*/); + void DoCastSpellOnPlayer(Player* player, uint32 spell, bool includePets = false, bool includeControlled = false); + + // Cast spell on all players in instance + void DoCastSpellOnPlayers(uint32 spell, Unit* caster = nullptr, bool triggered = true); // Return wether server allow two side groups or not bool ServerAllowsTwoSideGroups() { return sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP); } @@ -253,6 +272,30 @@ class InstanceScript : public ZoneScript uint32 GetEncounterCount() const { return bosses.size(); } + // Sets the entrance location (WorldSafeLoc) id + void SetEntranceLocation(uint32 worldSafeLocationId); + + // Sets a temporary entrance that does not get saved to db + void SetTemporaryEntranceLocation(uint32 worldSafeLocationId) { _temporaryEntranceId = worldSafeLocationId; } + + // Get's the current entrance id + uint32 GetEntranceLocation() const { uint32 locationId = _temporaryEntranceId ? _temporaryEntranceId : _entranceId; OnGetEntranceLocation(locationId); return locationId; } + + virtual void OnGetEntranceLocation(uint32& /*worldSafeLocationId*/) const { } + + void AddTimedDelayedOperation(uint32 timeout, std::function&& function) + { + emptyWarned = false; + timedDelayedOperations.push_back(std::pair>(timeout, function)); + } + + /// Called after last delayed operation was deleted + /// Do whatever you want + virtual void LastOperationCalled() { } + + std::vector>> timedDelayedOperations; ///< Delayed operations + bool emptyWarned; ///< Warning when there are no more delayed operations + // Only used by areatriggers that inherit from OnlyOnceAreaTriggerScript void MarkAreaTriggerDone(uint32 id) { _activatedAreaTriggers.insert(id); } void ResetAreaTriggerDone(uint32 id) { _activatedAreaTriggers.erase(id); } @@ -263,6 +306,29 @@ class InstanceScript : public ZoneScript // Allows executing code using all creatures registered in the instance script as minions void DoForAllMinions(uint32 id, std::function exec); + + // hater: m+ + void StartChallengeMode(Player* player, KeyInfo* key, uint8 level, uint32 affixOne, uint32 affixTwo = 0, uint32 affixThree = 0); + void CompleteChallengeMode(Position); + + bool IsChallengeModeStarted() const { return _challengeModeStarted; } + uint8 GetChallengeModeLevel() const { return _challengeModeLevel; } + uint32 GetChallengeModeCurrentDuration(); + + void SendChallengeModeStart(Player* player = nullptr); + void SendChallengeModeDeathCount(Player* player = nullptr); + void SendChallengeModeElapsedTimer(Player* player = nullptr); + void SendChallengeModeCriteria(Player* player = nullptr); + void CastChallengeCreatureSpell(Creature* creature); + void CastChallengePlayerSpell(Player* player); + + void SetChallengeDoorPos(Position pos) { _challengeModeDoorPosition = pos; } + void SpawnChallengeModeRewardChest(Position); + + ChallengeModeCriteria* GetCriteria() { + return _challengeModeCriteria; + } + protected: void SetHeaders(std::string const& dataHeaders); void SetBossNumber(uint32 number) { bosses.resize(number); } @@ -305,6 +371,23 @@ class InstanceScript : public ZoneScript ObjectGuidMap _objectGuids; uint32 completedEncounters; // completed encounter mask, bit indexes are DungeonEncounter.dbc boss numbers, used for packets std::unordered_set _activatedAreaTriggers; + + uint32 _entranceId; + uint32 _temporaryEntranceId; + + bool _challengeModeStarted; + uint8 _challengeModeLevel = 1; + uint32 _challengeModeStartTime; + uint32 _challengeModeDeathCount; + Optional _challengeModeDoorPosition; + ChallengeModeCriteria* _challengeModeCriteria; + Player* _challengeOwner; + KeyInfo* _challengeKey; + uint32 _challengeModeTimerMax; + + uint32 tierOneAffix; + uint32 tierTwoAffix = 0; + uint32 tierThreeAffix = 0; }; #endif diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 5f6a1418ed0e92..965fe33b6296f3 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -3024,12 +3024,17 @@ void InstanceMap::Update(const uint32 t_diff, const uint32 s_diff, bool /*thread Map::Update(t_diff, s_diff); if (t_diff) - if (instance_data) + if (instance_data) { + instance_data->UpdateOperations(t_diff); instance_data->Update(t_diff); + } } void InstanceMap::RemovePlayerFromMap(Player* player, bool remove) { + if (instance_data) + instance_data->OnPlayerExit(player); + // pussywizard: moved m_unloadTimer to InstanceMap::AfterPlayerUnlinkFromMap(), in this function if 2 players run out at the same time the instance won't close //if (!m_unloadTimer && m_mapRefMgr.getSize() == 1) // m_unloadTimer = m_unloadWhenEmpty ? MIN_UNLOAD_DELAY : std::max(sWorld->getIntConfig(CONFIG_INSTANCE_UNLOAD_DELAY), (uint32)MIN_UNLOAD_DELAY); @@ -3514,12 +3519,23 @@ void Map::UpdateEncounterState(EncounterCreditType type, uint32 creditEntry, Uni for (Map::PlayerList::const_iterator i = players.begin(); i != players.end(); ++i) { if (Player* player = i->GetSource()) - if (Group* grp = player->GetGroup()) + if (Group* grp = player->GetGroup()) { if (grp->isLFGGroup()) { sLFGMgr->FinishDungeon(grp->GetGUID(), dungeonId, this); return; } + else if (IsHeroic()) + { + auto map = sObjectMgr->GetDungeonKeyMap(); + auto key = map.find(GetId()); + if (key != map.end()) { + Map::PlayerList const& players = GetPlayers(); + for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) + itr->GetSource()->AddChallengeKey(sObjectMgr->GetRandomMythicKey(), 2); + } + } + } } } } diff --git a/src/server/game/Maps/ZoneScript.h b/src/server/game/Maps/ZoneScript.h index ae795f6aac0e10..7a3e9c6364aef2 100644 --- a/src/server/game/Maps/ZoneScript.h +++ b/src/server/game/Maps/ZoneScript.h @@ -39,8 +39,13 @@ class ZoneScript virtual void OnGameObjectRemove(GameObject*) { } virtual void OnUnitDeath(Unit*) { } + virtual void OnPlayerDeath(Player*) { } virtual void OnCreatureEvade(Creature*) { } + // Called when a player successfully enters or exit the zone. + virtual void OnPlayerEnter(Player* /*player*/) { } + virtual void OnPlayerExit(Player* /*player*/) { } + //All-purpose data storage 64 bit virtual ObjectGuid GetGuidData(uint32 /*DataId*/) const { return ObjectGuid::Empty; } virtual void SetGuidData(uint32 /*DataId*/, ObjectGuid /*Value*/) {} diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 8160ae03d35c1c..060cbf56bf3f26 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -634,10 +634,6 @@ void AuraEffect::CalculatePeriodic(Unit* caster, bool create, bool load) if (m_amplitude <= 0) m_amplitude = 1000; - float dmgRatio; - m_tickCount = GetTotalTicks(dmgRatio); - m_dmgRatio = 1 + dmgRatio; - Player* modOwner = caster ? caster->GetSpellModOwner() : nullptr; // Apply casting time mods @@ -654,6 +650,8 @@ void AuraEffect::CalculatePeriodic(Unit* caster, bool create, bool load) } } + m_tickCount = GetTotalTicks(); + if (load) // aura loaded from db { m_tickNumber = m_amplitude ? GetBase()->GetDuration() / m_amplitude : 0; @@ -7115,49 +7113,7 @@ void AuraEffect::HandleRaidProcFromChargeWithValueAuraProc(AuraApplication* aurA int32 AuraEffect::GetTotalTicks(float& dmgRatio) const { - return GetTotalTicks(dmgRatio, false); -} - -int32 AuraEffect::GetTotalTicks(float& dmgRatio, bool noHaste) const -{ - uint32 totalTicks = 1; - dmgRatio = 0; - - if (m_amplitude) - { - bool applyHaste = false; - switch (GetAuraType()) - { - case SPELL_AURA_PERIODIC_DAMAGE: - case SPELL_AURA_PERIODIC_HEAL: - case SPELL_AURA_PERIODIC_LEECH: - case SPELL_AURA_PERIODIC_TRIGGER_SPELL_FROM_CLIENT: - applyHaste = true; - break; - default: - break; - } - - if (GetCaster()->HasAuraTypeWithAffectMask(SPELL_AURA_PERIODIC_HASTE, m_spellInfo) || m_spellInfo->HasAttribute(SPELL_ATTR5_SPELL_HASTE_AFFECTS_PERIODIC)) - applyHaste = true; - - if (noHaste) - applyHaste = false; - - auto max = GetBase()->GetMaxDuration(); - - if (applyHaste) - totalTicks = GetBase()->GetSpellInfo()->CalculateTicks(m_amplitude, max, GetCaster(), dmgRatio); - else - totalTicks = max / m_amplitude; - - if (m_spellInfo->HasAttribute(SPELL_ATTR5_EXTRA_INITIAL_PERIOD)) - { - ++totalTicks; - } - } - - return totalTicks; + return GetTotalTicks(); } uint32 AuraEffect::GetTriggerSpell() const diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.h b/src/server/game/Spells/Auras/SpellAuraEffects.h index ce54d47855bfc3..0b54d475362527 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.h +++ b/src/server/game/Spells/Auras/SpellAuraEffects.h @@ -88,7 +88,7 @@ class AuraEffect uint32 GetTickNumber() const { return m_tickNumber; } int32 GetTotalTicks(float&) const; - int32 GetTotalTicks(float&, bool noHaste) const; + int32 GetTotalTicks() const { return m_amplitude ? (GetBase()->GetMaxDuration() / m_amplitude) : 1; } void ResetPeriodic(bool resetPeriodicTimer = false) { if (resetPeriodicTimer) m_periodicTimer = m_amplitude; m_tickNumber = 0;} void ResetTicks() { m_tickNumber = 0; } diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index c1db230dd1a3ed..46f66b3dbce087 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1993,6 +1993,10 @@ void World::SetInitialWorldSettings() LOG_INFO("server.loading", "Loading Client Addons..."); AddonMgr::LoadFromDB(); + // hater: xmog + LOG_INFO("server.loading", "Load xmog enchants..."); + Transmogrification::instance().LoadEnchants(); + // pussywizard: LOG_INFO("server.loading", "Deleting Invalid Mail Items..."); LOG_INFO("server.loading", " "); diff --git a/src/server/scripts/Spells/spell_druid.cpp b/src/server/scripts/Spells/spell_druid.cpp index dcf8bb5cdc39a9..72906aee8e7ef6 100644 --- a/src/server/scripts/Spells/spell_druid.cpp +++ b/src/server/scripts/Spells/spell_druid.cpp @@ -124,10 +124,7 @@ class spell_dru_t10_balance_4p_bonus : public AuraScript uint32 triggered_spell_id = 71023; SpellInfo const* triggeredSpell = sSpellMgr->GetSpellInfo(triggered_spell_id); - float dmgRatio; - int32 amount = CalculatePct(eventInfo.GetDamageInfo()->GetDamage(), aurEff->GetAmount()) / triggeredSpell->GetMaxTicks(eventInfo.GetActor(), dmgRatio); - if (dmgRatio != 0) - amount = amount * dmgRatio; + int32 amount = CalculatePct(eventInfo.GetDamageInfo()->GetDamage(), aurEff->GetAmount()) / triggeredSpell->GetMaxTicks(); eventInfo.GetProcTarget()->CastDelayedSpellWithPeriodicAmount(GetTarget(), triggered_spell_id, SPELL_AURA_PERIODIC_DAMAGE, amount, EFFECT_0); @@ -536,10 +533,8 @@ class spell_dru_innervate : public AuraScript void CalculateAmount(AuraEffect const* aurEff, int32& amount, bool& /*canBeRecalculated*/) { - float f; - if (Unit* caster = GetCaster()) - amount = int32(CalculatePct(caster->GetCreatePowers(POWER_MANA), amount) / aurEff->GetTotalTicks(f, true)); + amount = int32(CalculatePct(caster->GetCreatePowers(POWER_MANA), amount) / aurEff->GetTotalTicks()); else amount = 0; } diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp index ed26575b37f14a..d3c8dd475c3b34 100644 --- a/src/server/scripts/Spells/spell_generic.cpp +++ b/src/server/scripts/Spells/spell_generic.cpp @@ -2429,13 +2429,8 @@ class spell_gen_lifeblood : public AuraScript void CalculateAmount(AuraEffect const* aurEff, int32& amount, bool& /*canBeRecalculated*/) { - float f; - if (Unit* owner = GetUnitOwner()) - amount += int32(CalculatePct(owner->GetMaxHealth(), 1.5f / aurEff->GetTotalTicks(f, true))); - - if (f != 0) - amount += amount * f; + amount += int32(CalculatePct(owner->GetMaxHealth(), 1.5f / aurEff->GetTotalTicks())); } void Register() override @@ -4250,12 +4245,8 @@ class spell_gen_gift_of_naaru : public AuraScript default: break; } - float f; - - int32 healTick = floor(heal / aurEff->GetTotalTicks(f, true)); - if (f != 0) - healTick += healTick * f; + int32 healTick = floor(heal / aurEff->GetTotalTicks()); amount += int32(std::max(healTick, 0)); } diff --git a/src/server/scripts/Spells/spell_hunter.cpp b/src/server/scripts/Spells/spell_hunter.cpp index 442758b291c64d..6f4df54d64aecc 100644 --- a/src/server/scripts/Spells/spell_hunter.cpp +++ b/src/server/scripts/Spells/spell_hunter.cpp @@ -464,8 +464,7 @@ class spell_hun_chimera_shot : public SpellScript // Viper Sting - Instantly restores mana to you equal to 60% of the total amount drained by your Viper Sting. else if (familyFlag[1] & 0x00000080) { - float f; - int32 TickCount = aura->GetEffect(0)->GetTotalTicks(f, true); + int32 TickCount = aura->GetEffect(0)->GetTotalTicks(); spellId = SPELL_HUNTER_CHIMERA_SHOT_VIPER; diff --git a/src/server/scripts/Spells/spell_mage.cpp b/src/server/scripts/Spells/spell_mage.cpp index 034b2a02787a89..810b638df69ae7 100644 --- a/src/server/scripts/Spells/spell_mage.cpp +++ b/src/server/scripts/Spells/spell_mage.cpp @@ -708,11 +708,7 @@ class spell_mage_ignite : public AuraScript SpellInfo const* igniteDot = sSpellMgr->AssertSpellInfo(SPELL_MAGE_IGNITE); int32 pct = 8 * GetSpellInfo()->GetRank(); - float dmgRatio; - int32 amount = int32(CalculatePct(eventInfo.GetDamageInfo()->GetDamage(), pct) / igniteDot->GetMaxTicks(eventInfo.GetActor(), dmgRatio)); - - if (dmgRatio != 0) - amount = amount * dmgRatio; + int32 amount = int32(CalculatePct(eventInfo.GetDamageInfo()->GetDamage(), pct) / igniteDot->GetMaxTicks()); // Xinef: implement ignite bug eventInfo.GetProcTarget()->CastDelayedSpellWithPeriodicAmount(eventInfo.GetActor(), SPELL_MAGE_IGNITE, SPELL_AURA_PERIODIC_DAMAGE, amount); diff --git a/src/server/scripts/Spells/spell_priest.cpp b/src/server/scripts/Spells/spell_priest.cpp index f3e3be0bc4b429..8de8fbb1edaddd 100644 --- a/src/server/scripts/Spells/spell_priest.cpp +++ b/src/server/scripts/Spells/spell_priest.cpp @@ -260,8 +260,7 @@ class spell_pri_glyph_of_prayer_of_healing : public AuraScript } SpellInfo const* triggeredSpellInfo = sSpellMgr->AssertSpellInfo(SPELL_PRIEST_GLYPH_OF_PRAYER_OF_HEALING_HEAL); - float dmgRatio; - int32 heal = int32(CalculatePct(int32(healInfo->GetHeal()), aurEff->GetAmount()) / triggeredSpellInfo->GetMaxTicks(eventInfo.GetActor(), dmgRatio)); + int32 heal = int32(CalculatePct(int32(healInfo->GetHeal()), aurEff->GetAmount()) / triggeredSpellInfo->GetMaxTicks()); GetTarget()->CastCustomSpell(SPELL_PRIEST_GLYPH_OF_PRAYER_OF_HEALING_HEAL, SPELLVALUE_BASE_POINT0, heal, eventInfo.GetProcTarget(), true, nullptr, aurEff); } diff --git a/src/server/scripts/Spells/spell_shaman.cpp b/src/server/scripts/Spells/spell_shaman.cpp index b0ab8f1d2161d6..5f4f4f25dbdae0 100644 --- a/src/server/scripts/Spells/spell_shaman.cpp +++ b/src/server/scripts/Spells/spell_shaman.cpp @@ -132,11 +132,8 @@ class spell_sha_t10_restoration_4p_bonus : public AuraScript return; } - float dmgRatio; - int32 amount = CalculatePct(healInfo->GetHeal(), aurEff->GetAmount()) / triggeredSpell->GetMaxTicks(eventInfo.GetActor(), dmgRatio); + int32 amount = CalculatePct(healInfo->GetHeal(), aurEff->GetAmount()) / triggeredSpell->GetMaxTicks(); - if (dmgRatio != 0) - amount = amount * dmgRatio; eventInfo.GetProcTarget()->CastDelayedSpellWithPeriodicAmount(GetTarget(), triggered_spell_id, SPELL_AURA_PERIODIC_HEAL, amount, EFFECT_0); } diff --git a/src/server/shared/SharedDefines.h b/src/server/shared/SharedDefines.h index d5b6f810ffba14..26f3686f65cd64 100644 --- a/src/server/shared/SharedDefines.h +++ b/src/server/shared/SharedDefines.h @@ -82,7 +82,18 @@ enum class ForgeTopic COLLECTION_SETUP_STARTED = 52, COLLECTION_SETUP_FINISHED = 53, ADD_XMOG = 54, - APPLY_XMOG_ERROR = 55 + APPLY_XMOG_ERROR = 55, + + // m+ + MYTHIC_GET_WEEKLY_REWARD = 101, + MYTHIC_GET_MAP_STATS = 102, + MYTHIC_GET_AFFIXES_LIST = 103, + MYTHIC_SET_AFFIXES_AND_START = 104, + MYTHIC_KEY_COMPLETED = 105, // send confirmation that key has ended + MYTHIC_OPEN_WINDOW = 106, + MYTHIC_UPDATE_TIMER = 107, + MYTHIC_UPDATE_DEATHS = 108, + MYTHIC_UPDATE_CRITERIA = 109, }; enum class ForgeError