├── .editorconfig ├── .git_commit_template.txt ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ └── feature_request.yml └── workflows │ └── core-build.yml ├── .gitignore ├── README.md ├── acore-module.json ├── conf └── AutoBalance.conf.dist ├── icon.png ├── include.sh ├── pull_request_template.md ├── setup_git_commit_template.sh └── src ├── ABAllCreatureScript.cpp ├── ABAllCreatureScript.h ├── ABAllMapScript.cpp ├── ABAllMapScript.h ├── ABCommandScript.cpp ├── ABCommandScript.h ├── ABConfig.cpp ├── ABConfig.h ├── ABCreatureInfo.h ├── ABGameObjectScript.cpp ├── ABGameObjectScript.h ├── ABGlobalScript.cpp ├── ABGlobalScript.h ├── ABInflectionPointSettings.h ├── ABLevelScalingDynamicLevelSettings.h ├── ABMapInfo.h ├── ABModuleScript.cpp ├── ABModuleScript.h ├── ABPlayerScript.cpp ├── ABPlayerScript.h ├── ABScriptMgr.cpp ├── ABScriptMgr.h ├── ABStatModifiers.h ├── ABUnitScript.cpp ├── ABUnitScript.h ├── ABUtils.cpp ├── ABUtils.h ├── ABWorldScript.cpp ├── ABWorldScript.h ├── AB_loader.cpp ├── AutoBalance.cpp ├── AutoBalance.h ├── Message.cpp └── Message.h /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | charset = utf-8 3 | indent_style = space 4 | indent_size = 4 5 | tab_width = 4 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | max_line_length = 80 9 | -------------------------------------------------------------------------------- /.git_commit_template.txt: -------------------------------------------------------------------------------- 1 | ### TITLE 2 | ## Type(Scope/Subscope): Commit ultra short explanation 3 | ## |---- Write below the examples with a maximum of 50 characters ----| 4 | ## Example 1: fix(DB/SAI): Missing spell to NPC Hogger 5 | ## Example 2: fix(CORE/Raid): Phase 2 of Ragnaros 6 | ## Example 3: feat(CORE/Commands): New GM command to do something 7 | 8 | 9 | ### DESCRIPTION 10 | ## Explain why this change is being made, what does it fix etc... 11 | ## |---- Write below the examples with a maximum of 72 characters per lines ----| 12 | ## Example: Hogger (id: 492) was not charging player when being engaged. 13 | 14 | 15 | ## Provide links to any issue, commit, pull request or other resource 16 | ## Example 1: Closes issue #23 17 | ## Example 2: Ported from other project's commit (link) 18 | ## Example 3: References taken from wowpedia / wowhead / wowwiki / https://wowgaming.altervista.org/aowow/ 19 | 20 | 21 | 22 | ## ======================================================= 23 | ## EXTRA INFOS 24 | ## ======================================================= 25 | ## "Type" can be: 26 | ## feat (new feature) 27 | ## fix (bug fix) 28 | ## refactor (refactoring production code) 29 | ## style (formatting, missing semi colons, etc; no code change) 30 | ## docs (changes to documentation) 31 | ## test (adding or refactoring tests; no production code change) 32 | ## chore (updating bash scripts, git files etc; no production code change) 33 | ## -------------------- 34 | ## Remember to 35 | ## Capitalize the subject line 36 | ## Use the imperative mood in the subject line 37 | ## Do not end the subject line with a period 38 | ## Separate subject from body with a blank line 39 | ## Use the body to explain what and why rather than how 40 | ## Can use multiple lines with "-" for bullet points in body 41 | ## -------------------- 42 | ## More info here https://www.conventionalcommits.org/en/v1.0.0-beta.2/ 43 | ## ======================================================= 44 | ## "Scope" can be: 45 | ## CORE (core related, c++) 46 | ## DB (database related, sql) 47 | ## ======================================================= 48 | ## "Subscope" is optional and depends on the nature of the commit. 49 | ## ======================================================= 50 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ## AUTO-DETECT 2 | ## Handle line endings automatically for files detected as 3 | ## text and leave all files detected as binary untouched. 4 | ## This will handle all files NOT defined below. 5 | * text=auto eol=lf 6 | 7 | # Text 8 | *.conf text 9 | *.conf.dist text 10 | *.cmake text 11 | 12 | ## Scripts 13 | *.sh text 14 | *.fish text 15 | *.lua text 16 | 17 | ## SQL 18 | *.sql text 19 | 20 | ## C++ 21 | *.c text 22 | *.cc text 23 | *.cxx text 24 | *.cpp text 25 | *.c++ text 26 | *.hpp text 27 | *.h text 28 | *.h++ text 29 | *.hh text 30 | 31 | 32 | ## For documentation 33 | 34 | # Documents 35 | *.doc diff=astextplain 36 | *.DOC diff=astextplain 37 | *.docx diff=astextplain 38 | *.DOCX diff=astextplain 39 | *.dot diff=astextplain 40 | *.DOT diff=astextplain 41 | *.pdf diff=astextplain 42 | *.PDF diff=astextplain 43 | *.rtf diff=astextplain 44 | *.RTF diff=astextplain 45 | 46 | ## DOCUMENTATION 47 | *.markdown text 48 | *.md text 49 | *.mdwn text 50 | *.mdown text 51 | *.mkd text 52 | *.mkdn text 53 | *.mdtxt text 54 | *.mdtext text 55 | *.txt text 56 | AUTHORS text 57 | CHANGELOG text 58 | CHANGES text 59 | CONTRIBUTING text 60 | COPYING text 61 | copyright text 62 | *COPYRIGHT* text 63 | INSTALL text 64 | license text 65 | LICENSE text 66 | NEWS text 67 | readme text 68 | *README* text 69 | TODO text 70 | 71 | ## GRAPHICS 72 | *.ai binary 73 | *.bmp binary 74 | *.eps binary 75 | *.gif binary 76 | *.ico binary 77 | *.jng binary 78 | *.jp2 binary 79 | *.jpg binary 80 | *.jpeg binary 81 | *.jpx binary 82 | *.jxr binary 83 | *.pdf binary 84 | *.png binary 85 | *.psb binary 86 | *.psd binary 87 | *.svg text 88 | *.svgz binary 89 | *.tif binary 90 | *.tiff binary 91 | *.wbmp binary 92 | *.webp binary 93 | 94 | 95 | ## ARCHIVES 96 | *.7z binary 97 | *.gz binary 98 | *.jar binary 99 | *.rar binary 100 | *.tar binary 101 | *.zip binary 102 | 103 | ## EXECUTABLES 104 | *.exe binary 105 | *.pyc binary 106 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug report 2 | description: Create a bug report to help us improve. 3 | title: "Bug: " 4 | body: 5 | - type: textarea 6 | id: current 7 | attributes: 8 | label: Current Behaviour 9 | description: | 10 | Description of the problem or issue here. 11 | Include entries of affected creatures / items / quests / spells etc. 12 | If this is a crash, post the crashlog (upload to https://gist.github.com/) and include the link here. 13 | Never upload files! Use GIST for text and YouTube for videos! 14 | validations: 15 | required: true 16 | - type: textarea 17 | id: expected 18 | attributes: 19 | label: Expected Behaviour 20 | description: | 21 | Tell us what should happen instead. 22 | validations: 23 | required: true 24 | - type: textarea 25 | id: reproduce 26 | attributes: 27 | label: Steps to reproduce the problem 28 | description: | 29 | What does someone else need to do to encounter the same bug? 30 | placeholder: | 31 | 1. Step 1 32 | 2. Step 2 33 | 3. Step 3 34 | validations: 35 | required: true 36 | - type: textarea 37 | id: extra 38 | attributes: 39 | label: Extra Notes 40 | description: | 41 | Do you have any extra notes that can help solve the issue that does not fit any other field? 42 | placeholder: | 43 | None 44 | validations: 45 | required: false 46 | - type: textarea 47 | id: abcommands 48 | attributes: 49 | label: AutoBalance Debug Commands 50 | description: | 51 | Please include text or an image of the `.ab mapstat` command. If your issue is concerning scaling of creatures, please also include the `.ab creaturestat` command while targeting the problematic creature. 52 | placeholder: | 53 | None 54 | validations: 55 | required: false 56 | - type: textarea 57 | id: commit 58 | attributes: 59 | label: AC rev. hash/commit 60 | description: | 61 | Copy the result of the `.server debug` command (if you need to run it from the client get a prat addon) 62 | validations: 63 | required: true 64 | - type: input 65 | id: os 66 | attributes: 67 | label: Operating system 68 | description: | 69 | The Operating System the Server is running on. 70 | i.e. Windows 11 x64, Debian 10 x64, macOS 12, Ubuntu 20.04 71 | validations: 72 | required: true 73 | - type: textarea 74 | id: custom 75 | attributes: 76 | label: Custom changes or Modules 77 | description: | 78 | List which custom changes or modules you have applied, i.e. Eluna module, etc. 79 | placeholder: | 80 | None 81 | validations: 82 | required: false 83 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature request 2 | description: Suggest an idea for this project 3 | title: "Feature: " 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thank you for taking your time to fill out a feature request. Remember to fill out all fields including the title above. 9 | An issue that is not properly filled out will be closed. 10 | - type: textarea 11 | id: description 12 | attributes: 13 | label: Describe your feature request or suggestion in detail 14 | description: | 15 | A clear and concise description of what you want to happen. 16 | validations: 17 | required: true 18 | - type: textarea 19 | id: solution 20 | attributes: 21 | label: Describe a possible solution to your feature or suggestion in detail 22 | description: | 23 | A clear and concise description of any alternative solutions or features you've considered. 24 | validations: 25 | required: false 26 | - type: textarea 27 | id: additional 28 | attributes: 29 | label: Additional context 30 | description: | 31 | Add any other context or screenshots about the feature request here. 32 | validations: 33 | required: false 34 | -------------------------------------------------------------------------------- /.github/workflows/core-build.yml: -------------------------------------------------------------------------------- 1 | name: core-build 2 | on: 3 | push: 4 | branches: 5 | - 'master' 6 | pull_request: 7 | 8 | jobs: 9 | build: 10 | uses: azerothcore/reusable-workflows/.github/workflows/core_build_modules.yml@main 11 | with: 12 | module_repo: ${{ github.event.repository.name }} 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | !.gitignore 2 | 3 | # 4 | #Generic 5 | # 6 | 7 | .directory 8 | .mailmap 9 | *.orig 10 | *.rej 11 | *.*~ 12 | .hg/ 13 | *.kdev* 14 | .DS_Store 15 | CMakeLists.txt.user 16 | *.bak 17 | *.patch 18 | *.diff 19 | *.REMOTE.* 20 | *.BACKUP.* 21 | *.BASE.* 22 | *.LOCAL.* 23 | 24 | # 25 | # IDE & other softwares 26 | # 27 | /.settings/ 28 | /.externalToolBuilders/* 29 | # exclude in all levels 30 | nbproject/ 31 | .sync.ffs_db 32 | *.kate-swp 33 | .idea 34 | cmake-build-debug 35 | .vscode 36 | 37 | # 38 | # Eclipse 39 | # 40 | *.pydevproject 41 | .metadata 42 | .gradle 43 | tmp/ 44 | *.tmp 45 | *.swp 46 | *~.nib 47 | local.properties 48 | .settings/ 49 | .loadpath 50 | .project 51 | .cproject 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ![logo](https://raw.githubusercontent.com/azerothcore/azerothcore.github.io/master/images/logo-github.png) AzerothCore 2 | 3 | ## Beta testing warning 4 | 5 | The current version of the master branch is currently in beta, under testing to ensure all the functionalities are working properly. 6 | If you encur in problems please open an Issue and consider to use the last [stable version](https://github.com/azerothcore/mod-autobalance/releases/tag/stable) of this repository. 7 | 8 | ## AutoBalance 9 | 10 | - Latest build status with azerothcore: [![Build Status](https://github.com/azerothcore/mod-autobalance/workflows/core-build/badge.svg?branch=master&event=push)](https://github.com/azerothcore/mod-autobalance) 11 | 12 | This module is intended to scale based on number of players, instance mobs and bosses' health, mana, and damage. 13 | 14 | **NOTE:** This module requires at least [this commit](https://github.com/azerothcore/azerothcore-wotlk/commit/f127e583aae3cfa51a77d056c1892a7de07ffb52) of AzerothCore in order to work correctly. Older versions are not supported. 15 | 16 | All settings are well-described in the configuration file. 17 | 18 | **PLEASE** include the output from the `.ab mapstat` and `.ab creaturestat` commands (while targeting a problematic creature) when reporting issues. This will help us to quickly identify the problem and provide a solution. 19 | 20 | ## In-game Commands 21 | | Command | Permission | Description | 22 | | :------ | :--------- | :---------- | 23 | | `.ab mapstat` | All Players | Displays AB-calcualted settings for the current map, including player count, difficulty, world modifiers, and others. | 24 | | `.ab creaturestat` | All Players | Displays AB-calculated settings for the targeted dungeon creature including level scaling, difficulty, modifiers, and boss status. | 25 | | `.ab setoffset` | Game Masters | Sets the server-wide player difficulty offset. Instances will be scaled as though they had this many more/less players than they really do. | 26 | | `.ab getoffset` | All Players | Gets the current server-wide player difficulty offset. Instances will be scaled as though they had this many more/less players than they really do. | 27 | | `.reload config` | Game Masters | Reloads all your configuration files, including `AutoBalance.conf`. This lets you update AutoBalance settings without restarting your worldserver. This module is designed to contiue to work as expected when this command is issued. | 28 | 29 | ## Logger Names 30 | | Logger | Description | 31 | | :----- | ----------- | 32 | | `Logger.module.AutoBalance` | Main logger, verbose debug logs. Map detection, list management, creature adjustments, multiplier, modifiers. Catch-all. | 33 | | `Logger.module.AutoBalance_CombatLocking` | Debug logs related to the combat locking/unlocking mechanism for maps. | 34 | | `Logger.module.AutoBalance_DamageHealingCC` | Debug logs for the spell/melee/CC modifications that are made in real-time. | 35 | | `Logger.module.AutoBalance_StatGeneration` | Detailed debug logs that show all the calculation steps in how different multipliers are derived. | 36 | 37 | ## References 38 | - [Interactive Inflection Point Spreadsheet](https://docs.google.com/spreadsheets/d/100cmKIJIjCZ-ncWd0K9ykO8KUgwFTcwg4h2nfE_UeCc/copy) 39 | - [InflectionPoint Curve Examples](https://i.imgur.com/x42UnUR.png) 40 | - [Impact of CurveFloor and CurveCeiling on enemy multiplier](https://i.imgur.com/I8S4cwJ.png) 41 | -------------------------------------------------------------------------------- /acore-module.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mod-autobalance", 3 | "version": "2.2.0", 4 | "compatibility" : [ 5 | { "version": "1.0.0", "branch": "none" }, 6 | { "version": "^2.0.0", "branch": "2.0" }, 7 | { "version": "^3.0.0", "branch": "master" } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azerothcore/mod-autobalance/37455446fe99f073e4a6113987e3228705054639/icon.png -------------------------------------------------------------------------------- /include.sh: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azerothcore/mod-autobalance/37455446fe99f073e4a6113987e3228705054639/include.sh -------------------------------------------------------------------------------- /pull_request_template.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Changes Proposed: 4 | - 5 | - 6 | 7 | ## Issues Addressed: 8 | 9 | - Closes 10 | 11 | ## SOURCE: 12 | 13 | 14 | ## Tests Performed: 15 | 16 | - 17 | - 18 | 19 | 20 | ## How to Test the Changes: 21 | 22 | 23 | 1. 24 | 2. 25 | 3. 26 | -------------------------------------------------------------------------------- /setup_git_commit_template.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## Set a local git commit template 4 | git config --local commit.template ".git_commit_template.txt" ; 5 | -------------------------------------------------------------------------------- /src/ABAllCreatureScript.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016+ AzerothCore , license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE 3 | */ 4 | 5 | #ifndef __AB_ALL_CREATURE_SCRIPT_H 6 | #define __AB_ALL_CREATURE_SCRIPT_H 7 | 8 | #include "ScriptMgr.h" 9 | 10 | class AutoBalance_AllCreatureScript : public AllCreatureScript 11 | { 12 | public: 13 | AutoBalance_AllCreatureScript() 14 | : AllCreatureScript("AutoBalance_AllCreatureScript") 15 | { 16 | } 17 | 18 | void OnBeforeCreatureSelectLevel(const CreatureTemplate* /*creatureTemplate*/, Creature* creature, uint8& level) override; 19 | void Creature_SelectLevel(const CreatureTemplate* /* cinfo */, Creature* creature) override; 20 | void OnCreatureAddWorld(Creature* creature) override; 21 | void OnCreatureRemoveWorld(Creature* creature) override; 22 | void OnAllCreatureUpdate(Creature* creature, uint32 /*diff*/) override; 23 | 24 | // Reset the passed creature to stock if the config has changed 25 | bool ResetCreatureIfNeeded(Creature* creature); 26 | void ModifyCreatureAttributes(Creature* creature); 27 | 28 | private: 29 | bool _isSummonCloneOfSummoner(Creature* summon); 30 | }; 31 | 32 | #endif /* __AB_ALL_CREATURE_SCRIPT_H */ 33 | -------------------------------------------------------------------------------- /src/ABAllMapScript.cpp: -------------------------------------------------------------------------------- 1 | #include "ABAllMapScript.h" 2 | 3 | #include "ABConfig.h" 4 | #include "ABMapInfo.h" 5 | #include "ABUtils.h" 6 | 7 | #include "Chat.h" 8 | #include "Message.h" 9 | 10 | 11 | void AutoBalance_AllMapScript::OnPlayerEnterAll(Map* map, Player* player) 12 | { 13 | if (!EnableGlobal) 14 | return; 15 | 16 | if (!map->IsDungeon()) 17 | return; 18 | 19 | LOG_DEBUG("module.AutoBalance", "AutoBalance:: ------------------------------------------------"); 20 | 21 | LOG_DEBUG("module.AutoBalance", "AutoBalance_AllMapScript::OnPlayerEnterAll: Player {}{} | enters {} ({}{})", 22 | player->GetName(), 23 | player->IsGameMaster() ? " (GM)" : "", 24 | map->GetMapName(), 25 | map->GetId(), 26 | map->GetInstanceId() ? "-" + std::to_string(map->GetInstanceId()) : "" 27 | ); 28 | 29 | // get the map's info 30 | AutoBalanceMapInfo* mapABInfo = GetMapInfo(map); 31 | 32 | // store the previous difficulty for comparison later 33 | int prevAdjustedPlayerCount = mapABInfo->adjustedPlayerCount; 34 | 35 | // add player to this map's player list 36 | AddPlayerToMap(map, player); 37 | 38 | // recalculate the zone's level stats 39 | mapABInfo->highestCreatureLevel = 0; 40 | mapABInfo->lowestCreatureLevel = 0; 41 | //mapABInfo->avgCreatureLevel = 0; 42 | mapABInfo->activeCreatureCount = 0; 43 | 44 | WorldSession* session = player->GetSession(); 45 | LocaleConstant locale = session->GetSessionDbLocaleIndex(); 46 | 47 | // if the previous player count is the same as the new player count, update without force 48 | if ((prevAdjustedPlayerCount == mapABInfo->adjustedPlayerCount) && (mapABInfo->adjustedPlayerCount != 1)) 49 | { 50 | LOG_DEBUG("module.AutoBalance", "AutoBalance_AllMapScript::OnPlayerEnterAll: Player difficulty unchanged at {}. Updating map data (no force).", 51 | mapABInfo->adjustedPlayerCount 52 | ); 53 | 54 | // Update the map's data 55 | UpdateMapDataIfNeeded(map, false); 56 | } 57 | else 58 | { 59 | LOG_DEBUG("module.AutoBalance", "AutoBalance_AllMapScript::OnPlayerEnterAll: Player difficulty changed from ({})->({}). Updating map data (force).", 60 | prevAdjustedPlayerCount, 61 | mapABInfo->adjustedPlayerCount 62 | ); 63 | 64 | // Update the map's data, forced 65 | UpdateMapDataIfNeeded(map, true); 66 | } 67 | 68 | // see which existing creatures are active 69 | for (std::vector::iterator creatureIterator = mapABInfo->allMapCreatures.begin(); creatureIterator != mapABInfo->allMapCreatures.end(); ++creatureIterator) 70 | { 71 | AddCreatureToMapCreatureList(*creatureIterator, false, true); 72 | } 73 | 74 | // Notify players of the change 75 | if (PlayerChangeNotify && mapABInfo->enabled) 76 | { 77 | if (map->GetEntry()->IsDungeon() && player) 78 | { 79 | if (mapABInfo->playerCount) 80 | { 81 | for (std::vector::const_iterator playerIterator = mapABInfo->allMapPlayers.begin(); playerIterator != mapABInfo->allMapPlayers.end(); ++playerIterator) 82 | { 83 | Player* thisPlayer = *playerIterator; 84 | if (thisPlayer) 85 | { 86 | ChatHandler chatHandle = ChatHandler(thisPlayer->GetSession()); 87 | InstanceMap* instanceMap = map->ToInstanceMap(); 88 | 89 | std::string instanceDifficulty; if (instanceMap->IsHeroic()) instanceDifficulty = "Heroic"; else instanceDifficulty = "Normal"; 90 | 91 | if (thisPlayer && thisPlayer == player) // This is the player that entered 92 | { 93 | chatHandle.PSendSysMessage(ABGetLocaleText(locale, "welcome_to_player").c_str(), 94 | map->GetMapName(), 95 | instanceMap->GetMaxPlayers(), 96 | instanceDifficulty.c_str(), 97 | mapABInfo->playerCount, 98 | mapABInfo->adjustedPlayerCount); 99 | 100 | // notify GMs that they won't be accounted for 101 | if (player->IsGameMaster()) 102 | chatHandle.PSendSysMessage(ABGetLocaleText(locale, "welcome_to_gm").c_str()); 103 | } 104 | else 105 | { 106 | // announce non-GMs entering the instance only 107 | if (!player->IsGameMaster()) 108 | chatHandle.PSendSysMessage(ABGetLocaleText(locale, "announce_non_gm_entering_instance").c_str(), player->GetName().c_str(), mapABInfo->playerCount, mapABInfo->adjustedPlayerCount); 109 | } 110 | } 111 | } 112 | } 113 | } 114 | } 115 | } 116 | 117 | void AutoBalance_AllMapScript::OnPlayerLeaveAll(Map* map, Player* player) 118 | { 119 | if (!EnableGlobal) 120 | return; 121 | 122 | if (!map->IsDungeon()) 123 | return; 124 | 125 | LOG_DEBUG("module.AutoBalance", "AutoBalance:: ------------------------------------------------"); 126 | 127 | LOG_DEBUG("module.AutoBalance", "AutoBalance_AllMapScript::OnPlayerLeaveAll: Player {}{} | exits {} ({}{})", 128 | player->GetName(), 129 | player->IsGameMaster() ? " (GM)" : "", 130 | map->GetMapName(), 131 | map->GetId(), 132 | map->GetInstanceId() ? "-" + std::to_string(map->GetInstanceId()) : "" 133 | ); 134 | 135 | // get the map's info 136 | AutoBalanceMapInfo* mapABInfo = GetMapInfo(map); 137 | 138 | // store the previous difficulty for comparison later 139 | int prevAdjustedPlayerCount = mapABInfo->adjustedPlayerCount; 140 | 141 | // remove this player from this map's player list 142 | bool playerWasRemoved = RemovePlayerFromMap(map, player); 143 | 144 | // report the number of players in the map 145 | LOG_DEBUG("module.AutoBalance", "AutoBalance_AllMapScript::OnPlayerLeaveAll: There are {} player(s) left in the map.", mapABInfo->allMapPlayers.size()); 146 | 147 | // if a player was NOT removed, return now - stats don't need to be updated 148 | if (!playerWasRemoved) 149 | return; 150 | 151 | // recalculate the zone's level stats 152 | mapABInfo->highestCreatureLevel = 0; 153 | mapABInfo->lowestCreatureLevel = 0; 154 | //mapABInfo->avgCreatureLevel = 0; 155 | mapABInfo->activeCreatureCount = 0; 156 | 157 | // see which existing creatures are active 158 | for (std::vector::iterator creatureIterator = mapABInfo->allMapCreatures.begin(); creatureIterator != mapABInfo->allMapCreatures.end(); ++creatureIterator) 159 | AddCreatureToMapCreatureList(*creatureIterator, false, true); 160 | 161 | // if the previous player count is the same as the new player count, update without force 162 | if (prevAdjustedPlayerCount == mapABInfo->adjustedPlayerCount) 163 | { 164 | LOG_DEBUG("module.AutoBalance", "AutoBalance_AllMapScript::OnPlayerLeaveAll: Player difficulty unchanged at {}. Updating map data (no force).", 165 | mapABInfo->adjustedPlayerCount 166 | ); 167 | 168 | // Update the map's data 169 | UpdateMapDataIfNeeded(map, false); 170 | } 171 | else 172 | { 173 | LOG_DEBUG("module.AutoBalance", "AutoBalance_AllMapScript::OnPlayerLeaveAll: Player difficulty changed from ({})->({}). Updating map data (force).", 174 | prevAdjustedPlayerCount, 175 | mapABInfo->adjustedPlayerCount 176 | ); 177 | 178 | // Update the map's data, forced 179 | UpdateMapDataIfNeeded(map, true); 180 | } 181 | 182 | // updates the player count and levels for the map 183 | if (map->GetEntry() && map->GetEntry()->IsDungeon()) 184 | { 185 | { 186 | mapABInfo->playerCount = mapABInfo->allMapPlayers.size(); 187 | LOG_DEBUG("module.AutoBalance", "AutoBalance_AllMapScript::OnPlayerLeaveAll: Player {} left the instance.", 188 | player->GetName(), 189 | mapABInfo->playerCount, 190 | mapABInfo->adjustedPlayerCount 191 | ); 192 | } 193 | } 194 | 195 | // Notify remaining players in the instance that a player left 196 | if (PlayerChangeNotify && mapABInfo->enabled) 197 | { 198 | if (map->GetEntry()->IsDungeon() && player && !player->IsGameMaster()) 199 | { 200 | if (mapABInfo->playerCount) 201 | { 202 | for (std::vector::const_iterator playerIterator = mapABInfo->allMapPlayers.begin(); playerIterator != mapABInfo->allMapPlayers.end(); ++playerIterator) 203 | { 204 | Player* thisPlayer = *playerIterator; 205 | if (thisPlayer && thisPlayer != player) 206 | { 207 | ChatHandler chatHandle = ChatHandler(thisPlayer->GetSession()); 208 | 209 | if (mapABInfo->combatLocked) 210 | { 211 | chatHandle.PSendSysMessage(ABGetLocaleText(thisPlayer->GetSession()->GetSessionDbLocaleIndex(), "leaving_instance_combat").c_str(), 212 | player->GetName().c_str(), 213 | mapABInfo->adjustedPlayerCount); 214 | } 215 | else 216 | { 217 | chatHandle.PSendSysMessage(ABGetLocaleText(thisPlayer->GetSession()->GetSessionDbLocaleIndex(), "leaving_instance").c_str(), 218 | player->GetName().c_str(), 219 | mapABInfo->playerCount, 220 | mapABInfo->adjustedPlayerCount); 221 | } 222 | } 223 | } 224 | } 225 | } 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /src/ABAllMapScript.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016+ AzerothCore , license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE 3 | */ 4 | 5 | #ifndef __AB_ALL_MAP_SCRIPT_H 6 | #define __AB_ALL_MAP_SCRIPT_H 7 | 8 | #include "ScriptMgr.h" 9 | 10 | class AutoBalance_AllMapScript : public AllMapScript 11 | { 12 | public: 13 | AutoBalance_AllMapScript() 14 | : AllMapScript("AutoBalance_AllMapScript", { 15 | ALLMAPHOOK_ON_PLAYER_ENTER_ALL, 16 | ALLMAPHOOK_ON_PLAYER_LEAVE_ALL 17 | }) 18 | { 19 | } 20 | 21 | // hook triggers after the player has already entered the world 22 | void OnPlayerEnterAll(Map* map, Player* player) override; 23 | // hook triggers just before the player left the world 24 | void OnPlayerLeaveAll(Map* map, Player* player) override; 25 | }; 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /src/ABCommandScript.cpp: -------------------------------------------------------------------------------- 1 | #include "ABCommandScript.h" 2 | 3 | #include "ABConfig.h" 4 | #include "ABCreatureInfo.h" 5 | #include "ABMapInfo.h" 6 | #include "ABUtils.h" 7 | #include "Message.h" 8 | 9 | bool AutoBalance_CommandScript::HandleABSetOffsetCommand(ChatHandler* handler, const char* args) 10 | { 11 | if (!*args) 12 | { 13 | handler->PSendSysMessage(".autobalance setoffset #"); 14 | handler->PSendSysMessage(ABGetLocaleText(handler->GetSession()->GetSessionDbLocaleIndex(), "set_offset_command_description").c_str()); 15 | return false; 16 | } 17 | char* offset = strtok((char*)args, " "); 18 | int32 offseti = -1; 19 | 20 | if (offset) 21 | { 22 | offseti = (uint32)atoi(offset); 23 | std::vector args = { std::to_string(offseti) }; 24 | handler->PSendSysMessage(ABGetLocaleText(handler->GetSession()->GetSessionDbLocaleIndex(), "set_offset_command_success").c_str(), offseti); 25 | PlayerCountDifficultyOffset = offseti; 26 | globalConfigTime = GetCurrentConfigTime(); 27 | return true; 28 | } 29 | else 30 | handler->PSendSysMessage(ABGetLocaleText(handler->GetSession()->GetSessionDbLocaleIndex(), "set_offset_command_error").c_str()); 31 | return false; 32 | } 33 | 34 | bool AutoBalance_CommandScript::HandleABGetOffsetCommand(ChatHandler* handler, const char* /*args*/) 35 | { 36 | handler->PSendSysMessage(ABGetLocaleText(handler->GetSession()->GetSessionDbLocaleIndex(), "get_offset_command_success").c_str(), PlayerCountDifficultyOffset); 37 | return true; 38 | } 39 | 40 | bool AutoBalance_CommandScript::HandleABMapStatsCommand(ChatHandler* handler, const char* /*args*/) 41 | { 42 | Player* player = handler->GetPlayer(); 43 | auto locale = handler->GetSession()->GetSessionDbLocaleIndex(); 44 | 45 | AutoBalanceMapInfo* mapABInfo = GetMapInfo(player->GetMap()); 46 | 47 | if (player->GetMap()->IsDungeon()) 48 | { 49 | handler->PSendSysMessage("---"); 50 | // Map basics 51 | handler->PSendSysMessage("{} ({}-player {}) | ID {}-{}{}", 52 | player->GetMap()->GetMapName(), 53 | player->GetMap()->ToInstanceMap()->GetMaxPlayers(), 54 | player->GetMap()->ToInstanceMap()->IsHeroic() ? "Heroic" : "Normal", 55 | player->GetMapId(), 56 | player->GetInstanceId(), 57 | mapABInfo->enabled ? "" : " | AutoBalance DISABLED"); 58 | 59 | // if the map isn't enabled, don't display anything else 60 | // if (!mapABInfo->enabled) { return true; } 61 | 62 | // Player stats 63 | handler->PSendSysMessage("Players on map: {} (Lvl {} - {})", 64 | mapABInfo->playerCount, 65 | mapABInfo->lowestPlayerLevel, 66 | mapABInfo->highestPlayerLevel 67 | ); 68 | 69 | // Adjusted player count (multiple scenarios) 70 | if (mapABInfo->combatLockTripped) 71 | handler->PSendSysMessage(ABGetLocaleText(locale, "adjusted_player_count_combat_locked").c_str(), mapABInfo->adjustedPlayerCount); 72 | else if (mapABInfo->playerCount < mapABInfo->minPlayers && !PlayerCountDifficultyOffset) 73 | handler->PSendSysMessage(ABGetLocaleText(locale, "adjusted_player_count_map_minimum").c_str(), mapABInfo->adjustedPlayerCount); 74 | else if (mapABInfo->playerCount < mapABInfo->minPlayers && PlayerCountDifficultyOffset) 75 | handler->PSendSysMessage(ABGetLocaleText(locale, "adjusted_player_count_map_minimum_difficulty_offset").c_str(), mapABInfo->adjustedPlayerCount, PlayerCountDifficultyOffset); 76 | else if (PlayerCountDifficultyOffset) 77 | handler->PSendSysMessage(ABGetLocaleText(locale, "adjusted_player_count_difficulty_offset").c_str(), mapABInfo->adjustedPlayerCount, PlayerCountDifficultyOffset); 78 | else 79 | handler->PSendSysMessage(ABGetLocaleText(locale, "adjusted_player_count").c_str(), mapABInfo->adjustedPlayerCount); 80 | 81 | // LFG levels 82 | handler->PSendSysMessage(ABGetLocaleText(locale, "lfg_range").c_str(), mapABInfo->lfgMinLevel, mapABInfo->lfgMaxLevel, mapABInfo->lfgTargetLevel); 83 | 84 | // Calculated map level (creature average) 85 | handler->PSendSysMessage(ABGetLocaleText(locale, "map_level").c_str(), 86 | (uint8)(mapABInfo->avgCreatureLevel + 0.5f), 87 | mapABInfo->isLevelScalingEnabled && mapABInfo->enabled ? "->" + std::to_string(mapABInfo->highestPlayerLevel) + ABGetLocaleText(locale, "level_scaling_enabled").c_str() : ABGetLocaleText(locale, "level_scaling_disabled").c_str() 88 | ); 89 | 90 | // World Health Multiplier 91 | handler->PSendSysMessage(ABGetLocaleText(locale, "world_health_multiplier").c_str(), mapABInfo->worldHealthMultiplier); 92 | 93 | // World Damage and Healing Multiplier 94 | if (mapABInfo->worldDamageHealingMultiplier != mapABInfo->scaledWorldDamageHealingMultiplier) 95 | { 96 | handler->PSendSysMessage(ABGetLocaleText(locale, "world_hostile_damage_healing_multiplier_to").c_str(), 97 | mapABInfo->worldDamageHealingMultiplier, 98 | mapABInfo->scaledWorldDamageHealingMultiplier 99 | ); 100 | } 101 | else 102 | { 103 | handler->PSendSysMessage(ABGetLocaleText(locale, "world_hostile_damage_healing_multiplier").c_str(), 104 | mapABInfo->worldDamageHealingMultiplier 105 | ); 106 | } 107 | 108 | // Creature Stats 109 | handler->PSendSysMessage(ABGetLocaleText(locale, "original_creature_level_range").c_str(), 110 | mapABInfo->lowestCreatureLevel, 111 | mapABInfo->highestCreatureLevel, 112 | mapABInfo->avgCreatureLevel 113 | ); 114 | handler->PSendSysMessage(ABGetLocaleText(locale, "active_total_creatures_in_map").c_str(), 115 | mapABInfo->activeCreatureCount, 116 | mapABInfo->allMapCreatures.size() 117 | ); 118 | 119 | return true; 120 | } 121 | else 122 | { 123 | handler->PSendSysMessage(ABGetLocaleText(locale, "ab_command_only_in_instance").c_str()); 124 | return false; 125 | } 126 | } 127 | 128 | bool AutoBalance_CommandScript::HandleABCreatureStatsCommand(ChatHandler* handler, const char* /*args*/) 129 | { 130 | Creature* target = handler->getSelectedCreature(); 131 | 132 | auto locale = handler->GetSession()->GetSessionDbLocaleIndex(); 133 | 134 | if (!target) 135 | { 136 | handler->SendSysMessage(LANG_SELECT_CREATURE); 137 | handler->SetSentErrorMessage(true); 138 | return false; 139 | } 140 | else if (!target->GetMap()->IsDungeon()) 141 | { 142 | handler->PSendSysMessage(ABGetLocaleText(locale, "target_no_in_instance").c_str()); 143 | handler->SetSentErrorMessage(true); 144 | return false; 145 | } 146 | 147 | AutoBalanceCreatureInfo* targetABInfo = target->CustomData.GetDefault("AutoBalanceCreatureInfo"); 148 | 149 | handler->PSendSysMessage("---"); 150 | handler->PSendSysMessage("{} ({}{}{}), {}", 151 | target->GetName(), 152 | targetABInfo->UnmodifiedLevel, 153 | isCreatureRelevant(target) && targetABInfo->UnmodifiedLevel != target->GetLevel() ? "->" + std::to_string(targetABInfo->selectedLevel) : "", 154 | isBossOrBossSummon(target) ? " | Boss" : "", 155 | targetABInfo->isActive ? ABGetLocaleText(locale, "active_for_map_stats").c_str() : ABGetLocaleText(locale, "ignored_for_map_stats").c_str()); 156 | handler->PSendSysMessage(ABGetLocaleText(locale, "creature_difficulty_level").c_str(), targetABInfo->instancePlayerCount); 157 | 158 | // summon 159 | if (target->IsSummon() && targetABInfo->summoner && targetABInfo->isCloneOfSummoner) 160 | handler->PSendSysMessage(ABGetLocaleText(locale, "clone_of_summon").c_str(), targetABInfo->summonerName, targetABInfo->summonerLevel); 161 | else if (target->IsSummon() && targetABInfo->summoner) 162 | handler->PSendSysMessage(ABGetLocaleText(locale, "summon_of_summon").c_str(), targetABInfo->summonerName, targetABInfo->summonerLevel); 163 | else if (target->IsSummon()) 164 | handler->PSendSysMessage(ABGetLocaleText(locale, "summon_without_summoner").c_str()); 165 | 166 | // level scaled 167 | if (targetABInfo->UnmodifiedLevel != target->GetLevel()) 168 | { 169 | handler->PSendSysMessage(ABGetLocaleText(locale, "health_multiplier_to").c_str(), targetABInfo->HealthMultiplier, targetABInfo->ScaledHealthMultiplier); 170 | handler->PSendSysMessage(ABGetLocaleText(locale, "mana_multiplier_to").c_str(), targetABInfo->ManaMultiplier, targetABInfo->ScaledManaMultiplier); 171 | handler->PSendSysMessage(ABGetLocaleText(locale, "armor_multiplier_to").c_str(), targetABInfo->ArmorMultiplier, targetABInfo->ScaledArmorMultiplier); 172 | handler->PSendSysMessage(ABGetLocaleText(locale, "damage_multiplier_to").c_str(), targetABInfo->DamageMultiplier, targetABInfo->ScaledDamageMultiplier); 173 | } 174 | // not level scaled 175 | else 176 | { 177 | handler->PSendSysMessage(ABGetLocaleText(locale, "health_multiplier").c_str(), targetABInfo->HealthMultiplier); 178 | handler->PSendSysMessage(ABGetLocaleText(locale, "mana_multiplier").c_str(), targetABInfo->ManaMultiplier); 179 | handler->PSendSysMessage(ABGetLocaleText(locale, "armor_multiplier").c_str(), targetABInfo->ArmorMultiplier); 180 | handler->PSendSysMessage(ABGetLocaleText(locale, "damage_multiplier").c_str(), targetABInfo->DamageMultiplier); 181 | } 182 | handler->PSendSysMessage(ABGetLocaleText(locale, "cc_duration_multiplier").c_str(), targetABInfo->CCDurationMultiplier); 183 | handler->PSendSysMessage(ABGetLocaleText(locale, "xp_money_multiplier").c_str(), targetABInfo->XPModifier, targetABInfo->MoneyModifier); 184 | 185 | return true; 186 | } 187 | -------------------------------------------------------------------------------- /src/ABCommandScript.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016+ AzerothCore , license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE 3 | */ 4 | 5 | #ifndef __AB_COMMAND_SCRIPT_H 6 | #define __AB_COMMAND_SCRIPT_H 7 | 8 | #include "Chat.h" 9 | #include "Config.h" 10 | #include "ScriptMgr.h" 11 | 12 | using namespace Acore::ChatCommands; 13 | 14 | class AutoBalance_CommandScript : public CommandScript 15 | { 16 | public: 17 | AutoBalance_CommandScript() : CommandScript("AutoBalance_CommandScript") { } 18 | 19 | ChatCommandTable GetCommands() const override 20 | { 21 | static ChatCommandTable ABCommandTable = 22 | { 23 | { "setoffset", HandleABSetOffsetCommand, SEC_GAMEMASTER, Console::Yes }, 24 | { "getoffset", HandleABGetOffsetCommand, SEC_PLAYER, Console::Yes }, 25 | { "mapstat", HandleABMapStatsCommand, SEC_PLAYER, Console::Yes }, 26 | { "creaturestat", HandleABCreatureStatsCommand, SEC_PLAYER, Console::Yes } 27 | }; 28 | 29 | static ChatCommandTable commandTable = 30 | { 31 | { "autobalance", ABCommandTable }, 32 | { "ab", ABCommandTable }, 33 | }; 34 | 35 | return commandTable; 36 | }; 37 | 38 | static bool HandleABSetOffsetCommand(ChatHandler* handler, const char* args); 39 | static bool HandleABGetOffsetCommand(ChatHandler* handler, const char* args); 40 | static bool HandleABMapStatsCommand(ChatHandler* handler, const char* args); 41 | static bool HandleABCreatureStatsCommand(ChatHandler* handler, const char* args); 42 | }; 43 | 44 | #endif /* __AB_COMMAND_SCRIPT_H */ 45 | -------------------------------------------------------------------------------- /src/ABConfig.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016+ AzerothCore , license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE 3 | */ 4 | 5 | #include "ABConfig.h" 6 | #include "ABUtils.h" 7 | 8 | std::map forcedCreatureIds; 9 | std::list disabledDungeonIds; 10 | 11 | uint32 minPlayersNormal; 12 | uint32 minPlayersHeroic; 13 | uint32 minPlayersRaid; 14 | uint32 minPlayersRaidHeroic; 15 | std::map minPlayersPerDungeonIdMap; 16 | std::map minPlayersPerHeroicDungeonIdMap; 17 | 18 | std::map dungeonOverrides; 19 | std::map bossOverrides; 20 | std::map statModifierOverrides; 21 | std::map statModifierBossOverrides; 22 | std::map statModifierCreatureOverrides; 23 | std::map levelScalingDynamicLevelOverrides; 24 | std::map levelScalingDistanceCheckOverrides; 25 | 26 | // spell IDs that spend player health 27 | // player abilities don't actually appear to be caught by `ModifySpellDamageTaken`, 28 | // but I'm leaving them here in case they ever DO get caught by it 29 | std::list spellIdsThatSpendPlayerHealth = 30 | { 31 | 45529, // Blood Tap 32 | 2687, // Bloodrage 33 | 27869, // Dark Rune 34 | 16666, // Demonic Rune 35 | 755, // Health Funnel (Rank 1) 36 | 3698, // Health Funnel (Rank 2) 37 | 3699, // Health Funnel (Rank 3) 38 | 3700, // Health Funnel (Rank 4) 39 | 11693, // Health Funnel (Rank 5) 40 | 11694, // Health Funnel (Rank 6) 41 | 11695, // Health Funnel (Rank 7) 42 | 27259, // Health Funnel (Rank 8) 43 | 47856, // Health Funnel (Rank 9) 44 | 1454, // Life Tap (Rank 1) 45 | 1455, // Life Tap (Rank 2) 46 | 1456, // Life Tap (Rank 3) 47 | 11687, // Life Tap (Rank 4) 48 | 11688, // Life Tap (Rank 5) 49 | 11689, // Life Tap (Rank 6) 50 | 27222, // Life Tap (Rank 7) 51 | 57946, // Life Tap (Rank 8) 52 | 29858, // Soulshatter 53 | 55213 // Unholy Frenzy 54 | }; 55 | 56 | // spell IDs that should never be modified 57 | // handles cases where a spell is reflecting damage or otherwise converting player damage to something else 58 | std::list spellIdsToNeverModify = 59 | { 60 | 1177 // Twin Empathy (AQ40 Twin Emperors, only in `spell_dbc` database table) 61 | }; 62 | 63 | // creature IDs that should never be considered clones 64 | // handles cases where a creature is spawned by another creature, but is not a clone (doesn't retain health/mana values) 65 | std::list creatureIDsThatAreNotClones = 66 | { 67 | 16152 // Attumen the Huntsman (Karazhan) combined form 68 | }; 69 | 70 | int8 PlayerCountDifficultyOffset; 71 | 72 | bool LevelScaling; 73 | int8 LevelScalingSkipHigherLevels; 74 | int8 LevelScalingSkipLowerLevels; 75 | int8 LevelScalingDynamicLevelCeilingDungeons; 76 | int8 LevelScalingDynamicLevelFloorDungeons; 77 | int8 LevelScalingDynamicLevelCeilingRaids; 78 | int8 LevelScalingDynamicLevelFloorRaids; 79 | int8 LevelScalingDynamicLevelCeilingHeroicDungeons; 80 | int8 LevelScalingDynamicLevelFloorHeroicDungeons; 81 | int8 LevelScalingDynamicLevelCeilingHeroicRaids; 82 | int8 LevelScalingDynamicLevelFloorHeroicRaids; 83 | ScalingMethod LevelScalingMethod; 84 | 85 | uint32 rewardRaid; 86 | uint32 rewardDungeon; 87 | uint32 MinPlayerReward; 88 | 89 | bool Announcement; 90 | 91 | bool LevelScalingEndGameBoost; 92 | bool PlayerChangeNotify; 93 | bool rewardEnabled; 94 | 95 | float MinHPModifier; 96 | float MinManaModifier; 97 | float MinDamageModifier; 98 | float MinCCDurationModifier; 99 | float MaxCCDurationModifier; 100 | 101 | // 102 | // RewardScaling.* 103 | // 104 | 105 | ScalingMethod RewardScalingMethod; 106 | bool RewardScalingXP; 107 | bool RewardScalingMoney; 108 | float RewardScalingXPModifier; 109 | float RewardScalingMoneyModifier; 110 | 111 | uint64_t globalConfigTime = GetCurrentConfigTime(); 112 | 113 | // 114 | // Enable.* 115 | // 116 | 117 | bool EnableGlobal; 118 | bool Enable5M; 119 | bool Enable10M; 120 | bool Enable15M; 121 | bool Enable20M; 122 | bool Enable25M; 123 | bool Enable40M; 124 | bool Enable5MHeroic; 125 | bool Enable10MHeroic; 126 | bool Enable25MHeroic; 127 | bool EnableOtherNormal; 128 | bool EnableOtherHeroic; 129 | 130 | // 131 | // InflectionPoint* 132 | // 133 | 134 | float InflectionPoint; 135 | float InflectionPointCurveFloor; 136 | float InflectionPointCurveCeiling; 137 | float InflectionPointBoss; 138 | 139 | float InflectionPointHeroic; 140 | float InflectionPointHeroicCurveFloor; 141 | float InflectionPointHeroicCurveCeiling; 142 | float InflectionPointHeroicBoss; 143 | 144 | float InflectionPointRaid; 145 | float InflectionPointRaidCurveFloor; 146 | float InflectionPointRaidCurveCeiling; 147 | float InflectionPointRaidBoss; 148 | 149 | float InflectionPointRaidHeroic; 150 | float InflectionPointRaidHeroicCurveFloor; 151 | float InflectionPointRaidHeroicCurveCeiling; 152 | float InflectionPointRaidHeroicBoss; 153 | 154 | float InflectionPointRaid10M; 155 | float InflectionPointRaid10MCurveFloor; 156 | float InflectionPointRaid10MCurveCeiling; 157 | float InflectionPointRaid10MBoss; 158 | 159 | float InflectionPointRaid10MHeroic; 160 | float InflectionPointRaid10MHeroicCurveFloor; 161 | float InflectionPointRaid10MHeroicCurveCeiling; 162 | float InflectionPointRaid10MHeroicBoss; 163 | 164 | float InflectionPointRaid15M; 165 | float InflectionPointRaid15MCurveFloor; 166 | float InflectionPointRaid15MCurveCeiling; 167 | float InflectionPointRaid15MBoss; 168 | 169 | float InflectionPointRaid20M; 170 | float InflectionPointRaid20MCurveFloor; 171 | float InflectionPointRaid20MCurveCeiling; 172 | float InflectionPointRaid20MBoss; 173 | 174 | float InflectionPointRaid25M; 175 | float InflectionPointRaid25MCurveFloor; 176 | float InflectionPointRaid25MCurveCeiling; 177 | float InflectionPointRaid25MBoss; 178 | 179 | float InflectionPointRaid25MHeroic; 180 | float InflectionPointRaid25MHeroicCurveFloor; 181 | float InflectionPointRaid25MHeroicCurveCeiling; 182 | float InflectionPointRaid25MHeroicBoss; 183 | 184 | float InflectionPointRaid40M; 185 | float InflectionPointRaid40MCurveFloor; 186 | float InflectionPointRaid40MCurveCeiling; 187 | float InflectionPointRaid40MBoss; 188 | 189 | // 190 | // StatModifier* 191 | // 192 | 193 | float StatModifier_Global; 194 | float StatModifier_Health; 195 | float StatModifier_Mana; 196 | float StatModifier_Armor; 197 | float StatModifier_Damage; 198 | float StatModifier_CCDuration; 199 | 200 | float StatModifierHeroic_Global; 201 | float StatModifierHeroic_Health; 202 | float StatModifierHeroic_Mana; 203 | float StatModifierHeroic_Armor; 204 | float StatModifierHeroic_Damage; 205 | float StatModifierHeroic_CCDuration; 206 | 207 | float StatModifierRaid_Global; 208 | float StatModifierRaid_Health; 209 | float StatModifierRaid_Mana; 210 | float StatModifierRaid_Armor; 211 | float StatModifierRaid_Damage; 212 | float StatModifierRaid_CCDuration; 213 | 214 | float StatModifierRaidHeroic_Global; 215 | float StatModifierRaidHeroic_Health; 216 | float StatModifierRaidHeroic_Mana; 217 | float StatModifierRaidHeroic_Armor; 218 | float StatModifierRaidHeroic_Damage; 219 | float StatModifierRaidHeroic_CCDuration; 220 | 221 | float StatModifierRaid10M_Global; 222 | float StatModifierRaid10M_Health; 223 | float StatModifierRaid10M_Mana; 224 | float StatModifierRaid10M_Armor; 225 | float StatModifierRaid10M_Damage; 226 | float StatModifierRaid10M_CCDuration; 227 | 228 | float StatModifierRaid10MHeroic_Global; 229 | float StatModifierRaid10MHeroic_Health; 230 | float StatModifierRaid10MHeroic_Mana; 231 | float StatModifierRaid10MHeroic_Armor; 232 | float StatModifierRaid10MHeroic_Damage; 233 | float StatModifierRaid10MHeroic_CCDuration; 234 | 235 | float StatModifierRaid15M_Global; 236 | float StatModifierRaid15M_Health; 237 | float StatModifierRaid15M_Mana; 238 | float StatModifierRaid15M_Armor; 239 | float StatModifierRaid15M_Damage; 240 | float StatModifierRaid15M_CCDuration; 241 | 242 | float StatModifierRaid20M_Global; 243 | float StatModifierRaid20M_Health; 244 | float StatModifierRaid20M_Mana; 245 | float StatModifierRaid20M_Armor; 246 | float StatModifierRaid20M_Damage; 247 | float StatModifierRaid20M_CCDuration; 248 | 249 | float StatModifierRaid25M_Global; 250 | float StatModifierRaid25M_Health; 251 | float StatModifierRaid25M_Mana; 252 | float StatModifierRaid25M_Armor; 253 | float StatModifierRaid25M_Damage; 254 | float StatModifierRaid25M_CCDuration; 255 | 256 | float StatModifierRaid25MHeroic_Global; 257 | float StatModifierRaid25MHeroic_Health; 258 | float StatModifierRaid25MHeroic_Mana; 259 | float StatModifierRaid25MHeroic_Armor; 260 | float StatModifierRaid25MHeroic_Damage; 261 | float StatModifierRaid25MHeroic_CCDuration; 262 | 263 | float StatModifierRaid40M_Global; 264 | float StatModifierRaid40M_Health; 265 | float StatModifierRaid40M_Mana; 266 | float StatModifierRaid40M_Armor; 267 | float StatModifierRaid40M_Damage; 268 | float StatModifierRaid40M_CCDuration; 269 | 270 | // 271 | // StatModifier* (Boss) 272 | // 273 | 274 | float StatModifier_Boss_Global; 275 | float StatModifier_Boss_Health; 276 | float StatModifier_Boss_Mana; 277 | float StatModifier_Boss_Armor; 278 | float StatModifier_Boss_Damage; 279 | float StatModifier_Boss_CCDuration; 280 | 281 | float StatModifierHeroic_Boss_Global; 282 | float StatModifierHeroic_Boss_Health; 283 | float StatModifierHeroic_Boss_Mana; 284 | float StatModifierHeroic_Boss_Armor; 285 | float StatModifierHeroic_Boss_Damage; 286 | float StatModifierHeroic_Boss_CCDuration; 287 | 288 | float StatModifierRaid_Boss_Global; 289 | float StatModifierRaid_Boss_Health; 290 | float StatModifierRaid_Boss_Mana; 291 | float StatModifierRaid_Boss_Armor; 292 | float StatModifierRaid_Boss_Damage; 293 | float StatModifierRaid_Boss_CCDuration; 294 | 295 | float StatModifierRaidHeroic_Boss_Global; 296 | float StatModifierRaidHeroic_Boss_Health; 297 | float StatModifierRaidHeroic_Boss_Mana; 298 | float StatModifierRaidHeroic_Boss_Armor; 299 | float StatModifierRaidHeroic_Boss_Damage; 300 | float StatModifierRaidHeroic_Boss_CCDuration; 301 | 302 | float StatModifierRaid10M_Boss_Global; 303 | float StatModifierRaid10M_Boss_Health; 304 | float StatModifierRaid10M_Boss_Mana; 305 | float StatModifierRaid10M_Boss_Armor; 306 | float StatModifierRaid10M_Boss_Damage; 307 | float StatModifierRaid10M_Boss_CCDuration; 308 | 309 | float StatModifierRaid10MHeroic_Boss_Global; 310 | float StatModifierRaid10MHeroic_Boss_Health; 311 | float StatModifierRaid10MHeroic_Boss_Mana; 312 | float StatModifierRaid10MHeroic_Boss_Armor; 313 | float StatModifierRaid10MHeroic_Boss_Damage; 314 | float StatModifierRaid10MHeroic_Boss_CCDuration; 315 | 316 | float StatModifierRaid15M_Boss_Global; 317 | float StatModifierRaid15M_Boss_Health; 318 | float StatModifierRaid15M_Boss_Mana; 319 | float StatModifierRaid15M_Boss_Armor; 320 | float StatModifierRaid15M_Boss_Damage; 321 | float StatModifierRaid15M_Boss_CCDuration; 322 | 323 | float StatModifierRaid20M_Boss_Global; 324 | float StatModifierRaid20M_Boss_Health; 325 | float StatModifierRaid20M_Boss_Mana; 326 | float StatModifierRaid20M_Boss_Armor; 327 | float StatModifierRaid20M_Boss_Damage; 328 | float StatModifierRaid20M_Boss_CCDuration; 329 | 330 | float StatModifierRaid25M_Boss_Global; 331 | float StatModifierRaid25M_Boss_Health; 332 | float StatModifierRaid25M_Boss_Mana; 333 | float StatModifierRaid25M_Boss_Armor; 334 | float StatModifierRaid25M_Boss_Damage; 335 | float StatModifierRaid25M_Boss_CCDuration; 336 | 337 | float StatModifierRaid25MHeroic_Boss_Global; 338 | float StatModifierRaid25MHeroic_Boss_Health; 339 | float StatModifierRaid25MHeroic_Boss_Mana; 340 | float StatModifierRaid25MHeroic_Boss_Armor; 341 | float StatModifierRaid25MHeroic_Boss_Damage; 342 | float StatModifierRaid25MHeroic_Boss_CCDuration; 343 | 344 | float StatModifierRaid40M_Boss_Global; 345 | float StatModifierRaid40M_Boss_Health; 346 | float StatModifierRaid40M_Boss_Mana; 347 | float StatModifierRaid40M_Boss_Armor; 348 | float StatModifierRaid40M_Boss_Damage; 349 | float StatModifierRaid40M_Boss_CCDuration; 350 | -------------------------------------------------------------------------------- /src/ABConfig.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016+ AzerothCore , license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE 3 | */ 4 | 5 | #ifndef __AB_CONFIG_H 6 | #define __AB_CONFIG_H 7 | 8 | #include "ABInflectionPointSettings.h" 9 | #include "ABLevelScalingDynamicLevelSettings.h" 10 | #include "ABStatModifiers.h" 11 | #include "AutoBalance.h" 12 | 13 | #include "SharedDefines.h" 14 | 15 | #include 16 | #include 17 | 18 | extern std::map dungeonOverrides; 19 | extern std::map bossOverrides; 20 | extern std::map statModifierOverrides; 21 | extern std::map statModifierBossOverrides; 22 | extern std::map statModifierCreatureOverrides; 23 | extern std::map levelScalingDynamicLevelOverrides; 24 | extern std::map levelScalingDistanceCheckOverrides; 25 | 26 | extern std::map forcedCreatureIds; 27 | extern std::list disabledDungeonIds; 28 | 29 | extern uint32 minPlayersNormal; 30 | extern uint32 minPlayersHeroic; 31 | extern uint32 minPlayersRaid; 32 | extern uint32 minPlayersRaidHeroic; 33 | extern std::map minPlayersPerDungeonIdMap; 34 | extern std::map minPlayersPerHeroicDungeonIdMap; 35 | 36 | extern std::list spellIdsThatSpendPlayerHealth; 37 | extern std::list spellIdsToNeverModify; 38 | extern std::list creatureIDsThatAreNotClones; 39 | 40 | extern int8 PlayerCountDifficultyOffset; 41 | 42 | extern bool LevelScaling; 43 | extern int8 LevelScalingSkipHigherLevels; 44 | extern int8 LevelScalingSkipLowerLevels; 45 | extern int8 LevelScalingDynamicLevelCeilingDungeons; 46 | extern int8 LevelScalingDynamicLevelFloorDungeons; 47 | extern int8 LevelScalingDynamicLevelCeilingRaids; 48 | extern int8 LevelScalingDynamicLevelFloorRaids; 49 | extern int8 LevelScalingDynamicLevelCeilingHeroicDungeons; 50 | extern int8 LevelScalingDynamicLevelFloorHeroicDungeons; 51 | extern int8 LevelScalingDynamicLevelCeilingHeroicRaids; 52 | extern int8 LevelScalingDynamicLevelFloorHeroicRaids; 53 | extern ScalingMethod LevelScalingMethod; 54 | 55 | extern uint32 rewardRaid; 56 | extern uint32 rewardDungeon; 57 | extern uint32 MinPlayerReward; 58 | 59 | extern bool Announcement; 60 | 61 | extern bool LevelScalingEndGameBoost; 62 | extern bool PlayerChangeNotify; 63 | extern bool rewardEnabled; 64 | 65 | extern float MinHPModifier; 66 | extern float MinManaModifier; 67 | extern float MinDamageModifier; 68 | extern float MinCCDurationModifier; 69 | extern float MaxCCDurationModifier; 70 | 71 | // 72 | // RewardScaling.* 73 | // 74 | 75 | extern ScalingMethod RewardScalingMethod; 76 | extern bool RewardScalingXP; 77 | extern bool RewardScalingMoney; 78 | extern float RewardScalingXPModifier; 79 | extern float RewardScalingMoneyModifier; 80 | 81 | extern uint64_t globalConfigTime; 82 | 83 | // 84 | // Enable.* 85 | // 86 | 87 | extern bool EnableGlobal; 88 | extern bool Enable5M; 89 | extern bool Enable10M; 90 | extern bool Enable15M; 91 | extern bool Enable20M; 92 | extern bool Enable25M; 93 | extern bool Enable40M; 94 | extern bool Enable5MHeroic; 95 | extern bool Enable10MHeroic; 96 | extern bool Enable25MHeroic; 97 | extern bool EnableOtherNormal; 98 | extern bool EnableOtherHeroic; 99 | 100 | // 101 | // InflectionPoint* 102 | // 103 | 104 | extern float InflectionPoint; 105 | extern float InflectionPointCurveFloor; 106 | extern float InflectionPointCurveCeiling; 107 | extern float InflectionPointBoss; 108 | 109 | extern float InflectionPointHeroic; 110 | extern float InflectionPointHeroicCurveFloor; 111 | extern float InflectionPointHeroicCurveCeiling; 112 | extern float InflectionPointHeroicBoss; 113 | 114 | extern float InflectionPointRaid; 115 | extern float InflectionPointRaidCurveFloor; 116 | extern float InflectionPointRaidCurveCeiling; 117 | extern float InflectionPointRaidBoss; 118 | 119 | extern float InflectionPointRaidHeroic; 120 | extern float InflectionPointRaidHeroicCurveFloor; 121 | extern float InflectionPointRaidHeroicCurveCeiling; 122 | extern float InflectionPointRaidHeroicBoss; 123 | 124 | extern float InflectionPointRaid10M; 125 | extern float InflectionPointRaid10MCurveFloor; 126 | extern float InflectionPointRaid10MCurveCeiling; 127 | extern float InflectionPointRaid10MBoss; 128 | 129 | extern float InflectionPointRaid10MHeroic; 130 | extern float InflectionPointRaid10MHeroicCurveFloor; 131 | extern float InflectionPointRaid10MHeroicCurveCeiling; 132 | extern float InflectionPointRaid10MHeroicBoss; 133 | 134 | extern float InflectionPointRaid15M; 135 | extern float InflectionPointRaid15MCurveFloor; 136 | extern float InflectionPointRaid15MCurveCeiling; 137 | extern float InflectionPointRaid15MBoss; 138 | 139 | extern float InflectionPointRaid20M; 140 | extern float InflectionPointRaid20MCurveFloor; 141 | extern float InflectionPointRaid20MCurveCeiling; 142 | extern float InflectionPointRaid20MBoss; 143 | 144 | extern float InflectionPointRaid25M; 145 | extern float InflectionPointRaid25MCurveFloor; 146 | extern float InflectionPointRaid25MCurveCeiling; 147 | extern float InflectionPointRaid25MBoss; 148 | 149 | extern float InflectionPointRaid25MHeroic; 150 | extern float InflectionPointRaid25MHeroicCurveFloor; 151 | extern float InflectionPointRaid25MHeroicCurveCeiling; 152 | extern float InflectionPointRaid25MHeroicBoss; 153 | 154 | extern float InflectionPointRaid40M; 155 | extern float InflectionPointRaid40MCurveFloor; 156 | extern float InflectionPointRaid40MCurveCeiling; 157 | extern float InflectionPointRaid40MBoss; 158 | 159 | // 160 | // StatModifier* 161 | // 162 | 163 | extern float StatModifier_Global; 164 | extern float StatModifier_Health; 165 | extern float StatModifier_Mana; 166 | extern float StatModifier_Armor; 167 | extern float StatModifier_Damage; 168 | extern float StatModifier_CCDuration; 169 | 170 | extern float StatModifierHeroic_Global; 171 | extern float StatModifierHeroic_Health; 172 | extern float StatModifierHeroic_Mana; 173 | extern float StatModifierHeroic_Armor; 174 | extern float StatModifierHeroic_Damage; 175 | extern float StatModifierHeroic_CCDuration; 176 | 177 | extern float StatModifierRaid_Global; 178 | extern float StatModifierRaid_Health; 179 | extern float StatModifierRaid_Mana; 180 | extern float StatModifierRaid_Armor; 181 | extern float StatModifierRaid_Damage; 182 | extern float StatModifierRaid_CCDuration; 183 | 184 | extern float StatModifierRaidHeroic_Global; 185 | extern float StatModifierRaidHeroic_Health; 186 | extern float StatModifierRaidHeroic_Mana; 187 | extern float StatModifierRaidHeroic_Armor; 188 | extern float StatModifierRaidHeroic_Damage; 189 | extern float StatModifierRaidHeroic_CCDuration; 190 | 191 | extern float StatModifierRaid10M_Global; 192 | extern float StatModifierRaid10M_Health; 193 | extern float StatModifierRaid10M_Mana; 194 | extern float StatModifierRaid10M_Armor; 195 | extern float StatModifierRaid10M_Damage; 196 | extern float StatModifierRaid10M_CCDuration; 197 | 198 | extern float StatModifierRaid10MHeroic_Global; 199 | extern float StatModifierRaid10MHeroic_Health; 200 | extern float StatModifierRaid10MHeroic_Mana; 201 | extern float StatModifierRaid10MHeroic_Armor; 202 | extern float StatModifierRaid10MHeroic_Damage; 203 | extern float StatModifierRaid10MHeroic_CCDuration; 204 | 205 | extern float StatModifierRaid15M_Global; 206 | extern float StatModifierRaid15M_Health; 207 | extern float StatModifierRaid15M_Mana; 208 | extern float StatModifierRaid15M_Armor; 209 | extern float StatModifierRaid15M_Damage; 210 | extern float StatModifierRaid15M_CCDuration; 211 | 212 | extern float StatModifierRaid20M_Global; 213 | extern float StatModifierRaid20M_Health; 214 | extern float StatModifierRaid20M_Mana; 215 | extern float StatModifierRaid20M_Armor; 216 | extern float StatModifierRaid20M_Damage; 217 | extern float StatModifierRaid20M_CCDuration; 218 | 219 | extern float StatModifierRaid25M_Global; 220 | extern float StatModifierRaid25M_Health; 221 | extern float StatModifierRaid25M_Mana; 222 | extern float StatModifierRaid25M_Armor; 223 | extern float StatModifierRaid25M_Damage; 224 | extern float StatModifierRaid25M_CCDuration; 225 | 226 | extern float StatModifierRaid25MHeroic_Global; 227 | extern float StatModifierRaid25MHeroic_Health; 228 | extern float StatModifierRaid25MHeroic_Mana; 229 | extern float StatModifierRaid25MHeroic_Armor; 230 | extern float StatModifierRaid25MHeroic_Damage; 231 | extern float StatModifierRaid25MHeroic_CCDuration; 232 | 233 | extern float StatModifierRaid40M_Global; 234 | extern float StatModifierRaid40M_Health; 235 | extern float StatModifierRaid40M_Mana; 236 | extern float StatModifierRaid40M_Armor; 237 | extern float StatModifierRaid40M_Damage; 238 | extern float StatModifierRaid40M_CCDuration; 239 | 240 | // 241 | // StatModifier* (Boss) 242 | // 243 | 244 | extern float StatModifier_Boss_Global; 245 | extern float StatModifier_Boss_Health; 246 | extern float StatModifier_Boss_Mana; 247 | extern float StatModifier_Boss_Armor; 248 | extern float StatModifier_Boss_Damage; 249 | extern float StatModifier_Boss_CCDuration; 250 | 251 | extern float StatModifierHeroic_Boss_Global; 252 | extern float StatModifierHeroic_Boss_Health; 253 | extern float StatModifierHeroic_Boss_Mana; 254 | extern float StatModifierHeroic_Boss_Armor; 255 | extern float StatModifierHeroic_Boss_Damage; 256 | extern float StatModifierHeroic_Boss_CCDuration; 257 | 258 | extern float StatModifierRaid_Boss_Global; 259 | extern float StatModifierRaid_Boss_Health; 260 | extern float StatModifierRaid_Boss_Mana; 261 | extern float StatModifierRaid_Boss_Armor; 262 | extern float StatModifierRaid_Boss_Damage; 263 | extern float StatModifierRaid_Boss_CCDuration; 264 | 265 | extern float StatModifierRaidHeroic_Boss_Global; 266 | extern float StatModifierRaidHeroic_Boss_Health; 267 | extern float StatModifierRaidHeroic_Boss_Mana; 268 | extern float StatModifierRaidHeroic_Boss_Armor; 269 | extern float StatModifierRaidHeroic_Boss_Damage; 270 | extern float StatModifierRaidHeroic_Boss_CCDuration; 271 | 272 | extern float StatModifierRaid10M_Boss_Global; 273 | extern float StatModifierRaid10M_Boss_Health; 274 | extern float StatModifierRaid10M_Boss_Mana; 275 | extern float StatModifierRaid10M_Boss_Armor; 276 | extern float StatModifierRaid10M_Boss_Damage; 277 | extern float StatModifierRaid10M_Boss_CCDuration; 278 | 279 | extern float StatModifierRaid10MHeroic_Boss_Global; 280 | extern float StatModifierRaid10MHeroic_Boss_Health; 281 | extern float StatModifierRaid10MHeroic_Boss_Mana; 282 | extern float StatModifierRaid10MHeroic_Boss_Armor; 283 | extern float StatModifierRaid10MHeroic_Boss_Damage; 284 | extern float StatModifierRaid10MHeroic_Boss_CCDuration; 285 | 286 | extern float StatModifierRaid15M_Boss_Global; 287 | extern float StatModifierRaid15M_Boss_Health; 288 | extern float StatModifierRaid15M_Boss_Mana; 289 | extern float StatModifierRaid15M_Boss_Armor; 290 | extern float StatModifierRaid15M_Boss_Damage; 291 | extern float StatModifierRaid15M_Boss_CCDuration; 292 | 293 | extern float StatModifierRaid40M_Boss_Damage; 294 | extern float StatModifierRaid20M_Boss_Global; 295 | extern float StatModifierRaid20M_Boss_Health; 296 | extern float StatModifierRaid20M_Boss_Mana; 297 | extern float StatModifierRaid20M_Boss_Armor; 298 | extern float StatModifierRaid20M_Boss_Damage; 299 | extern float StatModifierRaid20M_Boss_CCDuration; 300 | 301 | extern float StatModifierRaid25M_Boss_Global; 302 | extern float StatModifierRaid25M_Boss_Health; 303 | extern float StatModifierRaid25M_Boss_Mana; 304 | extern float StatModifierRaid25M_Boss_Armor; 305 | extern float StatModifierRaid25M_Boss_Damage; 306 | extern float StatModifierRaid25M_Boss_CCDuration; 307 | 308 | extern float StatModifierRaid25MHeroic_Boss_Global; 309 | extern float StatModifierRaid25MHeroic_Boss_Health; 310 | extern float StatModifierRaid25MHeroic_Boss_Mana; 311 | extern float StatModifierRaid25MHeroic_Boss_Armor; 312 | extern float StatModifierRaid25MHeroic_Boss_Damage; 313 | extern float StatModifierRaid25MHeroic_Boss_CCDuration; 314 | 315 | extern float StatModifierRaid40M_Boss_Global; 316 | extern float StatModifierRaid40M_Boss_Health; 317 | extern float StatModifierRaid40M_Boss_Mana; 318 | extern float StatModifierRaid40M_Boss_Armor; 319 | extern float StatModifierRaid40M_Boss_CCDuration; 320 | 321 | #endif 322 | -------------------------------------------------------------------------------- /src/ABCreatureInfo.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016+ AzerothCore , license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE 3 | */ 4 | 5 | #ifndef __AB_CREATURE_INFO_H 6 | #define __AB_CREATURE_INFO_H 7 | 8 | #include "AutoBalance.h" 9 | 10 | #include "Creature.h" 11 | #include "DataMap.h" 12 | 13 | class AutoBalanceCreatureInfo : public DataMap::Base 14 | { 15 | public: 16 | AutoBalanceCreatureInfo() {} 17 | 18 | uint64_t mapConfigTime = 1; // The last map config time that this creature was updated 19 | 20 | uint32 instancePlayerCount = 0; // The number of players this creature has been scaled for 21 | uint8 selectedLevel = 0; // The level that this creature should be set to 22 | 23 | float DamageMultiplier = 1.0f; // Per-player damage multiplier (no level scaling) 24 | float ScaledDamageMultiplier = 1.0f; // Per-player and level scaling damage multiplier 25 | 26 | float HealthMultiplier = 1.0f; // Per-player health multiplier (no level scaling) 27 | float ScaledHealthMultiplier = 1.0f; // Per-player and level scaling health multiplier 28 | 29 | float ManaMultiplier = 1.0f; // Per-player mana multiplier (no level scaling) 30 | float ScaledManaMultiplier = 1.0f; // Per-player and level scaling mana multiplier 31 | 32 | float ArmorMultiplier = 1.0f; // Per-player armor multiplier (no level scaling) 33 | float ScaledArmorMultiplier = 1.0f; // Per-player and level scaling armor multiplier 34 | 35 | float CCDurationMultiplier = 1.0f; // Per-player crowd control duration multiplier (level scaling doesn't affect this) 36 | 37 | float XPModifier = 1.0f; // Per-player XP modifier (level scaling provided by normal XP distribution) 38 | float MoneyModifier = 1.0f; // Per-player money modifier (no level scaling) 39 | 40 | uint8 UnmodifiedLevel = 0; // Original level of the creature as determined by the game 41 | 42 | bool isActive = false; // Whether or not the current creature is affecting map stats. May change as conditions change. 43 | bool wasAliveNowDead = false; // Whether or not the creature was alive and is now dead 44 | bool isInCreatureList = false; // Whether or not the creature is in the map's creature list 45 | bool isBrandNew = false; // Whether or not the creature is brand new to the map (hasn't been added to the world yet) 46 | bool neverLevelScale = false; // Whether or not the creature should never be level scaled (can still be player scaled) 47 | 48 | uint32 initialMaxHealth = 0; // Stored max health value to be applied just before being added to the world 49 | 50 | // creature->IsSummon() // Whether or not the creature is a summon 51 | Creature* summoner = nullptr; // The creature that summoned this creature 52 | std::string summonerName = ""; // The name of the creature that summoned this creature 53 | uint8 summonerLevel = 0; // The level of the creature that summoned this creature 54 | bool isCloneOfSummoner = false; // Whether or not the creature is a clone of its summoner 55 | 56 | Relevance relevance = AUTOBALANCE_RELEVANCE_UNCHECKED; // Whether or not the creature is relevant for scaling 57 | }; 58 | 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /src/ABGameObjectScript.cpp: -------------------------------------------------------------------------------- 1 | #include "ABGameObjectScript.h" 2 | 3 | #include "ABConfig.h" 4 | #include "ABMapInfo.h" 5 | #include "ABUtils.h" 6 | 7 | void AutoBalance_GameObjectScript::OnGameObjectModifyHealth(GameObject* target, Unit* source, int32& amount, SpellInfo const* spellInfo) 8 | { 9 | // uncomment to debug this hook 10 | bool _debug_damage_and_healing = (source && target && (source->GetTypeId() == TYPEID_PLAYER || source->IsControlledByPlayer())); 11 | 12 | if (_debug_damage_and_healing) 13 | _Debug_Output("OnGameObjectModifyHealth", target, source, amount, "BEFORE:", spellInfo->SpellName[0], spellInfo->Id); 14 | 15 | // modify the amount 16 | amount = _Modify_GameObject_Damage_Healing(target, source, amount, spellInfo); 17 | 18 | if (_debug_damage_and_healing) 19 | _Debug_Output("OnGameObjectModifyHealth", target, source, amount, "AFTER:", spellInfo->SpellName[0], spellInfo->Id); 20 | } 21 | 22 | void AutoBalance_GameObjectScript::_Debug_Output(std::string function_name, GameObject* target, Unit* source, int32 amount, std::string prefix, std::string spell_name, uint32 spell_id) 23 | { 24 | if (target && source && amount) 25 | { 26 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_GameObjectScript::{}: {} {} {} {} ({} - {})", 27 | function_name, 28 | prefix, 29 | source->GetName(), 30 | amount, 31 | target->GetName(), 32 | spell_name, 33 | spell_id 34 | ); 35 | } 36 | else if (target && source) 37 | { 38 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_GameObjectScript::{}: {} {} 0 {} ({} - {})", 39 | function_name, 40 | prefix, 41 | source->GetName(), 42 | target->GetName(), 43 | spell_name, 44 | spell_id 45 | ); 46 | } 47 | else if (target && amount) 48 | { 49 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_GameObjectScript::{}: {} ?? {} {} ({} - {})", 50 | function_name, 51 | prefix, 52 | amount, 53 | target->GetName(), 54 | spell_name, 55 | spell_id 56 | ); 57 | } 58 | else if (target) 59 | { 60 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_GameObjectScript::{}: {} ?? ?? {} ({} - {})", 61 | function_name, 62 | prefix, 63 | target->GetName(), 64 | spell_name, 65 | spell_id 66 | ); 67 | } 68 | else 69 | { 70 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_GameObjectScript::{}: {} W? T? F? ({} - {})", 71 | function_name, 72 | prefix, 73 | spell_name, 74 | spell_id 75 | ); 76 | } 77 | } 78 | 79 | int32 AutoBalance_GameObjectScript::_Modify_GameObject_Damage_Healing(GameObject* target, Unit* source, int32 amount, SpellInfo const* spellInfo) 80 | { 81 | // 82 | // Pre-flight Checks 83 | // 84 | 85 | // uncomment to debug this function 86 | bool _debug_damage_and_healing = (source && target && (source->GetTypeId() == TYPEID_PLAYER || source->IsControlledByPlayer())); 87 | 88 | // check that we're enabled globally, else return the original value 89 | if (!EnableGlobal) 90 | { 91 | if (_debug_damage_and_healing) 92 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_GameObjectScript::_Modify_GameObject_Damage_Healing: EnableGlobal is false, returning original value of ({}).", amount); 93 | 94 | return amount; 95 | } 96 | 97 | // make sure the target is in an instance, else return the original damage 98 | if (!(target->GetMap()->IsDungeon())) 99 | { 100 | if (_debug_damage_and_healing) 101 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_GameObjectScript::_Modify_GameObject_Damage_Healing: Target is not in an instance, returning original value of ({}).", amount); 102 | 103 | return amount; 104 | } 105 | 106 | // make sure the target is in the world, else return the original value 107 | if (!target->IsInWorld()) 108 | { 109 | if (_debug_damage_and_healing) 110 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_GameObjectScript::_Modify_GameObject_Damage_Healing: Target does not exist in the world, returning original value of ({}).", amount); 111 | 112 | return amount; 113 | } 114 | 115 | // if the spell ID is in our "never modify" list, return the original value 116 | if 117 | ( 118 | spellInfo && 119 | spellInfo->Id && 120 | std::find 121 | ( 122 | spellIdsToNeverModify.begin(), 123 | spellIdsToNeverModify.end(), 124 | spellInfo->Id 125 | ) != spellIdsToNeverModify.end() 126 | ) 127 | { 128 | if (_debug_damage_and_healing) 129 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_UnitScript::_Modify_Damage_Healing: Spell {}({}) is in the never modify list, returning original value of ({}).", 130 | spellInfo->SpellName[0], 131 | spellInfo->Id, 132 | amount 133 | ); 134 | 135 | return amount; 136 | } 137 | 138 | // get the map's info 139 | AutoBalanceMapInfo* targetMapABInfo = GetMapInfo(target->GetMap()); 140 | 141 | // if the target's map is not enabled, return the original damage 142 | if (!targetMapABInfo->enabled) 143 | { 144 | if (_debug_damage_and_healing) 145 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_GameObjectScript::_Modify_GameObject_Damage_Healing: Target's map is not enabled, returning original value of ({}).", amount); 146 | 147 | return amount; 148 | } 149 | 150 | // 151 | // Multiplier calculation 152 | // 153 | 154 | // calculate the new damage amount using the map's World Health Multiplier 155 | int32 newAmount = _Calculate_Amount_For_GameObject(target, amount, targetMapABInfo->worldHealthMultiplier); 156 | 157 | if (_debug_damage_and_healing) 158 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_GameObjectScript::_Modify_GameObject_Damage_Healing: Returning modified damage: ({}) -> ({})", amount, newAmount); 159 | 160 | return newAmount; 161 | } 162 | 163 | int32 AutoBalance_GameObjectScript::_Calculate_Amount_For_GameObject(GameObject* target, int32 amount, float multiplier) 164 | { 165 | // since it would be very complicated to reduce the real health of destructible game objects, instead we will 166 | // adjust the damage to them as though their health were scaled. Damage will usually be dealt by vehicles and 167 | // other non-player sources, so this effect shouldn't be as noticable as if we applied it to the player. 168 | uint32 realMaxHealth = target->GetGOValue()->Building.MaxHealth; 169 | 170 | uint32 scaledMaxHealth = realMaxHealth * multiplier; 171 | float percentDamageOfScaledMaxHealth = (float)amount / (float)scaledMaxHealth; 172 | 173 | uint32 scaledAmount = realMaxHealth * percentDamageOfScaledMaxHealth; 174 | 175 | return scaledAmount; 176 | } 177 | -------------------------------------------------------------------------------- /src/ABGameObjectScript.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016+ AzerothCore , license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE 3 | */ 4 | 5 | #ifndef __AB_GAME_OBJECT_SCRIPT_H 6 | #define __AB_GAME_OBJECT_SCRIPT_H 7 | 8 | #include "ScriptMgr.h" 9 | #include "SpellInfo.h" 10 | #include "Unit.h" 11 | 12 | class AutoBalance_GameObjectScript : public AllGameObjectScript 13 | { 14 | public: 15 | AutoBalance_GameObjectScript() 16 | : AllGameObjectScript("AutoBalance_GameObjectScript") 17 | {} 18 | 19 | void OnGameObjectModifyHealth(GameObject* target, Unit* source, int32& amount, SpellInfo const* spellInfo) override; 20 | 21 | private: 22 | 23 | [[maybe_unused]] bool _debug_damage_and_healing = false; // defaults to false, overwritten in each function 24 | 25 | void _Debug_Output(std::string function_name, GameObject* target, Unit* source, int32 amount, std::string prefix = "", std::string spell_name = "Unknown Spell", uint32 spell_id = 0); 26 | int32 _Modify_GameObject_Damage_Healing(GameObject* target, Unit* source, int32 amount, SpellInfo const* spellInfo); 27 | int32 _Calculate_Amount_For_GameObject(GameObject* target, int32 amount, float multiplier); 28 | }; 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/ABGlobalScript.cpp: -------------------------------------------------------------------------------- 1 | #include "ABGlobalScript.h" 2 | 3 | #include "ABConfig.h" 4 | #include "ABMapInfo.h" 5 | #include "ABUtils.h" 6 | 7 | void AutoBalance_GlobalScript::OnAfterUpdateEncounterState(Map* map, EncounterCreditType type, uint32 /*creditEntry*/, Unit* /*source*/, Difficulty /*difficulty_fixed*/, DungeonEncounterList const* /*encounters*/, uint32 /*dungeonCompleted*/, bool updated) 8 | { 9 | //if (!dungeonCompleted) 10 | // return; 11 | 12 | if (!rewardEnabled || !updated) 13 | return; 14 | 15 | AutoBalanceMapInfo* mapABInfo = GetMapInfo(map); 16 | 17 | if (mapABInfo->adjustedPlayerCount < MinPlayerReward) 18 | return; 19 | 20 | // skip if it's not a pre-wotlk dungeon/raid and if it's not scaled 21 | if (!LevelScaling || mapABInfo->mapLevel <= 70 || mapABInfo->lfgMinLevel <= 70 22 | // skip when not in dungeon or not kill credit 23 | || type != ENCOUNTER_CREDIT_KILL_CREATURE || !map->IsDungeon()) 24 | return; 25 | 26 | Map::PlayerList const& playerList = map->GetPlayers(); 27 | 28 | if (playerList.IsEmpty()) 29 | return; 30 | 31 | uint32 reward = map->ToInstanceMap()->GetMaxPlayers() > 5 ? rewardRaid : rewardDungeon; 32 | if (!reward) 33 | return; 34 | 35 | //instanceStart=0, endTime; 36 | uint8 difficulty = map->GetDifficulty(); 37 | 38 | for (Map::PlayerList::const_iterator itr = playerList.begin(); itr != playerList.end(); ++itr) 39 | { 40 | if (!itr->GetSource() || itr->GetSource()->IsGameMaster() || itr->GetSource()->GetLevel() < DEFAULT_MAX_LEVEL) 41 | continue; 42 | 43 | itr->GetSource()->AddItem(reward, 1 + difficulty); // difficulty boost 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/ABGlobalScript.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016+ AzerothCore , license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE 3 | */ 4 | 5 | #ifndef __AB_GLOBAL_SCRIPT_H 6 | #define __AB_GLOBAL_SCRIPT_H 7 | 8 | #include "ScriptMgr.h" 9 | 10 | class AutoBalance_GlobalScript : public GlobalScript { 11 | public: 12 | AutoBalance_GlobalScript() : GlobalScript("AutoBalance_GlobalScript", { 13 | GLOBALHOOK_ON_AFTER_UPDATE_ENCOUNTER_STATE 14 | }) { } 15 | 16 | void OnAfterUpdateEncounterState(Map* map, EncounterCreditType type, uint32 creditEntry, Unit* source, Difficulty difficulty_fixed, DungeonEncounterList const* encounters, uint32 dungeonCompleted, bool updated) override; 17 | }; 18 | 19 | #endif /* __AB_GLOBAL_SCRIPT_H */ 20 | -------------------------------------------------------------------------------- /src/ABInflectionPointSettings.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016+ AzerothCore , license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE 3 | */ 4 | 5 | #ifndef __AB_INFLECTION_POINT_SETTINGS_H 6 | #define __AB_INFLECTION_POINT_SETTINGS_H 7 | 8 | #include "DataMap.h" 9 | 10 | class AutoBalanceInflectionPointSettings : public DataMap::Base 11 | { 12 | public: 13 | AutoBalanceInflectionPointSettings() {} 14 | AutoBalanceInflectionPointSettings(float value, float curveFloor, float curveCeiling) : 15 | value(value), curveFloor(curveFloor), curveCeiling(curveCeiling) {} 16 | 17 | float value = 0.5; 18 | float curveFloor = 0.0; 19 | float curveCeiling = 1.0; 20 | }; 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /src/ABLevelScalingDynamicLevelSettings.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016+ AzerothCore , license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE 3 | */ 4 | 5 | #ifndef __AB_LEVEL_SCALING_DYNAMIC_LEVEL_SETTINGS_H 6 | #define __AB_LEVEL_SCALING_DYNAMIC_LEVEL_SETTINGS_H 7 | 8 | #include "DataMap.h" 9 | 10 | class AutoBalanceLevelScalingDynamicLevelSettings : public DataMap::Base 11 | { 12 | public: 13 | AutoBalanceLevelScalingDynamicLevelSettings() {} 14 | AutoBalanceLevelScalingDynamicLevelSettings(int skipHigher, int skipLower, int ceiling, int floor) : 15 | skipHigher(skipHigher), skipLower(skipLower), ceiling(ceiling), floor(floor) {} 16 | 17 | int skipHigher = 0; 18 | int skipLower = 0; 19 | int ceiling = 1; 20 | int floor = 1; 21 | }; 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/ABMapInfo.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016+ AzerothCore , license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE 3 | */ 4 | 5 | #ifndef __AB_MAP_INFO_H 6 | #define __AB_MAP_INFO_H 7 | 8 | #include "Creature.h" 9 | #include "DataMap.h" 10 | #include "Player.h" 11 | 12 | #include 13 | 14 | class AutoBalanceMapInfo : public DataMap::Base 15 | { 16 | public: 17 | AutoBalanceMapInfo() {} 18 | 19 | std::vector allMapCreatures; // All creatures in the map, active and non-active 20 | std::vector allMapPlayers; // All players that are currently in the map 21 | 22 | bool enabled = false; // Should AutoBalance make any changes to this map or its creatures? 23 | 24 | uint64_t globalConfigTime = 1; // The last global config time that this map was updated 25 | uint64_t mapConfigTime = 1; // The last map config time that this map was updated 26 | 27 | uint8 playerCount = 0; // The actual number of non-GM players in the map 28 | uint8 adjustedPlayerCount = 0; // The currently difficulty level expressed as number of players 29 | uint8 minPlayers = 1; // Will be set by the config 30 | 31 | uint8 mapLevel = 0; // Calculated from the avgCreatureLevel 32 | uint8 lowestPlayerLevel = 0; // The lowest-level player in the map 33 | uint8 highestPlayerLevel = 0; // The highest-level player in the map 34 | 35 | uint8 lfgMinLevel = 0; // The minimum level for the map according to LFG 36 | uint8 lfgTargetLevel = 0; // The target level for the map according to LFG 37 | uint8 lfgMaxLevel = 0; // The maximum level for the map according to LFG 38 | 39 | uint8 worldMultiplierTargetLevel = 0; // The level of the pseudo-creature that the world modifiers scale to 40 | float worldDamageHealingMultiplier = 1.0f; // The damage/healing multiplier for the world (where source isn't an enemy creature) 41 | float scaledWorldDamageHealingMultiplier = 1.0f; // The damage/healing multiplier for the world (where source isn't an enemy creature) 42 | float worldHealthMultiplier = 1.0f; // The "health" multiplier for any destructible buildings in the map 43 | 44 | bool combatLocked = false; // Whether or not the map is combat locked 45 | bool combatLockTripped = false; // Set to true when combat locking was needed during this current combat (some tried to leave) 46 | uint8 combatLockMinPlayers = 0; // The instance cannot be set to less than this number of players until combat ends 47 | 48 | uint8 highestCreatureLevel = 0; // The highest-level creature in the map 49 | uint8 lowestCreatureLevel = 0; // The lowest-level creature in the map 50 | float avgCreatureLevel = 0; // The average level of all active creatures in the map (continuously updated) 51 | uint32 activeCreatureCount = 0; // The number of creatures in the map that are included in the map's stats (not necessarily alive) 52 | 53 | bool isLevelScalingEnabled = false; // Whether level scaling is enabled on this map 54 | uint8 levelScalingSkipHigherLevels = 0; // Used to determine if this map should scale or not 55 | uint8 levelScalingSkipLowerLevels = 0; // Used to determine if this map should scale or not 56 | uint8 levelScalingDynamicCeiling = 0; // How many levels MORE than the highestPlayerLevel creature should be scaled to 57 | uint8 levelScalingDynamicFloor = 0; // How many levels LESS than the highestPlayerLevel creature should be scaled to 58 | 59 | uint8 prevMapLevel = 0; // Used to reduce calculations when they are not necessary 60 | bool initialized = false; // Whether or not the map has been initialized 61 | }; 62 | #endif 63 | -------------------------------------------------------------------------------- /src/ABModuleScript.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016+ AzerothCore , license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE 3 | */ 4 | 5 | #include "ABModuleScript.h" 6 | 7 | ABModuleScript::ABModuleScript(const char* name) 8 | : ModuleScript(name) 9 | { 10 | ScriptRegistry::AddScript(this); 11 | } 12 | -------------------------------------------------------------------------------- /src/ABModuleScript.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016+ AzerothCore , license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE 3 | */ 4 | 5 | #ifndef __AB_MODULE_SCRIPT_H 6 | #define __AB_MODULE_SCRIPT_H 7 | 8 | #include "Creature.h" 9 | #include "ScriptMgr.h" 10 | 11 | /* 12 | * Dedicated hooks for Autobalance Module 13 | * Can be used to extend/customize this system 14 | */ 15 | class ABModuleScript : public ModuleScript 16 | { 17 | protected: 18 | 19 | ABModuleScript(const char* name); 20 | 21 | public: 22 | virtual bool OnBeforeModifyAttributes(Creature* /*creature*/, uint32& /*instancePlayerCount*/) { return true; } 23 | virtual bool OnAfterDefaultMultiplier(Creature* /*creature*/, float& /*defaultMultiplier*/) { return true; } 24 | virtual bool OnBeforeUpdateStats (Creature* /*creature*/, uint32& /*scaledHealth*/, uint32& /*scaledMana*/, float& /*damageMultiplier*/, uint32& /*newBaseArmor*/) { return true; } 25 | }; 26 | 27 | template class ScriptRegistry; 28 | 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/ABPlayerScript.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "ABConfig.h" 4 | #include "ABCreatureInfo.h" 5 | #include "ABMapInfo.h" 6 | #include "ABPlayerScript.h" 7 | #include "ABUtils.h" 8 | 9 | #include "Chat.h" 10 | #include "Message.h" 11 | 12 | void AutoBalance_PlayerScript::OnPlayerLogin(Player* Player) 13 | { 14 | if (EnableGlobal && Announcement) 15 | ChatHandler(Player->GetSession()).SendSysMessage("This server is running the |cff4CFF00AutoBalance |rmodule."); 16 | } 17 | 18 | void AutoBalance_PlayerScript::OnPlayerLevelChanged(Player* player, uint8 oldlevel) 19 | { 20 | LOG_DEBUG("module.AutoBalance", "AutoBalance:: ------------------------------------------------"); 21 | 22 | LOG_DEBUG("module.AutoBalance", "AutoBalance_PlayerScript::OnLevelChanged: {} has leveled ({}->{})", player->GetName(), oldlevel, player->GetLevel()); 23 | if (!player || player->IsGameMaster()) 24 | return; 25 | 26 | Map* map = player->GetMap(); 27 | 28 | if (!map || !map->IsDungeon()) 29 | return; 30 | 31 | // update the map's player stats 32 | UpdateMapPlayerStats(map); 33 | 34 | // schedule all creatures for an update 35 | AutoBalanceMapInfo* mapABInfo = GetMapInfo(map); 36 | mapABInfo->mapConfigTime = GetCurrentConfigTime(); 37 | } 38 | 39 | void AutoBalance_PlayerScript::OnPlayerGiveXP(Player* player, uint32& amount, Unit* victim, uint8 /*xpSource*/) 40 | { 41 | Map* map = player->GetMap(); 42 | 43 | // If this isn't a dungeon, make no changes 44 | if (!map->IsDungeon() || !map->GetInstanceId() || !victim) 45 | return; 46 | 47 | AutoBalanceMapInfo* mapABInfo = GetMapInfo(map); 48 | 49 | if (victim && RewardScalingXP && mapABInfo->enabled) 50 | { 51 | Map* map = player->GetMap(); 52 | 53 | AutoBalanceCreatureInfo* creatureABInfo = victim->CustomData.GetDefault("AutoBalanceCreatureInfo"); 54 | 55 | if (map->IsDungeon()) 56 | { 57 | if (RewardScalingMethod == AUTOBALANCE_SCALING_DYNAMIC) 58 | { 59 | LOG_DEBUG("module.AutoBalance", "AutoBalance_PlayerScript::OnGiveXP: Distributing XP from '{}' to '{}' in dynamic mode - {}->{}", 60 | victim->GetName(), player->GetName(), amount, uint32(amount * creatureABInfo->XPModifier)); 61 | amount = uint32(amount * creatureABInfo->XPModifier); 62 | } 63 | else if (RewardScalingMethod == AUTOBALANCE_SCALING_FIXED) 64 | { 65 | // Ensure that the players always get the same XP, even when entering the dungeon alone 66 | auto maxPlayerCount = map->ToInstanceMap()->GetMaxPlayers(); 67 | auto currentPlayerCount = mapABInfo->playerCount; 68 | LOG_DEBUG("module.AutoBalance", "AutoBalance_PlayerScript::OnGiveXP: Distributing XP from '{}' to '{}' in fixed mode - {}->{}", 69 | victim->GetName(), player->GetName(), amount, uint32(amount * creatureABInfo->XPModifier * ((float)currentPlayerCount / maxPlayerCount))); 70 | amount = uint32(amount * creatureABInfo->XPModifier * ((float)currentPlayerCount / maxPlayerCount)); 71 | } 72 | } 73 | } 74 | } 75 | 76 | void AutoBalance_PlayerScript::OnPlayerBeforeLootMoney(Player* player, Loot* loot) 77 | { 78 | Map* map = player->GetMap(); 79 | 80 | // If this isn't a dungeon, make no changes 81 | if (!map->IsDungeon()) 82 | return; 83 | 84 | AutoBalanceMapInfo* mapABInfo = GetMapInfo(map); 85 | ObjectGuid sourceGuid = loot->sourceWorldObjectGUID; 86 | 87 | if (mapABInfo->enabled && RewardScalingMoney) 88 | { 89 | // if the loot source is a creature, honor the modifiers for that creature 90 | if (sourceGuid.IsCreature()) 91 | { 92 | Creature* sourceCreature = ObjectAccessor::GetCreature(*player, sourceGuid); 93 | AutoBalanceCreatureInfo* creatureABInfo = sourceCreature->CustomData.GetDefault("AutoBalanceCreatureInfo"); 94 | 95 | // Dynamic Mode 96 | if (RewardScalingMethod == AUTOBALANCE_SCALING_DYNAMIC) 97 | { 98 | LOG_DEBUG("module.AutoBalance", "AutoBalance_PlayerScript::OnBeforeLootMoney: Distributing money from '{}' in dynamic mode - {}->{}", 99 | sourceCreature->GetName(), loot->gold, uint32(loot->gold * creatureABInfo->MoneyModifier)); 100 | loot->gold = uint32(loot->gold * creatureABInfo->MoneyModifier); 101 | } 102 | // Fixed Mode 103 | else if (RewardScalingMethod == AUTOBALANCE_SCALING_FIXED) 104 | { 105 | // Ensure that the players always get the same money, even when entering the dungeon alone 106 | auto maxPlayerCount = map->ToInstanceMap()->GetMaxPlayers(); 107 | auto currentPlayerCount = mapABInfo->playerCount; 108 | LOG_DEBUG("module.AutoBalance", "AutoBalance_PlayerScript::OnBeforeLootMoney: Distributing money from '{}' in fixed mode - {}->{}", 109 | sourceCreature->GetName(), loot->gold, uint32(loot->gold * creatureABInfo->MoneyModifier * ((float)currentPlayerCount / maxPlayerCount))); 110 | loot->gold = uint32(loot->gold * creatureABInfo->MoneyModifier * ((float)currentPlayerCount / maxPlayerCount)); 111 | } 112 | } 113 | // for all other loot sources, just distribute in Fixed mode as though the instance was full 114 | else 115 | { 116 | auto maxPlayerCount = map->ToInstanceMap()->GetMaxPlayers(); 117 | auto currentPlayerCount = mapABInfo->playerCount; 118 | LOG_DEBUG("module.AutoBalance", "AutoBalance_PlayerScript::OnBeforeLootMoney: Distributing money from a non-creature in fixed mode - {}->{}", 119 | loot->gold, uint32(loot->gold * ((float)currentPlayerCount / maxPlayerCount))); 120 | loot->gold = uint32(loot->gold * ((float)currentPlayerCount / maxPlayerCount)); 121 | } 122 | } 123 | } 124 | 125 | void AutoBalance_PlayerScript::OnPlayerEnterCombat(Player* player, Unit* /*enemy*/) 126 | { 127 | // if the player or their map is gone, return 128 | if (!player || !player->GetMap()) 129 | return; 130 | 131 | Map* map = player->GetMap(); 132 | 133 | // If this isn't a dungeon, no work to do 134 | if (!map || !map->IsDungeon()) 135 | return; 136 | 137 | LOG_DEBUG("module.AutoBalance_CombatLocking", "AutoBalance_PlayerScript::OnPlayerEnterCombat: {} enters combat.", player->GetName()); 138 | 139 | AutoBalanceMapInfo* mapABInfo = GetMapInfo(map); 140 | 141 | // if this map isn't enabled, no work to do 142 | if (!mapABInfo->enabled) 143 | return; 144 | 145 | // lock the current map 146 | if (!mapABInfo->combatLocked) 147 | { 148 | mapABInfo->combatLocked = true; 149 | mapABInfo->combatLockMinPlayers = mapABInfo->playerCount; 150 | 151 | LOG_DEBUG("module.AutoBalance_CombatLocking", "AutoBalance_PlayerScript::OnPlayerEnterCombat: Map {} ({}{}) | Locking difficulty to no less than ({}) as {} enters combat.", 152 | map->GetMapName(), 153 | map->GetId(), 154 | map->GetInstanceId() ? "-" + std::to_string(map->GetInstanceId()) : "", 155 | mapABInfo->combatLockMinPlayers, 156 | player->GetName() 157 | ); 158 | } 159 | } 160 | 161 | void AutoBalance_PlayerScript::OnPlayerLeaveCombat(Player* player) 162 | { 163 | // if the player or their map is gone, return 164 | if (!player || !player->GetMap()) 165 | return; 166 | 167 | Map* map = player->GetMap(); 168 | 169 | // If this isn't a dungeon, no work to do 170 | if (!map || !map->IsDungeon()) 171 | return; 172 | 173 | // this hook can get called even if the player isn't in combat 174 | // I believe this happens whenever AC attempts to remove combat, but it doesn't check to see if the player is in combat first 175 | // unfortunately, `player->IsInCombat()` doesn't work here 176 | LOG_DEBUG("module.AutoBalance_CombatLocking", "AutoBalance_PlayerScript::OnPlayerLeaveCombat: {} leaves (or wasn't in) combat.", player->GetName()); 177 | 178 | AutoBalanceMapInfo* mapABInfo = GetMapInfo(map); 179 | 180 | // if this map isn't enabled, no work to do 181 | if (!mapABInfo->enabled) 182 | return; 183 | 184 | // check to see if any of the other players are in combat 185 | bool anyPlayersInCombat = false; 186 | for (auto player : mapABInfo->allMapPlayers) 187 | { 188 | if (player && player->IsInCombat()) 189 | { 190 | anyPlayersInCombat = true; 191 | 192 | LOG_DEBUG("module.AutoBalance_CombatLocking", "AutoBalance_PlayerScript::OnPlayerLeaveCombat: Map {} ({}{}) | Player {} (and potentially others) are still in combat.", 193 | map->GetMapName(), 194 | map->GetId(), 195 | map->GetInstanceId() ? "-" + std::to_string(map->GetInstanceId()) : "", 196 | player->GetName() 197 | ); 198 | 199 | break; 200 | } 201 | } 202 | 203 | auto locale = player->GetSession()->GetSessionDbLocaleIndex(); 204 | 205 | // if no players are in combat, unlock the map 206 | if (!anyPlayersInCombat && mapABInfo->combatLocked) 207 | { 208 | mapABInfo->combatLocked = false; 209 | mapABInfo->combatLockMinPlayers = 0; 210 | 211 | LOG_DEBUG("module.AutoBalance_CombatLocking", "AutoBalance_PlayerScript::OnPlayerLeaveCombat: Map {} ({}{}) | Unlocking difficulty as {} leaves combat.", 212 | map->GetMapName(), 213 | map->GetId(), 214 | map->GetInstanceId() ? "-" + std::to_string(map->GetInstanceId()) : "", 215 | player->GetName() 216 | ); 217 | 218 | // if the combat lock needed to be used, notify the players of it lifting 219 | if (mapABInfo->combatLockTripped) 220 | { 221 | for (auto player : mapABInfo->allMapPlayers) 222 | { 223 | if (player && player->GetSession()) 224 | ChatHandler(player->GetSession()).PSendSysMessage(ABGetLocaleText(locale, "leaving_instance_combat_change").c_str()); 225 | } 226 | } 227 | 228 | // if the number of players changed while combat was in progress, schedule the map for an update 229 | if (mapABInfo->combatLockTripped && mapABInfo->playerCount != mapABInfo->combatLockMinPlayers) 230 | { 231 | mapABInfo->mapConfigTime = 1; 232 | LOG_DEBUG("module.AutoBalance_CombatLocking", "AutoBalance_PlayerScript::OnPlayerLeaveCombat: Map {} ({}{}) | Reset map config time to ({}).", 233 | map->GetMapName(), 234 | map->GetId(), 235 | map->GetInstanceId() ? "-" + std::to_string(map->GetInstanceId()) : "", 236 | mapABInfo->mapConfigTime 237 | ); 238 | 239 | mapABInfo->combatLockTripped = false; 240 | } 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /src/ABPlayerScript.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016+ AzerothCore , license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE 3 | */ 4 | 5 | #ifndef __AB_PLAYER_SCRIPT_H 6 | #define __AB_PLAYER_SCRIPT_H 7 | 8 | #include "Player.h" 9 | #include "ScriptMgr.h" 10 | #include "Unit.h" 11 | 12 | class AutoBalance_PlayerScript : public PlayerScript 13 | { 14 | public: 15 | AutoBalance_PlayerScript() 16 | : PlayerScript("AutoBalance_PlayerScript") 17 | { 18 | } 19 | 20 | void OnPlayerLogin(Player* Player) override; 21 | virtual void OnPlayerLevelChanged(Player* player, uint8 oldlevel) override; 22 | void OnPlayerGiveXP(Player* player, uint32& amount, Unit* victim, uint8 xpSource) override; 23 | void OnPlayerBeforeLootMoney(Player* player, Loot* loot) override; 24 | virtual void OnPlayerEnterCombat(Player* player, Unit* enemy) override; 25 | virtual void OnPlayerLeaveCombat(Player* player) override; 26 | }; 27 | 28 | #endif /* __AB_PLAYER_SCRIPT_H */ 29 | -------------------------------------------------------------------------------- /src/ABScriptMgr.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016+ AzerothCore , license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE 3 | */ 4 | 5 | #include "ABModuleScript.h" 6 | #include "ABScriptMgr.h" 7 | 8 | #include "ScriptMgr.h" 9 | #include "ScriptMgrMacros.h" 10 | 11 | ABScriptMgr* ABScriptMgr::instance() 12 | { 13 | static ABScriptMgr instance; 14 | return &instance; 15 | } 16 | 17 | bool ABScriptMgr::OnBeforeModifyAttributes(Creature* creature, uint32& instancePlayerCount) 18 | { 19 | auto ret = IsValidBoolScript([&](ABModuleScript* script) 20 | { 21 | return !script->OnBeforeModifyAttributes(creature, instancePlayerCount); 22 | }); 23 | 24 | if (ret && *ret) 25 | return false; 26 | 27 | return true; 28 | } 29 | 30 | bool ABScriptMgr::OnAfterDefaultMultiplier(Creature* creature, float& defaultMultiplier) 31 | { 32 | auto ret = IsValidBoolScript([&](ABModuleScript* script) 33 | { 34 | return !script->OnAfterDefaultMultiplier(creature, defaultMultiplier); 35 | }); 36 | 37 | if (ret && *ret) 38 | return false; 39 | 40 | return true; 41 | } 42 | 43 | bool ABScriptMgr::OnBeforeUpdateStats(Creature* creature, uint32& scaledHealth, uint32& scaledMana, float& damageMultiplier, uint32& newBaseArmor) 44 | { 45 | auto ret = IsValidBoolScript([&](ABModuleScript* script) 46 | { 47 | return !script->OnBeforeUpdateStats(creature, scaledHealth, scaledMana, damageMultiplier, newBaseArmor); 48 | }); 49 | 50 | if (ret && *ret) 51 | return false; 52 | 53 | return true; 54 | } 55 | -------------------------------------------------------------------------------- /src/ABScriptMgr.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016+ AzerothCore , license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE 3 | */ 4 | 5 | #ifndef __AB_SCRIPT_MGR_H 6 | #define __AB_SCRIPT_MGR_H 7 | 8 | #include "Creature.h" 9 | #include "ScriptMgr.h" 10 | 11 | // Manages registration, loading, and execution of scripts. 12 | class ABScriptMgr 13 | { 14 | public: /* Initialization */ 15 | 16 | static ABScriptMgr* instance(); 17 | 18 | // Called at the start of ModifyCreatureAttributes method. 19 | // It can be used to add some condition to skip autobalancing system for example. 20 | bool OnBeforeModifyAttributes(Creature* creature, uint32 & instancePlayerCount); 21 | 22 | // Called right after default multiplier has been set, you can use it to change. 23 | // Current scaling formula based on number of players or just skip modifications 24 | bool OnAfterDefaultMultiplier(Creature* creature, float &defaultMultiplier); 25 | 26 | // Called before change creature values, to tune some values or skip modifications. 27 | bool OnBeforeUpdateStats (Creature* creature, uint32 &scaledHealth, uint32 &scaledMana, float &damageMultiplier, uint32 &newBaseArmor); 28 | }; 29 | 30 | #define sABScriptMgr ABScriptMgr::instance() 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /src/ABStatModifiers.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016+ AzerothCore , license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE 3 | */ 4 | 5 | #ifndef __AB_MODULE_STAT_MODIFIERS_H 6 | #define __AB_MODULE_STAT_MODIFIERS_H 7 | 8 | #include "DataMap.h" 9 | 10 | class AutoBalanceStatModifiers : public DataMap::Base 11 | { 12 | public: 13 | AutoBalanceStatModifiers() {} 14 | AutoBalanceStatModifiers(float global, float health, float mana, float armor, float damage, float ccduration) : 15 | global(global), health(health), mana(mana), armor(armor), damage(damage), ccduration(ccduration) {} 16 | 17 | float global = 1.0; 18 | float health = 1.0; 19 | float mana = 1.0; 20 | float armor = 1.0; 21 | float damage = 1.0; 22 | float ccduration = 1.0; 23 | }; 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /src/ABUnitScript.cpp: -------------------------------------------------------------------------------- 1 | #include "ABUnitScript.h" 2 | 3 | #include "ABConfig.h" 4 | #include "ABCreatureInfo.h" 5 | #include "ABMapInfo.h" 6 | #include "ABUtils.h" 7 | 8 | void AutoBalance_UnitScript::ModifyPeriodicDamageAurasTick(Unit* target, Unit* source, uint32& amount, SpellInfo const* spellInfo) 9 | { 10 | // if the spell is negative (damage), we need to flip the sign 11 | // if the spell is positive (healing or other) we keep it the same 12 | int32 adjustedAmount = !spellInfo->IsPositive() ? amount * -1 : amount; 13 | 14 | // only debug if the source or target is a player 15 | bool _debug_damage_and_healing = ((source && (source->GetTypeId() == TYPEID_PLAYER || source->IsControlledByPlayer())) || (target && target->GetTypeId() == TYPEID_PLAYER)); 16 | _debug_damage_and_healing = (source && source->GetMap()->GetInstanceId()); 17 | 18 | if (_debug_damage_and_healing) 19 | _Debug_Output("ModifyPeriodicDamageAurasTick", target, source, adjustedAmount, AUTOBALANCE_DAMAGE_HEALING_DEBUG_PHASE_BEFORE, spellInfo->SpellName[0], spellInfo->Id); 20 | 21 | // set amount to the absolute value of the function call 22 | // the provided amount doesn't indicate whether it's a positive or negative value 23 | adjustedAmount = _Modify_Damage_Healing(target, source, adjustedAmount, spellInfo); 24 | amount = abs(adjustedAmount); 25 | 26 | if (_debug_damage_and_healing) 27 | _Debug_Output("ModifyPeriodicDamageAurasTick", target, source, adjustedAmount, AUTOBALANCE_DAMAGE_HEALING_DEBUG_PHASE_AFTER, spellInfo->SpellName[0], spellInfo->Id); 28 | } 29 | 30 | void AutoBalance_UnitScript::ModifySpellDamageTaken(Unit* target, Unit* source, int32& amount, SpellInfo const* spellInfo) 31 | { 32 | // if the spell is negative (damage), we need to flip the sign to negative 33 | // if the spell is positive (healing or other) we keep it the same (positive) 34 | int32 adjustedAmount = !spellInfo->IsPositive() ? amount * -1 : amount; 35 | 36 | // only debug if the source or target is a player 37 | bool _debug_damage_and_healing = ((source && (source->GetTypeId() == TYPEID_PLAYER || source->IsControlledByPlayer())) || (target && target->GetTypeId() == TYPEID_PLAYER)); 38 | _debug_damage_and_healing = (source && source->GetMap()->GetInstanceId()); 39 | 40 | if (_debug_damage_and_healing) 41 | _Debug_Output("ModifySpellDamageTaken", target, source, adjustedAmount, AUTOBALANCE_DAMAGE_HEALING_DEBUG_PHASE_BEFORE, spellInfo->SpellName[0], spellInfo->Id); 42 | 43 | // set amount to the absolute value of the function call 44 | // the provided amount doesn't indicate whether it's a positive or negative value 45 | adjustedAmount = _Modify_Damage_Healing(target, source, adjustedAmount, spellInfo); 46 | amount = abs(adjustedAmount); 47 | 48 | if (_debug_damage_and_healing) 49 | _Debug_Output("ModifySpellDamageTaken", target, source, adjustedAmount, AUTOBALANCE_DAMAGE_HEALING_DEBUG_PHASE_AFTER, spellInfo->SpellName[0], spellInfo->Id); 50 | } 51 | 52 | void AutoBalance_UnitScript::ModifyMeleeDamage(Unit* target, Unit* source, uint32& amount) 53 | { 54 | // melee damage is always negative, so we need to flip the sign to negative 55 | int32 adjustedAmount = amount * -1; 56 | 57 | // only debug if the source or target is a player 58 | bool _debug_damage_and_healing = ((source && (source->GetTypeId() == TYPEID_PLAYER || source->IsControlledByPlayer())) || (target && target->GetTypeId() == TYPEID_PLAYER)); 59 | _debug_damage_and_healing = (source && source->GetMap()->GetInstanceId()); 60 | 61 | if (_debug_damage_and_healing) 62 | _Debug_Output("ModifyMeleeDamage", target, source, adjustedAmount, AUTOBALANCE_DAMAGE_HEALING_DEBUG_PHASE_BEFORE, "Melee"); 63 | 64 | // set amount to the absolute value of the function call 65 | adjustedAmount = _Modify_Damage_Healing(target, source, adjustedAmount); 66 | amount = abs(adjustedAmount); 67 | 68 | if (_debug_damage_and_healing) 69 | _Debug_Output("ModifyMeleeDamage", target, source, adjustedAmount, AUTOBALANCE_DAMAGE_HEALING_DEBUG_PHASE_AFTER, "Melee"); 70 | } 71 | 72 | void AutoBalance_UnitScript::ModifyHealReceived(Unit* target, Unit* source, uint32& amount, SpellInfo const* spellInfo) 73 | { 74 | // healing is always positive, no need for any sign flip 75 | 76 | // only debug if the source or target is a player 77 | bool _debug_damage_and_healing = ((source && (source->GetTypeId() == TYPEID_PLAYER || source->IsControlledByPlayer())) || (target && target->GetTypeId() == TYPEID_PLAYER)); 78 | _debug_damage_and_healing = (source && source->GetMap()->GetInstanceId()); 79 | 80 | if (_debug_damage_and_healing) 81 | _Debug_Output("ModifyHealReceived", target, source, amount, AUTOBALANCE_DAMAGE_HEALING_DEBUG_PHASE_BEFORE, spellInfo->SpellName[0], spellInfo->Id); 82 | 83 | amount = _Modify_Damage_Healing(target, source, amount, spellInfo); 84 | 85 | if (_debug_damage_and_healing) 86 | _Debug_Output("ModifyHealReceived", target, source, amount, AUTOBALANCE_DAMAGE_HEALING_DEBUG_PHASE_AFTER, spellInfo->SpellName[0], spellInfo->Id); 87 | } 88 | 89 | void AutoBalance_UnitScript::OnAuraApply(Unit* unit, Aura* aura) 90 | { 91 | // only debug if the source or target is a player 92 | bool _debug_damage_and_healing = (unit && unit->GetTypeId() == TYPEID_PLAYER); 93 | _debug_damage_and_healing = (unit && unit->GetMap()->GetInstanceId()); 94 | 95 | // Only if this aura has a duration 96 | if (aura && (aura->GetDuration() > 0 || aura->GetMaxDuration() > 0)) 97 | { 98 | uint32 auraDuration = _Modifier_CCDuration(unit, aura->GetCaster(), aura); 99 | 100 | // only update if we decided to change it 101 | if (auraDuration != (float)aura->GetDuration()) 102 | { 103 | if (_debug_damage_and_healing) 104 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_UnitScript::OnAuraApply(): Spell '{}' had it's duration adjusted ({}->{}).", 105 | aura->GetSpellInfo()->SpellName[0], 106 | aura->GetMaxDuration() / 1000, 107 | auraDuration / 1000 108 | ); 109 | 110 | aura->SetMaxDuration(auraDuration); 111 | aura->SetDuration(auraDuration); 112 | } 113 | } 114 | } 115 | 116 | void AutoBalance_UnitScript::_Debug_Output(std::string function_name, Unit* target, Unit* source, int32 amount, Damage_Healing_Debug_Phase phase, std::string spell_name, uint32 spell_id) 117 | { 118 | if (phase == AUTOBALANCE_DAMAGE_HEALING_DEBUG_PHASE_BEFORE) 119 | { 120 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance:: ------------------------------------------------"); 121 | } 122 | 123 | if (target && source && amount) 124 | { 125 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_UnitScript::{}: {}: {}{} {} {}{} with {}{} for ({})", 126 | function_name, 127 | phase ? "AFTER" : "BEFORE", 128 | source->GetName(), 129 | source->GetEntry() ? " (" + std::to_string(source->GetEntry()) + ")" : "", 130 | amount > 0 ? "heals" : "damages", 131 | target->GetName(), 132 | target->GetEntry() ? " (" + std::to_string(target->GetEntry()) + ")" : "", 133 | spell_name, 134 | spell_id ? " (" + std::to_string(spell_id) + ")" : "", 135 | amount 136 | ); 137 | } 138 | else if (target && source) 139 | { 140 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_UnitScript::{}: {}: {}{} damages {}{} with {}{} for (0)", 141 | function_name, 142 | phase ? "AFTER" : "BEFORE", 143 | source->GetName(), 144 | source->GetEntry() ? " (" + std::to_string(source->GetEntry()) + ")" : "", 145 | target->GetName(), 146 | target->GetEntry() ? " (" + std::to_string(target->GetEntry()) + ")" : "", 147 | spell_name, 148 | spell_id ? " (" + std::to_string(spell_id) + ")" : "" 149 | ); 150 | } 151 | else if (target && amount) 152 | { 153 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_UnitScript::{}: {}: ?? {} {}{} with {}{} for ({})", 154 | function_name, 155 | phase ? "AFTER" : "BEFORE", 156 | amount > 0 ? "heals" : "damages", 157 | target->GetName(), 158 | target->GetEntry() ? " (" + std::to_string(target->GetEntry()) + ")" : "", 159 | spell_name, 160 | spell_id ? " (" + std::to_string(spell_id) + ")" : "", 161 | amount 162 | ); 163 | } 164 | else if (target) 165 | { 166 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_UnitScript::{}: {}: ?? affects {}{} with {}{}", 167 | function_name, 168 | phase ? "AFTER" : "BEFORE", 169 | target->GetName(), 170 | target->GetEntry() ? " (" + std::to_string(target->GetEntry()) + ")" : "", 171 | spell_name, 172 | spell_id ? " (" + std::to_string(spell_id) + ")" : "" 173 | ); 174 | } 175 | else 176 | { 177 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_UnitScript::{}: {}: W? T? F? with {}{}", 178 | function_name, 179 | phase ? "AFTER" : "BEFORE", 180 | spell_name, 181 | spell_id ? " (" + std::to_string(spell_id) + ")" : "" 182 | ); 183 | } 184 | } 185 | 186 | int32 AutoBalance_UnitScript::_Modify_Damage_Healing(Unit* target, Unit* source, int32 amount, SpellInfo const* spellInfo) 187 | { 188 | // 189 | // Pre-flight Checks 190 | // 191 | 192 | // only debug if the source or target is a player 193 | bool _debug_damage_and_healing = ((source && (source->GetTypeId() == TYPEID_PLAYER || source->IsControlledByPlayer())) || (target && target->GetTypeId() == TYPEID_PLAYER)); 194 | _debug_damage_and_healing = (source && source->GetMap()->GetInstanceId()); 195 | 196 | // check that we're enabled globally, else return the original value 197 | if (!EnableGlobal) 198 | { 199 | if (_debug_damage_and_healing) 200 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_UnitScript::_Modify_Damage_Healing: EnableGlobal is false, returning original value of ({}).", amount); 201 | 202 | return amount; 203 | } 204 | 205 | // if the source is gone (logged off? despawned?), use the same target and source. 206 | // hacky, but better than crashing or having the damage go to 1.0x 207 | if (!source) 208 | { 209 | if (_debug_damage_and_healing) 210 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_UnitScript::_Modify_Damage_Healing: Source is null, using target as source."); 211 | 212 | source = target; 213 | } 214 | 215 | // make sure the source and target are in an instance, else return the original damage 216 | if (!(source->GetMap()->IsDungeon() && target->GetMap()->IsDungeon())) 217 | { 218 | //if (_debug_damage_and_healing) 219 | // LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_UnitScript::_Modify_Damage_Healing: Not in an instance, returning original value of ({}).", amount); 220 | 221 | return amount; 222 | } 223 | 224 | // make sure that the source is in the world, else return the original value 225 | if (!source->IsInWorld()) 226 | { 227 | if (_debug_damage_and_healing) 228 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_UnitScript::_Modify_Damage_Healing: Source does not exist in the world, returning original value of ({}).", amount); 229 | 230 | return amount; 231 | } 232 | 233 | // if the spell ID is in our "never modify" list, return the original value 234 | if 235 | ( 236 | spellInfo && 237 | spellInfo->Id && 238 | std::find 239 | ( 240 | spellIdsToNeverModify.begin(), 241 | spellIdsToNeverModify.end(), 242 | spellInfo->Id 243 | ) != spellIdsToNeverModify.end() 244 | ) 245 | { 246 | if (_debug_damage_and_healing) 247 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_UnitScript::_Modify_Damage_Healing: Spell {}({}) is in the never modify list, returning original value of ({}).", 248 | spellInfo->SpellName[0], 249 | spellInfo->Id, 250 | amount 251 | ); 252 | 253 | return amount; 254 | } 255 | 256 | // get the maps' info 257 | AutoBalanceMapInfo* sourceMapABInfo = GetMapInfo(source->GetMap()); 258 | AutoBalanceMapInfo* targetMapABInfo = GetMapInfo(target->GetMap()); 259 | 260 | // if either the target or the source's maps are not enabled, return the original damage 261 | if (!sourceMapABInfo->enabled || !targetMapABInfo->enabled) 262 | { 263 | if (_debug_damage_and_healing) 264 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_UnitScript::_Modify_Damage_Healing: Source or Target's map is not enabled, returning original value of ({}).", amount); 265 | 266 | return amount; 267 | } 268 | 269 | // 270 | // Source and Target Checking 271 | // 272 | 273 | // if the source is a player and they are healing themselves, return the original value 274 | if (source->GetTypeId() == TYPEID_PLAYER && source->GetGUID() == target->GetGUID() && amount >= 0) 275 | { 276 | if (_debug_damage_and_healing) 277 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_UnitScript::_Modify_Damage_Healing: Source is a player that is self-healing, returning original value of ({}).", amount); 278 | 279 | return amount; 280 | } 281 | // if the source is a player and they are damaging themselves, log to debug but continue 282 | else if (source->GetTypeId() == TYPEID_PLAYER && source->GetGUID() == target->GetGUID() && amount < 0) 283 | { 284 | // if the spell used is in our list of spells to ignore, return the original value 285 | if 286 | ( 287 | spellInfo && 288 | spellInfo->Id && 289 | std::find 290 | ( 291 | spellIdsThatSpendPlayerHealth.begin(), 292 | spellIdsThatSpendPlayerHealth.end(), 293 | spellInfo->Id 294 | ) != spellIdsThatSpendPlayerHealth.end() 295 | ) 296 | { 297 | if (_debug_damage_and_healing) 298 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_UnitScript::_Modify_Damage_Healing: Source is a player that is self-damaging with a spell that is ignored, returning original value of ({}).", amount); 299 | 300 | return amount; 301 | } 302 | 303 | if (_debug_damage_and_healing) 304 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_UnitScript::_Modify_Damage_Healing: Source is a player that is self-damaging, continuing."); 305 | } 306 | // if the source is a player and they are damaging unit that is friendly, log to debug but continue 307 | else if (source->GetTypeId() == TYPEID_PLAYER && target->IsFriendlyTo(source) && amount < 0) 308 | { 309 | if (_debug_damage_and_healing) 310 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_UnitScript::_Modify_Damage_Healing: Source is a player that is damaging a friendly unit, continuing."); 311 | } 312 | // if the source is a player under any other condition, return the original value 313 | else if (source->GetTypeId() == TYPEID_PLAYER) 314 | { 315 | if (_debug_damage_and_healing) 316 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_UnitScript::_Modify_Damage_Healing: Source is an enemy player, returning original value of ({}).", amount); 317 | 318 | return amount; 319 | } 320 | // if the creature is attacking itself with an aura with effect type SPELL_AURA_SHARE_DAMAGE_PCT, return the orginal damage 321 | else if 322 | ( 323 | source->GetTypeId() == TYPEID_UNIT && 324 | source->GetTypeId() != TYPEID_PLAYER && 325 | source->GetGUID() == target->GetGUID() && 326 | _isAuraWithEffectType(spellInfo, SPELL_AURA_SHARE_DAMAGE_PCT) 327 | ) 328 | { 329 | if (_debug_damage_and_healing) 330 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_UnitScript::_Modify_Damage_Healing: Source is a creature that is self-damaging with an aura that shares damage, returning original value of ({}).", amount); 331 | 332 | return amount; 333 | } 334 | 335 | // if the source is under the control of the player, return the original damage 336 | // noteably, this should NOT include mind control targets 337 | if ((source->IsHunterPet() || source->IsPet() || source->IsSummon()) && source->IsControlledByPlayer()) 338 | { 339 | if (_debug_damage_and_healing) 340 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_UnitScript::_Modify_Damage_Healing: Source is a player-controlled pet or summon, returning original value of ({}).", amount); 341 | 342 | return amount; 343 | } 344 | 345 | // 346 | // Multiplier calculation 347 | // 348 | float damageMultiplier = 1.0f; 349 | 350 | // if the source is a player AND the target is that same player AND the value is damage (negative), use the map's multiplier 351 | if (source->GetTypeId() == TYPEID_PLAYER && source->GetGUID() == target->GetGUID() && amount < 0) 352 | { 353 | // if this aura damages based on a percent of the player's max health, use the un-level-scaled multiplier 354 | if (_isAuraWithEffectType(spellInfo, SPELL_AURA_PERIODIC_DAMAGE_PERCENT)) 355 | { 356 | damageMultiplier = sourceMapABInfo->worldDamageHealingMultiplier; 357 | if (_debug_damage_and_healing) 358 | { 359 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_UnitScript::_Modify_Damage_Healing: Spell damage based on percent of max health. Ignore level scaling."); 360 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", 361 | "AutoBalance_UnitScript::_Modify_Damage_Healing: Source is a player and the target is that same player, using the map's (level-scaling ignored) multiplier: ({})", 362 | damageMultiplier 363 | ); 364 | } 365 | } 366 | else 367 | { 368 | damageMultiplier = sourceMapABInfo->scaledWorldDamageHealingMultiplier; 369 | if (_debug_damage_and_healing) 370 | { 371 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", 372 | "AutoBalance_UnitScript::_Modify_Damage_Healing: Source is a player and the target is that same player, using the map's multiplier: ({})", 373 | damageMultiplier 374 | ); 375 | } 376 | } 377 | } 378 | // if the target is a player AND the value is healing (positive), use the map's damage multiplier 379 | // (player to player healing was already eliminated in the Source and Target Checking section) 380 | else if (target->GetTypeId() == TYPEID_PLAYER && amount >= 0) 381 | { 382 | damageMultiplier = targetMapABInfo->scaledWorldDamageHealingMultiplier; 383 | if (_debug_damage_and_healing) 384 | { 385 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", 386 | "AutoBalance_UnitScript::_Modify_Damage_Healing: A non-player is healing a player, using the map's multiplier: ({})", 387 | damageMultiplier 388 | ); 389 | } 390 | } 391 | // if the target is a player AND the source is not a creature, use the map's multiplier 392 | else if (target->GetTypeId() == TYPEID_PLAYER && source->GetTypeId() != TYPEID_UNIT && amount < 0) 393 | { 394 | // if this aura damages based on a percent of the player's max health, use the un-level-scaled multiplier 395 | if (_isAuraWithEffectType(spellInfo, SPELL_AURA_PERIODIC_DAMAGE_PERCENT)) 396 | { 397 | damageMultiplier = targetMapABInfo->worldDamageHealingMultiplier; 398 | if (_debug_damage_and_healing) 399 | { 400 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_UnitScript::_Modify_Damage_Healing: Spell damage based on percent of max health. Ignore level scaling."); 401 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", 402 | "AutoBalance_UnitScript::_Modify_Damage_Healing: Target is a player and the source is not a creature, using the map's (level-scaling-ignored) multiplier: ({})", 403 | damageMultiplier 404 | ); 405 | } 406 | } 407 | else 408 | { 409 | damageMultiplier = targetMapABInfo->scaledWorldDamageHealingMultiplier; 410 | if (_debug_damage_and_healing) 411 | { 412 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", 413 | "AutoBalance_UnitScript::_Modify_Damage_Healing: Target is a player and the source is not a creature, using the map's multiplier: ({})", 414 | damageMultiplier 415 | ); 416 | } 417 | } 418 | } 419 | // otherwise, use the source creature's damage multiplier 420 | else 421 | { 422 | // if this aura damages based on a percent of the player's max health, use the un-level-scaled multiplier 423 | if (_isAuraWithEffectType(spellInfo, SPELL_AURA_PERIODIC_DAMAGE_PERCENT)) 424 | { 425 | damageMultiplier = source->CustomData.GetDefault("AutoBalanceCreatureInfo")->DamageMultiplier; 426 | if (_debug_damage_and_healing) 427 | { 428 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_UnitScript::_Modify_Damage_Healing: Spell damage based on percent of max health. Ignore level scaling."); 429 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", 430 | "AutoBalance_UnitScript::_Modify_Damage_Healing: Using the source creature's (level-scaling ignored) damage multiplier: ({})", 431 | damageMultiplier 432 | ); 433 | } 434 | } 435 | // non percent-based, used the normal multiplier 436 | else 437 | { 438 | damageMultiplier = source->CustomData.GetDefault("AutoBalanceCreatureInfo")->ScaledDamageMultiplier; 439 | if (_debug_damage_and_healing) 440 | { 441 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", 442 | "AutoBalance_UnitScript::_Modify_Damage_Healing: Using the source creature's damage multiplier: ({})", 443 | damageMultiplier 444 | ); 445 | } 446 | } 447 | } 448 | 449 | // we are good to go, return the original damage times the multiplier 450 | if (_debug_damage_and_healing) 451 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_UnitScript::_Modify_Damage_Healing: Returning modified {}: ({}) * ({}) = ({})", 452 | amount <= 0 ? "damage" : "healing", 453 | amount, 454 | damageMultiplier, 455 | amount * damageMultiplier 456 | ); 457 | 458 | return amount * damageMultiplier; 459 | } 460 | 461 | uint32 AutoBalance_UnitScript::_Modifier_CCDuration(Unit* target, Unit* caster, Aura* aura) 462 | { 463 | // store the original duration of the aura 464 | float originalDuration = (float)aura->GetDuration(); 465 | 466 | // check that we're enabled globally, else return the original duration 467 | if (!EnableGlobal) 468 | return originalDuration; 469 | 470 | // ensure that both the target and the caster are defined 471 | if (!target || !caster) 472 | return originalDuration; 473 | 474 | // if the aura wasn't cast just now, don't change it 475 | if (aura->GetDuration() != aura->GetMaxDuration()) 476 | return originalDuration; 477 | 478 | // if the target isn't a player or the caster is a player, return the original duration 479 | if (!target->IsPlayer() || caster->IsPlayer()) 480 | return originalDuration; 481 | 482 | // make sure we're in an instance, else return the original duration 483 | if (!(target->GetMap()->IsDungeon() && caster->GetMap()->IsDungeon())) 484 | return originalDuration; 485 | 486 | // get the current creature's CC duration multiplier 487 | float ccDurationMultiplier = caster->CustomData.GetDefault("AutoBalanceCreatureInfo")->CCDurationMultiplier; 488 | 489 | // if it's the default of 1.0, return the original damage 490 | if (ccDurationMultiplier == 1) 491 | return originalDuration; 492 | 493 | // if the aura was cast by a pet or summon, return the original duration 494 | if ((caster->IsHunterPet() || caster->IsPet() || caster->IsSummon()) && caster->IsControlledByPlayer()) 495 | return originalDuration; 496 | 497 | // only if this aura is a CC 498 | if ( 499 | aura->HasEffectType(SPELL_AURA_MOD_CHARM) || 500 | aura->HasEffectType(SPELL_AURA_MOD_CONFUSE) || 501 | aura->HasEffectType(SPELL_AURA_MOD_DISARM) || 502 | aura->HasEffectType(SPELL_AURA_MOD_FEAR) || 503 | aura->HasEffectType(SPELL_AURA_MOD_PACIFY) || 504 | aura->HasEffectType(SPELL_AURA_MOD_POSSESS) || 505 | aura->HasEffectType(SPELL_AURA_MOD_SILENCE) || 506 | aura->HasEffectType(SPELL_AURA_MOD_STUN) || 507 | aura->HasEffectType(SPELL_AURA_MOD_SPEED_SLOW_ALL) 508 | ) 509 | { 510 | return originalDuration * ccDurationMultiplier; 511 | } 512 | else 513 | return originalDuration; 514 | } 515 | 516 | bool AutoBalance_UnitScript::_isAuraWithEffectType(SpellInfo const* spellInfo, AuraType auraType, bool log) 517 | { 518 | // if the spell is not defined, return false 519 | if (!spellInfo) 520 | { 521 | if (log) 522 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_UnitScript::_isAuraWithEffectType: SpellInfo is null, returning false."); 523 | return false; 524 | } 525 | 526 | // if the spell doesn't have any effects, return false 527 | if (!spellInfo->GetEffects().size()) 528 | { 529 | if (log) 530 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_UnitScript::_isAuraWithEffectType: SpellInfo has no effects, returning false."); 531 | return false; 532 | } 533 | 534 | // iterate through the spell effects 535 | for (SpellEffectInfo effect : spellInfo->GetEffects()) 536 | { 537 | // if the effect is not an aura, continue to next effect 538 | if (!effect.IsAura()) 539 | { 540 | if (log) 541 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_UnitScript::_isAuraWithEffectType: SpellInfo has an effect that is not an aura, continuing to next effect."); 542 | continue; 543 | } 544 | 545 | if (effect.ApplyAuraName == auraType) 546 | { 547 | // if the effect is an aura of the target type, return true 548 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_UnitScript::_isAuraWithEffectType: SpellInfo has an aura of the target type, returning true."); 549 | return true; 550 | } 551 | } 552 | 553 | // if no aura effect of type auraType was found, return false 554 | if (log) 555 | LOG_DEBUG("module.AutoBalance_DamageHealingCC", "AutoBalance_UnitScript::_isAuraWithEffectType: SpellInfo has no aura of the target type, returning false."); 556 | return false; 557 | } 558 | -------------------------------------------------------------------------------- /src/ABUnitScript.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016+ AzerothCore , license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE 3 | */ 4 | 5 | #ifndef __AB_UNIT_SCRIPT_H 6 | #define __AB_UNIT_SCRIPT_H 7 | 8 | #include "AutoBalance.h" 9 | 10 | #include "ScriptMgr.h" 11 | #include "SpellAuras.h" 12 | #include "SpellInfo.h" 13 | 14 | class AutoBalance_UnitScript : public UnitScript 15 | { 16 | public: 17 | AutoBalance_UnitScript() 18 | : UnitScript("AutoBalance_UnitScript", true, { 19 | UNITHOOK_MODIFY_PERIODIC_DAMAGE_AURAS_TICK, 20 | UNITHOOK_MODIFY_SPELL_DAMAGE_TAKEN, 21 | UNITHOOK_MODIFY_MELEE_DAMAGE, 22 | UNITHOOK_MODIFY_HEAL_RECEIVED, 23 | UNITHOOK_ON_AURA_APPLY 24 | }) 25 | { 26 | } 27 | 28 | void ModifyPeriodicDamageAurasTick(Unit* target, Unit* source, uint32& amount, SpellInfo const* spellInfo) override; 29 | void ModifySpellDamageTaken(Unit* target, Unit* source, int32& amount, SpellInfo const* spellInfo) override; 30 | void ModifyMeleeDamage(Unit* target, Unit* source, uint32& amount) override; 31 | void ModifyHealReceived(Unit* target, Unit* source, uint32& amount, SpellInfo const* spellInfo) override; 32 | void OnAuraApply(Unit* unit, Aura* aura) override; 33 | 34 | private: 35 | [[maybe_unused]] bool _debug_damage_and_healing = false; // defaults to false, overwritten in each function 36 | 37 | void _Debug_Output(std::string function_name, Unit* target, Unit* source, int32 amount, Damage_Healing_Debug_Phase phase, std::string spell_name = "Unknown Spell", uint32 spell_id = 0); 38 | int32 _Modify_Damage_Healing(Unit* target, Unit* source, int32 amount, SpellInfo const* spellInfo = nullptr); 39 | uint32 _Modifier_CCDuration(Unit* target, Unit* caster, Aura* aura); 40 | bool _isAuraWithEffectType(SpellInfo const* spellInfo, AuraType auraType, bool log = false); 41 | }; 42 | 43 | 44 | #endif /* __AB_UNIT_SCRIPT_H */ 45 | -------------------------------------------------------------------------------- /src/ABUtils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016+ AzerothCore , license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE 3 | */ 4 | 5 | #ifndef __AB_UTILS_H 6 | #define __AB_UTILS_H 7 | 8 | #include "ABInflectionPointSettings.h" 9 | #include "ABLevelScalingDynamicLevelSettings.h" 10 | #include "ABMapInfo.h" 11 | #include "ABStatModifiers.h" 12 | #include "AutoBalance.h" 13 | 14 | #include "Creature.h" 15 | #include "Map.h" 16 | #include "SharedDefines.h" 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | void AddCreatureToMapCreatureList(Creature* creature, bool addToCreatureList = true, bool forceRecalculation = false); 23 | void RemoveCreatureFromMapData(Creature* creature); 24 | 25 | uint64_t GetCurrentConfigTime(); 26 | uint32 getBaseExpansionValueForLevel(const uint32 baseValues[3], uint8 targetLevel); 27 | float getBaseExpansionValueForLevel(const float baseValues[3], uint8 targetLevel); 28 | float getDefaultMultiplier(Map* map, AutoBalanceInflectionPointSettings inflectionPointSettings); 29 | int GetForcedNumPlayers(int creatureId); 30 | World_Multipliers getWorldMultiplier(Map* map, BaseValueType baseValueType); 31 | AutoBalanceInflectionPointSettings getInflectionPointSettings(InstanceMap* instanceMap, bool isBoss = false); 32 | void getStatModifiersDebug(Map* map, Creature* creature, std::string message); 33 | AutoBalanceStatModifiers getStatModifiers(Map* map, Creature* creature = nullptr); 34 | 35 | bool hasBossOverride(uint32 dungeonId); 36 | bool hasDungeonOverride(uint32 dungeonId); 37 | bool hasDynamicLevelOverride(uint32 dungeonId); 38 | bool hasLevelScalingDistanceCheckOverride(uint32 dungeonId); 39 | bool hasStatModifierBossOverride(uint32 dungeonId); 40 | bool hasStatModifierCreatureOverride(uint32 creatureId); 41 | bool hasStatModifierOverride(uint32 dungeonId); 42 | 43 | bool isBossOrBossSummon(Creature* creature, bool log = false); 44 | bool isCreatureRelevant(Creature* creature); 45 | bool isDungeonInDisabledDungeonIds(uint32 dungeonId); 46 | bool isDungeonInMinPlayerMap(uint32 dungeonId, bool isHeroic); 47 | 48 | void LoadForcedCreatureIdsFromString(std::string creatureIds, int forcedPlayerCount); 49 | std::list LoadDisabledDungeons(std::string dungeonIdString); 50 | std::map LoadDistanceCheckOverrides(std::string dungeonIdString); 51 | std::map LoadDynamicLevelOverrides(std::string dungeonIdString); 52 | std::map LoadInflectionPointOverrides(std::string dungeonIdString); 53 | void LoadMapSettings(Map* map); 54 | std::map LoadMinPlayersPerDungeonId(std::string minPlayersString); 55 | std::map LoadStatModifierOverrides(std::string dungeonIdString); 56 | 57 | bool ShouldMapBeEnabled (Map* map); 58 | void UpdateMapPlayerStats (Map* map); 59 | void AddPlayerToMap(Map* map, Player* player); 60 | bool RemovePlayerFromMap(Map* map, Player* player); 61 | bool UpdateMapDataIfNeeded(Map* map, bool force = false); 62 | AutoBalanceMapInfo* GetMapInfo(Map* map); 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /src/ABWorldScript.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016+ AzerothCore , license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE 3 | */ 4 | 5 | #ifndef __AB_WORLD_SCRIPT_H 6 | #define __AB_WORLD_SCRIPT_H 7 | 8 | #include "WorldScript.h" 9 | 10 | class AutoBalance_WorldScript : public WorldScript 11 | { 12 | public: 13 | AutoBalance_WorldScript() 14 | : WorldScript("AutoBalance_WorldScript", { 15 | WORLDHOOK_ON_BEFORE_CONFIG_LOAD 16 | }) 17 | { 18 | } 19 | 20 | void OnBeforeConfigLoad(bool /*reload*/) override; 21 | 22 | void SetInitialWorldSettings(); 23 | }; 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /src/AB_loader.cpp: -------------------------------------------------------------------------------- 1 | void AddAutoBalanceScripts(); 2 | 3 | void Addmod_autobalanceScripts() 4 | { 5 | AddAutoBalanceScripts(); 6 | } 7 | -------------------------------------------------------------------------------- /src/AutoBalance.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 AzerothCore 3 | * Copyright (C) 2012 CVMagic 4 | * Copyright (C) 2008-2010 TrinityCore 5 | * Copyright (C) 2006-2009 ScriptDev2 6 | * Copyright (C) 1985-2010 KalCorp 7 | * 8 | * This program is free software; you can redistribute it and/or modify it 9 | * under the terms of the GNU General Public License as published by the 10 | * Free Software Foundation; either version 2 of the License, or (at your 11 | * option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 16 | * more details. 17 | * 18 | * You should have received a copy of the GNU General Public License along 19 | * with this program. If not, see . 20 | */ 21 | 22 | /* 23 | * Script Name: AutoBalance 24 | * Original Authors: KalCorp and Vaughner 25 | * Maintainer(s): AzerothCore 26 | * Original Script Name: AutoBalance 27 | * Description: This script is intended to scale based on number of players, 28 | * instance mobs & world bosses' level, health, mana, and damage. 29 | */ 30 | 31 | #include "AutoBalance.h" 32 | 33 | #include "ABAllCreatureScript.h" 34 | #include "ABAllMapScript.h" 35 | #include "ABCommandScript.h" 36 | #include "ABConfig.h" 37 | #include "ABCreatureInfo.h" 38 | #include "ABGameObjectScript.h" 39 | #include "ABGlobalScript.h" 40 | #include "ABInflectionPointSettings.h" 41 | #include "ABLevelScalingDynamicLevelSettings.h" 42 | #include "ABMapInfo.h" 43 | #include "ABModuleScript.h" 44 | #include "ABPlayerScript.h" 45 | #include "ABScriptMgr.h" 46 | #include "ABStatModifiers.h" 47 | #include "ABUnitScript.h" 48 | #include "ABUtils.h" 49 | #include "ABWorldScript.h" 50 | 51 | #include "Configuration/Config.h" 52 | #include "Chat.h" 53 | #include "Creature.h" 54 | #include "Group.h" 55 | #include "Language.h" 56 | #include "Log.h" 57 | #include "Map.h" 58 | #include "MapMgr.h" 59 | #include "Message.h" 60 | #include "ObjectMgr.h" 61 | #include "Player.h" 62 | #include "ScriptMgr.h" 63 | #include "ScriptMgrMacros.h" 64 | #include "SharedDefines.h" 65 | #include "Unit.h" 66 | #include "World.h" 67 | 68 | #include 69 | #include 70 | 71 | #if AC_COMPILER == AC_COMPILER_GNU 72 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 73 | #endif 74 | 75 | void AddAutoBalanceScripts() 76 | { 77 | new AutoBalance_WorldScript(); 78 | new AutoBalance_PlayerScript(); 79 | new AutoBalance_UnitScript(); 80 | new AutoBalance_GameObjectScript(); 81 | new AutoBalance_AllCreatureScript(); 82 | new AutoBalance_AllMapScript(); 83 | new AutoBalance_CommandScript(); 84 | new AutoBalance_GlobalScript(); 85 | } 86 | -------------------------------------------------------------------------------- /src/AutoBalance.h: -------------------------------------------------------------------------------- 1 | #ifndef MOD_AUTOBALANCE_H 2 | #define MOD_AUTOBALANCE_H 3 | 4 | enum ScalingMethod 5 | { 6 | AUTOBALANCE_SCALING_FIXED, 7 | AUTOBALANCE_SCALING_DYNAMIC 8 | }; 9 | 10 | enum BaseValueType 11 | { 12 | AUTOBALANCE_HEALTH, 13 | AUTOBALANCE_DAMAGE_HEALING 14 | }; 15 | 16 | enum Relevance 17 | { 18 | AUTOBALANCE_RELEVANCE_FALSE, 19 | AUTOBALANCE_RELEVANCE_TRUE, 20 | AUTOBALANCE_RELEVANCE_UNCHECKED 21 | }; 22 | 23 | enum Damage_Healing_Debug_Phase 24 | { 25 | AUTOBALANCE_DAMAGE_HEALING_DEBUG_PHASE_BEFORE, 26 | AUTOBALANCE_DAMAGE_HEALING_DEBUG_PHASE_AFTER 27 | }; 28 | 29 | struct World_Multipliers 30 | { 31 | float scaled = 1.0f; 32 | float unscaled = 1.0f; 33 | }; 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /src/Message.cpp: -------------------------------------------------------------------------------- 1 | #include "Message.h" 2 | 3 | #include "DatabaseEnv.h" 4 | #include "ItemTemplate.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | const std::unordered_map AB_WELCOME_TO_PLAYER = { 13 | {LOCALE_enUS, "|cffc3dbff [AutoBalance]|r|cffFF8000 Welcome to {} ({}-player {}). There are {} player(s) in this instance. Difficulty set to {} player(s).|r"}, 14 | {LOCALE_koKR, "|cffc3dbff [AutoBalance]|r|cffFF8000 {} ({}-player {})에 오신 것을 환영합니다. 이 인스턴스에는 {}명의 플레이어가 있습니다. 난이도가 {}명으로 설정되었습니다.|r"}, 15 | {LOCALE_frFR, "|cffc3dbff [AutoBalance]|r|cffFF8000 Bienvenue dans {} ({} {}). Il y a {} joueur(s) dans cette instance. La difficulté est réglée sur {} joueur(s).|r"}, 16 | {LOCALE_deDE, "|cffc3dbff [AutoBalance]|r|cffFF8000 Willkommen in {} ({} Spieler {}). Es gibt {} Spieler in dieser Instanz. Schwierigkeit auf {} Spieler eingestellt.|r"}, 17 | {LOCALE_zhCN, "|cffc3dbff [AutoBalance]|r|cffFF8000 欢迎来到 {}({}人 {})。此副本中有 {} 名玩家。难度设置为 {} 名玩家。|r"}, 18 | {LOCALE_zhTW, "|cffc3dbff [AutoBalance]|r|cffFF8000 歡迎來到 {}({}人 {})。此副本中有 {} 名玩家。難度設定為 {} 名玩家。|r"}, 19 | {LOCALE_esES, "|cffc3dbff [AutoBalance]|r|cffFF8000 Bienvenido a {} ({} jugador {}). Hay {} jugador(es) en esta instancia. La dificultad se establece en {} jugador(es).|r"}, 20 | {LOCALE_esMX, "|cffc3dbff [AutoBalance]|r|cffFF8000 Bienvenido a {} ({} jugador {}). Hay {} jugador(es) en esta instancia. La dificultad se establece en {} jugador(es).|r"}, 21 | {LOCALE_ruRU, "|cffc3dbff [AutoBalance]|r|cffFF8000 Добро пожаловать в {} ({} игрок {}). В этом экземпляре {} игроков. Сложность установлена на {} игроков.|r"}, 22 | }; 23 | 24 | const std::unordered_map AB_WELCOME_TO_GM = { 25 | {LOCALE_enUS, "|cffc3dbff [AutoBalance]|r|cffFF8000 Your GM flag is turned on. AutoBalance will ignore you. Please turn GM off and exit/re-enter the instance if you'd like to be considering for AutoBalancing.|r"}, 26 | {LOCALE_koKR, "|cffc3dbff [AutoBalance]|r|cffFF8000 GM 플래그가 켜져 있습니다. AutoBalance는 당신을 무시합니다. AutoBalancing을 고려하려면 GM을 끄고 인스턴스를 나가고 다시 들어가십시오.|r"}, 27 | {LOCALE_frFR, "|cffc3dbff [AutoBalance]|r|cffFF8000 Votre drapeau GM est activé. AutoBalance vous ignorera. Veuillez désactiver GM et sortir/revenir dans l'instance si vous souhaitez être pris en compte pour l'AutoBalancing.|r"}, 28 | {LOCALE_deDE, "|cffc3dbff [AutoBalance]|r|cffFF8000 Ihre GM-Flagge ist eingeschaltet. AutoBalance wird Sie ignorieren. Bitte schalten Sie GM aus und verlassen Sie das Instanz, wenn Sie für das AutoBalancing berücksichtigt werden möchten.|r"}, 29 | {LOCALE_zhCN, "|cffc3dbff [AutoBalance]|r|cffFF8000 您的GM模式已打开。AutoBalance将忽略。如果您希望考虑自动平衡,请关闭GM并退出/重新进入副本。|r"}, 30 | {LOCALE_zhTW, "|cffc3dbff [AutoBalance]|r|cffFF8000 您的GM模式已打開。AutoBalance將忽略。如果您希望考慮自動平衡,請關閉GM並退出/重新進入副本。|r"}, 31 | {LOCALE_esES, "|cffc3dbff [AutoBalance]|r|cffFF8000 Su bandera de GM está encendida. AutoBalance te ignorará. Por favor, apague GM y salga/vuelva a entrar en la instancia si desea ser considerado para el AutoBalance.|r"}, 32 | {LOCALE_esMX, "|cffc3dbff [AutoBalance]|r|cffFF8000 Su bandera de GM está encendida. AutoBalance te ignorará. Por favor, apague GM y salga/vuelva a entrar en la instancia si desea ser considerado para el AutoBalance.|r"}, 33 | {LOCALE_ruRU, "|cffc3dbff [AutoBalance]|r|cffFF8000 Ваш флаг GM включен. AutoBalance будет игнорировать вас. Пожалуйста, отключите GM и выйдите/войдите в экземпляр, если хотите, чтобы вас учитывали при автобалансировке.|r"}, 34 | }; 35 | 36 | const std::unordered_map AB_ANNOUNCE_NON_GM_ENTERING_INSTANCE = { 37 | {LOCALE_enUS, "|cffc3dbff [AutoBalance]|r|cffFF8000 {} enters the instance. There are {} player(s) in this instance. Difficulty set to {} player(s).|r"}, 38 | {LOCALE_koKR, "|cffc3dbff [AutoBalance]|r|cffFF8000 {}이(가) 인스턴스에 들어왔습니다. 이 인스턴스에는 {}명의 플레이어가 있습니다. 난이도가 {}명으로 설정되었습니다.|r"}, 39 | {LOCALE_frFR, "|cffc3dbff [AutoBalance]|r|cffFF8000 {} entre dans l'instance. Il y a {} joueur(s) dans cette instance. La difficulté est réglée sur {} joueur(s).|r"}, 40 | {LOCALE_deDE, "|cffc3dbff [AutoBalance]|r|cffFF8000 {} betritt die Instanz. Es gibt {} Spieler in dieser Instanz. Schwierigkeit auf {} Spieler eingestellt.|r"}, 41 | {LOCALE_zhCN, "|cffc3dbff [AutoBalance]|r|cffFF8000 {}进入了副本。此副本中有 {} 名玩家。难度设置为 {} 名玩家。|r"}, 42 | {LOCALE_zhTW, "|cffc3dbff [AutoBalance]|r|cffFF8000 {}進入了副本。此副本中有 {} 名玩家。難度設定為 {} 名玩家。|r"}, 43 | {LOCALE_esES, "|cffc3dbff [AutoBalance]|r|cffFF8000 {} entra en la instancia. Hay {} jugador(es) en esta instancia. La dificultad se establece en {} jugador(es).|r"}, 44 | {LOCALE_esMX, "|cffc3dbff [AutoBalance]|r|cffFF8000 {} entra en la instancia. Hay {} jugador(es) en esta instancia. La dificultad se establece en {} jugador(es).|r"}, 45 | {LOCALE_ruRU, "|cffc3dbff [AutoBalance]|r|cffFF8000 {} входит в экземпляр. В этом экземпляре {} игроков. Сложность установлена на {} игроков.|r"}, 46 | }; 47 | 48 | const std::unordered_map AB_LEAVING_INSTANCE_COMBAT = { 49 | {LOCALE_enUS, "|cffc3dbff [AutoBalance]|r|cffFF8000 {} left the instance while combat was in progress. Difficulty locked to no less than {} players until combat ends.|r"}, 50 | {LOCALE_koKR, "|cffc3dbff [AutoBalance]|r|cffFF8000 {}이(가) 전투 중에 인스턴스를 떠났습니다. 전투가 끝날 때까지 난이도가 {}명 미만으로 잠겨 있습니다.|r"}, 51 | {LOCALE_frFR, "|cffc3dbff [AutoBalance]|r|cffFF8000 {} a quitté l'instance alors que le combat était en cours. La difficulté est verrouillée à pas moins de {} joueur(s) jusqu'à la fin du combat.|r"}, 52 | {LOCALE_deDE, "|cffc3dbff [AutoBalance]|r|cffFF8000 {} hat die Instanz verlassen, während der Kampf im Gange war. Die Schwierigkeit ist gesperrt, bis der Kampf endet, auf nicht weniger als {} Spieler.|r"}, 53 | {LOCALE_zhCN, "|cffc3dbff [AutoBalance]|r|cffFF8000 {}在战斗进行中离开了副本。直到战斗结束,难度锁定为不少于 {} 名玩家。|r"}, 54 | {LOCALE_zhTW, "|cffc3dbff [AutoBalance]|r|cffFF8000 {}在戰鬥進行中離開了副本。直到戰鬥結束,難度鎖定為不少於 {} 名玩家。|r"}, 55 | {LOCALE_esES, "|cffc3dbff [AutoBalance]|r|cffFF8000 {} salió de la instancia mientras el combate estaba en progreso. La dificultad está bloqueada a no menos de {} jugador(es) hasta que termine el combate.|r"} 56 | }; 57 | 58 | const std::unordered_map AB_LEAVING_INSTANCE = { 59 | {LOCALE_enUS, "|cffc3dbff [AutoBalance]|r|cffFF8000 {} left the instance. There are {} player(s) in this instance. Difficulty set to {} player(s).|r"}, 60 | {LOCALE_koKR, "|cffc3dbff [AutoBalance]|r|cffFF8000 {}이(가) 인스턴스를 떠났습니다. 이 인스턴스에는 {}명의 플레이어가 있습니다. 난이도가 {}명으로 설정되었습니다.|r"}, 61 | {LOCALE_frFR, "|cffc3dbff [AutoBalance]|r|cffFF8000 {} a quitté l'instance. Il y a {} joueur(s) dans cette instance. La difficulté est réglée sur {} joueur(s).|r"}, 62 | {LOCALE_deDE, "|cffc3dbff [AutoBalance]|r|cffFF8000 {} hat die Instanz verlassen. Es gibt {} Spieler in dieser Instanz. Schwierigkeit auf {} Spieler eingestellt.|r"}, 63 | {LOCALE_zhCN, "|cffc3dbff [AutoBalance]|r|cffFF8000 {}离开了副本。此副本中有 {} 名玩家。难度设置为 {} 名玩家。|r"}, 64 | {LOCALE_zhTW, "|cffc3dbff [AutoBalance]|r|cffFF8000 {}離開了副本。此副本中有 {} 名玩家。難度設定為 {} 名玩家。|r"}, 65 | {LOCALE_esES, "|cffc3dbff [AutoBalance]|r|cffFF8000 {} salió de la instancia. Hay {} jugador(es) en esta instancia. La dificultad se establece en {} jugador(es).|r"}, 66 | {LOCALE_esMX, "|cffc3dbff [AutoBalance]|r|cffFF8000 {} salió de la instancia. Hay {} jugador(es) en esta instancia. La dificultad se establece en {}"} 67 | }; 68 | 69 | const std::unordered_map AB_SET_OFFSET_COMMAND_DESCRIPTION = { 70 | {LOCALE_enUS, "Set the player difficulty offset for this instance. Usage: .ab offset .|r"}, 71 | {LOCALE_koKR, "이 인스턴스의 플레이어 난이도 오프셋을 설정합니다. 사용법: .ab offset <숫자>.|r"}, 72 | {LOCALE_frFR, "Définissez le décalage de difficulté du joueur pour cette instance. Utilisation : .ab offset .|r"}, 73 | {LOCALE_deDE, "Legen Sie den Spieler-Schwierigkeits-Offset für diese Instanz fest. Verwendung: .ab offset .|r"}, 74 | {LOCALE_zhCN, "设置此副本的玩家难度。用法:.ab offset <数字>。|r"}, 75 | {LOCALE_zhTW, "設定此副本的玩家難度。用法:.ab offset <數字>。|r"}, 76 | {LOCALE_esES, "Establece el desplazamiento de dificultad del jugador para esta instancia. Uso: .ab offset .|r"}, 77 | {LOCALE_esMX, "Establece el desplazamiento de dificultad del jugador para esta instancia. Uso: .ab offset .|r"}, 78 | {LOCALE_ruRU, "Устанавливает смещение сложности игрока для этого экземп"} 79 | }; 80 | 81 | const std::unordered_map AB_SET_OFFSET_COMMAND_SUCCESS = { 82 | {LOCALE_enUS, "Changing Player Difficulty Offset to {}.|r"}, 83 | {LOCALE_koKR, "플레이어 난이도 오프셋을 {}(으)로 변경합니다.|r"}, 84 | {LOCALE_frFR, "Modification du décalage de difficulté du joueur à {}.|r"}, 85 | {LOCALE_deDE, "Spieler-Schwierigkeits-Offset auf {} ändern.|r"}, 86 | {LOCALE_zhCN, "将玩家难度更改为 {}。|r"}, 87 | {LOCALE_zhTW, "將玩家難度更改為 {}。|r"}, 88 | {LOCALE_esES, "Cambiando el desplazamiento de dificultad del jugador a {}.|r"}, 89 | {LOCALE_esMX, "Cambiando el desplazamiento de dificultad del jugador a {}.|r"}, 90 | {LOCALE_ruRU, "Изменение смещения сложности игрока на {}.|r"}, 91 | }; 92 | 93 | const std::unordered_map AB_SET_OFFSET_COMMAND_ERROR = { 94 | {LOCALE_enUS, "Error changing Player Difficulty Offset! Please try again.|r"}, 95 | {LOCALE_koKR, "플레이어 난이도 오프셋 변경 중 오류가 발생했습니다! 다시 시도하십시오.|r"}, 96 | {LOCALE_frFR, "Erreur lors de la modification du décalage de difficulté du joueur ! Veuillez réessayer.|r"}, 97 | {LOCALE_deDE, "Fehler beim Ändern des Spieler-Schwierigkeits-Offsets! Bitte versuchen Sie es erneut.|r"}, 98 | {LOCALE_zhCN, "更改玩家难度时出错!请重试。|r"}, 99 | {LOCALE_zhTW, "更改玩家難度時出錯!請重試。|r"}, 100 | {LOCALE_esES, "¡Error al cambiar el desplazamiento de dificultad del jugador! Por favor, inténtelo de nuevo.|r"}, 101 | {LOCALE_esMX, "¡Error al cambiar el desplazamiento de dificultad del jugador! Por favor, inténtelo de nuevo.|r"}, 102 | {LOCALE_ruRU, "Ошибка при изменении смещения сложности игрока! Пожалуйста, попробуйте еще раз.|r"}, 103 | }; 104 | 105 | const std::unordered_map AB_GET_OFFSET_COMMAND_SUCCESS = { 106 | {LOCALE_enUS, "Current Player Difficulty Offset = {}.|r"}, 107 | {LOCALE_koKR, "현재 플레이어 난이도 오프셋 = {}.|r"}, 108 | {LOCALE_frFR, "Décalage de difficulté actuel du joueur = {}.|r"}, 109 | {LOCALE_deDE, "Aktueller Spieler-Schwierigkeits-Offset = {}.|r"}, 110 | {LOCALE_zhCN, "当前玩家难度偏移 = {}。|r"}, 111 | {LOCALE_zhTW, "當前玩家難度偏移 = {}。|r"}, 112 | {LOCALE_esES, "Desplazamiento de dificultad actual del jugador = {}.|r"}, 113 | {LOCALE_esMX, "Desplazamiento de dificultad actual del jugador = {}.|r"}, 114 | {LOCALE_ruRU, "Текущее смещение сложности игрока = {}.|r"}, 115 | }; 116 | 117 | const std::unordered_map AB_ADJUSTED_PLAYER_COUNT_COMBAT_LOCKED = { 118 | {LOCALE_enUS, "Adjusted Player Count: {} (Combat Locked)|r"}, 119 | {LOCALE_koKR, "조정된 플레이어 수: {} (전투 잠금됨)|r"}, 120 | {LOCALE_frFR, "Nombre de joueurs ajusté : {} (verrouillé en combat)|r"}, 121 | {LOCALE_deDE, "Angepasste Spieleranzahl: {} (Kampf gesperrt)|r"}, 122 | {LOCALE_zhCN, "调整后的玩家数量:{}(战斗锁定)|r"}, 123 | {LOCALE_zhTW, "調整後的玩家數量:{}(戰鬥鎖定)|r"}, 124 | {LOCALE_esES, "Cantidad de jugadores ajustada: {} (bloqueada en combate)|r"}, 125 | {LOCALE_esMX, "Cantidad de jugadores ajustada: {} (bloqueada en combate)|r"}, 126 | {LOCALE_ruRU, "Количество игроков, отрегулированное: {} (заблокировано в бою)|r"}, 127 | }; 128 | 129 | const std::unordered_map AB_ADJUSTED_PLAYER_COUNT_MAP_MINIMUM = { 130 | {LOCALE_enUS, "Adjusted Player Count: {} (Map Minimum)|r"}, 131 | {LOCALE_koKR, "조정된 플레이어 수: {} (지도 최소)|r"}, 132 | {LOCALE_frFR, "Nombre de joueurs ajusté : {} (minimum de la carte)|r"}, 133 | {LOCALE_deDE, "Angepasste Spieleranzahl: {} (Kartenminimum)|r"}, 134 | {LOCALE_zhCN, "调整后的玩家数量:{}(地图最小)|r"}, 135 | {LOCALE_zhTW, "調整後的玩家數量:{}(地圖最小)|r"}, 136 | {LOCALE_esES, "Cantidad de jugadores ajustada: {} (mínimo del mapa)|r"}, 137 | {LOCALE_esMX, "Cantidad de jugadores ajustada: {} (mínimo del mapa)|r"}, 138 | {LOCALE_ruRU, "Количество игроков, отрегулированное: {} (минимальное для карты)|r"}, 139 | }; 140 | 141 | const std::unordered_map AB_ADJUSTED_PLAYER_COUNT_MAP_MINIMUM_DIFFICULTY_OFFSET = { 142 | {LOCALE_enUS, "Adjusted Player Count: {} (Map Minimum + Difficulty Offset of {})|r"}, 143 | {LOCALE_koKR, "조정된 플레이어 수: {} (지도 최소 + {}의 난이도 오프셋)|r"}, 144 | {LOCALE_frFR, "Nombre de joueurs ajusté : {} (minimum de la carte + décalage de difficulté de {})|r"}, 145 | {LOCALE_deDE, "Angepasste Spieleranzahl: {} (Kartenminimum + Schwierigkeits-Offset von {})|r"}, 146 | {LOCALE_zhCN, "调整后的玩家数量:{}(地图最小 + {}的难度修正)|r"}, 147 | {LOCALE_zhTW, "調整後的玩家數量:{}(地圖最小 + {}的難度修正)|r"}, 148 | {LOCALE_esES, "Cantidad de jugadores ajustada: {} (mínimo del mapa + desplazamiento de dificultad de {})|r"}, 149 | {LOCALE_esMX, "Cantidad de jugadores ajustada: {} (mínimo del mapa + desplazamiento de dificultad de {})|r"}, 150 | {LOCALE_ruRU, "Количество игроков, отрегулированное: {} (минимальное для карты"} 151 | }; 152 | 153 | const std::unordered_map AB_ADJUSTED_PLAYER_COUNT_DIFFICULTY_OFFSET = { 154 | {LOCALE_enUS, "Adjusted Player Count: {} (Difficulty Offset of {})|r"}, 155 | {LOCALE_koKR, "조정된 플레이어 수: {} ({}의 난이도 오프셋)|r"}, 156 | {LOCALE_frFR, "Nombre de joueurs ajusté : {} (décalage de difficulté de {})|r"}, 157 | {LOCALE_deDE, "Angepasste Spieleranzahl: {} (Schwierigkeits-Offset von {})|r"}, 158 | {LOCALE_zhCN, "调整后的玩家数量:{}({}的难度修正)|r"}, 159 | {LOCALE_zhTW, "調整後的玩家數量:{}({}的難度修正)|r"}, 160 | {LOCALE_esES, "Cantidad de jugadores ajustada: {} (desplazamiento de dificultad de {})|r"}, 161 | {LOCALE_esMX, "Cantidad de jugadores ajustada: {} (desplazamiento de dificultad de {})|r"}, 162 | {LOCALE_ruRU, "Количество игроков, отрегулированное: {} (смещение сложности {})|r"}, 163 | }; 164 | 165 | const std::unordered_map AB_ADJUSTED_PLAYER_COUNT = { 166 | {LOCALE_enUS, "Adjusted Player Count: {}|r"}, 167 | {LOCALE_koKR, "조정된 플레이어 수: {}|r"}, 168 | {LOCALE_frFR, "Nombre de joueurs ajusté : {}|r"}, 169 | {LOCALE_deDE, "Angepasste Spieleranzahl: {}|r"}, 170 | {LOCALE_zhCN, "调整后的玩家数量:{}|r"}, 171 | {LOCALE_zhTW, "調整後的玩家數量:{}|r"}, 172 | {LOCALE_esES, "Cantidad de jugadores ajustada: {}|r"}, 173 | {LOCALE_esMX, "Cantidad de jugadores ajustada: {}|r"}, 174 | {LOCALE_ruRU, "Количество игроков, отрегулированное: {}|r"}, 175 | }; 176 | 177 | const std::unordered_map AB_LFG_RANGE = { 178 | {LOCALE_enUS, "LFG Range: Lvl {} - {} (Target: Lvl {})|r"}, 179 | {LOCALE_koKR, "LFG 범위: 레벨 {} - {} (대상: 레벨 {})|r"}, 180 | {LOCALE_frFR, "Plage LFG : Niveau {} - {} (Cible : Niveau {})|r"}, 181 | {LOCALE_deDE, "LFG-Bereich: Stufe {} - {} (Ziel: Stufe {})|r"}, 182 | {LOCALE_zhCN, "LFG范围:等级 {} - {}(目标:等级 {})|r"}, 183 | {LOCALE_zhTW, "LFG範圍:等級 {} - {}(目標:等級 {})|r"}, 184 | {LOCALE_esES, "Rango de LFG: Nivel {} - {} (Objetivo: Nivel {})|r"}, 185 | {LOCALE_esMX, "Rango de LFG: Nivel {} - {} (Objetivo: Nivel {})|r"}, 186 | {LOCALE_ruRU, "Диапазон поиска группы: Ур. {} - {} (Цель: Ур. {})|r"}, 187 | }; 188 | 189 | const std::unordered_map AB_MAP_LEVEL = { 190 | {LOCALE_enUS, "Map Level: {}{}|r"}, 191 | {LOCALE_koKR, "지도 레벨: {}{}|r"}, 192 | {LOCALE_frFR, "Niveau de la carte : {}{}|r"}, 193 | {LOCALE_deDE, "Kartenlevel: {}{}|r"}, 194 | {LOCALE_zhCN, "地图等级:{}{}|r"}, 195 | {LOCALE_zhTW, "地圖等級:{}{}|r"}, 196 | {LOCALE_esES, "Nivel del mapa: {}{}|r"}, 197 | {LOCALE_esMX, "Nivel del mapa: {}{}|r"}, 198 | {LOCALE_ruRU, "Уровень карты: {}{}|r"}, 199 | }; 200 | 201 | const std::unordered_map AB_LEVEL_SCALING_ENABLED = { 202 | {LOCALE_enUS, " (Level Scaling Enabled)|r"}, 203 | {LOCALE_koKR, " (레벨 스케일링 활성화됨)|r"}, 204 | {LOCALE_frFR, " (Mise à l'échelle des niveaux activée)|r"}, 205 | {LOCALE_deDE, " (Stufenanpassung aktiviert)|r"}, 206 | {LOCALE_zhCN, " (启用等级自动平衡)|r"}, 207 | {LOCALE_zhTW, " (啟用等級縮放平衡)|r"}, 208 | {LOCALE_esES, " (Escalado de nivel activado)|r"}, 209 | {LOCALE_esMX, " (Escalado de nivel activado)|r"}, 210 | {LOCALE_ruRU, " (Масштабирование уровней включено)|r"}, 211 | }; 212 | 213 | const std::unordered_map AB_LEVEL_SCALING_DISABLED = { 214 | {LOCALE_enUS, " (Level Scaling Disabled)|r"}, 215 | {LOCALE_koKR, " (레벨 스케일링 비활성화됨)|r"}, 216 | {LOCALE_frFR, " (Mise à l'échelle des niveaux désactivée)|r"}, 217 | {LOCALE_deDE, " (Stufenanpassung deaktiviert)|r"}, 218 | {LOCALE_zhCN, " (禁用等级自动平衡)|r"}, 219 | {LOCALE_zhTW, " (停用等級縮放平衡)|r"}, 220 | {LOCALE_esES, " (Escalado de nivel desactivado)|r"}, 221 | {LOCALE_esMX, " (Escalado de nivel desactivado)|r"}, 222 | {LOCALE_ruRU, " (Масштабирование уровней отключено)|r"}, 223 | }; 224 | 225 | const std::unordered_map AB_WORLD_HEALTH_MULTIPLIER = { 226 | {LOCALE_enUS, "World health multiplier: {:.3f}|r"}, 227 | {LOCALE_koKR, "월드 체력 배율: {:.3f}|r"}, 228 | {LOCALE_frFR, "Multiplicateur de santé mondiale : {:.3f}|r"}, 229 | {LOCALE_deDE, "Weltgesundheitsmultiplikator: {:.3f}|r"}, 230 | {LOCALE_zhCN, "全局生命值倍率:{:.3f}|r"}, 231 | {LOCALE_zhTW, "全局生命值倍增器:{:.3f}|r"}, 232 | {LOCALE_esES, "Multiplicador de salud mundial: {:.3f}|r"}, 233 | {LOCALE_esMX, "Multiplicador de salud mundial: {:.3f}|r"}, 234 | {LOCALE_ruRU, "Множитель здоровья мира: {:.3f}|r"}, 235 | }; 236 | 237 | const std::unordered_map AB_WORLD_HOSTILE_DAMAGE_HEALING_MULTIPLIER_TO = { 238 | {LOCALE_enUS, "World hostile damage and healing multiplier: {:.3f} -> {:.3f}|r"}, 239 | {LOCALE_koKR, "월드 적 대미지 및 치유 배율: {:.3f} -> {:.3f}|r"}, 240 | {LOCALE_frFR, "Multiplicateur de dégâts et de soins hostiles mondiaux : {:.3f} -> {:.3f}|r"}, 241 | {LOCALE_deDE, "Weltweiter feindlicher Schadens- und Heilungs-Multiplikator: {:.3f} -> {:.3f}|r"}, 242 | {LOCALE_zhCN, "全局伤害和治疗倍率:{:.3f} -> {:.3f}|r"}, 243 | {LOCALE_zhTW, "全局敵對傷害和治療倍增器:{:.3f} -> {:.3f}|r"}, 244 | {LOCALE_esES, "Multiplicador de daño y curación hostil mundial: {:.3f} -> {:.3f}|r"}, 245 | {LOCALE_esMX, "Multiplicador de daño y curación hostil mundial: {:.3f} -> {:.3f}|r"}, 246 | {LOCALE_ruRU, "Множитель урона и лечения мира: {:.3f} -> {:.3f}|r"}, 247 | }; 248 | 249 | const std::unordered_map AB_WORLD_HOSTILE_DAMAGE_HEALING_MULTIPLIER = { 250 | {LOCALE_enUS, "World hostile damage and healing multiplier: {:.3f}|r"}, 251 | {LOCALE_koKR, "월드 적 대미지 및 치유 배율: {:.3f}|r"}, 252 | {LOCALE_frFR, "Multiplicateur de dégâts et de soins hostiles mondiaux : {:.3f}|r"}, 253 | {LOCALE_deDE, "Weltweiter feindlicher Schadens- und Heilungs-Multiplikator: {:.3f}|r"}, 254 | {LOCALE_zhCN, "全局伤害和治疗倍率:{:.3f}|r"}, 255 | {LOCALE_zhTW, "全局敵對傷害和治療倍增器:{:.3f}|r"}, 256 | {LOCALE_esES, "Multiplicador de daño y curación hostil mundial: {:.3f}|r"}, 257 | {LOCALE_esMX, "Multiplicador de daño y curación hostil mundial: {:.3f}|r"}, 258 | {LOCALE_ruRU, "Множитель урона и лечения мира: {:.3f}|r"}, 259 | }; 260 | 261 | const std::unordered_map AB_ORIGINAL_CREATURE_LEVEL_RANGE = { 262 | {LOCALE_enUS, "Original Creature Level Range: {} - {} (Avg: {:.2f})|r"}, 263 | {LOCALE_koKR, "원래 크리쳐 레벨 범위: {} - {} (평균: {:.2f})|r"}, 264 | {LOCALE_frFR, "Plage de niveaux de créatures d'origine : {} - {} (Moyenne : {:.2f})|r"}, 265 | {LOCALE_deDE, "Originaler Kreaturenlevelbereich: {} - {} (Durchschnitt: {:.2f})|r"}, 266 | {LOCALE_zhCN, "原始生物等级范围:{} - {}(平均:{:2.f})|r"}, 267 | {LOCALE_zhTW, "原始生物等級範圍:{} - {}(平均:{:2.f})|r"}, 268 | {LOCALE_esES, "Rango de niveles de criaturas originales: {} - {} (Promedio: {:.2f})|r"}, 269 | {LOCALE_esMX, "Rango de niveles de criaturas originales: {} - {} (Promedio: {:.2f})|r"}, 270 | {LOCALE_ruRU, "Исходный диапазон уровней существ: {} - {} (Среднее: {:.2f})|r"}, 271 | }; 272 | 273 | const std::unordered_map AB_ACTIVE_TOTAL_CREATURES_IN_MAP = { 274 | {LOCALE_enUS, "Active | Total Creatures in map: {} | {}|r"}, 275 | {LOCALE_koKR, "활성 | 지도 내 총 크리쳐: {} | {}|r"}, 276 | {LOCALE_frFR, "Actif | Créatures totales dans la carte : {} | {}|r"}, 277 | {LOCALE_deDE, "Aktiv | Gesamte Kreaturen in der Karte: {} | {}|r"}, 278 | {LOCALE_zhCN, "Active | 地图中的总生物: {} | {}|r"}, 279 | {LOCALE_zhTW, "Active | 地圖中的總生物: {} | {}|r"}, 280 | {LOCALE_esES, "Activo | Criaturas totales en el mapa: {} | {}|r"}, 281 | {LOCALE_esMX, "Activo | Criaturas totales en el mapa: {} | {}|r"}, 282 | {LOCALE_ruRU, "Активные | Всего существ на карте: {} | {}|r"}, 283 | }; 284 | 285 | const std::unordered_map AB_COMMAND_ONLY_IN_INSTANCE = { 286 | {LOCALE_enUS, "This command can only be used in a dungeon or raid.|r"}, 287 | {LOCALE_koKR, "이 명령은 던전이나 공격대에서만 사용할 수 있습니다.|r"}, 288 | {LOCALE_frFR, "Cette commande ne peut être utilisée que dans un donjon ou un raid.|r"}, 289 | {LOCALE_deDE, "Dieser Befehl kann nur in einem Dungeon oder Schlachtzug verwendet werden.|r"}, 290 | {LOCALE_zhCN, "此命令只能在地下城或团队副本中使用。|r"}, 291 | {LOCALE_zhTW, "此命令只能在地城或團隊副本中使用。|r"}, 292 | {LOCALE_esES, "Este comando solo se puede usar en una mazmorra o banda.|r"}, 293 | {LOCALE_esMX, "Este comando solo se puede usar en una mazmorra o banda.|r"}, 294 | {LOCALE_ruRU, "Эту команду можно использовать только в подземелье или рейде.|r"}, 295 | }; 296 | 297 | const std::unordered_map AB_TARGET_NO_IN_INSTANCE = { 298 | {LOCALE_enUS, "That target is not in an instance.|r"}, 299 | {LOCALE_koKR, "그 대상은 인스턴스에 있지 않습니다.|r"}, 300 | {LOCALE_frFR, "Cette cible n'est pas dans une instance.|r"}, 301 | {LOCALE_deDE, "Dieses Ziel befindet sich nicht in einer Instanz.|r"}, 302 | {LOCALE_zhCN, "该目标不在副本中。|r"}, 303 | {LOCALE_zhTW, "該目標不在副本中。|r"}, 304 | {LOCALE_esES, "Ese objetivo no está en una instancia.|r"}, 305 | {LOCALE_esMX, "Ese objetivo no está en una instancia.|r"}, 306 | {LOCALE_ruRU, "Эта цель не находится в подземелье.|r"}, 307 | }; 308 | 309 | const std::unordered_map AB_ACTIVE_FOR_MAP_STATS = { 310 | {LOCALE_enUS, "Active for Map Stats|r"}, 311 | {LOCALE_koKR, "지도 통계용 활성|r"}, 312 | {LOCALE_frFR, "Actif pour les statistiques de la carte|r"}, 313 | {LOCALE_deDE, "Aktiv für Kartenstatistiken|r"}, 314 | {LOCALE_zhCN, "地图平衡激活|r"}, 315 | {LOCALE_zhTW, "地圖平衡激活|r"}, 316 | {LOCALE_esES, "Activo para estadísticas del mapa|r"}, 317 | {LOCALE_esMX, "Activo para estadísticas del mapa|r"}, 318 | {LOCALE_ruRU, "Активно для статистики карты|r"}, 319 | }; 320 | 321 | const std::unordered_map AB_IGNORED_FOR_MAP_STATS = { 322 | {LOCALE_enUS, "Ignored for Map Stats|r"}, 323 | {LOCALE_koKR, "지도 통계용 무시됨|r"}, 324 | {LOCALE_frFR, "Ignoré pour les statistiques de la carte|r"}, 325 | {LOCALE_deDE, "Ignoriert für Kartenstatistiken|r"}, 326 | {LOCALE_zhCN, "地图平衡已忽略|r"}, 327 | {LOCALE_zhTW, "地圖平衡已忽略|r"}, 328 | {LOCALE_esES, "Ignorado para estadísticas del mapa|r"}, 329 | {LOCALE_esMX, "Ignorado para estadísticas del mapa|r"}, 330 | {LOCALE_ruRU, "Игнорируется для статистики карты|r"}, 331 | }; 332 | 333 | const std::unordered_map AB_CREATURE_DIFFICULTY_LEVEL = { 334 | {LOCALE_enUS, "Creature difficulty level: {} player(s)|r"}, 335 | {LOCALE_koKR, "생물 난이도 레벨: {} 플레이어|r"}, 336 | {LOCALE_frFR, "Niveau de difficulté de la créature : {} joueur(s)|r"}, 337 | {LOCALE_deDE, "Kreaturschwierigkeitsstufe: {} Spieler|r"}, 338 | {LOCALE_zhCN, "生物难度等级:{} 玩家|r"}, 339 | {LOCALE_zhTW, "生物難度等級:{} 玩家|r"}, 340 | {LOCALE_esES, "Nivel de dificultad de la criatura: {} jugador(es)|r"}, 341 | {LOCALE_esMX, "Nivel de dificultad de la criatura: {} jugador(es)|r"}, 342 | {LOCALE_ruRU, "Уровень сложности существа: {} игрок(ов)|r"}, 343 | }; 344 | 345 | const std::unordered_map AB_CLONE_OF_SUMMON = { 346 | {LOCALE_enUS, "Clone of {} ({})|r"}, 347 | {LOCALE_koKR, "{}의 복제 ({})|r"}, 348 | {LOCALE_frFR, "Clone de {} ({})|r"}, 349 | {LOCALE_deDE, "Klon von {} ({})|r"}, 350 | {LOCALE_zhCN, "{}的克隆({})|r"}, 351 | {LOCALE_zhTW, "{}的克隆({})|r"}, 352 | {LOCALE_esES, "Clon de {} ({})|r"}, 353 | {LOCALE_esMX, "Clon de {} ({})|r"}, 354 | {LOCALE_ruRU, "Клон {} ({})|r"}, 355 | }; 356 | 357 | const std::unordered_map AB_SUMMON_OF_SUMMON = { 358 | {LOCALE_enUS, "Summon of {} ({})|r"}, 359 | {LOCALE_koKR, "{}의 소환 ({})|r"}, 360 | {LOCALE_frFR, "Invocation de {} ({})|r"}, 361 | {LOCALE_deDE, "Beschwörung von {} ({})|r"}, 362 | {LOCALE_zhCN, "{}的召唤({})|r"}, 363 | {LOCALE_zhTW, "{}的召喚({})|r"}, 364 | {LOCALE_esES, "Invocación de {} ({})|r"}, 365 | {LOCALE_esMX, "Invocación de {} ({})|r"}, 366 | {LOCALE_ruRU, "Призыв {} ({})|r"}, 367 | }; 368 | 369 | const std::unordered_map AB_SUMMON_WITHOUT_SUMMONER = { 370 | {LOCALE_enUS, "Summon without a summoner.|r"}, 371 | {LOCALE_koKR, "소환사 없는 소환.|r"}, 372 | {LOCALE_frFR, "Invocation sans invocateur.|r"}, 373 | {LOCALE_deDE, "Beschwörung ohne Beschwörer.|r"}, 374 | {LOCALE_zhCN, "没有召唤者的召唤物。|r"}, 375 | {LOCALE_zhTW, "沒有召喚者的召喚物。|r"}, 376 | {LOCALE_esES, "Invocación sin invocador.|r"}, 377 | {LOCALE_esMX, "Invocación sin invocador.|r"}, 378 | {LOCALE_ruRU, "Призыв без призывателя.|r"}, 379 | }; 380 | 381 | const std::unordered_map AB_HEALTH_MULTIPLIER_TO = { 382 | {LOCALE_enUS, "Health multiplier: {:.3f} -> {:.3f}|r"}, 383 | {LOCALE_koKR, "체력 배율: {:.3f} -> {:.3f}|r"}, 384 | {LOCALE_frFR, "Multiplicateur de santé : {:.3f} -> {:.3f}|r"}, 385 | {LOCALE_deDE, "Gesundheitsmultiplikator: {:.3f} -> {:.3f}|r"}, 386 | {LOCALE_zhCN, "生命值倍率:{:.3f} -> {:.3f}|r"}, 387 | {LOCALE_zhTW, "生命值倍增器:{:.3f} -> {:.3f}|r"}, 388 | {LOCALE_esES, "Multiplicador de salud: {:.3f} -> {:.3f}|r"}, 389 | {LOCALE_esMX, "Multiplicador de salud: {:.3f} -> {:.3f}|r"}, 390 | {LOCALE_ruRU, "Множитель здоровья: {:.3f} -> {:.3f}|r"}, 391 | }; 392 | 393 | const std::unordered_map AB_MANA_MULTIPLIER_TO = { 394 | {LOCALE_enUS, "Mana multiplier: {:.3f} -> {:.3f}|r"}, 395 | {LOCALE_koKR, "마나 배율: {:.3f} -> {:.3f}|r"}, 396 | {LOCALE_frFR, "Multiplicateur de mana : {:.3f} -> {:.3f}|r"}, 397 | {LOCALE_deDE, "Manamultiplikator: {:.3f} -> {:.3f}|r"}, 398 | {LOCALE_zhCN, "法力值倍率:{:.3f} -> {:.3f}|r"}, 399 | {LOCALE_zhTW, "法力值倍增器:{:.3f} -> {:.3f}|r"}, 400 | {LOCALE_esES, "Multiplicador de maná: {:.3f} -> {:.3f}|r"}, 401 | {LOCALE_esMX, "Multiplicador de maná: {:.3f} -> {:.3f}|r"}, 402 | {LOCALE_ruRU, "Множитель маны: {:.3f} -> {:.3f}|r"}, 403 | }; 404 | 405 | const std::unordered_map AB_ARMOR_MULTIPLIER_TO = { 406 | {LOCALE_enUS, "Armor multiplier: {:.3f} -> {:.3f}|r"}, 407 | {LOCALE_koKR, "방어구 배율: {:.3f} -> {:.3f}|r"}, 408 | {LOCALE_frFR, "Multiplicateur d'armure : {:.3f} -> {:.3f}|r"}, 409 | {LOCALE_deDE, "Rüstungsmultiplikator: {:.3f} -> {:.3f}|r"}, 410 | {LOCALE_zhCN, "护甲倍率:{:.3f} -> {:.3f}|r"}, 411 | {LOCALE_zhTW, "護甲倍增器:{:.3f} -> {:.3f}|r"}, 412 | {LOCALE_esES, "Multiplicador de armadura: {:.3f} -> {:.3f}|r"}, 413 | {LOCALE_esMX, "Multiplicador de armadura: {:.3f} -> {:.3f}|r"}, 414 | {LOCALE_ruRU, "Множитель брони: {:.3f} -> {:.3f}|r"}, 415 | }; 416 | 417 | const std::unordered_map AB_DAMAGE_MULTIPLIER_TO = { 418 | {LOCALE_enUS, "Damage multiplier: {:.3f} -> {:.3f}|r"}, 419 | {LOCALE_koKR, "피해 배율: {:.3f} -> {:.3f}|r"}, 420 | {LOCALE_frFR, "Multiplicateur de dégâts : {:.3f} -> {:.3f}|r"}, 421 | {LOCALE_deDE, "Schadensmultiplikator: {:.3f} -> {:.3f}|r"}, 422 | {LOCALE_zhCN, "伤害倍率:{:.3f} -> {:.3f}|r"}, 423 | {LOCALE_zhTW, "傷害倍增器:{:.3f} -> {:.3f}|r"}, 424 | {LOCALE_esES, "Multiplicador de daño: {:.3f} -> {:.3f}|r"}, 425 | {LOCALE_esMX, "Multiplicador de daño: {:.3f} -> {:.3f}|r"}, 426 | {LOCALE_ruRU, "Множитель урона: {:.3f} -> {:.3f}|r"}, 427 | }; 428 | 429 | const std::unordered_map AB_HEALTH_MULTIPLIER = { 430 | {LOCALE_enUS, "Health multiplier: {:.3f}|r"}, 431 | {LOCALE_koKR, "체력 배율: {:.3f}|r"}, 432 | {LOCALE_frFR, "Multiplicateur de santé : {:.3f}|r"}, 433 | {LOCALE_deDE, "Gesundheitsmultiplikator: {:.3f}|r"}, 434 | {LOCALE_zhCN, "生命值倍率:{:.3f}|r"}, 435 | {LOCALE_zhTW, "生命值倍增器:{:.3f}|r"}, 436 | {LOCALE_esES, "Multiplicador de salud: {:.3f}|r"}, 437 | {LOCALE_esMX, "Multiplicador de salud: {:.3f}|r"}, 438 | {LOCALE_ruRU, "Множитель здоровья: {:.3f}|r"}, 439 | }; 440 | 441 | const std::unordered_map AB_MANA_MULTIPLIER = { 442 | {LOCALE_enUS, "Mana multiplier: {:.3f}|r"}, 443 | {LOCALE_koKR, "마나 배율: {:.3f}|r"}, 444 | {LOCALE_frFR, "Multiplicateur de mana : {:.3f}|r"}, 445 | {LOCALE_deDE, "Manamultiplikator: {:.3f}|r"}, 446 | {LOCALE_zhCN, "法力值倍率:{:.3f}|r"}, 447 | {LOCALE_zhTW, "法力值倍增器:{:.3f}|r"}, 448 | {LOCALE_esES, "Multiplicador de maná: {:.3f}|r"}, 449 | {LOCALE_esMX, "Multiplicador de maná: {:.3f}|r"}, 450 | {LOCALE_ruRU, "Множитель маны: {:.3f}|r"}, 451 | }; 452 | 453 | const std::unordered_map AB_ARMOR_MULTIPLIER = { 454 | {LOCALE_enUS, "Armor multiplier: {:.3f}|r"}, 455 | {LOCALE_koKR, "방어구 배율: {:.3f}|r"}, 456 | {LOCALE_frFR, "Multiplicateur d'armure : {:.3f}|r"}, 457 | {LOCALE_deDE, "Rüstungsmultiplikator: {:.3f}|r"}, 458 | {LOCALE_zhCN, "护甲倍率:{:.3f}|r"}, 459 | {LOCALE_zhTW, "護甲倍增器:{:.3f}|r"}, 460 | {LOCALE_esES, "Multiplicador de armadura: {:.3f}|r"}, 461 | {LOCALE_esMX, "Multiplicador de armadura: {:.3f}|r"}, 462 | {LOCALE_ruRU, "Множитель брони: {:.3f}|r"}, 463 | }; 464 | 465 | const std::unordered_map AB_DAMAGE_MULTIPLIER = { 466 | {LOCALE_enUS, "Damage multiplier: {:.3f}|r"}, 467 | {LOCALE_koKR, "피해 배율: {:.3f}|r"}, 468 | {LOCALE_frFR, "Multiplicateur de dégâts : {:.3f}|r"}, 469 | {LOCALE_deDE, "Schadensmultiplikator: {:.3f}|r"}, 470 | {LOCALE_zhCN, "伤害倍率:{:.3f}|r"}, 471 | {LOCALE_zhTW, "傷害倍增器:{:.3f}|r"}, 472 | {LOCALE_esES, "Multiplicador de daño: {:.3f}|r"}, 473 | {LOCALE_esMX, "Multiplicador de daño: {:.3f}|r"}, 474 | {LOCALE_ruRU, "Множитель урона: {:.3f}|r"}, 475 | }; 476 | 477 | const std::unordered_map AB_CC_DURATION_MULTIPLIER = { 478 | {LOCALE_enUS, "CC Duration multiplier: {:.3f}|r"}, 479 | {LOCALE_koKR, "CC 지속시간 배율: {:.3f}|r"}, 480 | {LOCALE_frFR, "Multiplicateur de durée de la CC : {:.3f}|r"}, 481 | {LOCALE_deDE, "CC-Dauer-Multiplikator: {:.3f}|r"}, 482 | {LOCALE_zhCN, "控制持续时间倍率:{:.3f}|r"}, 483 | {LOCALE_zhTW, "控制持續時間倍增器:{:.3f}|r"}, 484 | {LOCALE_esES, "Multiplicador de duración de CC: {:.3f}|r"}, 485 | {LOCALE_esMX, "Multiplicador de duración de CC: {:.3f}|r"}, 486 | {LOCALE_ruRU, "Множитель длительности контроля: {:.3f}|r"}, 487 | }; 488 | 489 | const std::unordered_map AB_XP_MONEY_MULTIPLIER = { 490 | {LOCALE_enUS, "XP multiplier: {:.3f} Money multiplier: {:.3f}|r"}, 491 | {LOCALE_koKR, "경험치 배율: {:.3f} 돈 배율: {:.3f}|r"}, 492 | {LOCALE_frFR, "Multiplicateur d'XP : {:.3f} Multiplicateur d'argent : {:.3f}|r"}, 493 | {LOCALE_deDE, "XP-Multiplikator: {:.3f} Geldmultiplikator: {:.3f}|r"}, 494 | {LOCALE_zhCN, "经验值倍率:{:.3f} 金钱倍率:{:.3f}|r"}, 495 | {LOCALE_zhTW, "經驗值倍增器:{:.3f} 金錢倍增器:{:.3f}|r"}, 496 | {LOCALE_esES, "Multiplicador de XP: {:.3f} Multiplicador de dinero: {:.3f}|r"}, 497 | {LOCALE_esMX, "Multiplicador de XP: {:.3f} Multiplicador de dinero: {:.3f}|r"}, 498 | {LOCALE_ruRU, "Множитель опыта: {:.3f} Множитель денег: {:.3f}|r"}, 499 | }; 500 | 501 | const std::unordered_map AB_LEAVING_INSTANCE_COMBAT_CHANGE = { 502 | {LOCALE_enUS, "|cffc3dbff [AutoBalance]|r|cffFF8000 Combat has ended. Difficulty is no longer locked.|r"}, 503 | {LOCALE_koKR, "|cffc3dbff [AutoBalance]|r|cffFF8000 전투가 종료되었습니다. 난이도가 더 이상 잠겨 있지 않습니다.|r"}, 504 | {LOCALE_frFR, "|cffc3dbff [AutoBalance]|r|cffFF8000 Le combat est terminé. La difficulté n'est plus verrouillée.|r"}, 505 | {LOCALE_deDE, "|cffc3dbff [AutoBalance]|r|cffFF8000 Der Kampf ist vorbei. Die Schwierigkeit ist nicht mehr gesperrt.|r"}, 506 | {LOCALE_zhCN, "|cffc3dbff [AutoBalance]|r|cffFF8000 战斗结束了。难度不再被锁定。|r"}, 507 | {LOCALE_zhTW, "|cffc3dbff [AutoBalance]|r|cffFF8000 戰鬥已結束。難度不再被鎖定。|r"}, 508 | {LOCALE_esES, "|cffc3dbff [AutoBalance]|r|cffFF8000 El combate ha terminado. La dificultad ya no está bloqueada.|r"}, 509 | {LOCALE_esMX, "|cffc3dbff [AutoBalance]|r|cffFF8000 El combate ha terminado. La dificultad ya no está bloqueada.|r"}, 510 | {LOCALE_ruRU, "|cffc3dbff [AutoBalance]|r|cffFF8000 Бой окончен. Сложность больше не заблокирована.|r"}, 511 | }; 512 | 513 | std::unordered_map*> abTextMaps = { 514 | {"welcome_to_player", &AB_WELCOME_TO_PLAYER}, 515 | {"welcome_to_gm", &AB_WELCOME_TO_GM}, 516 | {"announce_non_gm_entering_instance", &AB_ANNOUNCE_NON_GM_ENTERING_INSTANCE}, 517 | {"leaving_instance_combat", &AB_LEAVING_INSTANCE_COMBAT}, 518 | {"leaving_instance", &AB_LEAVING_INSTANCE}, 519 | {"set_offset_command_description", &AB_SET_OFFSET_COMMAND_DESCRIPTION}, 520 | {"set_offset_command_success", &AB_SET_OFFSET_COMMAND_SUCCESS}, 521 | {"set_offset_command_error", &AB_SET_OFFSET_COMMAND_ERROR}, 522 | {"get_offset_command_success", &AB_GET_OFFSET_COMMAND_SUCCESS}, 523 | {"adjusted_player_count_combat_locked", &AB_ADJUSTED_PLAYER_COUNT_COMBAT_LOCKED}, 524 | {"adjusted_player_count_map_minimum", &AB_ADJUSTED_PLAYER_COUNT_MAP_MINIMUM}, 525 | {"adjusted_player_count_map_minimum_difficulty_offset", &AB_ADJUSTED_PLAYER_COUNT_MAP_MINIMUM_DIFFICULTY_OFFSET}, 526 | {"adjusted_player_count_difficulty_offset", &AB_ADJUSTED_PLAYER_COUNT_DIFFICULTY_OFFSET}, 527 | {"adjusted_player_count", &AB_ADJUSTED_PLAYER_COUNT}, 528 | {"lfg_range", &AB_LFG_RANGE}, 529 | {"map_level", &AB_MAP_LEVEL}, 530 | {"level_scaling_enabled", &AB_LEVEL_SCALING_ENABLED}, 531 | {"level_scaling_disabled", &AB_LEVEL_SCALING_DISABLED}, 532 | {"world_health_multiplier", &AB_WORLD_HEALTH_MULTIPLIER}, 533 | {"world_hostile_damage_healing_multiplier_to", &AB_WORLD_HOSTILE_DAMAGE_HEALING_MULTIPLIER_TO}, 534 | {"world_hostile_damage_healing_multiplier", &AB_WORLD_HOSTILE_DAMAGE_HEALING_MULTIPLIER}, 535 | {"original_creature_level_range", &AB_ORIGINAL_CREATURE_LEVEL_RANGE}, 536 | {"active_total_creatures_in_map", &AB_ACTIVE_TOTAL_CREATURES_IN_MAP}, 537 | {"ab_command_only_in_instance", &AB_COMMAND_ONLY_IN_INSTANCE}, 538 | {"target_no_in_instance", &AB_TARGET_NO_IN_INSTANCE}, 539 | {"active_for_map_stats", &AB_ACTIVE_FOR_MAP_STATS}, 540 | {"ignored_for_map_stats", &AB_IGNORED_FOR_MAP_STATS}, 541 | {"creature_difficulty_level", &AB_CREATURE_DIFFICULTY_LEVEL}, 542 | {"clone_of_summon", &AB_CLONE_OF_SUMMON}, 543 | {"summon_of_summon", &AB_SUMMON_OF_SUMMON}, 544 | {"summon_without_summoner", &AB_SUMMON_WITHOUT_SUMMONER}, 545 | {"health_multiplier_to", &AB_HEALTH_MULTIPLIER_TO}, 546 | {"mana_multiplier_to", &AB_MANA_MULTIPLIER_TO}, 547 | {"armor_multiplier_to", &AB_ARMOR_MULTIPLIER_TO}, 548 | {"damage_multiplier_to", &AB_DAMAGE_MULTIPLIER_TO}, 549 | {"health_multiplier", &AB_HEALTH_MULTIPLIER}, 550 | {"mana_multiplier", &AB_MANA_MULTIPLIER}, 551 | {"armor_multiplier", &AB_ARMOR_MULTIPLIER}, 552 | {"damage_multiplier", &AB_DAMAGE_MULTIPLIER}, 553 | {"cc_duration_multiplier", &AB_CC_DURATION_MULTIPLIER}, 554 | {"xp_money_multiplier", &AB_XP_MONEY_MULTIPLIER}, 555 | {"leaving_instance_combat_change", &AB_LEAVING_INSTANCE_COMBAT_CHANGE}, 556 | }; 557 | 558 | std::string ABGetLocaleText(LocaleConstant locale, const std::string& titleType) { 559 | auto textMapIt = abTextMaps.find(titleType); 560 | if (textMapIt != abTextMaps.end()) 561 | { 562 | const std::unordered_map* textMap = textMapIt->second; 563 | auto it = textMap->find(locale); 564 | if (it != textMap->end()) 565 | return it->second; 566 | } 567 | 568 | return ""; 569 | } 570 | -------------------------------------------------------------------------------- /src/Message.h: -------------------------------------------------------------------------------- 1 | // Message.h 2 | #ifndef AB_MESSAGE_H 3 | #define AB_MESSAGE_H 4 | 5 | #include "Common.h" 6 | #include "ItemTemplate.h" 7 | 8 | #include 9 | 10 | std::string ABGetLocaleText(LocaleConstant locale, const std::string& titleType); 11 | 12 | #endif // AB_MESSAGE_H 13 | --------------------------------------------------------------------------------