From 1b208f2b1bd140a8e295b01e643365117d0dcb9f Mon Sep 17 00:00:00 2001 From: Flerp Date: Fri, 14 Nov 2025 17:37:26 -0800 Subject: [PATCH] Initial fix for new methods --- LICENSE | 21 + README.md | 26 + conf/challenge_modes.conf.dist | 132 +++ conf/conf.sh.dist | 32 + data/sql/db-auth/updates/.gitkeep | 0 data/sql/db-characters/updates/.gitkeep | 0 .../db-world/base/challenge_mode_gobject.sql | 15 + data/sql/db-world/updates/.gitkeep | 0 include.sh | 10 + src/ChallengeModes.cpp | 861 ++++++++++++++++++ src/ChallengeModes.h | 106 +++ src/ChallengeModes_loader.cpp | 14 + 12 files changed, 1217 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 conf/challenge_modes.conf.dist create mode 100644 conf/conf.sh.dist create mode 100644 data/sql/db-auth/updates/.gitkeep create mode 100644 data/sql/db-characters/updates/.gitkeep create mode 100644 data/sql/db-world/base/challenge_mode_gobject.sql create mode 100644 data/sql/db-world/updates/.gitkeep create mode 100644 include.sh create mode 100644 src/ChallengeModes.cpp create mode 100644 src/ChallengeModes.h create mode 100644 src/ChallengeModes_loader.cpp diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9031171 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 AzerothCore + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..e579dd1 --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +# AzerothCore Challenge Modes +Challenge Modes Module for AzerothCore + +This module adds the following challenge modes: + +- **Hardcore** - Players who die are permanently ghosts and can never be revived. +- **Semi-Hardcore** - Players who die lose all worn equipment and carried gold. +- **Self Crafted** - Players can only wear equipment that they have crafted. +- **Item Quality Level** - Players can only wear equipment that is of Normal or Poor quality +- **Slow XP Gain** - Players receive 0.5x the normal amount of XP. +- **Very Slow XP Gain** - Players receive 0.25x the normal amount of XP. +- **Quest XP Only** - Players can receive XP only from quests +- **Iron Man Mode** - Enforces the [Iron Man Ruleset](https://wowchallenges.com/challangeinfo/iron-man/) + +Challenges can be activated per-character by interacting with the Shrine of Challenge added near the graveyard of each starting area. +Challenges can only be enabled on characters at level 1 (or level 55 for Death Knights). + +Multiple challenges can be activated on a single character as long as they do not conflict, such as Hardcore and Semi-Hardcore. + +Rewards for reaching level thresholds for each challenge can be added using the Config file, and can include: +- Items +- Titles +- Talent Points +- Increased XP Rate + +Please note that this module uses Player Settings to store enabled challenges, so please ensure EnablePlayerSettings is set to 1 in your worldserver.conf. diff --git a/conf/challenge_modes.conf.dist b/conf/challenge_modes.conf.dist new file mode 100644 index 0000000..6483775 --- /dev/null +++ b/conf/challenge_modes.conf.dist @@ -0,0 +1,132 @@ +# +# Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 +# + +[worldserver] + +######################################## +# Challenge Mods Config +######################################## +# ChallengeModes.Enable +# Description: Enable challenge modes. +# If set to 0, all challenges will be disabled, and the challenge mode selection object will not be present in starting areas. +# Default: 0 - Disabled +# 1 - Enabled +# + +ChallengeModes.Enable = 1 +# +# The following challenge modes are available: +# Hardcore - Players who die are permanently ghosts and can never be revived. +# SemiHardcore - Players who die lose all worn equipment and carried gold. Cannot be enabled at the same time as Hardcore. +# SelfCrafted - Players can only wear equipment that they have crafted. +# ItemQualityLevel - Players can only wear equipment that is of Normal or Poor quality +# SlowXpGain - Players receive 0.5x the normal amount of XP. +# VerySlowXpGain - Players receive 0.25x the normal amount of XP. Provides all rewards of SlowXpGain as well. +# QuestXpOnly - Players can receive XP only from quests +# IronManMode - Enforces the Iron Man ruleset (https://wowchallenges.com/challangeinfo/iron-man/) +# +# +# The options for each mode follow the same format. "" is replaced with the name of the challenge, such as Hardcore. The following options are possible: +# .Enable = 1 +# If set to 0, this challenge option will not be selectable at the challenge modes object. +# .TitleRewards = "" +# Rewards titles for players when reaching the given levels with the challenge enabled. +# The IDs used are from CharTitles.dbc. The format is the level followed by the title ID, separated by commas. +# Example: .TitleRewards = "60 143, 70 123, 80 145" +# .XPMultiplier = "" +# Multiplies XP the player gains if this challege is enabled. This is a multiplier values, so bonus are applied multiplicatively. +# This reward option is not available for SlowXpGain and VerySlowXpGain +# Example: .XPMultiplier = "1.5" +# .TalentRewards = "" +# Rewards talent points for players when reaching the given levels with the challenge enabled. +# The format is the level followed by the number of talent points given at that level, separated by commas. +# Example: .TalentRewards = "30 1, 35 1, 40 1, 45 1, 50 1, 60 2, 70 2, 80 5" +# .ItemRewards = "" +# Rewards items for players when reaching the given levels with the challenge enabled. +# The IDs used are item entry IDs. The format is the level followed by the item ID, separated by commas. +# Example: .ItemRewards = "80 54811" +# .DisableLevel = "" +# When the player reaches this level, the challenge will be automatically disabled. +# To never disable a challenge, set to 0. +# Example: .DisableLevel = 80 +# .ItemRewardAmount = 1 +# Rewards a set amount of .ItemRewards for players when reaching the given levels with the challenge enabled. +# Example: .ItemRewards = 1 +# .AchievementReward = "" +# Rewards an achievement for players when reaching the given levels with the challenge enabled. +# The IDs used are achievement entry IDs. The format is the level followed by the achievement ID, separated by commas. +# Example: .AchievementReward = "80 1234" +# + +Hardcore.Enable = 1 +Hardcore.TitleRewards = "" +Hardcore.XPMultiplier = 1 +Hardcore.TalentRewards = "" +Hardcore.ItemRewards = "" +Hardcore.ItemRewardAmount = 1 +Hardcore.DisableLevel = 0 +Hardcore.AchievementReward = "" + +SemiHardcore.Enable = 1 +SemiHardcore.TitleRewards = "" +SemiHardcore.XPMultiplier = 1 +SemiHardcore.TalentRewards = "" +SemiHardcore.ItemRewards = "" +SemiHardcore.ItemRewardAmount = 1 +SemiHardcore.DisableLevel = 0 +SemiHardcore.AchievementReward = "" + +SelfCrafted.Enable = 1 +SelfCrafted.TitleRewards = "" +SelfCrafted.XPMultiplier = 1 +SelfCrafted.TalentRewards = "" +SelfCrafted.ItemRewards = "" +SelfCrafted.ItemRewardAmount = 1 +SelfCrafted.DisableLevel = 0 +SelfCrafted.AchievementReward = "" + +ItemQualityLevel.Enable = 1 +ItemQualityLevel.TitleRewards = "" +ItemQualityLevel.XPMultiplier = 1 +ItemQualityLevel.TalentRewards = "" +ItemQualityLevel.ItemRewards = "" +ItemQualityLevel.ItemRewardAmount = 1 +ItemQualityLevel.DisableLevel = 0 +ItemQualityLevel.AchievementReward = "" + +SlowXpGain.Enable = 1 +SlowXpGain.TitleRewards = "" +SlowXpGain.TalentRewards = "" +SlowXpGain.ItemRewards = "" +SlowXpGain.ItemRewardAmount = 1 +SlowXpGain.DisableLevel = 0 +SlowXpGain.XPMultiplier = 0.50 +SlowXpGain.AchievementReward = "" + +VerySlowXpGain.Enable = 1 +VerySlowXpGain.TitleRewards = "" +VerySlowXpGain.TalentRewards = "" +VerySlowXpGain.ItemRewards = "" +VerySlowXpGain.ItemRewardAmount = 1 +VerySlowXpGain.DisableLevel = 0 +VerySlowXpGain.XPMultiplier = 0.25 +VerySlowXpGain.AchievementReward = "" + +QuestXpOnly.Enable = 1 +QuestXpOnly.TitleRewards = "" +QuestXpOnly.XPMultiplier = 1 +QuestXpOnly.TalentRewards = "" +QuestXpOnly.ItemRewards = "" +QuestXpOnly.ItemRewardAmount = 1 +QuestXpOnly.DisableLevel = 0 +QuestXpOnly.AchievementReward = "" + +IronMan.Enable = 1 +IronMan.TitleRewards = "" +IronMan.TalentRewards = "" +IronMan.ItemRewards = "" +IronMan.ItemRewardAmount = 1 +IronMan.DisableLevel = 0 +IronMan.XPMultiplier = 1 +IronMan.AchievementReward = "" diff --git a/conf/conf.sh.dist b/conf/conf.sh.dist new file mode 100644 index 0000000..7b59da6 --- /dev/null +++ b/conf/conf.sh.dist @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +## CUSTOM SQL - Important file used by the db_assembler.sh +## Keep only the required variables (base sql files or updates, depending on the DB) + +## BASE SQL + +DB_AUTH_CUSTOM_PATHS+=( + "$MOD_SKELETON_ROOT/data/sql/db-auth/base/" +) + +DB_CHARACTERS_CUSTOM_PATHS+=( + "$MOD_SKELETON_ROOT/data/sql/db-characters/base/" +) + +DB_WORLD_CUSTOM_PATHS+=( + "$MOD_SKELETON_ROOT/data/sql/db-world/base/" +) + +## UPDATES + +DB_AUTH_UPDATES_PATHS+=( + "$MOD_SKELETON_ROOT/data/sql/db-auth/updates/" +) + +DB_CHARACTERS_UPDATES_PATHS+=( + "$MOD_SKELETON_ROOT/data/sql/db-characters/updates/" +) + +DB_WORLD_UPDATES_PATHS+=( + "$MOD_SKELETON_ROOT/data/sql/db-world/updates/" +) diff --git a/data/sql/db-auth/updates/.gitkeep b/data/sql/db-auth/updates/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/data/sql/db-characters/updates/.gitkeep b/data/sql/db-characters/updates/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/data/sql/db-world/base/challenge_mode_gobject.sql b/data/sql/db-world/base/challenge_mode_gobject.sql new file mode 100644 index 0000000..e442d5b --- /dev/null +++ b/data/sql/db-world/base/challenge_mode_gobject.sql @@ -0,0 +1,15 @@ +DELETE FROM `gameobject_template` WHERE `entry`=254605; +INSERT INTO `gameobject_template` (`entry`, `type`, `displayId`, `name`, `IconName`, `castBarCaption`, `unk1`, `size`, `Data0`, `Data1`, `Data2`, `Data3`, `Data4`, `Data5`, `Data6`, `Data7`, `Data8`, `Data9`, `Data10`, `Data11`, `Data12`, `Data13`, `Data14`, `Data15`, `Data16`, `Data17`, `Data18`, `Data19`, `Data20`, `Data21`, `Data22`, `Data23`, `AIName`, `ScriptName`, `VerifiedBuild`) VALUES +(254605, 2, 6925, 'Shrine of Challenge', '', '', '', 1.2, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '', 'gobject_challenge_modes', 0); + +DELETE FROM `gameobject` WHERE `guid` BETWEEN 5530536 AND 5530544; +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`) VALUES +(5530536, 254605, 0, 0, 0, 1, 1, -8920.64, -178.191, 80.891, 4.3208, -0, -0, -0.83116, 0.556033, 300, 0, 1, '', 0), +(5530537, 254605, 0, 0, 0, 1, 1, -6135.29, 336.119, 402.238, 5.55195, -0, -0, -0.357526, 0.933903, 300, 0, 1, '', 0), +(5530538, 254605, 1, 0, 0, 1, 1, 10415.2, 809.575, 1318.19, 2.37082, -0, -0, -0.926654, -0.375916, 300, 0, 1, '', 0), +(5530539, 254605, 530, 0, 0, 1, 1, -4147.11, -13667.7, 75.8166, 5.06421, -0, -0, -0.572447, 0.819942, 300, 0, 1, '', 0), +(5530540, 254605, 1, 0, 0, 1, 1, -658.88, -4311.88, 45.666, 3.06225, -0, -0, -0.999213, -0.0396603, 300, 0, 1, '', 0), +(5530541, 254605, 0, 0, 0, 1, 1, 1842.91, 1651.33, 95.6206, 1.58336, -0, -0, -0.711535, -0.702651, 300, 0, 1, '', 0), +(5530542, 254605, 1, 0, 0, 1, 1, -2994.22, -136.321, 77.9491, 1.05411, -0, -0, -0.502992, -0.864291, 300, 0, 1, '', 0), +(5530543, 254605, 530, 0, 0, 1, 1, 10452, -6389.91, 43.7962, 1.84851, -0, -0, -0.798173, -0.602429, 300, 0, 1, '', 0), +(5530544, 254605, 609, 0, 0, 1, 1, 2415.84, -5649.91, 376.819, 1.87356, -0, -0, -0.805655, -0.592385, 300, 0, 1, '', 0); diff --git a/data/sql/db-world/updates/.gitkeep b/data/sql/db-world/updates/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/include.sh b/include.sh new file mode 100644 index 0000000..a241572 --- /dev/null +++ b/include.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +## GETS THE CURRENT MODULE ROOT DIRECTORY +MOD_SKELETON_ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )/" && pwd )" + +source $MOD_SKELETON_ROOT"/conf/conf.sh.dist" + +if [ -f $MOD_SKELETON_ROOT"/conf/conf.sh" ]; then + source $MOD_SKELETON_ROOT"/conf/conf.sh" +fi diff --git a/src/ChallengeModes.cpp b/src/ChallengeModes.cpp new file mode 100644 index 0000000..c1c4a62 --- /dev/null +++ b/src/ChallengeModes.cpp @@ -0,0 +1,861 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + */ + +#include "ChallengeModes.h" + +ChallengeModes* ChallengeModes::instance() +{ + static ChallengeModes instance; + return &instance; +} + +bool ChallengeModes::challengeEnabledForPlayer(ChallengeModeSettings setting, Player* player) const +{ + if (!enabled() || !challengeEnabled(setting)) + { + return false; + } + return player->GetPlayerSetting("mod-challenge-modes", setting).value; +} + +bool ChallengeModes::challengeEnabled(ChallengeModeSettings setting) const +{ + switch (setting) + { + case SETTING_HARDCORE: + return hardcoreEnable; + case SETTING_SEMI_HARDCORE: + return semiHardcoreEnable; + case SETTING_SELF_CRAFTED: + return selfCraftedEnable; + case SETTING_ITEM_QUALITY_LEVEL: + return itemQualityLevelEnable; + case SETTING_SLOW_XP_GAIN: + return slowXpGainEnable; + case SETTING_VERY_SLOW_XP_GAIN: + return verySlowXpGainEnable; + case SETTING_QUEST_XP_ONLY: + return questXpOnlyEnable; + case SETTING_IRON_MAN: + return ironManEnable; + case HARDCORE_DEAD: + break; + } + return false; +} + +uint32 ChallengeModes::getDisableLevel(ChallengeModeSettings setting) const +{ + switch (setting) + { + case SETTING_HARDCORE: + return hardcoreDisableLevel; + case SETTING_SEMI_HARDCORE: + return semiHardcoreDisableLevel; + case SETTING_SELF_CRAFTED: + return selfCraftedDisableLevel; + case SETTING_ITEM_QUALITY_LEVEL: + return itemQualityLevelDisableLevel; + case SETTING_SLOW_XP_GAIN: + return slowXpGainDisableLevel; + case SETTING_VERY_SLOW_XP_GAIN: + return verySlowXpGainDisableLevel; + case SETTING_QUEST_XP_ONLY: + return questXpOnlyDisableLevel; + case SETTING_IRON_MAN: + return ironManDisableLevel; + case HARDCORE_DEAD: + break; + } + return 0; +} + +float ChallengeModes::getXpBonusForChallenge(ChallengeModeSettings setting) const +{ + switch (setting) + { + case SETTING_HARDCORE: + return hardcoreXpBonus; + case SETTING_SEMI_HARDCORE: + return semiHardcoreXpBonus; + case SETTING_SELF_CRAFTED: + return selfCraftedXpBonus; + case SETTING_ITEM_QUALITY_LEVEL: + return itemQualityLevelXpBonus; + case SETTING_SLOW_XP_GAIN: + return slowXpGainBonus; + case SETTING_VERY_SLOW_XP_GAIN: + return verySlowXpGainBonus; + case SETTING_QUEST_XP_ONLY: + return questXpOnlyXpBonus; + case SETTING_IRON_MAN: + return ironManXpBonus; + case HARDCORE_DEAD: + break; + } + return 1; +} + +const std::unordered_map *ChallengeModes::getTitleMapForChallenge(ChallengeModeSettings setting) const +{ + switch (setting) + { + case SETTING_HARDCORE: + return &hardcoreTitleRewards; + case SETTING_SEMI_HARDCORE: + return &semiHardcoreTitleRewards; + case SETTING_SELF_CRAFTED: + return &selfCraftedTitleRewards; + case SETTING_ITEM_QUALITY_LEVEL: + return &itemQualityLevelTitleRewards; + case SETTING_SLOW_XP_GAIN: + return &slowXpGainTitleRewards; + case SETTING_VERY_SLOW_XP_GAIN: + return &verySlowXpGainTitleRewards; + case SETTING_QUEST_XP_ONLY: + return &questXpOnlyTitleRewards; + case SETTING_IRON_MAN: + return &ironManTitleRewards; + case HARDCORE_DEAD: + break; + } + return {}; +} + +const std::unordered_map *ChallengeModes::getTalentMapForChallenge(ChallengeModeSettings setting) const +{ + switch (setting) + { + case SETTING_HARDCORE: + return &hardcoreTalentRewards; + case SETTING_SEMI_HARDCORE: + return &semiHardcoreTalentRewards; + case SETTING_SELF_CRAFTED: + return &selfCraftedTalentRewards; + case SETTING_ITEM_QUALITY_LEVEL: + return &itemQualityLevelTalentRewards; + case SETTING_SLOW_XP_GAIN: + return &slowXpGainTalentRewards; + case SETTING_VERY_SLOW_XP_GAIN: + return &verySlowXpGainTalentRewards; + case SETTING_QUEST_XP_ONLY: + return &questXpOnlyTalentRewards; + case SETTING_IRON_MAN: + return &ironManTalentRewards; + case HARDCORE_DEAD: + break; + } + return {}; +} + +const std::unordered_map *ChallengeModes::getItemMapForChallenge(ChallengeModeSettings setting) const +{ + switch (setting) + { + case SETTING_HARDCORE: + return &hardcoreItemRewards; + case SETTING_SEMI_HARDCORE: + return &semiHardcoreItemRewards; + case SETTING_SELF_CRAFTED: + return &selfCraftedItemRewards; + case SETTING_ITEM_QUALITY_LEVEL: + return &itemQualityLevelItemRewards; + case SETTING_SLOW_XP_GAIN: + return &slowXpGainItemRewards; + case SETTING_VERY_SLOW_XP_GAIN: + return &verySlowXpGainItemRewards; + case SETTING_QUEST_XP_ONLY: + return &questXpOnlyItemRewards; + case SETTING_IRON_MAN: + return &ironManItemRewards; + case HARDCORE_DEAD: + break; + } + return {}; +} + +uint32 ChallengeModes::getItemRewardAmount(ChallengeModeSettings setting) const +{ + switch (setting) + { + case SETTING_HARDCORE: + return hardcoreItemRewardAmount; + case SETTING_SEMI_HARDCORE: + return semiHardcoreItemRewardAmount; + case SETTING_SELF_CRAFTED: + return selfCraftedItemRewardAmount; + case SETTING_ITEM_QUALITY_LEVEL: + return itemQualityLevelItemRewardAmount; + case SETTING_SLOW_XP_GAIN: + return slowXpGainItemRewardAmount; + case SETTING_VERY_SLOW_XP_GAIN: + return verySlowXpGainItemRewardAmount; + case SETTING_QUEST_XP_ONLY: + return questXpOnlyItemRewardAmount; + case SETTING_IRON_MAN: + return ironManItemRewardAmount; + case HARDCORE_DEAD: + break; + } + return 0; +} + +const std::unordered_map *ChallengeModes::getAchievementMapForChallenge(ChallengeModeSettings setting) const +{ + switch (setting) + { + case SETTING_HARDCORE: + return &hardcoreAchievementReward; + case SETTING_SEMI_HARDCORE: + return &semiHardcoreAchievementReward; + case SETTING_SELF_CRAFTED: + return &selfCraftedAchievementReward; + case SETTING_ITEM_QUALITY_LEVEL: + return &itemQualityLevelAchievementReward; + case SETTING_SLOW_XP_GAIN: + return &slowXpGainAchievementReward; + case SETTING_VERY_SLOW_XP_GAIN: + return &verySlowXpGainAchievementReward; + case SETTING_QUEST_XP_ONLY: + return &questXpOnlyAchievementReward; + case SETTING_IRON_MAN: + return &ironManAchievementReward; + case HARDCORE_DEAD: + break; + } + return {}; +} + +class ChallengeModes_WorldScript : public WorldScript +{ +public: + ChallengeModes_WorldScript() + : WorldScript("ChallengeModes_WorldScript") + {} + + void OnBeforeConfigLoad(bool /*reload*/) override + { + LoadConfig(); + } + +private: + static void LoadStringToMap(std::unordered_map &mapToLoad, const std::string &configString) + { + std::string delimitedValue; + std::stringstream configIdStream; + + configIdStream.str(configString); + // Process each config ID in the string, delimited by the comma - "," and then space " " + while (std::getline(configIdStream, delimitedValue, ',')) + { + std::string pairOne, pairTwo; + std::stringstream configPairStream(delimitedValue); + configPairStream>>pairOne>>pairTwo; + auto configLevel = atoi(pairOne.c_str()); + auto rewardValue = atoi(pairTwo.c_str()); + mapToLoad[configLevel] = rewardValue; + } + } + + static void LoadConfig() + { + sChallengeModes->challengesEnabled = sConfigMgr->GetOption("ChallengeModes.Enable", false); + if (sChallengeModes->enabled()) + { + for (auto& [confName, rewardMap] : sChallengeModes->rewardConfigMap) + { + rewardMap->clear(); + LoadStringToMap(*rewardMap, sConfigMgr->GetOption(confName, "")); + } + + sChallengeModes->hardcoreEnable = sConfigMgr->GetOption("Hardcore.Enable", true); + sChallengeModes->semiHardcoreEnable = sConfigMgr->GetOption("SemiHardcore.Enable", true); + sChallengeModes->selfCraftedEnable = sConfigMgr->GetOption("SelfCrafted.Enable", true); + sChallengeModes->itemQualityLevelEnable = sConfigMgr->GetOption("ItemQualityLevel.Enable", true); + sChallengeModes->slowXpGainEnable = sConfigMgr->GetOption("SlowXpGain.Enable", true); + sChallengeModes->verySlowXpGainEnable = sConfigMgr->GetOption("VerySlowXpGain.Enable", true); + sChallengeModes->questXpOnlyEnable = sConfigMgr->GetOption("QuestXpOnly.Enable", true); + sChallengeModes->ironManEnable = sConfigMgr->GetOption("IronMan.Enable", true); + + sChallengeModes->hardcoreDisableLevel = sConfigMgr->GetOption("Hardcore.DisableLevel", 0); + sChallengeModes->semiHardcoreDisableLevel = sConfigMgr->GetOption("SemiHardcore.DisableLevel", 0); + sChallengeModes->selfCraftedDisableLevel = sConfigMgr->GetOption("SelfCrafted.DisableLevel", 0); + sChallengeModes->itemQualityLevelDisableLevel = sConfigMgr->GetOption("ItemQualityLevel.DisableLevel", 0); + sChallengeModes->slowXpGainDisableLevel = sConfigMgr->GetOption("SlowXpGain.DisableLevel", 0); + sChallengeModes->verySlowXpGainDisableLevel = sConfigMgr->GetOption("VerySlowXpGain.DisableLevel", 0); + sChallengeModes->questXpOnlyDisableLevel = sConfigMgr->GetOption("QuestXpOnly.DisableLevel", 0); + sChallengeModes->ironManDisableLevel = sConfigMgr->GetOption("IronMan.DisableLevel", 0); + + sChallengeModes->hardcoreXpBonus = sConfigMgr->GetOption("Hardcore.XPMultiplier", 1.0f); + sChallengeModes->semiHardcoreXpBonus = sConfigMgr->GetOption("SemiHardcore.XPMultiplier", 1.0f); + sChallengeModes->selfCraftedXpBonus = sConfigMgr->GetOption("SelfCrafted.XPMultiplier", 1.0f); + sChallengeModes->itemQualityLevelXpBonus = sConfigMgr->GetOption("ItemQualityLevel.XPMultiplier", 1.0f); + sChallengeModes->questXpOnlyXpBonus = sConfigMgr->GetOption("QuestXpOnly.XPMultiplier", 1.0f); + sChallengeModes->slowXpGainBonus = sConfigMgr->GetOption("SlowXpGain.XPMultiplier", 0.50f); + sChallengeModes->verySlowXpGainBonus = sConfigMgr->GetOption("VerySlowXpGain.XPMultiplier", 0.25f); + sChallengeModes->ironManXpBonus = sConfigMgr->GetOption("IronMan.XPMultiplier", 1.0f); + + sChallengeModes->hardcoreItemRewardAmount = sConfigMgr->GetOption("Hardcore.ItemRewardAmount", 1); + sChallengeModes->semiHardcoreItemRewardAmount = sConfigMgr->GetOption("SemiHardcore.ItemRewardAmount", 1); + sChallengeModes->selfCraftedItemRewardAmount = sConfigMgr->GetOption("SelfCrafted.ItemRewardAmount", 1); + sChallengeModes->itemQualityLevelItemRewardAmount = sConfigMgr->GetOption("ItemQualityLevel.ItemRewardAmount", 1); + sChallengeModes->slowXpGainItemRewardAmount = sConfigMgr->GetOption("SlowXpGain.ItemRewardAmount", 1); + sChallengeModes->verySlowXpGainItemRewardAmount = sConfigMgr->GetOption("VerySlowXpGain.ItemRewardAmount", 1); + sChallengeModes->questXpOnlyItemRewardAmount = sConfigMgr->GetOption("QuestXpOnly.ItemRewardAmount", 1); + sChallengeModes->ironManItemRewardAmount = sConfigMgr->GetOption("IronMan.ItemRewardAmount", 1); + + LoadStringToMap(sChallengeModes->hardcoreAchievementReward, sConfigMgr->GetOption("Hardcore.AchievementReward", "")); + LoadStringToMap(sChallengeModes->semiHardcoreAchievementReward, sConfigMgr->GetOption("SemiHardcore.AchievementReward", "")); + LoadStringToMap(sChallengeModes->selfCraftedAchievementReward, sConfigMgr->GetOption("SelfCrafted.AchievementReward", "")); + LoadStringToMap(sChallengeModes->itemQualityLevelAchievementReward, sConfigMgr->GetOption("ItemQualityLevel.AchievementReward", "")); + LoadStringToMap(sChallengeModes->slowXpGainAchievementReward, sConfigMgr->GetOption("SlowXpGain.AchievementReward", "")); + LoadStringToMap(sChallengeModes->verySlowXpGainAchievementReward, sConfigMgr->GetOption("VerySlowXpGain.AchievementReward", "")); + LoadStringToMap(sChallengeModes->questXpOnlyAchievementReward, sConfigMgr->GetOption("QuestXpOnly.AchievementReward", "")); + LoadStringToMap(sChallengeModes->ironManAchievementReward, sConfigMgr->GetOption("IronMan.AchievementReward", "")); + } + } +}; + +class ChallengeMode : public PlayerScript +{ +public: + explicit ChallengeMode(const char *scriptName, + ChallengeModeSettings settingName) + : PlayerScript(scriptName), settingName(settingName) + { } + + static bool mapContainsKey(const std::unordered_map* mapToCheck, uint8 key) + { + return (mapToCheck->find(key) != mapToCheck->end()); + } + + void OnPlayerGiveXP(Player* player, uint32& amount, Unit* /*victim*/, uint8 /*xpSource*/) override + { + if (!sChallengeModes->challengeEnabledForPlayer(settingName, player)) + { + return; + } + amount *= sChallengeModes->getXpBonusForChallenge(settingName); + } + +void OnPlayerLevelChanged(Player* player, uint8 /*oldlevel*/) override +{ + if (!sChallengeModes->challengeEnabledForPlayer(settingName, player)) + { + return; + } + + const std::unordered_map* titleRewardMap = sChallengeModes->getTitleMapForChallenge(settingName); + const std::unordered_map* talentRewardMap = sChallengeModes->getTalentMapForChallenge(settingName); + const std::unordered_map* itemRewardMap = sChallengeModes->getItemMapForChallenge(settingName); + const std::unordered_map* achievementRewardMap = sChallengeModes->getAchievementMapForChallenge(settingName); + uint8 level = player->GetLevel(); + + if (mapContainsKey(titleRewardMap, level)) + { + CharTitlesEntry const* titleInfo = sCharTitlesStore.LookupEntry(titleRewardMap->at(level)); + if (!titleInfo) + { + LOG_ERROR("mod-challenge-modes", "Invalid title ID {}!", titleRewardMap->at(level)); + return; + } + ChatHandler handler(player->GetSession()); + std::string tNameLink = handler.GetNameLink(player); + std::string titleNameStr = Acore::StringFormat(player->getGender() == GENDER_MALE ? titleInfo->nameMale[handler.GetSessionDbcLocale()] : titleInfo->nameFemale[handler.GetSessionDbcLocale()], player->GetName()); + player->SetTitle(titleInfo); + } + + if (mapContainsKey(talentRewardMap, level)) + { + player->RewardExtraBonusTalentPoints(talentRewardMap->at(level)); + } + + if (mapContainsKey(achievementRewardMap, level)) + { + AchievementEntry const* achievementInfo = sAchievementStore.LookupEntry(achievementRewardMap->at(level)); + if (!achievementInfo) + { + LOG_ERROR("mod-challenge-modes", "Invalid Achievement ID {}!", achievementRewardMap->at(level)); + return; + } + + ChatHandler handler(player->GetSession()); + std::string tNameLink = handler.GetNameLink(player); + player->CompletedAchievement(achievementInfo); + } + + if (mapContainsKey(itemRewardMap, level)) + { + uint32 itemEntry = itemRewardMap->at(level); + uint32 itemAmount = sChallengeModes->getItemRewardAmount(settingName); // Fetch item amount from config + player->SendItemRetrievalMail({ { itemEntry, itemAmount } }); + } + + if (sChallengeModes->getDisableLevel(settingName) && sChallengeModes->getDisableLevel(settingName) <= level) + { + player->UpdatePlayerSetting("mod-challenge-modes", settingName, 0); + } +} + +private: + ChallengeModeSettings settingName; +}; + +class ChallengeMode_Hardcore : public ChallengeMode +{ +public: + ChallengeMode_Hardcore() : ChallengeMode("ChallengeMode_Hardcore", SETTING_HARDCORE) {} + + void OnPlayerLogin(Player* player) override + { + if (!sChallengeModes->challengeEnabledForPlayer(SETTING_HARDCORE, player) || !sChallengeModes->challengeEnabledForPlayer(HARDCORE_DEAD, player)) + { + return; + } + player->KillPlayer(); + player->GetSession()->KickPlayer("Hardcore character died"); + } + + void OnPlayerReleasedGhost(Player* player) override + { + if (!sChallengeModes->challengeEnabledForPlayer(SETTING_HARDCORE, player)) + { + return; + } + player->UpdatePlayerSetting("mod-challenge-modes", HARDCORE_DEAD, 1); + player->GetSession()->KickPlayer("Hardcore character died"); + } + + void OnPlayerPVPKill(Player* /*killer*/, Player* killed) override + { + if (!sChallengeModes->challengeEnabledForPlayer(SETTING_HARDCORE, killed)) + { + return; + } + killed->UpdatePlayerSetting("mod-challenge-modes", HARDCORE_DEAD, 1); + } + + void OnPlayerKilledByCreature(Creature* /*killer*/, Player* killed) override + { + if (!sChallengeModes->challengeEnabledForPlayer(SETTING_HARDCORE, killed)) + { + return; + } + killed->UpdatePlayerSetting("mod-challenge-modes", HARDCORE_DEAD, 1); + } + + void OnPlayerResurrect(Player* player, float /*restore_percent*/, bool /*applySickness*/) override + { + if (!sChallengeModes->challengeEnabledForPlayer(SETTING_HARDCORE, player)) + { + return; + } + // A better implementation is to not allow the resurrect but this will need a new hook added first + player->UpdatePlayerSetting("mod-challenge-modes", HARDCORE_DEAD, 1); + player->KillPlayer(); + player->GetSession()->KickPlayer("Hardcore character died"); + } + + void OnPlayerGiveXP(Player* player, uint32& amount, Unit* victim, uint8 xpSource) override + { + ChallengeMode::OnPlayerGiveXP(player, amount, victim, xpSource); + } + + void OnPlayerLevelChanged(Player* player, uint8 oldlevel) override + { + ChallengeMode::OnPlayerLevelChanged(player, oldlevel); + } +}; + +class ChallengeMode_SemiHardcore : public ChallengeMode +{ +public: + ChallengeMode_SemiHardcore() : ChallengeMode("ChallengeMode_SemiHardcore", SETTING_SEMI_HARDCORE) {} + + void OnPlayerKilledByCreature(Creature* /*killer*/, Player* player) override + { + if (!sChallengeModes->challengeEnabledForPlayer(SETTING_SEMI_HARDCORE, player)) + { + return; + } + for (uint8 i = 0; i < EQUIPMENT_SLOT_END; ++i) + { + if (Item* pItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + { + if (pItem->GetTemplate() && !pItem->IsEquipped()) + continue; + uint8 slot = pItem->GetSlot(); + ChatHandler(player->GetSession()).PSendSysMessage("|cffDA70D6You have lost your |cffffffff|Hitem:%d:0:0:0:0:0:0:0:0|h[%s]|h|r", pItem->GetEntry(), pItem->GetTemplate()->Name1.c_str()); + player->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true); + } + } + player->SetMoney(0); + } + + void OnPlayerGiveXP(Player* player, uint32& amount, Unit* victim, uint8 xpSource) override + { + ChallengeMode::OnPlayerGiveXP(player, amount, victim, xpSource); + } + + void OnPlayerLevelChanged(Player* player, uint8 oldlevel) override + { + ChallengeMode::OnPlayerLevelChanged(player, oldlevel); + } +}; + +class ChallengeMode_SelfCrafted : public ChallengeMode +{ +public: + ChallengeMode_SelfCrafted() : ChallengeMode("ChallengeMode_SelfCrafted", SETTING_SELF_CRAFTED) {} + + bool OnPlayerCanEquipItem(Player* player, uint8 /*slot*/, uint16& /*dest*/, Item* pItem, bool /*swap*/, bool /*not_loading*/) override + { + if (!sChallengeModes->challengeEnabledForPlayer(SETTING_SELF_CRAFTED, player)) + { + return true; + } + if (!pItem->GetTemplate()->HasSignature()) + { + return false; + } + return pItem->GetGuidValue(ITEM_FIELD_CREATOR) == player->GetGUID(); + } + + void OnPlayerGiveXP(Player* player, uint32& amount, Unit* victim, uint8 xpSource) override + { + ChallengeMode::OnPlayerGiveXP(player, amount, victim, xpSource); + } + + void OnPlayerLevelChanged(Player* player, uint8 oldlevel) override + { + ChallengeMode::OnPlayerLevelChanged(player, oldlevel); + } +}; + +class ChallengeMode_ItemQualityLevel : public ChallengeMode +{ +public: + ChallengeMode_ItemQualityLevel() : ChallengeMode("ChallengeMode_ItemQualityLevel", SETTING_ITEM_QUALITY_LEVEL) {} + + bool OnPlayerCanEquipItem(Player* player, uint8 /*slot*/, uint16& /*dest*/, Item* pItem, bool /*swap*/, bool /*not_loading*/) override + { + if (!sChallengeModes->challengeEnabledForPlayer(SETTING_ITEM_QUALITY_LEVEL, player)) + { + return true; + } + return pItem->GetTemplate()->Quality <= ITEM_QUALITY_NORMAL; + } + + void OnPlayerGiveXP(Player* player, uint32& amount, Unit* victim, uint8 xpSource) override + { + ChallengeMode::OnPlayerGiveXP(player, amount, victim, xpSource); + } + + void OnPlayerLevelChanged(Player* player, uint8 oldlevel) override + { + ChallengeMode::OnPlayerLevelChanged(player, oldlevel); + } +}; + +class ChallengeMode_SlowXpGain : public ChallengeMode +{ +public: + ChallengeMode_SlowXpGain() : ChallengeMode("ChallengeMode_SlowXpGain", SETTING_SLOW_XP_GAIN) {} + + void OnPlayerGiveXP(Player* player, uint32& amount, Unit* victim, uint8 xpSource) override + { + ChallengeMode::OnPlayerGiveXP(player, amount, victim, xpSource); + } + + void OnPlayerLevelChanged(Player* player, uint8 oldlevel) override + { + ChallengeMode::OnPlayerLevelChanged(player, oldlevel); + } +}; + +class ChallengeMode_VerySlowXpGain : public ChallengeMode +{ +public: + ChallengeMode_VerySlowXpGain() : ChallengeMode("ChallengeMode_VerySlowXpGain", SETTING_VERY_SLOW_XP_GAIN) {} + + void OnPlayerGiveXP(Player* player, uint32& amount, Unit* victim, uint8 xpSource) override + { + ChallengeMode::OnPlayerGiveXP(player, amount, victim, xpSource); + } + + void OnPlayerLevelChanged(Player* player, uint8 oldlevel) override + { + ChallengeMode::OnPlayerLevelChanged(player, oldlevel); + } +}; + +class ChallengeMode_QuestXpOnly : public ChallengeMode +{ +public: + ChallengeMode_QuestXpOnly() : ChallengeMode("ChallengeMode_QuestXpOnly", SETTING_QUEST_XP_ONLY) {} + + void OnPlayerGiveXP(Player* player, uint32& amount, Unit* victim, uint8 xpSource) override + { + if (!sChallengeModes->challengeEnabledForPlayer(SETTING_QUEST_XP_ONLY, player)) + { + return; + } + if (victim) + { + // Still award XP to pets - they won't be able to pass the player's level + Pet* pet = player->GetPet(); + if (pet && xpSource == XPSOURCE_KILL) + pet->GivePetXP(player->GetGroup() ? amount / 2 : amount); + amount = 0; + } + else + { + ChallengeMode::OnPlayerGiveXP(player, amount, victim, xpSource); + } + } + + void OnPlayerLevelChanged(Player* player, uint8 oldlevel) override + { + ChallengeMode::OnPlayerLevelChanged(player, oldlevel); + } +}; + +class ChallengeMode_IronMan : public ChallengeMode +{ +public: + ChallengeMode_IronMan() : ChallengeMode("ChallengeMode_IronMan", SETTING_IRON_MAN) {} + + void OnPlayerResurrect(Player* player, float /*restore_percent*/, bool /*applySickness*/) override + { + if (!sChallengeModes->challengeEnabledForPlayer(SETTING_IRON_MAN, player)) + { + return; + } + // A better implementation is to not allow the resurrect but this will need a new hook added first + player->KillPlayer(); + } + + void OnPlayerGiveXP(Player* player, uint32& amount, Unit* victim, uint8 xpSource) override + { + ChallengeMode::OnPlayerGiveXP(player, amount, victim, xpSource); + } + + void OnPlayerLevelChanged(Player* player, uint8 oldlevel) override + { + if (!sChallengeModes->challengeEnabledForPlayer(SETTING_IRON_MAN, player)) + { + return; + } + player->SetFreeTalentPoints(0); // Remove all talent points + ChallengeMode::OnPlayerLevelChanged(player, oldlevel); + } + + void OnPlayerTalentsReset(Player* player, bool /*noCost*/) override + { + if (!sChallengeModes->challengeEnabledForPlayer(SETTING_IRON_MAN, player)) + { + return; + } + player->SetFreeTalentPoints(0); // Remove all talent points + } + + bool OnPlayerCanEquipItem(Player* player, uint8 /*slot*/, uint16& /*dest*/, Item* pItem, bool /*swap*/, bool /*not_loading*/) override + { + if (!sChallengeModes->challengeEnabledForPlayer(SETTING_IRON_MAN, player)) + { + return true; + } + return pItem->GetTemplate()->Quality <= ITEM_QUALITY_NORMAL; + } + + bool OnPlayerCanApplyEnchantment(Player* player, Item* /*item*/, EnchantmentSlot /*slot*/, bool /*apply*/, bool /*apply_dur*/, bool /*ignore_condition*/) override + { + if (!sChallengeModes->challengeEnabledForPlayer(SETTING_IRON_MAN, player)) + { + return true; + } + // Are there any exceptions in WotLK? If so need to be added here + return false; + } + + void OnPlayerLearnSpell(Player* player, uint32 spellID) override + { + if (!sChallengeModes->challengeEnabledForPlayer(SETTING_IRON_MAN, player)) + { + return; + } + // These professions are class skills so they are always acceptable + switch (spellID) + { + case RUNEFORGING: + case POISONS: + case BEAST_TRAINING: + return; + default: + break; + } + // Do not allow learning any trade skills + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellID); + if (!spellInfo) + return; + bool shouldForget = false; + for (uint8 i = 0; i < 3; i++) + { + if (spellInfo->Effects[i].Effect == SPELL_EFFECT_TRADE_SKILL) + { + shouldForget = true; + } + } + if (shouldForget) + { + player->removeSpell(spellID, SPEC_MASK_ALL, false); + } + } + + bool CanUseItem(Player* player, ItemTemplate const* proto, InventoryResult& /*result*/) override + { + if (!sChallengeModes->challengeEnabledForPlayer(SETTING_IRON_MAN, player)) + { + return true; + } + // Do not allow using elixir, potion, or flask + if (proto->Class == ITEM_CLASS_CONSUMABLE && + (proto->SubClass == ITEM_SUBCLASS_POTION || + proto->SubClass == ITEM_SUBCLASS_ELIXIR || + proto->SubClass == ITEM_SUBCLASS_FLASK)) + { + return false; + } + // Do not allow food that gives food buffs + if (proto->Class == ITEM_CLASS_CONSUMABLE && proto->SubClass == ITEM_SUBCLASS_FOOD) + { + for (const auto & Spell : proto->Spells) + { + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(Spell.SpellId); + if (!spellInfo) + continue; + + for (uint8 i = 0; i < 3; i++) + { + if (spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_PERIODIC_TRIGGER_SPELL) + { + return false; + } + } + } + } + return true; + } + + bool CanGroupInvite(Player* player, std::string& /*membername*/) override + { + if (!sChallengeModes->challengeEnabledForPlayer(SETTING_IRON_MAN, player)) + { + return true; + } + return false; + } + + bool OnPlayerCanGroupAccept(Player* player, Group* /*group*/) override + { + if (!sChallengeModes->challengeEnabledForPlayer(SETTING_IRON_MAN, player)) + { + return true; + } + return false; + } + +}; + +class gobject_challenge_modes : public GameObjectScript +{ +private: + static bool playerSettingEnabled(Player* player, uint8 settingIndex) + { + return player->GetPlayerSetting("mod-challenge-modes", settingIndex).value; + } + +public: + gobject_challenge_modes() : GameObjectScript("gobject_challenge_modes") { } + + struct gobject_challenge_modesAI: GameObjectAI + { + explicit gobject_challenge_modesAI(GameObject* object) : GameObjectAI(object) { }; + + bool CanBeSeen(Player const* player) override + { + if ((player->GetLevel() > 1 && player->getClass() != CLASS_DEATH_KNIGHT) || (player->GetLevel() > 55)) + { + return false; + } + return sChallengeModes->enabled(); + } + }; + + bool OnGossipHello(Player* player, GameObject* go) override + { + if (sChallengeModes->challengeEnabled(SETTING_HARDCORE) && !playerSettingEnabled(player, SETTING_HARDCORE) && !playerSettingEnabled(player, SETTING_SEMI_HARDCORE)) + { + AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Enable Hardcore Mode", 0, SETTING_HARDCORE); + } + if (sChallengeModes->challengeEnabled(SETTING_SEMI_HARDCORE) && !playerSettingEnabled(player, SETTING_HARDCORE) && !playerSettingEnabled(player, SETTING_SEMI_HARDCORE)) + { + AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Enable Semi-Hardcore Mode", 0, SETTING_SEMI_HARDCORE); + } + if (sChallengeModes->challengeEnabled(SETTING_SELF_CRAFTED) && !playerSettingEnabled(player, SETTING_SELF_CRAFTED) && !playerSettingEnabled(player, SETTING_IRON_MAN)) + { + AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Enable Self-Crafted Mode", 0, SETTING_SELF_CRAFTED); + } + if (sChallengeModes->challengeEnabled(SETTING_ITEM_QUALITY_LEVEL) && !playerSettingEnabled(player, SETTING_ITEM_QUALITY_LEVEL)) + { + AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Enable Low Quality Item Mode", 0, SETTING_ITEM_QUALITY_LEVEL); + } + if (sChallengeModes->challengeEnabled(SETTING_SLOW_XP_GAIN) && !playerSettingEnabled(player, SETTING_SLOW_XP_GAIN) && !playerSettingEnabled(player, SETTING_VERY_SLOW_XP_GAIN)) + { + AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Enable Slow XP Mode", 0, SETTING_SLOW_XP_GAIN); + } + if (sChallengeModes->challengeEnabled(SETTING_VERY_SLOW_XP_GAIN) && !playerSettingEnabled(player, SETTING_SLOW_XP_GAIN) && !playerSettingEnabled(player, SETTING_VERY_SLOW_XP_GAIN)) + { + AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Enable Very Slow XP Mode", 0, SETTING_VERY_SLOW_XP_GAIN); + } + if (sChallengeModes->challengeEnabled(SETTING_QUEST_XP_ONLY) && !playerSettingEnabled(player, SETTING_QUEST_XP_ONLY)) + { + AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Enable Quest XP Only Mode", 0, SETTING_QUEST_XP_ONLY); + } + if (sChallengeModes->challengeEnabled(SETTING_IRON_MAN) && !playerSettingEnabled(player, SETTING_IRON_MAN) && !playerSettingEnabled(player, SETTING_SELF_CRAFTED)) + { + AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Enable Iron Man Mode", 0, SETTING_IRON_MAN); + } + SendGossipMenuFor(player, 12669, go->GetGUID()); + return true; + } + + bool OnGossipSelect(Player* player, GameObject* /*go*/, uint32 /*sender*/, uint32 action) override + { + player->UpdatePlayerSetting("mod-challenge-modes", action, 1); + ChatHandler(player->GetSession()).PSendSysMessage("Challenge enabled."); + CloseGossipMenuFor(player); + return true; + } + + GameObjectAI* GetAI(GameObject* object) const override + { + return new gobject_challenge_modesAI(object); + } +}; + +// Add all scripts in one +void AddSC_mod_challenge_modes() +{ + new ChallengeModes_WorldScript(); + new gobject_challenge_modes(); + new ChallengeMode_Hardcore(); + new ChallengeMode_SemiHardcore(); + new ChallengeMode_SelfCrafted(); + new ChallengeMode_ItemQualityLevel(); + new ChallengeMode_SlowXpGain(); + new ChallengeMode_VerySlowXpGain(); + new ChallengeMode_QuestXpOnly(); + new ChallengeMode_IronMan(); +} diff --git a/src/ChallengeModes.h b/src/ChallengeModes.h new file mode 100644 index 0000000..91c3fcf --- /dev/null +++ b/src/ChallengeModes.h @@ -0,0 +1,106 @@ +#ifndef AZEROTHCORE_CHALLENGEMODES_H +#define AZEROTHCORE_CHALLENGEMODES_H + +#include "ScriptMgr.h" +#include "Player.h" +#include "Config.h" +#include "Chat.h" +#include "ScriptedCreature.h" +#include "ScriptedGossip.h" +#include "SpellMgr.h" +#include "Item.h" +#include "ItemTemplate.h" +#include "GameObjectAI.h" +#include "Pet.h" +#include + + +enum ChallengeModeSettings +{ + SETTING_HARDCORE = 0, + SETTING_SEMI_HARDCORE = 1, + SETTING_SELF_CRAFTED = 2, + SETTING_ITEM_QUALITY_LEVEL = 3, + SETTING_SLOW_XP_GAIN = 4, + SETTING_VERY_SLOW_XP_GAIN = 5, + SETTING_QUEST_XP_ONLY = 6, + SETTING_IRON_MAN = 7, + HARDCORE_DEAD = 8 +}; + +enum AllowedProfessions +{ + RUNEFORGING = 53428, + POISONS = 2842, + BEAST_TRAINING = 5149 +}; + + + +class ChallengeModes +{ +public: + static ChallengeModes* instance(); + + bool challengesEnabled, hardcoreEnable, semiHardcoreEnable, selfCraftedEnable, itemQualityLevelEnable, slowXpGainEnable, verySlowXpGainEnable, questXpOnlyEnable, ironManEnable; + uint32 hardcoreDisableLevel, semiHardcoreDisableLevel, selfCraftedDisableLevel, itemQualityLevelDisableLevel, slowXpGainDisableLevel, verySlowXpGainDisableLevel, questXpOnlyDisableLevel, ironManDisableLevel, hardcoreItemRewardAmount, semiHardcoreItemRewardAmount, selfCraftedItemRewardAmount, itemQualityLevelItemRewardAmount, slowXpGainItemRewardAmount, verySlowXpGainItemRewardAmount, questXpOnlyItemRewardAmount, ironManItemRewardAmount; + float hardcoreXpBonus, semiHardcoreXpBonus, selfCraftedXpBonus, itemQualityLevelXpBonus, questXpOnlyXpBonus, slowXpGainBonus, verySlowXpGainBonus, ironManXpBonus; + std::unordered_map hardcoreTitleRewards, semiHardcoreTitleRewards, selfCraftedTitleRewards, itemQualityLevelTitleRewards, slowXpGainTitleRewards, verySlowXpGainTitleRewards, questXpOnlyTitleRewards, ironManTitleRewards; + std::unordered_map hardcoreItemRewards, semiHardcoreItemRewards, selfCraftedItemRewards, itemQualityLevelItemRewards, slowXpGainItemRewards, verySlowXpGainItemRewards, questXpOnlyItemRewards, ironManItemRewards; + std::unordered_map hardcoreTalentRewards, semiHardcoreTalentRewards, selfCraftedTalentRewards, itemQualityLevelTalentRewards, slowXpGainTalentRewards, verySlowXpGainTalentRewards, questXpOnlyTalentRewards, ironManTalentRewards; + std::unordered_map hardcoreAchievementReward, semiHardcoreAchievementReward, selfCraftedAchievementReward, itemQualityLevelAchievementReward, slowXpGainAchievementReward, verySlowXpGainAchievementReward, questXpOnlyAchievementReward, ironManAchievementReward; + + std::unordered_map*> rewardConfigMap = + { + { "Hardcore.TitleRewards", &hardcoreTitleRewards }, + { "SemiHardcore.TitleRewards", &semiHardcoreTitleRewards }, + { "SelfCrafted.TitleRewards", &selfCraftedTitleRewards }, + { "ItemQualityLevel.TitleRewards", &itemQualityLevelTitleRewards }, + { "SlowXpGain.TitleRewards", &slowXpGainTitleRewards }, + { "VerySlowXpGain.TitleRewards", &verySlowXpGainTitleRewards }, + { "QuestXpOnly.TitleRewards", &questXpOnlyTitleRewards }, + { "IronMan.TitleRewards", &ironManTitleRewards }, + + { "Hardcore.TalentRewards", &hardcoreTalentRewards }, + { "SemiHardcore.TalentRewards", &semiHardcoreTalentRewards }, + { "SelfCrafted.TalentRewards", &selfCraftedTalentRewards }, + { "ItemQualityLevel.TalentRewards", &itemQualityLevelTalentRewards }, + { "SlowXpGain.TalentRewards", &slowXpGainTalentRewards }, + { "VerySlowXpGain.TalentRewards", &verySlowXpGainTalentRewards }, + { "QuestXpOnly.TalentRewards", &questXpOnlyTalentRewards }, + { "IronMan.TalentRewards", &ironManTalentRewards }, + + { "Hardcore.ItemRewards", &hardcoreItemRewards }, + { "SemiHardcore.ItemRewards", &semiHardcoreItemRewards }, + { "SelfCrafted.ItemRewards", &selfCraftedItemRewards }, + { "ItemQualityLevel.ItemRewards", &itemQualityLevelItemRewards }, + { "SlowXpGain.ItemRewards", &slowXpGainItemRewards }, + { "VerySlowXpGain.ItemRewards", &verySlowXpGainItemRewards }, + { "QuestXpOnly.ItemRewards", &questXpOnlyItemRewards }, + { "IronMan.ItemRewards", &ironManItemRewards }, + + { "Hardcore.AchievementReward", &hardcoreAchievementReward }, + { "SemiHardcore.AchievementReward", &semiHardcoreAchievementReward }, + { "SelfCrafted.AchievementReward", &selfCraftedAchievementReward }, + { "ItemQualityLevel.AchievementReward", &itemQualityLevelAchievementReward }, + { "SlowXpGain.AchievementReward", &slowXpGainAchievementReward }, + { "VerySlowXpGain.AchievementReward", &verySlowXpGainAchievementReward }, + { "QuestXpOnly.AchievementReward", &questXpOnlyAchievementReward }, + { "IronMan.AchievementReward", &ironManAchievementReward } + }; + + [[nodiscard]] bool enabled() const { return challengesEnabled; } + [[nodiscard]] bool challengeEnabled(ChallengeModeSettings setting) const; + [[nodiscard]] uint32 getDisableLevel(ChallengeModeSettings setting) const; + [[nodiscard]] float getXpBonusForChallenge(ChallengeModeSettings setting) const; + bool challengeEnabledForPlayer(ChallengeModeSettings setting, Player* player) const; + [[nodiscard]] const std::unordered_map *getTitleMapForChallenge(ChallengeModeSettings setting) const; + [[nodiscard]] const std::unordered_map *getTalentMapForChallenge(ChallengeModeSettings setting) const; + [[nodiscard]] const std::unordered_map *getItemMapForChallenge(ChallengeModeSettings setting) const; + [[nodiscard]] const std::unordered_map *getAchievementMapForChallenge(ChallengeModeSettings setting) const; + [[nodiscard]] uint32 getItemRewardAmount(ChallengeModeSettings setting) const; +}; + +#define sChallengeModes ChallengeModes::instance() + +#endif //AZEROTHCORE_CHALLENGEMODES_H diff --git a/src/ChallengeModes_loader.cpp b/src/ChallengeModes_loader.cpp new file mode 100644 index 0000000..f6ac4d4 --- /dev/null +++ b/src/ChallengeModes_loader.cpp @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + */ + +// From SC +void AddSC_mod_challenge_modes(); + +// Add all +// cf. the naming convention https://github.com/azerothcore/azerothcore-wotlk/blob/master/doc/changelog/master.md#how-to-upgrade-4 +// additionally replace all '-' in the module folder name with '_' here +void Addmod_challenge_modesScripts() +{ + AddSC_mod_challenge_modes(); +}