From c68f9b6906ec590006bd8c96b17b13a114df4e9a Mon Sep 17 00:00:00 2001 From: luis Date: Fri, 3 Jul 2026 21:29:27 -0300 Subject: [PATCH] Mythic+: Phase 3 - death penalty, run scoring and best-run persistence --- sql/updates/characters/master/.gitignore | 24 ----- .../master/2026_07_04_00_characters.sql | 18 ++++ .../Implementation/CharacterDatabase.cpp | 3 + .../Implementation/CharacterDatabase.h | 3 + .../game/ChallengeMode/ChallengeMode.cpp | 41 +++++-- .../game/ChallengeMode/ChallengeModeMgr.cpp | 26 ++++- .../game/ChallengeMode/ChallengeModeMgr.h | 7 ++ .../game/ChallengeMode/MythicPlusData.cpp | 102 ++++++++++++++++++ .../game/ChallengeMode/MythicPlusData.h | 63 +++++++++++ src/server/game/Entities/Player/Player.cpp | 15 ++- src/server/game/Entities/Player/Player.h | 11 +- src/server/game/Entities/Unit/Unit.cpp | 11 ++ src/server/game/Handlers/CharacterHandler.cpp | 5 + 13 files changed, 294 insertions(+), 35 deletions(-) delete mode 100644 sql/updates/characters/master/.gitignore create mode 100644 sql/updates/characters/master/2026_07_04_00_characters.sql create mode 100644 src/server/game/ChallengeMode/MythicPlusData.cpp create mode 100644 src/server/game/ChallengeMode/MythicPlusData.h diff --git a/sql/updates/characters/master/.gitignore b/sql/updates/characters/master/.gitignore deleted file mode 100644 index 9ef72e70b..000000000 --- a/sql/updates/characters/master/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ - -build*/ -.directory -.mailmap -*.orig -*.rej -*~ -.hg/ -*.kdev* -.DS_Store -CMakeLists.txt.user -*.bak -*.patch -*.diff -*.REMOTE.* -*.BACKUP.* -*.BASE.* -*.LOCAL.* -nbproject/* -.idea/* -.browse.VC* -.vscode -cmake-build-*/ -.vs diff --git a/sql/updates/characters/master/2026_07_04_00_characters.sql b/sql/updates/characters/master/2026_07_04_00_characters.sql new file mode 100644 index 000000000..dcebcd789 --- /dev/null +++ b/sql/updates/characters/master/2026_07_04_00_characters.sql @@ -0,0 +1,18 @@ +-- +-- Mythic+ (Challenge Mode): per-character best-run storage. +-- +CREATE TABLE IF NOT EXISTS `character_mythic_plus` ( + `guid` bigint unsigned NOT NULL DEFAULT 0 COMMENT 'Global Unique Identifier', + `challengeModeId` int unsigned NOT NULL DEFAULT 0 COMMENT 'MapChallengeMode.db2 ID', + `level` int unsigned NOT NULL DEFAULT 0 COMMENT 'Keystone level of the best run', + `durationMs` int unsigned NOT NULL DEFAULT 0 COMMENT 'Effective run time in milliseconds (incl. death penalty)', + `deaths` int unsigned NOT NULL DEFAULT 0, + `completionDate` bigint NOT NULL DEFAULT 0 COMMENT 'Unix time of the run', + `score` float NOT NULL DEFAULT 0, + `affix1` int unsigned NOT NULL DEFAULT 0, + `affix2` int unsigned NOT NULL DEFAULT 0, + `affix3` int unsigned NOT NULL DEFAULT 0, + `affix4` int unsigned NOT NULL DEFAULT 0, + PRIMARY KEY (`guid`,`challengeModeId`), + CONSTRAINT `fk_character_mythic_plus_character` FOREIGN KEY (`guid`) REFERENCES `characters` (`guid`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Mythic Keystone best runs'; diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index fea468d77..e28d9ddcd 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -1098,6 +1098,9 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_SEL_WARBAND_RENOWN_REWARDS_GRANTED, "SELECT renownRewardId FROM warband_renown_rewards_granted WHERE battlenetAccountId = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_WARBAND_RENOWN_REWARD_GRANTED, "INSERT IGNORE INTO warband_renown_rewards_granted (battlenetAccountId, renownRewardId) VALUES (?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_CHARACTER_MYTHIC_PLUS, "SELECT challengeModeId, level, durationMs, deaths, completionDate, score, affix1, affix2, affix3, affix4 FROM character_mythic_plus WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_CHARACTER_MYTHIC_PLUS, "INSERT INTO character_mythic_plus (guid, challengeModeId, level, durationMs, deaths, completionDate, score, affix1, affix2, affix3, affix4) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_CHARACTER_MYTHIC_PLUS, "DELETE FROM character_mythic_plus WHERE guid = ?", CONNECTION_ASYNC); // WowCommunity end } diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h index 584dd63b3..c52d58c7d 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.h +++ b/src/server/database/Database/Implementation/CharacterDatabase.h @@ -852,6 +852,9 @@ enum CharacterDatabaseStatements : uint32 CHAR_SEL_WARBAND_RENOWN_REWARDS_GRANTED, CHAR_INS_WARBAND_RENOWN_REWARD_GRANTED, + CHAR_SEL_CHARACTER_MYTHIC_PLUS, + CHAR_INS_CHARACTER_MYTHIC_PLUS, + CHAR_DEL_CHARACTER_MYTHIC_PLUS, //WowCommunity end MAX_CHARACTERDATABASE_STATEMENTS diff --git a/src/server/game/ChallengeMode/ChallengeMode.cpp b/src/server/game/ChallengeMode/ChallengeMode.cpp index 871466631..131743195 100644 --- a/src/server/game/ChallengeMode/ChallengeMode.cpp +++ b/src/server/game/ChallengeMode/ChallengeMode.cpp @@ -18,14 +18,16 @@ #include "ChallengeMode.h" #include "ChallengeModeMgr.h" #include "Creature.h" +#include "GameTime.h" #include "Group.h" #include "Log.h" #include "Map.h" #include "MiscPackets.h" +#include "MythicPlusData.h" #include "ObjectAccessor.h" #include "Player.h" -ChallengeMode::ChallengeMode(InstanceMap* instance) : _instance(instance) { } +ChallengeMode::ChallengeMode(InstanceMap* instance) : _instance(instance) {} ChallengeMode::~ChallengeMode() = default; void ChallengeMode::Start(uint32 mapChallengeModeId, uint32 keystoneLevel, std::array const& affixes, ObjectGuid starter) @@ -48,7 +50,7 @@ void ChallengeMode::Start(uint32 mapChallengeModeId, uint32 keystoneLevel, std:: BroadcastTimer(_timeLimitMs); // Re-apply stats to already-spawned creatures so they pick up the keystone scaling immediately - // (creatures spawned/reset after this point read the level directly in Get{Max,Base}...ForLevel). + // (creatures spawned/reset after this point read the level directly in Get{Max,Base}...ForLevel). for (auto const& [spawnId, creature] : _instance->GetCreatureBySpawnIdStore()) if (creature && creature->IsAlive()) creature->UpdateLevelDependantStats(); @@ -95,20 +97,43 @@ void ChallengeMode::Complete() _active = false; _completed = true; - uint32 const effectiveSeconds = GetEffectiveTimeMs() / IN_MILLISECONDS; - uint32 const keystoneUpgrade = sChallengeModeMgr.GetKeystoneUpgradeAmount(_mapChallengeModeId, effectiveSeconds); + uint32 const effectiveTimeMs = GetEffectiveTimeMs(); + uint32 const keystoneUpgrade = sChallengeModeMgr.GetKeystoneUpgradeAmount(_mapChallengeModeId, effectiveTimeMs / IN_MILLISECONDS); + + uint32 affixCount = 0; + for (uint32 affixId : _affixes) + if (affixId) + ++affixCount; + + float const runScore = sChallengeModeMgr.CalculateRunScore(_keystoneLevel, effectiveTimeMs, _timeLimitMs, affixCount); + + // Record the run for every player present at completion (keeps the best per dungeon). + MythicPlusRunRecord record; + record.ChallengeModeID = _mapChallengeModeId; + record.Level = _keystoneLevel; + record.DurationMs = effectiveTimeMs; + record.Deaths = _deathCount; + record.CompletionDate = GameTime::GetGameTime(); + record.Score = runScore; + record.Affixes = _affixes; + + _instance->DoOnPlayers([&record](Player* player) + { + if (MythicPlusData* data = player->GetMythicPlusData()) + data->RecordRun(record); + }); // Stop the client dungeon timer. if (Player* starterPlayer = ObjectAccessor::GetPlayer(*_instance, _starterGuid)) if (Group* group = starterPlayer->GetGroup()) group->StartCountdown(CountdownTimerType::ChallengeMode, Seconds(0)); - TC_LOG_INFO("challengemode", "ChallengeMode complete: instance {} challengeMode {} level {} time {}s (+{}s deaths, limit {}s) -> +{} keystone level", + TC_LOG_INFO("challengemode", "ChallengeMode complete: instance {} challengeMode {} level {} time {}s (+{}s deaths, limit {}s) -> +{} keystone, score {:.1f}", _instance->GetInstanceId(), _mapChallengeModeId, _keystoneLevel, GetElapsedMs() / IN_MILLISECONDS, - (_deathCount * DEATH_TIME_PENALTY_MS) / IN_MILLISECONDS, _timeLimitMs / IN_MILLISECONDS, keystoneUpgrade); + (_deathCount * DEATH_TIME_PENALTY_MS) / IN_MILLISECONDS, _timeLimitMs / IN_MILLISECONDS, keystoneUpgrade, runScore); - // Dungeon-score computation, the SMSG_CHALLENGE_MODE_COMPLETE reward packet, the keystone-upgrade item and - // end-of-run / Great Vault loot are applied in later phases (P3/P4). + // The SMSG_CHALLENGE_MODE_COMPLETE reward packet, the keystone-upgrade item and the end-of-run / Great Vault + // loot are applied in Phase 4. } void ChallengeMode::BroadcastTimer(uint32 timeLeftMs) const diff --git a/src/server/game/ChallengeMode/ChallengeModeMgr.cpp b/src/server/game/ChallengeMode/ChallengeModeMgr.cpp index ef371c5f9..65ff25b52 100644 --- a/src/server/game/ChallengeMode/ChallengeModeMgr.cpp +++ b/src/server/game/ChallengeMode/ChallengeModeMgr.cpp @@ -41,7 +41,7 @@ namespace if (pos) result.push_back(uint32(v)); } - catch (std::exception const&) { } + catch (std::exception const&) {} } return result; } @@ -185,6 +185,30 @@ uint32 ChallengeModeMgr::GetKeystoneUpgradeAmount(uint32 challengeModeId, uint32 return 1; // in time -> +1 } +float ChallengeModeMgr::CalculateRunScore(uint32 keystoneLevel, uint32 effectiveTimeMs, uint32 timeLimitMs, uint32 affixCount) const +{ + if (!keystoneLevel) + return 0.0f; + + // Config-tunable Blizzlike-shaped rating. Constants are a server design value (not in the client/DB2); + // defaults approximate the retail shape and can be matched to a live sniff without a rebuild. + float const perLevel = sConfigMgr->GetFloatDefault("ChallengeMode.ScorePerLevel", 7.5f); + float const perAffix = sConfigMgr->GetFloatDefault("ChallengeMode.ScorePerAffix", 5.0f); + float const maxTimeBonus = sConfigMgr->GetFloatDefault("ChallengeMode.ScoreMaxTimeBonus", 7.5f); + + float score = keystoneLevel * perLevel + affixCount * perAffix; + + // Time bonus: +maxTimeBonus for a 40%-under-par clear, tapering to -maxTimeBonus at/over par. + if (timeLimitMs) + { + float const parRatio = float(effectiveTimeMs) / float(timeLimitMs); // < 1.0 = under par + float const t = std::clamp((1.0f - parRatio) / 0.4f, -1.0f, 1.0f); + score += t * maxTimeBonus; + } + + return std::max(0.0f, score); +} + float ChallengeModeMgr::GetHealthMultiplier(uint32 keystoneLevel) const { if (!_healthCurveId || !keystoneLevel) diff --git a/src/server/game/ChallengeMode/ChallengeModeMgr.h b/src/server/game/ChallengeMode/ChallengeModeMgr.h index ae56963c8..94e4a494a 100644 --- a/src/server/game/ChallengeMode/ChallengeModeMgr.h +++ b/src/server/game/ChallengeMode/ChallengeModeMgr.h @@ -54,6 +54,13 @@ public: // keystone levels gained on completion given time spent; 0 = over time (depleted / no upgrade) uint32 GetKeystoneUpgradeAmount(uint32 challengeModeId, uint32 timeUsedSeconds) const; + // Per-run dungeon score (the client's "Mythic+ Rating" contribution). The exact retail constants are a + // server-side design value (not present in the client binary or DB2), so the base-per-level and the + // time bonus/penalty are config-tunable (ChallengeMode.Score*) to be matched to a sniff without a rebuild. + // The shape is Blizzlike: score grows with keystone level, with a bonus for beating par and a penalty for + // running over. affixCount contributes a small per-affix bonus. + float CalculateRunScore(uint32 keystoneLevel, uint32 effectiveTimeMs, uint32 timeLimitMs, uint32 affixCount) const; + // --- Blizzlike scaling engine: creature HP/damage multiplier by keystone level --- // Reproduces the client's C_ChallengeMode.GetPowerLevelDamageHealthMod via GlobalCurve // ChallengeModeHealth(21)/ChallengeModeDamage(22) -> CurvePoint (68275: CurveID 61692/61693, ~+10%/level). diff --git a/src/server/game/ChallengeMode/MythicPlusData.cpp b/src/server/game/ChallengeMode/MythicPlusData.cpp new file mode 100644 index 000000000..03bd9599b --- /dev/null +++ b/src/server/game/ChallengeMode/MythicPlusData.cpp @@ -0,0 +1,102 @@ +/* + * 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 "MythicPlusData.h" +#include "CharacterDatabase.h" +#include "DatabaseEnv.h" +#include "Player.h" + +MythicPlusData::MythicPlusData(Player* owner) : _owner(owner) { } + +void MythicPlusData::LoadFromDB(PreparedQueryResult result) +{ + if (!result) + return; + + do + { + Field* fields = result->Fetch(); + + MythicPlusRunRecord run; + run.ChallengeModeID = fields[0].GetUInt32(); + run.Level = fields[1].GetUInt32(); + run.DurationMs = fields[2].GetUInt32(); + run.Deaths = fields[3].GetUInt32(); + run.CompletionDate = fields[4].GetInt64(); + run.Score = fields[5].GetFloat(); + run.Affixes[0] = fields[6].GetUInt32(); + run.Affixes[1] = fields[7].GetUInt32(); + run.Affixes[2] = fields[8].GetUInt32(); + run.Affixes[3] = fields[9].GetUInt32(); + + _bestRuns[run.ChallengeModeID] = run; + } while (result->NextRow()); +} + +void MythicPlusData::SaveToDB(CharacterDatabaseTransaction trans) +{ + ObjectGuid::LowType guid = _owner->GetGUID().GetCounter(); + + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_MYTHIC_PLUS); + stmt->setUInt64(0, guid); + trans->Append(stmt); + + for (auto const& [challengeModeId, run] : _bestRuns) + { + stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_MYTHIC_PLUS); + stmt->setUInt64(0, guid); + stmt->setUInt32(1, run.ChallengeModeID); + stmt->setUInt32(2, run.Level); + stmt->setUInt32(3, run.DurationMs); + stmt->setUInt32(4, run.Deaths); + stmt->setInt64(5, run.CompletionDate); + stmt->setFloat(6, run.Score); + stmt->setUInt32(7, run.Affixes[0]); + stmt->setUInt32(8, run.Affixes[1]); + stmt->setUInt32(9, run.Affixes[2]); + stmt->setUInt32(10, run.Affixes[3]); + trans->Append(stmt); + } +} + +bool MythicPlusData::RecordRun(MythicPlusRunRecord const& run) +{ + auto itr = _bestRuns.find(run.ChallengeModeID); + if (itr != _bestRuns.end()) + { + MythicPlusRunRecord const& best = itr->second; + if (run.Level < best.Level || (run.Level == best.Level && run.DurationMs >= best.DurationMs)) + return false; + } + + _bestRuns[run.ChallengeModeID] = run; + return true; +} + +MythicPlusRunRecord const* MythicPlusData::GetBestRun(uint32 challengeModeId) const +{ + auto itr = _bestRuns.find(challengeModeId); + return itr != _bestRuns.end() ? &itr->second : nullptr; +} + +float MythicPlusData::GetOverallScore() const +{ + float total = 0.0f; + for (auto const& [challengeModeId, run] : _bestRuns) + total += run.Score; + return total; +} diff --git a/src/server/game/ChallengeMode/MythicPlusData.h b/src/server/game/ChallengeMode/MythicPlusData.h new file mode 100644 index 000000000..84f57cdda --- /dev/null +++ b/src/server/game/ChallengeMode/MythicPlusData.h @@ -0,0 +1,63 @@ +/* + * 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 . + */ + +#ifndef MythicPlusData_h__ +#define MythicPlusData_h__ + +#include "DatabaseEnvFwd.h" +#include "Define.h" +#include +#include + +class Player; + +struct MythicPlusRunRecord +{ + uint32 ChallengeModeID = 0; + uint32 Level = 0; + uint32 DurationMs = 0; + uint32 Deaths = 0; + int64 CompletionDate = 0; // unix timestamp of the run + float Score = 0.0f; + std::array Affixes = { }; +}; + +// Per-player Mythic+ progression: the best run recorded for each dungeon and the overall rating derived from them. +// Attached to Player (see Player::GetMythicPlusData), loaded/saved with the character. +class TC_GAME_API MythicPlusData +{ +public: + explicit MythicPlusData(Player* owner); + + void LoadFromDB(PreparedQueryResult result); + void SaveToDB(CharacterDatabaseTransaction trans); + + // Records a completed run, keeping the best per dungeon (higher level wins, ties broken by faster time). + // Returns true if it became the new best for that dungeon. + bool RecordRun(MythicPlusRunRecord const& run); + MythicPlusRunRecord const* GetBestRun(uint32 challengeModeId) const; + std::unordered_map const& GetBestRuns() const { return _bestRuns; } + + // Sum of the best-run scores across all dungeons (the client's overall Mythic+ Rating). + float GetOverallScore() const; + +private: + Player* _owner; + std::unordered_map _bestRuns; +}; + +#endif // MythicPlusData_h__ diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 16c28e42f..ba5c1f3c1 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -149,6 +149,10 @@ #include #include +//WowCommunity +#include "MythicPlusData.h" +//WowCommunity + // corpse reclaim times #define DEATH_EXPIRE_STEP (5*MINUTE) #define MAX_DEATH_COUNT 3 @@ -18825,6 +18829,11 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_GARRISON_ARCHIVED_MISSIONS))) _garrisons[garrison->GetType()] = std::move(garrison); + //WowCommunity + _mythicPlusData = std::make_unique(this); + _mythicPlusData->LoadFromDB(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MYTHIC_PLUS)); + //WowCommunity + _InitHonorLevelOnLoadFromDB(fields.honor, fields.honorLevel); _restMgr->LoadRestBonus(REST_TYPE_HONOR, fields.honorRestState, fields.honorRestBonus); @@ -20755,7 +20764,11 @@ void Player::SaveToDB(LoginDatabaseTransaction loginTransaction, CharacterDataba _SaveCharacterBankTabSettings(trans); for (auto const& [type, garrison] : _garrisons) garrison->SaveToDB(trans); - + //WowCommunity + if (_mythicPlusData) + _mythicPlusData->SaveToDB(trans); + //WowCommunity + // check if stats should only be saved on logout // save stats can be out of transaction if (m_session->isLogingOut() || !sWorld->getBoolConfig(CONFIG_STATS_SAVE_ONLY_ON_LOGOUT)) diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 8f2103343..98406f56b 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -34,7 +34,9 @@ #include "QuestDef.h" #include "SceneMgr.h" #include - +//WowCommunity +class MythicPlusData; +//WowCommunity struct AccessRequirement; struct AchievementEntry; struct AreaTableEntry; @@ -1056,6 +1058,7 @@ enum PlayerLoginQueryIndex PLAYER_LOGIN_QUERY_LOAD_PERKS_PURCHASES, PLAYER_LOGIN_QUERY_LOAD_PERKS_FROZEN, PLAYER_LOGIN_QUERY_LOAD_PERKS_MILESTONES, + PLAYER_LOGIN_QUERY_LOAD_MYTHIC_PLUS, MAX_PLAYER_LOGIN_QUERY }; @@ -3193,6 +3196,9 @@ class TC_GAME_API Player final : public Unit, public GridObject void SendAreaSpiritHealerTime(Unit* spiritHealer) const; void SendAreaSpiritHealerTime(ObjectGuid const& spiritHealerGUID, int32 timeLeft) const; + //WowCommunity + MythicPlusData* GetMythicPlusData() const { return _mythicPlusData.get(); } + //WowCommunity protected: // Gamemaster whisper whitelist GuidList WhisperList; @@ -3559,6 +3565,9 @@ class TC_GAME_API Player final : public Unit, public GridObject uint8 _warbandMaxLevelCharCount = 0; + //WowCommunity + std::unique_ptr _mythicPlusData; + //WowCommunity bool _advancedCombatLoggingEnabled; // variables to save health and mana before duel and restore them after duel diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index bb883a362..e7e0974f7 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -94,6 +94,9 @@ #include #include #include +//WowCommunity +#include "ChallengeMode.h" +//WowCommunity float baseMoveSpeed[MAX_MOVE_TYPE] = { @@ -9286,6 +9289,14 @@ void Unit::setDeathState(DeathState s) // players in instance don't have ZoneScript, but they have InstanceScript if (ZoneScript* zoneScript = GetZoneScript() ? GetZoneScript() : GetInstanceScript()) zoneScript->OnUnitDeath(this); + + //WowCommunity + // Mythic Keystone: each player death adds a time penalty to the active run. + if (IsPlayer()) + if (InstanceMap* instanceMap = GetMap()->ToInstanceMap()) + if (ChallengeMode* challenge = instanceMap->GetChallengeMode()) + challenge->OnPlayerDeath(ToPlayer()); + //WowCommunity } else if (s == JUST_RESPAWNED) RemoveUnitFlag(UNIT_FLAG_SKINNABLE); // clear skinnable for creature and player (at battleground) diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index fd4105768..530ce8b2f 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -367,6 +367,11 @@ bool LoginQueryHolder::Initialize() stmt->setUInt64(0, lowGuid); res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_BANK_TAB_SETTINGS, stmt); + //WowCommunity + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_MYTHIC_PLUS); + stmt->setUInt64(0, lowGuid); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_MYTHIC_PLUS, stmt); + //WowCommunity return res; }