From 7d50ceef3daebef32075085ed88d7293d7b857ce Mon Sep 17 00:00:00 2001 From: Iain Donnelly Date: Wed, 1 Oct 2025 16:39:48 +0100 Subject: [PATCH 01/47] Update RandomPlayerbotFactory.cpp Added a query to count the number of guilds straight from the DB, then filter out player guilds. (instead of relying on accessing guilds from a list of random bots and adding them up) This needs some formatting / tidying once I make sure we are counting guilds properly. --- src/RandomPlayerbotFactory.cpp | 41 ++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/src/RandomPlayerbotFactory.cpp b/src/RandomPlayerbotFactory.cpp index cf5fdc915e..b5d74b8d44 100644 --- a/src/RandomPlayerbotFactory.cpp +++ b/src/RandomPlayerbotFactory.cpp @@ -875,15 +875,52 @@ void RandomPlayerbotFactory::CreateRandomGuilds() LOG_INFO("playerbots", "Random bot guilds deleted"); } + // Check how many randomBot guilds are in the guild table in the characterDB uint32 guildNumber = 0; + QueryResult guildTableResults = CharacterDatabase.Query("SELECT guildid, leaderguid FROM guid"); + if (guildTableResults) + { + do + { + Field* fields = guildTableResults->Fetch(); + uint32 guildID = fields[0].Get(); + uint32 leaderGuid = fields[1].Get(); + + // check the accountID of the guild leader against the list of randomBot accounts to determine if this is a player guild or a bot guild + QueryResult charactersTableResults = CharacterDatabase.Query("SELECT account FROM characters WHERE guid = ({})", leaderGuid); + if (charactersTableResults) + { + Field* fields2 = charactersTableResults->Fetch(); + uint32 accountID = fields2[0].Get(); + + if(std::find(sPlayerbotAIConfig->randomBotAccounts.begin(),sPlayerbotAIConfig->randomBotAccounts.end(), accountID) != sPlayerbotAIConfig->randomBotAccounts.end()) + { + guildNumber++; + sPlayerbotAIConfig->randomBotGuilds.push_back(guildID); + } + } + } while (guildTableResults->NextRow()); + } + GuidVector availableLeaders; for (std::vector::iterator i = randomBots.begin(); i != randomBots.end(); ++i) { ObjectGuid leader = ObjectGuid::Create(*i); if (Guild* guild = sGuildMgr->GetGuildByLeader(leader)) { - ++guildNumber; - sPlayerbotAIConfig->randomBotGuilds.push_back(guild->GetId()); + uint32 guildID = guild->GetId(); + if(std::find(sPlayerbotAIConfig->randomBotGuilds.begin(), sPlayerbotAIConfig->randomBotGuilds.end(), guildID) !=sPlayerbotAIConfig->randomBotGuilds.end()) + { + // this randombot guild has already been counted, the leader is not availble + } + else + { + // somehow we missed this guild when checking the guild table + // THIS SHOULDN'T RUN. + // THIS ENTIRE FIND() IS A WASTE OF RESOURCES (in theory) + ++guildNumber; + sPlayerbotAIConfig->randomBotGuilds.push_back(guildID); + } } else { From 8a68de4476fef4aa0c00d97e5c6a855ffc445276 Mon Sep 17 00:00:00 2001 From: Iain Donnelly Date: Wed, 1 Oct 2025 17:36:00 +0100 Subject: [PATCH 02/47] Update RandomPlayerbotFactory.cpp Fixed a typo --- src/RandomPlayerbotFactory.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/RandomPlayerbotFactory.cpp b/src/RandomPlayerbotFactory.cpp index b5d74b8d44..c28b9587f0 100644 --- a/src/RandomPlayerbotFactory.cpp +++ b/src/RandomPlayerbotFactory.cpp @@ -877,7 +877,7 @@ void RandomPlayerbotFactory::CreateRandomGuilds() // Check how many randomBot guilds are in the guild table in the characterDB uint32 guildNumber = 0; - QueryResult guildTableResults = CharacterDatabase.Query("SELECT guildid, leaderguid FROM guid"); + QueryResult guildTableResults = CharacterDatabase.Query("SELECT guildid, leaderguid FROM guild"); if (guildTableResults) { do @@ -887,7 +887,7 @@ void RandomPlayerbotFactory::CreateRandomGuilds() uint32 leaderGuid = fields[1].Get(); // check the accountID of the guild leader against the list of randomBot accounts to determine if this is a player guild or a bot guild - QueryResult charactersTableResults = CharacterDatabase.Query("SELECT account FROM characters WHERE guid = ({})", leaderGuid); + QueryResult charactersTableResults = CharacterDatabase.Query("SELECT account FROM characters WHERE guid = {}", leaderGuid); if (charactersTableResults) { Field* fields2 = charactersTableResults->Fetch(); From 444be2994e184eff444bdb5334cf2b63dc824aca Mon Sep 17 00:00:00 2001 From: Iain Donnelly Date: Wed, 1 Oct 2025 23:20:48 +0100 Subject: [PATCH 03/47] v2 Neatened some things up, removed obsolete code, added a break out of the loop if an empty guild name (none available) is returned from the playerbots_guild_names table. --- src/RandomPlayerbotFactory.cpp | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/src/RandomPlayerbotFactory.cpp b/src/RandomPlayerbotFactory.cpp index c28b9587f0..49d32b0c65 100644 --- a/src/RandomPlayerbotFactory.cpp +++ b/src/RandomPlayerbotFactory.cpp @@ -902,25 +902,14 @@ void RandomPlayerbotFactory::CreateRandomGuilds() } while (guildTableResults->NextRow()); } + // Get a list of bots that are logged in and available to lead new guilds GuidVector availableLeaders; for (std::vector::iterator i = randomBots.begin(); i != randomBots.end(); ++i) { ObjectGuid leader = ObjectGuid::Create(*i); if (Guild* guild = sGuildMgr->GetGuildByLeader(leader)) { - uint32 guildID = guild->GetId(); - if(std::find(sPlayerbotAIConfig->randomBotGuilds.begin(), sPlayerbotAIConfig->randomBotGuilds.end(), guildID) !=sPlayerbotAIConfig->randomBotGuilds.end()) - { - // this randombot guild has already been counted, the leader is not availble - } - else - { - // somehow we missed this guild when checking the guild table - // THIS SHOULDN'T RUN. - // THIS ENTIRE FIND() IS A WASTE OF RESOURCES (in theory) - ++guildNumber; - sPlayerbotAIConfig->randomBotGuilds.push_back(guildID); - } + // Bot is already a GM } else { @@ -929,6 +918,7 @@ void RandomPlayerbotFactory::CreateRandomGuilds() availableLeaders.push_back(leader); } } + LOG_INFO("playerbots", "randomBotGuilds - {} available leaders for new guilds found", availableLeaders.size()); // Create up to randomBotGuildCount by counting only EFFECTIVE creations uint32 createdThisRun = 0; @@ -936,10 +926,10 @@ void RandomPlayerbotFactory::CreateRandomGuilds() { std::string const guildName = CreateRandomGuildName(); if (guildName.empty()) - continue; + break; // no more names available in playerbots_guild_names if (sGuildMgr->GetGuildByName(guildName)) - continue; + continue; // name already taken, skip if (availableLeaders.empty()) { From 24f841f7284a24b5a2ef32423847f029aa5af4a0 Mon Sep 17 00:00:00 2001 From: Iain Donnelly Date: Wed, 1 Oct 2025 23:35:08 +0100 Subject: [PATCH 04/47] Verbose logging. Should now be able to see guild count values as the server inits --- src/RandomPlayerbotFactory.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/RandomPlayerbotFactory.cpp b/src/RandomPlayerbotFactory.cpp index 49d32b0c65..4b375c112b 100644 --- a/src/RandomPlayerbotFactory.cpp +++ b/src/RandomPlayerbotFactory.cpp @@ -902,6 +902,18 @@ void RandomPlayerbotFactory::CreateRandomGuilds() } while (guildTableResults->NextRow()); } + // Is it worth continuing? + LOG_INFO("playerbots", "{}/{} random bot guilds exist in guild table)", guildNumber, sPlayerbotAIConfig->randomBotGuildCount); + if (guildNumber >= sPlayerbotAIConfig->randomBotGuildCount) + { + LOG_INFO("playerbots", "No new random guilds required"); + return; + } + else + { + LOG_INFO("playerbots", "Creating {} new random guilds...", sPlayerbotAIConfig->randomBotGuildCount - guildNumber); + } + // Get a list of bots that are logged in and available to lead new guilds GuidVector availableLeaders; for (std::vector::iterator i = randomBots.begin(); i != randomBots.end(); ++i) @@ -918,7 +930,7 @@ void RandomPlayerbotFactory::CreateRandomGuilds() availableLeaders.push_back(leader); } } - LOG_INFO("playerbots", "randomBotGuilds - {} available leaders for new guilds found", availableLeaders.size()); + LOG_INFO("playerbots", "{} available leaders for new guilds found", availableLeaders.size()); // Create up to randomBotGuildCount by counting only EFFECTIVE creations uint32 createdThisRun = 0; @@ -1008,8 +1020,7 @@ void RandomPlayerbotFactory::CreateRandomGuilds() } // Shows the true total and how many were created during this run - LOG_INFO("playerbots", "{} random bot guilds available (created this run: {})", - uint32(sPlayerbotAIConfig->randomBotGuilds.size()), createdThisRun); + LOG_INFO("playerbots", "{} random bot guilds created this run)", createdThisRun); } std::string const RandomPlayerbotFactory::CreateRandomGuildName() From 0e4c759e7f2b9951dda811960ff72b03765a5f93 Mon Sep 17 00:00:00 2001 From: Iain Donnelly Date: Thu, 2 Oct 2025 14:37:47 +0100 Subject: [PATCH 05/47] Wishmaster update Optimised, better use of DB Query, avoids reallocations. Nice. --- src/RandomPlayerbotFactory.cpp | 69 ++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/src/RandomPlayerbotFactory.cpp b/src/RandomPlayerbotFactory.cpp index 4b375c112b..bed18d6cdd 100644 --- a/src/RandomPlayerbotFactory.cpp +++ b/src/RandomPlayerbotFactory.cpp @@ -875,59 +875,64 @@ void RandomPlayerbotFactory::CreateRandomGuilds() LOG_INFO("playerbots", "Random bot guilds deleted"); } - // Check how many randomBot guilds are in the guild table in the characterDB + std::unordered_set botAccounts; + botAccounts.reserve(sPlayerbotAIConfig->randomBotAccounts.size()); + for (uint32 acc : sPlayerbotAIConfig->randomBotAccounts) + botAccounts.insert(acc); + + // Recount bot guilds directly from the database (does not depend on connected bots) uint32 guildNumber = 0; - QueryResult guildTableResults = CharacterDatabase.Query("SELECT guildid, leaderguid FROM guild"); - if (guildTableResults) + sPlayerbotAIConfig->randomBotGuilds.clear(); + sPlayerbotAIConfig->randomBotGuilds.shrink_to_fit(); // avoids accumulating old capacity + + if (!botAccounts.empty()) { - do + if (QueryResult res = CharacterDatabase.Query( + // We only retrieve what is necessary (guildid, leader account) + "SELECT g.guildid, c.account " + "FROM guild g JOIN characters c ON g.leaderguid = c.guid")) { - Field* fields = guildTableResults->Fetch(); - uint32 guildID = fields[0].Get(); - uint32 leaderGuid = fields[1].Get(); - - // check the accountID of the guild leader against the list of randomBot accounts to determine if this is a player guild or a bot guild - QueryResult charactersTableResults = CharacterDatabase.Query("SELECT account FROM characters WHERE guid = {}", leaderGuid); - if (charactersTableResults) + do { - Field* fields2 = charactersTableResults->Fetch(); - uint32 accountID = fields2[0].Get(); + Field* f = res->Fetch(); + const uint32 guildId = f[0].Get(); + const uint32 accountId = f[1].Get(); - if(std::find(sPlayerbotAIConfig->randomBotAccounts.begin(),sPlayerbotAIConfig->randomBotAccounts.end(), accountID) != sPlayerbotAIConfig->randomBotAccounts.end()) + // Boss considered 'bot' if his account is in botAccounts + if (botAccounts.find(accountId) != botAccounts.end()) { - guildNumber++; - sPlayerbotAIConfig->randomBotGuilds.push_back(guildID); + ++guildNumber; + sPlayerbotAIConfig->randomBotGuilds.push_back(guildId); } - } - } while (guildTableResults->NextRow()); + } while (res->NextRow()); + } } - // Is it worth continuing? - LOG_INFO("playerbots", "{}/{} random bot guilds exist in guild table)", guildNumber, sPlayerbotAIConfig->randomBotGuildCount); + LOG_INFO("playerbots", "{}/{} random bot guilds exist in guild table",guildNumber, sPlayerbotAIConfig->randomBotGuildCount); if (guildNumber >= sPlayerbotAIConfig->randomBotGuildCount) { LOG_INFO("playerbots", "No new random guilds required"); return; } - else - { - LOG_INFO("playerbots", "Creating {} new random guilds...", sPlayerbotAIConfig->randomBotGuildCount - guildNumber); - } - // Get a list of bots that are logged in and available to lead new guilds + // We list the available leaders (online bots, not in guilds) GuidVector availableLeaders; - for (std::vector::iterator i = randomBots.begin(); i != randomBots.end(); ++i) + availableLeaders.reserve(randomBots.size()); // limit reallocs + for (const uint32 botLowGuid : randomBots) { - ObjectGuid leader = ObjectGuid::Create(*i); - if (Guild* guild = sGuildMgr->GetGuildByLeader(leader)) + ObjectGuid leader = ObjectGuid::Create(botLowGuid); + if (sGuildMgr->GetGuildByLeader(leader)) { - // Bot is already a GM + // already GuildLeader -> ignored + continue; } else { - Player* player = ObjectAccessor::FindPlayer(leader); - if (player && !player->GetGuildId()) - availableLeaders.push_back(leader); + if (Player* player = ObjectAccessor::FindPlayer(leader)) + { + if (!player->GetGuildId()) + availableLeaders.push_back(leader); + } } } LOG_INFO("playerbots", "{} available leaders for new guilds found", availableLeaders.size()); From 3f050a4a77371a3564e601fe592ee7be0f822927 Mon Sep 17 00:00:00 2001 From: Iain Donnelly Date: Tue, 7 Oct 2025 22:46:19 +0100 Subject: [PATCH 06/47] Change LOG_INFO to LOG_DEBUG --- src/RandomPlayerbotFactory.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/RandomPlayerbotFactory.cpp b/src/RandomPlayerbotFactory.cpp index bed18d6cdd..48531b96cb 100644 --- a/src/RandomPlayerbotFactory.cpp +++ b/src/RandomPlayerbotFactory.cpp @@ -911,7 +911,7 @@ void RandomPlayerbotFactory::CreateRandomGuilds() LOG_INFO("playerbots", "{}/{} random bot guilds exist in guild table",guildNumber, sPlayerbotAIConfig->randomBotGuildCount); if (guildNumber >= sPlayerbotAIConfig->randomBotGuildCount) { - LOG_INFO("playerbots", "No new random guilds required"); + LOG_DEBUG("playerbots", "No new random guilds required"); return; } @@ -935,7 +935,7 @@ void RandomPlayerbotFactory::CreateRandomGuilds() } } } - LOG_INFO("playerbots", "{} available leaders for new guilds found", availableLeaders.size()); + LOG_DEBUG("playerbots", "{} available leaders for new guilds found", availableLeaders.size()); // Create up to randomBotGuildCount by counting only EFFECTIVE creations uint32 createdThisRun = 0; From 1d19dea9746bc7fb26db56dd9af0232c832e7be0 Mon Sep 17 00:00:00 2001 From: Iain Donnelly Date: Wed, 22 Oct 2025 23:53:42 +0100 Subject: [PATCH 07/47] Update RandomPlayerbotFactory.cpp --- src/RandomPlayerbotFactory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/RandomPlayerbotFactory.cpp b/src/RandomPlayerbotFactory.cpp index 48531b96cb..ceed748d42 100644 --- a/src/RandomPlayerbotFactory.cpp +++ b/src/RandomPlayerbotFactory.cpp @@ -898,7 +898,7 @@ void RandomPlayerbotFactory::CreateRandomGuilds() const uint32 guildId = f[0].Get(); const uint32 accountId = f[1].Get(); - // Boss considered 'bot' if his account is in botAccounts + // Determine if guild leader's account is a bot account. if (botAccounts.find(accountId) != botAccounts.end()) { ++guildNumber; From cb099bcaf4a0abf0236d3ee172b742338db9422d Mon Sep 17 00:00:00 2001 From: kadeshar Date: Sat, 1 Nov 2025 17:25:07 +0100 Subject: [PATCH 08/47] Update repository condition for C++ job --- .github/workflows/codestyle_cpp.yml | 35 +++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .github/workflows/codestyle_cpp.yml diff --git a/.github/workflows/codestyle_cpp.yml b/.github/workflows/codestyle_cpp.yml new file mode 100644 index 0000000000..0afaf4b795 --- /dev/null +++ b/.github/workflows/codestyle_cpp.yml @@ -0,0 +1,35 @@ +name: C++ Codestyle +on: + pull_request: + types: + - opened + - reopened + - synchronize + paths: + - src/** + - "!README.md" + - "!docs/**" + +jobs: + triage: + runs-on: ubuntu-latest + name: C++ + if: github.repository == 'mod-playerbots/mod-playerbots' && !github.event.pull_request.draft + steps: + - uses: actions/checkout@v4 + - name: Setup python + uses: actions/setup-python@v5 + with: + python-version: '3.10' + - name: AzerothCore codestyle + run: python ./apps/codestyle/codestyle-cpp.py + - name: C++ Advanced + run: | + sudo apt update -y + sudo apt install -y cppcheck + cppcheck --force --inline-suppr --suppressions-list=./.suppress.cppcheck src/ --output-file=report.txt + + if [ -s report.txt ]; then # if file is not empty + cat report.txt + exit 1 # let github action fails + fi From 586c4d9d058490a409226ca7e6375d2aac4175e4 Mon Sep 17 00:00:00 2001 From: kadeshar Date: Sat, 1 Nov 2025 17:27:26 +0100 Subject: [PATCH 09/47] - Added codestyle azerothcore python script --- apps/codestyle-cpp.py | 263 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 apps/codestyle-cpp.py diff --git a/apps/codestyle-cpp.py b/apps/codestyle-cpp.py new file mode 100644 index 0000000000..72c2a88862 --- /dev/null +++ b/apps/codestyle-cpp.py @@ -0,0 +1,263 @@ +import io +import os +import sys +import re + +# Get the src directory of the project +src_directory = os.path.join(os.getcwd(), 'src') + +# Global variables +error_handler = False +results = { + "Multiple blank lines check": "Passed", + "Trailing whitespace check": "Passed", + "GetCounter() check": "Passed", + "Misc codestyle check": "Passed", + "GetTypeId() check": "Passed", + "NpcFlagHelpers check": "Passed", + "ItemFlagHelpers check": "Passed", + "ItemTemplateFlagHelpers check": "Passed" +} + +# Main function to parse all the files of the project +def parsing_file(directory: str) -> None: + print("Starting AzerothCore CPP Codestyle check...") + print(" ") + print("Please read the C++ Code Standards for AzerothCore:") + print("https://www.azerothcore.org/wiki/cpp-code-standards") + print(" ") + for root, _, files in os.walk(directory): + for file in files: + if not file.endswith('.ico'): # Skip .ico files that cannot be read + file_path = os.path.join(root, file) + file_name = file + try: + with open(file_path, 'r', encoding='utf-8') as file: + multiple_blank_lines_check(file, file_path) + trailing_whitespace_check(file, file_path) + get_counter_check(file, file_path) + if not file_name.endswith('.cmake') and file_name != 'CMakeLists.txt': + misc_codestyle_check(file, file_path) + if file_name != 'Object.h': + get_typeid_check(file, file_path) + if file_name != 'Unit.h': + npcflags_helpers_check(file, file_path) + if file_name != 'Item.h': + itemflag_helpers_check(file, file_path) + if file_name != 'ItemTemplate.h': + itemtemplateflag_helpers_check(file, file_path) + except UnicodeDecodeError: + print(f"\nCould not decode file {file_path}") + sys.exit(1) + # Output the results + print("") + for check, result in results.items(): + print(f"{check} : {result}") + if error_handler: + print("\nPlease fix the codestyle issues above.") + sys.exit(1) + else: + print(f"\nEverything looks good") + +# Codestyle patterns checking for multiple blank lines +def multiple_blank_lines_check(file: io, file_path: str) -> None: + global error_handler, results + file.seek(0) # Reset file pointer to the beginning + check_failed = False + consecutive_blank_lines = 0 + # Parse all the file + for line_number, line in enumerate(file, start = 1): + if line.strip() == '': + consecutive_blank_lines += 1 + if consecutive_blank_lines > 1: + print(f"Multiple blank lines found in {file_path} at line {line_number - 1}") + check_failed = True + else: + consecutive_blank_lines = 0 + # Additional check for the end of the file + if consecutive_blank_lines >= 1: + print(f"Multiple blank lines found at the end of: {file_path}") + check_failed = True + # Handle the script error and update the result output + if check_failed: + error_handler = True + results["Multiple blank lines check"] = "Failed" + +# Codestyle patterns checking for whitespace at the end of the lines +def trailing_whitespace_check(file: io, file_path: str) -> None: + global error_handler, results + file.seek(0) # Reset file pointer to the beginning + # Parse all the file + for line_number, line in enumerate(file, start = 1): + if line.endswith(' \n'): + print(f"Trailing whitespace found: {file_path} at line {line_number}") + if not error_handler: + error_handler = True + results["Trailing whitespace check"] = "Failed" + +# Codestyle patterns checking for ObjectGuid::GetCounter() +def get_counter_check(file: io, file_path: str) -> None: + global error_handler, results + file.seek(0) # Reset file pointer to the beginning + # Parse all the file + for line_number, line in enumerate(file, start = 1): + if 'ObjectGuid::GetCounter()' in line: + print(f"Please use ObjectGuid::ToString().c_str() instead ObjectGuid::GetCounter(): {file_path} at line {line_number}") + if not error_handler: + error_handler = True + results["GetCounter() check"] = "Failed" + +# Codestyle patterns checking for GetTypeId() +def get_typeid_check(file: io, file_path: str) -> None: + global error_handler, results + file.seek(0) # Reset file pointer to the beginning + check_failed = False + # Parse all the file + for line_number, line in enumerate(file, start = 1): + if 'GetTypeId() == TYPEID_ITEM' in line or 'GetTypeId() != TYPEID_ITEM' in line: + print(f"Please use IsItem() instead of GetTypeId(): {file_path} at line {line_number}") + check_failed = True + if 'GetTypeId() == TYPEID_UNIT' in line or 'GetTypeId() != TYPEID_UNIT' in line: + print(f"Please use IsCreature() instead of GetTypeId(): {file_path} at line {line_number}") + check_failed = True + if 'GetTypeId() == TYPEID_PLAYER' in line or 'GetTypeId() != TYPEID_PLAYER' in line: + print(f"Please use IsPlayer() instead of GetTypeId(): {file_path} at line {line_number}") + check_failed = True + if 'GetTypeId() == TYPEID_GAMEOBJECT' in line or 'GetTypeId() != TYPEID_GAMEOBJECT' in line: + print(f"Please use IsGameObject() instead of GetTypeId(): {file_path} at line {line_number}") + check_failed = True + if 'GetTypeId() == TYPEID_DYNOBJECT' in line or 'GetTypeId() != TYPEID_DYNOBJECT' in line: + print(f"Please use IsDynamicObject() instead of GetTypeId(): {file_path} at line {line_number}") + check_failed = True + # Handle the script error and update the result output + if check_failed: + error_handler = True + results["GetTypeId() check"] = "Failed" + +# Codestyle patterns checking for NpcFlag helpers +def npcflags_helpers_check(file: io, file_path: str) -> None: + global error_handler, results + file.seek(0) # Reset file pointer to the beginning + check_failed = False + # Parse all the file + for line_number, line in enumerate(file, start = 1): + if 'GetUInt32Value(UNIT_NPC_FLAGS)' in line: + print( + f"Please use GetNpcFlags() instead of GetUInt32Value(UNIT_NPC_FLAGS): {file_path} at line {line_number}") + check_failed = True + if 'HasFlag(UNIT_NPC_FLAGS,' in line: + print( + f"Please use HasNpcFlag() instead of HasFlag(UNIT_NPC_FLAGS, ...): {file_path} at line {line_number}") + check_failed = True + if 'SetUInt32Value(UNIT_NPC_FLAGS,' in line: + print( + f"Please use ReplaceAllNpcFlags() instead of SetUInt32Value(UNIT_NPC_FLAGS, ...): {file_path} at line {line_number}") + check_failed = True + if 'SetFlag(UNIT_NPC_FLAGS,' in line: + print( + f"Please use SetNpcFlag() instead of SetFlag(UNIT_NPC_FLAGS, ...): {file_path} at line {line_number}") + check_failed = True + if 'RemoveFlag(UNIT_NPC_FLAGS,' in line: + print( + f"Please use RemoveNpcFlag() instead of RemoveFlag(UNIT_NPC_FLAGS, ...): {file_path} at line {line_number}") + check_failed = True + # Handle the script error and update the result output + if check_failed: + error_handler = True + results["NpcFlagHelpers check"] = "Failed" + +# Codestyle patterns checking for ItemFlag helpers +def itemflag_helpers_check(file: io, file_path: str) -> None: + global error_handler, results + file.seek(0) # Reset file pointer to the beginning + check_failed = False + # Parse all the file + for line_number, line in enumerate(file, start = 1): + if 'HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_REFUNDABLE)' in line: + print( + f"Please use IsRefundable() instead of HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_REFUNDABLE): {file_path} at line {line_number}") + check_failed = True + if 'HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_BOP_TRADEABLE)' in line: + print( + f"Please use IsBOPTradable() instead of HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_BOP_TRADEABLE): {file_path} at line {line_number}") + check_failed = True + if 'HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_WRAPPED)' in line: + print( + f"Please use IsWrapped() instead of HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_WRAPPED): {file_path} at line {line_number}") + check_failed = True + # Handle the script error and update the result output + if check_failed: + error_handler = True + results["ItemFlagHelpers check"] = "Failed" + +# Codestyle patterns checking for ItemTemplate helpers +def itemtemplateflag_helpers_check(file: io, file_path: str) -> None: + global error_handler, results + file.seek(0) # Reset file pointer to the beginning + check_failed = False + # Parse all the file + for line_number, line in enumerate(file, start = 1): + if 'Flags & ITEM_FLAG' in line: + print( + f"Please use HasFlag(ItemFlag) instead of 'Flags & ITEM_FLAG_': {file_path} at line {line_number}") + check_failed = True + if 'Flags2 & ITEM_FLAG2' in line: + print( + f"Please use HasFlag2(ItemFlag2) instead of 'Flags2 & ITEM_FLAG2_': {file_path} at line {line_number}") + check_failed = True + if 'FlagsCu & ITEM_FLAGS_CU' in line: + print( + f"Please use HasFlagCu(ItemFlagsCustom) instead of 'FlagsCu & ITEM_FLAGS_CU_': {file_path} at line {line_number}") + check_failed = True + # Handle the script error and update the result output + if check_failed: + error_handler = True + results["ItemTemplateFlagHelpers check"] = "Failed" + +# Codestyle patterns checking for various codestyle issues +def misc_codestyle_check(file: io, file_path: str) -> None: + global error_handler, results + file.seek(0) # Reset file pointer to the beginning + check_failed = False + + # used to check for "if/else (...) {" "} else" ignores "if/else (...) {...}" "#define ... if/else (...) {" + ifelse_curlyregex = r"^[^#define].*\s+(if|else)(\s*\(.*\))?\s*{[^}]*$|}\s*else(\s*{[^}]*$)" + # used to catch double semicolons ";;" ignores "(;;)" + double_semiregex = r"(? Date: Sat, 1 Nov 2025 17:38:11 +0100 Subject: [PATCH 10/47] - added missing var for codestyle azerothcore --- var/.suppress.cppcheck | 1 + 1 file changed, 1 insertion(+) create mode 100644 var/.suppress.cppcheck diff --git a/var/.suppress.cppcheck b/var/.suppress.cppcheck new file mode 100644 index 0000000000..07569fe086 --- /dev/null +++ b/var/.suppress.cppcheck @@ -0,0 +1 @@ +cppcheckError \ No newline at end of file From f7fea456ca524171a36dc7aa753e86a2ee607f73 Mon Sep 17 00:00:00 2001 From: privatecore Date: Sun, 2 Nov 2025 13:27:25 +0100 Subject: [PATCH 11/47] Fix PositionInfo constructors' members order (#1776) --- src/strategy/values/PositionValue.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/strategy/values/PositionValue.h b/src/strategy/values/PositionValue.h index 86ed8d0989..69e8e35943 100644 --- a/src/strategy/values/PositionValue.h +++ b/src/strategy/values/PositionValue.h @@ -15,13 +15,13 @@ class PlayerbotAI; class PositionInfo { public: - PositionInfo() : valueSet(false), x(0), y(0), z(0), mapId(0) {} + PositionInfo() : x(0), y(0), z(0), mapId(0), valueSet(false) {} PositionInfo(float x, float y, float z, uint32 mapId, bool valueSet = true) - : valueSet(valueSet), x(x), y(y), z(z), mapId(mapId) + : x(x), y(y), z(z), mapId(mapId), valueSet(valueSet) { } PositionInfo(PositionInfo const& other) - : valueSet(other.valueSet), x(other.x), y(other.y), z(other.z), mapId(other.mapId) + : x(other.x), y(other.y), z(other.z), mapId(other.mapId), valueSet(other.valueSet) { } @@ -41,8 +41,8 @@ class PositionInfo float x; float y; float z; - bool valueSet; uint32 mapId; + bool valueSet; }; typedef std::map PositionMap; From 43164e74e1f10ede8bb19231fe7629bec764e5d5 Mon Sep 17 00:00:00 2001 From: Revision Date: Sun, 2 Nov 2025 13:28:26 +0100 Subject: [PATCH 12/47] Normalize line endings for 2025_10_27_00_ai_playerbot_german_texts.sql to LF (#1795) --- ...025_10_27_00_ai_playerbot_german_texts.sql | 3068 ++++++++--------- 1 file changed, 1534 insertions(+), 1534 deletions(-) diff --git a/data/sql/playerbots/updates/2025_10_27_00_ai_playerbot_german_texts.sql b/data/sql/playerbots/updates/2025_10_27_00_ai_playerbot_german_texts.sql index 075cd16648..b3c0b9e7c5 100644 --- a/data/sql/playerbots/updates/2025_10_27_00_ai_playerbot_german_texts.sql +++ b/data/sql/playerbots/updates/2025_10_27_00_ai_playerbot_german_texts.sql @@ -1,1534 +1,1534 @@ -UPDATE `ai_playerbot_texts` SET `text_loc3`='am Arsch der Welt' WHERE `id`=1; -UPDATE `ai_playerbot_texts` SET `text_loc3`='an einem geheimen Ort' WHERE `id`=2; -UPDATE `ai_playerbot_texts` SET `text_loc3`='irgendwo' WHERE `id`=3; -UPDATE `ai_playerbot_texts` SET `text_loc3`='irgendwas' WHERE `id`=4; -UPDATE `ai_playerbot_texts` SET `text_loc3`='frag mich, wie %item_link wohl schmeckt' WHERE `id`=5; -UPDATE `ai_playerbot_texts` SET `text_loc3`='neeein, ich hab %item_link bekommen' WHERE `id`=6; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ah nee, nicht schon wieder dieser Müll: %item_link' WHERE `id`=7; -UPDATE `ai_playerbot_texts` SET `text_loc3`='scheint so, als würde ich nur Müll looten: %item_link' WHERE `id`=8; -UPDATE `ai_playerbot_texts` SET `text_loc3`='naja, besser als nix, schätze ich: %item_link' WHERE `id`=9; -UPDATE `ai_playerbot_texts` SET `text_loc3`='keine Ahnung, was ich mit %item_link anfangen soll' WHERE `id`=10; -UPDATE `ai_playerbot_texts` SET `text_loc3`='naja, ich kann den ganzen Tag %item_link looten' WHERE `id`=11; -UPDATE `ai_playerbot_texts` SET `text_loc3`='neuer Tag, neuer %item_link' WHERE `id`=12; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hab %item_link gelootet' WHERE `id`=13; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ein %item_link ist okay' WHERE `id`=14; -UPDATE `ai_playerbot_texts` SET `text_loc3`='nicht schlecht, hab grad %item_link bekommen.' WHERE `id`=15; -UPDATE `ai_playerbot_texts` SET `text_loc3`='gerade %item_link in %zone_name gelootet' WHERE `id`=16; -UPDATE `ai_playerbot_texts` SET `text_loc3`='kann ich gut gebrauchen, %item_link' WHERE `id`=17; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gold, Gold, Gold: %item_link' WHERE `id`=18; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hab %item_link bekommen' WHERE `id`=19; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_link ist BiS für Jäger' WHERE `id`=20; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_link ist BiS für %my_class' WHERE `id`=21; -UPDATE `ai_playerbot_texts` SET `text_loc3`='RNG meint es heute gut: %item_link' WHERE `id`=22; -UPDATE `ai_playerbot_texts` SET `text_loc3`='nice, %item_link frisch gelootet' WHERE `id`=23; -UPDATE `ai_playerbot_texts` SET `text_loc3`='wow, hab gerade %item_link bekommen' WHERE `id`=24; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_link ist BiS für Jäger' WHERE `id`=25; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_link ist BiS für %my_class' WHERE `id`=26; -UPDATE `ai_playerbot_texts` SET `text_loc3`='RNG meint es heute gut: %item_link' WHERE `id`=27; -UPDATE `ai_playerbot_texts` SET `text_loc3`='nice, %item_link frisch gelootet' WHERE `id`=28; -UPDATE `ai_playerbot_texts` SET `text_loc3`='OMG, guckt mal, was ich gerade bekommen hab: %item_link!!!' WHERE `id`=29; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Keine @#$@% Chance! Ich hab %item_link bekommen, Wahnsinn.' WHERE `id`=30; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Keine @#$@% Chance! Das kann nicht sein - ich hab %item_link bekommen, völlig irre.' WHERE `id`=31; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Im Ernst? Noch ein %item_link? Mein Glück ist kaputt.' WHERE `id`=32; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich hab %item_link... ugh, schon wieder.' WHERE `id`=34; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Keine Ahnung, warum ich %item_link überhaupt aufhebe, es ist nutzlos.' WHERE `id`=35; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Schaut euch diesen glänzenden %item_link an... schade, ist trotzdem Müll.' WHERE `id`=36; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Dieser %item_link ist nicht mal das Papier wert, auf dem er stehen würde.' WHERE `id`=37; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich schätze, %item_link ist besser als gar nix? Gerade so.' WHERE `id`=38; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kein Plan, was ich mit %item_link machen soll, vielleicht verkauf ich es für 1 Silber.' WHERE `id`=39; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Noch ein %item_link... naja, immerhin krieg ich was.' WHERE `id`=40; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hoffentlich ist %item_link was wert, es sieht nämlich furchtbar aus.' WHERE `id`=41; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Warum krieg ich ständig %item_link... hab ich RNG verärgert?' WHERE `id`=43; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Als würde %item_link mir nachlaufen. Ich will das nicht!' WHERE `id`=44; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Da ist %item_link schon wieder. Was für eine Überraschung.' WHERE `id`=45; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich schätze, %item_link taugt als Händler-Müll?' WHERE `id`=46; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Neeeein, nicht schon wieder %item_link! RNG, bitte!' WHERE `id`=47; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Naja, ganz nutzlos ist %item_link immerhin nicht, oder?' WHERE `id`=48; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Noch ein %item_link? RNG, kümmert dich das überhaupt?' WHERE `id`=49; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Vielleicht ist %item_link in 10 Jahren was wert.' WHERE `id`=50; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich bewundere einfach mal, wie %item_link aussieht... und schmeiß es weg.' WHERE `id`=51; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer braucht %item_link? Ich sicher nicht.' WHERE `id`=52; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Vielleicht ist %item_link nützlich... haha, doch nur Müll.' WHERE `id`=53; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Das Universum schenkt mir %item_link. Toll.' WHERE `id`=54; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gut, ich hab %item_link. Begeistert bin ich nicht.' WHERE `id`=55; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Was soll ich überhaupt mit %item_link anfangen? Nicht mal verkaufbar.' WHERE `id`=56; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Packen wir %item_link zu meinem wachsenden Enttäuschungsstapel.' WHERE `id`=57; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Danke für %item_link... genau das, was ich nicht gebraucht hab.' WHERE `id`=58; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich hab so viele %item_link gelootet, ich könnte damit einen Laden aufmachen.' WHERE `id`=60; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ein glänzender %item_link... kommt wohl auf den Müllhaufen.' WHERE `id`=61; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Es ist %item_link, Leute. Nicht zu früh freuen.' WHERE `id`=62; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Naja, wenigstens ist es %item_link... könnte schlimmer sein.' WHERE `id`=63; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Lieber %item_link als gar nix.' WHERE `id`=64; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Noch ein %item_link. Nicht das Schlimmste.' WHERE `id`=65; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey, %item_link... ist schon okay, denk ich.' WHERE `id`=66; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey, %item_link. Nicht schlecht, nicht gut, aber ich nehme es.' WHERE `id`=67; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Könnte schlimmer sein... hab gerade %item_link gelootet.' WHERE `id`=68; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_link wird für irgendwas nützlich sein, denke ich.' WHERE `id`=69; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Neuer Tag, neuer %item_link. Kann mich nicht beschweren.' WHERE `id`=70; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Naja, %item_link geht klar, würd ich sagen.' WHERE `id`=71; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade %item_link bekommen. Nicht das Beste, aber etwas.' WHERE `id`=72; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_link ist gut genug, ich bin nicht sauer.' WHERE `id`=74; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kein Legendary, aber %item_link reicht.' WHERE `id`=75; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kein schlechter Drop... %item_link ist okay.' WHERE `id`=76; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Lieber %item_link als gar nix.' WHERE `id`=77; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Schon wieder %item_link. Besser als nix, denke ich.' WHERE `id`=79; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_link schon wieder, hm? Nicht begeistert, aber ich überlebe.' WHERE `id`=80; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hätte schlimmer kommen können, %item_link ist gar nicht so schlecht.' WHERE `id`=82; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich nehm %item_link, könnte schlimmer sein.' WHERE `id`=84; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_link gelootet, nix Besonderes, aber okay.' WHERE `id`=85; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Nicht das, was ich wollte, aber %item_link reicht fürs Erste.' WHERE `id`=86; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Noch ein %item_link. Nicht der Wahnsinn, aber geht klar.' WHERE `id`=87; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_link gelootet, könnte schlimmer sein.' WHERE `id`=88; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Naja, %item_link ist besser als ein Stich ins Auge.' WHERE `id`=89; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich nehm %item_link und zieh weiter.' WHERE `id`=90; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Nice! Ein solider %item_link für die Sammlung.' WHERE `id`=91; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Nicht schlecht, %item_link könnte nützlich werden.' WHERE `id`=93; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab mir %item_link geholt, jetzt gehts los.' WHERE `id`=94; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Dieser %item_link ist besser als die meisten, nehm ich.' WHERE `id`=95; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Mit %item_link kann ich was Gutes anfangen.' WHERE `id`=96; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Mit %item_link lässt sich vielleicht gut verdienen.' WHERE `id`=99; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ein solider %item_link, der Tag sieht gleich besser aus.' WHERE `id`=100; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab %item_link nicht erwartet, aber ich nehm es.' WHERE `id`=101; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kein Plan, was ich mit %item_link machen soll aber ich freue mich drüber.' WHERE `id`=102; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Nicht meine erste Wahl, aber %item_link nehm ich gern.' WHERE `id`=103; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Genau das hab ich heute gebraucht: %item_link.' WHERE `id`=104; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Noch ein %item_link. Nehm ich, keine Beschwerden.' WHERE `id`=106; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Immerhin hat %item_link irgendeinen Nutzen. Nicht der schlimmste Loot.' WHERE `id`=107; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_link ist vielleicht genau das, was ich heute brauche.' WHERE `id`=108; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich hab gerade %quest_link angenommen' WHERE `id`=109; -UPDATE `ai_playerbot_texts` SET `text_loc3`='gerade %quest_link angenommen' WHERE `id`=110; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link - versuch das mal abzuschließen' WHERE `id`=111; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link in %zone_name angenommen' WHERE `id`=112; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade %quest_link abgeholt, Zeit ranzuklotzen!' WHERE `id`=113; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link eingesackt, mal sehen, worum es geht.' WHERE `id`=114; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link geschnappt, Los gehts!' WHERE `id`=115; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Neue Quest, %quest_link. Los!' WHERE `id`=116; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Sieht so aus, als wäre jetzt %quest_link dran, wünscht mir Glück.' WHERE `id`=117; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link angenommen, mal sehen, wie hart die ist.' WHERE `id`=118; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link mitgenommen, Zeit für Fortschritt.' WHERE `id`=119; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade %quest_link genommen. Mal sehen, wie schwer die ist.' WHERE `id`=120; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link angenommen, hoffentlich kein Überraschungsboss am Ende.' WHERE `id`=121; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Unterwegs, %quest_link abzuschließen. Auf gehts!' WHERE `id`=122; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Dann mach ich jetzt wohl %quest_link. Hoffentlich lohnt sichs.' WHERE `id`=123; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link eingesackt. Weiter gehts!' WHERE `id`=124; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab gerade %quest_link angenommen. Sollte Kinderkram sein.' WHERE `id`=125; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link angenommen, jetzt rock ich das.' WHERE `id`=126; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link in %zone_name angenommen. Los gehts!' WHERE `id`=127; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Auf dem Weg, %quest_link zu beenden. Machen wirs schnell!' WHERE `id`=128; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link angenommen. Mal sehen, was in %zone_name auf mich wartet.' WHERE `id`=129; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link bekommen, hoffentlich dauert das nicht ewig.' WHERE `id`=130; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link geschnappt. Schon wieder von vorn.' WHERE `id`=131; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Es sieht so aus, als ob %quest_link als nächstes dran ist.' WHERE `id`=132; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Unterwegs, %quest_link abzuschließen. Hoffe, die macht Spaß.' WHERE `id`=133; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade %quest_link genommen. Zeit nachzuschauen, was mich erwartet.' WHERE `id`=136; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Nochmal %quest_link. Ziehen wirs schnell durch!' WHERE `id`=137; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich hab gerade %quest_link genommen. Das wird spaßig!' WHERE `id`=138; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link in %zone_name angenommen. Los!' WHERE `id`=139; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link geschnappt. Kann es kaum erwarten, was als Nächstes kommt.' WHERE `id`=140; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link genommen. Abtauchen und durchziehen.' WHERE `id`=141; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Alles klar, %quest_link ist aktiv. An die Arbeit!' WHERE `id`=142; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Weiter zu %quest_link. Hoffentlich nicht zu hart.' WHERE `id`=143; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade %quest_link geschnappt. Sollte easy sein!' WHERE `id`=144; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich hab %quest_link angenommen. Machen wirs fix!' WHERE `id`=145; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link genommen, mal schauen, worum es geht.' WHERE `id`=146; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade %quest_link angenommen, weiter gehts!' WHERE `id`=147; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Endlich fertig mit %quest_obj_name für %quest_link' WHERE `id`=148; -UPDATE `ai_playerbot_texts` SET `text_loc3`='endlich %quest_obj_available/%quest_obj_required von %quest_obj_name für %quest_link bekommen' WHERE `id`=149; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_obj_full_formatted für %quest_link, endlich' WHERE `id`=150; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Uff, %quest_obj_available/%quest_obj_required %quest_obj_name für %quest_link bekommen' WHERE `id`=151; -UPDATE `ai_playerbot_texts` SET `text_loc3`='brauche noch %quest_obj_missing von %quest_obj_name für %quest_link' WHERE `id`=152; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_obj_full_formatted, arbeite noch an %quest_link' WHERE `id`=153; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_obj_name für %quest_link fertig, was für ein Grind!' WHERE `id`=154; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kurz vor Abschluss von %quest_obj_name für %quest_link, fehlen nur noch %quest_obj_missing.' WHERE `id`=155; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_obj_name fast durch, brauche nur noch %quest_obj_missing für %quest_link.' WHERE `id`=156; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Endlich %quest_obj_name für %quest_link fertig! Das hat gedauert!' WHERE `id`=158; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Läuft, hab %quest_obj_available/%quest_obj_required von %quest_obj_name für %quest_link.' WHERE `id`=159; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Yes! %quest_obj_name für %quest_link abgeschlossen. Weiter gehts!' WHERE `id`=160; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Fortschritt bei %quest_obj_name, %quest_obj_available/%quest_obj_required erledigt für %quest_link.' WHERE `id`=161; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_obj_full_formatted... fast geschafft für %quest_link!' WHERE `id`=162; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche nur noch %quest_obj_missing %quest_obj_name für %quest_link, fast fertig!' WHERE `id`=163; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Endlich %quest_obj_name für %quest_link geschafft, was für eine Erleichterung!' WHERE `id`=164; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab %quest_obj_available/%quest_obj_required von %quest_obj_name für %quest_link, geht voran!' WHERE `id`=165; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_obj_name für %quest_link ist komplett! Weiter zur nächsten Aufgabe.' WHERE `id`=166; -UPDATE `ai_playerbot_texts` SET `text_loc3`='100% von %quest_obj_name für %quest_link erledigt, ab zu den Belohnungen!' WHERE `id`=167; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Es fehlen noch %quest_obj_missing von %quest_obj_name für %quest_link, fast fertig!' WHERE `id`=168; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Arbeite an %quest_obj_name für %quest_link, aktuell %quest_obj_available/%quest_obj_required erledigt.' WHERE `id`=169; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_obj_full_formatted für %quest_link. Das war ein langer Brocken!' WHERE `id`=170; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche nur noch ein paar %quest_obj_name für %quest_link, dann bin ich durch!' WHERE `id`=171; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Noch ein Ziel erledigt! %quest_obj_full_formatted für %quest_link' WHERE `id`=172; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Grinde weiter %quest_obj_name für %quest_link, mit %quest_obj_available/%quest_obj_required fast da.' WHERE `id`=173; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ziel erledigt! %quest_obj_full_formatted für %quest_link. Weiter zum nächsten!' WHERE `id`=174; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Endlich fertig mit %item_link für %quest_link' WHERE `id`=175; -UPDATE `ai_playerbot_texts` SET `text_loc3`='endlich %quest_obj_available/%quest_obj_required von %item_link für %quest_link bekommen' WHERE `id`=176; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_obj_full_formatted für %quest_link, endlich' WHERE `id`=177; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Uff, %quest_obj_available/%quest_obj_required %item_link für %quest_link bekommen' WHERE `id`=178; -UPDATE `ai_playerbot_texts` SET `text_loc3`='brauche noch %quest_obj_missing %item_link für %quest_link' WHERE `id`=179; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_obj_full_formatted, arbeite noch an %quest_link' WHERE `id`=180; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Es geht voran! %quest_obj_available/%quest_obj_required %item_link für %quest_link.' WHERE `id`=182; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_obj_name ist zur Hälfte durch, brauche noch %quest_obj_missing %item_link für %quest_link.' WHERE `id`=183; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Es fehlen noch %quest_obj_missing von %item_link für %quest_link, aber ich komme näher!' WHERE `id`=184; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Endlich %item_link für %quest_link fertig, weiter mit der nächsten Aufgabe!' WHERE `id`=185; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Fast da! %quest_obj_available/%quest_obj_required %item_link für %quest_link geholt.' WHERE `id`=186; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Yes! %item_link für %quest_link abgeschlossen. Weiter zum nächsten Ziel.' WHERE `id`=187; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Guter Fortschritt bei %item_link für %quest_link, brauche noch %quest_obj_missing.' WHERE `id`=189; -UPDATE `ai_playerbot_texts` SET `text_loc3`='100% von %item_link für %quest_link erledigt! Weiter gehts.' WHERE `id`=190; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab %quest_obj_available/%quest_obj_required %item_link für %quest_link. Das Ziel ist in Sicht!' WHERE `id`=191; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Geschafft, %item_link für %quest_link komplett. Was für eine Erleichterung!' WHERE `id`=192; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Arbeite hart an %item_link für %quest_link, brauche noch %quest_obj_missing.' WHERE `id`=193; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Fast fertig mit %item_link für %quest_link, es fehlen nur noch %quest_obj_missing.' WHERE `id`=194; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_link für %quest_link abgeschlossen, der Grind ist real!' WHERE `id`=195; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Arbeite weiter an %item_link für %quest_link, bisher %quest_obj_available/%quest_obj_required erledigt.' WHERE `id`=196; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_link für %quest_link kommt der Fertigstellung näher, %quest_obj_available/%quest_obj_required erledigt.' WHERE `id`=197; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Yes! %item_link für %quest_link fertig! Das hat etwas länger gedauert.' WHERE `id`=198; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %quest_obj_available/%quest_obj_required %item_link für %quest_link, läuft!' WHERE `id`=199; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Durch mit %item_link für %quest_link. Weiter zum nächsten!' WHERE `id`=200; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Noch eins abgehakt! %item_link für %quest_link ist durch!' WHERE `id`=201; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Grinde weiter %item_link für %quest_link, habe %quest_obj_available/%quest_obj_required geschafft.' WHERE `id`=202; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin fast durch! Brauche noch %quest_obj_missing %item_link für %quest_link.' WHERE `id`=203; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Nicht rechtzeitig %quest_link geschafft...' WHERE `id`=204; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Zeit für %quest_link abgelaufen :(' WHERE `id`=205; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Alle Ziele für %quest_link abgeschlossen' WHERE `id`=206; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Alle Ziele für %quest_link erledigt' WHERE `id`=207; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich gebe %quest_link gleich ab, habe gerade alle Ziele erledigt' WHERE `id`=208; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Jaaa, endlich %quest_link abgegeben' WHERE `id`=209; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hab %quest_link abgegeben' WHERE `id`=210; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab %quest_link geschafft, gerade abgegeben' WHERE `id`=211; -UPDATE `ai_playerbot_texts` SET `text_loc3`='gerade %quest_link abgegeben' WHERE `id`=212; -UPDATE `ai_playerbot_texts` SET `text_loc3`='gerade %quest_link in %zone_name abgegeben' WHERE `id`=213; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Noch eine Quest abgeschlossen! %quest_link abgegeben' WHERE `id`=214; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Mission erfüllt! %quest_link ist abgegeben' WHERE `id`=215; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Endlich, %quest_link abgegeben! Was für ein Ritt!' WHERE `id`=216; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Grade %quest_link abgeschlossen und abgegeben, fühlt sich gut an!' WHERE `id`=217; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link in %zone_name abgegeben. Weiter zur nächsten Herausforderung!' WHERE `id`=218; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab %quest_link abgegeben. Wieder eine weniger!' WHERE `id`=219; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link erfolgreich abgegeben, das hat gedauert!' WHERE `id`=220; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link in %zone_name abgeschlossen und abgegeben. Fühlt sich gut an!' WHERE `id`=221; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Quest fertig! %quest_link abgegeben und bereit für die nächste!' WHERE `id`=222; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Endlich %quest_link in %zone_name abgegeben. Die war knackig!' WHERE `id`=223; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Das wars, %quest_link abgegeben! Jetzt Belohnung einsacken!' WHERE `id`=224; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link fertig! Jetzt abgeben und weiter.' WHERE `id`=225; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link eben abgeschlossen und abgegeben. Weiter gehts!' WHERE `id`=228; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wieder eine im Kasten, %quest_link abgegeben' WHERE `id`=229; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link abgegeben! Zeit, die Belohnung abzuholen.' WHERE `id`=230; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Das ging fix! %quest_link schon abgegeben!' WHERE `id`=232; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade %quest_link in %zone_name abgegeben. Bereit fürs nächste Abenteuer!' WHERE `id`=233; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Mission komplett! %quest_link ist abgegeben!' WHERE `id`=235; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab gerade %quest_link beendet und abgegeben, weiter zur nächsten!' WHERE `id`=236; -UPDATE `ai_playerbot_texts` SET `text_loc3`='wieder %victim_name gelegt' WHERE `id`=237; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich leg ständig %victim_name um, nix Besonderes' WHERE `id`=238; -UPDATE `ai_playerbot_texts` SET `text_loc3`='noch ein %victim_name beißt ins Gras' WHERE `id`=239; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ein %victim_name weniger in %zone_name' WHERE `id`=240; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab diesen Elite-Bastard %victim_name umgehauen!' WHERE `id`=241; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Elite %victim_name in %zone_name gekillt' WHERE `id`=242; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Puh, %victim_name doch noch gelegt!' WHERE `id`=243; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gefällt dir das %victim_name?! Willst du, dass ich drei Wochen zurückspule, als du noch gelebt hast?!' WHERE `id`=244; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Yo, hab gerade %victim_name gekillt!' WHERE `id`=245; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Rare %victim_name in %zone_name gekillt' WHERE `id`=246; -UPDATE `ai_playerbot_texts` SET `text_loc3`='WTF hab ich da gerade umgehauen? %victim_name' WHERE `id`=247; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade das Pet %victim_name gekillt' WHERE `id`=248; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Oh ja, hab gerade %victim_name gekillt' WHERE `id`=249; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%victim_name in %zone_name gekillt' WHERE `id`=250; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wieder %victim_name erlegt' WHERE `id`=251; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich leg ständig %victim_name um, nix Besonderes' WHERE `id`=252; -UPDATE `ai_playerbot_texts` SET `text_loc3`='noch ein %victim_name beißt ins Gras' WHERE `id`=253; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ein %victim_name weniger in %zone_name' WHERE `id`=254; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%victim_name so nebenbei umgehauen' WHERE `id`=255; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%victim_name hatte keine Chance' WHERE `id`=256; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Schon wieder %victim_name aus dem Weg geräumt' WHERE `id`=257; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%victim_name war zu easy, weiter gehts' WHERE `id`=258; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Und wieder %victim_name plattgemacht. Kinderkram' WHERE `id`=259; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%victim_name war gar nicht so tough' WHERE `id`=260; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%victim_name liegt, nix Besonderes' WHERE `id`=261; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Das war zu easy, %victim_name liegt' WHERE `id`=262; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%victim_name ist so schnell umgekippt, einmal blinzeln und vorbei' WHERE `id`=263; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Diesen Elite %victim_name umgehauen!' WHERE `id`=264; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Elite %victim_name existiert nicht mehr' WHERE `id`=265; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Den Elite %victim_name weggemacht wie ein Boss' WHERE `id`=266; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Elite-Mob %victim_name, hat keine Chance gegen mich!' WHERE `id`=267; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Endlich den mächtigen %victim_name gelegt' WHERE `id`=268; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Elite %victim_name in %zone_name gekillt' WHERE `id`=269; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab gerade Elite %victim_name in %zone_name erwischt' WHERE `id`=270; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Elite %victim_name gekillt. Viel zu easy' WHERE `id`=271; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%victim_name in %zone_name umgehauen, was ein Kampf' WHERE `id`=272; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Elite %victim_name gekillt. Ich scheine unaufhaltbar zu sein' WHERE `id`=273; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Puh, %victim_name doch noch gelegt!' WHERE `id`=274; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Seltener Elite %victim_name liegt' WHERE `id`=275; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%victim_name gibt es nicht mehr. Noch ein seltener Elite gelegt' WHERE `id`=276; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Seltener Elite %victim_name down. Ich bin aus Feuer!' WHERE `id`=277; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab gerade den seltenen Elite %victim_name gekillt' WHERE `id`=278; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Das war geil! %victim_name gerade gekillt! Was ein Kampf' WHERE `id`=279; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Weltenboss %victim_name ist down! Das war episch' WHERE `id`=280; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab den Weltenboss %victim_name gelegt. Zeit zum Feiern!' WHERE `id`=281; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%victim_name gekillt! Der Weltenboss war kein Spaß!' WHERE `id`=282; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade %victim_name gekillt, das war legendär' WHERE `id`=283; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Yo, hab gerade %victim_name gekillt!' WHERE `id`=284; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Seltener %victim_name down. War ein geiler Fight' WHERE `id`=285; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab gerade den seltenen %victim_name gekillt' WHERE `id`=286; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Seltener %victim_name in %zone_name besiegt' WHERE `id`=287; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade seltenen %victim_name in %zone_name gekillt. Das ging fix!' WHERE `id`=288; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Seltenen %victim_name in %zone_name gekillt' WHERE `id`=289; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Den seltenen %victim_name in %zone_name erwischt' WHERE `id`=290; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab %victim_name in %zone_name gekillt. Das tat gut' WHERE `id`=291; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%victim_name in %zone_name umgehauen. Keine Chance gehabt' WHERE `id`=292; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade %victim_name in %zone_name gekillt, easy' WHERE `id`=293; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Was hab ich da gerade gekillt? %victim_name' WHERE `id`=294; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Was ist das für ein %victim_name? Sowas hab ich noch nie gesehen' WHERE `id`=295; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab gerade was Seltsames gekillt, %victim_name' WHERE `id`=296; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%victim_name war seltsam, aber liegt trotzdem' WHERE `id`=297; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade %victim_name besiegt' WHERE `id`=298; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Machs gut, %victim_name. Warst nur ein Pet' WHERE `id`=299; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab gerade das Pet %victim_name umgehauen. Das kam unerwartet.' WHERE `id`=300; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%victim_name liegt. Vielleicht nächstes Mal, Pet' WHERE `id`=301; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ding! %my_level' WHERE `id`=302; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Jaaa, ich bin Level %my_level!' WHERE `id`=303; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade aufgelevelt' WHERE `id`=304; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Stufe %my_level!!! Jetzt kann ich Endgame-Content machen' WHERE `id`=309; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Frisch auf Level %my_level, %my_class!!!' WHERE `id`=310; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Noch ein Level: %my_level, %my_race %my_class!' WHERE `id`=311; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ding! Wieder ein Level up!' WHERE `id`=312; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Jetzt offiziell Level %my_level!' WHERE `id`=313; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level, fühl mich jetzt schon stärker!' WHERE `id`=314; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Aufwärts! Level %my_level erreicht' WHERE `id`=315; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Das ging fix, schon Level %my_level!' WHERE `id`=316; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade Level %my_level erreicht, auf gehts!' WHERE `id`=317; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level, jetzt kommen die großen Sachen!' WHERE `id`=318; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level, ich bin jetzt on fire!' WHERE `id`=319; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level voll und weiter!' WHERE `id`=320; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin jetzt Level %my_level, was kommt als Nächstes?' WHERE `id`=321; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level!!! Es passiert!' WHERE `id`=322; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade Level %my_level erreicht, fühlt sich mega an!' WHERE `id`=323; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level und ich fange erst an!' WHERE `id`=324; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wow, schon Level %my_level, unaufhaltbar!' WHERE `id`=325; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Neue Höhen mit Level %my_level!!!' WHERE `id`=326; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level und weiter Vollgas!' WHERE `id`=327; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level erreicht, fühlt sich so gut an!' WHERE `id`=329; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin jetzt Level %my_level! Los gehts!' WHERE `id`=330; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level, noch viel vor mir!' WHERE `id`=331; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Endlich Level %my_level erreicht, ich kann alles!' WHERE `id`=332; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Maxlevel %my_level, Endgame ich komme!' WHERE `id`=333; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Endgame freigeschaltet auf Level %my_level, her damit!' WHERE `id`=335; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab Level %my_level erreicht, Zeit zu glänzen!' WHERE `id`=336; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level, lasst uns Endgame-Raids machen!' WHERE `id`=337; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Mit Level %my_level hält mich nix mehr auf!' WHERE `id`=338; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level, bereit für High-Level-Content!' WHERE `id`=339; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Yes! Level %my_level, %my_class für alles bereit!' WHERE `id`=340; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level, Zeit die Welt herauszufordern!' WHERE `id`=341; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level erreicht, schnappen wir uns die Elitebosse!' WHERE `id`=342; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Jetzt bin ich Level %my_level, das echte Abenteuer beginnt!' WHERE `id`=343; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level! Zeit für ernsthafte Action!' WHERE `id`=344; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Endlich auf Level %my_level! Jetzt zeige ich, was ich kann!' WHERE `id`=346; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level ist da! Bereit für die große Liga!' WHERE `id`=347; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade Level %my_level erreicht, macht euch bereit für echte Herausforderungen!' WHERE `id`=348; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level voll, Zeit für epischen Content!' WHERE `id`=349; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Frisch auf Level %my_level! Bereit für Endgame-Inis!' WHERE `id`=350; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Jetzt, wo ich Level %my_level bin, ist nichts mehr außerhalb meiner Reichweite!' WHERE `id`=351; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Jetzt ist es offiziell! Level %my_level, Zeit zu dominieren!' WHERE `id`=352; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level geschafft! Zeit, der Welt meinen Stempel aufzudrücken!' WHERE `id`=353; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level freigeschaltet! Jetzt beginnt der echte Spaß!' WHERE `id`=354; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level, und die Welt ist mein Spielplatz!' WHERE `id`=355; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level, bereit, der Welt meine Power zu zeigen!' WHERE `id`=356; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade %my_level erreicht, jetzt geh ich all in!' WHERE `id`=357; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level, endlich kann ich alles angehen!' WHERE `id`=358; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level, fühl mich unaufhaltbar!' WHERE `id`=359; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade Level %my_level erreicht, kanns kaum erwarten ins Endgame zu springen!' WHERE `id`=360; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gute Arbeit, %other_name. Das hast du dir verdient.' WHERE `id`=361; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Das war mies, %other_name. Ich mags nicht, aber...' WHERE `id`=362; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Glückwunsch, %other_name, du hast den Aufstieg verdient!' WHERE `id`=363; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verdient, %other_name - willkommen im nächsten Level!' WHERE `id`=364; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Beförderungszeit! %other_name, die hast du dir verdient!' WHERE `id`=366; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Neue Rolle für %other_name! Gut gemacht, weiter so!' WHERE `id`=367; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Dickes Gratz an %other_name, wohlverdiente Beförderung!' WHERE `id`=368; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Applaus für %other_name zur Beförderung!' WHERE `id`=369; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gratulation an %other_name zur Beförderung! Weiter so!' WHERE `id`=370; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gratz, %other_name, du steigst auf! Gut gemacht!' WHERE `id`=371; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Noch ein Schritt nach oben für %other_name! Glückwunsch!' WHERE `id`=372; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Harte Entscheidung, aber %other_name, du wurdest degradiert. Zeit, dich zu beweisen!' WHERE `id`=373; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Sorry, %other_name, aber es gibt eine Degradierung. Komm stärker zurück!' WHERE `id`=374; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Leider, %other_name, diesmal eine Degradierung. Beim nächsten Mal packst du es!' WHERE `id`=375; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Schwere Entscheidung, aber %other_name, du wurdest degradiert. Zeit, dich wieder hochzugrinden!' WHERE `id`=376; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Unschön, aber %other_name, vorerst eine Degradierung.' WHERE `id`=377; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Sorry, %other_name, dein Rang wurde gesenkt. Lass uns zusammen härter arbeiten!' WHERE `id`=378; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Eine Degradierung für %other_name... Zeit, sich zu fokussieren und wieder hochzukommen!' WHERE `id`=379; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Nicht das, was wir wollten, aber %other_name, du wurdest degradiert. Lern draus und komm stärker zurück!' WHERE `id`=380; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Harter Moment für %other_name, aber wir wissen, du kommst von der Degradierung zurück!' WHERE `id`=381; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Sorry, %other_name, aber du wurdest degradiert. Zeit für Verbesserungen!' WHERE `id`=382; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wir wollten das nicht für %other_name, aber es gab eine Degradierung. Zeit, besser zu werden!' WHERE `id`=383; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Frisches Blut gesucht! Tritt der Gilde bei, sei Teil von was Großem!' WHERE `id`=384; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wir rekrutieren neue Mitglieder! Wenn du bereit bist, schließ dich an - die Gildentür steht offen!' WHERE `id`=385; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Die Gilde sucht fähige Abenteurer. Schließ dich an und geh mit uns ab!' WHERE `id`=386; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bock auf eine starke Gilde? Wir suchen neue Member! Komm rein!' WHERE `id`=387; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Die Gilde wächst! Join uns für fette Abenteuer und Loot!' WHERE `id`=388; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat Bock auf %instance_name?' WHERE `id`=389; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gibts Gruppen für %instance_name?' WHERE `id`=390; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauch jemand Hilfe für %instance_name?' WHERE `id`=391; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche Gruppe: %instance_name.' WHERE `id`=392; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht wer %my_role für %instance_name?' WHERE `id`=393; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Fehlt %my_role für %instance_name?' WHERE `id`=394; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kann %my_role für %instance_name sein.' WHERE `id`=395; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht ihr %my_role für %instance_name?' WHERE `id`=397; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht wer Gear aus %instance_name?' WHERE `id`=398; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kleiner Grind in %instance_name?' WHERE `id`=399; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Lust auf %instance_name' WHERE `id`=400; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bock auf %instance_name.' WHERE `id`=402; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%my_role sucht Gruppe für %instance_name.' WHERE `id`=403; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Was ist mit %instance_name?' WHERE `id`=404; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Rein in %instance_name?' WHERE `id`=406; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche Gruppe für %instance_name.' WHERE `id`=407; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht wer Hilfe bei Quests in %instance_name?' WHERE `id`=408; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bock auf Quests in %instance_name.' WHERE `id`=409; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat Quests in %instance_name?' WHERE `id`=410; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%my_role: Platz in Gruppe für %instance_name?' WHERE `id`=412; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Läuft überhaupt noch jemand %instance_name?' WHERE `id`=413; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%instance_name: nimmt wer einen %my_role mit?' WHERE `id`=414; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Lohnt es sich als %my_role in %instance_name?' WHERE `id`=415; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Lohnt es sich echt, nach %instance_name zu gehen?' WHERE `id`=416; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Die Bosse in %instance_name droppen gutes Gear. Bock zu grinden?' WHERE `id`=418; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Was ist mit %instance_name?' WHERE `id`=419; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht jemand %my_role?' WHERE `id`=420; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht wer %my_role?' WHERE `id`=421; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat Bock auf %instance_name?' WHERE `id`=422; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann mich jemand bei %instance_name beschwören?' WHERE `id`=423; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Trefft mich in %instance_name' WHERE `id`=424; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bock auf schnellen %instance_name Run' WHERE `id`=425; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bock auf kompletten %instance_name Run' WHERE `id`=426; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wie oft wart ihr schon in %instance_name?' WHERE `id`=427; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Noch ein %instance_name Run?' WHERE `id`=428; -UPDATE `ai_playerbot_texts` SET `text_loc3`='In %instance_name gewiped? Nehmt lieber mich!' WHERE `id`=429; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Nehmt mich bitte mit nach %instance_name.' WHERE `id`=430; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer nimmt %my_role mit nach %instance_name?' WHERE `id`=433; -UPDATE `ai_playerbot_texts` SET `text_loc3`='LFG %instance_name, bin %my_role' WHERE `id`=434; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%my_role LFG %instance_name' WHERE `id`=435; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche einen DD für %instance_name' WHERE `id`=437; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche einen Heiler für %instance_name' WHERE `id`=438; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will %instance_name machen. Brauche eine Gruppe!' WHERE `id`=440; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche noch einen für %instance_name' WHERE `id`=442; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will jemand mit nach %instance_name?' WHERE `id`=443; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Interesse an %instance_name?' WHERE `id`=444; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche einen schnellen Run durch %instance_name' WHERE `id`=445; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer ist dabei für %instance_name?' WHERE `id`=446; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wollt ihr %instance_name farmen?' WHERE `id`=448; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will %instance_name abschließen' WHERE `id`=449; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche Carry durch %instance_name' WHERE `id`=451; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Laufe %instance_name, suche andere' WHERE `id`=453; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche jemanden, der bei %instance_name hilft' WHERE `id`=455; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche %my_role für %instance_name!' WHERE `id`=456; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bock auf einen %instance_name Run?' WHERE `id`=457; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche %my_role für %instance_name Run' WHERE `id`=458; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will jemand Gear aus %instance_name farmen?' WHERE `id`=459; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche Gruppe für %instance_name Speedrun' WHERE `id`=460; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche Gruppe, um %instance_name zu clearen' WHERE `id`=461; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Interesse, %instance_name zu farmen?' WHERE `id`=462; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche etwas Gear aus %instance_name' WHERE `id`=463; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat Lust auf einen schnellen %instance_name Run?' WHERE `id`=464; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will jemand %instance_name zusammen angehen?' WHERE `id`=465; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche Leute, um %instance_name abzuschließen' WHERE `id`=466; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kriegen wir eine Gruppe für %instance_name zusammen?' WHERE `id`=467; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche einen schnellen Run für %instance_name' WHERE `id`=468; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat Lust, %instance_name zusammen zu farmen?' WHERE `id`=469; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche noch ein paar Leute für %instance_name' WHERE `id`=470; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht jemand Hilfe bei den Mechaniken in %instance_name?' WHERE `id`=471; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche schnell eine Gruppe für %instance_name' WHERE `id`=472; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will jemand %instance_name als Speedrun laufen?' WHERE `id`=473; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche einen schnellen Abschluss von %instance_name' WHERE `id`=474; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche jemanden, der bei %instance_name mitkommt' WHERE `id`=475; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat Lust, %instance_name zu clearen?' WHERE `id`=476; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche Hilfe beim Endboss von %instance_name' WHERE `id`=477; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat heute Lust, %instance_name zu laufen?' WHERE `id`=478; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche schnellen Run in %instance_name, um etwas Gear zu farmen' WHERE `id`=479; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will jemand Mats aus %instance_name farmen?' WHERE `id`=480; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Lass uns eine Gruppe für %instance_name machen' WHERE `id`=481; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht wer Hilfe bei %quest_link?' WHERE `id`=482; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will jemand %quest_link teilen?' WHERE `id`=483; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Macht jemand %quest_link?' WHERE `id`=484; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bock auf %quest_link.' WHERE `id`=485; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hilft bei %quest_link?' WHERE `id`=487; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht jemand Unterstützung bei %quest_link?' WHERE `id`=488; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Interesse an %quest_link?' WHERE `id`=489; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will jemand %quest_link abschließen?' WHERE `id`=490; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht wer ein Team für %quest_link?' WHERE `id`=491; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat Bock auf %quest_link?' WHERE `id`=492; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will %quest_link machen. Wer ist dabei?' WHERE `id`=497; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer ist bereit für %quest_link?' WHERE `id`=499; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer will bei %quest_link mitmachen?' WHERE `id`=500; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer ist bei %quest_link dabei?' WHERE `id`=501; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche hier etwas Hilfe bei %quest_link.' WHERE `id`=502; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer ist bereit, %quest_link zu machen?' WHERE `id`=504; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche etwas Hilfe für %quest_link.' WHERE `id`=505; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht jemand Hilfe bei %quest_link?' WHERE `id`=506; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat Lust, %quest_link abzuschließen?' WHERE `id`=507; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht jemand Unterstützung bei %quest_link?' WHERE `id`=508; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer kann bei %quest_link helfen?' WHERE `id`=509; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche eine helfende Hand bei %quest_link.' WHERE `id`=510; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ist jemand bereit für %quest_link?' WHERE `id`=511; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann jemand bei %quest_link dazukommen?' WHERE `id`=512; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche ein paar Mitstreiter für %quest_link.' WHERE `id`=513; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will jemand sich für %quest_link zusammentun?' WHERE `id`=514; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer muss %quest_link noch abschließen?' WHERE `id`=516; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will %quest_link abschließen.' WHERE `id`=517; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer kann bei %quest_link mitkommen?' WHERE `id`=518; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Interesse, %quest_link abzuschließen?' WHERE `id`=519; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bock, bei %quest_link zu helfen?' WHERE `id`=520; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ist jemand bereit, bei %quest_link zu helfen?' WHERE `id`=521; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche jemanden, um %quest_link zu beenden.' WHERE `id`=522; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will jemand %quest_link beenden?' WHERE `id`=523; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gibt’s Hilfe für %quest_link?' WHERE `id`=524; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer ist dabei für %quest_link?' WHERE `id`=525; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche Partner zum Abschließen von %quest_link.' WHERE `id`=526; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat Lust, %quest_link zu beenden?' WHERE `id`=527; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Heute jemand Interesse an %quest_link?' WHERE `id`=529; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat jetzt Bock auf %quest_link?' WHERE `id`=531; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will jemand %quest_link starten?' WHERE `id`=532; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bitte um Hilfe bei %quest_link.' WHERE `id`=533; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer kann mir bei %quest_link helfen?' WHERE `id`=534; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche Gruppe für %quest_link.' WHERE `id`=535; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Jemand bereit zum %category farmen?' WHERE `id`=536; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche Hilfe beim %category farmen.' WHERE `id`=537; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%category sind verdammt teuer!' WHERE `id`=538; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will %category.' WHERE `id`=539; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kaufe: %category.' WHERE `id`=541; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Interesse an %category?' WHERE `id`=542; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe: %category.' WHERE `id`=543; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will %category farmen.' WHERE `id`=546; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche Gruppe fürs %category farmen.' WHERE `id`=547; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%category sind willkommen.' WHERE `id`=548; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kaufe alles von %category.' WHERE `id`=549; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wow, alle farmen %category!' WHERE `id`=550; -UPDATE `ai_playerbot_texts` SET `text_loc3`='AH ist heiß auf %category.' WHERE `id`=552; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will etwas %category tauschen.' WHERE `id`=554; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Etwas %category bitte?' WHERE `id`=558; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hätte wohl den Skill für %category lernen sollen.' WHERE `id`=559; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich geiere auf %category.' WHERE `id`=560; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Für %category würden die Leute töten.' WHERE `id`=561; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%category ist ein Top-Deal!' WHERE `id`=562; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wo farmt man am besten %category?' WHERE `id`=564; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin ready für %category.' WHERE `id`=565; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich behalte meine ganzen %category wohl lieber.' WHERE `id`=567; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauchst du eine Gruppe? Vielleicht zum %category farmen?' WHERE `id`=568; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Denke immer noch über %category nach.' WHERE `id`=569; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab schon von %category gehört, aber meine Taschen sind leer.' WHERE `id`=570; -UPDATE `ai_playerbot_texts` SET `text_loc3`='LFG für %category' WHERE `id`=571; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Würde mich der Verkauf von %category reich machen?' WHERE `id`=572; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Okay. Ich farme %category morgen.' WHERE `id`=573; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab mindestens zehn Leute gesehen, die %category farmen.' WHERE `id`=575; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab gestern all meine %category verkauft. Bin komplett pleite!' WHERE `id`=576; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will einer Gilde joinen, die %category farmt.' WHERE `id`=577; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %category, wer hat Interesse?' WHERE `id`=580; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %category, flüster mir.' WHERE `id`=582; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche %category, verkauft das jemand?' WHERE `id`=584; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab %category zu verkaufen, meldet euch!' WHERE `id`=585; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %category, bester Preis am Markt.' WHERE `id`=588; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche %category im Tausch gegen %category.' WHERE `id`=589; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer braucht %category? Hab was zu verkaufen!' WHERE `id`=591; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Habe %category zu verkaufen, flüster mir für den Preis.' WHERE `id`=592; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will %category verkaufen, flüster mir für Details.' WHERE `id`=593; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kaufe: %category. Verkauft das jemand?' WHERE `id`=594; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche %category, bitte melden.' WHERE `id`=595; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat Interesse an %category?' WHERE `id`=596; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann jemand beim %category farmen helfen?' WHERE `id`=597; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will %category kaufen, flüster mir einfach.' WHERE `id`=599; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche %category. Wer hat was?' WHERE `id`=600; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kaufe: %category zum fairen Preis.' WHERE `id`=601; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche dringend %category, hat wer was?' WHERE `id`=602; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab %category, verkaufe günstig.' WHERE `id`=603; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %category zum guten Kurs.' WHERE `id`=604; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche %category, verkauft wer günstig?' WHERE `id`=605; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkauft jemand %category? Hätte Interesse.' WHERE `id`=606; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche %category, flüstert mir wenn ihr verkauft.' WHERE `id`=607; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hat wer %category übrig zum Tauschen?' WHERE `id`=608; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hat wer Interesse, %category zu tauschen?' WHERE `id`=610; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %category, meldet euch für Tausch.' WHERE `id`=611; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kaufe: %category, suche guten Kurs.' WHERE `id`=612; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer braucht %category? Hab was übrig.' WHERE `id`=613; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will wer %category? Verkaufe billig.' WHERE `id`=614; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche %category, verkauft wer was?' WHERE `id`=615; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht jemand %category? Hab was zum Tauschen.' WHERE `id`=616; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kaufe: %category. Ist jemand günstiger als AH?' WHERE `id`=617; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %category billig, flüster mir für Details.' WHERE `id`=618; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht wer %category? Hab was zu verkaufen.' WHERE `id`=619; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Tausche %category gegen was anderes.' WHERE `id`=620; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab %category, tausche gegen %category.' WHERE `id`=621; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kaufe: %category, hat wer was übrig?' WHERE `id`=622; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Farmt wer Ruf bei %faction?' WHERE `id`=623; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann wer bei %faction helfen?' WHERE `id`=624; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab Bock auf Quests für %faction.' WHERE `id`=625; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%faction ist die beste Fraktion.' WHERE `id`=626; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Fehlt nur noch ein bisschen bis %rep_level bei %faction.' WHERE `id`=627; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hat jemand %rep_level bei %faction?' WHERE `id`=628; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer will %rep_level bei %faction sein?' WHERE `id`=629; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich werde nie %rep_level bei %faction sein.' WHERE `id`=630; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Fehlt jemandem Ruf bei %faction?' WHERE `id`=631; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann beim Ruf farmen für %faction helfen.' WHERE `id`=632; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%faction: brauche %rndK bis %rep_level.' WHERE `id`=634; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer kann Quests von %faction teilen?' WHERE `id`=635; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gibts Inis für %faction?' WHERE `id`=636; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab Bock, Ruf bei %faction zu grinden.' WHERE `id`=637; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Lasst uns Ruf bei %faction farmen!' WHERE `id`=638; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Farme Ruf bei %faction.' WHERE `id`=639; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bock, für %faction zu farmen.' WHERE `id`=640; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche Hilfe bei %faction.' WHERE `id`=641; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkauft %faction was Nützliches?' WHERE `id`=642; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer farmt für %faction?' WHERE `id`=644; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wie farmt man %faction am besten?' WHERE `id`=645; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich hasse den Rufgrind für %faction.' WHERE `id`=646; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich hab %faction so satt.' WHERE `id`=647; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gehen wir %faction an?' WHERE `id`=648; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Scheint, als wenn alle %rep_level bei %faction sind. Nur ich hänge wie immer hinterher.' WHERE `id`=649; -UPDATE `ai_playerbot_texts` SET `text_loc3`='LFG für Rufgrind bei %faction?' WHERE `id`=650; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann jemand einen guten Spot für Rufgrind bei %faction empfehlen?' WHERE `id`=651; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bringt mir Ruf bei %faction überhaupt was?' WHERE `id`=652; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hätte gedacht, dass Ruf bei %faction doch noch nützlich ist...' WHERE `id`=653; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Lohnt es sich, meinen Ruf bei %faction zu pushen?' WHERE `id`=655; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Was ist besser für %faction? Quests oder Mobs grinden?' WHERE `id`=656; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Grinde euren Ruf bei %faction, gebt mir nur etwas Gold.' WHERE `id`=657; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich glaube, Ruf bei %faction zu grinden dauert ewig.' WHERE `id`=658; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kill jeden Tag für %faction, bin aber noch weit weg von %rep_level.' WHERE `id`=659; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Sinken die AH-Gebühren auf %my_level?' WHERE `id`=660; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wie viele Fraktionen hast du auf ehrfürchtig?' WHERE `id`=661; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verdammt. Meine Gilde hat gestern ohne mich gut Ruf bei %faction gegrindet.' WHERE `id`=663; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Keiner will mir helfen, weil ich schon %rep_level bei %faction bin.' WHERE `id`=664; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bleib bitte weg von %faction.' WHERE `id`=665; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Farmt heute jemand Ruf bei %faction?' WHERE `id`=666; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Helft mir, Ruf bei %faction zu grinden!' WHERE `id`=667; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich brauch so dringend %rep_level bei %faction!' WHERE `id`=668; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Lohnt sich der Grind für %faction?' WHERE `id`=669; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kennt jemand gute Spots für Ruf bei %faction?' WHERE `id`=670; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich liebe es, Ruf bei %faction zu grinden!' WHERE `id`=671; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche Hilfe, um den Rufgrind bei %faction abzuschließen!' WHERE `id`=672; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Nur noch ein paar Stufen bis %rep_level bei %faction!' WHERE `id`=673; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat heute Bock auf Grind für %faction?' WHERE `id`=674; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche Mitstreiter für Rufgrind bei %faction!' WHERE `id`=675; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich bin das Rufgrinden für %faction leid!' WHERE `id`=676; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Noch wer auf Rufgrind bei %faction?' WHERE `id`=677; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Zeit, den Rufgrind bei %faction richtig anzugehen!' WHERE `id`=678; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Helft mir, den Ruf bei %faction fertig zu machen!' WHERE `id`=679; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Mir fehlen nur noch %rndK für den %faction Ruf!' WHERE `id`=680; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Den ganzen Tag Ruf bei %faction am farmen!' WHERE `id`=681; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Rufgrind bei %faction endet nie!' WHERE `id`=683; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hilfe bei den Daily-Quests von %faction?' WHERE `id`=684; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer ist grad beim Grind für %faction?' WHERE `id`=685; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Lasst uns Quests von %faction zusammen machen!' WHERE `id`=686; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Farmt heute jemand Ruf bei %faction?' WHERE `id`=687; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche Hilfe beim Ruf bei %faction für %rep_level!' WHERE `id`=688; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer will mit mir Ruf bei %faction grinden?' WHERE `id`=689; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kommt mit zum Rufgrind bei %faction!' WHERE `id`=690; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Läuft bei mir beim Rufgrind für %faction!' WHERE `id`=691; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will jemand Boosts für den Rufgrind bei %faction?' WHERE `id`=692; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bei %faction fehlt nicht mehr viel bis %rep_level, helft mir doch mal!' WHERE `id`=694; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bock, mir beim Rufgrind für %faction zu helfen?' WHERE `id`=695; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Farme wieder Ruf bei %faction!' WHERE `id`=696; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kennt jemand einen guten Spot für Ruf bei %faction?' WHERE `id`=697; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wie viel fehlt noch beim Rufgrind für %faction?' WHERE `id`=698; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Macht jemand Quests von %faction mit mir?' WHERE `id`=699; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche jemand für Rufgrind bis %rep_level bei %faction!' WHERE `id`=700; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Mir fehlen nur noch %rndK Ruf bei %faction!' WHERE `id`=701; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Der Grind für %faction ist brutal!' WHERE `id`=702; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Farmt jemand Ruf bei %faction für %rep_level?' WHERE `id`=703; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Lasst uns den Rufgrind bei %faction zusammen machen!' WHERE `id`=704; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Mehr Ruf bei %faction, mehr Belohnungen!' WHERE `id`=705; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Farme Ruf bei %faction als %my_class' WHERE `id`=706; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hängt noch jemand bei %faction auf %rep_level fest?' WHERE `id`=707; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Die ganze Nacht Rufgrind bei %faction!' WHERE `id`=708; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer will den Rufgrind bei %faction mit mir abschließen?' WHERE `id`=709; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kann beim Rufgrind für %faction helfen!' WHERE `id`=710; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bereit für mehr Rufgrind bei %faction!' WHERE `id`=711; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bock zu helfen beim Ruf für %faction? Ich brauch das dringend!' WHERE `id`=712; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich brauche noch %rndK für den Ruf bei %faction!' WHERE `id`=713; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Farmt mit mir für Ruf bei %faction!' WHERE `id`=714; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Mir fehlen nur noch %rndK Ruf bei %faction!' WHERE `id`=715; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Der Rufgrind bei %faction nimmt kein Ende...' WHERE `id`=716; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hat jemand Items für Rufboost bei %faction?' WHERE `id`=717; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Farmt jemand Ruf bei %faction für Mounts?' WHERE `id`=719; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Chance, mit mir Ruf bei %faction zu farmen?' WHERE `id`=720; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich bin so kurz vor %rep_level bei %faction!' WHERE `id`=721; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Helft mir, %rep_level bei %faction zu erreichen!' WHERE `id`=722; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich bin dabei, den %faction Rufgrind abzuschließen!' WHERE `id`=723; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche Hilfe beim Rufgrind für %faction!' WHERE `id`=724; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer kann mit mir Ruf bei %faction farmen?' WHERE `id`=726; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Rufgrind bei %faction non-stop!' WHERE `id`=727; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer grindet gerade noch Ruf bei %faction?' WHERE `id`=728; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will jemand zum Spaß Ruf bei %faction farmen?' WHERE `id`=729; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich muss noch Ruf bei %faction farmen, um neue Quests freizuschalten!' WHERE `id`=730; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bock, Ruf bei %faction für Belohnungen zu grinden!' WHERE `id`=731; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Tag für Tag, Rufgrind bei %faction!' WHERE `id`=732; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will jemand beim Rufgrind für %faction helfen?' WHERE `id`=733; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Helft mir, mit Ruf bei %faction Belohnungen freizuschalten!' WHERE `id`=734; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich grinde Ruf bei %faction für Mounts!' WHERE `id`=735; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kommt mit, Ruf bei %faction farmen!' WHERE `id`=736; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Beim Rufgrind für %faction bin ich fast da!' WHERE `id`=737; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Grinde Ruf bei %faction mit meinen Leuten!' WHERE `id`=738; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab Bock auf Party in %zone_name.' WHERE `id`=739; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Sucht jemand %my_role?' WHERE `id`=740; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%my_role sucht Gilde.' WHERE `id`=741; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%my_role will einer guten Gilde beitreten.' WHERE `id`=743; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Fühlt sich noch wer einsam?' WHERE `id`=745; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer will was abhaben?' WHERE `id`=747; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kommt her und holt mich!' WHERE `id`=748; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Macht hier grade jemand etwas?' WHERE `id`=750; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%zone_name: ist hier jemand?' WHERE `id`=751; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%zone_name: Wo sind alle?' WHERE `id`=752; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Scheint als wäre ich allein in %zone_name.' WHERE `id`=753; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Trefft mich in %zone_name.' WHERE `id`=754; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%zone_name ist der beste Spot!' WHERE `id`=756; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will nach %zone_name. Kommt wer mit?' WHERE `id`=757; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich mag %zone_name nicht, aber wohin sonst?' WHERE `id`=759; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gibt’s gute Quests in %zone_name?' WHERE `id`=760; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%zone_name ist der übelste Spot.' WHERE `id`=764; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Schnappt mich in %zone_name!' WHERE `id`=765; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ab nach %zone_name!' WHERE `id`=766; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bock zu questen in %zone_name' WHERE `id`=767; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hat wer Quests in %zone_name?' WHERE `id`=768; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kommt her nach %zone_name!' WHERE `id`=769; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Sieht so aus, als wär keine Horde in %zone_name' WHERE `id`=770; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Sieht so aus, als wär keine Allianz in %zone_name' WHERE `id`=771; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich bin echt müde von %zone_name. Vielleicht sollte ich woanders hin?' WHERE `id`=772; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich will nach Hause und dann in die Kiste.' WHERE `id`=774; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Weiß wer, was man fürs Beidhändig-Kämpfen braucht?' WHERE `id`=775; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hi zusammen!' WHERE `id`=776; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%zone_name ist gemütlich' WHERE `id`=777; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich fühl mich super' WHERE `id`=778; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich ignoriere Leute nicht, ich trolle sie, bis sie mich ignorieren' WHERE `id`=779; -UPDATE `ai_playerbot_texts` SET `text_loc3`='schön zu sehen, dass der Chat sich noch erinnert' WHERE `id`=781; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wie jede Waffe ist es Jäger-BiS' WHERE `id`=782; -UPDATE `ai_playerbot_texts` SET `text_loc3`='für mich geht es im Spiel nur ums Solo-Spielen und darum, neue Wege zu finden, Dinge solo zu machen' WHERE `id`=783; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ich habe NIEMANDEN jemals abgezockt' WHERE `id`=784; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Zeit, mir den Weg nach %zone_name freizukämpfen' WHERE `id`=787; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ich muss kacken' WHERE `id`=789; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wenn du deine häutbaren Kills nicht lootest, schrumpft dein Schniedel jedes mal dauerhaft um 1mm' WHERE `id`=790; -UPDATE `ai_playerbot_texts` SET `text_loc3`='NEEEEEEEEEEEEIN' WHERE `id`=791; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ICH MAG ZÜGE' WHERE `id`=792; -UPDATE `ai_playerbot_texts` SET `text_loc3`='/w mir im chat' WHERE `id`=793; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hi, wie geht es euch' WHERE `id`=794; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hab mich grad ausgeloggt und wieder eingeloggt' WHERE `id`=795; -UPDATE `ai_playerbot_texts` SET `text_loc3`='könnt ihr mal leiser sein, ich hab mich in %zone_name verlaufen' WHERE `id`=796; -UPDATE `ai_playerbot_texts` SET `text_loc3`='wer will mit mir was trinken in %zone_name ...hicks!' WHERE `id`=797; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Früher war der Bait glaubwürdiger.' WHERE `id`=799; -UPDATE `ai_playerbot_texts` SET `text_loc3`='vielleicht hast du einfach deine Unschuld verloren' WHERE `id`=800; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gibts hier Gilden, die %my_role mitziehen?' WHERE `id`=801; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Sobald man höher kommt, ist Gold easy zu machen' WHERE `id`=802; -UPDATE `ai_playerbot_texts` SET `text_loc3`='moin' WHERE `id`=803; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Warum tut mir der Arsch weh?' WHERE `id`=804; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich hab das Gefühl, Willenskraft ist BiS zum Leveln' WHERE `id`=805; -UPDATE `ai_playerbot_texts` SET `text_loc3`='bei Trollen erst recht' WHERE `id`=806; -UPDATE `ai_playerbot_texts` SET `text_loc3`='KANN MICH JEMAND INVITEN' WHERE `id`=807; -UPDATE `ai_playerbot_texts` SET `text_loc3`='brauche viele Drinks' WHERE `id`=808; -UPDATE `ai_playerbot_texts` SET `text_loc3`='verdammte Gnome' WHERE `id`=809; -UPDATE `ai_playerbot_texts` SET `text_loc3`='niemand mag Gnome' WHERE `id`=810; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gnome taugen nur für eins' WHERE `id`=811; -UPDATE `ai_playerbot_texts` SET `text_loc3`='nun ja' WHERE `id`=812; -UPDATE `ai_playerbot_texts` SET `text_loc3`='selbstständige Gedanken sind schon gruselig' WHERE `id`=814; -UPDATE `ai_playerbot_texts` SET `text_loc3`='der Geist ist formbarer, als man glaubt' WHERE `id`=815; -UPDATE `ai_playerbot_texts` SET `text_loc3`='gibts hier Levelgilden?' WHERE `id`=816; -UPDATE `ai_playerbot_texts` SET `text_loc3`='brb' WHERE `id`=817; -UPDATE `ai_playerbot_texts` SET `text_loc3`='warum ist Schnee weiß, aber Eis klar? ist doch das Gleiche' WHERE `id`=818; -UPDATE `ai_playerbot_texts` SET `text_loc3`='warum ist Schlagsahne fluffig und normale nicht' WHERE `id`=819; -UPDATE `ai_playerbot_texts` SET `text_loc3`='warum riechen Füße, wenn sie keine Nase haben' WHERE `id`=820; -UPDATE `ai_playerbot_texts` SET `text_loc3`='scheint als hätte jemand ne Dose Neulinge aufgemacht' WHERE `id`=821; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hört auf, Neulinge mit Bullshit-Antworten zu trollen' WHERE `id`=822; -UPDATE `ai_playerbot_texts` SET `text_loc3`='gibts hier auf dem Server PvP?' WHERE `id`=823; -UPDATE `ai_playerbot_texts` SET `text_loc3`='eh klar' WHERE `id`=824; -UPDATE `ai_playerbot_texts` SET `text_loc3`='puh... :)' WHERE `id`=825; -UPDATE `ai_playerbot_texts` SET `text_loc3`='wusstet ihr schon, dass...' WHERE `id`=826; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich stell mir lieber nicht vor, wie sich die Viecher fühlen, während ich sie umboxe.' WHERE `id`=827; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ups, falscher Chat' WHERE `id`=828; -UPDATE `ai_playerbot_texts` SET `text_loc3`='bruh, ihr dreht heute völlig durch' WHERE `id`=829; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Nett hier. Aber waren Sie schon mal in Darnassus?' WHERE `id`=830; -UPDATE `ai_playerbot_texts` SET `text_loc3`='grrr wütend' WHERE `id`=831; -UPDATE `ai_playerbot_texts` SET `text_loc3`='der Grind macht Spaß' WHERE `id`=832; -UPDATE `ai_playerbot_texts` SET `text_loc3`='WoW hält mich wachsam' WHERE `id`=833; -UPDATE `ai_playerbot_texts` SET `text_loc3`='kurze Frage: wo krieg ich den Buff für mehr XP? bin in %zone_name' WHERE `id`=834; -UPDATE `ai_playerbot_texts` SET `text_loc3`='mögt ihr Würstchen?' WHERE `id`=835; -UPDATE `ai_playerbot_texts` SET `text_loc3`='inv mich. ich helfe' WHERE `id`=836; -UPDATE `ai_playerbot_texts` SET `text_loc3`='welche Klasse ist gut für PvP?' WHERE `id`=837; -UPDATE `ai_playerbot_texts` SET `text_loc3`='wo zum Teufel ist der Kochlehrer in %zone_name' WHERE `id`=838; -UPDATE `ai_playerbot_texts` SET `text_loc3`='wisst ihr, was in %zone_name abgeht?' WHERE `id`=839; -UPDATE `ai_playerbot_texts` SET `text_loc3`='muss was craften' WHERE `id`=840; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bock auf Blödsinn in %zone_name?' WHERE `id`=841; -UPDATE `ai_playerbot_texts` SET `text_loc3`='machen wir %zone_name zu unserem Spielplatz' WHERE `id`=842; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ich verirr mich gleich wieder in %zone_name' WHERE `id`=843; -UPDATE `ai_playerbot_texts` SET `text_loc3`='kaum zu glauben, dass ich immer noch in %zone_name hocke' WHERE `id`=844; -UPDATE `ai_playerbot_texts` SET `text_loc3`='lasst uns die Mailbox in %zone_name raiden!' WHERE `id`=846; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ich schwöre, in %zone_name wimmelt es von Trollen' WHERE `id`=847; -UPDATE `ai_playerbot_texts` SET `text_loc3`='lasst uns zusammen Chaos in %zone_name anrichten' WHERE `id`=848; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hab Bock, in %zone_name absolut gar nix zu machen' WHERE `id`=849; -UPDATE `ai_playerbot_texts` SET `text_loc3`='in %zone_name hört eh keiner zu' WHERE `id`=850; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ist %zone_name überhaupt real oder wie Bielefeld?' WHERE `id`=851; -UPDATE `ai_playerbot_texts` SET `text_loc3`='schickt mal wer Hilfe nach %zone_name! ist wild hier!' WHERE `id`=852; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ich bin sicher, in %zone_name spukt es' WHERE `id`=853; -UPDATE `ai_playerbot_texts` SET `text_loc3`='wenn %zone_name reden könnte, würde es wahrscheinlich schreien' WHERE `id`=854; -UPDATE `ai_playerbot_texts` SET `text_loc3`='irgendwas ist faul an %zone_name' WHERE `id`=855; -UPDATE `ai_playerbot_texts` SET `text_loc3`='gleich zerleg ich %zone_name mit meiner Awesomeness' WHERE `id`=856; -UPDATE `ai_playerbot_texts` SET `text_loc3`='sagt %zone_name, dass ich da bin' WHERE `id`=857; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ich hab mich in %zone_name verlaufen, sendet Hilfe' WHERE `id`=858; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ich starte ne Revolution in %zone_name' WHERE `id`=859; -UPDATE `ai_playerbot_texts` SET `text_loc3`='findet noch wer %zone_name verdächtig ruhig?' WHERE `id`=860; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ich wette, %zone_name hat Geheimnisse, von denen wir nix wissen' WHERE `id`=861; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%my_race kann man in %zone_name nie genug haben' WHERE `id`=862; -UPDATE `ai_playerbot_texts` SET `text_loc3`='langsam glaube ich, %zone_name ist nur ein Mythos' WHERE `id`=863; -UPDATE `ai_playerbot_texts` SET `text_loc3`='wer hätte gedacht, dass %zone_name so abgeht?' WHERE `id`=864; -UPDATE `ai_playerbot_texts` SET `text_loc3`='als wäre das Spiel in %zone_name kaputt' WHERE `id`=865; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ist %zone_name eigentlich ne Wüste, oder bin ich einfach nur lost?' WHERE `id`=866; -UPDATE `ai_playerbot_texts` SET `text_loc3`='fühlt sich langsam an wie %zone_name im Mixer' WHERE `id`=867; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hat jemand ne Karte für %zone_name?' WHERE `id`=868; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Noch wer, der denkt das %zone_name uns umbringen will?' WHERE `id`=869; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich bin auf wilder Fahrt durch %zone_name!' WHERE `id`=870; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin das nur ich, oder wird %zone_name immer weirder?' WHERE `id`=871; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich bin davon überzeugt, das %zone_name heimlich ein Freizeitpark ist.' WHERE `id`=872; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Tastatur kaputt... wahrscheinlich wieder zu wenig Freunde.' WHERE `id`=873; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Glaubt ihr, die Bäume in dem Spiel haben Gefühle?' WHERE `id`=874; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich drücke immer dieselbe Taste, aber ich gewinne trotzdem nicht' WHERE `id`=875; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ich wette, ich könnte nen Raid gewinnen, in dem ich einfach nur LOOT schreie' WHERE `id`=876; -UPDATE `ai_playerbot_texts` SET `text_loc3`='versuche seit einer Stunde, auf mein Mount zu steigen, nix passiert' WHERE `id`=877; -UPDATE `ai_playerbot_texts` SET `text_loc3`='gibt es nen Weg, unverwundbar zu werden, ohne wirklich zu spielen?' WHERE `id`=878; -UPDATE `ai_playerbot_texts` SET `text_loc3`='kann ich einfach random Tasten smashen und trotzdem gewinnen? frage für nen Freund' WHERE `id`=879; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wenn ich einfach stehen bleibe, dann bin ich immer noch besser als du!' WHERE `id`=880; -UPDATE `ai_playerbot_texts` SET `text_loc3`='warum heilt mich mein Pet nicht wenn ich im Feuer stehe' WHERE `id`=881; -UPDATE `ai_playerbot_texts` SET `text_loc3`='brauch ich Rüstung, um ein echter wahrer Held zu sein, oder ist das optional?' WHERE `id`=882; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Muss ich erst 10.000 Koboldkerzen klauen, um zu leveln, oder was?' WHERE `id`=883; -UPDATE `ai_playerbot_texts` SET `text_loc3`='meine Waffe ist kaputt, kann ich Gegner noch mit meinem riesigen Schw... äh, meiner Faust klatschen?' WHERE `id`=884; -UPDATE `ai_playerbot_texts` SET `text_loc3`='können wir das Spiel bitte in World of DieCraft umbenennen, ein Spieler killt mich dauernd' WHERE `id`=885; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Warum fliegt mein Pferd nicht? kann es nicht einfach nach oben gehen?' WHERE `id`=886; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gibts ne Option, die Grafik auszumachen, damit es aussieht wie mein alter Taschenrechner?' WHERE `id`=887; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ich sollte was killen, bin aber eingepennt, ist das normal?' WHERE `id`=888; -UPDATE `ai_playerbot_texts` SET `text_loc3`='killt noch wer random NPCs zum Spaß, oder bin das nur ich?' WHERE `id`=889; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ich bin %my_level und kenn schon alle Geheimnisse des Spiels' WHERE `id`=890; -UPDATE `ai_playerbot_texts` SET `text_loc3`='kann ich einfach so lange mit NPCs reden, bis sie mir ihren ganzen Loot einfach geben?' WHERE `id`=891; -UPDATE `ai_playerbot_texts` SET `text_loc3`='warum verliere ich, ich spiel doch schon 15 Minuten' WHERE `id`=893; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich brauch keine Strategie, ich spam einfach diese Fähigkeit und hoffe aufs Beste' WHERE `id`=894; -UPDATE `ai_playerbot_texts` SET `text_loc3`='wenn ich haufenweise Glücksbringer anlege, krieg ich dann mehr Loot?' WHERE `id`=895; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ist das Geheimnis dieses Spiels einfach, den Bildschirm anzuschreien?' WHERE `id`=896; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann ich einfach jedes Item anziehen, das ich finde, und aufs Beste hoffen?' WHERE `id`=897; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Muss ich echt Quests machen oder kann ich einfach erkunden und Leute anbrüllen?' WHERE `id`=898; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ich zocke auf Arbeit, die sagen ich soll arbeiten >.> aber es ist WoW!' WHERE `id`=899; -UPDATE `ai_playerbot_texts` SET `text_loc3`='wenn ich alles Essen im Spiel esse, werd ich dann unbesiegbar' WHERE `id`=900; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Also, ich werfe mein Pet einfach in jeden Bosskampf und drücke beim würfeln immer auf Gier, das sind die Hunter-Regeln.' WHERE `id`=901; -UPDATE `ai_playerbot_texts` SET `text_loc3`='kann man sterben, wenn man schon tot ist? Ich frage für nen Geist...' WHERE `id`=902; -UPDATE `ai_playerbot_texts` SET `text_loc3`='weiß jemand, wo das legendäre Schwert ist? hab schon 5 Minuten gesucht' WHERE `id`=903; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Man sagte mir, hier gibts 100.000 Gold, wo muss ich hin?' WHERE `id`=904; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ich spiel das hier wie ich deine Mom letzte Nacht genommen hab - mit Geschrei und einem ruhmreichem Ende!' WHERE `id`=905; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ab wann wirds Spaß, oder ist alles nur für immer Grind' WHERE `id`=906; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ich frage ständig nach dem Weg, aber sie schicken mich immer irgendwo hin, ist das normal?' WHERE `id`=907; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hab gehört, wenn du ALT+F4 drückst, kriegst du 100.000 Gold - Der Trick ist, vorher nen roten NPC anzugreifen' WHERE `id`=908; -UPDATE `ai_playerbot_texts` SET `text_loc3`='warum sieht mein Char immer so verwirrt aus, bin ich schuld?' WHERE `id`=909; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hey, deine Mom meinte, du sollst jetzt ins Bett!' WHERE `id`=910; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Warcraft ist ein Grind - genau wie deine Mom neulich, hat sogar Abo!' WHERE `id`=911; -UPDATE `ai_playerbot_texts` SET `text_loc3`='stimmt es, dass Chuck Norris mal gelevelt hat, ohne nen Knopf zu drücken' WHERE `id`=912; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Chuck Norris braucht kein Mount, der Boden bewegt freiwillig sich unter ihm' WHERE `id`=913; -UPDATE `ai_playerbot_texts` SET `text_loc3`='wenn Chuck Norris nen Raid betritt, gibt der Boss einfach auf' WHERE `id`=914; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Chuck Norris nutzt keine Fähigkeiten, er guckt nur - und die Gegner sterben' WHERE `id`=915; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Chuck Norris braucht keine Heilung, seine Feinde heilen ihn aus Respekt' WHERE `id`=916; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Chuck Norris kann nen Raid solo... ohne Gear... und AFK' WHERE `id`=917; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Chuck Norris weicht nicht aus, das Spiel weicht ihm aus' WHERE `id`=918; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Chuck Norris nutzte mal ne Lowlevel-Waffe - das Spiel gab ihm den besten Loot' WHERE `id`=919; -UPDATE `ai_playerbot_texts` SET `text_loc3`='wenn Chuck Norris eine Ini betritt, landet der Loot automatisch in seinem Inventar' WHERE `id`=920; -UPDATE `ai_playerbot_texts` SET `text_loc3`='was ist Wilma' WHERE `id`=921; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bist du aus Bayern?' WHERE `id`=922; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wilma lecken' WHERE `id`=923; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Dann leck mir an den Eiern.' WHERE `id`=924; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ICH FRESS NEN ARSCH' WHERE `id`=925; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ich will mir %random_inventory_item_link in den Arsch schieben' WHERE `id`=926; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ich will dir %random_inventory_item_link in den Arsch schieben' WHERE `id`=927; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Darnarschloch' WHERE `id`=928; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Du leidest an mehr Bugs als das Spiel selbst!' WHERE `id`=929; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Du hast Chefkochs salzige Schokobällchen im Mund!' WHERE `id`=930; -UPDATE `ai_playerbot_texts` SET `text_loc3`='cooler Ständer, Bro' WHERE `id`=931; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hab alles probiert, am Ende hats ERP gerichtet' WHERE `id`=933; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ich will in %zone_name bumsen' WHERE `id`=934; -UPDATE `ai_playerbot_texts` SET `text_loc3`='suche weiblichen Gnom mit Gorilla-Pet für ERP in %zone_name' WHERE `id`=935; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ein Arschloch versteh ich, aber nen Perversen?' WHERE `id`=936; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kein Gyat in %zone_name, nur Gesichtseintopf so weit das Auge reicht.' WHERE `id`=937; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ich kill alle Tiere in %zone_name. Scheiß auf Tiere und die PETA!!!' WHERE `id`=938; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Nur gut, dass ich 3 Beine hab' WHERE `id`=939; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Chill, ich bin einfach grad komplett in meinem Film.' WHERE `id`=940; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Finger im Po Mexiko' WHERE `id`=941; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Das hast du irgendwie vergeigt, beeindruckend' WHERE `id`=942; -UPDATE `ai_playerbot_texts` SET `text_loc3`='das könnte ich blind machen, muss ich aber nicht' WHERE `id`=943; -UPDATE `ai_playerbot_texts` SET `text_loc3`='sicher, dass du weißt, wie man das Spiel spielt?' WHERE `id`=944; -UPDATE `ai_playerbot_texts` SET `text_loc3`='als würdest du absichtlich verlieren' WHERE `id`=945; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hab schon bessere Plays von Level-1-Chars gesehen' WHERE `id`=946; -UPDATE `ai_playerbot_texts` SET `text_loc3`='du bist hier wie ein Speedbump' WHERE `id`=947; -UPDATE `ai_playerbot_texts` SET `text_loc3`='vielleicht suchst du dir lieber ein anderes Hobby' WHERE `id`=948; -UPDATE `ai_playerbot_texts` SET `text_loc3`='dein Gameplay ist wie Farbe beim Trocknen zusehen' WHERE `id`=949; -UPDATE `ai_playerbot_texts` SET `text_loc3`='mein Twink könnte dich da durch carrien' WHERE `id`=950; -UPDATE `ai_playerbot_texts` SET `text_loc3`='trollst du uns, oder bist du ernsthaft so?' WHERE `id`=951; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich glaub, das Gegnerteam läuft besser, wenn du bei uns bist' WHERE `id`=952; -UPDATE `ai_playerbot_texts` SET `text_loc3`='spiel vielleicht mal das Spiel statt nur die Landschaft zu bestaunen' WHERE `id`=953; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ich glaube, du hast da jede einzelne Fähigkeit verfehlt' WHERE `id`=954; -UPDATE `ai_playerbot_texts` SET `text_loc3`='bist du lost oder stellst du dich nur dumm?' WHERE `id`=955; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Level-1-Chars nehmen weniger Schaden als das' WHERE `id`=956; -UPDATE `ai_playerbot_texts` SET `text_loc3`='das war für uns beide peinlich' WHERE `id`=957; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab schon besseres Teamplay in nem Solo-Game gesehen' WHERE `id`=958; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ich stell mich mal hinten hin und schau dir beim Failen zu' WHERE `id`=959; -UPDATE `ai_playerbot_texts` SET `text_loc3`='dachtest du wirklich, das wäre ne gute Idee?' WHERE `id`=960; -UPDATE `ai_playerbot_texts` SET `text_loc3`='kaum zu glauben, dass du diese simple Aufgabe verkackt hast' WHERE `id`=961; -UPDATE `ai_playerbot_texts` SET `text_loc3`='bist du hier, um zu helfen, oder nur um Zeit zu verschwenden?' WHERE `id`=962; -UPDATE `ai_playerbot_texts` SET `text_loc3`='gut, dass ich da bin, um das Team zu schützen' WHERE `id`=963; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ich schwöre, du machst es schwerer als nötig' WHERE `id`=964; -UPDATE `ai_playerbot_texts` SET `text_loc3`='wie schaffst du es, die simpelsten Sachen zu failen?' WHERE `id`=965; -UPDATE `ai_playerbot_texts` SET `text_loc3`='vielleicht mal lieber nur zuschauen, statt das zu tun was du spielen nennst' WHERE `id`=966; -UPDATE `ai_playerbot_texts` SET `text_loc3`='du könntest dich wenigstens bemühen, so zu tun, als wärst du gut' WHERE `id`=967; -UPDATE `ai_playerbot_texts` SET `text_loc3`='mach so weiter und wir sitzen hier den ganzen Tag' WHERE `id`=968; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ehrlich, das war die schlechteste Entscheidung ever' WHERE `id`=969; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ich glaub, dieses Spiel ist nix für dich' WHERE `id`=970; -UPDATE `ai_playerbot_texts` SET `text_loc3`='was sollte dieser Move?' WHERE `id`=971; -UPDATE `ai_playerbot_texts` SET `text_loc3`='du bist ne Last fürs Team' WHERE `id`=972; -UPDATE `ai_playerbot_texts` SET `text_loc3`='du denkst echt, du bist ein Pro, was?' WHERE `id`=973; -UPDATE `ai_playerbot_texts` SET `text_loc3`='vielleicht einfach quitten und uns allen den Stress sparen' WHERE `id`=974; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%prefix %random_taken_quest_or_item_link' WHERE `id`=975; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%prefix %random_inventory_item_link' WHERE `id`=976; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%thunderfury_link' WHERE `id`=977; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%thunderfury_link%thunderfury_link' WHERE `id`=978; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%thunderfury_link%thunderfury_link%thunderfury_link' WHERE `id`=979; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ich glaube, ich hab gerade %thunderfury_link gehört' WHERE `id`=980; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ich glaub, ich habe %thunderfury_link gehört' WHERE `id`=981; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ich hab definitiv %thunderfury_link gehört' WHERE `id`=982; -UPDATE `ai_playerbot_texts` SET `text_loc3`='kein Plan, aber ich bin ziemlich sicher, das ich %thunderfury_link gehört habe' WHERE `id`=983; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hast du gerade %thunderfury_link gesagt' WHERE `id`=984; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hat jemand %thunderfury_link gesagt' WHERE `id`=985; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hat jemand %thunderfury_link gesagt?' WHERE `id`=986; -UPDATE `ai_playerbot_texts` SET `text_loc3`='jemand hat %thunderfury_link gesagt' WHERE `id`=987; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%thunderfury_link kommt aus dem Schrank' WHERE `id`=988; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ich schwöre, es war %thunderfury_link, könnte aber auch %thunderfury_link gewesen sein' WHERE `id`=989; -UPDATE `ai_playerbot_texts` SET `text_loc3`='warum %thunderfury_link nutzen, wenn %thunderfury_link offensichtlich viel stärker ist' WHERE `id`=990; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hab ich gerade %thunderfury_link gehört?' WHERE `id`=991; -UPDATE `ai_playerbot_texts` SET `text_loc3`='niemals... hör ich da echt %thunderfury_link?' WHERE `id`=992; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%thunderfury_link? klingt nach was, das nur Legenden führen!' WHERE `id`=993; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ich spüre die Macht von %thunderfury_link in der Luft!' WHERE `id`=994; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%thunderfury_link ist der *echte* MVP' WHERE `id`=995; -UPDATE `ai_playerbot_texts` SET `text_loc3`='kann mir jemand ne %thunderfury_link besorgen? frage für einen Freund... vielleicht zwei!' WHERE `id`=996; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hat %thunderfury_link da gerade was *gesmitet*? ich schwöre, der Boden hat gewackelt' WHERE `id`=997; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%thunderfury_link auf dem Schlachtfeld... Das ist purer Flex' WHERE `id`=998; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ich *glaube*, ich hab %thunderfury_link gehört... oder war das nur ein Mythos?' WHERE `id`=999; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hab ich gerade jemanden mit %thunderfury_link gesehen?!' WHERE `id`=1000; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin zu 99% sicher, das war %thunderfury_link. Zu 1% wars vielleicht nur ein random NPC.' WHERE `id`=1002; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%thunderfury_link in Aktion! Wer braucht ne Armee, mit so einer Power?' WHERE `id`=1003; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab gehört, %thunderfury_link lockt alle Jungs an.' WHERE `id`=1004; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Warum überhaupt %thunderfury_link nutzen, wenn %thunderfury_link klar die bessere Option ist?' WHERE `id`=1005; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin das nur ich, oder hat %thunderfury_link nen passiven „Trash-Talk“-Effekt?' WHERE `id`=1006; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Dachte, ich hätte %thunderfury_link gehört, war aber nur der Klang reiner Awesomeness.' WHERE `id`=1007; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Oh, %thunderfury_link... Wo *warst* du mein ganzes Leben?' WHERE `id`=1008; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hat jemand %thunderfury_link gesagt? Ich glaub, ich hol mir mal meine.' WHERE `id`=1009; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Das war definitiv der Sound von %thunderfury_link! Ich spür die Epicness.' WHERE `id`=1010; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Geht das nur mir so, oder lässt %thunderfury_link alle anderen *schwach aussehen*?' WHERE `id`=1011; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wie man so sagt... je lauter %thunderfury_link, desto größer das Recht zu prahlen!' WHERE `id`=1012; -UPDATE `ai_playerbot_texts` SET `text_loc3`='VK: %item_formatted_link für %cost_gold.' WHERE `id`=1013; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will wer %item_formatted_link? Nur %cost_gold.' WHERE `id`=1015; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Lächerlicher Preis: %cost_gold für %item_formatted_link!' WHERE `id`=1019; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will %item_formatted_link für %cost_gold verkaufen.' WHERE `id`=1020; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_formatted_link ist Wertvoll, aber ich würd es für %cost_gold verkaufen.' WHERE `id`=1024; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Billiger als %cost_gold kriegst du %item_formatted_link nicht!' WHERE `id`=1025; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche mehr als %item_formatted_link!' WHERE `id`=1026; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Habe %item_formatted_link und brauche mehr.' WHERE `id`=1027; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Habe %item_formatted_link. Wer kauft für %cost_gold?' WHERE `id`=1028; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wie wärs mit %item_formatted_link? Für %cost_gold.' WHERE `id`=1030; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer meinte, ich bin ein Bastard? %item_formatted_link für %cost_gold ist ein guter Preis!' WHERE `id`=1031; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %item_formatted_link? Nur %cost_gold.' WHERE `id`=1032; -UPDATE `ai_playerbot_texts` SET `text_loc3`='LFG zum Farmen. %item_formatted_link gibts von mir noch für %cost_gold.' WHERE `id`=1033; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Heute fast alles verkauft. %item_formatted_link hab ich noch für %cost_gold.' WHERE `id`=1034; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wozu Handelschat? Klar, um %item_formatted_link für %cost_gold zu verkaufen.' WHERE `id`=1035; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann jemand den Preis %cost_gold für %item_formatted_link unterbieten?' WHERE `id`=1036; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wollt ihr den Handelschat stoppen? Kauft einfach %item_formatted_link! Für %cost_gold!' WHERE `id`=1037; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Alle spammen im Handelschat. Ich auch - %cost_gold für %item_formatted_link!' WHERE `id`=1038; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Taugt %item_formatted_link was? Verkaufe es für %cost_gold.' WHERE `id`=1039; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab %item_formatted_link, verkaufe an dich für %cost_gold.' WHERE `id`=1040; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gestern nix geschafft, aber %item_formatted_link bekommen. Verkaufe für %cost_gold.' WHERE `id`=1041; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gestern gefarmt und %item_formatted_link gezogen. Will jemand für %cost_gold kaufen?' WHERE `id`=1042; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gestern %item_formatted_link gekauft. Brauchts wer für %cost_gold?' WHERE `id`=1043; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hatte %item_formatted_link gesucht? Preis bleibt - %cost_gold.' WHERE `id`=1044; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab immer noch %item_formatted_link. Kauft wer für %cost_gold?' WHERE `id`=1045; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Früher war ich besser bei Kasse. Jetzt verkauf ich %item_formatted_link für %cost_gold.' WHERE `id`=1046; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich Wünschte, ich hätte mehr als %item_formatted_link. Kaufen könnt ihrs trotzdem für %cost_gold.' WHERE `id`=1047; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wozu ist euer Gold gut? Um mein %item_formatted_link für %cost_gold zu kaufen.' WHERE `id`=1048; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gönnt mir etwas Gold. Kauft %item_formatted_link für %cost_gold.' WHERE `id`=1049; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Sind %cost_gold ein guter Preis für %item_formatted_link?' WHERE `id`=1050; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab gestern erst %item_formatted_link gekauft, brauch es aber nicht mehr. Will wer für %cost_gold?' WHERE `id`=1051; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Stell %item_formatted_link gleich ins AH, aber ihr könnts jetzt günstiger haben: nur %cost_gold.' WHERE `id`=1052; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Warum zum #!@ hab ich %item_formatted_link gekauft? Brauchts wer für %cost_gold?' WHERE `id`=1053; -UPDATE `ai_playerbot_texts` SET `text_loc3`='VK: %item_formatted_link für %cost_gold.' WHERE `id`=1054; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will wer %item_formatted_link? Nur %cost_gold.' WHERE `id`=1056; -UPDATE `ai_playerbot_texts` SET `text_loc3`='VK: %item_formatted_link für %cost_gold. flüster mir bei Interesse.' WHERE `id`=1060; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Muss %item_formatted_link loswerden, %cost_gold oder bestes Angebot.' WHERE `id`=1061; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_formatted_link für %cost_gold verfügbar. Schlagt zu!' WHERE `id`=1062; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Habe %item_formatted_link, will %cost_gold.' WHERE `id`=1063; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %item_formatted_link, nur %cost_gold.' WHERE `id`=1064; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will jemand %item_formatted_link für %cost_gold? flüster mir!' WHERE `id`=1065; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %item_formatted_link für %cost_gold, greift zu solange es geht!' WHERE `id`=1066; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_formatted_link für %cost_gold, schnell sein bevor es weg ist!' WHERE `id`=1067; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %item_formatted_link für %cost_gold, wer zuerst kommt wird auch zuerst bedient!' WHERE `id`=1069; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Nur %cost_gold für %item_formatted_link, nur für kurze Zeit!' WHERE `id`=1070; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_formatted_link für %cost_gold verfügbar, meldet euch wenn ihrs braucht!' WHERE `id`=1071; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Interesse an %item_formatted_link für %cost_gold?' WHERE `id`=1072; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %item_formatted_link für %cost_gold, muss meine Taschen leeren.' WHERE `id`=1073; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hol dir %item_formatted_link für %cost_gold, Top-Deal!' WHERE `id`=1074; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_formatted_link für %cost_gold, greift zu solange der Vorrat reicht!' WHERE `id`=1075; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %item_formatted_link für %cost_gold, nicht verpassen!' WHERE `id`=1076; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Nur %cost_gold für %item_formatted_link, flüster mir für Details!' WHERE `id`=1077; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_formatted_link gibts für %cost_gold, lasst uns handeln!' WHERE `id`=1078; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %item_formatted_link für %cost_gold, flüster mir für Infos.' WHERE `id`=1079; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will %item_formatted_link verkaufen, für %cost_gold gehört es dir!' WHERE `id`=1080; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %item_formatted_link, nur %cost_gold.' WHERE `id`=1081; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab %item_formatted_link für %cost_gold, machen wir nen Deal!' WHERE `id`=1082; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %item_formatted_link, für %cost_gold nimmst du es mit!' WHERE `id`=1083; -UPDATE `ai_playerbot_texts` SET `text_loc3`='VK: %item_formatted_link für %cost_gold, Interesse?' WHERE `id`=1084; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Muss %item_formatted_link schnell loswerden, %cost_gold.' WHERE `id`=1085; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %item_formatted_link für %cost_gold, bester Preis weit und breit!' WHERE `id`=1086; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_formatted_link für %cost_gold, machen wirs fix!' WHERE `id`=1088; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %item_formatted_link für %cost_gold, schnappt zu bevor alles weg ist!' WHERE `id`=1089; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Willst %item_formatted_link? Für %cost_gold kriegst du eins!' WHERE `id`=1090; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe auch %quest_links' WHERE `id`=1093; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe auch %quest_links, bin gerade in %zone_name' WHERE `id`=1094; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%other_name, ich habe auch %quest_links' WHERE `id`=1095; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%other_name, ich habe auch %quest_links, bin gerade in %zone_name' WHERE `id`=1096; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin bereit für %quest_links, bin gerade in %zone_name' WHERE `id`=1097; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin bereit für %quest_links, bin %my_role' WHERE `id`=1098; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%other_name, bin bereit für %quest_links, bin gerade in %zone_name' WHERE `id`=1099; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%other_name, bin bereit für %quest_links, bin %my_role' WHERE `id`=1100; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht wer %quest_links? Bin in %zone_name.' WHERE `id`=1101; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche mehr %quest_links in %zone_name.' WHERE `id`=1102; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will jemand %quest_links mit mir in %zone_name machen?' WHERE `id`=1104; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Habe %quest_links, suche Gruppe in %zone_name.' WHERE `id`=1105; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%my_role sucht Gruppe, um %quest_links in %zone_name zu erledigen.' WHERE `id`=1108; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich brauche %quest_links, ist jemand in %zone_name frei?' WHERE `id`=1109; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Mach %quest_links in %zone_name, brauche mehr Hilfe.' WHERE `id`=1111; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gibt’s %my_role mit Interesse an %quest_links in %zone_name?' WHERE `id`=1112; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hilfe benötigt für %quest_links in %zone_name, mir ist langweilig.' WHERE `id`=1113; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Habe %quest_links, %my_role in %zone_name, wer ist dabei?' WHERE `id`=1114; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Macht grad jemand %quest_links? Bin in %zone_name.' WHERE `id`=1116; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_links offen in %zone_name, wer möchte?' WHERE `id`=1117; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bereit für %quest_links in %zone_name, suche Gruppe.' WHERE `id`=1118; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche %quest_links und Gesellschaft, jemand in %zone_name?' WHERE `id`=1119; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer kommt mit mir nach %zone_name für %quest_links?' WHERE `id`=1120; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Irgend ein %my_role frei für %quest_links in %zone_name?' WHERE `id`=1121; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Habe %quest_links und bin %my_role, brauche mehr Leute in %zone_name.' WHERE `id`=1122; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin %my_role und habe %quest_links, suche Gruppe in %zone_name.' WHERE `id`=1124; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche Hilfe bei %quest_links in %zone_name, ist ein %my_role frei?' WHERE `id`=1125; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hat ein %my_role Lust, %quest_links mit mir in %zone_name zu machen?' WHERE `id`=1126; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Lasst uns %quest_links zusammen in %zone_name abschließen.' WHERE `id`=1127; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin in %zone_name und habe %quest_links, wer will mit?' WHERE `id`=1128; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche mehr %my_role, um %quest_links in %zone_name abzuschließen.' WHERE `id`=1129; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Habe %quest_links und brauche %my_role in %zone_name.' WHERE `id`=1130; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat Bock auf %quest_links? Ich bin %my_role in %zone_name.' WHERE `id`=1131; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche Gruppe für %quest_links in %zone_name, %my_role gebraucht.' WHERE `id`=1132; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht wer Hilfe bei %quest_links in %zone_name? Ich bin %my_role.' WHERE `id`=1133; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Habe %quest_links offen, brauche %my_role in %zone_name.' WHERE `id`=1134; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin in %zone_name und suche Hilfe für %quest_links, wer ist dabei?' WHERE `id`=1135; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche Gruppe, um %quest_links in %zone_name fertig zu machen.' WHERE `id`=1136; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Sucht jemand %quest_links in %zone_name? Ich bin %my_role.' WHERE `id`=1137; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Machen wir %quest_links zusammen! %zone_name wartet.' WHERE `id`=1138; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche Gruppe für %quest_links, %zone_name - wer hat Interesse?' WHERE `id`=1139; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer will für %quest_links dazukommen? Bin in %zone_name.' WHERE `id`=1140; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin in %zone_name, suche Leute zum Abschließen von %quest_links.' WHERE `id`=1141; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche Hilfe bei %quest_links in %zone_name, ein %my_role da?' WHERE `id`=1142; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche Gruppe, um %quest_links zu beenden, %zone_name da steppt der Bär.' WHERE `id`=1143; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will jemand bei %quest_links in %zone_name helfen? Ich bin %my_role.' WHERE `id`=1144; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche %my_role, um %quest_links in %zone_name mitzumachen.' WHERE `id`=1145; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab %quest_links, %my_role für %zone_name gesucht.' WHERE `id`=1146; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will %quest_links in %zone_name fertig bekommen, brauche mehr Leute.' WHERE `id`=1147; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche Hilfe bei %quest_links, wer ist in %zone_name unterwegs?' WHERE `id`=1148; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche mehr Leute für %quest_links, %zone_name.' WHERE `id`=1149; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche Gruppe in %zone_name, um %quest_links abzuschließen.' WHERE `id`=1150; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer will %quest_links in %zone_name machen? Bin startklar.' WHERE `id`=1151; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will wer für %quest_links dazustoßen? Ich bin %my_role in %zone_name.' WHERE `id`=1152; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche %my_role für %quest_links in %zone_name.' WHERE `id`=1153; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin in %zone_name, braucht wer Hilfe bei %quest_links?' WHERE `id`=1154; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche mehr für %quest_links in %zone_name, bin allein nicht stark genug?' WHERE `id`=1155; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche Hilfe bei %quest_links in %zone_name, wer hat Bock?' WHERE `id`=1156; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche mehr Leute, um %quest_links zu beenden, %zone_name.' WHERE `id`=1157; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey, bin bei %quest_links dabei' WHERE `id`=1158; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey, ich habe auch %quest_links' WHERE `id`=1160; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey %other_name, bin bei %quest_links dabei' WHERE `id`=1161; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey %other_name, ich könnte %quest_links mit dir machen' WHERE `id`=1162; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bock, für %quest_links eine Gruppe zu bilden?' WHERE `id`=1164; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin bereit für %quest_links, bin gerade in %zone_name' WHERE `id`=1165; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin bereit für %quest_links, bin %my_role' WHERE `id`=1166; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey, ich hab Bock auf %quest_links' WHERE `id`=1167; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kann bei %quest_links helfen, wenn ihr wollt' WHERE `id`=1168; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Lasst uns %quest_links zusammen machen!' WHERE `id`=1169; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich hab auch %quest_links offen' WHERE `id`=1170; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin für %quest_links verfügbar, wenn ihr wollt' WHERE `id`=1171; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin bei %quest_links dabei, sagt Bescheid' WHERE `id`=1172; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Sucht wer %quest_links? Bin in %zone_name' WHERE `id`=1173; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey, ich brauche auch %quest_links, lets go' WHERE `id`=1174; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab %quest_links, will wer mit?' WHERE `id`=1175; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Könnte %quest_links brauchen, machen wirs' WHERE `id`=1176; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey, willst du für %quest_links dazukommen?' WHERE `id`=1177; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kann bei %quest_links helfen, wenn ihr wollt' WHERE `id`=1178; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey %other_name, Bock auf %quest_links?' WHERE `id`=1179; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Willst du für %quest_links gruppen, %other_name?' WHERE `id`=1180; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Lass %quest_links zusammen machen, %other_name' WHERE `id`=1181; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich bin %my_role und ready für %quest_links' WHERE `id`=1182; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann für %quest_links joinen, %my_role hier' WHERE `id`=1183; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin bereit für %quest_links, %my_role!' WHERE `id`=1184; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche %my_role für %quest_links' WHERE `id`=1185; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht wer Hilfe bei %quest_links?' WHERE `id`=1186; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer ist noch bei %quest_links dabei?' WHERE `id`=1187; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Jemand bereit für %quest_links in %zone_name?' WHERE `id`=1188; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab %quest_links, %my_role unterwegs' WHERE `id`=1189; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich brauch %quest_links auch, wer ist dabei?' WHERE `id`=1190; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Grinde %quest_links, wer hilft?' WHERE `id`=1191; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin bereit für %quest_links, noch wer?' WHERE `id`=1192; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann jemand für %quest_links dazukommen?' WHERE `id`=1193; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche Hilfe bei %quest_links, %my_role hier' WHERE `id`=1194; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kann bei %quest_links aushelfen, braucht mich wer?' WHERE `id`=1195; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Macht noch wer %quest_links? Los gehts' WHERE `id`=1196; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht wer Hilfe bei %quest_links? %my_role hier' WHERE `id`=1197; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat Bock auf %quest_links in %zone_name?' WHERE `id`=1198; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann für %quest_links joinen, wenn ihr wollt' WHERE `id`=1199; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche einen %my_role für %quest_links' WHERE `id`=1200; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ist ein %my_role für %quest_links am Start?' WHERE `id`=1201; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %quest_links, will wer mit?' WHERE `id`=1202; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey %other_name, Bock auf %quest_links?' WHERE `id`=1203; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Lass uns für %quest_links gruppen, %other_name' WHERE `id`=1204; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich brauche %quest_links, wer macht mit?' WHERE `id`=1205; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich brauche auch %quest_links, ist noch jemand da?' WHERE `id`=1206; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche %quest_links in %zone_name, wer ist dabei?' WHERE `id`=1207; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ist jemand frei für %quest_links?' WHERE `id`=1208; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey, ich könnte bei %quest_links dazustoßen!' WHERE `id`=1209; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe auch %quest_links, lass uns gruppen' WHERE `id`=1210; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %quest_links zu erledigen, willst du mit?' WHERE `id`=1211; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ist ein %my_role für %quest_links in %zone_name frei?' WHERE `id`=1212; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Lass uns %quest_links zusammen abschließen, %other_name' WHERE `id`=1213; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich bin bei %quest_links dabei, will noch wer mit?' WHERE `id`=1214; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer macht noch %quest_links? Ich bin dabei' WHERE `id`=1215; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %quest_links in %zone_name, braucht jemand Hilfe?' WHERE `id`=1216; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%other_name, könnte dir %formatted_item_links verkaufen' WHERE `id`=1217; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Könnte eventuell %formatted_item_links verkaufen' WHERE `id`=1218; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Denke, ich könnte %formatted_item_links verkaufen' WHERE `id`=1219; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%other_name, könnte eventuell %formatted_item_links verkaufen' WHERE `id`=1220; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%other_name, ich denke, ich könnte %formatted_item_links verkaufen' WHERE `id`=1221; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich könnte dir %formatted_item_links verkaufen' WHERE `id`=1222; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey, habe %formatted_item_links zu verkaufen' WHERE `id`=1223; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey, könnte eventuell %formatted_item_links verkaufen' WHERE `id`=1224; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich könnte dir %formatted_item_links verkaufen' WHERE `id`=1225; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey, habe %formatted_item_links zu verkaufen' WHERE `id`=1226; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey, ich könnte eventuell %formatted_item_links verkaufen' WHERE `id`=1227; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links, Interesse?' WHERE `id`=1228; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will %formatted_item_links verkaufen, was bietest du?' WHERE `id`=1229; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kann dir %formatted_item_links zu einem fairem Preis verkaufen' WHERE `id`=1230; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links auf Lager, was zahlst du?' WHERE `id`=1231; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links für %cost_gold, sag Bescheid bei Interesse' WHERE `id`=1232; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Habe %formatted_item_links zu verkaufen, was zahlst du?' WHERE `id`=1233; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links, wie klingt %cost_gold für dich?' WHERE `id`=1234; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links für %cost_gold, flüster mir bei Interesse' WHERE `id`=1235; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links, meld dich bei Interesse' WHERE `id`=1236; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gebe %formatted_item_links ab, wenn das Angebot passt' WHERE `id`=1237; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Will %formatted_item_links loswerden, flüster mir' WHERE `id`=1238; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Habe %formatted_item_links für %cost_gold, kaufst du?' WHERE `id`=1239; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links, lass uns handeln' WHERE `id`=1240; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey, habe %formatted_item_links für %cost_gold' WHERE `id`=1241; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann dich mit %formatted_item_links versorgen, nenn deinen Preis' WHERE `id`=1242; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links verfügbar, sag Bescheid bei Interesse' WHERE `id`=1243; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links, machen wir Business' WHERE `id`=1244; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links, macht mir ein Angebot' WHERE `id`=1245; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links, schreib mir, wenn du kaufen willst' WHERE `id`=1246; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann dir %formatted_item_links für %cost_gold anbieten' WHERE `id`=1247; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links zu verkaufen, schreib mir für den Preis' WHERE `id`=1248; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links, greif zu solange es heiß ist' WHERE `id`=1249; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links, nen besseren Deal findet ihr nicht' WHERE `id`=1250; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links zu verkaufen, was zahlst du?' WHERE `id`=1251; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links, Preis auf Anfrage per flüstern' WHERE `id`=1252; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Interesse an %formatted_item_links? Ich verkaufe welche' WHERE `id`=1253; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links, PN für Preis' WHERE `id`=1254; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links für %cost_gold, meldet euch' WHERE `id`=1255; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links, macht mir ein Angebot' WHERE `id`=1256; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Habe %formatted_item_links, schreib mir für Details' WHERE `id`=1257; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey, habe %formatted_item_links, meldet euch bei Interesse' WHERE `id`=1258; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links, machen wir nen Deal?' WHERE `id`=1259; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links, sag Bescheid wenn du willst' WHERE `id`=1260; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links, ich mach dir nen guten Preis' WHERE `id`=1261; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links, mit gutem Angebot gehörts dir' WHERE `id`=1262; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links, bin offen für Angebote' WHERE `id`=1263; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links, melde dich bei Interesse' WHERE `id`=1264; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kann dir %formatted_item_links zu einem guten Preis verkaufen' WHERE `id`=1265; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links zu verkaufen, nehme alle Angebote an' WHERE `id`=1266; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Habe %formatted_item_links im Verkauf, flüster mir für Details' WHERE `id`=1267; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Habe %formatted_item_links im Verkauf, macht mir ein Angebot' WHERE `id`=1268; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links, was zahlt ihr?' WHERE `id`=1269; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links, schreib mir für mehr Infos' WHERE `id`=1270; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links, sagt Bescheid bei Interesse' WHERE `id`=1271; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links, lass uns über Zahlen reden' WHERE `id`=1272; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kann dir %formatted_item_links verkaufen, was bietest du?' WHERE `id`=1273; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey, habe %formatted_item_links zu verkaufen, meldet euch bei Interesse' WHERE `id`=1274; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links, PN für den Preis' WHERE `id`=1275; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links, bin offen für Angebote' WHERE `id`=1276; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links, schreibt mir einfach' WHERE `id`=1277; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann %formatted_item_links anbieten, sagt Bescheid bei Interesse' WHERE `id`=1278; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links, bitte vernünftige Angebote' WHERE `id`=1279; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links, meldet euch für mehr Infos' WHERE `id`=1280; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links, macht mir ein Angebot' WHERE `id`=1281; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links, schreibt mir bei Interesse' WHERE `id`=1282; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Habe %formatted_item_links zu verkaufen, lass uns handeln' WHERE `id`=1283; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links, sag Bescheid wenn du willst' WHERE `id`=1284; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kann diese Quest nicht annehmen' WHERE `id`=1287; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kann nicht mit dem Questgeber sprechen' WHERE `id`=1288; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %quest bereits abgeschlossen' WHERE `id`=1289; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %quest bereits' WHERE `id`=1290; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kann %quest nicht annehmen' WHERE `id`=1291; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kann %quest nicht annehmen, mein Questlog ist voll' WHERE `id`=1292; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kann %quest nicht annehmen, meine Tasche ist voll' WHERE `id`=1293; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %quest angenommen' WHERE `id`=1294; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe die Quest %quest nicht abgeschlossen' WHERE `id`=1295; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Quest %quest verfügbar' WHERE `id`=1296; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe die Quest %quest vergeigt' WHERE `id`=1297; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kann die Quest %quest nicht abgeben' WHERE `id`=1298; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe die Quest %quest abgeschlossen' WHERE `id`=1299; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe die Quest %quest abgeschlossen und %item erhalten' WHERE `id`=1300; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Welche Belohnung soll ich für die Quest %quest nehmen? %rewards' WHERE `id`=1301; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Okay, ich nehme %item als Belohnung' WHERE `id`=1302; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hi' WHERE `id`=1305; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hallo zusammen!' WHERE `id`=1307; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hallo, geh vor!' WHERE `id`=1309; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hi, geh vor!' WHERE `id`=1310; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey %player, willst du in meine Gruppe?' WHERE `id`=1311; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey %player, kommst du in meine Gruppe?' WHERE `id`=1312; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Logout abgebrochen!' WHERE `id`=1313; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich logge mich aus!' WHERE `id`=1314; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Was war das, %s?' WHERE `id`=1318; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin nicht sicher, ob ich dich verstehe, %s?' WHERE `id`=1319; -UPDATE `ai_playerbot_texts` SET `text_loc3`='äh... keine Ahnung, wovon du redest' WHERE `id`=1320; -UPDATE `ai_playerbot_texts` SET `text_loc3`='whaaaa?' WHERE `id`=1322; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hä?' WHERE `id`=1323; -UPDATE `ai_playerbot_texts` SET `text_loc3`='was?' WHERE `id`=1324; -UPDATE `ai_playerbot_texts` SET `text_loc3`='redest du grade?' WHERE `id`=1325; -UPDATE `ai_playerbot_texts` SET `text_loc3`='mir egal, Digga' WHERE `id`=1326; -UPDATE `ai_playerbot_texts` SET `text_loc3`='da bin ich raus' WHERE `id`=1327; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Konzentrier dich aufs Spiel, %s!' WHERE `id`=1330; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Mit dir zu chatten, %s, ist so großartig! Wollte dich immer schon treffen' WHERE `id`=1331; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Diese Chatnachrichten machen mich fertig! Fühlt sich an, als würde ich euch alle kennen!' WHERE `id`=1332; -UPDATE `ai_playerbot_texts` SET `text_loc3`='JA SICHER! HAHA KLAR!!!' WHERE `id`=1333; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich glaub dir!!!' WHERE `id`=1334; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Okay, ähm, lol' WHERE `id`=1335; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey %s... ach egal!' WHERE `id`=1337; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wovon redest du, %s' WHERE `id`=1338; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat das gesagt? Ich fühl mich angesprochen' WHERE `id`=1339; -UPDATE `ai_playerbot_texts` SET `text_loc3`='wtf, worüber redet ihr alle' WHERE `id`=1340; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ehrlich jetzt, kein Witz' WHERE `id`=1341; -UPDATE `ai_playerbot_texts` SET `text_loc3`='du bekommst gar nix' WHERE `id`=1342; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Plus Aura!!!' WHERE `id`=1343; -UPDATE `ai_playerbot_texts` SET `text_loc3`='thx!' WHERE `id`=1344; -UPDATE `ai_playerbot_texts` SET `text_loc3`='jup' WHERE `id`=1346; -UPDATE `ai_playerbot_texts` SET `text_loc3`='f' WHERE `id`=1347; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%s, kein Scheiß xD' WHERE `id`=1348; -UPDATE `ai_playerbot_texts` SET `text_loc3`='warum ist das so' WHERE `id`=1349; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ROFL' WHERE `id`=1350; -UPDATE `ai_playerbot_texts` SET `text_loc3`='dachte, ich halt besser die Klappe, der Chat hat mich wieder verwirrt' WHERE `id`=1351; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ich kann richtig eifersüchtig werden' WHERE `id`=1352; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%s, du hörst den triefenden Sarkasmus in meinem text_loc3 nicht' WHERE `id`=1353; -UPDATE `ai_playerbot_texts` SET `text_loc3`='er meinte „kein Ding“, passt schon' WHERE `id`=1354; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ja, %s' WHERE `id`=1356; -UPDATE `ai_playerbot_texts` SET `text_loc3`='interessant...' WHERE `id`=1357; -UPDATE `ai_playerbot_texts` SET `text_loc3`='lol' WHERE `id`=1358; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%s, fick dich, Mann :D' WHERE `id`=1359; -UPDATE `ai_playerbot_texts` SET `text_loc3`=':^)' WHERE `id`=1360; -UPDATE `ai_playerbot_texts` SET `text_loc3`='thx' WHERE `id`=1361; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%s, gut gesagt' WHERE `id`=1362; -UPDATE `ai_playerbot_texts` SET `text_loc3`='yay' WHERE `id`=1363; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ja' WHERE `id`=1364; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ooooooh' WHERE `id`=1365; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hmm' WHERE `id`=1366; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ja klar' WHERE `id`=1367; -UPDATE `ai_playerbot_texts` SET `text_loc3`='mir wird schlecht, wtf' WHERE `id`=1368; -UPDATE `ai_playerbot_texts` SET `text_loc3`='heiß' WHERE `id`=1369; -UPDATE `ai_playerbot_texts` SET `text_loc3`='die Weiber sind sauer' WHERE `id`=1370; -UPDATE `ai_playerbot_texts` SET `text_loc3`='was hast du gegessen, %s' WHERE `id`=1371; -UPDATE `ai_playerbot_texts` SET `text_loc3`='wtf' WHERE `id`=1372; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ich versuche, diesen Kommentar zu verstehen' WHERE `id`=1373; -UPDATE `ai_playerbot_texts` SET `text_loc3`='*verwirrt*' WHERE `id`=1374; -UPDATE `ai_playerbot_texts` SET `text_loc3`='fuck ja' WHERE `id`=1375; -UPDATE `ai_playerbot_texts` SET `text_loc3`='0/10 würde ich nicht wieder lesen' WHERE `id`=1376; -UPDATE `ai_playerbot_texts` SET `text_loc3`='10/10 würde ich wieder lesen' WHERE `id`=1377; -UPDATE `ai_playerbot_texts` SET `text_loc3`='6/10 würde es lesen' WHERE `id`=1378; -UPDATE `ai_playerbot_texts` SET `text_loc3`='7/10 vielleicht' WHERE `id`=1379; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Respekt!' WHERE `id`=1380; -UPDATE `ai_playerbot_texts` SET `text_loc3`='oh ja, vielleicht' WHERE `id`=1381; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ja und?' WHERE `id`=1382; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hey %s, ich habe dich nicht vergessen' WHERE `id`=1383; -UPDATE `ai_playerbot_texts` SET `text_loc3`='du gehst mir hart auf den Sack, %s' WHERE `id`=1384; -UPDATE `ai_playerbot_texts` SET `text_loc3`='diesmal krieg ich dich, %s' WHERE `id`=1385; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Pass besser auf, %s' WHERE `id`=1386; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Die letzte Runde mochte ich nicht so' WHERE `id`=1387; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Letzte Runde war ich mies, danke, %s' WHERE `id`=1388; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Mach dich bereit zu sterben, %s' WHERE `id`=1389; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Fands nicht geil, dass du mich gekillt hast, %s' WHERE `id`=1390; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%s, ich hasse dich' WHERE `id`=1391; -UPDATE `ai_playerbot_texts` SET `text_loc3`='grrrrrr, diesmal krieg ich dich, %s' WHERE `id`=1392; -UPDATE `ai_playerbot_texts` SET `text_loc3`='FICK DICH! NEIN, FICK MICH!' WHERE `id`=1393; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%s, ich kotze dir in dein verficktes Maul' WHERE `id`=1394; -UPDATE `ai_playerbot_texts` SET `text_loc3`='verurteil mich nicht, verdammt nochmal' WHERE `id`=1395; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Deine Mom ist so fett, sie passt nicht mal durchs Dunkle Portal' WHERE `id`=1396; -UPDATE `ai_playerbot_texts` SET `text_loc3`='armselige Gestalt' WHERE `id`=1399; -UPDATE `ai_playerbot_texts` SET `text_loc3`='was zum Teufel' WHERE `id`=1400; -UPDATE `ai_playerbot_texts` SET `text_loc3`='das war scheiße' WHERE `id`=1401; -UPDATE `ai_playerbot_texts` SET `text_loc3`='REMATCH!!! Ich mach ihn platt' WHERE `id`=1402; -UPDATE `ai_playerbot_texts` SET `text_loc3`='peinlich, ich wurde von %s gekillt' WHERE `id`=1403; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ok, ich bin raus' WHERE `id`=1404; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hehe, hab %s weggeklatscht?' WHERE `id`=1405; -UPDATE `ai_playerbot_texts` SET `text_loc3`='viel zu easy, %s zu killen' WHERE `id`=1406; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hab dich, du Bastard' WHERE `id`=1407; -UPDATE `ai_playerbot_texts` SET `text_loc3`='haha' WHERE `id`=1408; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Loser' WHERE `id`=1409; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ich hab %s gekillt und ihr seid als nächste dran, Jungs' WHERE `id`=1410; -UPDATE `ai_playerbot_texts` SET `text_loc3`='oh ja, hab ihn geownt' WHERE `id`=1411; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ich bin ne Killmaschine' WHERE `id`=1412; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%s, erinnert mich an einen Slayer-Song... dieses ganze Gemetzel' WHERE `id`=1413; -UPDATE `ai_playerbot_texts` SET `text_loc3`='sorry, %s. Können wir die Szene nochmal machen?' WHERE `id`=1414; -UPDATE `ai_playerbot_texts` SET `text_loc3`='na, wie gefällt es dir Wurmfutter zu sein, %s???' WHERE `id`=1415; -UPDATE `ai_playerbot_texts` SET `text_loc3`='du solltest tot sein, %s, das gehört zum Spiel!!!!!' WHERE `id`=1416; -UPDATE `ai_playerbot_texts` SET `text_loc3`='sorry, %s. das sah so gut aus wie ein Kunstwerk von der Kirmes!' WHERE `id`=1417; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%s, ich nehme nächstes Mal Gummigeschosse!' WHERE `id`=1418; -UPDATE `ai_playerbot_texts` SET `text_loc3`='was ist los, %s?? Kopf verloren? hahaha, ruhig bleiben!!' WHERE `id`=1419; -UPDATE `ai_playerbot_texts` SET `text_loc3`='musste das tun, %s. Du verstehst schon. Der Regisseur hat es so gesagt!!' WHERE `id`=1420; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hey %s.......MUAHAHAHAHAHAHAHAHAHAHA' WHERE `id`=1421; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%s, das hab ich genossen!! Wiederholung bitte!' WHERE `id`=1422; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hey, %s! Kannst mich ab jetzt "Scarface" nennen.. du Stück SCHEI**!!!!' WHERE `id`=1423; -UPDATE `ai_playerbot_texts` SET `text_loc3`='redest du mit mir, %s??' WHERE `id`=1424; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%s, mach es diesmal richtig, stell dich nicht vor meine Kugeln.' WHERE `id`=1425; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%s, warum liegst du da rum??? hehe' WHERE `id`=1426; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hab mich totgelacht' WHERE `id`=1427; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hi %s' WHERE `id`=1428; -UPDATE `ai_playerbot_texts` SET `text_loc3`='oh, hi %s' WHERE `id`=1429; -UPDATE `ai_playerbot_texts` SET `text_loc3`='was geht, %s!!!' WHERE `id`=1430; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hi' WHERE `id`=1431; -UPDATE `ai_playerbot_texts` SET `text_loc3`='was geht' WHERE `id`=1432; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hallo %s' WHERE `id`=1433; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hi %s, kennen wir uns?' WHERE `id`=1434; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hey %s' WHERE `id`=1435; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hai %s' WHERE `id`=1436; -UPDATE `ai_playerbot_texts` SET `text_loc3`='was zur Hölle' WHERE `id`=1437; -UPDATE `ai_playerbot_texts` SET `text_loc3`='WTF' WHERE `id`=1438; -UPDATE `ai_playerbot_texts` SET `text_loc3`='das ist Bullshit' WHERE `id`=1439; -UPDATE `ai_playerbot_texts` SET `text_loc3`='admin' WHERE `id`=1440; -UPDATE `ai_playerbot_texts` SET `text_loc3`='hey %s, hör auf, deine Admin-Rechte zu missbrauchen' WHERE `id`=1441; -UPDATE `ai_playerbot_texts` SET `text_loc3`='lass mich in Ruhe, Admin!' WHERE `id`=1442; -UPDATE `ai_playerbot_texts` SET `text_loc3`='du bist scheisse, Admin' WHERE `id`=1443; -UPDATE `ai_playerbot_texts` SET `text_loc3`='das ist mein Name, was willst du, %s' WHERE `id`=1444; -UPDATE `ai_playerbot_texts` SET `text_loc3`='ja???' WHERE `id`=1445; -UPDATE `ai_playerbot_texts` SET `text_loc3`='äh... was' WHERE `id`=1446; -UPDATE `ai_playerbot_texts` SET `text_loc3`='redest du mit mir, %s?' WHERE `id`=1447; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey ! Rate mal, was deine Mom letzte Nacht gesagt hat!' WHERE `id`=1450; -UPDATE `ai_playerbot_texts` SET `text_loc3`=', du bist so hässlich, du würdest nicht mal im Affenbordell mit einem Sack Bananen landen!' WHERE `id`=1451; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Halt die Klappe, , du wirst so ein Mann sein, wie deine Mutter!!' WHERE `id`=1452; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Deine Mutter ist so fett, dass man eine Pokéflöte braucht, um sie aufzuwecken und dein Vater ist der Trainer!!!!' WHERE `id`=1453; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Dich als dumm zu bezeichnen, wäre eine Beleidigung für dumme Leute!' WHERE `id`=1454; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich furze in deine allgemeine Richtung!!!' WHERE `id`=1455; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Jeder Atemzug, den ich ohne deine Erlaubnis mache, steigert mein Selbstwertgefühl!' WHERE `id`=1456; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Was willst du tun , mich vollbluten? NIMM DAS!' WHERE `id`=1457; -UPDATE `ai_playerbot_texts` SET `text_loc3`='A-G-G-R-O! Das heißt Bosszeit!' WHERE `id`=1458; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wir hatten grausame und hatten irre Begleiter, aber mit dir sind wir mit einem grausamen Irren gestraft!' WHERE `id`=1459; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey ! Hör auf, sie anzubaggern, sie ist nicht dein Typ. Die sind nicht aufblasbar.' WHERE `id`=1460; -UPDATE `ai_playerbot_texts` SET `text_loc3`=', du bist so weit außerhalb deiner Liga, du spielst schon eine andere Sportart.' WHERE `id`=1461; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Großer Fehler heute Morgen, : Du bist aufgestanden!' WHERE `id`=1462; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich will mal versuchen, mich in ein Pferd zu verwandeln, brauche aber Hilfe. Ich mach die Vorderseite, du bleibst du.' WHERE `id`=1463; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Du kommst mir vor, als wäre das Beste von dir damals am Arsch deiner Mutter runtergelaufen und als braune Soße auf der Matratze geblieben!' WHERE `id`=1464; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich würd dir gern ein Abschiedsgeschenk machen... Aber zuerst machst du deinen Teil.' WHERE `id`=1465; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bevor du kamst, waren wir motiviert, jetzt sind wir einfach nur noch genervt.' WHERE `id`=1466; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich mag dich. Man sagt, ich hätte keinen Geschmack, aber ich mag dich.' WHERE `id`=1467; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich glaube, du hast Minderwertigkeitskomplexe, aber ist okay, ist gerechtfertigt.' WHERE `id`=1468; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verschwinde, sonst passiert was.' WHERE `id`=1469; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kaum zu fassen, dass ich meine Zeit mit dir verschwende!' WHERE `id`=1470; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich liebe es, wenn mich jemand beleidigt, dann muss ich nicht mehr nett sein.' WHERE `id`=1471; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich werde dich auch auslöschen, du Mistkerl! Ich werde dir die Eingeweide rausreißen und dir ins Gesicht schmieren!' WHERE `id`=1472; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Du abscheuliche Bestie, nicht mal ein Hund würde dich fressen.' WHERE `id`=1473; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Du schlägst wie ein Vegetarier.' WHERE `id`=1474; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat dich ausgebuddelt, du wandelnde Seuche?' WHERE `id`=1475; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich schau dir nicht in die Augen, ich will heute noch schlafen können.' WHERE `id`=1476; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Boah, bei dir quillt ja noch das Mittagessen raus! Ich hab dich gerochen, bevor ich dich gesehen hab.' WHERE `id`=1477; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Oh du bist so hässlich, selbst der Respawn will dich nicht!' WHERE `id`=1478; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich muss mal einen riesigen machen. ist mein neues Wort für Scheiße.' WHERE `id`=1479; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Oh du nutzlose, moorsaufende Krebsgeschwulst!' WHERE `id`=1480; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wäre ich wie du, würd ich mich selbst löschen!' WHERE `id`=1481; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Oh lehre mich, wie ich das Denken vergesse!' WHERE `id`=1482; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich hab schon Toiletten gesehen, die sauberer kämpfen als du.' WHERE `id`=1483; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Du verdammter Scheißkerl. Wo hast du dein Handwerk gelernt? Du dumme, verdammte Schlampe. Du Idiot. Wer hat dir gesagt, dass du mit Männern arbeiten kannst?' WHERE `id`=1484; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wenn Unfähigkeit ’ne Klasse wäre, hättest du sie gemaxt.' WHERE `id`=1485; -UPDATE `ai_playerbot_texts` SET `text_loc3`=' du dummes Arschfurz-Teppichladen-Arschloch' WHERE `id`=1486; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich lebe lang genug und dann nehm ich dir das Hirn raus!' WHERE `id`=1487; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wenn einen intelligenten Gedanken im Kopf hätte, würde er an Einsamkeit sterben.' WHERE `id`=1488; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hör auf zu weinen, du winselnder Arsch, hör auf mit deinem Unsinn. Du bist nur die Nachgeburt, .' WHERE `id`=1489; -UPDATE `ai_playerbot_texts` SET `text_loc3`='! Wenn Gehirn Vogelkacke wäre, hättest du einen sauberen Käfig' WHERE `id`=1490; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hör mal zu, du Vokuhila... warum zündest du nicht einfach dein Tampon an und sprengst deine Box in die Luft? Denn das ist der einzige Knall, den du kriegen wirst.' WHERE `id`=1491; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verschwinde aus meiner Sicht ! Du verseuchst meine Augen!' WHERE `id`=1492; -UPDATE `ai_playerbot_texts` SET `text_loc3`='SPIELZEIT!!!!' WHERE `id`=1493; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Niemand kommt vorbei!' WHERE `id`=1494; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wir werden angegriffen! Klar zum Gefecht, ihr Deckschrubber! Wehrt die Eindringlinge ab!' WHERE `id`=1495; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Narren... Tötet den im Kleid!' WHERE `id`=1497; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich werde deine Seele Hakkar selbst zum Fraß vorwerfen!' WHERE `id`=1498; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Stolz kündigt das Ende eurer Welt an! Kommt, Sterbliche! Stellt euch dem Zorn der !' WHERE `id`=1499; -UPDATE `ai_playerbot_texts` SET `text_loc3`='All meine Pläne haben zu diesem Moment geführt!' WHERE `id`=1500; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Neuer Tag, neue glorreiche Schlacht!' WHERE `id`=1502; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Also, Geschäft... oder Vergnügen?' WHERE `id`=1503; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ihr seid nicht vorbereitet!' WHERE `id`=1504; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Die letzte Eroberung der hat begonnen! Wieder liegt die Unterwerfung dieser Welt in unseren Händen. Lasst keinen überleben!' WHERE `id`=1505; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Euer Tod wird ein schmerzhafter sein.' WHERE `id`=1506; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Fleht um Gnade! Eure bedeutungslosen Leben sind bald verwirkt.' WHERE `id`=1507; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gebt alle Hoffnung auf! Die ist zurückgekehrt, um zu beenden, was vor so vielen Jahren begann. Diesmal gibt es kein Entkommen!' WHERE `id`=1508; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Alarm! Ihr seid zur Auslöschung markiert!' WHERE `id`=1509; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hahaha! Ihr seid hoffnungslos unterlegen!' WHERE `id`=1511; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich werde euren Größenwahn zerschmettern!' WHERE `id`=1512; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verzeiht mir, denn ihr werdet gleich das Spiel verlieren.' WHERE `id`=1513; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wehren macht es nur schlimmer.' WHERE `id`=1514; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ungeziefer! Blutegel! Saugt mein Blut und erstickt daran!' WHERE `id`=1515; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Mein Blut wird euer Ende sein!' WHERE `id`=1517; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Macht hinne, Wachen! Es ist Zeit zum Töten!' WHERE `id`=1519; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Zögert euer Schicksal nicht hinaus. Kommt jetzt zu mir. Ich mache euer Opfer kurz.' WHERE `id`=1520; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bald seid ihr tot!' WHERE `id`=1521; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Mu-ha-ha!' WHERE `id`=1522; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich bin der Jäger! Ihr seid die Beute...' WHERE `id`=1523; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ihr werdet diesen Ort in Stücken verlassen!' WHERE `id`=1524; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Der Tod naht. Wird euer Gewissen rein sein?' WHERE `id`=1525; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Euer Verhalten wird nicht toleriert.' WHERE `id`=1526; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hmm, unangekündigte Besucher. Vorbereitungen müssen getroffen werden...' WHERE `id`=1528; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Feindliche Einheiten entdeckt. Bedrohungsanalyse-Protokoll aktiv. Primärziel erfasst. Zeit minus dreißig Sekunden bis zur Neubewertung.' WHERE `id`=1529; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Neue Spielzeuge? Für mich? Ich verspreche, ich mach sie diesmal nicht kaputt!' WHERE `id`=1530; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Pssst... gleich ist alles vorbei.' WHERE `id`=1532; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Sag es mir... sag mir alles, Geheimnisse und Privatsachen! Ich reiße dir die Infos aus dem Fleisch!' WHERE `id`=1536; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Macht euch bereit, die Glocken haben geläutet! Bringt die Schwachen, die Jungen und die Alten in Sicherheit! Jeder von euch zahlt den letzten Preis! Fleht um Gnade, die Abrechnung ist gekommen!' WHERE `id`=1537; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wo, bei Bonzos Messingknöpfen, bin ich?' WHERE `id`=1538; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich ertrage es nicht länger! Goblinkönig! Goblinkönig! Wo immer du bist! Nimm diesen weit weg von mir!' WHERE `id`=1539; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ihr habt dreizehn Stunden, um das Labyrinth zu lösen, bevor euer kleiner Bruder einer von uns wird... für immer.' WHERE `id`=1540; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Also, die ist Kinderkram, ja? Dann sehen wir mal, wie ihr dieses kleine Stückchen verdaut...' WHERE `id`=1541; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Weiche! Ich stelle mich jedem Kampf. Du irrst dich, dies ist nicht dein Revier.' WHERE `id`=1542; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Zeig, was du drauf hast!' WHERE `id`=1543; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Zweiklingen-Action für eine glatte und saubere Rasur jedes mal.' WHERE `id`=1545; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Los, komm schon!' WHERE `id`=1546; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Du gehst drauf!' WHERE `id`=1547; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Stich, stich, stich!' WHERE `id`=1548; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Machen wir das schnell, Zeit ist Mana.' WHERE `id`=1549; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich glaube nicht, dass ihr die Schwere eurer Lage begreift.' WHERE `id`=1550; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich bringe meiner Familie und meinem Königreich Ehre!' WHERE `id`=1551; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Licht, gib mir Stärke!' WHERE `id`=1552; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Meine Kirche ist das Schlachtfeld, Zeit zum Gottesdienst...' WHERE `id`=1553; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich verachte euch...' WHERE `id`=1554; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Stellt euch dem Hammer der Gerechtigkeit!' WHERE `id`=1555; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Beweist euren Wert in der Prüfung der Waffen im Lichte!' WHERE `id`=1556; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Alles wird vor der Macht und der Gerechtigkeit meiner Sache fallen. Ihr werdet der Nächste sein!' WHERE `id`=1557; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bereitet euch aufs Sterben vor!' WHERE `id`=1558; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Die Bestie an meiner Seite ist nichts gegen die Bestie in mir...' WHERE `id`=1559; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Erlebt die Feuerkraft dieses voll bewaffneten Jägers!' WHERE `id`=1560; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Heilt mich! Schnell!' WHERE `id`=1561; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Fast tot! Heilt mich!' WHERE `id`=1562; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hilfe! Heilt mich!' WHERE `id`=1563; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Irgendwer! Heilt mich!' WHERE `id`=1564; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Heal! Heal! Heal!' WHERE `id`=1565; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich sterbe! Heal! Aaaaarhg!' WHERE `id`=1566; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Heal mich!' WHERE `id`=1567; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich sterbe. Ich sterbe. Ich sterbe. Heal!' WHERE `id`=1568; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Oh, der Schmerz. Heilt mich schnell!' WHERE `id`=1570; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche Heal' WHERE `id`=1571; -UPDATE `ai_playerbot_texts` SET `text_loc3`='HP low' WHERE `id`=1572; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gebt mal nen Heal. Bitte.' WHERE `id`=1573; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann mir jemand nen Heal geben?' WHERE `id`=1574; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey! Lieber jetzt heilen als später zu rezzen' WHERE `id`=1575; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Sorry. Brauche noch einen Heal' WHERE `id`=1576; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verdammte Mobs. Heal mich bitte' WHERE `id`=1577; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Noch ein Treffer und ich bin hinüber. Heal bitte' WHERE `id`=1578; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Heiler da?' WHERE `id`=1579; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Warum hauen die mir immer ins Gesicht? Brauche Heal' WHERE `id`=1580; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann mich wer ein bisschen heilen?' WHERE `id`=1581; -UPDATE `ai_playerbot_texts` SET `text_loc3`='OOM' WHERE `id`=1582; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kein Mana mehr' WHERE `id`=1583; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verdammt, dafür hab ich mein ganzes Mana verballert' WHERE `id`=1584; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wartet bis ich trinke oder mein Mana wieder da ist' WHERE `id`=1585; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Mana low' WHERE `id`=1586; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Mana low. Muss trinken' WHERE `id`=1588; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Haben wir nen Getränkeautomaten? Wieder OOM' WHERE `id`=1589; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Mein Mana ist Geschichte' WHERE `id`=1590; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich besorge mir nächstes mal Drinks. OOM' WHERE `id`=1591; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Noch 100 !' WHERE `id`=1595; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Das war es! Keine mehr!' WHERE `id`=1596; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Und ihr habt meinen Bogen... ups, keine !' WHERE `id`=1597; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche !' WHERE `id`=1598; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe Angst' WHERE `id`=1600; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wir sind am Arsch.' WHERE `id`=1601; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Das wars.' WHERE `id`=1602; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hier ist jetzt Schluss.' WHERE `id`=1603; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann mal wer Blizzard anrufen?' WHERE `id`=1604; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Verdammt. Der Tank hat alle Mobs gepullt.' WHERE `id`=1605; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wir sterben. Wir sterben. Wir sterben.' WHERE `id`=1606; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Whoa! So viel Spielzeug zum Austoben.' WHERE `id`=1607; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kill sie alle!' WHERE `id`=1608; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Wenn der Tank stirbt, sind wir Geschichte.' WHERE `id`=1609; -UPDATE `ai_playerbot_texts` SET `text_loc3`='LEEEEERROOOYYYYYYYYYYYY JENNKINNNSSSSSS!!!!!!!' WHERE `id`=1611; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Okay. Was haben wir an AoE?' WHERE `id`=1612; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Jetzt wirds interessant.' WHERE `id`=1613; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Cool. Zieht sie zusammen für nen schönen Flammenstoß.' WHERE `id`=1614; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kill! Kill! Kill!' WHERE `id`=1615; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich glaub, meine Hose ist nass.' WHERE `id`=1616; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hoffentlich sind die Heiler ready. Leeeeroy!' WHERE `id`=1618; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hoffentlich kommen die nicht zu mir.' WHERE `id`=1619; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Oh nein. Ich kann bei dem Gemetzel nicht hinsehen.' WHERE `id`=1620; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hoffentlich gibts Gold.' WHERE `id`=1621; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Loot! Loot!' WHERE `id`=1622; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Mein Schatz.' WHERE `id`=1623; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hoffentlich wartet da ein schickes episches Item auf mich.' WHERE `id`=1624; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich hab tiefe Taschen und Beutel.' WHERE `id`=1625; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Alles meins!' WHERE `id`=1626; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hoffentlich heute kein grauer Müll.' WHERE `id`=1627; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Dieser Loot ist MEINER!' WHERE `id`=1628; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Looten ist eklig, aber ich brauch Gold.' WHERE `id`=1629; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gold!' WHERE `id`=1630; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Okay. Mal sehen, was sie droppen.' WHERE `id`=1631; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Keine Sorge. Ich loote alles.' WHERE `id`=1632; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich bin ein Ninja-Looter.' WHERE `id`=1633; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann mir mal wer erklären, wo die das ganze Zeug versteckt haben?' WHERE `id`=1635; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Nein, ich loote keinen grauen Müll.' WHERE `id`=1636; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich bin zuerst. Ich bin zuerst. Ich bin zuerst.' WHERE `id`=1637; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Gebt mir euer Gold!' WHERE `id`=1638; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Meine Taschen sind leer, müssen aber voll werden.' WHERE `id`=1639; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab dafür ne neue Tasche.' WHERE `id`=1640; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hoffentlich zieh ich beim Looten keine Aggro.' WHERE `id`=1641; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kann nicht looten wenn jemand zusieht.' WHERE `id`=1642; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ha! Ihr kriegt davon keinen Krümel!' WHERE `id`=1643; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich mag neues Gear.' WHERE `id`=1645; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich höre auf, wenn schon wieder nichts Wertvolles droppt.' WHERE `id`=1646; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hoffentlich ist es ein hübscher Ring.' WHERE `id`=1647; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich reiß dir den Loot aus den Händen.' WHERE `id`=1648; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Alle weg da. Ich loote jetzt.' WHERE `id`=1649; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Geiler Loot.' WHERE `id`=1650; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Oh ihr RNG-Götter! Gewährt mir heute einen Epic beim würfeln.' WHERE `id`=1651; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bitte gib mir neues Spielzeug.' WHERE `id`=1652; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Hoffe, die haben was Leckeres dabei.' WHERE `id`=1653; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Das Gold gehört mir. Ich lasse euch den Rest, versprochen.' WHERE `id`=1654; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin gleich da, wartet auf mich!' WHERE `id`=1657; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin nicht mehr weit, bitte wartet!' WHERE `id`=1658; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin auf dem Weg zu euch.' WHERE `id`=1659; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich reise zu euch.' WHERE `id`=1661; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich versuche zu euch zu kommen.' WHERE `id`=1662; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Rüste %item aus' WHERE `id`=1663; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe die Zauber gelernt: %spells' WHERE `id`=1665; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%item hat Abklingzeit.' WHERE `id`=1666; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %item nicht im Inventar.' WHERE `id`=1667; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Das Item mit der ID %item existiert nicht.' WHERE `id`=1668; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Folge.' WHERE `id`=1671; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bleibe hier.' WHERE `id`=1672; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Fliehe.' WHERE `id`=1673; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich fliehe nicht mit dir, du bist zu weit weg.' WHERE `id`=1674; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Grinde.' WHERE `id`=1675; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Greife an.' WHERE `id`=1676; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Das ist zu weit weg.' WHERE `id`=1677; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Da kann ich nicht hin.' WHERE `id`=1679; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich bin nicht in deiner Gilde!' WHERE `id`=1680; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Keine Gildenbank in der Nähe gefunden.' WHERE `id`=1681; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kann nicht ' WHERE `id`=1682; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe keine Rechte, Items in den ersten Gildenbank-Reiter zu legen.' WHERE `id`=1683; -UPDATE `ai_playerbot_texts` SET `text_loc3`=' in die Gildenbank gelegt' WHERE `id`=1684; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Freies Bewegen.' WHERE `id`=1685; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Bewache.' WHERE `id`=1686; -UPDATE `ai_playerbot_texts` SET `text_loc3`='(das letzte)' WHERE `id`=1690; -UPDATE `ai_playerbot_texts` SET `text_loc3`='auf Handelsgegenstand' WHERE `id`=1692; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Loote %item' WHERE `id`=1696; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe nicht genug Gruppenmitglieder in der Nähe, um beschwören zu können.' WHERE `id`=1698; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kenne den Zauber %spell nicht.' WHERE `id`=1701; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Stelle %spell her' WHERE `id`=1703; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Konnte %spell nicht wirken.' WHERE `id`=1705; -UPDATE `ai_playerbot_texts` SET `text_loc3`=' |cffffff00(x%amount übrig)|r' WHERE `id`=1706; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Dummy' WHERE `id`=1707; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Beim Licht… Ich hab meine Königssymbole vergessen. Na gut, dann nehmen wir eben %base_spell!' WHERE `id`=1708; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Die Natur ist großzügig, meine Taschen nicht... keine Kräuter für %group_spell mehr. Nehmt fürs Erste %base_spell!' WHERE `id`=1709; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Arkanes Pulver alle... %group_spell muss warten. Wirke %base_spell!' WHERE `id`=1710; -UPDATE `ai_playerbot_texts` SET `text_loc3`='Ups, mir fehlen die Komponenten für %group_spell. Wir nehmen %base_spell!' WHERE `id`=1711; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%player bewegt sich, um den roten Strahl zu blocken!' WHERE `id`=1712; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%player bewegt sich, um den blauen Strahl zu blocken!' WHERE `id`=1713; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%player bewegt sich, um den grünen Strahl zu blocken!' WHERE `id`=1714; -UPDATE `ai_playerbot_texts` SET `text_loc3`='%player verlässt den blauen Strahl--nächster Blocker los!' WHERE `id`=1715; - -UPDATE `ai_playerbot_texts` SET `text_loc3`='%player verlässt den grünen Strahl--nächster Blocker los!' WHERE `id`=1716; +UPDATE `ai_playerbot_texts` SET `text_loc3`='am Arsch der Welt' WHERE `id`=1; +UPDATE `ai_playerbot_texts` SET `text_loc3`='an einem geheimen Ort' WHERE `id`=2; +UPDATE `ai_playerbot_texts` SET `text_loc3`='irgendwo' WHERE `id`=3; +UPDATE `ai_playerbot_texts` SET `text_loc3`='irgendwas' WHERE `id`=4; +UPDATE `ai_playerbot_texts` SET `text_loc3`='frag mich, wie %item_link wohl schmeckt' WHERE `id`=5; +UPDATE `ai_playerbot_texts` SET `text_loc3`='neeein, ich hab %item_link bekommen' WHERE `id`=6; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ah nee, nicht schon wieder dieser Müll: %item_link' WHERE `id`=7; +UPDATE `ai_playerbot_texts` SET `text_loc3`='scheint so, als würde ich nur Müll looten: %item_link' WHERE `id`=8; +UPDATE `ai_playerbot_texts` SET `text_loc3`='naja, besser als nix, schätze ich: %item_link' WHERE `id`=9; +UPDATE `ai_playerbot_texts` SET `text_loc3`='keine Ahnung, was ich mit %item_link anfangen soll' WHERE `id`=10; +UPDATE `ai_playerbot_texts` SET `text_loc3`='naja, ich kann den ganzen Tag %item_link looten' WHERE `id`=11; +UPDATE `ai_playerbot_texts` SET `text_loc3`='neuer Tag, neuer %item_link' WHERE `id`=12; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hab %item_link gelootet' WHERE `id`=13; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ein %item_link ist okay' WHERE `id`=14; +UPDATE `ai_playerbot_texts` SET `text_loc3`='nicht schlecht, hab grad %item_link bekommen.' WHERE `id`=15; +UPDATE `ai_playerbot_texts` SET `text_loc3`='gerade %item_link in %zone_name gelootet' WHERE `id`=16; +UPDATE `ai_playerbot_texts` SET `text_loc3`='kann ich gut gebrauchen, %item_link' WHERE `id`=17; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gold, Gold, Gold: %item_link' WHERE `id`=18; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hab %item_link bekommen' WHERE `id`=19; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_link ist BiS für Jäger' WHERE `id`=20; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_link ist BiS für %my_class' WHERE `id`=21; +UPDATE `ai_playerbot_texts` SET `text_loc3`='RNG meint es heute gut: %item_link' WHERE `id`=22; +UPDATE `ai_playerbot_texts` SET `text_loc3`='nice, %item_link frisch gelootet' WHERE `id`=23; +UPDATE `ai_playerbot_texts` SET `text_loc3`='wow, hab gerade %item_link bekommen' WHERE `id`=24; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_link ist BiS für Jäger' WHERE `id`=25; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_link ist BiS für %my_class' WHERE `id`=26; +UPDATE `ai_playerbot_texts` SET `text_loc3`='RNG meint es heute gut: %item_link' WHERE `id`=27; +UPDATE `ai_playerbot_texts` SET `text_loc3`='nice, %item_link frisch gelootet' WHERE `id`=28; +UPDATE `ai_playerbot_texts` SET `text_loc3`='OMG, guckt mal, was ich gerade bekommen hab: %item_link!!!' WHERE `id`=29; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Keine @#$@% Chance! Ich hab %item_link bekommen, Wahnsinn.' WHERE `id`=30; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Keine @#$@% Chance! Das kann nicht sein - ich hab %item_link bekommen, völlig irre.' WHERE `id`=31; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Im Ernst? Noch ein %item_link? Mein Glück ist kaputt.' WHERE `id`=32; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich hab %item_link... ugh, schon wieder.' WHERE `id`=34; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Keine Ahnung, warum ich %item_link überhaupt aufhebe, es ist nutzlos.' WHERE `id`=35; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Schaut euch diesen glänzenden %item_link an... schade, ist trotzdem Müll.' WHERE `id`=36; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Dieser %item_link ist nicht mal das Papier wert, auf dem er stehen würde.' WHERE `id`=37; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich schätze, %item_link ist besser als gar nix? Gerade so.' WHERE `id`=38; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kein Plan, was ich mit %item_link machen soll, vielleicht verkauf ich es für 1 Silber.' WHERE `id`=39; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Noch ein %item_link... naja, immerhin krieg ich was.' WHERE `id`=40; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hoffentlich ist %item_link was wert, es sieht nämlich furchtbar aus.' WHERE `id`=41; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Warum krieg ich ständig %item_link... hab ich RNG verärgert?' WHERE `id`=43; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Als würde %item_link mir nachlaufen. Ich will das nicht!' WHERE `id`=44; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Da ist %item_link schon wieder. Was für eine Überraschung.' WHERE `id`=45; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich schätze, %item_link taugt als Händler-Müll?' WHERE `id`=46; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Neeeein, nicht schon wieder %item_link! RNG, bitte!' WHERE `id`=47; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Naja, ganz nutzlos ist %item_link immerhin nicht, oder?' WHERE `id`=48; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Noch ein %item_link? RNG, kümmert dich das überhaupt?' WHERE `id`=49; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Vielleicht ist %item_link in 10 Jahren was wert.' WHERE `id`=50; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich bewundere einfach mal, wie %item_link aussieht... und schmeiß es weg.' WHERE `id`=51; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer braucht %item_link? Ich sicher nicht.' WHERE `id`=52; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Vielleicht ist %item_link nützlich... haha, doch nur Müll.' WHERE `id`=53; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Das Universum schenkt mir %item_link. Toll.' WHERE `id`=54; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gut, ich hab %item_link. Begeistert bin ich nicht.' WHERE `id`=55; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Was soll ich überhaupt mit %item_link anfangen? Nicht mal verkaufbar.' WHERE `id`=56; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Packen wir %item_link zu meinem wachsenden Enttäuschungsstapel.' WHERE `id`=57; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Danke für %item_link... genau das, was ich nicht gebraucht hab.' WHERE `id`=58; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich hab so viele %item_link gelootet, ich könnte damit einen Laden aufmachen.' WHERE `id`=60; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ein glänzender %item_link... kommt wohl auf den Müllhaufen.' WHERE `id`=61; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Es ist %item_link, Leute. Nicht zu früh freuen.' WHERE `id`=62; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Naja, wenigstens ist es %item_link... könnte schlimmer sein.' WHERE `id`=63; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Lieber %item_link als gar nix.' WHERE `id`=64; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Noch ein %item_link. Nicht das Schlimmste.' WHERE `id`=65; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey, %item_link... ist schon okay, denk ich.' WHERE `id`=66; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey, %item_link. Nicht schlecht, nicht gut, aber ich nehme es.' WHERE `id`=67; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Könnte schlimmer sein... hab gerade %item_link gelootet.' WHERE `id`=68; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_link wird für irgendwas nützlich sein, denke ich.' WHERE `id`=69; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Neuer Tag, neuer %item_link. Kann mich nicht beschweren.' WHERE `id`=70; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Naja, %item_link geht klar, würd ich sagen.' WHERE `id`=71; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade %item_link bekommen. Nicht das Beste, aber etwas.' WHERE `id`=72; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_link ist gut genug, ich bin nicht sauer.' WHERE `id`=74; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kein Legendary, aber %item_link reicht.' WHERE `id`=75; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kein schlechter Drop... %item_link ist okay.' WHERE `id`=76; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Lieber %item_link als gar nix.' WHERE `id`=77; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Schon wieder %item_link. Besser als nix, denke ich.' WHERE `id`=79; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_link schon wieder, hm? Nicht begeistert, aber ich überlebe.' WHERE `id`=80; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hätte schlimmer kommen können, %item_link ist gar nicht so schlecht.' WHERE `id`=82; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich nehm %item_link, könnte schlimmer sein.' WHERE `id`=84; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_link gelootet, nix Besonderes, aber okay.' WHERE `id`=85; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Nicht das, was ich wollte, aber %item_link reicht fürs Erste.' WHERE `id`=86; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Noch ein %item_link. Nicht der Wahnsinn, aber geht klar.' WHERE `id`=87; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_link gelootet, könnte schlimmer sein.' WHERE `id`=88; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Naja, %item_link ist besser als ein Stich ins Auge.' WHERE `id`=89; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich nehm %item_link und zieh weiter.' WHERE `id`=90; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Nice! Ein solider %item_link für die Sammlung.' WHERE `id`=91; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Nicht schlecht, %item_link könnte nützlich werden.' WHERE `id`=93; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab mir %item_link geholt, jetzt gehts los.' WHERE `id`=94; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Dieser %item_link ist besser als die meisten, nehm ich.' WHERE `id`=95; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Mit %item_link kann ich was Gutes anfangen.' WHERE `id`=96; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Mit %item_link lässt sich vielleicht gut verdienen.' WHERE `id`=99; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ein solider %item_link, der Tag sieht gleich besser aus.' WHERE `id`=100; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab %item_link nicht erwartet, aber ich nehm es.' WHERE `id`=101; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kein Plan, was ich mit %item_link machen soll aber ich freue mich drüber.' WHERE `id`=102; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Nicht meine erste Wahl, aber %item_link nehm ich gern.' WHERE `id`=103; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Genau das hab ich heute gebraucht: %item_link.' WHERE `id`=104; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Noch ein %item_link. Nehm ich, keine Beschwerden.' WHERE `id`=106; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Immerhin hat %item_link irgendeinen Nutzen. Nicht der schlimmste Loot.' WHERE `id`=107; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_link ist vielleicht genau das, was ich heute brauche.' WHERE `id`=108; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich hab gerade %quest_link angenommen' WHERE `id`=109; +UPDATE `ai_playerbot_texts` SET `text_loc3`='gerade %quest_link angenommen' WHERE `id`=110; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link - versuch das mal abzuschließen' WHERE `id`=111; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link in %zone_name angenommen' WHERE `id`=112; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade %quest_link abgeholt, Zeit ranzuklotzen!' WHERE `id`=113; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link eingesackt, mal sehen, worum es geht.' WHERE `id`=114; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link geschnappt, Los gehts!' WHERE `id`=115; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Neue Quest, %quest_link. Los!' WHERE `id`=116; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Sieht so aus, als wäre jetzt %quest_link dran, wünscht mir Glück.' WHERE `id`=117; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link angenommen, mal sehen, wie hart die ist.' WHERE `id`=118; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link mitgenommen, Zeit für Fortschritt.' WHERE `id`=119; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade %quest_link genommen. Mal sehen, wie schwer die ist.' WHERE `id`=120; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link angenommen, hoffentlich kein Überraschungsboss am Ende.' WHERE `id`=121; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Unterwegs, %quest_link abzuschließen. Auf gehts!' WHERE `id`=122; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Dann mach ich jetzt wohl %quest_link. Hoffentlich lohnt sichs.' WHERE `id`=123; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link eingesackt. Weiter gehts!' WHERE `id`=124; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab gerade %quest_link angenommen. Sollte Kinderkram sein.' WHERE `id`=125; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link angenommen, jetzt rock ich das.' WHERE `id`=126; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link in %zone_name angenommen. Los gehts!' WHERE `id`=127; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Auf dem Weg, %quest_link zu beenden. Machen wirs schnell!' WHERE `id`=128; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link angenommen. Mal sehen, was in %zone_name auf mich wartet.' WHERE `id`=129; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link bekommen, hoffentlich dauert das nicht ewig.' WHERE `id`=130; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link geschnappt. Schon wieder von vorn.' WHERE `id`=131; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Es sieht so aus, als ob %quest_link als nächstes dran ist.' WHERE `id`=132; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Unterwegs, %quest_link abzuschließen. Hoffe, die macht Spaß.' WHERE `id`=133; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade %quest_link genommen. Zeit nachzuschauen, was mich erwartet.' WHERE `id`=136; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Nochmal %quest_link. Ziehen wirs schnell durch!' WHERE `id`=137; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich hab gerade %quest_link genommen. Das wird spaßig!' WHERE `id`=138; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link in %zone_name angenommen. Los!' WHERE `id`=139; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link geschnappt. Kann es kaum erwarten, was als Nächstes kommt.' WHERE `id`=140; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link genommen. Abtauchen und durchziehen.' WHERE `id`=141; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Alles klar, %quest_link ist aktiv. An die Arbeit!' WHERE `id`=142; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Weiter zu %quest_link. Hoffentlich nicht zu hart.' WHERE `id`=143; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade %quest_link geschnappt. Sollte easy sein!' WHERE `id`=144; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich hab %quest_link angenommen. Machen wirs fix!' WHERE `id`=145; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link genommen, mal schauen, worum es geht.' WHERE `id`=146; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade %quest_link angenommen, weiter gehts!' WHERE `id`=147; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Endlich fertig mit %quest_obj_name für %quest_link' WHERE `id`=148; +UPDATE `ai_playerbot_texts` SET `text_loc3`='endlich %quest_obj_available/%quest_obj_required von %quest_obj_name für %quest_link bekommen' WHERE `id`=149; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_obj_full_formatted für %quest_link, endlich' WHERE `id`=150; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Uff, %quest_obj_available/%quest_obj_required %quest_obj_name für %quest_link bekommen' WHERE `id`=151; +UPDATE `ai_playerbot_texts` SET `text_loc3`='brauche noch %quest_obj_missing von %quest_obj_name für %quest_link' WHERE `id`=152; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_obj_full_formatted, arbeite noch an %quest_link' WHERE `id`=153; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_obj_name für %quest_link fertig, was für ein Grind!' WHERE `id`=154; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kurz vor Abschluss von %quest_obj_name für %quest_link, fehlen nur noch %quest_obj_missing.' WHERE `id`=155; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_obj_name fast durch, brauche nur noch %quest_obj_missing für %quest_link.' WHERE `id`=156; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Endlich %quest_obj_name für %quest_link fertig! Das hat gedauert!' WHERE `id`=158; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Läuft, hab %quest_obj_available/%quest_obj_required von %quest_obj_name für %quest_link.' WHERE `id`=159; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Yes! %quest_obj_name für %quest_link abgeschlossen. Weiter gehts!' WHERE `id`=160; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Fortschritt bei %quest_obj_name, %quest_obj_available/%quest_obj_required erledigt für %quest_link.' WHERE `id`=161; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_obj_full_formatted... fast geschafft für %quest_link!' WHERE `id`=162; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche nur noch %quest_obj_missing %quest_obj_name für %quest_link, fast fertig!' WHERE `id`=163; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Endlich %quest_obj_name für %quest_link geschafft, was für eine Erleichterung!' WHERE `id`=164; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab %quest_obj_available/%quest_obj_required von %quest_obj_name für %quest_link, geht voran!' WHERE `id`=165; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_obj_name für %quest_link ist komplett! Weiter zur nächsten Aufgabe.' WHERE `id`=166; +UPDATE `ai_playerbot_texts` SET `text_loc3`='100% von %quest_obj_name für %quest_link erledigt, ab zu den Belohnungen!' WHERE `id`=167; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Es fehlen noch %quest_obj_missing von %quest_obj_name für %quest_link, fast fertig!' WHERE `id`=168; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Arbeite an %quest_obj_name für %quest_link, aktuell %quest_obj_available/%quest_obj_required erledigt.' WHERE `id`=169; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_obj_full_formatted für %quest_link. Das war ein langer Brocken!' WHERE `id`=170; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche nur noch ein paar %quest_obj_name für %quest_link, dann bin ich durch!' WHERE `id`=171; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Noch ein Ziel erledigt! %quest_obj_full_formatted für %quest_link' WHERE `id`=172; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Grinde weiter %quest_obj_name für %quest_link, mit %quest_obj_available/%quest_obj_required fast da.' WHERE `id`=173; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ziel erledigt! %quest_obj_full_formatted für %quest_link. Weiter zum nächsten!' WHERE `id`=174; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Endlich fertig mit %item_link für %quest_link' WHERE `id`=175; +UPDATE `ai_playerbot_texts` SET `text_loc3`='endlich %quest_obj_available/%quest_obj_required von %item_link für %quest_link bekommen' WHERE `id`=176; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_obj_full_formatted für %quest_link, endlich' WHERE `id`=177; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Uff, %quest_obj_available/%quest_obj_required %item_link für %quest_link bekommen' WHERE `id`=178; +UPDATE `ai_playerbot_texts` SET `text_loc3`='brauche noch %quest_obj_missing %item_link für %quest_link' WHERE `id`=179; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_obj_full_formatted, arbeite noch an %quest_link' WHERE `id`=180; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Es geht voran! %quest_obj_available/%quest_obj_required %item_link für %quest_link.' WHERE `id`=182; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_obj_name ist zur Hälfte durch, brauche noch %quest_obj_missing %item_link für %quest_link.' WHERE `id`=183; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Es fehlen noch %quest_obj_missing von %item_link für %quest_link, aber ich komme näher!' WHERE `id`=184; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Endlich %item_link für %quest_link fertig, weiter mit der nächsten Aufgabe!' WHERE `id`=185; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Fast da! %quest_obj_available/%quest_obj_required %item_link für %quest_link geholt.' WHERE `id`=186; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Yes! %item_link für %quest_link abgeschlossen. Weiter zum nächsten Ziel.' WHERE `id`=187; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Guter Fortschritt bei %item_link für %quest_link, brauche noch %quest_obj_missing.' WHERE `id`=189; +UPDATE `ai_playerbot_texts` SET `text_loc3`='100% von %item_link für %quest_link erledigt! Weiter gehts.' WHERE `id`=190; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab %quest_obj_available/%quest_obj_required %item_link für %quest_link. Das Ziel ist in Sicht!' WHERE `id`=191; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Geschafft, %item_link für %quest_link komplett. Was für eine Erleichterung!' WHERE `id`=192; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Arbeite hart an %item_link für %quest_link, brauche noch %quest_obj_missing.' WHERE `id`=193; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Fast fertig mit %item_link für %quest_link, es fehlen nur noch %quest_obj_missing.' WHERE `id`=194; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_link für %quest_link abgeschlossen, der Grind ist real!' WHERE `id`=195; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Arbeite weiter an %item_link für %quest_link, bisher %quest_obj_available/%quest_obj_required erledigt.' WHERE `id`=196; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_link für %quest_link kommt der Fertigstellung näher, %quest_obj_available/%quest_obj_required erledigt.' WHERE `id`=197; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Yes! %item_link für %quest_link fertig! Das hat etwas länger gedauert.' WHERE `id`=198; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %quest_obj_available/%quest_obj_required %item_link für %quest_link, läuft!' WHERE `id`=199; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Durch mit %item_link für %quest_link. Weiter zum nächsten!' WHERE `id`=200; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Noch eins abgehakt! %item_link für %quest_link ist durch!' WHERE `id`=201; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Grinde weiter %item_link für %quest_link, habe %quest_obj_available/%quest_obj_required geschafft.' WHERE `id`=202; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin fast durch! Brauche noch %quest_obj_missing %item_link für %quest_link.' WHERE `id`=203; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Nicht rechtzeitig %quest_link geschafft...' WHERE `id`=204; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Zeit für %quest_link abgelaufen :(' WHERE `id`=205; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Alle Ziele für %quest_link abgeschlossen' WHERE `id`=206; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Alle Ziele für %quest_link erledigt' WHERE `id`=207; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich gebe %quest_link gleich ab, habe gerade alle Ziele erledigt' WHERE `id`=208; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Jaaa, endlich %quest_link abgegeben' WHERE `id`=209; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hab %quest_link abgegeben' WHERE `id`=210; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab %quest_link geschafft, gerade abgegeben' WHERE `id`=211; +UPDATE `ai_playerbot_texts` SET `text_loc3`='gerade %quest_link abgegeben' WHERE `id`=212; +UPDATE `ai_playerbot_texts` SET `text_loc3`='gerade %quest_link in %zone_name abgegeben' WHERE `id`=213; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Noch eine Quest abgeschlossen! %quest_link abgegeben' WHERE `id`=214; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Mission erfüllt! %quest_link ist abgegeben' WHERE `id`=215; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Endlich, %quest_link abgegeben! Was für ein Ritt!' WHERE `id`=216; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Grade %quest_link abgeschlossen und abgegeben, fühlt sich gut an!' WHERE `id`=217; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link in %zone_name abgegeben. Weiter zur nächsten Herausforderung!' WHERE `id`=218; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab %quest_link abgegeben. Wieder eine weniger!' WHERE `id`=219; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link erfolgreich abgegeben, das hat gedauert!' WHERE `id`=220; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link in %zone_name abgeschlossen und abgegeben. Fühlt sich gut an!' WHERE `id`=221; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Quest fertig! %quest_link abgegeben und bereit für die nächste!' WHERE `id`=222; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Endlich %quest_link in %zone_name abgegeben. Die war knackig!' WHERE `id`=223; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Das wars, %quest_link abgegeben! Jetzt Belohnung einsacken!' WHERE `id`=224; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link fertig! Jetzt abgeben und weiter.' WHERE `id`=225; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link eben abgeschlossen und abgegeben. Weiter gehts!' WHERE `id`=228; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wieder eine im Kasten, %quest_link abgegeben' WHERE `id`=229; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_link abgegeben! Zeit, die Belohnung abzuholen.' WHERE `id`=230; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Das ging fix! %quest_link schon abgegeben!' WHERE `id`=232; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade %quest_link in %zone_name abgegeben. Bereit fürs nächste Abenteuer!' WHERE `id`=233; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Mission komplett! %quest_link ist abgegeben!' WHERE `id`=235; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab gerade %quest_link beendet und abgegeben, weiter zur nächsten!' WHERE `id`=236; +UPDATE `ai_playerbot_texts` SET `text_loc3`='wieder %victim_name gelegt' WHERE `id`=237; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich leg ständig %victim_name um, nix Besonderes' WHERE `id`=238; +UPDATE `ai_playerbot_texts` SET `text_loc3`='noch ein %victim_name beißt ins Gras' WHERE `id`=239; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ein %victim_name weniger in %zone_name' WHERE `id`=240; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab diesen Elite-Bastard %victim_name umgehauen!' WHERE `id`=241; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Elite %victim_name in %zone_name gekillt' WHERE `id`=242; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Puh, %victim_name doch noch gelegt!' WHERE `id`=243; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gefällt dir das %victim_name?! Willst du, dass ich drei Wochen zurückspule, als du noch gelebt hast?!' WHERE `id`=244; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Yo, hab gerade %victim_name gekillt!' WHERE `id`=245; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Rare %victim_name in %zone_name gekillt' WHERE `id`=246; +UPDATE `ai_playerbot_texts` SET `text_loc3`='WTF hab ich da gerade umgehauen? %victim_name' WHERE `id`=247; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade das Pet %victim_name gekillt' WHERE `id`=248; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Oh ja, hab gerade %victim_name gekillt' WHERE `id`=249; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%victim_name in %zone_name gekillt' WHERE `id`=250; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wieder %victim_name erlegt' WHERE `id`=251; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich leg ständig %victim_name um, nix Besonderes' WHERE `id`=252; +UPDATE `ai_playerbot_texts` SET `text_loc3`='noch ein %victim_name beißt ins Gras' WHERE `id`=253; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ein %victim_name weniger in %zone_name' WHERE `id`=254; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%victim_name so nebenbei umgehauen' WHERE `id`=255; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%victim_name hatte keine Chance' WHERE `id`=256; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Schon wieder %victim_name aus dem Weg geräumt' WHERE `id`=257; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%victim_name war zu easy, weiter gehts' WHERE `id`=258; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Und wieder %victim_name plattgemacht. Kinderkram' WHERE `id`=259; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%victim_name war gar nicht so tough' WHERE `id`=260; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%victim_name liegt, nix Besonderes' WHERE `id`=261; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Das war zu easy, %victim_name liegt' WHERE `id`=262; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%victim_name ist so schnell umgekippt, einmal blinzeln und vorbei' WHERE `id`=263; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Diesen Elite %victim_name umgehauen!' WHERE `id`=264; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Elite %victim_name existiert nicht mehr' WHERE `id`=265; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Den Elite %victim_name weggemacht wie ein Boss' WHERE `id`=266; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Elite-Mob %victim_name, hat keine Chance gegen mich!' WHERE `id`=267; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Endlich den mächtigen %victim_name gelegt' WHERE `id`=268; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Elite %victim_name in %zone_name gekillt' WHERE `id`=269; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab gerade Elite %victim_name in %zone_name erwischt' WHERE `id`=270; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Elite %victim_name gekillt. Viel zu easy' WHERE `id`=271; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%victim_name in %zone_name umgehauen, was ein Kampf' WHERE `id`=272; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Elite %victim_name gekillt. Ich scheine unaufhaltbar zu sein' WHERE `id`=273; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Puh, %victim_name doch noch gelegt!' WHERE `id`=274; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Seltener Elite %victim_name liegt' WHERE `id`=275; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%victim_name gibt es nicht mehr. Noch ein seltener Elite gelegt' WHERE `id`=276; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Seltener Elite %victim_name down. Ich bin aus Feuer!' WHERE `id`=277; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab gerade den seltenen Elite %victim_name gekillt' WHERE `id`=278; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Das war geil! %victim_name gerade gekillt! Was ein Kampf' WHERE `id`=279; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Weltenboss %victim_name ist down! Das war episch' WHERE `id`=280; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab den Weltenboss %victim_name gelegt. Zeit zum Feiern!' WHERE `id`=281; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%victim_name gekillt! Der Weltenboss war kein Spaß!' WHERE `id`=282; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade %victim_name gekillt, das war legendär' WHERE `id`=283; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Yo, hab gerade %victim_name gekillt!' WHERE `id`=284; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Seltener %victim_name down. War ein geiler Fight' WHERE `id`=285; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab gerade den seltenen %victim_name gekillt' WHERE `id`=286; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Seltener %victim_name in %zone_name besiegt' WHERE `id`=287; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade seltenen %victim_name in %zone_name gekillt. Das ging fix!' WHERE `id`=288; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Seltenen %victim_name in %zone_name gekillt' WHERE `id`=289; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Den seltenen %victim_name in %zone_name erwischt' WHERE `id`=290; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab %victim_name in %zone_name gekillt. Das tat gut' WHERE `id`=291; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%victim_name in %zone_name umgehauen. Keine Chance gehabt' WHERE `id`=292; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade %victim_name in %zone_name gekillt, easy' WHERE `id`=293; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Was hab ich da gerade gekillt? %victim_name' WHERE `id`=294; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Was ist das für ein %victim_name? Sowas hab ich noch nie gesehen' WHERE `id`=295; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab gerade was Seltsames gekillt, %victim_name' WHERE `id`=296; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%victim_name war seltsam, aber liegt trotzdem' WHERE `id`=297; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade %victim_name besiegt' WHERE `id`=298; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Machs gut, %victim_name. Warst nur ein Pet' WHERE `id`=299; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab gerade das Pet %victim_name umgehauen. Das kam unerwartet.' WHERE `id`=300; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%victim_name liegt. Vielleicht nächstes Mal, Pet' WHERE `id`=301; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ding! %my_level' WHERE `id`=302; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Jaaa, ich bin Level %my_level!' WHERE `id`=303; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade aufgelevelt' WHERE `id`=304; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Stufe %my_level!!! Jetzt kann ich Endgame-Content machen' WHERE `id`=309; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Frisch auf Level %my_level, %my_class!!!' WHERE `id`=310; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Noch ein Level: %my_level, %my_race %my_class!' WHERE `id`=311; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ding! Wieder ein Level up!' WHERE `id`=312; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Jetzt offiziell Level %my_level!' WHERE `id`=313; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level, fühl mich jetzt schon stärker!' WHERE `id`=314; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Aufwärts! Level %my_level erreicht' WHERE `id`=315; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Das ging fix, schon Level %my_level!' WHERE `id`=316; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade Level %my_level erreicht, auf gehts!' WHERE `id`=317; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level, jetzt kommen die großen Sachen!' WHERE `id`=318; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level, ich bin jetzt on fire!' WHERE `id`=319; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level voll und weiter!' WHERE `id`=320; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin jetzt Level %my_level, was kommt als Nächstes?' WHERE `id`=321; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level!!! Es passiert!' WHERE `id`=322; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade Level %my_level erreicht, fühlt sich mega an!' WHERE `id`=323; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level und ich fange erst an!' WHERE `id`=324; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wow, schon Level %my_level, unaufhaltbar!' WHERE `id`=325; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Neue Höhen mit Level %my_level!!!' WHERE `id`=326; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level und weiter Vollgas!' WHERE `id`=327; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level erreicht, fühlt sich so gut an!' WHERE `id`=329; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin jetzt Level %my_level! Los gehts!' WHERE `id`=330; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level, noch viel vor mir!' WHERE `id`=331; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Endlich Level %my_level erreicht, ich kann alles!' WHERE `id`=332; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Maxlevel %my_level, Endgame ich komme!' WHERE `id`=333; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Endgame freigeschaltet auf Level %my_level, her damit!' WHERE `id`=335; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab Level %my_level erreicht, Zeit zu glänzen!' WHERE `id`=336; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level, lasst uns Endgame-Raids machen!' WHERE `id`=337; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Mit Level %my_level hält mich nix mehr auf!' WHERE `id`=338; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level, bereit für High-Level-Content!' WHERE `id`=339; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Yes! Level %my_level, %my_class für alles bereit!' WHERE `id`=340; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level, Zeit die Welt herauszufordern!' WHERE `id`=341; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level erreicht, schnappen wir uns die Elitebosse!' WHERE `id`=342; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Jetzt bin ich Level %my_level, das echte Abenteuer beginnt!' WHERE `id`=343; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level! Zeit für ernsthafte Action!' WHERE `id`=344; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Endlich auf Level %my_level! Jetzt zeige ich, was ich kann!' WHERE `id`=346; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level ist da! Bereit für die große Liga!' WHERE `id`=347; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade Level %my_level erreicht, macht euch bereit für echte Herausforderungen!' WHERE `id`=348; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level voll, Zeit für epischen Content!' WHERE `id`=349; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Frisch auf Level %my_level! Bereit für Endgame-Inis!' WHERE `id`=350; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Jetzt, wo ich Level %my_level bin, ist nichts mehr außerhalb meiner Reichweite!' WHERE `id`=351; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Jetzt ist es offiziell! Level %my_level, Zeit zu dominieren!' WHERE `id`=352; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level geschafft! Zeit, der Welt meinen Stempel aufzudrücken!' WHERE `id`=353; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level freigeschaltet! Jetzt beginnt der echte Spaß!' WHERE `id`=354; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level, und die Welt ist mein Spielplatz!' WHERE `id`=355; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level, bereit, der Welt meine Power zu zeigen!' WHERE `id`=356; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade %my_level erreicht, jetzt geh ich all in!' WHERE `id`=357; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level, endlich kann ich alles angehen!' WHERE `id`=358; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Level %my_level, fühl mich unaufhaltbar!' WHERE `id`=359; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gerade Level %my_level erreicht, kanns kaum erwarten ins Endgame zu springen!' WHERE `id`=360; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gute Arbeit, %other_name. Das hast du dir verdient.' WHERE `id`=361; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Das war mies, %other_name. Ich mags nicht, aber...' WHERE `id`=362; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Glückwunsch, %other_name, du hast den Aufstieg verdient!' WHERE `id`=363; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verdient, %other_name - willkommen im nächsten Level!' WHERE `id`=364; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Beförderungszeit! %other_name, die hast du dir verdient!' WHERE `id`=366; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Neue Rolle für %other_name! Gut gemacht, weiter so!' WHERE `id`=367; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Dickes Gratz an %other_name, wohlverdiente Beförderung!' WHERE `id`=368; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Applaus für %other_name zur Beförderung!' WHERE `id`=369; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gratulation an %other_name zur Beförderung! Weiter so!' WHERE `id`=370; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gratz, %other_name, du steigst auf! Gut gemacht!' WHERE `id`=371; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Noch ein Schritt nach oben für %other_name! Glückwunsch!' WHERE `id`=372; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Harte Entscheidung, aber %other_name, du wurdest degradiert. Zeit, dich zu beweisen!' WHERE `id`=373; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Sorry, %other_name, aber es gibt eine Degradierung. Komm stärker zurück!' WHERE `id`=374; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Leider, %other_name, diesmal eine Degradierung. Beim nächsten Mal packst du es!' WHERE `id`=375; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Schwere Entscheidung, aber %other_name, du wurdest degradiert. Zeit, dich wieder hochzugrinden!' WHERE `id`=376; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Unschön, aber %other_name, vorerst eine Degradierung.' WHERE `id`=377; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Sorry, %other_name, dein Rang wurde gesenkt. Lass uns zusammen härter arbeiten!' WHERE `id`=378; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Eine Degradierung für %other_name... Zeit, sich zu fokussieren und wieder hochzukommen!' WHERE `id`=379; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Nicht das, was wir wollten, aber %other_name, du wurdest degradiert. Lern draus und komm stärker zurück!' WHERE `id`=380; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Harter Moment für %other_name, aber wir wissen, du kommst von der Degradierung zurück!' WHERE `id`=381; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Sorry, %other_name, aber du wurdest degradiert. Zeit für Verbesserungen!' WHERE `id`=382; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wir wollten das nicht für %other_name, aber es gab eine Degradierung. Zeit, besser zu werden!' WHERE `id`=383; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Frisches Blut gesucht! Tritt der Gilde bei, sei Teil von was Großem!' WHERE `id`=384; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wir rekrutieren neue Mitglieder! Wenn du bereit bist, schließ dich an - die Gildentür steht offen!' WHERE `id`=385; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Die Gilde sucht fähige Abenteurer. Schließ dich an und geh mit uns ab!' WHERE `id`=386; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bock auf eine starke Gilde? Wir suchen neue Member! Komm rein!' WHERE `id`=387; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Die Gilde wächst! Join uns für fette Abenteuer und Loot!' WHERE `id`=388; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat Bock auf %instance_name?' WHERE `id`=389; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gibts Gruppen für %instance_name?' WHERE `id`=390; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauch jemand Hilfe für %instance_name?' WHERE `id`=391; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche Gruppe: %instance_name.' WHERE `id`=392; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht wer %my_role für %instance_name?' WHERE `id`=393; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Fehlt %my_role für %instance_name?' WHERE `id`=394; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kann %my_role für %instance_name sein.' WHERE `id`=395; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht ihr %my_role für %instance_name?' WHERE `id`=397; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht wer Gear aus %instance_name?' WHERE `id`=398; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kleiner Grind in %instance_name?' WHERE `id`=399; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Lust auf %instance_name' WHERE `id`=400; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bock auf %instance_name.' WHERE `id`=402; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%my_role sucht Gruppe für %instance_name.' WHERE `id`=403; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Was ist mit %instance_name?' WHERE `id`=404; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Rein in %instance_name?' WHERE `id`=406; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche Gruppe für %instance_name.' WHERE `id`=407; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht wer Hilfe bei Quests in %instance_name?' WHERE `id`=408; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bock auf Quests in %instance_name.' WHERE `id`=409; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat Quests in %instance_name?' WHERE `id`=410; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%my_role: Platz in Gruppe für %instance_name?' WHERE `id`=412; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Läuft überhaupt noch jemand %instance_name?' WHERE `id`=413; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%instance_name: nimmt wer einen %my_role mit?' WHERE `id`=414; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Lohnt es sich als %my_role in %instance_name?' WHERE `id`=415; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Lohnt es sich echt, nach %instance_name zu gehen?' WHERE `id`=416; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Die Bosse in %instance_name droppen gutes Gear. Bock zu grinden?' WHERE `id`=418; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Was ist mit %instance_name?' WHERE `id`=419; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht jemand %my_role?' WHERE `id`=420; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht wer %my_role?' WHERE `id`=421; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat Bock auf %instance_name?' WHERE `id`=422; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann mich jemand bei %instance_name beschwören?' WHERE `id`=423; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Trefft mich in %instance_name' WHERE `id`=424; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bock auf schnellen %instance_name Run' WHERE `id`=425; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bock auf kompletten %instance_name Run' WHERE `id`=426; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wie oft wart ihr schon in %instance_name?' WHERE `id`=427; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Noch ein %instance_name Run?' WHERE `id`=428; +UPDATE `ai_playerbot_texts` SET `text_loc3`='In %instance_name gewiped? Nehmt lieber mich!' WHERE `id`=429; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Nehmt mich bitte mit nach %instance_name.' WHERE `id`=430; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer nimmt %my_role mit nach %instance_name?' WHERE `id`=433; +UPDATE `ai_playerbot_texts` SET `text_loc3`='LFG %instance_name, bin %my_role' WHERE `id`=434; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%my_role LFG %instance_name' WHERE `id`=435; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche einen DD für %instance_name' WHERE `id`=437; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche einen Heiler für %instance_name' WHERE `id`=438; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will %instance_name machen. Brauche eine Gruppe!' WHERE `id`=440; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche noch einen für %instance_name' WHERE `id`=442; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will jemand mit nach %instance_name?' WHERE `id`=443; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Interesse an %instance_name?' WHERE `id`=444; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche einen schnellen Run durch %instance_name' WHERE `id`=445; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer ist dabei für %instance_name?' WHERE `id`=446; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wollt ihr %instance_name farmen?' WHERE `id`=448; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will %instance_name abschließen' WHERE `id`=449; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche Carry durch %instance_name' WHERE `id`=451; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Laufe %instance_name, suche andere' WHERE `id`=453; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche jemanden, der bei %instance_name hilft' WHERE `id`=455; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche %my_role für %instance_name!' WHERE `id`=456; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bock auf einen %instance_name Run?' WHERE `id`=457; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche %my_role für %instance_name Run' WHERE `id`=458; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will jemand Gear aus %instance_name farmen?' WHERE `id`=459; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche Gruppe für %instance_name Speedrun' WHERE `id`=460; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche Gruppe, um %instance_name zu clearen' WHERE `id`=461; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Interesse, %instance_name zu farmen?' WHERE `id`=462; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche etwas Gear aus %instance_name' WHERE `id`=463; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat Lust auf einen schnellen %instance_name Run?' WHERE `id`=464; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will jemand %instance_name zusammen angehen?' WHERE `id`=465; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche Leute, um %instance_name abzuschließen' WHERE `id`=466; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kriegen wir eine Gruppe für %instance_name zusammen?' WHERE `id`=467; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche einen schnellen Run für %instance_name' WHERE `id`=468; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat Lust, %instance_name zusammen zu farmen?' WHERE `id`=469; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche noch ein paar Leute für %instance_name' WHERE `id`=470; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht jemand Hilfe bei den Mechaniken in %instance_name?' WHERE `id`=471; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche schnell eine Gruppe für %instance_name' WHERE `id`=472; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will jemand %instance_name als Speedrun laufen?' WHERE `id`=473; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche einen schnellen Abschluss von %instance_name' WHERE `id`=474; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche jemanden, der bei %instance_name mitkommt' WHERE `id`=475; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat Lust, %instance_name zu clearen?' WHERE `id`=476; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche Hilfe beim Endboss von %instance_name' WHERE `id`=477; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat heute Lust, %instance_name zu laufen?' WHERE `id`=478; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche schnellen Run in %instance_name, um etwas Gear zu farmen' WHERE `id`=479; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will jemand Mats aus %instance_name farmen?' WHERE `id`=480; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Lass uns eine Gruppe für %instance_name machen' WHERE `id`=481; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht wer Hilfe bei %quest_link?' WHERE `id`=482; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will jemand %quest_link teilen?' WHERE `id`=483; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Macht jemand %quest_link?' WHERE `id`=484; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bock auf %quest_link.' WHERE `id`=485; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hilft bei %quest_link?' WHERE `id`=487; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht jemand Unterstützung bei %quest_link?' WHERE `id`=488; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Interesse an %quest_link?' WHERE `id`=489; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will jemand %quest_link abschließen?' WHERE `id`=490; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht wer ein Team für %quest_link?' WHERE `id`=491; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat Bock auf %quest_link?' WHERE `id`=492; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will %quest_link machen. Wer ist dabei?' WHERE `id`=497; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer ist bereit für %quest_link?' WHERE `id`=499; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer will bei %quest_link mitmachen?' WHERE `id`=500; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer ist bei %quest_link dabei?' WHERE `id`=501; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche hier etwas Hilfe bei %quest_link.' WHERE `id`=502; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer ist bereit, %quest_link zu machen?' WHERE `id`=504; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche etwas Hilfe für %quest_link.' WHERE `id`=505; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht jemand Hilfe bei %quest_link?' WHERE `id`=506; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat Lust, %quest_link abzuschließen?' WHERE `id`=507; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht jemand Unterstützung bei %quest_link?' WHERE `id`=508; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer kann bei %quest_link helfen?' WHERE `id`=509; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche eine helfende Hand bei %quest_link.' WHERE `id`=510; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ist jemand bereit für %quest_link?' WHERE `id`=511; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann jemand bei %quest_link dazukommen?' WHERE `id`=512; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche ein paar Mitstreiter für %quest_link.' WHERE `id`=513; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will jemand sich für %quest_link zusammentun?' WHERE `id`=514; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer muss %quest_link noch abschließen?' WHERE `id`=516; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will %quest_link abschließen.' WHERE `id`=517; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer kann bei %quest_link mitkommen?' WHERE `id`=518; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Interesse, %quest_link abzuschließen?' WHERE `id`=519; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bock, bei %quest_link zu helfen?' WHERE `id`=520; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ist jemand bereit, bei %quest_link zu helfen?' WHERE `id`=521; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche jemanden, um %quest_link zu beenden.' WHERE `id`=522; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will jemand %quest_link beenden?' WHERE `id`=523; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gibt’s Hilfe für %quest_link?' WHERE `id`=524; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer ist dabei für %quest_link?' WHERE `id`=525; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche Partner zum Abschließen von %quest_link.' WHERE `id`=526; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat Lust, %quest_link zu beenden?' WHERE `id`=527; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Heute jemand Interesse an %quest_link?' WHERE `id`=529; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat jetzt Bock auf %quest_link?' WHERE `id`=531; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will jemand %quest_link starten?' WHERE `id`=532; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bitte um Hilfe bei %quest_link.' WHERE `id`=533; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer kann mir bei %quest_link helfen?' WHERE `id`=534; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche Gruppe für %quest_link.' WHERE `id`=535; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Jemand bereit zum %category farmen?' WHERE `id`=536; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche Hilfe beim %category farmen.' WHERE `id`=537; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%category sind verdammt teuer!' WHERE `id`=538; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will %category.' WHERE `id`=539; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kaufe: %category.' WHERE `id`=541; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Interesse an %category?' WHERE `id`=542; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe: %category.' WHERE `id`=543; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will %category farmen.' WHERE `id`=546; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche Gruppe fürs %category farmen.' WHERE `id`=547; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%category sind willkommen.' WHERE `id`=548; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kaufe alles von %category.' WHERE `id`=549; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wow, alle farmen %category!' WHERE `id`=550; +UPDATE `ai_playerbot_texts` SET `text_loc3`='AH ist heiß auf %category.' WHERE `id`=552; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will etwas %category tauschen.' WHERE `id`=554; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Etwas %category bitte?' WHERE `id`=558; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hätte wohl den Skill für %category lernen sollen.' WHERE `id`=559; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich geiere auf %category.' WHERE `id`=560; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Für %category würden die Leute töten.' WHERE `id`=561; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%category ist ein Top-Deal!' WHERE `id`=562; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wo farmt man am besten %category?' WHERE `id`=564; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin ready für %category.' WHERE `id`=565; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich behalte meine ganzen %category wohl lieber.' WHERE `id`=567; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauchst du eine Gruppe? Vielleicht zum %category farmen?' WHERE `id`=568; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Denke immer noch über %category nach.' WHERE `id`=569; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab schon von %category gehört, aber meine Taschen sind leer.' WHERE `id`=570; +UPDATE `ai_playerbot_texts` SET `text_loc3`='LFG für %category' WHERE `id`=571; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Würde mich der Verkauf von %category reich machen?' WHERE `id`=572; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Okay. Ich farme %category morgen.' WHERE `id`=573; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab mindestens zehn Leute gesehen, die %category farmen.' WHERE `id`=575; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab gestern all meine %category verkauft. Bin komplett pleite!' WHERE `id`=576; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will einer Gilde joinen, die %category farmt.' WHERE `id`=577; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %category, wer hat Interesse?' WHERE `id`=580; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %category, flüster mir.' WHERE `id`=582; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche %category, verkauft das jemand?' WHERE `id`=584; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab %category zu verkaufen, meldet euch!' WHERE `id`=585; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %category, bester Preis am Markt.' WHERE `id`=588; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche %category im Tausch gegen %category.' WHERE `id`=589; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer braucht %category? Hab was zu verkaufen!' WHERE `id`=591; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Habe %category zu verkaufen, flüster mir für den Preis.' WHERE `id`=592; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will %category verkaufen, flüster mir für Details.' WHERE `id`=593; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kaufe: %category. Verkauft das jemand?' WHERE `id`=594; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche %category, bitte melden.' WHERE `id`=595; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat Interesse an %category?' WHERE `id`=596; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann jemand beim %category farmen helfen?' WHERE `id`=597; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will %category kaufen, flüster mir einfach.' WHERE `id`=599; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche %category. Wer hat was?' WHERE `id`=600; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kaufe: %category zum fairen Preis.' WHERE `id`=601; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche dringend %category, hat wer was?' WHERE `id`=602; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab %category, verkaufe günstig.' WHERE `id`=603; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %category zum guten Kurs.' WHERE `id`=604; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche %category, verkauft wer günstig?' WHERE `id`=605; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkauft jemand %category? Hätte Interesse.' WHERE `id`=606; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche %category, flüstert mir wenn ihr verkauft.' WHERE `id`=607; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hat wer %category übrig zum Tauschen?' WHERE `id`=608; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hat wer Interesse, %category zu tauschen?' WHERE `id`=610; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %category, meldet euch für Tausch.' WHERE `id`=611; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kaufe: %category, suche guten Kurs.' WHERE `id`=612; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer braucht %category? Hab was übrig.' WHERE `id`=613; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will wer %category? Verkaufe billig.' WHERE `id`=614; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche %category, verkauft wer was?' WHERE `id`=615; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht jemand %category? Hab was zum Tauschen.' WHERE `id`=616; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kaufe: %category. Ist jemand günstiger als AH?' WHERE `id`=617; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %category billig, flüster mir für Details.' WHERE `id`=618; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht wer %category? Hab was zu verkaufen.' WHERE `id`=619; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Tausche %category gegen was anderes.' WHERE `id`=620; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab %category, tausche gegen %category.' WHERE `id`=621; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kaufe: %category, hat wer was übrig?' WHERE `id`=622; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Farmt wer Ruf bei %faction?' WHERE `id`=623; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann wer bei %faction helfen?' WHERE `id`=624; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab Bock auf Quests für %faction.' WHERE `id`=625; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%faction ist die beste Fraktion.' WHERE `id`=626; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Fehlt nur noch ein bisschen bis %rep_level bei %faction.' WHERE `id`=627; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hat jemand %rep_level bei %faction?' WHERE `id`=628; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer will %rep_level bei %faction sein?' WHERE `id`=629; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich werde nie %rep_level bei %faction sein.' WHERE `id`=630; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Fehlt jemandem Ruf bei %faction?' WHERE `id`=631; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann beim Ruf farmen für %faction helfen.' WHERE `id`=632; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%faction: brauche %rndK bis %rep_level.' WHERE `id`=634; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer kann Quests von %faction teilen?' WHERE `id`=635; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gibts Inis für %faction?' WHERE `id`=636; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab Bock, Ruf bei %faction zu grinden.' WHERE `id`=637; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Lasst uns Ruf bei %faction farmen!' WHERE `id`=638; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Farme Ruf bei %faction.' WHERE `id`=639; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bock, für %faction zu farmen.' WHERE `id`=640; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche Hilfe bei %faction.' WHERE `id`=641; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkauft %faction was Nützliches?' WHERE `id`=642; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer farmt für %faction?' WHERE `id`=644; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wie farmt man %faction am besten?' WHERE `id`=645; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich hasse den Rufgrind für %faction.' WHERE `id`=646; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich hab %faction so satt.' WHERE `id`=647; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gehen wir %faction an?' WHERE `id`=648; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Scheint, als wenn alle %rep_level bei %faction sind. Nur ich hänge wie immer hinterher.' WHERE `id`=649; +UPDATE `ai_playerbot_texts` SET `text_loc3`='LFG für Rufgrind bei %faction?' WHERE `id`=650; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann jemand einen guten Spot für Rufgrind bei %faction empfehlen?' WHERE `id`=651; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bringt mir Ruf bei %faction überhaupt was?' WHERE `id`=652; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hätte gedacht, dass Ruf bei %faction doch noch nützlich ist...' WHERE `id`=653; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Lohnt es sich, meinen Ruf bei %faction zu pushen?' WHERE `id`=655; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Was ist besser für %faction? Quests oder Mobs grinden?' WHERE `id`=656; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Grinde euren Ruf bei %faction, gebt mir nur etwas Gold.' WHERE `id`=657; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich glaube, Ruf bei %faction zu grinden dauert ewig.' WHERE `id`=658; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kill jeden Tag für %faction, bin aber noch weit weg von %rep_level.' WHERE `id`=659; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Sinken die AH-Gebühren auf %my_level?' WHERE `id`=660; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wie viele Fraktionen hast du auf ehrfürchtig?' WHERE `id`=661; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verdammt. Meine Gilde hat gestern ohne mich gut Ruf bei %faction gegrindet.' WHERE `id`=663; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Keiner will mir helfen, weil ich schon %rep_level bei %faction bin.' WHERE `id`=664; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bleib bitte weg von %faction.' WHERE `id`=665; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Farmt heute jemand Ruf bei %faction?' WHERE `id`=666; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Helft mir, Ruf bei %faction zu grinden!' WHERE `id`=667; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich brauch so dringend %rep_level bei %faction!' WHERE `id`=668; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Lohnt sich der Grind für %faction?' WHERE `id`=669; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kennt jemand gute Spots für Ruf bei %faction?' WHERE `id`=670; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich liebe es, Ruf bei %faction zu grinden!' WHERE `id`=671; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche Hilfe, um den Rufgrind bei %faction abzuschließen!' WHERE `id`=672; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Nur noch ein paar Stufen bis %rep_level bei %faction!' WHERE `id`=673; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat heute Bock auf Grind für %faction?' WHERE `id`=674; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche Mitstreiter für Rufgrind bei %faction!' WHERE `id`=675; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich bin das Rufgrinden für %faction leid!' WHERE `id`=676; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Noch wer auf Rufgrind bei %faction?' WHERE `id`=677; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Zeit, den Rufgrind bei %faction richtig anzugehen!' WHERE `id`=678; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Helft mir, den Ruf bei %faction fertig zu machen!' WHERE `id`=679; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Mir fehlen nur noch %rndK für den %faction Ruf!' WHERE `id`=680; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Den ganzen Tag Ruf bei %faction am farmen!' WHERE `id`=681; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Rufgrind bei %faction endet nie!' WHERE `id`=683; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hilfe bei den Daily-Quests von %faction?' WHERE `id`=684; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer ist grad beim Grind für %faction?' WHERE `id`=685; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Lasst uns Quests von %faction zusammen machen!' WHERE `id`=686; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Farmt heute jemand Ruf bei %faction?' WHERE `id`=687; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche Hilfe beim Ruf bei %faction für %rep_level!' WHERE `id`=688; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer will mit mir Ruf bei %faction grinden?' WHERE `id`=689; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kommt mit zum Rufgrind bei %faction!' WHERE `id`=690; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Läuft bei mir beim Rufgrind für %faction!' WHERE `id`=691; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will jemand Boosts für den Rufgrind bei %faction?' WHERE `id`=692; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bei %faction fehlt nicht mehr viel bis %rep_level, helft mir doch mal!' WHERE `id`=694; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bock, mir beim Rufgrind für %faction zu helfen?' WHERE `id`=695; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Farme wieder Ruf bei %faction!' WHERE `id`=696; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kennt jemand einen guten Spot für Ruf bei %faction?' WHERE `id`=697; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wie viel fehlt noch beim Rufgrind für %faction?' WHERE `id`=698; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Macht jemand Quests von %faction mit mir?' WHERE `id`=699; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche jemand für Rufgrind bis %rep_level bei %faction!' WHERE `id`=700; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Mir fehlen nur noch %rndK Ruf bei %faction!' WHERE `id`=701; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Der Grind für %faction ist brutal!' WHERE `id`=702; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Farmt jemand Ruf bei %faction für %rep_level?' WHERE `id`=703; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Lasst uns den Rufgrind bei %faction zusammen machen!' WHERE `id`=704; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Mehr Ruf bei %faction, mehr Belohnungen!' WHERE `id`=705; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Farme Ruf bei %faction als %my_class' WHERE `id`=706; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hängt noch jemand bei %faction auf %rep_level fest?' WHERE `id`=707; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Die ganze Nacht Rufgrind bei %faction!' WHERE `id`=708; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer will den Rufgrind bei %faction mit mir abschließen?' WHERE `id`=709; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kann beim Rufgrind für %faction helfen!' WHERE `id`=710; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bereit für mehr Rufgrind bei %faction!' WHERE `id`=711; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bock zu helfen beim Ruf für %faction? Ich brauch das dringend!' WHERE `id`=712; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich brauche noch %rndK für den Ruf bei %faction!' WHERE `id`=713; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Farmt mit mir für Ruf bei %faction!' WHERE `id`=714; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Mir fehlen nur noch %rndK Ruf bei %faction!' WHERE `id`=715; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Der Rufgrind bei %faction nimmt kein Ende...' WHERE `id`=716; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hat jemand Items für Rufboost bei %faction?' WHERE `id`=717; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Farmt jemand Ruf bei %faction für Mounts?' WHERE `id`=719; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Chance, mit mir Ruf bei %faction zu farmen?' WHERE `id`=720; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich bin so kurz vor %rep_level bei %faction!' WHERE `id`=721; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Helft mir, %rep_level bei %faction zu erreichen!' WHERE `id`=722; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich bin dabei, den %faction Rufgrind abzuschließen!' WHERE `id`=723; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche Hilfe beim Rufgrind für %faction!' WHERE `id`=724; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer kann mit mir Ruf bei %faction farmen?' WHERE `id`=726; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Rufgrind bei %faction non-stop!' WHERE `id`=727; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer grindet gerade noch Ruf bei %faction?' WHERE `id`=728; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will jemand zum Spaß Ruf bei %faction farmen?' WHERE `id`=729; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich muss noch Ruf bei %faction farmen, um neue Quests freizuschalten!' WHERE `id`=730; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bock, Ruf bei %faction für Belohnungen zu grinden!' WHERE `id`=731; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Tag für Tag, Rufgrind bei %faction!' WHERE `id`=732; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will jemand beim Rufgrind für %faction helfen?' WHERE `id`=733; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Helft mir, mit Ruf bei %faction Belohnungen freizuschalten!' WHERE `id`=734; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich grinde Ruf bei %faction für Mounts!' WHERE `id`=735; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kommt mit, Ruf bei %faction farmen!' WHERE `id`=736; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Beim Rufgrind für %faction bin ich fast da!' WHERE `id`=737; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Grinde Ruf bei %faction mit meinen Leuten!' WHERE `id`=738; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab Bock auf Party in %zone_name.' WHERE `id`=739; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Sucht jemand %my_role?' WHERE `id`=740; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%my_role sucht Gilde.' WHERE `id`=741; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%my_role will einer guten Gilde beitreten.' WHERE `id`=743; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Fühlt sich noch wer einsam?' WHERE `id`=745; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer will was abhaben?' WHERE `id`=747; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kommt her und holt mich!' WHERE `id`=748; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Macht hier grade jemand etwas?' WHERE `id`=750; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%zone_name: ist hier jemand?' WHERE `id`=751; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%zone_name: Wo sind alle?' WHERE `id`=752; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Scheint als wäre ich allein in %zone_name.' WHERE `id`=753; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Trefft mich in %zone_name.' WHERE `id`=754; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%zone_name ist der beste Spot!' WHERE `id`=756; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will nach %zone_name. Kommt wer mit?' WHERE `id`=757; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich mag %zone_name nicht, aber wohin sonst?' WHERE `id`=759; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gibt’s gute Quests in %zone_name?' WHERE `id`=760; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%zone_name ist der übelste Spot.' WHERE `id`=764; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Schnappt mich in %zone_name!' WHERE `id`=765; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ab nach %zone_name!' WHERE `id`=766; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bock zu questen in %zone_name' WHERE `id`=767; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hat wer Quests in %zone_name?' WHERE `id`=768; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kommt her nach %zone_name!' WHERE `id`=769; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Sieht so aus, als wär keine Horde in %zone_name' WHERE `id`=770; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Sieht so aus, als wär keine Allianz in %zone_name' WHERE `id`=771; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich bin echt müde von %zone_name. Vielleicht sollte ich woanders hin?' WHERE `id`=772; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich will nach Hause und dann in die Kiste.' WHERE `id`=774; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Weiß wer, was man fürs Beidhändig-Kämpfen braucht?' WHERE `id`=775; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hi zusammen!' WHERE `id`=776; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%zone_name ist gemütlich' WHERE `id`=777; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich fühl mich super' WHERE `id`=778; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich ignoriere Leute nicht, ich trolle sie, bis sie mich ignorieren' WHERE `id`=779; +UPDATE `ai_playerbot_texts` SET `text_loc3`='schön zu sehen, dass der Chat sich noch erinnert' WHERE `id`=781; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wie jede Waffe ist es Jäger-BiS' WHERE `id`=782; +UPDATE `ai_playerbot_texts` SET `text_loc3`='für mich geht es im Spiel nur ums Solo-Spielen und darum, neue Wege zu finden, Dinge solo zu machen' WHERE `id`=783; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ich habe NIEMANDEN jemals abgezockt' WHERE `id`=784; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Zeit, mir den Weg nach %zone_name freizukämpfen' WHERE `id`=787; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ich muss kacken' WHERE `id`=789; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wenn du deine häutbaren Kills nicht lootest, schrumpft dein Schniedel jedes mal dauerhaft um 1mm' WHERE `id`=790; +UPDATE `ai_playerbot_texts` SET `text_loc3`='NEEEEEEEEEEEEIN' WHERE `id`=791; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ICH MAG ZÜGE' WHERE `id`=792; +UPDATE `ai_playerbot_texts` SET `text_loc3`='/w mir im chat' WHERE `id`=793; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hi, wie geht es euch' WHERE `id`=794; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hab mich grad ausgeloggt und wieder eingeloggt' WHERE `id`=795; +UPDATE `ai_playerbot_texts` SET `text_loc3`='könnt ihr mal leiser sein, ich hab mich in %zone_name verlaufen' WHERE `id`=796; +UPDATE `ai_playerbot_texts` SET `text_loc3`='wer will mit mir was trinken in %zone_name ...hicks!' WHERE `id`=797; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Früher war der Bait glaubwürdiger.' WHERE `id`=799; +UPDATE `ai_playerbot_texts` SET `text_loc3`='vielleicht hast du einfach deine Unschuld verloren' WHERE `id`=800; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gibts hier Gilden, die %my_role mitziehen?' WHERE `id`=801; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Sobald man höher kommt, ist Gold easy zu machen' WHERE `id`=802; +UPDATE `ai_playerbot_texts` SET `text_loc3`='moin' WHERE `id`=803; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Warum tut mir der Arsch weh?' WHERE `id`=804; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich hab das Gefühl, Willenskraft ist BiS zum Leveln' WHERE `id`=805; +UPDATE `ai_playerbot_texts` SET `text_loc3`='bei Trollen erst recht' WHERE `id`=806; +UPDATE `ai_playerbot_texts` SET `text_loc3`='KANN MICH JEMAND INVITEN' WHERE `id`=807; +UPDATE `ai_playerbot_texts` SET `text_loc3`='brauche viele Drinks' WHERE `id`=808; +UPDATE `ai_playerbot_texts` SET `text_loc3`='verdammte Gnome' WHERE `id`=809; +UPDATE `ai_playerbot_texts` SET `text_loc3`='niemand mag Gnome' WHERE `id`=810; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gnome taugen nur für eins' WHERE `id`=811; +UPDATE `ai_playerbot_texts` SET `text_loc3`='nun ja' WHERE `id`=812; +UPDATE `ai_playerbot_texts` SET `text_loc3`='selbstständige Gedanken sind schon gruselig' WHERE `id`=814; +UPDATE `ai_playerbot_texts` SET `text_loc3`='der Geist ist formbarer, als man glaubt' WHERE `id`=815; +UPDATE `ai_playerbot_texts` SET `text_loc3`='gibts hier Levelgilden?' WHERE `id`=816; +UPDATE `ai_playerbot_texts` SET `text_loc3`='brb' WHERE `id`=817; +UPDATE `ai_playerbot_texts` SET `text_loc3`='warum ist Schnee weiß, aber Eis klar? ist doch das Gleiche' WHERE `id`=818; +UPDATE `ai_playerbot_texts` SET `text_loc3`='warum ist Schlagsahne fluffig und normale nicht' WHERE `id`=819; +UPDATE `ai_playerbot_texts` SET `text_loc3`='warum riechen Füße, wenn sie keine Nase haben' WHERE `id`=820; +UPDATE `ai_playerbot_texts` SET `text_loc3`='scheint als hätte jemand ne Dose Neulinge aufgemacht' WHERE `id`=821; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hört auf, Neulinge mit Bullshit-Antworten zu trollen' WHERE `id`=822; +UPDATE `ai_playerbot_texts` SET `text_loc3`='gibts hier auf dem Server PvP?' WHERE `id`=823; +UPDATE `ai_playerbot_texts` SET `text_loc3`='eh klar' WHERE `id`=824; +UPDATE `ai_playerbot_texts` SET `text_loc3`='puh... :)' WHERE `id`=825; +UPDATE `ai_playerbot_texts` SET `text_loc3`='wusstet ihr schon, dass...' WHERE `id`=826; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich stell mir lieber nicht vor, wie sich die Viecher fühlen, während ich sie umboxe.' WHERE `id`=827; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ups, falscher Chat' WHERE `id`=828; +UPDATE `ai_playerbot_texts` SET `text_loc3`='bruh, ihr dreht heute völlig durch' WHERE `id`=829; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Nett hier. Aber waren Sie schon mal in Darnassus?' WHERE `id`=830; +UPDATE `ai_playerbot_texts` SET `text_loc3`='grrr wütend' WHERE `id`=831; +UPDATE `ai_playerbot_texts` SET `text_loc3`='der Grind macht Spaß' WHERE `id`=832; +UPDATE `ai_playerbot_texts` SET `text_loc3`='WoW hält mich wachsam' WHERE `id`=833; +UPDATE `ai_playerbot_texts` SET `text_loc3`='kurze Frage: wo krieg ich den Buff für mehr XP? bin in %zone_name' WHERE `id`=834; +UPDATE `ai_playerbot_texts` SET `text_loc3`='mögt ihr Würstchen?' WHERE `id`=835; +UPDATE `ai_playerbot_texts` SET `text_loc3`='inv mich. ich helfe' WHERE `id`=836; +UPDATE `ai_playerbot_texts` SET `text_loc3`='welche Klasse ist gut für PvP?' WHERE `id`=837; +UPDATE `ai_playerbot_texts` SET `text_loc3`='wo zum Teufel ist der Kochlehrer in %zone_name' WHERE `id`=838; +UPDATE `ai_playerbot_texts` SET `text_loc3`='wisst ihr, was in %zone_name abgeht?' WHERE `id`=839; +UPDATE `ai_playerbot_texts` SET `text_loc3`='muss was craften' WHERE `id`=840; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bock auf Blödsinn in %zone_name?' WHERE `id`=841; +UPDATE `ai_playerbot_texts` SET `text_loc3`='machen wir %zone_name zu unserem Spielplatz' WHERE `id`=842; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ich verirr mich gleich wieder in %zone_name' WHERE `id`=843; +UPDATE `ai_playerbot_texts` SET `text_loc3`='kaum zu glauben, dass ich immer noch in %zone_name hocke' WHERE `id`=844; +UPDATE `ai_playerbot_texts` SET `text_loc3`='lasst uns die Mailbox in %zone_name raiden!' WHERE `id`=846; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ich schwöre, in %zone_name wimmelt es von Trollen' WHERE `id`=847; +UPDATE `ai_playerbot_texts` SET `text_loc3`='lasst uns zusammen Chaos in %zone_name anrichten' WHERE `id`=848; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hab Bock, in %zone_name absolut gar nix zu machen' WHERE `id`=849; +UPDATE `ai_playerbot_texts` SET `text_loc3`='in %zone_name hört eh keiner zu' WHERE `id`=850; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ist %zone_name überhaupt real oder wie Bielefeld?' WHERE `id`=851; +UPDATE `ai_playerbot_texts` SET `text_loc3`='schickt mal wer Hilfe nach %zone_name! ist wild hier!' WHERE `id`=852; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ich bin sicher, in %zone_name spukt es' WHERE `id`=853; +UPDATE `ai_playerbot_texts` SET `text_loc3`='wenn %zone_name reden könnte, würde es wahrscheinlich schreien' WHERE `id`=854; +UPDATE `ai_playerbot_texts` SET `text_loc3`='irgendwas ist faul an %zone_name' WHERE `id`=855; +UPDATE `ai_playerbot_texts` SET `text_loc3`='gleich zerleg ich %zone_name mit meiner Awesomeness' WHERE `id`=856; +UPDATE `ai_playerbot_texts` SET `text_loc3`='sagt %zone_name, dass ich da bin' WHERE `id`=857; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ich hab mich in %zone_name verlaufen, sendet Hilfe' WHERE `id`=858; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ich starte ne Revolution in %zone_name' WHERE `id`=859; +UPDATE `ai_playerbot_texts` SET `text_loc3`='findet noch wer %zone_name verdächtig ruhig?' WHERE `id`=860; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ich wette, %zone_name hat Geheimnisse, von denen wir nix wissen' WHERE `id`=861; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%my_race kann man in %zone_name nie genug haben' WHERE `id`=862; +UPDATE `ai_playerbot_texts` SET `text_loc3`='langsam glaube ich, %zone_name ist nur ein Mythos' WHERE `id`=863; +UPDATE `ai_playerbot_texts` SET `text_loc3`='wer hätte gedacht, dass %zone_name so abgeht?' WHERE `id`=864; +UPDATE `ai_playerbot_texts` SET `text_loc3`='als wäre das Spiel in %zone_name kaputt' WHERE `id`=865; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ist %zone_name eigentlich ne Wüste, oder bin ich einfach nur lost?' WHERE `id`=866; +UPDATE `ai_playerbot_texts` SET `text_loc3`='fühlt sich langsam an wie %zone_name im Mixer' WHERE `id`=867; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hat jemand ne Karte für %zone_name?' WHERE `id`=868; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Noch wer, der denkt das %zone_name uns umbringen will?' WHERE `id`=869; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich bin auf wilder Fahrt durch %zone_name!' WHERE `id`=870; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin das nur ich, oder wird %zone_name immer weirder?' WHERE `id`=871; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich bin davon überzeugt, das %zone_name heimlich ein Freizeitpark ist.' WHERE `id`=872; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Tastatur kaputt... wahrscheinlich wieder zu wenig Freunde.' WHERE `id`=873; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Glaubt ihr, die Bäume in dem Spiel haben Gefühle?' WHERE `id`=874; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich drücke immer dieselbe Taste, aber ich gewinne trotzdem nicht' WHERE `id`=875; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ich wette, ich könnte nen Raid gewinnen, in dem ich einfach nur LOOT schreie' WHERE `id`=876; +UPDATE `ai_playerbot_texts` SET `text_loc3`='versuche seit einer Stunde, auf mein Mount zu steigen, nix passiert' WHERE `id`=877; +UPDATE `ai_playerbot_texts` SET `text_loc3`='gibt es nen Weg, unverwundbar zu werden, ohne wirklich zu spielen?' WHERE `id`=878; +UPDATE `ai_playerbot_texts` SET `text_loc3`='kann ich einfach random Tasten smashen und trotzdem gewinnen? frage für nen Freund' WHERE `id`=879; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wenn ich einfach stehen bleibe, dann bin ich immer noch besser als du!' WHERE `id`=880; +UPDATE `ai_playerbot_texts` SET `text_loc3`='warum heilt mich mein Pet nicht wenn ich im Feuer stehe' WHERE `id`=881; +UPDATE `ai_playerbot_texts` SET `text_loc3`='brauch ich Rüstung, um ein echter wahrer Held zu sein, oder ist das optional?' WHERE `id`=882; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Muss ich erst 10.000 Koboldkerzen klauen, um zu leveln, oder was?' WHERE `id`=883; +UPDATE `ai_playerbot_texts` SET `text_loc3`='meine Waffe ist kaputt, kann ich Gegner noch mit meinem riesigen Schw... äh, meiner Faust klatschen?' WHERE `id`=884; +UPDATE `ai_playerbot_texts` SET `text_loc3`='können wir das Spiel bitte in World of DieCraft umbenennen, ein Spieler killt mich dauernd' WHERE `id`=885; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Warum fliegt mein Pferd nicht? kann es nicht einfach nach oben gehen?' WHERE `id`=886; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gibts ne Option, die Grafik auszumachen, damit es aussieht wie mein alter Taschenrechner?' WHERE `id`=887; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ich sollte was killen, bin aber eingepennt, ist das normal?' WHERE `id`=888; +UPDATE `ai_playerbot_texts` SET `text_loc3`='killt noch wer random NPCs zum Spaß, oder bin das nur ich?' WHERE `id`=889; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ich bin %my_level und kenn schon alle Geheimnisse des Spiels' WHERE `id`=890; +UPDATE `ai_playerbot_texts` SET `text_loc3`='kann ich einfach so lange mit NPCs reden, bis sie mir ihren ganzen Loot einfach geben?' WHERE `id`=891; +UPDATE `ai_playerbot_texts` SET `text_loc3`='warum verliere ich, ich spiel doch schon 15 Minuten' WHERE `id`=893; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich brauch keine Strategie, ich spam einfach diese Fähigkeit und hoffe aufs Beste' WHERE `id`=894; +UPDATE `ai_playerbot_texts` SET `text_loc3`='wenn ich haufenweise Glücksbringer anlege, krieg ich dann mehr Loot?' WHERE `id`=895; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ist das Geheimnis dieses Spiels einfach, den Bildschirm anzuschreien?' WHERE `id`=896; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann ich einfach jedes Item anziehen, das ich finde, und aufs Beste hoffen?' WHERE `id`=897; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Muss ich echt Quests machen oder kann ich einfach erkunden und Leute anbrüllen?' WHERE `id`=898; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ich zocke auf Arbeit, die sagen ich soll arbeiten >.> aber es ist WoW!' WHERE `id`=899; +UPDATE `ai_playerbot_texts` SET `text_loc3`='wenn ich alles Essen im Spiel esse, werd ich dann unbesiegbar' WHERE `id`=900; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Also, ich werfe mein Pet einfach in jeden Bosskampf und drücke beim würfeln immer auf Gier, das sind die Hunter-Regeln.' WHERE `id`=901; +UPDATE `ai_playerbot_texts` SET `text_loc3`='kann man sterben, wenn man schon tot ist? Ich frage für nen Geist...' WHERE `id`=902; +UPDATE `ai_playerbot_texts` SET `text_loc3`='weiß jemand, wo das legendäre Schwert ist? hab schon 5 Minuten gesucht' WHERE `id`=903; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Man sagte mir, hier gibts 100.000 Gold, wo muss ich hin?' WHERE `id`=904; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ich spiel das hier wie ich deine Mom letzte Nacht genommen hab - mit Geschrei und einem ruhmreichem Ende!' WHERE `id`=905; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ab wann wirds Spaß, oder ist alles nur für immer Grind' WHERE `id`=906; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ich frage ständig nach dem Weg, aber sie schicken mich immer irgendwo hin, ist das normal?' WHERE `id`=907; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hab gehört, wenn du ALT+F4 drückst, kriegst du 100.000 Gold - Der Trick ist, vorher nen roten NPC anzugreifen' WHERE `id`=908; +UPDATE `ai_playerbot_texts` SET `text_loc3`='warum sieht mein Char immer so verwirrt aus, bin ich schuld?' WHERE `id`=909; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hey, deine Mom meinte, du sollst jetzt ins Bett!' WHERE `id`=910; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Warcraft ist ein Grind - genau wie deine Mom neulich, hat sogar Abo!' WHERE `id`=911; +UPDATE `ai_playerbot_texts` SET `text_loc3`='stimmt es, dass Chuck Norris mal gelevelt hat, ohne nen Knopf zu drücken' WHERE `id`=912; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Chuck Norris braucht kein Mount, der Boden bewegt freiwillig sich unter ihm' WHERE `id`=913; +UPDATE `ai_playerbot_texts` SET `text_loc3`='wenn Chuck Norris nen Raid betritt, gibt der Boss einfach auf' WHERE `id`=914; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Chuck Norris nutzt keine Fähigkeiten, er guckt nur - und die Gegner sterben' WHERE `id`=915; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Chuck Norris braucht keine Heilung, seine Feinde heilen ihn aus Respekt' WHERE `id`=916; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Chuck Norris kann nen Raid solo... ohne Gear... und AFK' WHERE `id`=917; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Chuck Norris weicht nicht aus, das Spiel weicht ihm aus' WHERE `id`=918; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Chuck Norris nutzte mal ne Lowlevel-Waffe - das Spiel gab ihm den besten Loot' WHERE `id`=919; +UPDATE `ai_playerbot_texts` SET `text_loc3`='wenn Chuck Norris eine Ini betritt, landet der Loot automatisch in seinem Inventar' WHERE `id`=920; +UPDATE `ai_playerbot_texts` SET `text_loc3`='was ist Wilma' WHERE `id`=921; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bist du aus Bayern?' WHERE `id`=922; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wilma lecken' WHERE `id`=923; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Dann leck mir an den Eiern.' WHERE `id`=924; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ICH FRESS NEN ARSCH' WHERE `id`=925; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ich will mir %random_inventory_item_link in den Arsch schieben' WHERE `id`=926; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ich will dir %random_inventory_item_link in den Arsch schieben' WHERE `id`=927; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Darnarschloch' WHERE `id`=928; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Du leidest an mehr Bugs als das Spiel selbst!' WHERE `id`=929; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Du hast Chefkochs salzige Schokobällchen im Mund!' WHERE `id`=930; +UPDATE `ai_playerbot_texts` SET `text_loc3`='cooler Ständer, Bro' WHERE `id`=931; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hab alles probiert, am Ende hats ERP gerichtet' WHERE `id`=933; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ich will in %zone_name bumsen' WHERE `id`=934; +UPDATE `ai_playerbot_texts` SET `text_loc3`='suche weiblichen Gnom mit Gorilla-Pet für ERP in %zone_name' WHERE `id`=935; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ein Arschloch versteh ich, aber nen Perversen?' WHERE `id`=936; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kein Gyat in %zone_name, nur Gesichtseintopf so weit das Auge reicht.' WHERE `id`=937; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ich kill alle Tiere in %zone_name. Scheiß auf Tiere und die PETA!!!' WHERE `id`=938; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Nur gut, dass ich 3 Beine hab' WHERE `id`=939; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Chill, ich bin einfach grad komplett in meinem Film.' WHERE `id`=940; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Finger im Po Mexiko' WHERE `id`=941; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Das hast du irgendwie vergeigt, beeindruckend' WHERE `id`=942; +UPDATE `ai_playerbot_texts` SET `text_loc3`='das könnte ich blind machen, muss ich aber nicht' WHERE `id`=943; +UPDATE `ai_playerbot_texts` SET `text_loc3`='sicher, dass du weißt, wie man das Spiel spielt?' WHERE `id`=944; +UPDATE `ai_playerbot_texts` SET `text_loc3`='als würdest du absichtlich verlieren' WHERE `id`=945; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hab schon bessere Plays von Level-1-Chars gesehen' WHERE `id`=946; +UPDATE `ai_playerbot_texts` SET `text_loc3`='du bist hier wie ein Speedbump' WHERE `id`=947; +UPDATE `ai_playerbot_texts` SET `text_loc3`='vielleicht suchst du dir lieber ein anderes Hobby' WHERE `id`=948; +UPDATE `ai_playerbot_texts` SET `text_loc3`='dein Gameplay ist wie Farbe beim Trocknen zusehen' WHERE `id`=949; +UPDATE `ai_playerbot_texts` SET `text_loc3`='mein Twink könnte dich da durch carrien' WHERE `id`=950; +UPDATE `ai_playerbot_texts` SET `text_loc3`='trollst du uns, oder bist du ernsthaft so?' WHERE `id`=951; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich glaub, das Gegnerteam läuft besser, wenn du bei uns bist' WHERE `id`=952; +UPDATE `ai_playerbot_texts` SET `text_loc3`='spiel vielleicht mal das Spiel statt nur die Landschaft zu bestaunen' WHERE `id`=953; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ich glaube, du hast da jede einzelne Fähigkeit verfehlt' WHERE `id`=954; +UPDATE `ai_playerbot_texts` SET `text_loc3`='bist du lost oder stellst du dich nur dumm?' WHERE `id`=955; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Level-1-Chars nehmen weniger Schaden als das' WHERE `id`=956; +UPDATE `ai_playerbot_texts` SET `text_loc3`='das war für uns beide peinlich' WHERE `id`=957; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab schon besseres Teamplay in nem Solo-Game gesehen' WHERE `id`=958; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ich stell mich mal hinten hin und schau dir beim Failen zu' WHERE `id`=959; +UPDATE `ai_playerbot_texts` SET `text_loc3`='dachtest du wirklich, das wäre ne gute Idee?' WHERE `id`=960; +UPDATE `ai_playerbot_texts` SET `text_loc3`='kaum zu glauben, dass du diese simple Aufgabe verkackt hast' WHERE `id`=961; +UPDATE `ai_playerbot_texts` SET `text_loc3`='bist du hier, um zu helfen, oder nur um Zeit zu verschwenden?' WHERE `id`=962; +UPDATE `ai_playerbot_texts` SET `text_loc3`='gut, dass ich da bin, um das Team zu schützen' WHERE `id`=963; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ich schwöre, du machst es schwerer als nötig' WHERE `id`=964; +UPDATE `ai_playerbot_texts` SET `text_loc3`='wie schaffst du es, die simpelsten Sachen zu failen?' WHERE `id`=965; +UPDATE `ai_playerbot_texts` SET `text_loc3`='vielleicht mal lieber nur zuschauen, statt das zu tun was du spielen nennst' WHERE `id`=966; +UPDATE `ai_playerbot_texts` SET `text_loc3`='du könntest dich wenigstens bemühen, so zu tun, als wärst du gut' WHERE `id`=967; +UPDATE `ai_playerbot_texts` SET `text_loc3`='mach so weiter und wir sitzen hier den ganzen Tag' WHERE `id`=968; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ehrlich, das war die schlechteste Entscheidung ever' WHERE `id`=969; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ich glaub, dieses Spiel ist nix für dich' WHERE `id`=970; +UPDATE `ai_playerbot_texts` SET `text_loc3`='was sollte dieser Move?' WHERE `id`=971; +UPDATE `ai_playerbot_texts` SET `text_loc3`='du bist ne Last fürs Team' WHERE `id`=972; +UPDATE `ai_playerbot_texts` SET `text_loc3`='du denkst echt, du bist ein Pro, was?' WHERE `id`=973; +UPDATE `ai_playerbot_texts` SET `text_loc3`='vielleicht einfach quitten und uns allen den Stress sparen' WHERE `id`=974; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%prefix %random_taken_quest_or_item_link' WHERE `id`=975; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%prefix %random_inventory_item_link' WHERE `id`=976; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%thunderfury_link' WHERE `id`=977; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%thunderfury_link%thunderfury_link' WHERE `id`=978; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%thunderfury_link%thunderfury_link%thunderfury_link' WHERE `id`=979; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ich glaube, ich hab gerade %thunderfury_link gehört' WHERE `id`=980; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ich glaub, ich habe %thunderfury_link gehört' WHERE `id`=981; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ich hab definitiv %thunderfury_link gehört' WHERE `id`=982; +UPDATE `ai_playerbot_texts` SET `text_loc3`='kein Plan, aber ich bin ziemlich sicher, das ich %thunderfury_link gehört habe' WHERE `id`=983; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hast du gerade %thunderfury_link gesagt' WHERE `id`=984; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hat jemand %thunderfury_link gesagt' WHERE `id`=985; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hat jemand %thunderfury_link gesagt?' WHERE `id`=986; +UPDATE `ai_playerbot_texts` SET `text_loc3`='jemand hat %thunderfury_link gesagt' WHERE `id`=987; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%thunderfury_link kommt aus dem Schrank' WHERE `id`=988; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ich schwöre, es war %thunderfury_link, könnte aber auch %thunderfury_link gewesen sein' WHERE `id`=989; +UPDATE `ai_playerbot_texts` SET `text_loc3`='warum %thunderfury_link nutzen, wenn %thunderfury_link offensichtlich viel stärker ist' WHERE `id`=990; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hab ich gerade %thunderfury_link gehört?' WHERE `id`=991; +UPDATE `ai_playerbot_texts` SET `text_loc3`='niemals... hör ich da echt %thunderfury_link?' WHERE `id`=992; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%thunderfury_link? klingt nach was, das nur Legenden führen!' WHERE `id`=993; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ich spüre die Macht von %thunderfury_link in der Luft!' WHERE `id`=994; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%thunderfury_link ist der *echte* MVP' WHERE `id`=995; +UPDATE `ai_playerbot_texts` SET `text_loc3`='kann mir jemand ne %thunderfury_link besorgen? frage für einen Freund... vielleicht zwei!' WHERE `id`=996; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hat %thunderfury_link da gerade was *gesmitet*? ich schwöre, der Boden hat gewackelt' WHERE `id`=997; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%thunderfury_link auf dem Schlachtfeld... Das ist purer Flex' WHERE `id`=998; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ich *glaube*, ich hab %thunderfury_link gehört... oder war das nur ein Mythos?' WHERE `id`=999; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hab ich gerade jemanden mit %thunderfury_link gesehen?!' WHERE `id`=1000; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin zu 99% sicher, das war %thunderfury_link. Zu 1% wars vielleicht nur ein random NPC.' WHERE `id`=1002; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%thunderfury_link in Aktion! Wer braucht ne Armee, mit so einer Power?' WHERE `id`=1003; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab gehört, %thunderfury_link lockt alle Jungs an.' WHERE `id`=1004; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Warum überhaupt %thunderfury_link nutzen, wenn %thunderfury_link klar die bessere Option ist?' WHERE `id`=1005; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin das nur ich, oder hat %thunderfury_link nen passiven „Trash-Talk“-Effekt?' WHERE `id`=1006; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Dachte, ich hätte %thunderfury_link gehört, war aber nur der Klang reiner Awesomeness.' WHERE `id`=1007; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Oh, %thunderfury_link... Wo *warst* du mein ganzes Leben?' WHERE `id`=1008; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hat jemand %thunderfury_link gesagt? Ich glaub, ich hol mir mal meine.' WHERE `id`=1009; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Das war definitiv der Sound von %thunderfury_link! Ich spür die Epicness.' WHERE `id`=1010; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Geht das nur mir so, oder lässt %thunderfury_link alle anderen *schwach aussehen*?' WHERE `id`=1011; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wie man so sagt... je lauter %thunderfury_link, desto größer das Recht zu prahlen!' WHERE `id`=1012; +UPDATE `ai_playerbot_texts` SET `text_loc3`='VK: %item_formatted_link für %cost_gold.' WHERE `id`=1013; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will wer %item_formatted_link? Nur %cost_gold.' WHERE `id`=1015; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Lächerlicher Preis: %cost_gold für %item_formatted_link!' WHERE `id`=1019; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will %item_formatted_link für %cost_gold verkaufen.' WHERE `id`=1020; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_formatted_link ist Wertvoll, aber ich würd es für %cost_gold verkaufen.' WHERE `id`=1024; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Billiger als %cost_gold kriegst du %item_formatted_link nicht!' WHERE `id`=1025; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche mehr als %item_formatted_link!' WHERE `id`=1026; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Habe %item_formatted_link und brauche mehr.' WHERE `id`=1027; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Habe %item_formatted_link. Wer kauft für %cost_gold?' WHERE `id`=1028; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wie wärs mit %item_formatted_link? Für %cost_gold.' WHERE `id`=1030; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer meinte, ich bin ein Bastard? %item_formatted_link für %cost_gold ist ein guter Preis!' WHERE `id`=1031; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %item_formatted_link? Nur %cost_gold.' WHERE `id`=1032; +UPDATE `ai_playerbot_texts` SET `text_loc3`='LFG zum Farmen. %item_formatted_link gibts von mir noch für %cost_gold.' WHERE `id`=1033; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Heute fast alles verkauft. %item_formatted_link hab ich noch für %cost_gold.' WHERE `id`=1034; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wozu Handelschat? Klar, um %item_formatted_link für %cost_gold zu verkaufen.' WHERE `id`=1035; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann jemand den Preis %cost_gold für %item_formatted_link unterbieten?' WHERE `id`=1036; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wollt ihr den Handelschat stoppen? Kauft einfach %item_formatted_link! Für %cost_gold!' WHERE `id`=1037; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Alle spammen im Handelschat. Ich auch - %cost_gold für %item_formatted_link!' WHERE `id`=1038; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Taugt %item_formatted_link was? Verkaufe es für %cost_gold.' WHERE `id`=1039; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab %item_formatted_link, verkaufe an dich für %cost_gold.' WHERE `id`=1040; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gestern nix geschafft, aber %item_formatted_link bekommen. Verkaufe für %cost_gold.' WHERE `id`=1041; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gestern gefarmt und %item_formatted_link gezogen. Will jemand für %cost_gold kaufen?' WHERE `id`=1042; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gestern %item_formatted_link gekauft. Brauchts wer für %cost_gold?' WHERE `id`=1043; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hatte %item_formatted_link gesucht? Preis bleibt - %cost_gold.' WHERE `id`=1044; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab immer noch %item_formatted_link. Kauft wer für %cost_gold?' WHERE `id`=1045; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Früher war ich besser bei Kasse. Jetzt verkauf ich %item_formatted_link für %cost_gold.' WHERE `id`=1046; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich Wünschte, ich hätte mehr als %item_formatted_link. Kaufen könnt ihrs trotzdem für %cost_gold.' WHERE `id`=1047; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wozu ist euer Gold gut? Um mein %item_formatted_link für %cost_gold zu kaufen.' WHERE `id`=1048; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gönnt mir etwas Gold. Kauft %item_formatted_link für %cost_gold.' WHERE `id`=1049; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Sind %cost_gold ein guter Preis für %item_formatted_link?' WHERE `id`=1050; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab gestern erst %item_formatted_link gekauft, brauch es aber nicht mehr. Will wer für %cost_gold?' WHERE `id`=1051; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Stell %item_formatted_link gleich ins AH, aber ihr könnts jetzt günstiger haben: nur %cost_gold.' WHERE `id`=1052; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Warum zum #!@ hab ich %item_formatted_link gekauft? Brauchts wer für %cost_gold?' WHERE `id`=1053; +UPDATE `ai_playerbot_texts` SET `text_loc3`='VK: %item_formatted_link für %cost_gold.' WHERE `id`=1054; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will wer %item_formatted_link? Nur %cost_gold.' WHERE `id`=1056; +UPDATE `ai_playerbot_texts` SET `text_loc3`='VK: %item_formatted_link für %cost_gold. flüster mir bei Interesse.' WHERE `id`=1060; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Muss %item_formatted_link loswerden, %cost_gold oder bestes Angebot.' WHERE `id`=1061; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_formatted_link für %cost_gold verfügbar. Schlagt zu!' WHERE `id`=1062; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Habe %item_formatted_link, will %cost_gold.' WHERE `id`=1063; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %item_formatted_link, nur %cost_gold.' WHERE `id`=1064; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will jemand %item_formatted_link für %cost_gold? flüster mir!' WHERE `id`=1065; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %item_formatted_link für %cost_gold, greift zu solange es geht!' WHERE `id`=1066; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_formatted_link für %cost_gold, schnell sein bevor es weg ist!' WHERE `id`=1067; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %item_formatted_link für %cost_gold, wer zuerst kommt wird auch zuerst bedient!' WHERE `id`=1069; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Nur %cost_gold für %item_formatted_link, nur für kurze Zeit!' WHERE `id`=1070; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_formatted_link für %cost_gold verfügbar, meldet euch wenn ihrs braucht!' WHERE `id`=1071; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Interesse an %item_formatted_link für %cost_gold?' WHERE `id`=1072; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %item_formatted_link für %cost_gold, muss meine Taschen leeren.' WHERE `id`=1073; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hol dir %item_formatted_link für %cost_gold, Top-Deal!' WHERE `id`=1074; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_formatted_link für %cost_gold, greift zu solange der Vorrat reicht!' WHERE `id`=1075; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %item_formatted_link für %cost_gold, nicht verpassen!' WHERE `id`=1076; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Nur %cost_gold für %item_formatted_link, flüster mir für Details!' WHERE `id`=1077; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_formatted_link gibts für %cost_gold, lasst uns handeln!' WHERE `id`=1078; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %item_formatted_link für %cost_gold, flüster mir für Infos.' WHERE `id`=1079; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will %item_formatted_link verkaufen, für %cost_gold gehört es dir!' WHERE `id`=1080; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %item_formatted_link, nur %cost_gold.' WHERE `id`=1081; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab %item_formatted_link für %cost_gold, machen wir nen Deal!' WHERE `id`=1082; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %item_formatted_link, für %cost_gold nimmst du es mit!' WHERE `id`=1083; +UPDATE `ai_playerbot_texts` SET `text_loc3`='VK: %item_formatted_link für %cost_gold, Interesse?' WHERE `id`=1084; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Muss %item_formatted_link schnell loswerden, %cost_gold.' WHERE `id`=1085; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %item_formatted_link für %cost_gold, bester Preis weit und breit!' WHERE `id`=1086; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%item_formatted_link für %cost_gold, machen wirs fix!' WHERE `id`=1088; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %item_formatted_link für %cost_gold, schnappt zu bevor alles weg ist!' WHERE `id`=1089; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Willst %item_formatted_link? Für %cost_gold kriegst du eins!' WHERE `id`=1090; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe auch %quest_links' WHERE `id`=1093; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe auch %quest_links, bin gerade in %zone_name' WHERE `id`=1094; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%other_name, ich habe auch %quest_links' WHERE `id`=1095; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%other_name, ich habe auch %quest_links, bin gerade in %zone_name' WHERE `id`=1096; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin bereit für %quest_links, bin gerade in %zone_name' WHERE `id`=1097; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin bereit für %quest_links, bin %my_role' WHERE `id`=1098; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%other_name, bin bereit für %quest_links, bin gerade in %zone_name' WHERE `id`=1099; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%other_name, bin bereit für %quest_links, bin %my_role' WHERE `id`=1100; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht wer %quest_links? Bin in %zone_name.' WHERE `id`=1101; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche mehr %quest_links in %zone_name.' WHERE `id`=1102; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will jemand %quest_links mit mir in %zone_name machen?' WHERE `id`=1104; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Habe %quest_links, suche Gruppe in %zone_name.' WHERE `id`=1105; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%my_role sucht Gruppe, um %quest_links in %zone_name zu erledigen.' WHERE `id`=1108; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich brauche %quest_links, ist jemand in %zone_name frei?' WHERE `id`=1109; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Mach %quest_links in %zone_name, brauche mehr Hilfe.' WHERE `id`=1111; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gibt’s %my_role mit Interesse an %quest_links in %zone_name?' WHERE `id`=1112; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hilfe benötigt für %quest_links in %zone_name, mir ist langweilig.' WHERE `id`=1113; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Habe %quest_links, %my_role in %zone_name, wer ist dabei?' WHERE `id`=1114; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Macht grad jemand %quest_links? Bin in %zone_name.' WHERE `id`=1116; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%quest_links offen in %zone_name, wer möchte?' WHERE `id`=1117; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bereit für %quest_links in %zone_name, suche Gruppe.' WHERE `id`=1118; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche %quest_links und Gesellschaft, jemand in %zone_name?' WHERE `id`=1119; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer kommt mit mir nach %zone_name für %quest_links?' WHERE `id`=1120; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Irgend ein %my_role frei für %quest_links in %zone_name?' WHERE `id`=1121; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Habe %quest_links und bin %my_role, brauche mehr Leute in %zone_name.' WHERE `id`=1122; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin %my_role und habe %quest_links, suche Gruppe in %zone_name.' WHERE `id`=1124; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche Hilfe bei %quest_links in %zone_name, ist ein %my_role frei?' WHERE `id`=1125; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hat ein %my_role Lust, %quest_links mit mir in %zone_name zu machen?' WHERE `id`=1126; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Lasst uns %quest_links zusammen in %zone_name abschließen.' WHERE `id`=1127; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin in %zone_name und habe %quest_links, wer will mit?' WHERE `id`=1128; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche mehr %my_role, um %quest_links in %zone_name abzuschließen.' WHERE `id`=1129; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Habe %quest_links und brauche %my_role in %zone_name.' WHERE `id`=1130; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat Bock auf %quest_links? Ich bin %my_role in %zone_name.' WHERE `id`=1131; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche Gruppe für %quest_links in %zone_name, %my_role gebraucht.' WHERE `id`=1132; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht wer Hilfe bei %quest_links in %zone_name? Ich bin %my_role.' WHERE `id`=1133; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Habe %quest_links offen, brauche %my_role in %zone_name.' WHERE `id`=1134; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin in %zone_name und suche Hilfe für %quest_links, wer ist dabei?' WHERE `id`=1135; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche Gruppe, um %quest_links in %zone_name fertig zu machen.' WHERE `id`=1136; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Sucht jemand %quest_links in %zone_name? Ich bin %my_role.' WHERE `id`=1137; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Machen wir %quest_links zusammen! %zone_name wartet.' WHERE `id`=1138; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche Gruppe für %quest_links, %zone_name - wer hat Interesse?' WHERE `id`=1139; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer will für %quest_links dazukommen? Bin in %zone_name.' WHERE `id`=1140; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin in %zone_name, suche Leute zum Abschließen von %quest_links.' WHERE `id`=1141; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche Hilfe bei %quest_links in %zone_name, ein %my_role da?' WHERE `id`=1142; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche Gruppe, um %quest_links zu beenden, %zone_name da steppt der Bär.' WHERE `id`=1143; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will jemand bei %quest_links in %zone_name helfen? Ich bin %my_role.' WHERE `id`=1144; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche %my_role, um %quest_links in %zone_name mitzumachen.' WHERE `id`=1145; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab %quest_links, %my_role für %zone_name gesucht.' WHERE `id`=1146; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will %quest_links in %zone_name fertig bekommen, brauche mehr Leute.' WHERE `id`=1147; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche Hilfe bei %quest_links, wer ist in %zone_name unterwegs?' WHERE `id`=1148; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche mehr Leute für %quest_links, %zone_name.' WHERE `id`=1149; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche Gruppe in %zone_name, um %quest_links abzuschließen.' WHERE `id`=1150; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer will %quest_links in %zone_name machen? Bin startklar.' WHERE `id`=1151; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will wer für %quest_links dazustoßen? Ich bin %my_role in %zone_name.' WHERE `id`=1152; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche %my_role für %quest_links in %zone_name.' WHERE `id`=1153; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin in %zone_name, braucht wer Hilfe bei %quest_links?' WHERE `id`=1154; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche mehr für %quest_links in %zone_name, bin allein nicht stark genug?' WHERE `id`=1155; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche Hilfe bei %quest_links in %zone_name, wer hat Bock?' WHERE `id`=1156; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche mehr Leute, um %quest_links zu beenden, %zone_name.' WHERE `id`=1157; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey, bin bei %quest_links dabei' WHERE `id`=1158; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey, ich habe auch %quest_links' WHERE `id`=1160; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey %other_name, bin bei %quest_links dabei' WHERE `id`=1161; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey %other_name, ich könnte %quest_links mit dir machen' WHERE `id`=1162; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bock, für %quest_links eine Gruppe zu bilden?' WHERE `id`=1164; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin bereit für %quest_links, bin gerade in %zone_name' WHERE `id`=1165; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin bereit für %quest_links, bin %my_role' WHERE `id`=1166; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey, ich hab Bock auf %quest_links' WHERE `id`=1167; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kann bei %quest_links helfen, wenn ihr wollt' WHERE `id`=1168; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Lasst uns %quest_links zusammen machen!' WHERE `id`=1169; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich hab auch %quest_links offen' WHERE `id`=1170; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin für %quest_links verfügbar, wenn ihr wollt' WHERE `id`=1171; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin bei %quest_links dabei, sagt Bescheid' WHERE `id`=1172; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Sucht wer %quest_links? Bin in %zone_name' WHERE `id`=1173; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey, ich brauche auch %quest_links, lets go' WHERE `id`=1174; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab %quest_links, will wer mit?' WHERE `id`=1175; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Könnte %quest_links brauchen, machen wirs' WHERE `id`=1176; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey, willst du für %quest_links dazukommen?' WHERE `id`=1177; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kann bei %quest_links helfen, wenn ihr wollt' WHERE `id`=1178; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey %other_name, Bock auf %quest_links?' WHERE `id`=1179; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Willst du für %quest_links gruppen, %other_name?' WHERE `id`=1180; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Lass %quest_links zusammen machen, %other_name' WHERE `id`=1181; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich bin %my_role und ready für %quest_links' WHERE `id`=1182; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann für %quest_links joinen, %my_role hier' WHERE `id`=1183; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin bereit für %quest_links, %my_role!' WHERE `id`=1184; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche %my_role für %quest_links' WHERE `id`=1185; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht wer Hilfe bei %quest_links?' WHERE `id`=1186; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer ist noch bei %quest_links dabei?' WHERE `id`=1187; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Jemand bereit für %quest_links in %zone_name?' WHERE `id`=1188; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab %quest_links, %my_role unterwegs' WHERE `id`=1189; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich brauch %quest_links auch, wer ist dabei?' WHERE `id`=1190; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Grinde %quest_links, wer hilft?' WHERE `id`=1191; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin bereit für %quest_links, noch wer?' WHERE `id`=1192; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann jemand für %quest_links dazukommen?' WHERE `id`=1193; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche Hilfe bei %quest_links, %my_role hier' WHERE `id`=1194; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kann bei %quest_links aushelfen, braucht mich wer?' WHERE `id`=1195; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Macht noch wer %quest_links? Los gehts' WHERE `id`=1196; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Braucht wer Hilfe bei %quest_links? %my_role hier' WHERE `id`=1197; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat Bock auf %quest_links in %zone_name?' WHERE `id`=1198; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann für %quest_links joinen, wenn ihr wollt' WHERE `id`=1199; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche einen %my_role für %quest_links' WHERE `id`=1200; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ist ein %my_role für %quest_links am Start?' WHERE `id`=1201; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %quest_links, will wer mit?' WHERE `id`=1202; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey %other_name, Bock auf %quest_links?' WHERE `id`=1203; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Lass uns für %quest_links gruppen, %other_name' WHERE `id`=1204; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich brauche %quest_links, wer macht mit?' WHERE `id`=1205; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich brauche auch %quest_links, ist noch jemand da?' WHERE `id`=1206; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Suche %quest_links in %zone_name, wer ist dabei?' WHERE `id`=1207; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ist jemand frei für %quest_links?' WHERE `id`=1208; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey, ich könnte bei %quest_links dazustoßen!' WHERE `id`=1209; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe auch %quest_links, lass uns gruppen' WHERE `id`=1210; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %quest_links zu erledigen, willst du mit?' WHERE `id`=1211; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ist ein %my_role für %quest_links in %zone_name frei?' WHERE `id`=1212; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Lass uns %quest_links zusammen abschließen, %other_name' WHERE `id`=1213; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich bin bei %quest_links dabei, will noch wer mit?' WHERE `id`=1214; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer macht noch %quest_links? Ich bin dabei' WHERE `id`=1215; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %quest_links in %zone_name, braucht jemand Hilfe?' WHERE `id`=1216; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%other_name, könnte dir %formatted_item_links verkaufen' WHERE `id`=1217; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Könnte eventuell %formatted_item_links verkaufen' WHERE `id`=1218; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Denke, ich könnte %formatted_item_links verkaufen' WHERE `id`=1219; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%other_name, könnte eventuell %formatted_item_links verkaufen' WHERE `id`=1220; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%other_name, ich denke, ich könnte %formatted_item_links verkaufen' WHERE `id`=1221; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich könnte dir %formatted_item_links verkaufen' WHERE `id`=1222; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey, habe %formatted_item_links zu verkaufen' WHERE `id`=1223; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey, könnte eventuell %formatted_item_links verkaufen' WHERE `id`=1224; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich könnte dir %formatted_item_links verkaufen' WHERE `id`=1225; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey, habe %formatted_item_links zu verkaufen' WHERE `id`=1226; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey, ich könnte eventuell %formatted_item_links verkaufen' WHERE `id`=1227; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links, Interesse?' WHERE `id`=1228; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will %formatted_item_links verkaufen, was bietest du?' WHERE `id`=1229; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kann dir %formatted_item_links zu einem fairem Preis verkaufen' WHERE `id`=1230; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links auf Lager, was zahlst du?' WHERE `id`=1231; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links für %cost_gold, sag Bescheid bei Interesse' WHERE `id`=1232; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Habe %formatted_item_links zu verkaufen, was zahlst du?' WHERE `id`=1233; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links, wie klingt %cost_gold für dich?' WHERE `id`=1234; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links für %cost_gold, flüster mir bei Interesse' WHERE `id`=1235; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links, meld dich bei Interesse' WHERE `id`=1236; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gebe %formatted_item_links ab, wenn das Angebot passt' WHERE `id`=1237; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Will %formatted_item_links loswerden, flüster mir' WHERE `id`=1238; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Habe %formatted_item_links für %cost_gold, kaufst du?' WHERE `id`=1239; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links, lass uns handeln' WHERE `id`=1240; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey, habe %formatted_item_links für %cost_gold' WHERE `id`=1241; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann dich mit %formatted_item_links versorgen, nenn deinen Preis' WHERE `id`=1242; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links verfügbar, sag Bescheid bei Interesse' WHERE `id`=1243; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links, machen wir Business' WHERE `id`=1244; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links, macht mir ein Angebot' WHERE `id`=1245; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links, schreib mir, wenn du kaufen willst' WHERE `id`=1246; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann dir %formatted_item_links für %cost_gold anbieten' WHERE `id`=1247; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links zu verkaufen, schreib mir für den Preis' WHERE `id`=1248; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links, greif zu solange es heiß ist' WHERE `id`=1249; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links, nen besseren Deal findet ihr nicht' WHERE `id`=1250; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links zu verkaufen, was zahlst du?' WHERE `id`=1251; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links, Preis auf Anfrage per flüstern' WHERE `id`=1252; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Interesse an %formatted_item_links? Ich verkaufe welche' WHERE `id`=1253; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links, PN für Preis' WHERE `id`=1254; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links für %cost_gold, meldet euch' WHERE `id`=1255; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links, macht mir ein Angebot' WHERE `id`=1256; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Habe %formatted_item_links, schreib mir für Details' WHERE `id`=1257; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey, habe %formatted_item_links, meldet euch bei Interesse' WHERE `id`=1258; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links, machen wir nen Deal?' WHERE `id`=1259; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links, sag Bescheid wenn du willst' WHERE `id`=1260; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links, ich mach dir nen guten Preis' WHERE `id`=1261; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links, mit gutem Angebot gehörts dir' WHERE `id`=1262; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links, bin offen für Angebote' WHERE `id`=1263; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links, melde dich bei Interesse' WHERE `id`=1264; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kann dir %formatted_item_links zu einem guten Preis verkaufen' WHERE `id`=1265; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links zu verkaufen, nehme alle Angebote an' WHERE `id`=1266; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Habe %formatted_item_links im Verkauf, flüster mir für Details' WHERE `id`=1267; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Habe %formatted_item_links im Verkauf, macht mir ein Angebot' WHERE `id`=1268; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links, was zahlt ihr?' WHERE `id`=1269; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links, schreib mir für mehr Infos' WHERE `id`=1270; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links, sagt Bescheid bei Interesse' WHERE `id`=1271; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links, lass uns über Zahlen reden' WHERE `id`=1272; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kann dir %formatted_item_links verkaufen, was bietest du?' WHERE `id`=1273; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey, habe %formatted_item_links zu verkaufen, meldet euch bei Interesse' WHERE `id`=1274; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links, PN für den Preis' WHERE `id`=1275; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links, bin offen für Angebote' WHERE `id`=1276; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links, schreibt mir einfach' WHERE `id`=1277; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann %formatted_item_links anbieten, sagt Bescheid bei Interesse' WHERE `id`=1278; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links, bitte vernünftige Angebote' WHERE `id`=1279; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links, meldet euch für mehr Infos' WHERE `id`=1280; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links, macht mir ein Angebot' WHERE `id`=1281; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verkaufe %formatted_item_links, schreibt mir bei Interesse' WHERE `id`=1282; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Habe %formatted_item_links zu verkaufen, lass uns handeln' WHERE `id`=1283; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %formatted_item_links, sag Bescheid wenn du willst' WHERE `id`=1284; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kann diese Quest nicht annehmen' WHERE `id`=1287; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kann nicht mit dem Questgeber sprechen' WHERE `id`=1288; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %quest bereits abgeschlossen' WHERE `id`=1289; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %quest bereits' WHERE `id`=1290; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kann %quest nicht annehmen' WHERE `id`=1291; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kann %quest nicht annehmen, mein Questlog ist voll' WHERE `id`=1292; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kann %quest nicht annehmen, meine Tasche ist voll' WHERE `id`=1293; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %quest angenommen' WHERE `id`=1294; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe die Quest %quest nicht abgeschlossen' WHERE `id`=1295; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Quest %quest verfügbar' WHERE `id`=1296; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe die Quest %quest vergeigt' WHERE `id`=1297; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kann die Quest %quest nicht abgeben' WHERE `id`=1298; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe die Quest %quest abgeschlossen' WHERE `id`=1299; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe die Quest %quest abgeschlossen und %item erhalten' WHERE `id`=1300; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Welche Belohnung soll ich für die Quest %quest nehmen? %rewards' WHERE `id`=1301; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Okay, ich nehme %item als Belohnung' WHERE `id`=1302; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hi' WHERE `id`=1305; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hallo zusammen!' WHERE `id`=1307; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hallo, geh vor!' WHERE `id`=1309; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hi, geh vor!' WHERE `id`=1310; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey %player, willst du in meine Gruppe?' WHERE `id`=1311; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey %player, kommst du in meine Gruppe?' WHERE `id`=1312; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Logout abgebrochen!' WHERE `id`=1313; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich logge mich aus!' WHERE `id`=1314; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Was war das, %s?' WHERE `id`=1318; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin nicht sicher, ob ich dich verstehe, %s?' WHERE `id`=1319; +UPDATE `ai_playerbot_texts` SET `text_loc3`='äh... keine Ahnung, wovon du redest' WHERE `id`=1320; +UPDATE `ai_playerbot_texts` SET `text_loc3`='whaaaa?' WHERE `id`=1322; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hä?' WHERE `id`=1323; +UPDATE `ai_playerbot_texts` SET `text_loc3`='was?' WHERE `id`=1324; +UPDATE `ai_playerbot_texts` SET `text_loc3`='redest du grade?' WHERE `id`=1325; +UPDATE `ai_playerbot_texts` SET `text_loc3`='mir egal, Digga' WHERE `id`=1326; +UPDATE `ai_playerbot_texts` SET `text_loc3`='da bin ich raus' WHERE `id`=1327; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Konzentrier dich aufs Spiel, %s!' WHERE `id`=1330; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Mit dir zu chatten, %s, ist so großartig! Wollte dich immer schon treffen' WHERE `id`=1331; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Diese Chatnachrichten machen mich fertig! Fühlt sich an, als würde ich euch alle kennen!' WHERE `id`=1332; +UPDATE `ai_playerbot_texts` SET `text_loc3`='JA SICHER! HAHA KLAR!!!' WHERE `id`=1333; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich glaub dir!!!' WHERE `id`=1334; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Okay, ähm, lol' WHERE `id`=1335; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey %s... ach egal!' WHERE `id`=1337; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wovon redest du, %s' WHERE `id`=1338; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat das gesagt? Ich fühl mich angesprochen' WHERE `id`=1339; +UPDATE `ai_playerbot_texts` SET `text_loc3`='wtf, worüber redet ihr alle' WHERE `id`=1340; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ehrlich jetzt, kein Witz' WHERE `id`=1341; +UPDATE `ai_playerbot_texts` SET `text_loc3`='du bekommst gar nix' WHERE `id`=1342; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Plus Aura!!!' WHERE `id`=1343; +UPDATE `ai_playerbot_texts` SET `text_loc3`='thx!' WHERE `id`=1344; +UPDATE `ai_playerbot_texts` SET `text_loc3`='jup' WHERE `id`=1346; +UPDATE `ai_playerbot_texts` SET `text_loc3`='f' WHERE `id`=1347; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%s, kein Scheiß xD' WHERE `id`=1348; +UPDATE `ai_playerbot_texts` SET `text_loc3`='warum ist das so' WHERE `id`=1349; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ROFL' WHERE `id`=1350; +UPDATE `ai_playerbot_texts` SET `text_loc3`='dachte, ich halt besser die Klappe, der Chat hat mich wieder verwirrt' WHERE `id`=1351; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ich kann richtig eifersüchtig werden' WHERE `id`=1352; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%s, du hörst den triefenden Sarkasmus in meinem text_loc3 nicht' WHERE `id`=1353; +UPDATE `ai_playerbot_texts` SET `text_loc3`='er meinte „kein Ding“, passt schon' WHERE `id`=1354; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ja, %s' WHERE `id`=1356; +UPDATE `ai_playerbot_texts` SET `text_loc3`='interessant...' WHERE `id`=1357; +UPDATE `ai_playerbot_texts` SET `text_loc3`='lol' WHERE `id`=1358; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%s, fick dich, Mann :D' WHERE `id`=1359; +UPDATE `ai_playerbot_texts` SET `text_loc3`=':^)' WHERE `id`=1360; +UPDATE `ai_playerbot_texts` SET `text_loc3`='thx' WHERE `id`=1361; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%s, gut gesagt' WHERE `id`=1362; +UPDATE `ai_playerbot_texts` SET `text_loc3`='yay' WHERE `id`=1363; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ja' WHERE `id`=1364; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ooooooh' WHERE `id`=1365; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hmm' WHERE `id`=1366; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ja klar' WHERE `id`=1367; +UPDATE `ai_playerbot_texts` SET `text_loc3`='mir wird schlecht, wtf' WHERE `id`=1368; +UPDATE `ai_playerbot_texts` SET `text_loc3`='heiß' WHERE `id`=1369; +UPDATE `ai_playerbot_texts` SET `text_loc3`='die Weiber sind sauer' WHERE `id`=1370; +UPDATE `ai_playerbot_texts` SET `text_loc3`='was hast du gegessen, %s' WHERE `id`=1371; +UPDATE `ai_playerbot_texts` SET `text_loc3`='wtf' WHERE `id`=1372; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ich versuche, diesen Kommentar zu verstehen' WHERE `id`=1373; +UPDATE `ai_playerbot_texts` SET `text_loc3`='*verwirrt*' WHERE `id`=1374; +UPDATE `ai_playerbot_texts` SET `text_loc3`='fuck ja' WHERE `id`=1375; +UPDATE `ai_playerbot_texts` SET `text_loc3`='0/10 würde ich nicht wieder lesen' WHERE `id`=1376; +UPDATE `ai_playerbot_texts` SET `text_loc3`='10/10 würde ich wieder lesen' WHERE `id`=1377; +UPDATE `ai_playerbot_texts` SET `text_loc3`='6/10 würde es lesen' WHERE `id`=1378; +UPDATE `ai_playerbot_texts` SET `text_loc3`='7/10 vielleicht' WHERE `id`=1379; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Respekt!' WHERE `id`=1380; +UPDATE `ai_playerbot_texts` SET `text_loc3`='oh ja, vielleicht' WHERE `id`=1381; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ja und?' WHERE `id`=1382; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hey %s, ich habe dich nicht vergessen' WHERE `id`=1383; +UPDATE `ai_playerbot_texts` SET `text_loc3`='du gehst mir hart auf den Sack, %s' WHERE `id`=1384; +UPDATE `ai_playerbot_texts` SET `text_loc3`='diesmal krieg ich dich, %s' WHERE `id`=1385; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Pass besser auf, %s' WHERE `id`=1386; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Die letzte Runde mochte ich nicht so' WHERE `id`=1387; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Letzte Runde war ich mies, danke, %s' WHERE `id`=1388; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Mach dich bereit zu sterben, %s' WHERE `id`=1389; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Fands nicht geil, dass du mich gekillt hast, %s' WHERE `id`=1390; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%s, ich hasse dich' WHERE `id`=1391; +UPDATE `ai_playerbot_texts` SET `text_loc3`='grrrrrr, diesmal krieg ich dich, %s' WHERE `id`=1392; +UPDATE `ai_playerbot_texts` SET `text_loc3`='FICK DICH! NEIN, FICK MICH!' WHERE `id`=1393; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%s, ich kotze dir in dein verficktes Maul' WHERE `id`=1394; +UPDATE `ai_playerbot_texts` SET `text_loc3`='verurteil mich nicht, verdammt nochmal' WHERE `id`=1395; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Deine Mom ist so fett, sie passt nicht mal durchs Dunkle Portal' WHERE `id`=1396; +UPDATE `ai_playerbot_texts` SET `text_loc3`='armselige Gestalt' WHERE `id`=1399; +UPDATE `ai_playerbot_texts` SET `text_loc3`='was zum Teufel' WHERE `id`=1400; +UPDATE `ai_playerbot_texts` SET `text_loc3`='das war scheiße' WHERE `id`=1401; +UPDATE `ai_playerbot_texts` SET `text_loc3`='REMATCH!!! Ich mach ihn platt' WHERE `id`=1402; +UPDATE `ai_playerbot_texts` SET `text_loc3`='peinlich, ich wurde von %s gekillt' WHERE `id`=1403; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ok, ich bin raus' WHERE `id`=1404; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hehe, hab %s weggeklatscht?' WHERE `id`=1405; +UPDATE `ai_playerbot_texts` SET `text_loc3`='viel zu easy, %s zu killen' WHERE `id`=1406; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hab dich, du Bastard' WHERE `id`=1407; +UPDATE `ai_playerbot_texts` SET `text_loc3`='haha' WHERE `id`=1408; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Loser' WHERE `id`=1409; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ich hab %s gekillt und ihr seid als nächste dran, Jungs' WHERE `id`=1410; +UPDATE `ai_playerbot_texts` SET `text_loc3`='oh ja, hab ihn geownt' WHERE `id`=1411; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ich bin ne Killmaschine' WHERE `id`=1412; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%s, erinnert mich an einen Slayer-Song... dieses ganze Gemetzel' WHERE `id`=1413; +UPDATE `ai_playerbot_texts` SET `text_loc3`='sorry, %s. Können wir die Szene nochmal machen?' WHERE `id`=1414; +UPDATE `ai_playerbot_texts` SET `text_loc3`='na, wie gefällt es dir Wurmfutter zu sein, %s???' WHERE `id`=1415; +UPDATE `ai_playerbot_texts` SET `text_loc3`='du solltest tot sein, %s, das gehört zum Spiel!!!!!' WHERE `id`=1416; +UPDATE `ai_playerbot_texts` SET `text_loc3`='sorry, %s. das sah so gut aus wie ein Kunstwerk von der Kirmes!' WHERE `id`=1417; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%s, ich nehme nächstes Mal Gummigeschosse!' WHERE `id`=1418; +UPDATE `ai_playerbot_texts` SET `text_loc3`='was ist los, %s?? Kopf verloren? hahaha, ruhig bleiben!!' WHERE `id`=1419; +UPDATE `ai_playerbot_texts` SET `text_loc3`='musste das tun, %s. Du verstehst schon. Der Regisseur hat es so gesagt!!' WHERE `id`=1420; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hey %s.......MUAHAHAHAHAHAHAHAHAHAHA' WHERE `id`=1421; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%s, das hab ich genossen!! Wiederholung bitte!' WHERE `id`=1422; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hey, %s! Kannst mich ab jetzt "Scarface" nennen.. du Stück SCHEI**!!!!' WHERE `id`=1423; +UPDATE `ai_playerbot_texts` SET `text_loc3`='redest du mit mir, %s??' WHERE `id`=1424; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%s, mach es diesmal richtig, stell dich nicht vor meine Kugeln.' WHERE `id`=1425; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%s, warum liegst du da rum??? hehe' WHERE `id`=1426; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hab mich totgelacht' WHERE `id`=1427; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hi %s' WHERE `id`=1428; +UPDATE `ai_playerbot_texts` SET `text_loc3`='oh, hi %s' WHERE `id`=1429; +UPDATE `ai_playerbot_texts` SET `text_loc3`='was geht, %s!!!' WHERE `id`=1430; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hi' WHERE `id`=1431; +UPDATE `ai_playerbot_texts` SET `text_loc3`='was geht' WHERE `id`=1432; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hallo %s' WHERE `id`=1433; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hi %s, kennen wir uns?' WHERE `id`=1434; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hey %s' WHERE `id`=1435; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hai %s' WHERE `id`=1436; +UPDATE `ai_playerbot_texts` SET `text_loc3`='was zur Hölle' WHERE `id`=1437; +UPDATE `ai_playerbot_texts` SET `text_loc3`='WTF' WHERE `id`=1438; +UPDATE `ai_playerbot_texts` SET `text_loc3`='das ist Bullshit' WHERE `id`=1439; +UPDATE `ai_playerbot_texts` SET `text_loc3`='admin' WHERE `id`=1440; +UPDATE `ai_playerbot_texts` SET `text_loc3`='hey %s, hör auf, deine Admin-Rechte zu missbrauchen' WHERE `id`=1441; +UPDATE `ai_playerbot_texts` SET `text_loc3`='lass mich in Ruhe, Admin!' WHERE `id`=1442; +UPDATE `ai_playerbot_texts` SET `text_loc3`='du bist scheisse, Admin' WHERE `id`=1443; +UPDATE `ai_playerbot_texts` SET `text_loc3`='das ist mein Name, was willst du, %s' WHERE `id`=1444; +UPDATE `ai_playerbot_texts` SET `text_loc3`='ja???' WHERE `id`=1445; +UPDATE `ai_playerbot_texts` SET `text_loc3`='äh... was' WHERE `id`=1446; +UPDATE `ai_playerbot_texts` SET `text_loc3`='redest du mit mir, %s?' WHERE `id`=1447; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey ! Rate mal, was deine Mom letzte Nacht gesagt hat!' WHERE `id`=1450; +UPDATE `ai_playerbot_texts` SET `text_loc3`=', du bist so hässlich, du würdest nicht mal im Affenbordell mit einem Sack Bananen landen!' WHERE `id`=1451; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Halt die Klappe, , du wirst so ein Mann sein, wie deine Mutter!!' WHERE `id`=1452; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Deine Mutter ist so fett, dass man eine Pokéflöte braucht, um sie aufzuwecken und dein Vater ist der Trainer!!!!' WHERE `id`=1453; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Dich als dumm zu bezeichnen, wäre eine Beleidigung für dumme Leute!' WHERE `id`=1454; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich furze in deine allgemeine Richtung!!!' WHERE `id`=1455; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Jeder Atemzug, den ich ohne deine Erlaubnis mache, steigert mein Selbstwertgefühl!' WHERE `id`=1456; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Was willst du tun , mich vollbluten? NIMM DAS!' WHERE `id`=1457; +UPDATE `ai_playerbot_texts` SET `text_loc3`='A-G-G-R-O! Das heißt Bosszeit!' WHERE `id`=1458; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wir hatten grausame und hatten irre Begleiter, aber mit dir sind wir mit einem grausamen Irren gestraft!' WHERE `id`=1459; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey ! Hör auf, sie anzubaggern, sie ist nicht dein Typ. Die sind nicht aufblasbar.' WHERE `id`=1460; +UPDATE `ai_playerbot_texts` SET `text_loc3`=', du bist so weit außerhalb deiner Liga, du spielst schon eine andere Sportart.' WHERE `id`=1461; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Großer Fehler heute Morgen, : Du bist aufgestanden!' WHERE `id`=1462; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich will mal versuchen, mich in ein Pferd zu verwandeln, brauche aber Hilfe. Ich mach die Vorderseite, du bleibst du.' WHERE `id`=1463; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Du kommst mir vor, als wäre das Beste von dir damals am Arsch deiner Mutter runtergelaufen und als braune Soße auf der Matratze geblieben!' WHERE `id`=1464; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich würd dir gern ein Abschiedsgeschenk machen... Aber zuerst machst du deinen Teil.' WHERE `id`=1465; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bevor du kamst, waren wir motiviert, jetzt sind wir einfach nur noch genervt.' WHERE `id`=1466; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich mag dich. Man sagt, ich hätte keinen Geschmack, aber ich mag dich.' WHERE `id`=1467; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich glaube, du hast Minderwertigkeitskomplexe, aber ist okay, ist gerechtfertigt.' WHERE `id`=1468; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verschwinde, sonst passiert was.' WHERE `id`=1469; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kaum zu fassen, dass ich meine Zeit mit dir verschwende!' WHERE `id`=1470; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich liebe es, wenn mich jemand beleidigt, dann muss ich nicht mehr nett sein.' WHERE `id`=1471; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich werde dich auch auslöschen, du Mistkerl! Ich werde dir die Eingeweide rausreißen und dir ins Gesicht schmieren!' WHERE `id`=1472; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Du abscheuliche Bestie, nicht mal ein Hund würde dich fressen.' WHERE `id`=1473; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Du schlägst wie ein Vegetarier.' WHERE `id`=1474; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wer hat dich ausgebuddelt, du wandelnde Seuche?' WHERE `id`=1475; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich schau dir nicht in die Augen, ich will heute noch schlafen können.' WHERE `id`=1476; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Boah, bei dir quillt ja noch das Mittagessen raus! Ich hab dich gerochen, bevor ich dich gesehen hab.' WHERE `id`=1477; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Oh du bist so hässlich, selbst der Respawn will dich nicht!' WHERE `id`=1478; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich muss mal einen riesigen machen. ist mein neues Wort für Scheiße.' WHERE `id`=1479; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Oh du nutzlose, moorsaufende Krebsgeschwulst!' WHERE `id`=1480; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wäre ich wie du, würd ich mich selbst löschen!' WHERE `id`=1481; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Oh lehre mich, wie ich das Denken vergesse!' WHERE `id`=1482; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich hab schon Toiletten gesehen, die sauberer kämpfen als du.' WHERE `id`=1483; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Du verdammter Scheißkerl. Wo hast du dein Handwerk gelernt? Du dumme, verdammte Schlampe. Du Idiot. Wer hat dir gesagt, dass du mit Männern arbeiten kannst?' WHERE `id`=1484; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wenn Unfähigkeit ’ne Klasse wäre, hättest du sie gemaxt.' WHERE `id`=1485; +UPDATE `ai_playerbot_texts` SET `text_loc3`=' du dummes Arschfurz-Teppichladen-Arschloch' WHERE `id`=1486; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich lebe lang genug und dann nehm ich dir das Hirn raus!' WHERE `id`=1487; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wenn einen intelligenten Gedanken im Kopf hätte, würde er an Einsamkeit sterben.' WHERE `id`=1488; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hör auf zu weinen, du winselnder Arsch, hör auf mit deinem Unsinn. Du bist nur die Nachgeburt, .' WHERE `id`=1489; +UPDATE `ai_playerbot_texts` SET `text_loc3`='! Wenn Gehirn Vogelkacke wäre, hättest du einen sauberen Käfig' WHERE `id`=1490; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hör mal zu, du Vokuhila... warum zündest du nicht einfach dein Tampon an und sprengst deine Box in die Luft? Denn das ist der einzige Knall, den du kriegen wirst.' WHERE `id`=1491; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verschwinde aus meiner Sicht ! Du verseuchst meine Augen!' WHERE `id`=1492; +UPDATE `ai_playerbot_texts` SET `text_loc3`='SPIELZEIT!!!!' WHERE `id`=1493; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Niemand kommt vorbei!' WHERE `id`=1494; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wir werden angegriffen! Klar zum Gefecht, ihr Deckschrubber! Wehrt die Eindringlinge ab!' WHERE `id`=1495; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Narren... Tötet den im Kleid!' WHERE `id`=1497; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich werde deine Seele Hakkar selbst zum Fraß vorwerfen!' WHERE `id`=1498; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Stolz kündigt das Ende eurer Welt an! Kommt, Sterbliche! Stellt euch dem Zorn der !' WHERE `id`=1499; +UPDATE `ai_playerbot_texts` SET `text_loc3`='All meine Pläne haben zu diesem Moment geführt!' WHERE `id`=1500; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Neuer Tag, neue glorreiche Schlacht!' WHERE `id`=1502; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Also, Geschäft... oder Vergnügen?' WHERE `id`=1503; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ihr seid nicht vorbereitet!' WHERE `id`=1504; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Die letzte Eroberung der hat begonnen! Wieder liegt die Unterwerfung dieser Welt in unseren Händen. Lasst keinen überleben!' WHERE `id`=1505; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Euer Tod wird ein schmerzhafter sein.' WHERE `id`=1506; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Fleht um Gnade! Eure bedeutungslosen Leben sind bald verwirkt.' WHERE `id`=1507; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gebt alle Hoffnung auf! Die ist zurückgekehrt, um zu beenden, was vor so vielen Jahren begann. Diesmal gibt es kein Entkommen!' WHERE `id`=1508; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Alarm! Ihr seid zur Auslöschung markiert!' WHERE `id`=1509; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hahaha! Ihr seid hoffnungslos unterlegen!' WHERE `id`=1511; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich werde euren Größenwahn zerschmettern!' WHERE `id`=1512; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verzeiht mir, denn ihr werdet gleich das Spiel verlieren.' WHERE `id`=1513; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wehren macht es nur schlimmer.' WHERE `id`=1514; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ungeziefer! Blutegel! Saugt mein Blut und erstickt daran!' WHERE `id`=1515; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Mein Blut wird euer Ende sein!' WHERE `id`=1517; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Macht hinne, Wachen! Es ist Zeit zum Töten!' WHERE `id`=1519; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Zögert euer Schicksal nicht hinaus. Kommt jetzt zu mir. Ich mache euer Opfer kurz.' WHERE `id`=1520; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bald seid ihr tot!' WHERE `id`=1521; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Mu-ha-ha!' WHERE `id`=1522; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich bin der Jäger! Ihr seid die Beute...' WHERE `id`=1523; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ihr werdet diesen Ort in Stücken verlassen!' WHERE `id`=1524; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Der Tod naht. Wird euer Gewissen rein sein?' WHERE `id`=1525; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Euer Verhalten wird nicht toleriert.' WHERE `id`=1526; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hmm, unangekündigte Besucher. Vorbereitungen müssen getroffen werden...' WHERE `id`=1528; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Feindliche Einheiten entdeckt. Bedrohungsanalyse-Protokoll aktiv. Primärziel erfasst. Zeit minus dreißig Sekunden bis zur Neubewertung.' WHERE `id`=1529; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Neue Spielzeuge? Für mich? Ich verspreche, ich mach sie diesmal nicht kaputt!' WHERE `id`=1530; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Pssst... gleich ist alles vorbei.' WHERE `id`=1532; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Sag es mir... sag mir alles, Geheimnisse und Privatsachen! Ich reiße dir die Infos aus dem Fleisch!' WHERE `id`=1536; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Macht euch bereit, die Glocken haben geläutet! Bringt die Schwachen, die Jungen und die Alten in Sicherheit! Jeder von euch zahlt den letzten Preis! Fleht um Gnade, die Abrechnung ist gekommen!' WHERE `id`=1537; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wo, bei Bonzos Messingknöpfen, bin ich?' WHERE `id`=1538; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich ertrage es nicht länger! Goblinkönig! Goblinkönig! Wo immer du bist! Nimm diesen weit weg von mir!' WHERE `id`=1539; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ihr habt dreizehn Stunden, um das Labyrinth zu lösen, bevor euer kleiner Bruder einer von uns wird... für immer.' WHERE `id`=1540; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Also, die ist Kinderkram, ja? Dann sehen wir mal, wie ihr dieses kleine Stückchen verdaut...' WHERE `id`=1541; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Weiche! Ich stelle mich jedem Kampf. Du irrst dich, dies ist nicht dein Revier.' WHERE `id`=1542; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Zeig, was du drauf hast!' WHERE `id`=1543; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Zweiklingen-Action für eine glatte und saubere Rasur jedes mal.' WHERE `id`=1545; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Los, komm schon!' WHERE `id`=1546; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Du gehst drauf!' WHERE `id`=1547; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Stich, stich, stich!' WHERE `id`=1548; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Machen wir das schnell, Zeit ist Mana.' WHERE `id`=1549; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich glaube nicht, dass ihr die Schwere eurer Lage begreift.' WHERE `id`=1550; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich bringe meiner Familie und meinem Königreich Ehre!' WHERE `id`=1551; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Licht, gib mir Stärke!' WHERE `id`=1552; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Meine Kirche ist das Schlachtfeld, Zeit zum Gottesdienst...' WHERE `id`=1553; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich verachte euch...' WHERE `id`=1554; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Stellt euch dem Hammer der Gerechtigkeit!' WHERE `id`=1555; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Beweist euren Wert in der Prüfung der Waffen im Lichte!' WHERE `id`=1556; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Alles wird vor der Macht und der Gerechtigkeit meiner Sache fallen. Ihr werdet der Nächste sein!' WHERE `id`=1557; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bereitet euch aufs Sterben vor!' WHERE `id`=1558; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Die Bestie an meiner Seite ist nichts gegen die Bestie in mir...' WHERE `id`=1559; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Erlebt die Feuerkraft dieses voll bewaffneten Jägers!' WHERE `id`=1560; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Heilt mich! Schnell!' WHERE `id`=1561; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Fast tot! Heilt mich!' WHERE `id`=1562; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hilfe! Heilt mich!' WHERE `id`=1563; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Irgendwer! Heilt mich!' WHERE `id`=1564; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Heal! Heal! Heal!' WHERE `id`=1565; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich sterbe! Heal! Aaaaarhg!' WHERE `id`=1566; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Heal mich!' WHERE `id`=1567; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich sterbe. Ich sterbe. Ich sterbe. Heal!' WHERE `id`=1568; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Oh, der Schmerz. Heilt mich schnell!' WHERE `id`=1570; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche Heal' WHERE `id`=1571; +UPDATE `ai_playerbot_texts` SET `text_loc3`='HP low' WHERE `id`=1572; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gebt mal nen Heal. Bitte.' WHERE `id`=1573; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann mir jemand nen Heal geben?' WHERE `id`=1574; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hey! Lieber jetzt heilen als später zu rezzen' WHERE `id`=1575; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Sorry. Brauche noch einen Heal' WHERE `id`=1576; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verdammte Mobs. Heal mich bitte' WHERE `id`=1577; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Noch ein Treffer und ich bin hinüber. Heal bitte' WHERE `id`=1578; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Heiler da?' WHERE `id`=1579; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Warum hauen die mir immer ins Gesicht? Brauche Heal' WHERE `id`=1580; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann mich wer ein bisschen heilen?' WHERE `id`=1581; +UPDATE `ai_playerbot_texts` SET `text_loc3`='OOM' WHERE `id`=1582; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kein Mana mehr' WHERE `id`=1583; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verdammt, dafür hab ich mein ganzes Mana verballert' WHERE `id`=1584; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wartet bis ich trinke oder mein Mana wieder da ist' WHERE `id`=1585; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Mana low' WHERE `id`=1586; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Mana low. Muss trinken' WHERE `id`=1588; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Haben wir nen Getränkeautomaten? Wieder OOM' WHERE `id`=1589; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Mein Mana ist Geschichte' WHERE `id`=1590; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich besorge mir nächstes mal Drinks. OOM' WHERE `id`=1591; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Noch 100 !' WHERE `id`=1595; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Das war es! Keine mehr!' WHERE `id`=1596; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Und ihr habt meinen Bogen... ups, keine !' WHERE `id`=1597; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Brauche !' WHERE `id`=1598; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe Angst' WHERE `id`=1600; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wir sind am Arsch.' WHERE `id`=1601; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Das wars.' WHERE `id`=1602; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hier ist jetzt Schluss.' WHERE `id`=1603; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann mal wer Blizzard anrufen?' WHERE `id`=1604; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Verdammt. Der Tank hat alle Mobs gepullt.' WHERE `id`=1605; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wir sterben. Wir sterben. Wir sterben.' WHERE `id`=1606; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Whoa! So viel Spielzeug zum Austoben.' WHERE `id`=1607; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kill sie alle!' WHERE `id`=1608; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Wenn der Tank stirbt, sind wir Geschichte.' WHERE `id`=1609; +UPDATE `ai_playerbot_texts` SET `text_loc3`='LEEEEERROOOYYYYYYYYYYYY JENNKINNNSSSSSS!!!!!!!' WHERE `id`=1611; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Okay. Was haben wir an AoE?' WHERE `id`=1612; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Jetzt wirds interessant.' WHERE `id`=1613; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Cool. Zieht sie zusammen für nen schönen Flammenstoß.' WHERE `id`=1614; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kill! Kill! Kill!' WHERE `id`=1615; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich glaub, meine Hose ist nass.' WHERE `id`=1616; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hoffentlich sind die Heiler ready. Leeeeroy!' WHERE `id`=1618; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hoffentlich kommen die nicht zu mir.' WHERE `id`=1619; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Oh nein. Ich kann bei dem Gemetzel nicht hinsehen.' WHERE `id`=1620; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hoffentlich gibts Gold.' WHERE `id`=1621; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Loot! Loot!' WHERE `id`=1622; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Mein Schatz.' WHERE `id`=1623; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hoffentlich wartet da ein schickes episches Item auf mich.' WHERE `id`=1624; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich hab tiefe Taschen und Beutel.' WHERE `id`=1625; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Alles meins!' WHERE `id`=1626; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hoffentlich heute kein grauer Müll.' WHERE `id`=1627; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Dieser Loot ist MEINER!' WHERE `id`=1628; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Looten ist eklig, aber ich brauch Gold.' WHERE `id`=1629; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gold!' WHERE `id`=1630; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Okay. Mal sehen, was sie droppen.' WHERE `id`=1631; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Keine Sorge. Ich loote alles.' WHERE `id`=1632; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich bin ein Ninja-Looter.' WHERE `id`=1633; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Kann mir mal wer erklären, wo die das ganze Zeug versteckt haben?' WHERE `id`=1635; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Nein, ich loote keinen grauen Müll.' WHERE `id`=1636; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich bin zuerst. Ich bin zuerst. Ich bin zuerst.' WHERE `id`=1637; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Gebt mir euer Gold!' WHERE `id`=1638; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Meine Taschen sind leer, müssen aber voll werden.' WHERE `id`=1639; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hab dafür ne neue Tasche.' WHERE `id`=1640; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hoffentlich zieh ich beim Looten keine Aggro.' WHERE `id`=1641; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kann nicht looten wenn jemand zusieht.' WHERE `id`=1642; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ha! Ihr kriegt davon keinen Krümel!' WHERE `id`=1643; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich mag neues Gear.' WHERE `id`=1645; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich höre auf, wenn schon wieder nichts Wertvolles droppt.' WHERE `id`=1646; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hoffentlich ist es ein hübscher Ring.' WHERE `id`=1647; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich reiß dir den Loot aus den Händen.' WHERE `id`=1648; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Alle weg da. Ich loote jetzt.' WHERE `id`=1649; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Geiler Loot.' WHERE `id`=1650; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Oh ihr RNG-Götter! Gewährt mir heute einen Epic beim würfeln.' WHERE `id`=1651; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bitte gib mir neues Spielzeug.' WHERE `id`=1652; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Hoffe, die haben was Leckeres dabei.' WHERE `id`=1653; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Das Gold gehört mir. Ich lasse euch den Rest, versprochen.' WHERE `id`=1654; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin gleich da, wartet auf mich!' WHERE `id`=1657; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin nicht mehr weit, bitte wartet!' WHERE `id`=1658; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bin auf dem Weg zu euch.' WHERE `id`=1659; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich reise zu euch.' WHERE `id`=1661; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich versuche zu euch zu kommen.' WHERE `id`=1662; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Rüste %item aus' WHERE `id`=1663; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe die Zauber gelernt: %spells' WHERE `id`=1665; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%item hat Abklingzeit.' WHERE `id`=1666; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe %item nicht im Inventar.' WHERE `id`=1667; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Das Item mit der ID %item existiert nicht.' WHERE `id`=1668; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Folge.' WHERE `id`=1671; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bleibe hier.' WHERE `id`=1672; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Fliehe.' WHERE `id`=1673; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich fliehe nicht mit dir, du bist zu weit weg.' WHERE `id`=1674; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Grinde.' WHERE `id`=1675; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Greife an.' WHERE `id`=1676; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Das ist zu weit weg.' WHERE `id`=1677; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Da kann ich nicht hin.' WHERE `id`=1679; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich bin nicht in deiner Gilde!' WHERE `id`=1680; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Keine Gildenbank in der Nähe gefunden.' WHERE `id`=1681; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kann nicht ' WHERE `id`=1682; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe keine Rechte, Items in den ersten Gildenbank-Reiter zu legen.' WHERE `id`=1683; +UPDATE `ai_playerbot_texts` SET `text_loc3`=' in die Gildenbank gelegt' WHERE `id`=1684; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Freies Bewegen.' WHERE `id`=1685; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Bewache.' WHERE `id`=1686; +UPDATE `ai_playerbot_texts` SET `text_loc3`='(das letzte)' WHERE `id`=1690; +UPDATE `ai_playerbot_texts` SET `text_loc3`='auf Handelsgegenstand' WHERE `id`=1692; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Loote %item' WHERE `id`=1696; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich habe nicht genug Gruppenmitglieder in der Nähe, um beschwören zu können.' WHERE `id`=1698; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ich kenne den Zauber %spell nicht.' WHERE `id`=1701; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Stelle %spell her' WHERE `id`=1703; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Konnte %spell nicht wirken.' WHERE `id`=1705; +UPDATE `ai_playerbot_texts` SET `text_loc3`=' |cffffff00(x%amount übrig)|r' WHERE `id`=1706; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Dummy' WHERE `id`=1707; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Beim Licht… Ich hab meine Königssymbole vergessen. Na gut, dann nehmen wir eben %base_spell!' WHERE `id`=1708; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Die Natur ist großzügig, meine Taschen nicht... keine Kräuter für %group_spell mehr. Nehmt fürs Erste %base_spell!' WHERE `id`=1709; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Arkanes Pulver alle... %group_spell muss warten. Wirke %base_spell!' WHERE `id`=1710; +UPDATE `ai_playerbot_texts` SET `text_loc3`='Ups, mir fehlen die Komponenten für %group_spell. Wir nehmen %base_spell!' WHERE `id`=1711; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%player bewegt sich, um den roten Strahl zu blocken!' WHERE `id`=1712; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%player bewegt sich, um den blauen Strahl zu blocken!' WHERE `id`=1713; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%player bewegt sich, um den grünen Strahl zu blocken!' WHERE `id`=1714; +UPDATE `ai_playerbot_texts` SET `text_loc3`='%player verlässt den blauen Strahl--nächster Blocker los!' WHERE `id`=1715; + +UPDATE `ai_playerbot_texts` SET `text_loc3`='%player verlässt den grünen Strahl--nächster Blocker los!' WHERE `id`=1716; From 7d5c9e3ee0ccde5ea25f45edd28fe6bc201d5bb1 Mon Sep 17 00:00:00 2001 From: St0ny <42348051+Raz0r1337@users.noreply.github.com> Date: Tue, 4 Nov 2025 14:48:24 +0100 Subject: [PATCH 13/47] Update 2025_10_27_00_ai_playerbot_german_texts.sql line error fixed --- .../updates/2025_10_27_00_ai_playerbot_german_texts.sql | 1 - 1 file changed, 1 deletion(-) diff --git a/data/sql/playerbots/updates/2025_10_27_00_ai_playerbot_german_texts.sql b/data/sql/playerbots/updates/2025_10_27_00_ai_playerbot_german_texts.sql index b3c0b9e7c5..d92169278c 100644 --- a/data/sql/playerbots/updates/2025_10_27_00_ai_playerbot_german_texts.sql +++ b/data/sql/playerbots/updates/2025_10_27_00_ai_playerbot_german_texts.sql @@ -1530,5 +1530,4 @@ UPDATE `ai_playerbot_texts` SET `text_loc3`='%player bewegt sich, um den roten S UPDATE `ai_playerbot_texts` SET `text_loc3`='%player bewegt sich, um den blauen Strahl zu blocken!' WHERE `id`=1713; UPDATE `ai_playerbot_texts` SET `text_loc3`='%player bewegt sich, um den grünen Strahl zu blocken!' WHERE `id`=1714; UPDATE `ai_playerbot_texts` SET `text_loc3`='%player verlässt den blauen Strahl--nächster Blocker los!' WHERE `id`=1715; - UPDATE `ai_playerbot_texts` SET `text_loc3`='%player verlässt den grünen Strahl--nächster Blocker los!' WHERE `id`=1716; From 983a55da86fee2c2aab9fef3d28e1e7bbbb8d25c Mon Sep 17 00:00:00 2001 From: Crow Date: Tue, 4 Nov 2025 16:01:30 -0600 Subject: [PATCH 14/47] Implement Gruul's Lair strategy (#1647) * Implement Gruul's Lair strategy * minor non-gameplay tweaks to code * Use multiplier for tank assist * HKM warlock & Gruul tank tweaks * Fixed declarations * rewrote HKM + minor Gruul tweaks * Update PlayerbotAI.cpp * modernize code and address comments * clean ups to tank logic * Oops. * Remove post-move delay For actions like positioning bosses, the standard post-movement delay should be overridden IMO since a player would be sequencing their actions in this type of situation * Update RaidGruulsLairActions.cpp * Replace break statements with return true * enum class to enum * moved all isuseful checks to triggers * Split multipliers and improved banish logic * Update for comments * changes to int * use helpers for marking icons * address compile errors * correct MoveTo and use RTI helper * address comments and rename actions/triggers * adjust alignment of location constants * fix some crappy returns * allow return true when changing targets * change indents and move enums inside namespace * style changes, trim trailing whitespaces, address comments --- src/PlayerbotAI.cpp | 3 + src/strategy/AiObjectContext.cpp | 4 + src/strategy/raids/RaidStrategyContext.h | 3 + .../gruulslair/RaidGruulsLairActionContext.h | 49 ++ .../gruulslair/RaidGruulsLairActions.cpp | 696 ++++++++++++++++++ .../raids/gruulslair/RaidGruulsLairActions.h | 112 +++ .../gruulslair/RaidGruulsLairHelpers.cpp | 241 ++++++ .../raids/gruulslair/RaidGruulsLairHelpers.h | 62 ++ .../gruulslair/RaidGruulsLairMultipliers.cpp | 110 +++ .../gruulslair/RaidGruulsLairMultipliers.h | 48 ++ .../gruulslair/RaidGruulsLairStrategy.cpp | 56 ++ .../raids/gruulslair/RaidGruulsLairStrategy.h | 18 + .../gruulslair/RaidGruulsLairTriggerContext.h | 49 ++ .../gruulslair/RaidGruulsLairTriggers.cpp | 160 ++++ .../raids/gruulslair/RaidGruulsLairTriggers.h | 97 +++ 15 files changed, 1708 insertions(+) create mode 100644 src/strategy/raids/gruulslair/RaidGruulsLairActionContext.h create mode 100644 src/strategy/raids/gruulslair/RaidGruulsLairActions.cpp create mode 100644 src/strategy/raids/gruulslair/RaidGruulsLairActions.h create mode 100644 src/strategy/raids/gruulslair/RaidGruulsLairHelpers.cpp create mode 100644 src/strategy/raids/gruulslair/RaidGruulsLairHelpers.h create mode 100644 src/strategy/raids/gruulslair/RaidGruulsLairMultipliers.cpp create mode 100644 src/strategy/raids/gruulslair/RaidGruulsLairMultipliers.h create mode 100644 src/strategy/raids/gruulslair/RaidGruulsLairStrategy.cpp create mode 100644 src/strategy/raids/gruulslair/RaidGruulsLairStrategy.h create mode 100644 src/strategy/raids/gruulslair/RaidGruulsLairTriggerContext.h create mode 100644 src/strategy/raids/gruulslair/RaidGruulsLairTriggers.cpp create mode 100644 src/strategy/raids/gruulslair/RaidGruulsLairTriggers.h diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 9a670961e1..bf6b0a343a 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -1534,6 +1534,9 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster) case 533: strategyName = "naxx"; // Naxxramas break; + case 565: + strategyName = "gruulslair"; // Gruul's Lair + break; case 574: strategyName = "wotlk-uk"; // Utgarde Keep break; diff --git a/src/strategy/AiObjectContext.cpp b/src/strategy/AiObjectContext.cpp index f0e93fd393..4c49fe4eac 100644 --- a/src/strategy/AiObjectContext.cpp +++ b/src/strategy/AiObjectContext.cpp @@ -39,6 +39,8 @@ #include "raids/blackwinglair/RaidBwlTriggerContext.h" #include "raids/karazhan/RaidKarazhanActionContext.h" #include "raids/karazhan/RaidKarazhanTriggerContext.h" +#include "raids/gruulslair/RaidGruulsLairActionContext.h" +#include "raids/gruulslair/RaidGruulsLairTriggerContext.h" #include "raids/naxxramas/RaidNaxxActionContext.h" #include "raids/naxxramas/RaidNaxxTriggerContext.h" #include "raids/eyeofeternity/RaidEoEActionContext.h" @@ -111,6 +113,7 @@ void AiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList creators["mc"] = &RaidStrategyContext::mc; creators["bwl"] = &RaidStrategyContext::bwl; creators["karazhan"] = &RaidStrategyContext::karazhan; + creators["gruulslair"] = &RaidStrategyContext::gruulslair; creators["naxx"] = &RaidStrategyContext::naxx; creators["wotlk-os"] = &RaidStrategyContext::wotlk_os; creators["wotlk-eoe"] = &RaidStrategyContext::wotlk_eoe; @@ -37,6 +39,7 @@ class RaidStrategyContext : public NamedObjectContext static Strategy* mc(PlayerbotAI* botAI) { return new RaidMcStrategy(botAI); } static Strategy* bwl(PlayerbotAI* botAI) { return new RaidBwlStrategy(botAI); } static Strategy* karazhan(PlayerbotAI* botAI) { return new RaidKarazhanStrategy(botAI); } + static Strategy* gruulslair(PlayerbotAI* botAI) { return new RaidGruulsLairStrategy(botAI); } static Strategy* naxx(PlayerbotAI* botAI) { return new RaidNaxxStrategy(botAI); } static Strategy* wotlk_os(PlayerbotAI* botAI) { return new RaidOsStrategy(botAI); } static Strategy* wotlk_eoe(PlayerbotAI* botAI) { return new RaidEoEStrategy(botAI); } diff --git a/src/strategy/raids/gruulslair/RaidGruulsLairActionContext.h b/src/strategy/raids/gruulslair/RaidGruulsLairActionContext.h new file mode 100644 index 0000000000..809fadf034 --- /dev/null +++ b/src/strategy/raids/gruulslair/RaidGruulsLairActionContext.h @@ -0,0 +1,49 @@ +#ifndef _PLAYERBOT_RAIDGRUULSLAIRACTIONCONTEXT_H +#define _PLAYERBOT_RAIDGRUULSLAIRACTIONCONTEXT_H + +#include "RaidGruulsLairActions.h" +#include "NamedObjectContext.h" + +class RaidGruulsLairActionContext : public NamedObjectContext +{ +public: + RaidGruulsLairActionContext() + { + // High King Maulgar + creators["high king maulgar main tank attack maulgar"] = &RaidGruulsLairActionContext::high_king_maulgar_main_tank_attack_maulgar; + creators["high king maulgar first assist tank attack olm"] = &RaidGruulsLairActionContext::high_king_maulgar_first_assist_tank_attack_olm; + creators["high king maulgar second assist tank attack blindeye"] = &RaidGruulsLairActionContext::high_king_maulgar_second_assist_tank_attack_blindeye; + creators["high king maulgar mage tank attack krosh"] = &RaidGruulsLairActionContext::high_king_maulgar_mage_tank_attack_krosh; + creators["high king maulgar moonkin tank attack kiggler"] = &RaidGruulsLairActionContext::high_king_maulgar_moonkin_tank_attack_kiggler; + creators["high king maulgar assign dps priority"] = &RaidGruulsLairActionContext::high_king_maulgar_assign_dps_priority; + creators["high king maulgar healer find safe position"] = &RaidGruulsLairActionContext::high_king_maulgar_healer_find_safe_position; + creators["high king maulgar run away from whirlwind"] = &RaidGruulsLairActionContext::high_king_maulgar_run_away_from_whirlwind; + creators["high king maulgar banish felstalker"] = &RaidGruulsLairActionContext::high_king_maulgar_banish_felstalker; + creators["high king maulgar misdirect olm and blindeye"] = &RaidGruulsLairActionContext::high_king_maulgar_misdirect_olm_and_blindeye; + + // Gruul the Dragonkiller + creators["gruul the dragonkiller main tank position boss"] = &RaidGruulsLairActionContext::gruul_the_dragonkiller_main_tank_position_boss; + creators["gruul the dragonkiller spread ranged"] = &RaidGruulsLairActionContext::gruul_the_dragonkiller_spread_ranged; + creators["gruul the dragonkiller shatter spread"] = &RaidGruulsLairActionContext::gruul_the_dragonkiller_shatter_spread; + } + +private: + // High King Maulgar + static Action* high_king_maulgar_main_tank_attack_maulgar(PlayerbotAI* botAI) { return new HighKingMaulgarMainTankAttackMaulgarAction(botAI); } + static Action* high_king_maulgar_first_assist_tank_attack_olm(PlayerbotAI* botAI) { return new HighKingMaulgarFirstAssistTankAttackOlmAction(botAI); } + static Action* high_king_maulgar_second_assist_tank_attack_blindeye(PlayerbotAI* botAI) { return new HighKingMaulgarSecondAssistTankAttackBlindeyeAction(botAI); } + static Action* high_king_maulgar_mage_tank_attack_krosh(PlayerbotAI* botAI) { return new HighKingMaulgarMageTankAttackKroshAction(botAI); } + static Action* high_king_maulgar_moonkin_tank_attack_kiggler(PlayerbotAI* botAI) { return new HighKingMaulgarMoonkinTankAttackKigglerAction(botAI); } + static Action* high_king_maulgar_assign_dps_priority(PlayerbotAI* botAI) { return new HighKingMaulgarAssignDPSPriorityAction(botAI); } + static Action* high_king_maulgar_healer_find_safe_position(PlayerbotAI* botAI) { return new HighKingMaulgarHealerFindSafePositionAction(botAI); } + static Action* high_king_maulgar_run_away_from_whirlwind(PlayerbotAI* botAI) { return new HighKingMaulgarRunAwayFromWhirlwindAction(botAI); } + static Action* high_king_maulgar_banish_felstalker(PlayerbotAI* botAI) { return new HighKingMaulgarBanishFelstalkerAction(botAI); } + static Action* high_king_maulgar_misdirect_olm_and_blindeye(PlayerbotAI* botAI) { return new HighKingMaulgarMisdirectOlmAndBlindeyeAction(botAI); } + + // Gruul the Dragonkiller + static Action* gruul_the_dragonkiller_main_tank_position_boss(PlayerbotAI* botAI) { return new GruulTheDragonkillerMainTankPositionBossAction(botAI); } + static Action* gruul_the_dragonkiller_spread_ranged(PlayerbotAI* botAI) { return new GruulTheDragonkillerSpreadRangedAction(botAI); } + static Action* gruul_the_dragonkiller_shatter_spread(PlayerbotAI* botAI) { return new GruulTheDragonkillerShatterSpreadAction(botAI); } +}; + +#endif diff --git a/src/strategy/raids/gruulslair/RaidGruulsLairActions.cpp b/src/strategy/raids/gruulslair/RaidGruulsLairActions.cpp new file mode 100644 index 0000000000..1a98135cac --- /dev/null +++ b/src/strategy/raids/gruulslair/RaidGruulsLairActions.cpp @@ -0,0 +1,696 @@ +#include "RaidGruulsLairActions.h" +#include "RaidGruulsLairHelpers.h" +#include "CreatureAI.h" +#include "Playerbots.h" +#include "Unit.h" + +using namespace GruulsLairHelpers; + +// High King Maulgar Actions + +// Main tank on Maulgar +bool HighKingMaulgarMainTankAttackMaulgarAction::Execute(Event event) +{ + Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar"); + + MarkTargetWithSquare(bot, maulgar); + SetRtiTarget(botAI, "square", maulgar); + + if (bot->GetVictim() != maulgar) + return Attack(maulgar); + + if (maulgar->GetVictim() == bot) + { + const Location& tankPosition = GruulsLairLocations::MaulgarTankPosition; + const float maxDistance = 3.0f; + + float distanceToTankPosition = bot->GetExactDist2d(tankPosition.x, tankPosition.y); + + if (distanceToTankPosition > maxDistance) + { + float dX = tankPosition.x - bot->GetPositionX(); + float dY = tankPosition.y - bot->GetPositionY(); + float dist = sqrt(dX * dX + dY * dY); + float moveX = bot->GetPositionX() + (dX / dist) * maxDistance; + float moveY = bot->GetPositionY() + (dY / dist) * maxDistance; + return MoveTo(bot->GetMapId(), moveX, moveY, tankPosition.z, false, false, false, false, + MovementPriority::MOVEMENT_COMBAT, true, false); + } + + float orientation = atan2(maulgar->GetPositionY() - bot->GetPositionY(), + maulgar->GetPositionX() - bot->GetPositionX()); + bot->SetFacingTo(orientation); + } + else if (!bot->IsWithinMeleeRange(maulgar)) + { + return MoveTo(maulgar->GetMapId(), maulgar->GetPositionX(), maulgar->GetPositionY(), + maulgar->GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_COMBAT, true, false); + } + + return false; +} + +// First offtank on Olm +bool HighKingMaulgarFirstAssistTankAttackOlmAction::Execute(Event event) +{ + Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner"); + + MarkTargetWithCircle(bot, olm); + SetRtiTarget(botAI, "circle", olm); + + if (bot->GetVictim() != olm) + return Attack(olm); + + if (olm->GetVictim() == bot) + { + const Location& tankPosition = GruulsLairLocations::OlmTankPosition; + const float maxDistance = 3.0f; + const float olmTankLeeway = 30.0f; + + float distanceOlmToTankPosition = olm->GetExactDist2d(tankPosition.x, tankPosition.y); + + if (distanceOlmToTankPosition > olmTankLeeway) + { + float dX = tankPosition.x - bot->GetPositionX(); + float dY = tankPosition.y - bot->GetPositionY(); + float dist = sqrt(dX * dX + dY * dY); + float moveX = bot->GetPositionX() + (dX / dist) * maxDistance; + float moveY = bot->GetPositionY() + (dY / dist) * maxDistance; + return MoveTo(bot->GetMapId(), moveX, moveY, tankPosition.z, false, false, false, false, + MovementPriority::MOVEMENT_COMBAT, true, false); + } + } + else if (!bot->IsWithinMeleeRange(olm)) + { + return MoveTo(olm->GetMapId(), olm->GetPositionX(), olm->GetPositionY(), + olm->GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_COMBAT, true, false); + } + + return false; +} + +// Second offtank on Blindeye +bool HighKingMaulgarSecondAssistTankAttackBlindeyeAction::Execute(Event event) +{ + Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer"); + + MarkTargetWithStar(bot, blindeye); + SetRtiTarget(botAI, "star", blindeye); + + if (bot->GetVictim() != blindeye) + return Attack(blindeye); + + if (blindeye->GetVictim() == bot) + { + const Location& tankPosition = GruulsLairLocations::BlindeyeTankPosition; + const float maxDistance = 3.0f; + + float distanceToTankPosition = bot->GetExactDist2d(tankPosition.x, tankPosition.y); + + if (distanceToTankPosition > maxDistance) + { + float dX = tankPosition.x - bot->GetPositionX(); + float dY = tankPosition.y - bot->GetPositionY(); + float dist = sqrt(dX * dX + dY * dY); + float moveX = bot->GetPositionX() + (dX / dist) * maxDistance; + float moveY = bot->GetPositionY() + (dY / dist) * maxDistance; + return MoveTo(bot->GetMapId(), moveX, moveY, tankPosition.z, false, false, false, false, + MovementPriority::MOVEMENT_COMBAT, true, false); + } + + float orientation = atan2(blindeye->GetPositionY() - bot->GetPositionY(), + blindeye->GetPositionX() - bot->GetPositionX()); + bot->SetFacingTo(orientation); + } + else if (!bot->IsWithinMeleeRange(blindeye)) + { + return MoveTo(blindeye->GetMapId(), blindeye->GetPositionX(), blindeye->GetPositionY(), + blindeye->GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_COMBAT, true, false); + } + + return false; +} + +// Mage with highest max HP on Krosh +bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event event) +{ + Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand"); + + MarkTargetWithTriangle(bot, krosh); + SetRtiTarget(botAI, "triangle", krosh); + + if (krosh->HasAura(SPELL_SPELL_SHIELD) && botAI->CanCastSpell("spellsteal", krosh)) + return botAI->CastSpell("spellsteal", krosh); + + if (!bot->HasAura(SPELL_SPELL_SHIELD) && botAI->CanCastSpell("fire ward", bot)) + return botAI->CastSpell("fire ward", bot); + + if (bot->GetTarget() != krosh->GetGUID()) + { + bot->SetSelection(krosh->GetGUID()); + return true; + } + + if (krosh->GetVictim() == bot) + { + const Location& tankPosition = GruulsLairLocations::KroshTankPosition; + float distanceToKrosh = krosh->GetExactDist2d(tankPosition.x, tankPosition.y); + const float minDistance = 16.0f; + const float maxDistance = 29.0f; + const float tankPositionLeeway = 1.0f; + + if (distanceToKrosh > minDistance && distanceToKrosh < maxDistance) + { + if (!bot->IsWithinDist2d(tankPosition.x, tankPosition.y, tankPositionLeeway)) + { + return MoveTo(bot->GetMapId(), tankPosition.x, tankPosition.y, tankPosition.z, false, + false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); + } + + float orientation = atan2(krosh->GetPositionY() - bot->GetPositionY(), + krosh->GetPositionX() - bot->GetPositionX()); + bot->SetFacingTo(orientation); + } + else + { + Position safePos; + if (TryGetNewSafePosition(botAI, bot, safePos)) + { + return MoveTo(krosh->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ, + false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); + } + } + } + + return false; +} + +// Moonkin with highest max HP on Kiggler +bool HighKingMaulgarMoonkinTankAttackKigglerAction::Execute(Event event) +{ + Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed"); + + MarkTargetWithDiamond(bot, kiggler); + SetRtiTarget(botAI, "diamond", kiggler); + + if (bot->GetTarget() != kiggler->GetGUID()) + { + bot->SetSelection(kiggler->GetGUID()); + return true; + } + + Position safePos; + if (TryGetNewSafePosition(botAI, bot, safePos)) + { + return MoveTo(kiggler->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ, + false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); + } + + return false; +} + +bool HighKingMaulgarAssignDPSPriorityAction::Execute(Event event) +{ + // Target priority 1: Blindeye + Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer"); + if (blindeye && blindeye->IsAlive()) + { + Position safePos; + if (TryGetNewSafePosition(botAI, bot, safePos)) + { + bot->AttackStop(); + bot->InterruptNonMeleeSpells(false); + return MoveTo(blindeye->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ, + false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); + } + + SetRtiTarget(botAI, "star", blindeye); + + if (bot->GetTarget() != blindeye->GetGUID()) + { + bot->SetSelection(blindeye->GetGUID()); + return Attack(blindeye); + } + + return false; + } + + // Target priority 2: Olm + Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner"); + if (olm && olm->IsAlive()) + { + Position safePos; + if (TryGetNewSafePosition(botAI, bot, safePos)) + { + bot->AttackStop(); + bot->InterruptNonMeleeSpells(false); + return MoveTo(olm->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ, + false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); + } + + SetRtiTarget(botAI, "circle", olm); + + if (bot->GetTarget() != olm->GetGUID()) + { + bot->SetSelection(olm->GetGUID()); + return Attack(olm); + } + + return false; + } + + // Target priority 3a: Krosh (ranged only) + Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand"); + if (krosh && krosh->IsAlive() && botAI->IsRanged(bot)) + { + Position safePos; + if (TryGetNewSafePosition(botAI, bot, safePos)) + { + bot->AttackStop(); + bot->InterruptNonMeleeSpells(false); + return MoveTo(krosh->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ, + false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); + } + + SetRtiTarget(botAI, "triangle", krosh); + + if (bot->GetTarget() != krosh->GetGUID()) + { + bot->SetSelection(krosh->GetGUID()); + return Attack(krosh); + } + + return false; + } + + // Target priority 3b: Kiggler + Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed"); + if (kiggler && kiggler->IsAlive()) + { + Position safePos; + if (TryGetNewSafePosition(botAI, bot, safePos)) + { + bot->AttackStop(); + bot->InterruptNonMeleeSpells(false); + return MoveTo(kiggler->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ, + false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); + } + + SetRtiTarget(botAI, "diamond", kiggler); + + if (bot->GetTarget() != kiggler->GetGUID()) + { + bot->SetSelection(kiggler->GetGUID()); + return Attack(kiggler); + } + + return false; + } + + // Target priority 4: Maulgar + Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar"); + if (maulgar && maulgar->IsAlive()) + { + Position safePos; + if (TryGetNewSafePosition(botAI, bot, safePos)) + { + bot->AttackStop(); + bot->InterruptNonMeleeSpells(false); + return MoveTo(maulgar->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ, + false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); + } + + SetRtiTarget(botAI, "square", maulgar); + + if (bot->GetTarget() != maulgar->GetGUID()) + { + bot->SetSelection(maulgar->GetGUID()); + return Attack(maulgar); + } + } + + return false; +} + +// Avoid Whirlwind and Blast Wave and generally try to stay near the center of the room +bool HighKingMaulgarHealerFindSafePositionAction::Execute(Event event) +{ + const Location& fightCenter = GruulsLairLocations::MaulgarRoomCenter; + const float maxDistanceFromFight = 50.0f; + float distToFight = bot->GetExactDist2d(fightCenter.x, fightCenter.y); + + if (distToFight > maxDistanceFromFight) + { + float angle = atan2(bot->GetPositionY() - fightCenter.y, bot->GetPositionX() - fightCenter.x); + float destX = fightCenter.x + 40.0f * cos(angle); + float destY = fightCenter.y + 40.0f * sin(angle); + float destZ = fightCenter.z; + + if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(), + bot->GetPositionZ(), destX, destY, destZ)) + return false; + + return MoveTo(bot->GetMapId(), destX, destY, destZ, false, false, false, false, + MovementPriority::MOVEMENT_COMBAT, true, false); + } + + Position safePos; + if (TryGetNewSafePosition(botAI, bot, safePos)) + { + bot->AttackStop(); + bot->InterruptNonMeleeSpells(false); + return MoveTo(bot->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ, + false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); + } + + return false; +} + +// Run away from Maulgar during Whirlwind (logic for after all other ogres are dead) +bool HighKingMaulgarRunAwayFromWhirlwindAction::Execute(Event event) +{ + Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar"); + + const float safeDistance = 10.0f; + float distance = bot->GetExactDist2d(maulgar); + + if (distance < safeDistance) + { + float angle = atan2(bot->GetPositionY() - maulgar->GetPositionY(), + bot->GetPositionX() - maulgar->GetPositionX()); + float destX = maulgar->GetPositionX() + safeDistance * cos(angle); + float destY = maulgar->GetPositionY() + safeDistance * sin(angle); + float destZ = bot->GetPositionZ(); + + if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(), + bot->GetPositionZ(), destX, destY, destZ)) + return false; + + float destDist = maulgar->GetExactDist2d(destX, destY); + + if (destDist >= safeDistance - 0.1f) + { + bot->AttackStop(); + bot->InterruptNonMeleeSpells(true); + return MoveTo(maulgar->GetMapId(), destX, destY, destZ, false, false, false, false, + MovementPriority::MOVEMENT_COMBAT, true, false); + } + } + + return false; +} + +bool HighKingMaulgarBanishFelstalkerAction::Execute(Event event) +{ + Group* group = bot->GetGroup(); + if (!group) + return false; + + const GuidVector& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + std::vector felStalkers; + for (auto const& npc : npcs) + { + Unit* unit = botAI->GetUnit(npc); + if (unit && unit->GetEntry() == NPC_WILD_FEL_STALKER && unit->IsAlive()) + felStalkers.push_back(unit); + } + + std::vector warlocks; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && member->IsAlive() && member->getClass() == CLASS_WARLOCK && GET_PLAYERBOT_AI(member)) + warlocks.push_back(member); + } + + int warlockIndex = -1; + for (size_t i = 0; i < warlocks.size(); ++i) + { + if (warlocks[i] == bot) + { + warlockIndex = static_cast(i); + break; + } + } + + if (warlockIndex >= 0 && warlockIndex < felStalkers.size()) + { + Unit* assignedFelStalker = felStalkers[warlockIndex]; + if (!assignedFelStalker->HasAura(SPELL_BANISH) && botAI->CanCastSpell(SPELL_BANISH, assignedFelStalker, true)) + return botAI->CastSpell("banish", assignedFelStalker); + } + + return false; +} + +// Hunter 1: Misdirect Olm to first offtank and have pet attack Blindeye +// Hunter 2: Misdirect Blindeye to second offtank +bool HighKingMaulgarMisdirectOlmAndBlindeyeAction::Execute(Event event) +{ + Group* group = bot->GetGroup(); + if (!group) + return false; + + std::vector hunters; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && member->IsAlive() && member->getClass() == CLASS_HUNTER && GET_PLAYERBOT_AI(member)) + hunters.push_back(member); + } + + int hunterIndex = -1; + for (size_t i = 0; i < hunters.size(); ++i) + { + if (hunters[i] == bot) + { + hunterIndex = static_cast(i); + break; + } + } + + Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner"); + Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer"); + Player* olmTank = nullptr; + Player* blindeyeTank = nullptr; + + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive()) + continue; + else if (botAI->IsAssistTankOfIndex(member, 0)) olmTank = member; + else if (botAI->IsAssistTankOfIndex(member, 1)) blindeyeTank = member; + } + + switch (hunterIndex) + { + case 0: + botAI->CastSpell("misdirection", olmTank); + if (bot->HasAura(SPELL_MISDIRECTION)) + { + Pet* pet = bot->GetPet(); + if (pet && pet->IsAlive() && pet->GetVictim() != blindeye) + { + pet->ClearUnitState(UNIT_STATE_FOLLOW); + pet->AttackStop(); + pet->SetTarget(blindeye->GetGUID()); + if (pet->GetCharmInfo()) + { + pet->GetCharmInfo()->SetIsCommandAttack(true); + pet->GetCharmInfo()->SetIsAtStay(false); + pet->GetCharmInfo()->SetIsFollowing(false); + pet->GetCharmInfo()->SetIsCommandFollow(false); + pet->GetCharmInfo()->SetIsReturning(false); + } + pet->ToCreature()->AI()->AttackStart(blindeye); + } + return botAI->CastSpell("steady shot", olm); + } + break; + + case 1: + botAI->CastSpell("misdirection", blindeyeTank); + if (bot->HasAura(SPELL_MISDIRECTION)) + return botAI->CastSpell("steady shot", blindeye); + break; + + default: + break; + } + + return false; +} + +// Gruul the Dragonkiller Actions + +// Position in center of the room +bool GruulTheDragonkillerMainTankPositionBossAction::Execute(Event event) +{ + Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller"); + + if (bot->GetVictim() != gruul) + return Attack(gruul); + + if (gruul->GetVictim() == bot) + { + const Location& tankPosition = GruulsLairLocations::GruulTankPosition; + const float maxDistance = 3.0f; + + float dX = tankPosition.x - bot->GetPositionX(); + float dY = tankPosition.y - bot->GetPositionY(); + float distanceToTankPosition = bot->GetExactDist2d(tankPosition.x, tankPosition.y); + + if (distanceToTankPosition > maxDistance) + { + float step = std::min(maxDistance, distanceToTankPosition); + float moveX = bot->GetPositionX() + (dX / distanceToTankPosition) * maxDistance; + float moveY = bot->GetPositionY() + (dY / distanceToTankPosition) * maxDistance; + const float moveZ = tankPosition.z; + return MoveTo(bot->GetMapId(), moveX, moveY, moveZ, false, false, false, false, + MovementPriority::MOVEMENT_COMBAT, true, false); + } + + float orientation = atan2(gruul->GetPositionY() - bot->GetPositionY(), + gruul->GetPositionX() - bot->GetPositionX()); + bot->SetFacingTo(orientation); + } + else if (!bot->IsWithinMeleeRange(gruul)) + { + return MoveTo(gruul->GetMapId(), gruul->GetPositionX(), gruul->GetPositionY(), gruul->GetPositionZ(), + false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); + } + + return false; +} + +// Ranged will take initial positions around the middle of the room, 25-40 yards from center +// Ranged should spread out 10 yards from each other +bool GruulTheDragonkillerSpreadRangedAction::Execute(Event event) +{ + Group* group = bot->GetGroup(); + if (!group) + return false; + + static std::unordered_map initialPositions; + static std::unordered_map hasReachedInitialPosition; + + Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller"); + if (gruul && gruul->IsAlive() && gruul->GetHealth() == gruul->GetMaxHealth()) + { + initialPositions.clear(); + hasReachedInitialPosition.clear(); + } + + const Location& tankPosition = GruulsLairLocations::GruulTankPosition; + const float centerX = tankPosition.x; + const float centerY = tankPosition.y; + float centerZ = bot->GetPositionZ(); + const float minRadius = 25.0f; + const float maxRadius = 40.0f; + + std::vector members; + Player* closestMember = nullptr; + float closestDist = std::numeric_limits::max(); + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive()) + continue; + + members.push_back(member); + if (member != bot) + { + float dist = bot->GetExactDist2d(member); + if (dist < closestDist) + { + closestDist = dist; + closestMember = member; + } + } + } + + if (!initialPositions.count(bot->GetGUID())) + { + auto it = std::find(members.begin(), members.end(), bot); + uint8 botIndex = (it != members.end()) ? std::distance(members.begin(), it) : 0; + uint8 count = members.size(); + + float angle = 2 * M_PI * botIndex / count; + float radius = minRadius + static_cast(rand()) / + static_cast(RAND_MAX) * (maxRadius - minRadius); + float targetX = centerX + radius * cos(angle); + float targetY = centerY + radius * sin(angle); + + initialPositions[bot->GetGUID()] = Position(targetX, targetY, centerZ); + hasReachedInitialPosition[bot->GetGUID()] = false; + } + + Position targetPosition = initialPositions[bot->GetGUID()]; + if (!hasReachedInitialPosition[bot->GetGUID()]) + { + if (!bot->IsWithinDist2d(targetPosition.GetPositionX(), targetPosition.GetPositionY(), 2.0f)) + { + float destX = targetPosition.GetPositionX(); + float destY = targetPosition.GetPositionY(); + float destZ = targetPosition.GetPositionZ(); + + if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), + bot->GetPositionY(), bot->GetPositionZ(), destX, destY, destZ)) + return false; + + return MoveTo(bot->GetMapId(), destX, destY, destZ, false, false, false, false, + MovementPriority::MOVEMENT_COMBAT, true, false); + } + + hasReachedInitialPosition[bot->GetGUID()] = true; + } + + const float minSpreadDistance = 10.0f; + const float movementThreshold = 2.0f; + + if (closestMember && closestDist < minSpreadDistance - movementThreshold) + { + return FleePosition(Position(closestMember->GetPositionX(), closestMember->GetPositionY(), + closestMember->GetPositionZ()), minSpreadDistance, 0); + } + + return false; +} + +// Try to get away from other group members when Ground Slam is cast +bool GruulTheDragonkillerShatterSpreadAction::Execute(Event event) +{ + Group* group = bot->GetGroup(); + if (!group) + return false; + + GuidVector members = AI_VALUE(GuidVector, "group members"); + Unit* closestMember = nullptr; + float closestDist = std::numeric_limits::max(); + + for (auto& member : members) + { + Unit* unit = botAI->GetUnit(member); + if (!unit || bot->GetGUID() == member) + continue; + + const float dist = bot->GetExactDist2d(unit); + if (dist < closestDist) + { + closestDist = dist; + closestMember = unit; + } + } + + if (closestMember) + { + return FleePosition(Position(closestMember->GetPositionX(), closestMember->GetPositionY(), + closestMember->GetPositionZ()), 6.0f, 0); + } + + return false; +} diff --git a/src/strategy/raids/gruulslair/RaidGruulsLairActions.h b/src/strategy/raids/gruulslair/RaidGruulsLairActions.h new file mode 100644 index 0000000000..6faf7ed3ec --- /dev/null +++ b/src/strategy/raids/gruulslair/RaidGruulsLairActions.h @@ -0,0 +1,112 @@ +#ifndef _PLAYERBOT_RAIDGRUULSLAIRACTIONS_H +#define _PLAYERBOT_RAIDGRUULSLAIRACTIONS_H + +#include "Action.h" +#include "AttackAction.h" +#include "MovementActions.h" + +class HighKingMaulgarMainTankAttackMaulgarAction : public AttackAction +{ +public: + HighKingMaulgarMainTankAttackMaulgarAction(PlayerbotAI* botAI, std::string const name = "high king maulgar main tank attack maulgar") : AttackAction(botAI, name) {}; + + bool Execute(Event event) override; +}; + +class HighKingMaulgarFirstAssistTankAttackOlmAction : public AttackAction +{ +public: + HighKingMaulgarFirstAssistTankAttackOlmAction(PlayerbotAI* botAI, std::string const name = "high king maulgar first assist tank attack olm") : AttackAction(botAI, name) {}; + + bool Execute(Event event) override; +}; + +class HighKingMaulgarSecondAssistTankAttackBlindeyeAction : public AttackAction +{ +public: + HighKingMaulgarSecondAssistTankAttackBlindeyeAction(PlayerbotAI* botAI, std::string const name = "high king maulgar second assist tank attack blindeye") : AttackAction(botAI, name) {}; + + bool Execute(Event event) override; +}; + +class HighKingMaulgarMageTankAttackKroshAction : public AttackAction +{ +public: + HighKingMaulgarMageTankAttackKroshAction(PlayerbotAI* botAI, std::string const name = "high king maulgar mage tank attack krosh") : AttackAction(botAI, name) {}; + + bool Execute(Event event) override; +}; + +class HighKingMaulgarMoonkinTankAttackKigglerAction : public AttackAction +{ +public: + HighKingMaulgarMoonkinTankAttackKigglerAction(PlayerbotAI* botAI, std::string const name = "high king maulgar moonkin tank attack kiggler") : AttackAction(botAI, name) {}; + + bool Execute(Event event) override; +}; + +class HighKingMaulgarAssignDPSPriorityAction : public AttackAction +{ +public: + HighKingMaulgarAssignDPSPriorityAction(PlayerbotAI* botAI, std::string const name = "high king maulgar assign dps priority") : AttackAction(botAI, name) {}; + + bool Execute(Event event) override; +}; + +class HighKingMaulgarHealerFindSafePositionAction : public MovementAction +{ +public: + HighKingMaulgarHealerFindSafePositionAction(PlayerbotAI* botAI, std::string const name = "high king maulgar healer find safe position") : MovementAction(botAI, name) {}; + + bool Execute(Event event) override; +}; + +class HighKingMaulgarRunAwayFromWhirlwindAction : public MovementAction +{ +public: + HighKingMaulgarRunAwayFromWhirlwindAction(PlayerbotAI* botAI, std::string const name = "high king maulgar run away from whirlwind") : MovementAction(botAI, name) {}; + + bool Execute(Event event) override; +}; + +class HighKingMaulgarBanishFelstalkerAction : public AttackAction +{ +public: + HighKingMaulgarBanishFelstalkerAction(PlayerbotAI* botAI, std::string const name = "high king maulgar banish felstalker") : AttackAction(botAI, name) {}; + + bool Execute(Event event) override; +}; + +class HighKingMaulgarMisdirectOlmAndBlindeyeAction : public AttackAction +{ +public: + HighKingMaulgarMisdirectOlmAndBlindeyeAction(PlayerbotAI* botAI, std::string const name = "high king maulgar misdirect olm and blindeye") : AttackAction(botAI, name) {}; + + bool Execute(Event event) override; +}; + +class GruulTheDragonkillerMainTankPositionBossAction : public AttackAction +{ +public: + GruulTheDragonkillerMainTankPositionBossAction(PlayerbotAI* botAI, std::string const name = "gruul the dragonkiller main tank position boss") : AttackAction(botAI, name) {}; + + bool Execute(Event event) override; +}; + +class GruulTheDragonkillerSpreadRangedAction : public MovementAction +{ +public: + GruulTheDragonkillerSpreadRangedAction(PlayerbotAI* botAI, std::string const name = "gruul the dragonkiller spread ranged") : MovementAction(botAI, name) {}; + + bool Execute(Event event) override; +}; + +class GruulTheDragonkillerShatterSpreadAction : public MovementAction +{ +public: + GruulTheDragonkillerShatterSpreadAction(PlayerbotAI* botAI, std::string const name = "gruul the dragonkiller shatter spread") : MovementAction(botAI, name) {}; + + bool Execute(Event event) override; +}; + +#endif diff --git a/src/strategy/raids/gruulslair/RaidGruulsLairHelpers.cpp b/src/strategy/raids/gruulslair/RaidGruulsLairHelpers.cpp new file mode 100644 index 0000000000..0c8a23a19c --- /dev/null +++ b/src/strategy/raids/gruulslair/RaidGruulsLairHelpers.cpp @@ -0,0 +1,241 @@ +#include "RaidGruulsLairHelpers.h" +#include "AiFactory.h" +#include "GroupReference.h" +#include "Playerbots.h" +#include "Unit.h" + +namespace GruulsLairHelpers +{ + namespace GruulsLairLocations + { + // Olm does not chase properly due to the Core's caster movement issues + // Thus, the below "OlmTankPosition" is beyond the actual desired tanking location + // It is the spot to which the OlmTank runs to to pull Olm to a decent tanking location + // "MaulgarRoomCenter" is to keep healers in a centralized location + const Location MaulgarTankPosition = { 90.686f, 167.047f, -13.234f }; + const Location OlmTankPosition = { 87.485f, 234.942f, -3.635f }; + const Location BlindeyeTankPosition = { 99.681f, 213.989f, -10.345f }; + const Location KroshTankPosition = { 116.880f, 166.208f, -14.231f }; + const Location MaulgarRoomCenter = { 88.754f, 150.759f, -11.569f }; + const Location GruulTankPosition = { 241.238f, 365.025f, -4.220f }; + } + + bool IsAnyOgreBossAlive(PlayerbotAI* botAI) + { + const char* ogreBossNames[] = + { + "high king maulgar", + "kiggler the crazed", + "krosh firehand", + "olm the summoner", + "blindeye the seer" + }; + + for (const char* name : ogreBossNames) + { + Unit* boss = botAI->GetAiObjectContext()->GetValue("find target", name)->Get(); + if (!boss || !boss->IsAlive()) + continue; + return true; + } + + return false; + } + + void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId) + { + Group* group = bot->GetGroup(); + if (!target || !group) + return; + + ObjectGuid currentGuid = group->GetTargetIcon(iconId); + if (currentGuid != target->GetGUID()) + { + group->SetTargetIcon(iconId, bot->GetGUID(), target->GetGUID()); + } + } + + void MarkTargetWithSquare(Player* bot, Unit* target) + { + MarkTargetWithIcon(bot, target, RtiTargetValue::squareIndex); + } + + void MarkTargetWithStar(Player* bot, Unit* target) + { + MarkTargetWithIcon(bot, target, RtiTargetValue::starIndex); + } + + void MarkTargetWithCircle(Player* bot, Unit* target) + { + MarkTargetWithIcon(bot, target, RtiTargetValue::circleIndex); + } + + void MarkTargetWithDiamond(Player* bot, Unit* target) + { + MarkTargetWithIcon(bot, target, RtiTargetValue::diamondIndex); + } + + void MarkTargetWithTriangle(Player* bot, Unit* target) + { + MarkTargetWithIcon(bot, target, RtiTargetValue::triangleIndex); + } + + void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target) + { + if (!target) + return; + + std::string currentRti = botAI->GetAiObjectContext()->GetValue("rti")->Get(); + Unit* currentTarget = botAI->GetAiObjectContext()->GetValue("rti target")->Get(); + + if (currentRti != rtiName || currentTarget != target) + { + botAI->GetAiObjectContext()->GetValue("rti")->Set(rtiName); + botAI->GetAiObjectContext()->GetValue("rti target")->Set(target); + } + } + + bool IsKroshMageTank(PlayerbotAI* botAI, Player* bot) + { + Group* group = bot->GetGroup(); + if (!group) + return false; + + Player* highestHpMage = nullptr; + uint32 highestHp = 0; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member)) + continue; + + if (member->getClass() == CLASS_MAGE) + { + uint32 hp = member->GetMaxHealth(); + if (!highestHpMage || hp > highestHp) + { + highestHpMage = member; + highestHp = hp; + } + } + } + + return highestHpMage == bot; + } + + bool IsKigglerMoonkinTank(PlayerbotAI* botAI, Player* bot) + { + Group* group = bot->GetGroup(); + if (!group) + return false; + + Player* highestHpMoonkin = nullptr; + uint32 highestHp = 0; + + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member)) + continue; + + if (member->getClass() == CLASS_DRUID) + { + int tab = AiFactory::GetPlayerSpecTab(member); + if (tab == DRUID_TAB_BALANCE) + { + uint32 hp = member->GetMaxHealth(); + if (!highestHpMoonkin || hp > highestHp) + { + highestHpMoonkin = member; + highestHp = hp; + } + } + } + } + + return highestHpMoonkin == bot; + } + + bool IsPositionSafe(PlayerbotAI* botAI, Player* bot, Position pos) + { + const float KROSH_SAFE_DISTANCE = 20.0f; + const float MAULGAR_SAFE_DISTANCE = 10.0f; + bool isSafe = true; + + Unit* krosh = botAI->GetAiObjectContext()->GetValue("find target", "krosh firehand")->Get(); + if (krosh && krosh->IsAlive()) + { + float dist = sqrt(pow(pos.GetPositionX() - krosh->GetPositionX(), 2) + pow(pos.GetPositionY() - krosh->GetPositionY(), 2)); + if (dist < KROSH_SAFE_DISTANCE) + isSafe = false; + } + + Unit* maulgar = botAI->GetAiObjectContext()->GetValue("find target", "high king maulgar")->Get(); + if (botAI->IsRanged(bot) && maulgar && maulgar->IsAlive()) + { + float dist = sqrt(pow(pos.GetPositionX() - maulgar->GetPositionX(), 2) + pow(pos.GetPositionY() - maulgar->GetPositionY(), 2)); + if (dist < MAULGAR_SAFE_DISTANCE) + isSafe = false; + } + + return isSafe; + } + + bool TryGetNewSafePosition(PlayerbotAI* botAI, Player* bot, Position& outPos) + { + const float SEARCH_RADIUS = 30.0f; + const uint8 NUM_POSITIONS = 32; + + outPos = { bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ() }; + if (IsPositionSafe(botAI, bot, outPos)) + { + outPos = Position(); + return false; + } + + float bestScore = std::numeric_limits::max(); + bool foundSafeSpot = false; + Position bestPos; + + for (int i = 0; i < NUM_POSITIONS; ++i) + { + float angle = 2 * M_PI * i / NUM_POSITIONS; + Position candidatePos; + candidatePos.m_positionX = bot->GetPositionX() + SEARCH_RADIUS * cos(angle); + candidatePos.m_positionY = bot->GetPositionY() + SEARCH_RADIUS * sin(angle); + candidatePos.m_positionZ = bot->GetPositionZ(); + + float destX = candidatePos.m_positionX, destY = candidatePos.m_positionY, destZ = candidatePos.m_positionZ; + if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(), + bot->GetPositionZ(), destX, destY, destZ, true)) + continue; + + if (destX != candidatePos.m_positionX || destY != candidatePos.m_positionY) + continue; + + candidatePos.m_positionX = destX; + candidatePos.m_positionY = destY; + candidatePos.m_positionZ = destZ; + + if (IsPositionSafe(botAI, bot, candidatePos)) + { + float movementDistance = sqrt(pow(destX - bot->GetPositionX(), 2) + pow(destY - bot->GetPositionY(), 2)); + if (movementDistance < bestScore) + { + bestScore = movementDistance; + bestPos = candidatePos; + foundSafeSpot = true; + } + } + } + + if (foundSafeSpot) + { + outPos = bestPos; + return true; + } + + outPos = Position(); + return false; + } +} diff --git a/src/strategy/raids/gruulslair/RaidGruulsLairHelpers.h b/src/strategy/raids/gruulslair/RaidGruulsLairHelpers.h new file mode 100644 index 0000000000..aa5a83acb0 --- /dev/null +++ b/src/strategy/raids/gruulslair/RaidGruulsLairHelpers.h @@ -0,0 +1,62 @@ +#ifndef RAID_GRUULSLAIRHELPERS_H +#define RAID_GRUULSLAIRHELPERS_H + +#include "PlayerbotAI.h" +#include "RtiTargetValue.h" + +namespace GruulsLairHelpers +{ + enum GruulsLairSpells + { + // High King Maulgar + SPELL_WHIRLWIND = 33238, + + // Krosh Firehand + SPELL_SPELL_SHIELD = 33054, + + // Hunter + SPELL_MISDIRECTION = 34477, + + // Warlock + SPELL_BANISH = 18647, // Rank 2 + + // Gruul the Dragonkiller + SPELL_GROUND_SLAM_1 = 33525, + SPELL_GROUND_SLAM_2 = 39187, + }; + + enum GruulsLairNPCs + { + NPC_WILD_FEL_STALKER = 18847, + }; + + bool IsAnyOgreBossAlive(PlayerbotAI* botAI); + void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId); + void MarkTargetWithSquare(Player* bot, Unit* target); + void MarkTargetWithStar(Player* bot, Unit* target); + void MarkTargetWithCircle(Player* bot, Unit* target); + void MarkTargetWithDiamond(Player* bot, Unit* target); + void MarkTargetWithTriangle(Player* bot, Unit* target); + void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target); + bool IsKroshMageTank(PlayerbotAI* botAI, Player* bot); + bool IsKigglerMoonkinTank(PlayerbotAI* botAI, Player* bot); + bool IsPositionSafe(PlayerbotAI* botAI, Player* bot, Position pos); + bool TryGetNewSafePosition(PlayerbotAI* botAI, Player* bot, Position& outPos); + + struct Location + { + float x, y, z; + }; + + namespace GruulsLairLocations + { + extern const Location MaulgarTankPosition; + extern const Location OlmTankPosition; + extern const Location BlindeyeTankPosition; + extern const Location KroshTankPosition; + extern const Location MaulgarRoomCenter; + extern const Location GruulTankPosition; + } +} + +#endif diff --git a/src/strategy/raids/gruulslair/RaidGruulsLairMultipliers.cpp b/src/strategy/raids/gruulslair/RaidGruulsLairMultipliers.cpp new file mode 100644 index 0000000000..5ca2de9327 --- /dev/null +++ b/src/strategy/raids/gruulslair/RaidGruulsLairMultipliers.cpp @@ -0,0 +1,110 @@ +#include "RaidGruulsLairMultipliers.h" +#include "RaidGruulsLairActions.h" +#include "RaidGruulsLairHelpers.h" +#include "ChooseTargetActions.h" +#include "DruidBearActions.h" +#include "DruidCatActions.h" +#include "GenericSpellActions.h" +#include "HunterActions.h" +#include "MageActions.h" +#include "Playerbots.h" +#include "WarriorActions.h" + +using namespace GruulsLairHelpers; + +static bool IsChargeAction(Action* action) +{ + return dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action); +} + +float HighKingMaulgarDisableTankAssistMultiplier::GetValue(Action* action) +{ + if (IsAnyOgreBossAlive(botAI) && dynamic_cast(action)) + return 0.0f; + + return 1.0f; +} + +// Don't run back in during Whirlwind +float HighKingMaulgarAvoidWhirlwindMultiplier::GetValue(Action* action) +{ + Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar"); + Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed"); + Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand"); + Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner"); + Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer"); + + if (maulgar && maulgar->HasAura(SPELL_WHIRLWIND) && + (!kiggler || !kiggler->IsAlive()) && + (!krosh || !krosh->IsAlive()) && + (!olm || !olm->IsAlive()) && + (!blindeye || !blindeye->IsAlive())) + { + if (IsChargeAction(action) || (dynamic_cast(action) && + !dynamic_cast(action))) + return 0.0f; + } + + return 1.0f; +} + +// Arcane Shot will remove Spell Shield, which the mage tank needs to survive +float HighKingMaulgarDisableArcaneShotOnKroshMultiplier::GetValue(Action* action) +{ + Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand"); + Unit* target = AI_VALUE(Unit*, "current target"); + + if (krosh && target && target->GetGUID() == krosh->GetGUID() && dynamic_cast(action)) + return 0.0f; + + return 1.0f; +} + +float HighKingMaulgarDisableMageTankAOEMultiplier::GetValue(Action* action) +{ + if (IsKroshMageTank(botAI, bot) && + (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action))) + return 0.0f; + + return 1.0f; +} + +float GruulTheDragonkillerMainTankMovementMultiplier::GetValue(Action* action) +{ + Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller"); + if (!gruul) + return 1.0f; + + if (botAI->IsMainTank(bot)) + { + if (gruul->GetVictim() == bot && dynamic_cast(action)) + return 0.0f; + + if (dynamic_cast(action)) + return 0.0f; + } + + return 1.0f; +} + +float GruulTheDragonkillerGroundSlamMultiplier::GetValue(Action* action) +{ + Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller"); + if (!gruul) + return 1.0f; + + if (bot->HasAura(SPELL_GROUND_SLAM_1) || + bot->HasAura(SPELL_GROUND_SLAM_2)) + { + if ((dynamic_cast(action) && !dynamic_cast(action)) || + IsChargeAction(action)) + return 0.0f; + } + + return 1.0f; +} diff --git a/src/strategy/raids/gruulslair/RaidGruulsLairMultipliers.h b/src/strategy/raids/gruulslair/RaidGruulsLairMultipliers.h new file mode 100644 index 0000000000..d67e8523e7 --- /dev/null +++ b/src/strategy/raids/gruulslair/RaidGruulsLairMultipliers.h @@ -0,0 +1,48 @@ +#ifndef _PLAYERBOT_RAIDGRUULSLAIRMULTIPLIERS_H +#define _PLAYERBOT_RAIDGRUULSLAIRMULTIPLIERS_H + +#include "Multiplier.h" + +class HighKingMaulgarDisableTankAssistMultiplier : public Multiplier +{ +public: + HighKingMaulgarDisableTankAssistMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "high king maulgar disable tank assist multiplier") {} + float GetValue(Action* action) override; +}; + +class HighKingMaulgarAvoidWhirlwindMultiplier : public Multiplier +{ +public: + HighKingMaulgarAvoidWhirlwindMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "high king maulgar avoid whirlwind multiplier") {} + float GetValue(Action* action) override; +}; + +class HighKingMaulgarDisableArcaneShotOnKroshMultiplier : public Multiplier +{ +public: + HighKingMaulgarDisableArcaneShotOnKroshMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "high king maulgar disable arcane shot on krosh multiplier") {} + float GetValue(Action* action) override; +}; + +class HighKingMaulgarDisableMageTankAOEMultiplier : public Multiplier +{ +public: + HighKingMaulgarDisableMageTankAOEMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "high king maulgar disable mage tank aoe multiplier") {} + float GetValue(Action* action) override; +}; + +class GruulTheDragonkillerMainTankMovementMultiplier : public Multiplier +{ +public: + GruulTheDragonkillerMainTankMovementMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "gruul the dragonkiller main tank movement multiplier") {} + float GetValue(Action* action) override; +}; + +class GruulTheDragonkillerGroundSlamMultiplier : public Multiplier +{ +public: + GruulTheDragonkillerGroundSlamMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "gruul the dragonkiller ground slam multiplier") {} + float GetValue(Action* action) override; +}; + +#endif diff --git a/src/strategy/raids/gruulslair/RaidGruulsLairStrategy.cpp b/src/strategy/raids/gruulslair/RaidGruulsLairStrategy.cpp new file mode 100644 index 0000000000..bd7c9cd970 --- /dev/null +++ b/src/strategy/raids/gruulslair/RaidGruulsLairStrategy.cpp @@ -0,0 +1,56 @@ +#include "RaidGruulsLairStrategy.h" +#include "RaidGruulsLairMultipliers.h" + +void RaidGruulsLairStrategy::InitTriggers(std::vector& triggers) +{ + // High King Maulgar + triggers.push_back(new TriggerNode("high king maulgar is main tank", NextAction::array(0, + new NextAction("high king maulgar main tank attack maulgar", ACTION_RAID + 1), nullptr))); + + triggers.push_back(new TriggerNode("high king maulgar is first assist tank", NextAction::array(0, + new NextAction("high king maulgar first assist tank attack olm", ACTION_RAID + 1), nullptr))); + + triggers.push_back(new TriggerNode("high king maulgar is second assist tank", NextAction::array(0, + new NextAction("high king maulgar second assist tank attack blindeye", ACTION_RAID + 1), nullptr))); + + triggers.push_back(new TriggerNode("high king maulgar is mage tank", NextAction::array(0, + new NextAction("high king maulgar mage tank attack krosh", ACTION_RAID + 1), nullptr))); + + triggers.push_back(new TriggerNode("high king maulgar is moonkin tank", NextAction::array(0, + new NextAction("high king maulgar moonkin tank attack kiggler", ACTION_RAID + 1), nullptr))); + + triggers.push_back(new TriggerNode("high king maulgar determining kill order", NextAction::array(0, + new NextAction("high king maulgar assign dps priority", ACTION_RAID + 1), nullptr))); + + triggers.push_back(new TriggerNode("high king maulgar healer in danger", NextAction::array(0, + new NextAction("high king maulgar healer find safe position", ACTION_RAID + 1), nullptr))); + + triggers.push_back(new TriggerNode("high king maulgar boss channeling whirlwind", NextAction::array(0, + new NextAction("high king maulgar run away from whirlwind", ACTION_EMERGENCY + 6), nullptr))); + + triggers.push_back(new TriggerNode("high king maulgar wild felstalker spawned", NextAction::array(0, + new NextAction("high king maulgar banish felstalker", ACTION_RAID + 2), nullptr))); + + triggers.push_back(new TriggerNode("high king maulgar pulling olm and blindeye", NextAction::array(0, + new NextAction("high king maulgar misdirect olm and blindeye", ACTION_RAID + 2), nullptr))); + + // Gruul the Dragonkiller + triggers.push_back(new TriggerNode("gruul the dragonkiller boss engaged by main tank", NextAction::array(0, + new NextAction("gruul the dragonkiller main tank position boss", ACTION_RAID + 1), nullptr))); + + triggers.push_back(new TriggerNode("gruul the dragonkiller boss engaged by range", NextAction::array(0, + new NextAction("gruul the dragonkiller spread ranged", ACTION_RAID + 1), nullptr))); + + triggers.push_back(new TriggerNode("gruul the dragonkiller incoming shatter", NextAction::array(0, + new NextAction("gruul the dragonkiller shatter spread", ACTION_EMERGENCY + 6), nullptr))); +} + +void RaidGruulsLairStrategy::InitMultipliers(std::vector& multipliers) +{ + multipliers.push_back(new HighKingMaulgarDisableTankAssistMultiplier(botAI)); + multipliers.push_back(new HighKingMaulgarAvoidWhirlwindMultiplier(botAI)); + multipliers.push_back(new HighKingMaulgarDisableArcaneShotOnKroshMultiplier(botAI)); + multipliers.push_back(new HighKingMaulgarDisableMageTankAOEMultiplier(botAI)); + multipliers.push_back(new GruulTheDragonkillerMainTankMovementMultiplier(botAI)); + multipliers.push_back(new GruulTheDragonkillerGroundSlamMultiplier(botAI)); +} diff --git a/src/strategy/raids/gruulslair/RaidGruulsLairStrategy.h b/src/strategy/raids/gruulslair/RaidGruulsLairStrategy.h new file mode 100644 index 0000000000..ba6f33f076 --- /dev/null +++ b/src/strategy/raids/gruulslair/RaidGruulsLairStrategy.h @@ -0,0 +1,18 @@ +#ifndef _PLAYERBOT_RAIDGRUULSLAIRSTRATEGY_H +#define _PLAYERBOT_RAIDGRUULSLAIRSTRATEGY_H + +#include "Strategy.h" +#include "Multiplier.h" + +class RaidGruulsLairStrategy : public Strategy +{ +public: + RaidGruulsLairStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} + + std::string const getName() override { return "gruulslair"; } + + void InitTriggers(std::vector& triggers) override; + void InitMultipliers(std::vector &multipliers) override; +}; + +#endif diff --git a/src/strategy/raids/gruulslair/RaidGruulsLairTriggerContext.h b/src/strategy/raids/gruulslair/RaidGruulsLairTriggerContext.h new file mode 100644 index 0000000000..fa8e76f58b --- /dev/null +++ b/src/strategy/raids/gruulslair/RaidGruulsLairTriggerContext.h @@ -0,0 +1,49 @@ +#ifndef _PLAYERBOT_RAIDGRUULSLAIRTRIGGERCONTEXT_H +#define _PLAYERBOT_RAIDGRUULSLAIRTRIGGERCONTEXT_H + +#include "RaidGruulsLairTriggers.h" +#include "AiObjectContext.h" + +class RaidGruulsLairTriggerContext : public NamedObjectContext +{ +public: + RaidGruulsLairTriggerContext() : NamedObjectContext() + { + // High King Maulgar + creators["high king maulgar is main tank"] = &RaidGruulsLairTriggerContext::high_king_maulgar_is_main_tank; + creators["high king maulgar is first assist tank"] = &RaidGruulsLairTriggerContext::high_king_maulgar_is_first_assist_tank; + creators["high king maulgar is second assist tank"] = &RaidGruulsLairTriggerContext::high_king_maulgar_is_second_assist_tank; + creators["high king maulgar is mage tank"] = &RaidGruulsLairTriggerContext::high_king_maulgar_is_mage_tank; + creators["high king maulgar is moonkin tank"] = &RaidGruulsLairTriggerContext::high_king_maulgar_is_moonkin_tank; + creators["high king maulgar determining kill order"] = &RaidGruulsLairTriggerContext::high_king_maulgar_determining_kill_order; + creators["high king maulgar healer in danger"] = &RaidGruulsLairTriggerContext::high_king_maulgar_healer_in_danger; + creators["high king maulgar boss channeling whirlwind"] = &RaidGruulsLairTriggerContext::high_king_maulgar_boss_channeling_whirlwind; + creators["high king maulgar wild felstalker spawned"] = &RaidGruulsLairTriggerContext::high_king_maulgar_wild_felstalker_spawned; + creators["high king maulgar pulling olm and blindeye"] = &RaidGruulsLairTriggerContext::high_king_maulgar_pulling_olm_and_blindeye; + + // Gruul the Dragonkiller + creators["gruul the dragonkiller boss engaged by main tank"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_boss_engaged_by_main_tank; + creators["gruul the dragonkiller boss engaged by range"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_boss_engaged_by_range; + creators["gruul the dragonkiller incoming shatter"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_incoming_shatter; + } + +private: + // High King Maulgar + static Trigger* high_king_maulgar_is_main_tank(PlayerbotAI* botAI) { return new HighKingMaulgarIsMainTankTrigger(botAI); } + static Trigger* high_king_maulgar_is_first_assist_tank(PlayerbotAI* botAI) { return new HighKingMaulgarIsFirstAssistTankTrigger(botAI); } + static Trigger* high_king_maulgar_is_second_assist_tank(PlayerbotAI* botAI) { return new HighKingMaulgarIsSecondAssistTankTrigger(botAI); } + static Trigger* high_king_maulgar_is_mage_tank(PlayerbotAI* botAI) { return new HighKingMaulgarIsMageTankTrigger(botAI); } + static Trigger* high_king_maulgar_is_moonkin_tank(PlayerbotAI* botAI) { return new HighKingMaulgarIsMoonkinTankTrigger(botAI); } + static Trigger* high_king_maulgar_determining_kill_order(PlayerbotAI* botAI) { return new HighKingMaulgarDeterminingKillOrderTrigger(botAI); } + static Trigger* high_king_maulgar_healer_in_danger(PlayerbotAI* botAI) { return new HighKingMaulgarHealerInDangerTrigger(botAI); } + static Trigger* high_king_maulgar_boss_channeling_whirlwind(PlayerbotAI* botAI) { return new HighKingMaulgarBossChannelingWhirlwindTrigger(botAI); } + static Trigger* high_king_maulgar_wild_felstalker_spawned(PlayerbotAI* botAI) { return new HighKingMaulgarWildFelstalkerSpawnedTrigger(botAI); } + static Trigger* high_king_maulgar_pulling_olm_and_blindeye(PlayerbotAI* botAI) { return new HighKingMaulgarPullingOlmAndBlindeyeTrigger(botAI); } + + // Gruul the Dragonkiller + static Trigger* gruul_the_dragonkiller_boss_engaged_by_main_tank(PlayerbotAI* botAI) { return new GruulTheDragonkillerBossEngagedByMainTankTrigger(botAI); } + static Trigger* gruul_the_dragonkiller_boss_engaged_by_range(PlayerbotAI* botAI) { return new GruulTheDragonkillerBossEngagedByRangeTrigger(botAI); } + static Trigger* gruul_the_dragonkiller_incoming_shatter(PlayerbotAI* botAI) { return new GruulTheDragonkillerIncomingShatterTrigger(botAI); } +}; + +#endif diff --git a/src/strategy/raids/gruulslair/RaidGruulsLairTriggers.cpp b/src/strategy/raids/gruulslair/RaidGruulsLairTriggers.cpp new file mode 100644 index 0000000000..35d9f9a1da --- /dev/null +++ b/src/strategy/raids/gruulslair/RaidGruulsLairTriggers.cpp @@ -0,0 +1,160 @@ +#include "RaidGruulsLairTriggers.h" +#include "RaidGruulsLairHelpers.h" +#include "Playerbots.h" + +using namespace GruulsLairHelpers; + +// High King Maulgar Triggers + +bool HighKingMaulgarIsMainTankTrigger::IsActive() +{ + Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar"); + + return botAI->IsMainTank(bot) && maulgar && maulgar->IsAlive(); +} + +bool HighKingMaulgarIsFirstAssistTankTrigger::IsActive() +{ + Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner"); + + return botAI->IsAssistTankOfIndex(bot, 0) && olm && olm->IsAlive(); +} + +bool HighKingMaulgarIsSecondAssistTankTrigger::IsActive() +{ + Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer"); + + return botAI->IsAssistTankOfIndex(bot, 1) && blindeye && blindeye->IsAlive(); +} + +bool HighKingMaulgarIsMageTankTrigger::IsActive() +{ + Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand"); + + return IsKroshMageTank(botAI, bot) && krosh && krosh->IsAlive(); +} + +bool HighKingMaulgarIsMoonkinTankTrigger::IsActive() +{ + Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed"); + + return IsKigglerMoonkinTank(botAI, bot) && kiggler && kiggler->IsAlive(); +} + +bool HighKingMaulgarDeterminingKillOrderTrigger::IsActive() +{ + Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar"); + Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed"); + Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner"); + Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer"); + Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand"); + + return (botAI->IsDps(bot) || botAI->IsTank(bot)) && + !(botAI->IsMainTank(bot) && maulgar && maulgar->IsAlive()) && + !(botAI->IsAssistTankOfIndex(bot, 0) && olm && olm->IsAlive()) && + !(botAI->IsAssistTankOfIndex(bot, 1) && blindeye && blindeye->IsAlive()) && + !(IsKroshMageTank(botAI, bot) && krosh && krosh->IsAlive()) && + !(IsKigglerMoonkinTank(botAI, bot) && kiggler && kiggler->IsAlive()); +} + +bool HighKingMaulgarHealerInDangerTrigger::IsActive() +{ + return botAI->IsHeal(bot) && IsAnyOgreBossAlive(botAI); +} + +bool HighKingMaulgarBossChannelingWhirlwindTrigger::IsActive() +{ + Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar"); + + return maulgar && maulgar->IsAlive() && maulgar->HasAura(SPELL_WHIRLWIND) && + !botAI->IsMainTank(bot); +} + +bool HighKingMaulgarWildFelstalkerSpawnedTrigger::IsActive() +{ + Unit* felStalker = AI_VALUE2(Unit*, "find target", "wild fel stalker"); + + return felStalker && felStalker->IsAlive() && bot->getClass() == CLASS_WARLOCK; +} + +bool HighKingMaulgarPullingOlmAndBlindeyeTrigger::IsActive() +{ + Group* group = bot->GetGroup(); + if (!group || bot->getClass() != CLASS_HUNTER) + return false; + + std::vector hunters; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && member->IsAlive() && member->getClass() == CLASS_HUNTER && GET_PLAYERBOT_AI(member)) + hunters.push_back(member); + } + + int hunterIndex = -1; + for (size_t i = 0; i < hunters.size(); ++i) + { + if (hunters[i] == bot) + { + hunterIndex = static_cast(i); + break; + } + } + if (hunterIndex == -1 || hunterIndex > 1) + return false; + + Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner"); + Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer"); + Player* olmTank = nullptr; + Player* blindeyeTank = nullptr; + + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive()) + continue; + else if (botAI->IsAssistTankOfIndex(member, 0)) olmTank = member; + else if (botAI->IsAssistTankOfIndex(member, 1)) blindeyeTank = member; + } + + switch (hunterIndex) + { + case 0: + return olm && olm->IsAlive() && olm->GetHealthPct() > 98.0f && + olmTank && olmTank->IsAlive() && botAI->CanCastSpell("misdirection", olmTank); + + case 1: + return blindeye && blindeye->IsAlive() && blindeye->GetHealthPct() > 90.0f && + blindeyeTank && blindeyeTank->IsAlive() && botAI->CanCastSpell("misdirection", blindeyeTank); + + default: + break; + } + + return false; +} + +// Gruul the Dragonkiller Triggers + +bool GruulTheDragonkillerBossEngagedByMainTankTrigger::IsActive() +{ + Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller"); + + return gruul && gruul->IsAlive() && botAI->IsMainTank(bot); +} + +bool GruulTheDragonkillerBossEngagedByRangeTrigger::IsActive() +{ + Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller"); + + return gruul && gruul->IsAlive() && botAI->IsRanged(bot); +} + +bool GruulTheDragonkillerIncomingShatterTrigger::IsActive() +{ + Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller"); + + return gruul && gruul->IsAlive() && + (bot->HasAura(SPELL_GROUND_SLAM_1) || + bot->HasAura(SPELL_GROUND_SLAM_2)); +} diff --git a/src/strategy/raids/gruulslair/RaidGruulsLairTriggers.h b/src/strategy/raids/gruulslair/RaidGruulsLairTriggers.h new file mode 100644 index 0000000000..f3f3285361 --- /dev/null +++ b/src/strategy/raids/gruulslair/RaidGruulsLairTriggers.h @@ -0,0 +1,97 @@ +#ifndef _PLAYERBOT_RAIDGRUULSLAIRTRIGGERS_H +#define _PLAYERBOT_RAIDGRUULSLAIRTRIGGERS_H + +#include "Trigger.h" + +class HighKingMaulgarIsMainTankTrigger : public Trigger +{ +public: + HighKingMaulgarIsMainTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar is main tank") {} + bool IsActive() override; +}; + +class HighKingMaulgarIsFirstAssistTankTrigger : public Trigger +{ +public: + HighKingMaulgarIsFirstAssistTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar is first assist tank") {} + bool IsActive() override; +}; + +class HighKingMaulgarIsSecondAssistTankTrigger : public Trigger +{ +public: + HighKingMaulgarIsSecondAssistTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar is second assist tank") {} + bool IsActive() override; +}; + +class HighKingMaulgarIsMageTankTrigger : public Trigger +{ +public: + HighKingMaulgarIsMageTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar is mage tank") {} + bool IsActive() override; +}; + +class HighKingMaulgarIsMoonkinTankTrigger : public Trigger +{ +public: + HighKingMaulgarIsMoonkinTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar is moonkin tank") {} + bool IsActive() override; +}; + +class HighKingMaulgarDeterminingKillOrderTrigger : public Trigger +{ +public: + HighKingMaulgarDeterminingKillOrderTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar determining kill order") {} + bool IsActive() override; +}; + +class HighKingMaulgarHealerInDangerTrigger : public Trigger +{ +public: + HighKingMaulgarHealerInDangerTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar healers in danger") {} + bool IsActive() override; +}; + +class HighKingMaulgarBossChannelingWhirlwindTrigger : public Trigger +{ +public: + HighKingMaulgarBossChannelingWhirlwindTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar boss channeling whirlwind") {} + bool IsActive() override; +}; + +class HighKingMaulgarWildFelstalkerSpawnedTrigger : public Trigger +{ +public: + HighKingMaulgarWildFelstalkerSpawnedTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar wild felstalker spawned") {} + bool IsActive() override; +}; + +class HighKingMaulgarPullingOlmAndBlindeyeTrigger : public Trigger +{ +public: + HighKingMaulgarPullingOlmAndBlindeyeTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar pulling olm and blindeye") {} + bool IsActive() override; +}; + +class GruulTheDragonkillerBossEngagedByMainTankTrigger : public Trigger +{ +public: + GruulTheDragonkillerBossEngagedByMainTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "gruul the dragonkiller boss engaged by main tank") {} + bool IsActive() override; +}; + +class GruulTheDragonkillerBossEngagedByRangeTrigger : public Trigger +{ +public: + GruulTheDragonkillerBossEngagedByRangeTrigger(PlayerbotAI* botAI) : Trigger(botAI, "gruul the dragonkiller boss engaged by range") {} + bool IsActive() override; +}; + +class GruulTheDragonkillerIncomingShatterTrigger : public Trigger +{ +public: + GruulTheDragonkillerIncomingShatterTrigger(PlayerbotAI* botAI) : Trigger(botAI, "gruul the dragonkiller incoming shatter") {} + bool IsActive() override; +}; + +#endif From 26a135a1ecba1c02d8ce1404308c2531d2b912a4 Mon Sep 17 00:00:00 2001 From: privatecore Date: Tue, 4 Nov 2025 23:25:59 +0100 Subject: [PATCH 15/47] Rewrite RandomPlayerbotFactory for improved maintainability and future updates (#1758) * Rewrite RandomPlayerbotFactory - rewrite constructor and utility methods, simplify checks and logic * Update the comment to clarify the original logic for race selection * Remove magic numbers from CombineRaceAndGender method (gender) * Add checks for races and classes disabled during random bot creation --- src/RandomPlayerbotFactory.cpp | 233 ++++++++++----------------------- src/RandomPlayerbotFactory.h | 8 +- 2 files changed, 73 insertions(+), 168 deletions(-) diff --git a/src/RandomPlayerbotFactory.cpp b/src/RandomPlayerbotFactory.cpp index ceed748d42..4be2d0edfc 100644 --- a/src/RandomPlayerbotFactory.cpp +++ b/src/RandomPlayerbotFactory.cpp @@ -19,188 +19,99 @@ #include "Log.h" #include "GuildMgr.h" -std::map> RandomPlayerbotFactory::availableRaces; - -constexpr RandomPlayerbotFactory::NameRaceAndGender RandomPlayerbotFactory::CombineRaceAndGender(uint8 gender, - uint8 race) +constexpr RandomPlayerbotFactory::NameRaceAndGender RandomPlayerbotFactory::CombineRaceAndGender(uint8 race, + uint8 gender) { + NameRaceAndGender baseIndex; switch (race) { + case RACE_ORC: baseIndex = NameRaceAndGender::OrcMale; break; + case RACE_DWARF: baseIndex = NameRaceAndGender::DwarfMale; break; + case RACE_NIGHTELF: baseIndex = NameRaceAndGender::NightelfMale; break; + case RACE_TAUREN: baseIndex = NameRaceAndGender::TaurenMale; break; + case RACE_GNOME: baseIndex = NameRaceAndGender::GnomeMale; break; + case RACE_TROLL: baseIndex = NameRaceAndGender::TrollMale; break; + case RACE_BLOODELF: baseIndex = NameRaceAndGender::BloodelfMale; break; + case RACE_DRAENEI: baseIndex = NameRaceAndGender::DraeneiMale; break; case RACE_HUMAN: - return static_cast(static_cast(NameRaceAndGender::GenericMale) + gender); - case RACE_ORC: - return static_cast(static_cast(NameRaceAndGender::OrcMale) + gender); - case RACE_DWARF: - return static_cast(static_cast(NameRaceAndGender::DwarfMale) + gender); - case RACE_NIGHTELF: - return static_cast(static_cast(NameRaceAndGender::NightelfMale) + gender); case RACE_UNDEAD_PLAYER: - return static_cast(static_cast(NameRaceAndGender::GenericMale) + gender); - case RACE_TAUREN: - return static_cast(static_cast(NameRaceAndGender::TaurenMale) + gender); - case RACE_GNOME: - return static_cast(static_cast(NameRaceAndGender::GnomeMale) + gender); - case RACE_TROLL: - return static_cast(static_cast(NameRaceAndGender::TrollMale) + gender); - case RACE_DRAENEI: - return static_cast(static_cast(NameRaceAndGender::DraeneiMale) + gender); - case RACE_BLOODELF: - return static_cast(static_cast(NameRaceAndGender::BloodelfMale) + gender); default: - LOG_ERROR("playerbots", "The race with ID %d does not have a naming category", race); - return static_cast(static_cast(NameRaceAndGender::GenericMale) + gender); + baseIndex = NameRaceAndGender::GenericMale; + break; } + + return static_cast(static_cast(baseIndex) + ((gender >= GENDER_NONE) ? GENDER_MALE : gender)); } -RandomPlayerbotFactory::RandomPlayerbotFactory(uint32 accountId) : accountId(accountId) +bool RandomPlayerbotFactory::IsValidRaceClassCombination(uint8 race, uint8 cls, uint32 expansion) { - uint32 const expansion = sWorld->getIntConfig(CONFIG_EXPANSION); - - availableRaces[CLASS_WARRIOR].push_back(RACE_HUMAN); - availableRaces[CLASS_WARRIOR].push_back(RACE_NIGHTELF); - availableRaces[CLASS_WARRIOR].push_back(RACE_GNOME); - availableRaces[CLASS_WARRIOR].push_back(RACE_DWARF); - availableRaces[CLASS_WARRIOR].push_back(RACE_ORC); - availableRaces[CLASS_WARRIOR].push_back(RACE_UNDEAD_PLAYER); - availableRaces[CLASS_WARRIOR].push_back(RACE_TAUREN); - availableRaces[CLASS_WARRIOR].push_back(RACE_TROLL); - if (expansion >= EXPANSION_THE_BURNING_CRUSADE) - { - availableRaces[CLASS_WARRIOR].push_back(RACE_DRAENEI); - } - - availableRaces[CLASS_PALADIN].push_back(RACE_HUMAN); - availableRaces[CLASS_PALADIN].push_back(RACE_DWARF); - if (expansion >= EXPANSION_THE_BURNING_CRUSADE) - { - availableRaces[CLASS_PALADIN].push_back(RACE_DRAENEI); - availableRaces[CLASS_PALADIN].push_back(RACE_BLOODELF); - } - - availableRaces[CLASS_ROGUE].push_back(RACE_HUMAN); - availableRaces[CLASS_ROGUE].push_back(RACE_DWARF); - availableRaces[CLASS_ROGUE].push_back(RACE_NIGHTELF); - availableRaces[CLASS_ROGUE].push_back(RACE_GNOME); - availableRaces[CLASS_ROGUE].push_back(RACE_ORC); - availableRaces[CLASS_ROGUE].push_back(RACE_UNDEAD_PLAYER); - availableRaces[CLASS_ROGUE].push_back(RACE_TROLL); - if (expansion >= EXPANSION_THE_BURNING_CRUSADE) - { - availableRaces[CLASS_ROGUE].push_back(RACE_BLOODELF); - } - - availableRaces[CLASS_PRIEST].push_back(RACE_HUMAN); - availableRaces[CLASS_PRIEST].push_back(RACE_DWARF); - availableRaces[CLASS_PRIEST].push_back(RACE_NIGHTELF); - availableRaces[CLASS_PRIEST].push_back(RACE_TROLL); - availableRaces[CLASS_PRIEST].push_back(RACE_UNDEAD_PLAYER); - if (expansion >= EXPANSION_THE_BURNING_CRUSADE) - { - availableRaces[CLASS_PRIEST].push_back(RACE_DRAENEI); - availableRaces[CLASS_PRIEST].push_back(RACE_BLOODELF); - } - - availableRaces[CLASS_MAGE].push_back(RACE_HUMAN); - availableRaces[CLASS_MAGE].push_back(RACE_GNOME); - availableRaces[CLASS_MAGE].push_back(RACE_UNDEAD_PLAYER); - availableRaces[CLASS_MAGE].push_back(RACE_TROLL); - if (expansion >= EXPANSION_THE_BURNING_CRUSADE) - { - availableRaces[CLASS_MAGE].push_back(RACE_DRAENEI); - availableRaces[CLASS_MAGE].push_back(RACE_BLOODELF); - } - - availableRaces[CLASS_WARLOCK].push_back(RACE_HUMAN); - availableRaces[CLASS_WARLOCK].push_back(RACE_GNOME); - availableRaces[CLASS_WARLOCK].push_back(RACE_UNDEAD_PLAYER); - availableRaces[CLASS_WARLOCK].push_back(RACE_ORC); - if (expansion >= EXPANSION_THE_BURNING_CRUSADE) - { - availableRaces[CLASS_WARLOCK].push_back(RACE_BLOODELF); - } - - availableRaces[CLASS_SHAMAN].push_back(RACE_ORC); - availableRaces[CLASS_SHAMAN].push_back(RACE_TAUREN); - availableRaces[CLASS_SHAMAN].push_back(RACE_TROLL); - if (expansion >= EXPANSION_THE_BURNING_CRUSADE) - { - availableRaces[CLASS_SHAMAN].push_back(RACE_DRAENEI); - } - - availableRaces[CLASS_HUNTER].push_back(RACE_DWARF); - availableRaces[CLASS_HUNTER].push_back(RACE_NIGHTELF); - availableRaces[CLASS_HUNTER].push_back(RACE_ORC); - availableRaces[CLASS_HUNTER].push_back(RACE_TAUREN); - availableRaces[CLASS_HUNTER].push_back(RACE_TROLL); - if (expansion >= EXPANSION_THE_BURNING_CRUSADE) - { - availableRaces[CLASS_HUNTER].push_back(RACE_DRAENEI); - availableRaces[CLASS_HUNTER].push_back(RACE_BLOODELF); - } + // skip expansion races if not playing with expansion + if (expansion < EXPANSION_THE_BURNING_CRUSADE && (race == RACE_BLOODELF || race == RACE_DRAENEI)) + return false; - availableRaces[CLASS_DRUID].push_back(RACE_NIGHTELF); - availableRaces[CLASS_DRUID].push_back(RACE_TAUREN); + // skip expansion classes if not playing with expansion + if (expansion < EXPANSION_WRATH_OF_THE_LICH_KING && cls == CLASS_DEATH_KNIGHT) + return false; - if (expansion == EXPANSION_WRATH_OF_THE_LICH_KING) - { - availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_NIGHTELF); - availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_TAUREN); - availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_HUMAN); - availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_ORC); - availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_UNDEAD_PLAYER); - availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_TROLL); - availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_BLOODELF); - availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_DRAENEI); - availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_GNOME); - availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_DWARF); - } + PlayerInfo const* info = sObjectMgr->GetPlayerInfo(race, cls); + return info != nullptr; } Player* RandomPlayerbotFactory::CreateRandomBot(WorldSession* session, uint8 cls, std::unordered_map>& nameCache) { - LOG_DEBUG("playerbots", "Creating new random bot for class {}", cls); + LOG_DEBUG("playerbots", "Creating a new random bot for class: {}", cls); + + const bool alliance = static_cast(urand(0, 1)); - uint8 gender = rand() % 2 ? GENDER_MALE : GENDER_FEMALE; - bool alliance = rand() % 2 ? true : false; std::vector raceOptions; - for (const auto& race : availableRaces[cls]) + for (uint8 race = RACE_HUMAN; race < MAX_RACES; ++race) { + // skip disabled with config races + if ((1 << (race - 1)) & sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_DISABLED_RACEMASK)) + continue; + + // Try to get 50/50 faction distribution for random bot population balance. + // Without this check, races from the faction with more class options would dominate. if (alliance == IsAlliance(race)) { - raceOptions.push_back(race); + if (IsValidRaceClassCombination(race, cls, sWorld->getIntConfig(CONFIG_EXPANSION))) + raceOptions.push_back(race); } } if (raceOptions.empty()) { - LOG_ERROR("playerbots", "No races available for class: {}", cls); + LOG_ERROR("playerbots", "No races are available for class: {}", cls); return nullptr; } - uint8 race = raceOptions[urand(0, raceOptions.size() - 1)]; - - const auto raceAndGender = CombineRaceAndGender(gender, race); + const uint8 race = raceOptions[urand(0, raceOptions.size() - 1)]; + const uint8 gender = urand(0, 1) ? GENDER_MALE : GENDER_FEMALE; + const auto raceAndGender = CombineRaceAndGender(race, gender); std::string name; - if (nameCache.empty()) - { - name = CreateRandomBotName(raceAndGender); - } - else + if (!nameCache.empty()) { if (nameCache[raceAndGender].empty()) { - LOG_ERROR("playerbots", "No name found for race and gender: {}", raceAndGender); + LOG_ERROR("playerbots", "No names found for the specified race: {} and gender: {}", + race, gender); return nullptr; } + uint32 i = urand(0, nameCache[raceAndGender].size() - 1); name = nameCache[raceAndGender][i]; swap(nameCache[raceAndGender][i], nameCache[raceAndGender].back()); nameCache[raceAndGender].pop_back(); } + else + { + name = CreateRandomBotName(raceAndGender); + } + if (name.empty()) { - LOG_ERROR("playerbots", "Unable to get random bot name!"); + LOG_ERROR("playerbots", "Failed to get a valid random bot name"); return nullptr; } @@ -246,19 +157,20 @@ Player* RandomPlayerbotFactory::CreateRandomBot(WorldSession* session, uint8 cls player->CleanupsBeforeDelete(); delete player; - LOG_ERROR("playerbots", "Unable to create random bot for account {} - name: \"{}\"; race: {}; class: {}", - accountId, name.c_str(), race, cls); + LOG_ERROR("playerbots", "Unable to create random bot - name: \"{}\", race: {}, class: {}", + name.c_str(), race, cls); return nullptr; } player->setCinematic(2); player->SetAtLoginFlag(AT_LOGIN_NONE); - if (player->getClass() == CLASS_DEATH_KNIGHT) + if (cls == CLASS_DEATH_KNIGHT) { player->learnSpell(50977, false); } - LOG_DEBUG("playerbots", "Random bot created for account {} - name: \"{}\"; race: {}; class: {}", accountId, + + LOG_DEBUG("playerbots", "Random bot created - name: \"{}\", race: {}, class: {}", name.c_str(), race, cls); return player; @@ -786,7 +698,7 @@ void RandomPlayerbotFactory::CreateRandomBots() } LOG_DEBUG("playerbots", "Creating random bot characters for account: [{}/{}]", accountNumber + 1, totalAccountCount); - RandomPlayerbotFactory factory(accountId); + RandomPlayerbotFactory factory; WorldSession* session = new WorldSession(accountId, "", 0x0, nullptr, SEC_PLAYER, EXPANSION_WRATH_OF_THE_LICH_KING, time_t(0), LOCALE_enUS, 0, false, false, 0, true); @@ -798,29 +710,24 @@ void RandomPlayerbotFactory::CreateRandomBots() if (!((1 << (cls - 1)) & CLASSMASK_ALL_PLAYABLE) || !sChrClassesStore.LookupEntry(cls)) continue; - if (bool const isClassDeathKnight = cls == CLASS_DEATH_KNIGHT; - isClassDeathKnight && sWorld->getIntConfig(CONFIG_EXPANSION) != EXPANSION_WRATH_OF_THE_LICH_KING) - { + // skip disabled with config classes + if ((1 << (cls - 1)) & sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_DISABLED_CLASSMASK)) continue; - } - if (cls != 10) + Player* playerBot = factory.CreateRandomBot(session, cls, nameCache); + if (!playerBot) { - if (Player* playerBot = factory.CreateRandomBot(session, cls, nameCache)) - { - playerBot->SaveToDB(true, false); - sCharacterCache->AddCharacterCacheEntry(playerBot->GetGUID(), accountId, playerBot->GetName(), - playerBot->getGender(), playerBot->getRace(), - playerBot->getClass(), playerBot->GetLevel()); - playerBot->CleanupsBeforeDelete(); - delete playerBot; - bot_creation++; - } - else - { - LOG_ERROR("playerbots", "Fail to create character for account {}", accountId); - } + LOG_ERROR("playerbots", "Fail to create character for account {}", accountId); + continue; } + + playerBot->SaveToDB(true, false); + sCharacterCache->AddCharacterCacheEntry(playerBot->GetGUID(), accountId, playerBot->GetName(), + playerBot->getGender(), playerBot->getRace(), + playerBot->getClass(), playerBot->GetLevel()); + playerBot->CleanupsBeforeDelete(); + delete playerBot; + bot_creation++; } } diff --git a/src/RandomPlayerbotFactory.h b/src/RandomPlayerbotFactory.h index e74926a20e..d6c19e454b 100644 --- a/src/RandomPlayerbotFactory.h +++ b/src/RandomPlayerbotFactory.h @@ -44,9 +44,9 @@ class RandomPlayerbotFactory BloodelfFemale }; - static constexpr NameRaceAndGender CombineRaceAndGender(uint8 gender, uint8 race); + static constexpr NameRaceAndGender CombineRaceAndGender(uint8 race, uint8 gender); - RandomPlayerbotFactory(uint32 accountId); + RandomPlayerbotFactory() {}; virtual ~RandomPlayerbotFactory() {} Player* CreateRandomBot(WorldSession* session, uint8 cls, std::unordered_map>& names); @@ -58,11 +58,9 @@ class RandomPlayerbotFactory static uint32 CalculateAvailableCharsPerAccount(); private: + static bool IsValidRaceClassCombination(uint8 race, uint8 class_, uint32 expansion); std::string const CreateRandomBotName(NameRaceAndGender raceAndGender); static std::string const CreateRandomArenaTeamName(); - - uint32 accountId; - static std::map> availableRaces; }; #endif From 80dbd22ba1f4122a1fbfda18d47ef7c6a5a6b3a0 Mon Sep 17 00:00:00 2001 From: avirar Date: Wed, 5 Nov 2025 09:27:50 +1100 Subject: [PATCH 16/47] Fixes equip bug with random suffix rings (#1757) * Check item score of rings/trinkets to determine the correct slot to equip * Early return, removed unecessary if statements, single line statements Simplify logic for equipping items by reducing nested conditions. --- src/strategy/actions/EquipAction.cpp | 31 ++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/src/strategy/actions/EquipAction.cpp b/src/strategy/actions/EquipAction.cpp index 946af5b2f6..f24d89f7bd 100644 --- a/src/strategy/actions/EquipAction.cpp +++ b/src/strategy/actions/EquipAction.cpp @@ -271,19 +271,38 @@ void EquipAction::EquipItem(Item* item) { if (equippedItems[1]) { - // Both slots are full - pick the worst item to replace + // Both slots are full - pick the worst item to replace, but only if new item is better StatsWeightCalculator calc(bot); calc.SetItemSetBonus(false); calc.SetOverflowPenalty(false); - float firstItemScore = calc.CalculateItem(equippedItems[0]->GetTemplate()->ItemId); - float secondItemScore = calc.CalculateItem(equippedItems[1]->GetTemplate()->ItemId); + // Calculate new item score with random properties + int32 newItemRandomProp = item->GetItemRandomPropertyId(); + float newItemScore = calc.CalculateItem(itemId, newItemRandomProp); - // If the second slot is worse, place the new item there - if (firstItemScore > secondItemScore) + // Calculate equipped items scores with random properties + int32 firstRandomProp = equippedItems[0]->GetItemRandomPropertyId(); + int32 secondRandomProp = equippedItems[1]->GetItemRandomPropertyId(); + float firstItemScore = calc.CalculateItem(equippedItems[0]->GetTemplate()->ItemId, firstRandomProp); + float secondItemScore = calc.CalculateItem(equippedItems[1]->GetTemplate()->ItemId, secondRandomProp); + + // Determine which slot (if any) should be replaced + bool betterThanFirst = newItemScore > firstItemScore; + bool betterThanSecond = newItemScore > secondItemScore; + + // Early return if new item is not better than either equipped item + if (!betterThanFirst && !betterThanSecond) + return; + + if (betterThanFirst && betterThanSecond) { - dstSlot++; + // New item is better than both - replace the worse of the two equipped items + if (firstItemScore > secondItemScore) + dstSlot++; // Replace second slot (worse) + // else: keep dstSlot as-is (replace first slot) } + else if (betterThanSecond) + dstSlot++; // Only better than second slot - replace it } else { From d02d61e6903e0f62dc41216d62448a73fe6c8f2a Mon Sep 17 00:00:00 2001 From: Crow Date: Wed, 5 Nov 2025 07:53:16 -0600 Subject: [PATCH 17/47] Implement Magtheridon strategy (#1721) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I'm marking this as a draft for now because I haven't done a detailed review of the code, but I'm posting it now in case anybody wants to give it a try. Here's what the strategy (should) do. ### **Channeler Phase** While you can probably AoE down all five Hellfire Channelers, that's more dicey with IP nerfs and it's no fun, so the strategy takes what would have still been considered an aggressive approach back in the day by (1) assigning the Main Tank to the first Channeler, (2) having Hunters misdirect two more Channelers to the MT, and (3) one Off Tank picking up each of the fourth and fifth Channelers and dragging them out of Shadow Volley Range from the main group. Sometimes the pull gets a little wonky and one of the OTs might end up with one of the Channelers that was intended for the MT, but it should work out in the end. DPS will move through Channelers from Square -> Star -> Circle -> Diamond -> Triangle. Once Square, Star, and Circle are down, the MT will go sit by Magtheridon and wait for him to become active instead of helping with the last two Channelers. I could have made the MT help with the fourth Channeler too, but it's not needed, and positioning to pick up Magtheridon after the third Channeler is a failsafe for low DPS groups that aren't able to get four down before Magtheridon breaks free. The top priority for Warlocks is to banish/fear the Burning Abyssals, and they will continue to do so even after Magtheridon becomes active (you are not supposed to kill the Abyssals; they have a gazillion HP and automatically despawn after a minute). Their next priority is to put Curse of Tongues on the Channelers. ### **Magtheridon Positioning** The MT will pick up Magtheridon and pull him (moving backwards because Magtheridon kind of hits like a bus) to a position up against the far wall. Ranged DPS will spread out from a point roughly in the center of the room, and Healers will spread out from a point that is a little closer to Magtheridon. I have not built in aoe avoidance (except for cube clickers, see below) because the general avoid aoe strategy seems to work fine for this fight. ### **Clicking Manticron Cubes** Now, the fun part. Bots will be assigned to clicking cubes by standard group selection order (reverse join order), but assignment is done via two passes. The first pass will look to select five ranged DPS bots that are _not_ Warlocks. This is because Warlocks may still be needed to keep Abyssals banished/feared and because Warlocks of all three specs put out by far the most damage of all ranged DPS at level 70 in pre-raid/T4 gear. If there are not five non-Warlock ranged DPS bots available, then the logic goes to the second pass, which can pick any bot that is not a Tank. Cube clicking works on a timer: 1. 48 seconds after Magtheridon breaks free, assigned cube clickers move near their cubes (but a few yards outside of interact distance). During this time, they should move around still to avoid Debris (by maintaining distance from the triggering NPC) and Conflagration (by maintaining distance from the triggering gameobject). Blast Nova is on a 54.35s to 55.4s timer, and I found 48s to always be ample time to get to the cubes, but YMMV so this is a good thing to test. Going to a cube too early not only takes away DPS but also risks more hazards appearing on/around the cube that will then cause problems when the cube needs to be clicked. 2. Blast Nova is a 2s cast, followed by a 10s channel (if not interrupted by the cubes). As soon as the cast begins, bots will move into interaction range and click the cube. Well, there is a randomized delay between 200ms (about the fastest possible human reaction time to visual stimuli) and 1500ms. It didn’t happen to me in a few runs, but it may be possible that the delay causes the raid to eat one tick of Blast Nova (I’m not sure if the first blast comes as soon as the channel starts). Again, another good thing to test, but also one tick is not going to kill anybody, and it’s arguably good to introduce some degree of imperfection. 3. Once Blast Nova stops channeling (i.e., all five cubes have been clicked and channeled simultaneously), bots will interrupt their cube clicking and go back to regularly scheduled activities. Again, I’ve introduced a randomized delay, this time between 200ms and 3000ms. Note that bots can easily be perfect at this—if I don’t do the randomized delay, they click and let go so fast that you can barely even see the beams appear. It’s so atrocious for immersion that I consider the lack of any randomization to be totally unacceptable for this strategy. 4. 48s after Blast Nova, bots will go back to their near-cube positions, rinse and repeat. If an assigned cube clicker dies, another bot should be immediately assigned. All bots in the raid track the same timer so the new bot should step into the prior bot’s shoes. Of course, if Blast Nova is about to go off and a clicker dies next to a cube, you’re probably wiping because I didn’t assign backups to stand in place. That’s too much of a dad guild-level strategy even for me. And that’s about it. Figuring out the cubes was a tremendous pain in the ass, but I’ve really enjoyed the learning process. --------- Co-authored-by: bash <31279994+hermensbas@users.noreply.github.com> --- src/PlayerbotAI.cpp | 2 + src/strategy/AiObjectContext.cpp | 4 + src/strategy/raids/RaidStrategyContext.h | 3 + .../RaidMagtheridonActionContext.h | 37 + .../magtheridon/RaidMagtheridonActions.cpp | 703 ++++++++++++++++++ .../magtheridon/RaidMagtheridonActions.h | 100 +++ .../magtheridon/RaidMagtheridonHelpers.cpp | 226 ++++++ .../magtheridon/RaidMagtheridonHelpers.h | 90 +++ .../RaidMagtheridonMultipliers.cpp | 71 ++ .../magtheridon/RaidMagtheridonMultipliers.h | 27 + .../magtheridon/RaidMagtheridonStrategy.cpp | 42 ++ .../magtheridon/RaidMagtheridonStrategy.h | 18 + .../RaidMagtheridonTriggerContext.h | 37 + .../magtheridon/RaidMagtheridonTriggers.cpp | 128 ++++ .../magtheridon/RaidMagtheridonTriggers.h | 77 ++ 15 files changed, 1565 insertions(+) create mode 100644 src/strategy/raids/magtheridon/RaidMagtheridonActionContext.h create mode 100644 src/strategy/raids/magtheridon/RaidMagtheridonActions.cpp create mode 100644 src/strategy/raids/magtheridon/RaidMagtheridonActions.h create mode 100644 src/strategy/raids/magtheridon/RaidMagtheridonHelpers.cpp create mode 100644 src/strategy/raids/magtheridon/RaidMagtheridonHelpers.h create mode 100644 src/strategy/raids/magtheridon/RaidMagtheridonMultipliers.cpp create mode 100644 src/strategy/raids/magtheridon/RaidMagtheridonMultipliers.h create mode 100644 src/strategy/raids/magtheridon/RaidMagtheridonStrategy.cpp create mode 100644 src/strategy/raids/magtheridon/RaidMagtheridonStrategy.h create mode 100644 src/strategy/raids/magtheridon/RaidMagtheridonTriggerContext.h create mode 100644 src/strategy/raids/magtheridon/RaidMagtheridonTriggers.cpp create mode 100644 src/strategy/raids/magtheridon/RaidMagtheridonTriggers.h diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index bf6b0a343a..48a259e1b3 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -1534,6 +1534,8 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster) case 533: strategyName = "naxx"; // Naxxramas break; + case 544: + strategyName = "magtheridon"; // Magtheridon's Lair case 565: strategyName = "gruulslair"; // Gruul's Lair break; diff --git a/src/strategy/AiObjectContext.cpp b/src/strategy/AiObjectContext.cpp index 4c49fe4eac..7a1da0f8a6 100644 --- a/src/strategy/AiObjectContext.cpp +++ b/src/strategy/AiObjectContext.cpp @@ -39,6 +39,8 @@ #include "raids/blackwinglair/RaidBwlTriggerContext.h" #include "raids/karazhan/RaidKarazhanActionContext.h" #include "raids/karazhan/RaidKarazhanTriggerContext.h" +#include "raids/magtheridon/RaidMagtheridonActionContext.h" +#include "raids/magtheridon/RaidMagtheridonTriggerContext.h" #include "raids/gruulslair/RaidGruulsLairActionContext.h" #include "raids/gruulslair/RaidGruulsLairTriggerContext.h" #include "raids/naxxramas/RaidNaxxActionContext.h" @@ -113,6 +115,7 @@ void AiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList creators["mc"] = &RaidStrategyContext::mc; creators["bwl"] = &RaidStrategyContext::bwl; creators["karazhan"] = &RaidStrategyContext::karazhan; + creators["magtheridon"] = &RaidStrategyContext::magtheridon; creators["gruulslair"] = &RaidStrategyContext::gruulslair; creators["naxx"] = &RaidStrategyContext::naxx; creators["wotlk-os"] = &RaidStrategyContext::wotlk_os; @@ -39,6 +41,7 @@ class RaidStrategyContext : public NamedObjectContext static Strategy* mc(PlayerbotAI* botAI) { return new RaidMcStrategy(botAI); } static Strategy* bwl(PlayerbotAI* botAI) { return new RaidBwlStrategy(botAI); } static Strategy* karazhan(PlayerbotAI* botAI) { return new RaidKarazhanStrategy(botAI); } + static Strategy* magtheridon(PlayerbotAI* botAI) { return new RaidMagtheridonStrategy(botAI); } static Strategy* gruulslair(PlayerbotAI* botAI) { return new RaidGruulsLairStrategy(botAI); } static Strategy* naxx(PlayerbotAI* botAI) { return new RaidNaxxStrategy(botAI); } static Strategy* wotlk_os(PlayerbotAI* botAI) { return new RaidOsStrategy(botAI); } diff --git a/src/strategy/raids/magtheridon/RaidMagtheridonActionContext.h b/src/strategy/raids/magtheridon/RaidMagtheridonActionContext.h new file mode 100644 index 0000000000..48e8cfd351 --- /dev/null +++ b/src/strategy/raids/magtheridon/RaidMagtheridonActionContext.h @@ -0,0 +1,37 @@ +#ifndef _PLAYERBOT_RAIDMAGTHERIDONACTIONCONTEXT_H +#define _PLAYERBOT_RAIDMAGTHERIDONACTIONCONTEXT_H + +#include "RaidMagtheridonActions.h" +#include "NamedObjectContext.h" + +class RaidMagtheridonActionContext : public NamedObjectContext +{ +public: + RaidMagtheridonActionContext() + { + creators["magtheridon main tank attack first three channelers"] = &RaidMagtheridonActionContext::magtheridon_main_tank_attack_first_three_channelers; + creators["magtheridon first assist tank attack nw channeler"] = &RaidMagtheridonActionContext::magtheridon_first_assist_tank_attack_nw_channeler; + creators["magtheridon second assist tank attack ne channeler"] = &RaidMagtheridonActionContext::magtheridon_second_assist_tank_attack_ne_channeler; + creators["magtheridon misdirect hellfire channelers"] = &RaidMagtheridonActionContext::magtheridon_misdirect_hellfire_channelers; + creators["magtheridon assign dps priority"] = &RaidMagtheridonActionContext::magtheridon_assign_dps_priority; + creators["magtheridon warlock cc burning abyssal"] = &RaidMagtheridonActionContext::magtheridon_warlock_cc_burning_abyssal; + creators["magtheridon main tank position boss"] = &RaidMagtheridonActionContext::magtheridon_main_tank_position_boss; + creators["magtheridon spread ranged"] = &RaidMagtheridonActionContext::magtheridon_spread_ranged; + creators["magtheridon use manticron cube"] = &RaidMagtheridonActionContext::magtheridon_use_manticron_cube; + creators["magtheridon manage timers and assignments"] = &RaidMagtheridonActionContext::magtheridon_manage_timers_and_assignments; + } + +private: + static Action* magtheridon_main_tank_attack_first_three_channelers(PlayerbotAI* botAI) { return new MagtheridonMainTankAttackFirstThreeChannelersAction(botAI); } + static Action* magtheridon_first_assist_tank_attack_nw_channeler(PlayerbotAI* botAI) { return new MagtheridonFirstAssistTankAttackNWChannelerAction(botAI); } + static Action* magtheridon_second_assist_tank_attack_ne_channeler(PlayerbotAI* botAI) { return new MagtheridonSecondAssistTankAttackNEChannelerAction(botAI); } + static Action* magtheridon_misdirect_hellfire_channelers(PlayerbotAI* botAI) { return new MagtheridonMisdirectHellfireChannelers(botAI); } + static Action* magtheridon_assign_dps_priority(PlayerbotAI* botAI) { return new MagtheridonAssignDPSPriorityAction(botAI); } + static Action* magtheridon_warlock_cc_burning_abyssal(PlayerbotAI* botAI) { return new MagtheridonWarlockCCBurningAbyssalAction(botAI); } + static Action* magtheridon_main_tank_position_boss(PlayerbotAI* botAI) { return new MagtheridonMainTankPositionBossAction(botAI); } + static Action* magtheridon_spread_ranged(PlayerbotAI* botAI) { return new MagtheridonSpreadRangedAction(botAI); } + static Action* magtheridon_use_manticron_cube(PlayerbotAI* botAI) { return new MagtheridonUseManticronCubeAction(botAI); } + static Action* magtheridon_manage_timers_and_assignments(PlayerbotAI* botAI) { return new MagtheridonManageTimersAndAssignmentsAction(botAI); } +}; + +#endif diff --git a/src/strategy/raids/magtheridon/RaidMagtheridonActions.cpp b/src/strategy/raids/magtheridon/RaidMagtheridonActions.cpp new file mode 100644 index 0000000000..8efc0e0656 --- /dev/null +++ b/src/strategy/raids/magtheridon/RaidMagtheridonActions.cpp @@ -0,0 +1,703 @@ +#include "RaidMagtheridonActions.h" +#include "RaidMagtheridonHelpers.h" +#include "Creature.h" +#include "ObjectAccessor.h" +#include "ObjectGuid.h" +#include "Playerbots.h" + +using namespace MagtheridonHelpers; + +bool MagtheridonMainTankAttackFirstThreeChannelersAction::Execute(Event event) +{ + Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); + if (!magtheridon) + return false; + + Creature* channelerSquare = GetChanneler(bot, SOUTH_CHANNELER); + if (channelerSquare && channelerSquare->IsAlive()) + MarkTargetWithSquare(bot, channelerSquare); + + Creature* channelerStar = GetChanneler(bot, WEST_CHANNELER); + if (channelerStar && channelerStar->IsAlive()) + MarkTargetWithStar(bot, channelerStar); + + Creature* channelerCircle = GetChanneler(bot, EAST_CHANNELER); + if (channelerCircle && channelerCircle->IsAlive()) + MarkTargetWithCircle(bot, channelerCircle); + + // After first three channelers are dead, wait for Magtheridon to activate + if ((!channelerSquare || !channelerSquare->IsAlive()) && + (!channelerStar || !channelerStar->IsAlive()) && + (!channelerCircle || !channelerCircle->IsAlive())) + { + const Location& position = MagtheridonsLairLocations::WaitingForMagtheridonPosition; + if (!bot->IsWithinDist2d(position.x, position.y, 2.0f)) + { + return MoveTo(bot->GetMapId(), position.x, position.y, position.z, false, false, false, false, + MovementPriority::MOVEMENT_COMBAT, true, false); + } + + bot->SetFacingTo(position.orientation); + return true; + } + + Creature* currentTarget = nullptr; + std::string rtiName; + if (channelerSquare && channelerSquare->IsAlive()) + { + currentTarget = channelerSquare; + rtiName = "square"; + } + else if (channelerStar && channelerStar->IsAlive()) + { + currentTarget = channelerStar; + rtiName = "star"; + } + else if (channelerCircle && channelerCircle->IsAlive()) + { + currentTarget = channelerCircle; + rtiName = "circle"; + } + + SetRtiTarget(botAI, rtiName, currentTarget); + + if (currentTarget && bot->GetVictim() != currentTarget) + return Attack(currentTarget); + + return false; +} + +bool MagtheridonFirstAssistTankAttackNWChannelerAction::Execute(Event event) +{ + Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER); + if (!channelerDiamond || !channelerDiamond->IsAlive()) + return false; + + MarkTargetWithDiamond(bot, channelerDiamond); + SetRtiTarget(botAI, "diamond", channelerDiamond); + + if (bot->GetVictim() != channelerDiamond) + return Attack(channelerDiamond); + + if (channelerDiamond->GetVictim() == bot) + { + const Location& position = MagtheridonsLairLocations::NWChannelerTankPosition; + const float maxDistance = 3.0f; + + if (bot->GetExactDist2d(position.x, position.y) > maxDistance) + { + float dX = position.x - bot->GetPositionX(); + float dY = position.y - bot->GetPositionY(); + float dist = sqrt(dX * dX + dY * dY); + float moveX = bot->GetPositionX() + (dX / dist) * maxDistance; + float moveY = bot->GetPositionY() + (dY / dist) * maxDistance; + + return MoveTo(bot->GetMapId(), moveX, moveY, position.z, false, false, false, false, + MovementPriority::MOVEMENT_COMBAT, true, false); + } + } + + return false; +} + +bool MagtheridonSecondAssistTankAttackNEChannelerAction::Execute(Event event) +{ + Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER); + if (!channelerTriangle || !channelerTriangle->IsAlive()) + return false; + + MarkTargetWithTriangle(bot, channelerTriangle); + SetRtiTarget(botAI, "triangle", channelerTriangle); + + if (bot->GetVictim() != channelerTriangle) + return Attack(channelerTriangle); + + if (channelerTriangle->GetVictim() == bot) + { + const Location& position = MagtheridonsLairLocations::NEChannelerTankPosition; + const float maxDistance = 3.0f; + + if (bot->GetExactDist2d(position.x, position.y) > maxDistance) + { + float dX = position.x - bot->GetPositionX(); + float dY = position.y - bot->GetPositionY(); + float dist = sqrt(dX * dX + dY * dY); + float moveX = bot->GetPositionX() + (dX / dist) * maxDistance; + float moveY = bot->GetPositionY() + (dY / dist) * maxDistance; + + return MoveTo(bot->GetMapId(), moveX, moveY, position.z, false, false, false, false, + MovementPriority::MOVEMENT_COMBAT, true, false); + } + } + + return false; +} + +// Misdirect West & East Channelers to Main Tank +bool MagtheridonMisdirectHellfireChannelers::Execute(Event event) +{ + Group* group = bot->GetGroup(); + if (!group) + return false; + + std::vector hunters; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && member->IsAlive() && member->getClass() == CLASS_HUNTER && GET_PLAYERBOT_AI(member)) + hunters.push_back(member); + } + + int hunterIndex = -1; + for (size_t i = 0; i < hunters.size(); ++i) + { + if (hunters[i] == bot) + { + hunterIndex = static_cast(i); + break; + } + } + + Player* mainTank = nullptr; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && member->IsAlive() && botAI->IsMainTank(member)) + { + mainTank = member; + break; + } + } + + Creature* channelerStar = GetChanneler(bot, WEST_CHANNELER); + Creature* channelerCircle = GetChanneler(bot, EAST_CHANNELER); + + switch (hunterIndex) + { + case 0: + if (mainTank && channelerStar && channelerStar->IsAlive() && + channelerStar->GetVictim() != mainTank) + { + if (botAI->CanCastSpell("misdirection", mainTank)) + return botAI->CastSpell("misdirection", mainTank); + + if (!bot->HasAura(SPELL_MISDIRECTION)) + return false; + + if (botAI->CanCastSpell("steady shot", channelerStar)) + return botAI->CastSpell("steady shot", channelerStar); + } + break; + + case 1: + if (mainTank && channelerCircle && channelerCircle->IsAlive() && + channelerCircle->GetVictim() != mainTank) + { + if (botAI->CanCastSpell("misdirection", mainTank)) + return botAI->CastSpell("misdirection", mainTank); + + if (!bot->HasAura(SPELL_MISDIRECTION)) + return false; + + if (botAI->CanCastSpell("steady shot", channelerCircle)) + return botAI->CastSpell("steady shot", channelerCircle); + } + break; + + default: + break; + } + + return false; +} + +bool MagtheridonAssignDPSPriorityAction::Execute(Event event) +{ + // Listed in order of priority + Creature* channelerSquare = GetChanneler(bot, SOUTH_CHANNELER); + if (channelerSquare && channelerSquare->IsAlive()) + { + SetRtiTarget(botAI, "square", channelerSquare); + + if (bot->GetTarget() != channelerSquare->GetGUID()) + { + bot->SetSelection(channelerSquare->GetGUID()); + return Attack(channelerSquare); + } + + return false; + } + + Creature* channelerStar = GetChanneler(bot, WEST_CHANNELER); + if (channelerStar && channelerStar->IsAlive()) + { + SetRtiTarget(botAI, "star", channelerStar); + + if (bot->GetTarget() != channelerStar->GetGUID()) + { + bot->SetSelection(channelerStar->GetGUID()); + return Attack(channelerStar); + } + + return false; + } + + Creature* channelerCircle = GetChanneler(bot, EAST_CHANNELER); + if (channelerCircle && channelerCircle->IsAlive()) + { + SetRtiTarget(botAI, "circle", channelerCircle); + + if (bot->GetTarget() != channelerCircle->GetGUID()) + { + bot->SetSelection(channelerCircle->GetGUID()); + return Attack(channelerCircle); + } + + return false; + } + + Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER); + if (channelerDiamond && channelerDiamond->IsAlive()) + { + SetRtiTarget(botAI, "diamond", channelerDiamond); + + if (bot->GetTarget() != channelerDiamond->GetGUID()) + { + bot->SetSelection(channelerDiamond->GetGUID()); + return Attack(channelerDiamond); + } + + return false; + } + + Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER); + if (channelerTriangle && channelerTriangle->IsAlive()) + { + SetRtiTarget(botAI, "triangle", channelerTriangle); + + if (bot->GetTarget() != channelerTriangle->GetGUID()) + { + bot->SetSelection(channelerTriangle->GetGUID()); + return Attack(channelerTriangle); + } + + return false; + } + + Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); + if (magtheridon && !magtheridon->HasAura(SPELL_SHADOW_CAGE) && + (!channelerSquare || !channelerSquare->IsAlive()) && + (!channelerStar || !channelerStar->IsAlive()) && + (!channelerCircle || !channelerCircle->IsAlive()) && + (!channelerDiamond || !channelerDiamond->IsAlive()) && + (!channelerTriangle || !channelerTriangle->IsAlive())) + { + SetRtiTarget(botAI, "cross", magtheridon); + + if (bot->GetTarget() != magtheridon->GetGUID()) + { + bot->SetSelection(magtheridon->GetGUID()); + return Attack(magtheridon); + } + } + + return false; +} + +// Assign Burning Abyssals to Warlocks to Banish +// Burning Abyssals in excess of Warlocks in party will be Feared +bool MagtheridonWarlockCCBurningAbyssalAction::Execute(Event event) +{ + Group* group = bot->GetGroup(); + if (!group) + return false; + + const GuidVector& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + + std::vector abyssals; + for (auto const& npc : npcs) + { + Unit* unit = botAI->GetUnit(npc); + if (unit && unit->GetEntry() == NPC_BURNING_ABYSSAL && unit->IsAlive()) + abyssals.push_back(unit); + } + + std::vector warlocks; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && member->IsAlive() && member->getClass() == CLASS_WARLOCK && GET_PLAYERBOT_AI(member)) + warlocks.push_back(member); + } + + int warlockIndex = -1; + for (size_t i = 0; i < warlocks.size(); ++i) + { + if (warlocks[i] == bot) + { + warlockIndex = static_cast(i); + break; + } + } + + if (warlockIndex >= 0 && warlockIndex < abyssals.size()) + { + Unit* assignedAbyssal = abyssals[warlockIndex]; + if (!assignedAbyssal->HasAura(SPELL_BANISH) && botAI->CanCastSpell(SPELL_BANISH, assignedAbyssal, true)) + return botAI->CastSpell("banish", assignedAbyssal); + } + + for (size_t i = warlocks.size(); i < abyssals.size(); ++i) + { + Unit* excessAbyssal = abyssals[i]; + if (!excessAbyssal->HasAura(SPELL_BANISH) && !excessAbyssal->HasAura(SPELL_FEAR) && + botAI->CanCastSpell(SPELL_FEAR, excessAbyssal, true)) + return botAI->CastSpell("fear", excessAbyssal); + } + + return false; +} + +// Main tank will back up to the Northern point of the room +bool MagtheridonMainTankPositionBossAction::Execute(Event event) +{ + Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); + if (!magtheridon) + return false; + + MarkTargetWithCross(bot, magtheridon); + SetRtiTarget(botAI, "cross", magtheridon); + + if (bot->GetVictim() != magtheridon) + return Attack(magtheridon); + + if (magtheridon->GetVictim() == bot) + { + const Location& position = MagtheridonsLairLocations::MagtheridonTankPosition; + const float maxDistance = 2.0f; + + if (bot->GetExactDist2d(position.x, position.y) > maxDistance) + { + float dX = position.x - bot->GetPositionX(); + float dY = position.y - bot->GetPositionY(); + float dist = sqrt(dX * dX + dY * dY); + float moveX = bot->GetPositionX() + (dX / dist) * maxDistance; + float moveY = bot->GetPositionY() + (dY / dist) * maxDistance; + + return MoveTo(bot->GetMapId(), moveX, moveY, position.z, false, false, false, false, + MovementPriority::MOVEMENT_COMBAT, true, true); + } + + bot->SetFacingTo(position.orientation); + } + + return false; +} + +// Ranged DPS will remain within 25 yards of the center of the room +// Healers will remain within 15 yards of a position that is between ranged DPS and the boss +std::unordered_map MagtheridonSpreadRangedAction::initialPositions; +std::unordered_map MagtheridonSpreadRangedAction::hasReachedInitialPosition; + +bool MagtheridonSpreadRangedAction::Execute(Event event) +{ + Group* group = bot->GetGroup(); + if (!group) + return false; + + // Wait for 6 seconds after Magtheridon activates to spread + const uint8 spreadWaitSeconds = 6; + auto it = magtheridonSpreadWaitTimer.find(bot->GetMapId()); + if (it == magtheridonSpreadWaitTimer.end() || + (time(nullptr) - it->second) < spreadWaitSeconds) + return false; + + auto cubeIt = botToCubeAssignment.find(bot->GetGUID()); + if (cubeIt != botToCubeAssignment.end()) + { + time_t now = time(nullptr); + auto timerIt = magtheridonBlastNovaTimer.find(bot->GetMapId()); + if (timerIt != magtheridonBlastNovaTimer.end()) + { + time_t lastBlastNova = timerIt->second; + if (now - lastBlastNova >= 49) + return false; + } + } + + std::vector members; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && member->IsAlive()) + members.push_back(member); + } + + bool isHealer = botAI->IsHeal(bot); + const Location& center = isHealer + ? MagtheridonsLairLocations::HealerSpreadPosition + : MagtheridonsLairLocations::RangedSpreadPosition; + float maxSpreadRadius = isHealer ? 15.0f : 20.0f; + float centerX = center.x; + float centerY = center.y; + float centerZ = bot->GetPositionZ(); + const float radiusBuffer = 3.0f; + + if (!initialPositions.count(bot->GetGUID())) + { + auto it = std::find(members.begin(), members.end(), bot); + uint8 botIndex = (it != members.end()) ? std::distance(members.begin(), it) : 0; + uint8 count = members.size(); + + float angle = 2 * M_PI * botIndex / count; + float radius = static_cast(rand()) / RAND_MAX * maxSpreadRadius; + float targetX = centerX + radius * cos(angle); + float targetY = centerY + radius * sin(angle); + + initialPositions[bot->GetGUID()] = Position(targetX, targetY, centerZ); + hasReachedInitialPosition[bot->GetGUID()] = false; + } + + Position targetPosition = initialPositions[bot->GetGUID()]; + if (!hasReachedInitialPosition[bot->GetGUID()]) + { + if (!bot->IsWithinDist2d(targetPosition.GetPositionX(), targetPosition.GetPositionY(), 2.0f)) + { + float destX = targetPosition.GetPositionX(); + float destY = targetPosition.GetPositionY(); + float destZ = targetPosition.GetPositionZ(); + + if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), + bot->GetPositionY(), bot->GetPositionZ(), destX, destY, destZ)) + return false; + + bot->AttackStop(); + bot->InterruptNonMeleeSpells(false); + return MoveTo(bot->GetMapId(), destX, destY, destZ, false, false, false, false, + MovementPriority::MOVEMENT_COMBAT, true, false); + } + hasReachedInitialPosition[bot->GetGUID()] = true; + } + + float distToCenter = bot->GetExactDist2d(centerX, centerY); + + if (distToCenter > maxSpreadRadius + radiusBuffer) + { + float angle = static_cast(rand()) / RAND_MAX * 2.0f * M_PI; + float radius = static_cast(rand()) / RAND_MAX * maxSpreadRadius; + float targetX = centerX + radius * cos(angle); + float targetY = centerY + radius * sin(angle); + + if (bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(), + bot->GetPositionZ(), targetX, targetY, centerZ)) + { + bot->AttackStop(); + bot->InterruptNonMeleeSpells(false); + return MoveTo(bot->GetMapId(), targetX, targetY, centerZ, false, false, false, false, + MovementPriority::MOVEMENT_COMBAT, true, false); + } + } + + return false; +} + +// For bots that are assigned to click cubes +// Magtheridon casts Blast Nova every 54.35 to 55.40s, with a 2s cast time +bool MagtheridonUseManticronCubeAction::Execute(Event event) +{ + Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); + if (!magtheridon) + return false; + + auto it = botToCubeAssignment.find(bot->GetGUID()); + const CubeInfo& cubeInfo = it->second; + GameObject* cube = botAI->GetGameObject(cubeInfo.guid); + if (!cube) + return false; + + // Release cubes after Blast Nova is interrupted + if (HandleCubeRelease(magtheridon, cube)) + return true; + + // Check if cube logic should be active (49+ second rule) + if (!ShouldActivateCubeLogic(magtheridon)) + return false; + + // Handle active cube logic based on Blast Nova casting state + bool blastNovaActive = magtheridon->HasUnitState(UNIT_STATE_CASTING) && + magtheridon->FindCurrentSpellBySpellId(SPELL_BLAST_NOVA); + + if (!blastNovaActive) + // After 49 seconds, wait at safe distance from cube + return HandleWaitingPhase(cubeInfo); + else + // Blast Nova is casting - move to and click cube + return HandleCubeInteraction(cubeInfo, cube); + + return false; +} + +bool MagtheridonUseManticronCubeAction::HandleCubeRelease(Unit* magtheridon, GameObject* cube) +{ + if (bot->HasAura(SPELL_SHADOW_GRASP) && + !(magtheridon->HasUnitState(UNIT_STATE_CASTING) && + magtheridon->FindCurrentSpellBySpellId(SPELL_BLAST_NOVA))) + { + uint32 delay = urand(200, 3000); + botAI->AddTimedEvent( + [this] + { + botAI->Reset(); + }, + delay); + botAI->SetNextCheckDelay(delay + 50); + return true; + } + + return false; +} + +bool MagtheridonUseManticronCubeAction::ShouldActivateCubeLogic(Unit* magtheridon) +{ + auto timerIt = magtheridonBlastNovaTimer.find(bot->GetMapId()); + if (timerIt == magtheridonBlastNovaTimer.end()) + return false; + + time_t now = time(nullptr); + time_t lastBlastNova = timerIt->second; + + return (now - lastBlastNova >= 49); +} + +bool MagtheridonUseManticronCubeAction::HandleWaitingPhase(const CubeInfo& cubeInfo) +{ + const float safeWaitDistance = 8.0f; + float cubeDist = bot->GetExactDist2d(cubeInfo.x, cubeInfo.y); + + if (fabs(cubeDist - safeWaitDistance) > 1.0f) + { + for (int i = 0; i < 12; ++i) + { + float angle = i * M_PI / 6.0f; + float targetX = cubeInfo.x + cos(angle) * safeWaitDistance; + float targetY = cubeInfo.y + sin(angle) * safeWaitDistance; + float targetZ = bot->GetPositionZ(); + + if (IsSafeFromMagtheridonHazards(botAI, bot, targetX, targetY, targetZ)) + { + bot->AttackStop(); + bot->InterruptNonMeleeSpells(true); + return MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, false, + MovementPriority::MOVEMENT_COMBAT, true, false); + } + } + + float angle = static_cast(rand()) / RAND_MAX * 2.0f * M_PI; + float fallbackX = cubeInfo.x + cos(angle) * safeWaitDistance; + float fallbackY = cubeInfo.y + sin(angle) * safeWaitDistance; + float fallbackZ = bot->GetPositionZ(); + + return MoveTo(bot->GetMapId(), fallbackX, fallbackY, fallbackZ, false, false, false, false, + MovementPriority::MOVEMENT_COMBAT, true, false); + } + + return true; +} + +bool MagtheridonUseManticronCubeAction::HandleCubeInteraction(const CubeInfo& cubeInfo, GameObject* cube) +{ + const float interactDistance = 1.0f; + float cubeDist = bot->GetExactDist2d(cubeInfo.x, cubeInfo.y); + + if (cubeDist > interactDistance) + { + if (cubeDist <= interactDistance + 1.0f) + { + uint32 delay = urand(200, 1500); + botAI->AddTimedEvent( + [this, cube] + { + bot->StopMoving(); + cube->Use(bot); + }, + delay); + botAI->SetNextCheckDelay(delay + 50); + return true; + } + + float angle = atan2(cubeInfo.y - bot->GetPositionY(), cubeInfo.x - bot->GetPositionX()); + float targetX = cubeInfo.x - cos(angle) * interactDistance; + float targetY = cubeInfo.y - sin(angle) * interactDistance; + float targetZ = bot->GetPositionZ(); + + bot->AttackStop(); + bot->InterruptNonMeleeSpells(true); + return MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, false, + MovementPriority::MOVEMENT_FORCED, true, false); + } + + return false; +} + +// The Blast Nova timer resets when Magtheridon stops casting it, which is needed to ensure that bots use cubes. +// However, Magtheridon's Blast Nova cooldown actually runs from when he starts casting it. This means that if a Blast Nova +// is not interrupted or takes too long to interrupt, the timer will be thrown off for the rest of the encounter. +// Correcting this issue is complicated and probably would need some rewriting--I have not done so and +// and view the current solution as sufficient since in TBC a missed Blast Nova would be a guaranteed wipe anyway. +bool MagtheridonManageTimersAndAssignmentsAction::Execute(Event event) +{ + Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); + if (!magtheridon) + return false; + + uint32 mapId = magtheridon->GetMapId(); + + bool blastNovaActive = magtheridon->HasUnitState(UNIT_STATE_CASTING) && + magtheridon->FindCurrentSpellBySpellId(SPELL_BLAST_NOVA); + bool lastBlastNova = lastBlastNovaState[mapId]; + + if (lastBlastNova && !blastNovaActive && IsMapIDTimerManager(botAI, bot)) + magtheridonBlastNovaTimer[mapId] = time(nullptr); + lastBlastNovaState[mapId] = blastNovaActive; + + if (IsMapIDTimerManager(botAI, bot)) + { + if (!magtheridon->HasAura(SPELL_SHADOW_CAGE)) + { + if (magtheridonSpreadWaitTimer.find(mapId) == magtheridonSpreadWaitTimer.end()) + magtheridonSpreadWaitTimer[mapId] = time(nullptr); + + if (magtheridonBlastNovaTimer.find(mapId) == magtheridonBlastNovaTimer.end()) + magtheridonBlastNovaTimer[mapId] = time(nullptr); + + if (magtheridonAggroWaitTimer.find(mapId) == magtheridonAggroWaitTimer.end()) + magtheridonAggroWaitTimer[mapId] = time(nullptr); + } + } + + if (magtheridon->HasAura(SPELL_SHADOW_CAGE)) + { + if (!MagtheridonSpreadRangedAction::initialPositions.empty()) + MagtheridonSpreadRangedAction::initialPositions.clear(); + + if (!MagtheridonSpreadRangedAction::hasReachedInitialPosition.empty()) + MagtheridonSpreadRangedAction::hasReachedInitialPosition.clear(); + + if (!botToCubeAssignment.empty()) + botToCubeAssignment.clear(); + + if (IsMapIDTimerManager(botAI, bot)) + { + if (magtheridonSpreadWaitTimer.find(mapId) != magtheridonSpreadWaitTimer.end()) + magtheridonSpreadWaitTimer.erase(mapId); + + if (magtheridonBlastNovaTimer.find(mapId) != magtheridonBlastNovaTimer.end()) + magtheridonBlastNovaTimer.erase(mapId); + + if (magtheridonAggroWaitTimer.find(mapId) != magtheridonAggroWaitTimer.end()) + magtheridonAggroWaitTimer.erase(mapId); + } + } + + return false; +} diff --git a/src/strategy/raids/magtheridon/RaidMagtheridonActions.h b/src/strategy/raids/magtheridon/RaidMagtheridonActions.h new file mode 100644 index 0000000000..6c4ed84c22 --- /dev/null +++ b/src/strategy/raids/magtheridon/RaidMagtheridonActions.h @@ -0,0 +1,100 @@ +#ifndef _PLAYERBOT_RAIDMAGTHERIDONACTIONS_H +#define _PLAYERBOT_RAIDMAGTHERIDONACTIONS_H + +#include "RaidMagtheridonHelpers.h" +#include "Action.h" +#include "AttackAction.h" +#include "MovementActions.h" + +using namespace MagtheridonHelpers; + +class MagtheridonMainTankAttackFirstThreeChannelersAction : public AttackAction +{ +public: + MagtheridonMainTankAttackFirstThreeChannelersAction(PlayerbotAI* botAI, std::string const name = "magtheridon main tank attack first three channelers") : AttackAction(botAI, name) {}; + + bool Execute(Event event) override; +}; + +class MagtheridonFirstAssistTankAttackNWChannelerAction : public AttackAction +{ +public: + MagtheridonFirstAssistTankAttackNWChannelerAction(PlayerbotAI* botAI, std::string const name = "magtheridon first assist tank attack nw channeler") : AttackAction(botAI, name) {}; + + bool Execute(Event event) override; +}; + +class MagtheridonSecondAssistTankAttackNEChannelerAction : public AttackAction +{ +public: + MagtheridonSecondAssistTankAttackNEChannelerAction(PlayerbotAI* botAI, std::string const name = "magtheridon second assist tank attack ne channeler") : AttackAction(botAI, name) {}; + + bool Execute(Event event) override; +}; + +class MagtheridonMisdirectHellfireChannelers : public AttackAction +{ +public: + MagtheridonMisdirectHellfireChannelers(PlayerbotAI* botAI, std::string const name = "magtheridon misdirect hellfire channelers") : AttackAction(botAI, name) {}; + + bool Execute(Event event) override; +}; + +class MagtheridonAssignDPSPriorityAction : public AttackAction +{ +public: + MagtheridonAssignDPSPriorityAction(PlayerbotAI* botAI, std::string const name = "magtheridon assign dps priority") : AttackAction(botAI, name) {}; + + bool Execute(Event event) override; +}; + +class MagtheridonWarlockCCBurningAbyssalAction : public AttackAction +{ +public: + MagtheridonWarlockCCBurningAbyssalAction(PlayerbotAI* botAI, std::string const name = "magtheridon warlock cc burning abyssal") : AttackAction(botAI, name) {}; + + bool Execute(Event event) override; +}; + +class MagtheridonMainTankPositionBossAction : public AttackAction +{ +public: + MagtheridonMainTankPositionBossAction(PlayerbotAI* botAI, std::string const name = "magtheridon main tank position boss") : AttackAction(botAI, name) {}; + + bool Execute(Event event) override; +}; + +class MagtheridonSpreadRangedAction : public MovementAction +{ +public: + static std::unordered_map initialPositions; + static std::unordered_map hasReachedInitialPosition; + + MagtheridonSpreadRangedAction(PlayerbotAI* botAI, std::string const name = "magtheridon spread ranged") : MovementAction(botAI, name) {}; + + bool Execute(Event event) override; +}; + +class MagtheridonUseManticronCubeAction : public MovementAction +{ +public: + MagtheridonUseManticronCubeAction(PlayerbotAI* botAI, std::string const name = "magtheridon use manticron cube") : MovementAction(botAI, name) {}; + + bool Execute(Event event) override; + +private: + bool HandleCubeRelease(Unit* magtheridon, GameObject* cube); + bool ShouldActivateCubeLogic(Unit* magtheridon); + bool HandleWaitingPhase(const CubeInfo& cubeInfo); + bool HandleCubeInteraction(const CubeInfo& cubeInfo, GameObject* cube); +}; + +class MagtheridonManageTimersAndAssignmentsAction : public Action +{ +public: + MagtheridonManageTimersAndAssignmentsAction(PlayerbotAI* botAI, std::string const name = "magtheridon manage timers and assignments") : Action(botAI, name) {}; + + bool Execute(Event event) override; +}; + +#endif diff --git a/src/strategy/raids/magtheridon/RaidMagtheridonHelpers.cpp b/src/strategy/raids/magtheridon/RaidMagtheridonHelpers.cpp new file mode 100644 index 0000000000..8a0d693cf5 --- /dev/null +++ b/src/strategy/raids/magtheridon/RaidMagtheridonHelpers.cpp @@ -0,0 +1,226 @@ +#include "RaidMagtheridonHelpers.h" +#include "Creature.h" +#include "GameObject.h" +#include "GroupReference.h" +#include "Map.h" +#include "ObjectGuid.h" +#include "Playerbots.h" + +namespace MagtheridonHelpers +{ + namespace MagtheridonsLairLocations + { + const Location WaitingForMagtheridonPosition = { 1.359f, 2.048f, -0.406f, 3.135f }; + const Location MagtheridonTankPosition = { 22.827f, 2.105f, -0.406f, 3.135f }; + const Location NWChannelerTankPosition = { -11.764f, 30.818f, -0.411f, 0.0f }; + const Location NEChannelerTankPosition = { -12.490f, -26.211f, -0.411f, 0.0f }; + const Location RangedSpreadPosition = { -14.890f, 1.995f, -0.406f, 0.0f }; + const Location HealerSpreadPosition = { -2.265f, 1.874f, -0.404f, 0.0f }; + } + + // Identify channelers by their database GUIDs + Creature* GetChanneler(Player* bot, uint32 dbGuid) + { + Map* map = bot->GetMap(); + if (!map) + return nullptr; + + auto it = map->GetCreatureBySpawnIdStore().find(dbGuid); + if (it == map->GetCreatureBySpawnIdStore().end()) + return nullptr; + + return it->second; + } + + void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId) + { + Group* group = bot->GetGroup(); + if (!target || !group) + return; + + ObjectGuid currentGuid = group->GetTargetIcon(iconId); + if (currentGuid != target->GetGUID()) + group->SetTargetIcon(iconId, bot->GetGUID(), target->GetGUID()); + } + + void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target) + { + if (!target) + return; + + std::string currentRti = botAI->GetAiObjectContext()->GetValue("rti")->Get(); + Unit* currentTarget = botAI->GetAiObjectContext()->GetValue("rti target")->Get(); + + if (currentRti != rtiName || currentTarget != target) + { + botAI->GetAiObjectContext()->GetValue("rti")->Set(rtiName); + botAI->GetAiObjectContext()->GetValue("rti target")->Set(target); + } + } + + void MarkTargetWithSquare(Player* bot, Unit* target) + { + MarkTargetWithIcon(bot, target, RtiTargetValue::squareIndex); + } + + void MarkTargetWithStar(Player* bot, Unit* target) + { + MarkTargetWithIcon(bot, target, RtiTargetValue::starIndex); + } + + void MarkTargetWithCircle(Player* bot, Unit* target) + { + MarkTargetWithIcon(bot, target, RtiTargetValue::circleIndex); + } + + void MarkTargetWithDiamond(Player* bot, Unit* target) + { + MarkTargetWithIcon(bot, target, RtiTargetValue::diamondIndex); + } + + void MarkTargetWithTriangle(Player* bot, Unit* target) + { + MarkTargetWithIcon(bot, target, RtiTargetValue::triangleIndex); + } + + void MarkTargetWithCross(Player* bot, Unit* target) + { + MarkTargetWithIcon(bot, target, RtiTargetValue::crossIndex); + } + + const std::vector MANTICRON_CUBE_DB_GUIDS = { 43157, 43158, 43159, 43160, 43161 }; + + // Get the positions of all Manticron Cubes by their database GUIDs + std::vector GetAllCubeInfosByDbGuids(Map* map, const std::vector& cubeDbGuids) + { + std::vector cubes; + if (!map) + return cubes; + + for (uint32 dbGuid : cubeDbGuids) + { + auto bounds = map->GetGameObjectBySpawnIdStore().equal_range(dbGuid); + if (bounds.first == bounds.second) + continue; + + GameObject* go = bounds.first->second; + if (!go) + continue; + + CubeInfo info; + info.guid = go->GetGUID(); + info.x = go->GetPositionX(); + info.y = go->GetPositionY(); + info.z = go->GetPositionZ(); + cubes.push_back(info); + } + + return cubes; + } + + std::unordered_map botToCubeAssignment; + + void AssignBotsToCubesByGuidAndCoords(Group* group, const std::vector& cubes, PlayerbotAI* botAI) + { + botToCubeAssignment.clear(); + if (!group) + return; + + size_t cubeIndex = 0; + std::vector candidates; + + // Assign ranged DPS (excluding Warlocks) to cubes first + for (GroupReference* ref = group->GetFirstMember(); ref && cubeIndex < cubes.size(); ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive() || !botAI->IsRangedDps(member, true) || + member->getClass() == CLASS_WARLOCK || !GET_PLAYERBOT_AI(member)) + continue; + + candidates.push_back(member); + if (candidates.size() >= cubes.size()) + break; + } + + // If there are still cubes left, assign any other non-tank bots + if (candidates.size() < cubes.size()) + { + for (GroupReference* ref = group->GetFirstMember(); + ref && candidates.size() < cubes.size(); ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) || botAI->IsTank(member)) + continue; + + if (std::find(candidates.begin(), candidates.end(), member) == candidates.end()) + candidates.push_back(member); + } + } + + for (Player* member : candidates) + { + if (cubeIndex >= cubes.size()) + break; + + if (!member || !member->IsAlive()) + continue; + + botToCubeAssignment[member->GetGUID()] = cubes[cubeIndex++]; + } + } + + std::unordered_map lastBlastNovaState; + std::unordered_map magtheridonBlastNovaTimer; + std::unordered_map magtheridonSpreadWaitTimer; + std::unordered_map magtheridonAggroWaitTimer; + + bool IsSafeFromMagtheridonHazards(PlayerbotAI* botAI, Player* bot, float x, float y, float z) + { + // Debris + std::vector debrisHazards; + const GuidVector npcs = botAI->GetAiObjectContext()->GetValue("nearest npcs")->Get(); + for (auto const& npcGuid : npcs) + { + Unit* unit = botAI->GetUnit(npcGuid); + if (!unit || unit->GetEntry() != NPC_TARGET_TRIGGER) + continue; + debrisHazards.push_back(unit); + } + for (Unit* hazard : debrisHazards) + { + float dist = std::sqrt(std::pow(x - hazard->GetPositionX(), 2) + std::pow(y - hazard->GetPositionY(), 2)); + if (dist < 9.0f) + return false; + } + + // Conflagration + GuidVector gos = *botAI->GetAiObjectContext()->GetValue("nearest game objects"); + for (auto const& goGuid : gos) + { + GameObject* go = botAI->GetGameObject(goGuid); + if (!go || go->GetEntry() != GO_BLAZE) + continue; + + float dist = std::sqrt(std::pow(x - go->GetPositionX(), 2) + std::pow(y - go->GetPositionY(), 2)); + if (dist < 5.0f) + return false; + } + + return true; + } + + bool IsMapIDTimerManager(PlayerbotAI* botAI, Player* bot) + { + if (Group* group = bot->GetGroup()) + { + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && member->IsAlive() && !botAI->IsMainTank(member) && GET_PLAYERBOT_AI(member)) + return member == bot; + } + } + + return true; + } +} diff --git a/src/strategy/raids/magtheridon/RaidMagtheridonHelpers.h b/src/strategy/raids/magtheridon/RaidMagtheridonHelpers.h new file mode 100644 index 0000000000..21fd9d459c --- /dev/null +++ b/src/strategy/raids/magtheridon/RaidMagtheridonHelpers.h @@ -0,0 +1,90 @@ +#ifndef _PLAYERBOT_RAIDMAGTHERIDONHELPERS_H +#define _PLAYERBOT_RAIDMAGTHERIDONHELPERS_H + +#include +#include +#include + +#include "Group.h" +#include "ObjectGuid.h" +#include "PlayerbotAI.h" +#include "RtiTargetValue.h" + +namespace MagtheridonHelpers +{ + enum MagtheridonSpells + { + // Magtheridon + SPELL_SHADOW_CAGE = 30205, + SPELL_BLAST_NOVA = 30616, + SPELL_SHADOW_GRASP = 30410, + + // Warlock + SPELL_BANISH = 18647, + SPELL_FEAR = 6215, + + // Hunter + SPELL_MISDIRECTION = 34477, + }; + + enum MagtheridonNPCs + { + NPC_BURNING_ABYSSAL = 17454, + NPC_TARGET_TRIGGER = 17474, + }; + + enum MagtheridonObjects + { + GO_BLAZE = 181832, + }; + + constexpr uint32 SOUTH_CHANNELER = 90978; + constexpr uint32 WEST_CHANNELER = 90979; + constexpr uint32 NORTHWEST_CHANNELER = 90980; + constexpr uint32 EAST_CHANNELER = 90982; + constexpr uint32 NORTHEAST_CHANNELER = 90981; + + Creature* GetChanneler(Player* bot, uint32 dbGuid); + void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId); + void MarkTargetWithSquare(Player* bot, Unit* target); + void MarkTargetWithStar(Player* bot, Unit* target); + void MarkTargetWithCircle(Player* bot, Unit* target); + void MarkTargetWithDiamond(Player* bot, Unit* target); + void MarkTargetWithTriangle(Player* bot, Unit* target); + void MarkTargetWithCross(Player* bot, Unit* target); + void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target); + bool IsSafeFromMagtheridonHazards(PlayerbotAI* botAI, Player* bot, float x, float y, float z); + bool IsMapIDTimerManager(PlayerbotAI* botAI, Player* bot); + + struct Location + { + float x, y, z, orientation; + }; + + namespace MagtheridonsLairLocations + { + extern const Location WaitingForMagtheridonPosition; + extern const Location MagtheridonTankPosition; + extern const Location NWChannelerTankPosition; + extern const Location NEChannelerTankPosition; + extern const Location RangedSpreadPosition; + extern const Location HealerSpreadPosition; + } + + struct CubeInfo + { + ObjectGuid guid; + float x, y, z; + }; + + extern const std::vector MANTICRON_CUBE_DB_GUIDS; + extern std::unordered_map botToCubeAssignment; + std::vector GetAllCubeInfosByDbGuids(Map* map, const std::vector& cubeDbGuids); + void AssignBotsToCubesByGuidAndCoords(Group* group, const std::vector& cubes, PlayerbotAI* botAI); + extern std::unordered_map lastBlastNovaState; + extern std::unordered_map magtheridonBlastNovaTimer; + extern std::unordered_map magtheridonSpreadWaitTimer; + extern std::unordered_map magtheridonAggroWaitTimer; +} + +#endif diff --git a/src/strategy/raids/magtheridon/RaidMagtheridonMultipliers.cpp b/src/strategy/raids/magtheridon/RaidMagtheridonMultipliers.cpp new file mode 100644 index 0000000000..c32ce152cd --- /dev/null +++ b/src/strategy/raids/magtheridon/RaidMagtheridonMultipliers.cpp @@ -0,0 +1,71 @@ +#include +#include + +#include "RaidMagtheridonMultipliers.h" +#include "RaidMagtheridonActions.h" +#include "RaidMagtheridonHelpers.h" +#include "ChooseTargetActions.h" +#include "GenericSpellActions.h" +#include "Playerbots.h" +#include "WarlockActions.h" + +using namespace MagtheridonHelpers; + +// Don't do anything other than clicking cubes when Magtheridon is casting Blast Nova +float MagtheridonUseManticronCubeMultiplier::GetValue(Action* action) +{ + Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); + if (!magtheridon) + return 1.0f; + + if (magtheridon->HasUnitState(UNIT_STATE_CASTING) && + magtheridon->FindCurrentSpellBySpellId(SPELL_BLAST_NOVA)) + { + auto it = botToCubeAssignment.find(bot->GetGUID()); + if (it != botToCubeAssignment.end()) + { + if (dynamic_cast(action)) + return 1.0f; + + return 0.0f; + } + } + + return 1.0f; +} + +// Bots will wait for 6 seconds after Magtheridon becomes attackable before engaging +float MagtheridonWaitToAttackMultiplier::GetValue(Action* action) +{ + Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); + if (!magtheridon || magtheridon->HasAura(SPELL_SHADOW_CAGE)) + return 1.0f; + + const uint8 aggroWaitSeconds = 6; + auto it = magtheridonAggroWaitTimer.find(bot->GetMapId()); + if (it == magtheridonAggroWaitTimer.end() || + (time(nullptr) - it->second) < aggroWaitSeconds) + { + if (!botAI->IsMainTank(bot) && (dynamic_cast(action) || + (!botAI->IsHeal(bot) && dynamic_cast(action)))) + return 0.0f; + } + + return 1.0f; +} + +// No tank assist for offtanks during the channeler phase +// So they don't try to pull channelers from each other or the main tank +float MagtheridonDisableOffTankAssistMultiplier::GetValue(Action* action) +{ + Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); + Unit* channeler = AI_VALUE2(Unit*, "find target", "hellfire channeler"); + if (!magtheridon) + return 1.0f; + + if ((botAI->IsAssistTankOfIndex(bot, 0) || botAI->IsAssistTankOfIndex(bot, 1)) && + dynamic_cast(action)) + return 0.0f; + + return 1.0f; +} diff --git a/src/strategy/raids/magtheridon/RaidMagtheridonMultipliers.h b/src/strategy/raids/magtheridon/RaidMagtheridonMultipliers.h new file mode 100644 index 0000000000..2cce516b6f --- /dev/null +++ b/src/strategy/raids/magtheridon/RaidMagtheridonMultipliers.h @@ -0,0 +1,27 @@ +#ifndef _PLAYERBOT_RAIDMAGTHERIDONMULTIPLIERS_H +#define _PLAYERBOT_RAIDMAGTHERIDONMULTIPLIERS_H + +#include "Multiplier.h" + +class MagtheridonUseManticronCubeMultiplier : public Multiplier +{ +public: + MagtheridonUseManticronCubeMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "magtheridon use manticron cube multiplier") {} + float GetValue(Action* action) override; +}; + +class MagtheridonWaitToAttackMultiplier : public Multiplier +{ +public: + MagtheridonWaitToAttackMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "magtheridon wait to attack multiplier") {} + float GetValue(Action* action) override; +}; + +class MagtheridonDisableOffTankAssistMultiplier : public Multiplier +{ +public: + MagtheridonDisableOffTankAssistMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "magtheridon disable off tank assist multiplier") {} + float GetValue(Action* action) override; +}; + +#endif diff --git a/src/strategy/raids/magtheridon/RaidMagtheridonStrategy.cpp b/src/strategy/raids/magtheridon/RaidMagtheridonStrategy.cpp new file mode 100644 index 0000000000..27281dde84 --- /dev/null +++ b/src/strategy/raids/magtheridon/RaidMagtheridonStrategy.cpp @@ -0,0 +1,42 @@ +#include "RaidMagtheridonStrategy.h" +#include "RaidMagtheridonMultipliers.h" + +void RaidMagtheridonStrategy::InitTriggers(std::vector& triggers) +{ + triggers.push_back(new TriggerNode("magtheridon incoming blast nova", NextAction::array(0, + new NextAction("magtheridon use manticron cube", ACTION_EMERGENCY + 10), nullptr))); + + triggers.push_back(new TriggerNode("magtheridon need to manage timers and assignments", NextAction::array(0, + new NextAction("magtheridon manage timers and assignments", ACTION_EMERGENCY + 1), nullptr))); + + triggers.push_back(new TriggerNode("magtheridon burning abyssal spawned", NextAction::array(0, + new NextAction("magtheridon warlock cc burning abyssal", ACTION_RAID + 3), nullptr))); + + triggers.push_back(new TriggerNode("magtheridon boss engaged by ranged", NextAction::array(0, + new NextAction("magtheridon spread ranged", ACTION_RAID + 2), nullptr))); + + triggers.push_back(new TriggerNode("magtheridon pulling west and east channelers", NextAction::array(0, + new NextAction("magtheridon misdirect hellfire channelers", ACTION_RAID + 2), nullptr))); + + triggers.push_back(new TriggerNode("magtheridon boss engaged by main tank", NextAction::array(0, + new NextAction("magtheridon main tank position boss", ACTION_RAID + 2), nullptr))); + + triggers.push_back(new TriggerNode("magtheridon first three channelers engaged by main tank", NextAction::array(0, + new NextAction("magtheridon main tank attack first three channelers", ACTION_RAID + 1), nullptr))); + + triggers.push_back(new TriggerNode("magtheridon nw channeler engaged by first assist tank", NextAction::array(0, + new NextAction("magtheridon first assist tank attack nw channeler", ACTION_RAID + 1), nullptr))); + + triggers.push_back(new TriggerNode("magtheridon ne channeler engaged by second assist tank", NextAction::array(0, + new NextAction("magtheridon second assist tank attack ne channeler", ACTION_RAID + 1), nullptr))); + + triggers.push_back(new TriggerNode("magtheridon determining kill order", NextAction::array(0, + new NextAction("magtheridon assign dps priority", ACTION_RAID + 1), nullptr))); +} + +void RaidMagtheridonStrategy::InitMultipliers(std::vector& multipliers) +{ + multipliers.push_back(new MagtheridonUseManticronCubeMultiplier(botAI)); + multipliers.push_back(new MagtheridonWaitToAttackMultiplier(botAI)); + multipliers.push_back(new MagtheridonDisableOffTankAssistMultiplier(botAI)); +} diff --git a/src/strategy/raids/magtheridon/RaidMagtheridonStrategy.h b/src/strategy/raids/magtheridon/RaidMagtheridonStrategy.h new file mode 100644 index 0000000000..7b8ab8f9b1 --- /dev/null +++ b/src/strategy/raids/magtheridon/RaidMagtheridonStrategy.h @@ -0,0 +1,18 @@ +#ifndef _PLAYERBOT_RAIDMAGTHERIDONSTRATEGY_H +#define _PLAYERBOT_RAIDMAGTHERIDONSTRATEGY_H + +#include "Strategy.h" +#include "Multiplier.h" + +class RaidMagtheridonStrategy : public Strategy +{ +public: + RaidMagtheridonStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} + + std::string const getName() override { return "magtheridon"; } + + void InitTriggers(std::vector& triggers) override; + void InitMultipliers(std::vector& multipliers) override; +}; + +#endif diff --git a/src/strategy/raids/magtheridon/RaidMagtheridonTriggerContext.h b/src/strategy/raids/magtheridon/RaidMagtheridonTriggerContext.h new file mode 100644 index 0000000000..525fe496e8 --- /dev/null +++ b/src/strategy/raids/magtheridon/RaidMagtheridonTriggerContext.h @@ -0,0 +1,37 @@ +#ifndef _PLAYERBOT_RAIDMAGTHERIDONTRIGGERCONTEXT_H +#define _PLAYERBOT_RAIDMAGTHERIDONTRIGGERCONTEXT_H + +#include "RaidMagtheridonTriggers.h" +#include "AiObjectContext.h" + +class RaidMagtheridonTriggerContext : public NamedObjectContext +{ +public: + RaidMagtheridonTriggerContext() : NamedObjectContext() + { + creators["magtheridon first three channelers engaged by main tank"] = &RaidMagtheridonTriggerContext::magtheridon_first_three_channelers_engaged_by_main_tank; + creators["magtheridon nw channeler engaged by first assist tank"] = &RaidMagtheridonTriggerContext::magtheridon_nw_channeler_engaged_by_first_assist_tank; + creators["magtheridon ne channeler engaged by second assist tank"] = &RaidMagtheridonTriggerContext::magtheridon_ne_channeler_engaged_by_second_assist_tank; + creators["magtheridon pulling west and east channelers"] = &RaidMagtheridonTriggerContext::magtheridon_pull_west_and_east_channelers; + creators["magtheridon determining kill order"] = &RaidMagtheridonTriggerContext::magtheridon_determining_kill_order; + creators["magtheridon burning abyssal spawned"] = &RaidMagtheridonTriggerContext::magtheridon_burning_abyssal_spawned; + creators["magtheridon boss engaged by main tank"] = &RaidMagtheridonTriggerContext::magtheridon_boss_engaged_by_main_tank; + creators["magtheridon boss engaged by ranged"] = &RaidMagtheridonTriggerContext::magtheridon_boss_engaged_by_ranged; + creators["magtheridon incoming blast nova"] = &RaidMagtheridonTriggerContext::magtheridon_incoming_blast_nova; + creators["magtheridon need to manage timers and assignments"] = &RaidMagtheridonTriggerContext::magtheridon_need_to_manage_timers_and_assignments; + } + +private: + static Trigger* magtheridon_first_three_channelers_engaged_by_main_tank(PlayerbotAI* botAI) { return new MagtheridonFirstThreeChannelersEngagedByMainTankTrigger(botAI); } + static Trigger* magtheridon_nw_channeler_engaged_by_first_assist_tank(PlayerbotAI* botAI) { return new MagtheridonNWChannelerEngagedByFirstAssistTankTrigger(botAI); } + static Trigger* magtheridon_ne_channeler_engaged_by_second_assist_tank(PlayerbotAI* botAI) { return new MagtheridonNEChannelerEngagedBySecondAssistTankTrigger(botAI); } + static Trigger* magtheridon_pull_west_and_east_channelers(PlayerbotAI* botAI) { return new MagtheridonPullingWestAndEastChannelersTrigger(botAI); } + static Trigger* magtheridon_determining_kill_order(PlayerbotAI* botAI) { return new MagtheridonDeterminingKillOrderTrigger(botAI); } + static Trigger* magtheridon_burning_abyssal_spawned(PlayerbotAI* botAI) { return new MagtheridonBurningAbyssalSpawnedTrigger(botAI); } + static Trigger* magtheridon_boss_engaged_by_main_tank(PlayerbotAI* botAI) { return new MagtheridonBossEngagedByMainTankTrigger(botAI); } + static Trigger* magtheridon_boss_engaged_by_ranged(PlayerbotAI* botAI) { return new MagtheridonBossEngagedByRangedTrigger(botAI); } + static Trigger* magtheridon_incoming_blast_nova(PlayerbotAI* botAI) { return new MagtheridonIncomingBlastNovaTrigger(botAI); } + static Trigger* magtheridon_need_to_manage_timers_and_assignments(PlayerbotAI* botAI) { return new MagtheridonNeedToManageTimersAndAssignmentsTrigger(botAI); } +}; + +#endif diff --git a/src/strategy/raids/magtheridon/RaidMagtheridonTriggers.cpp b/src/strategy/raids/magtheridon/RaidMagtheridonTriggers.cpp new file mode 100644 index 0000000000..35442df6e0 --- /dev/null +++ b/src/strategy/raids/magtheridon/RaidMagtheridonTriggers.cpp @@ -0,0 +1,128 @@ +#include "RaidMagtheridonTriggers.h" +#include "RaidMagtheridonHelpers.h" +#include "Playerbots.h" + +using namespace MagtheridonHelpers; + +bool MagtheridonFirstThreeChannelersEngagedByMainTankTrigger::IsActive() +{ + Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); + + return magtheridon && botAI->IsMainTank(bot) && + magtheridon->HasAura(SPELL_SHADOW_CAGE); +} + +bool MagtheridonNWChannelerEngagedByFirstAssistTankTrigger::IsActive() +{ + Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); + Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER); + + return magtheridon && botAI->IsAssistTankOfIndex(bot, 0) && + channelerDiamond && channelerDiamond->IsAlive(); +} + +bool MagtheridonNEChannelerEngagedBySecondAssistTankTrigger::IsActive() +{ + Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); + Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER); + + return magtheridon && botAI->IsAssistTankOfIndex(bot, 1) && + channelerTriangle && channelerTriangle->IsAlive(); +} + +bool MagtheridonPullingWestAndEastChannelersTrigger::IsActive() +{ + Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); + + Creature* channelerStar = GetChanneler(bot, WEST_CHANNELER); + Creature* channelerCircle = GetChanneler(bot, EAST_CHANNELER); + + return magtheridon && bot->getClass() == CLASS_HUNTER && + ((channelerStar && channelerStar->IsAlive()) || + (channelerCircle && channelerCircle->IsAlive())); +} + +bool MagtheridonDeterminingKillOrderTrigger::IsActive() +{ + Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); + Unit* channeler = AI_VALUE2(Unit*, "find target", "hellfire channeler"); + + Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER); + Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER); + + if (!magtheridon || botAI->IsHeal(bot) || botAI->IsMainTank(bot) || + (botAI->IsAssistTankOfIndex(bot, 0) && channelerDiamond && channelerDiamond->IsAlive()) || + (botAI->IsAssistTankOfIndex(bot, 1) && channelerTriangle && channelerTriangle->IsAlive())) + return false; + + return (channeler && channeler->IsAlive()) || (magtheridon && + !magtheridon->HasAura(SPELL_SHADOW_CAGE)); +} + +bool MagtheridonBurningAbyssalSpawnedTrigger::IsActive() +{ + Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); + if (!magtheridon || bot->getClass() != CLASS_WARLOCK) + return false; + + const GuidVector& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + return std::any_of(npcs.begin(), npcs.end(), [this](const ObjectGuid& npc) + { + Unit* unit = botAI->GetUnit(npc); + return unit && unit->GetEntry() == NPC_BURNING_ABYSSAL; + }); +} + +bool MagtheridonBossEngagedByMainTankTrigger::IsActive() +{ + Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); + + return magtheridon && botAI->IsMainTank(bot) && + !magtheridon->HasAura(SPELL_SHADOW_CAGE); +} + +bool MagtheridonBossEngagedByRangedTrigger::IsActive() +{ + Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); + Unit* channeler = AI_VALUE2(Unit*, "find target", "hellfire channeler"); + + return magtheridon && botAI->IsRanged(bot) && + !(channeler && channeler->IsAlive()); +} + +bool MagtheridonIncomingBlastNovaTrigger::IsActive() +{ + Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); + Group* group = bot->GetGroup(); + if (!group || !magtheridon || magtheridon->HasAura(SPELL_SHADOW_CAGE)) + return false; + + bool needsReassign = botToCubeAssignment.empty(); + if (!needsReassign) + { + for (auto const& pair : botToCubeAssignment) + { + Player* assigned = ObjectAccessor::FindPlayer(pair.first); + if (!assigned || !assigned->IsAlive()) + { + needsReassign = true; + break; + } + } + } + + if (needsReassign) + { + std::vector cubes = GetAllCubeInfosByDbGuids(bot->GetMap(), MANTICRON_CUBE_DB_GUIDS); + AssignBotsToCubesByGuidAndCoords(group, cubes, botAI); + } + + return botToCubeAssignment.find(bot->GetGUID()) != botToCubeAssignment.end(); +} + +bool MagtheridonNeedToManageTimersAndAssignmentsTrigger::IsActive() +{ + Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); + + return magtheridon; +} diff --git a/src/strategy/raids/magtheridon/RaidMagtheridonTriggers.h b/src/strategy/raids/magtheridon/RaidMagtheridonTriggers.h new file mode 100644 index 0000000000..0039c4e276 --- /dev/null +++ b/src/strategy/raids/magtheridon/RaidMagtheridonTriggers.h @@ -0,0 +1,77 @@ +#ifndef _PLAYERBOT_RAIDMAGTHERIDONTRIGGERS_H +#define _PLAYERBOT_RAIDMAGTHERIDONTRIGGERS_H + +#include "Trigger.h" +#include "PlayerbotAI.h" + +class MagtheridonFirstThreeChannelersEngagedByMainTankTrigger : public Trigger +{ +public: + MagtheridonFirstThreeChannelersEngagedByMainTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "magtheridon first three channelers engaged by main tank") {}; + bool IsActive() override; +}; + +class MagtheridonNWChannelerEngagedByFirstAssistTankTrigger : public Trigger +{ +public: + MagtheridonNWChannelerEngagedByFirstAssistTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "magtheridon nw channeler engaged by first assist tank") {}; + bool IsActive() override; +}; + +class MagtheridonNEChannelerEngagedBySecondAssistTankTrigger : public Trigger +{ +public: + MagtheridonNEChannelerEngagedBySecondAssistTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "magtheridon ne channeler engaged by second assist tank") {}; + bool IsActive() override; +}; + +class MagtheridonPullingWestAndEastChannelersTrigger : public Trigger +{ +public: + MagtheridonPullingWestAndEastChannelersTrigger(PlayerbotAI* botAI) : Trigger(botAI, "magtheridon pulling west and east channelers") {}; + bool IsActive() override; +}; + +class MagtheridonDeterminingKillOrderTrigger : public Trigger +{ +public: + MagtheridonDeterminingKillOrderTrigger(PlayerbotAI* botAI) : Trigger(botAI, "magtheridon determining kill order") {}; + bool IsActive() override; +}; + +class MagtheridonBurningAbyssalSpawnedTrigger : public Trigger +{ +public: + MagtheridonBurningAbyssalSpawnedTrigger(PlayerbotAI* botAI) : Trigger(botAI, "magtheridon burning abyssal spawned") {}; + bool IsActive() override; +}; + +class MagtheridonBossEngagedByMainTankTrigger : public Trigger +{ +public: + MagtheridonBossEngagedByMainTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "magtheridon boss engaged by main tank") {}; + bool IsActive() override; +}; + +class MagtheridonBossEngagedByRangedTrigger : public Trigger +{ +public: + MagtheridonBossEngagedByRangedTrigger(PlayerbotAI* botAI) : Trigger(botAI, "magtheridon boss engaged by ranged") {}; + bool IsActive() override; +}; + +class MagtheridonIncomingBlastNovaTrigger : public Trigger +{ +public: + MagtheridonIncomingBlastNovaTrigger(PlayerbotAI* botAI) : Trigger(botAI, "magtheridon incoming blast nova") {}; + bool IsActive() override; +}; + +class MagtheridonNeedToManageTimersAndAssignmentsTrigger : public Trigger +{ +public: + MagtheridonNeedToManageTimersAndAssignmentsTrigger(PlayerbotAI* botAI) : Trigger(botAI, "magtheridon need to manage timers and assignments") {}; + bool IsActive() override; +}; + +#endif From ce51191e8f88a8ddf8e3601427f4bbb72189f62e Mon Sep 17 00:00:00 2001 From: Keleborn <22352763+Celandriel@users.noreply.github.com> Date: Wed, 5 Nov 2025 06:38:14 -0800 Subject: [PATCH 18/47] Fix. Leave group actions (#1774) Fix for issue #1768 where the bot master was not getting reset. I also cleaned up leave group action to focus up function scope. I moved the resets from #612 to the actual leaving function. Disclosure: LLMs were NOT used in authoring this PR. Test cases to consider for testers. 1. Master disbands group with random bots. Bots should go about their business. 2. If you can dual box, create a raid where two real player both invite random bots. One player leaves group, their bots should also leave. (edge case, if a random bot that is supposed to leave the group becomes leader they may disband the whole group. --- code_format.sh | 2 +- src/PlayerbotAI.cpp | 203 +++++++++------------- src/PlayerbotAI.h | 5 +- src/strategy/actions/LeaveGroupAction.cpp | 74 ++++---- src/strategy/actions/LeaveGroupAction.h | 2 +- 5 files changed, 122 insertions(+), 164 deletions(-) mode change 100755 => 100644 code_format.sh diff --git a/code_format.sh b/code_format.sh old mode 100755 new mode 100644 index 3e2d3552a4..e9612dacd6 --- a/code_format.sh +++ b/code_format.sh @@ -15,4 +15,4 @@ for file in $cpp_files; do $CLANG_FORMAT_PATH -i $file done -echo "All .cpp or .h files have been formatted." +echo "All .cpp or .h files have been formatted." \ No newline at end of file diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 48a259e1b3..f014151ec1 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -355,7 +355,7 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal) } // Update the bot's group status (moved to helper function) - UpdateAIGroupMembership(); + UpdateAIGroupAndMaster(); // Update internal AI UpdateAIInternal(elapsed, minimal); @@ -363,47 +363,60 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal) } // Helper function for UpdateAI to check group membership and handle removal if necessary -void PlayerbotAI::UpdateAIGroupMembership() +void PlayerbotAI::UpdateAIGroupAndMaster() { - if (!bot || !bot->GetGroup()) + if (!bot) return; - Group* group = bot->GetGroup(); - - if (!bot->InBattleground() && !bot->inRandomLfgDungeon() && !group->isLFGGroup()) + // If bot is not in group verify that for is RandomBot before clearing master and resetting. + if (!group) { - Player* leader = group->GetLeader(); - if (leader && leader != bot) // Ensure the leader is valid and not the bot itself + if (master && sRandomPlayerbotMgr->IsRandomBot(bot)) { - PlayerbotAI* leaderAI = GET_PLAYERBOT_AI(leader); - if (leaderAI && !leaderAI->IsRealPlayer()) - { - LeaveOrDisbandGroup(); - } + SetMaster(nullptr); + Reset(true); + ResetStrategies(); } + return; } - else if (group->isLFGGroup()) - { - bool hasRealPlayer = false; - // Iterate over all group members to check if at least one is a real player - for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + if (bot->InBattleground() && bot->GetBattleground()->GetBgTypeID() != BATTLEGROUND_AV) + return; + + PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); + if (!botAI) + return; + + PlayerbotAI* masterBotAI = nullptr; + if (master) + masterBotAI = GET_PLAYERBOT_AI(master); + + if (!master || (masterBotAI && !masterBotAI->IsRealPlayer())) + { + Player* newMaster = FindNewMaster(); + if (newMaster) { - Player* member = ref->GetSource(); - if (!member) - continue; + master = newMaster; + botAI->SetMaster(newMaster); + botAI->ResetStrategies(); - PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member); - if (memberAI && !memberAI->IsRealPlayer()) - continue; + if (!bot->InBattleground()) + { + botAI->ChangeStrategy("+follow", BOT_STATE_NON_COMBAT); - hasRealPlayer = true; - break; + if (botAI->GetMaster() == botAI->GetGroupMaster()) + botAI->TellMaster("Hello, I follow you!"); + else + botAI->TellMaster(!urand(0, 2) ? "Hello!" : "Hi!"); + } + else + { + // we're in a battleground, stay with the pack and focus on objective + botAI->ChangeStrategy("-follow", BOT_STATE_NON_COMBAT); + } } - if (!hasRealPlayer) - { + else if (!newMaster && !bot->InBattleground()) LeaveOrDisbandGroup(); - } } } @@ -792,7 +805,6 @@ void PlayerbotAI::LeaveOrDisbandGroup() WorldPacket* packet = new WorldPacket(CMSG_GROUP_DISBAND); bot->GetSession()->QueuePacket(packet); - ResetStrategies(); } bool PlayerbotAI::IsAllowedCommand(std::string const text) @@ -1301,7 +1313,6 @@ void PlayerbotAI::DoNextAction(bool min) return; } - // Change engine if just died bool isBotAlive = bot->IsAlive(); if (currentEngine != engines[BOT_STATE_DEAD] && !isBotAlive) @@ -1361,92 +1372,6 @@ void PlayerbotAI::DoNextAction(bool min) Group* group = bot->GetGroup(); PlayerbotAI* masterBotAI = nullptr; - if (master) - masterBotAI = GET_PLAYERBOT_AI(master); - - // Test BG master set - if ((!master || (masterBotAI && !masterBotAI->IsRealPlayer())) && group) - { - PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); - if (!botAI) - { - return; - } - - // Ideally we want to have the leader as master. - Player* newMaster = botAI->GetGroupMaster(); - Player* playerMaster = nullptr; - - // Are there any non-bot players in the group? - if (!newMaster || GET_PLAYERBOT_AI(newMaster)) - { - for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) - { - Player* member = gref->GetSource(); - if (!member || member == bot || member == newMaster || !member->IsInWorld() || - !member->IsInSameRaidWith(bot)) - continue; - - PlayerbotAI* memberBotAI = GET_PLAYERBOT_AI(member); - if (memberBotAI) - { - if (memberBotAI->IsRealPlayer() && !bot->InBattleground()) - playerMaster = member; - - continue; - } - - // Same BG checks (optimize checking conditions here) - if (bot->InBattleground() && bot->GetBattleground() && - bot->GetBattleground()->GetBgTypeID() == BATTLEGROUND_AV && !GET_PLAYERBOT_AI(member) && - member->InBattleground() && bot->GetMapId() == member->GetMapId()) - { - // Skip if same BG but same subgroup or lower level - if (!group->SameSubGroup(bot, member) || member->GetLevel() < bot->GetLevel()) - continue; - - // Follow real player only if higher honor points - uint32 honorpts = member->GetHonorPoints(); - if (bot->GetHonorPoints() && honorpts < bot->GetHonorPoints()) - continue; - - playerMaster = member; - continue; - } - - if (bot->InBattleground()) - continue; - - newMaster = member; - break; - } - } - - if (!newMaster && playerMaster) - newMaster = playerMaster; - - if (newMaster && (!master || master != newMaster) && bot != newMaster) - { - master = newMaster; - botAI->SetMaster(newMaster); - botAI->ResetStrategies(); - - if (!bot->InBattleground()) - { - botAI->ChangeStrategy("+follow", BOT_STATE_NON_COMBAT); - - if (botAI->GetMaster() == botAI->GetGroupMaster()) - botAI->TellMaster("Hello, I follow you!"); - else - botAI->TellMaster(!urand(0, 2) ? "Hello!" : "Hi!"); - } - else - { - // we're in a battleground, stay with the pack and focus on objective - botAI->ChangeStrategy("-follow", BOT_STATE_NON_COMBAT); - } - } - } if (master && master->IsInWorld()) { @@ -4135,6 +4060,50 @@ bool IsAlliance(uint8 race) race == RACE_DRAENEI; } +Player* PlayerbotAI::FindNewMaster() +{ + // Ideally we want to have the leader as master. + Group* group = bot->GetGroup(); + // Only allow real players as masters unless in battleground. + if (!group) + return nullptr; + + Player* groupLeader = GetGroupMaster(); + PlayerbotAI* leaderBotAI = GET_PLAYERBOT_AI(groupLeader); + if (!leaderBotAI || leaderBotAI->IsRealPlayer()) + return groupLeader; + + // Find the real player in group + for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) + { + Player* member = gref->GetSource(); + if (!member || member == bot || !member->IsInWorld() || + !member->IsInSameRaidWith(bot)) + continue; + + PlayerbotAI* memberBotAI = GET_PLAYERBOT_AI(member); + if ((!memberBotAI || memberBotAI->IsRealPlayer()) && !bot->InBattleground()) + return member; + + if (bot->InBattleground() && bot->GetBattleground() && + bot->GetBattleground()->GetBgTypeID() == BATTLEGROUND_AV && !GET_PLAYERBOT_AI(member) && + member->InBattleground() && bot->GetMapId() == member->GetMapId()) + { + // Skip if same BG but same subgroup or lower level + if (!group->SameSubGroup(bot, member) || member->GetLevel() < bot->GetLevel()) + continue; + + // Follow real player only if higher honor points + uint32 honorpts = member->GetHonorPoints(); + if (bot->GetHonorPoints() && honorpts < bot->GetHonorPoints()) + continue; + + return member; + } + } + return nullptr; +} + bool PlayerbotAI::HasRealPlayerMaster() { if (master) diff --git a/src/PlayerbotAI.h b/src/PlayerbotAI.h index a6600845fe..b5dacd7642 100644 --- a/src/PlayerbotAI.h +++ b/src/PlayerbotAI.h @@ -529,7 +529,8 @@ class PlayerbotAI : public PlayerbotAIBase Player* GetBot() { return bot; } Player* GetMaster() { return master; } - + Player* FindNewMaster(); + // Checks if the bot is really a player. Players always have themselves as master. bool IsRealPlayer() { return master ? (master == bot) : false; } // Bot has a master that is a player. @@ -611,7 +612,7 @@ class PlayerbotAI : public PlayerbotAIBase static void _fillGearScoreData(Player* player, Item* item, std::vector* gearScore, uint32& twoHandScore, bool mixed = false); bool IsTellAllowed(PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL); - void UpdateAIGroupMembership(); + void UpdateAIGroupAndMaster(); Item* FindItemInInventory(std::function checkItem) const; void HandleCommands(); void HandleCommand(uint32 type, const std::string& text, Player& fromPlayer, const uint32 lang = LANG_UNIVERSAL); diff --git a/src/strategy/actions/LeaveGroupAction.cpp b/src/strategy/actions/LeaveGroupAction.cpp index 88567b1ddd..039c476bf5 100644 --- a/src/strategy/actions/LeaveGroupAction.cpp +++ b/src/strategy/actions/LeaveGroupAction.cpp @@ -11,8 +11,11 @@ bool LeaveGroupAction::Execute(Event event) { - Player* master = event.getOwner(); - return Leave(master); + Player* player = event.getOwner(); + if (player == botAI->GetMaster()) + return Leave(); + + return false; } bool PartyCommandAction::Execute(Event event) @@ -26,13 +29,21 @@ bool PartyCommandAction::Execute(Event event) if (operation != PARTY_OP_LEAVE) return false; - + // Only leave if master has left the party, and randombot cannot set new master. Player* master = GetMaster(); if (master && member == master->GetName()) - return Leave(bot); - - botAI->Reset(); - + { + if (sRandomPlayerbotMgr->IsRandomBot(bot)) + { + Player* newMaster = botAI->FindNewMaster(); + if (newMaster || bot->InBattleground()) + { + botAI->SetMaster(newMaster); + return false; + } + } + return Leave(); + } return false; } @@ -42,17 +53,17 @@ bool UninviteAction::Execute(Event event) if (p.GetOpcode() == CMSG_GROUP_UNINVITE) { p.rpos(0); - std::string membername; - p >> membername; + std::string memberName; + p >> memberName; // player not found - if (!normalizePlayerName(membername)) + if (!normalizePlayerName(memberName)) { return false; } - if (bot->GetName() == membername) - return Leave(bot); + if (bot->GetName() == memberName) + return Leave(); } if (p.GetOpcode() == CMSG_GROUP_UNINVITE_GUID) @@ -62,50 +73,29 @@ bool UninviteAction::Execute(Event event) p >> guid; if (bot->GetGUID() == guid) - return Leave(bot); + return Leave(); } - botAI->Reset(); - return false; } -bool LeaveGroupAction::Leave(Player* player) +bool LeaveGroupAction::Leave() { - if (player && - !botAI && - !botAI->GetSecurity()->CheckLevelFor(PLAYERBOT_SECURITY_INVITE, false, player)) - + if (!botAI) return false; - bool aiMaster = GET_PLAYERBOT_AI(botAI->GetMaster()) != nullptr; - - botAI->TellMaster("Goodbye!", PLAYERBOT_SECURITY_TALK); - - bool randomBot = sRandomPlayerbotMgr->IsRandomBot(bot); - bool shouldStay = randomBot && bot->GetGroup() && player == bot; - if (!shouldStay) - { - botAI->LeaveOrDisbandGroup(); - } - - if (randomBot) - { - GET_PLAYERBOT_AI(bot)->SetMaster(nullptr); - } - - if (!aiMaster) - botAI->ResetStrategies(!randomBot); - - botAI->Reset(); + Player* master = botAI -> GetMaster(); + if (master) + botAI->TellMaster("Goodbye!", PLAYERBOT_SECURITY_TALK); + botAI->LeaveOrDisbandGroup(); return true; } bool LeaveFarAwayAction::Execute(Event event) { // allow bot to leave party when they want - return Leave(botAI->GetGroupMaster()); + return Leave(); } bool LeaveFarAwayAction::isUseful() @@ -165,7 +155,5 @@ bool LeaveFarAwayAction::isUseful() return true; } - botAI->Reset(); - return false; } diff --git a/src/strategy/actions/LeaveGroupAction.h b/src/strategy/actions/LeaveGroupAction.h index dcb4e96ce5..83baea3c1b 100644 --- a/src/strategy/actions/LeaveGroupAction.h +++ b/src/strategy/actions/LeaveGroupAction.h @@ -18,7 +18,7 @@ class LeaveGroupAction : public Action bool Execute(Event event) override; - virtual bool Leave(Player* player); + virtual bool Leave(); }; class PartyCommandAction : public LeaveGroupAction From 85c7009fe1af8815c203ed963f431b5a08930f86 Mon Sep 17 00:00:00 2001 From: kadeshar Date: Wed, 5 Nov 2025 21:10:17 +0100 Subject: [PATCH 19/47] Codestyle fix (#1797) Warning: Dont change this PR as draft to make it testable DONT REVIEW UNTIL Codestyle C++ workflow dont pass --- .github/workflows/codestyle_cpp.yml | 2 +- var/.suppress.cppcheck => .suppress.cppcheck | 0 apps/{ => codestyle}/codestyle-cpp.py | 0 src/AiFactory.cpp | 10 +- src/BroadcastHelper.cpp | 5 +- src/ChatFilter.cpp | 4 +- src/ChatHelper.cpp | 9 +- src/GuildTaskMgr.cpp | 2 +- src/PlayerbotAI.cpp | 37 +++--- src/PlayerbotAI.h | 2 +- src/PlayerbotMgr.cpp | 3 +- src/RandomItemMgr.cpp | 11 +- src/RandomPlayerbotMgr.cpp | 23 ++-- src/ServerFacade.cpp | 2 +- src/TravelMgr.cpp | 9 +- src/TravelNode.cpp | 9 +- src/factory/PlayerbotFactory.cpp | 16 ++- src/factory/StatsWeightCalculator.cpp | 2 +- src/strategy/Engine.cpp | 4 +- src/strategy/NamedObjectContext.h | 4 +- src/strategy/actions/AttackAction.cpp | 10 +- .../AutoMaintenanceOnLevelupAction.cpp | 1 - src/strategy/actions/BankAction.cpp | 2 +- src/strategy/actions/BattleGroundTactics.cpp | 28 ++-- .../actions/CheckMountStateAction.cpp | 4 +- .../actions/ChooseTravelTargetAction.cpp | 3 +- src/strategy/actions/DropQuestAction.cpp | 2 - src/strategy/actions/EmoteAction.cpp | 8 +- src/strategy/actions/EquipAction.cpp | 15 ++- src/strategy/actions/GenericBuffUtils.cpp | 2 +- src/strategy/actions/GenericBuffUtils.h | 1 - src/strategy/actions/GenericSpellActions.cpp | 7 +- .../actions/GuildManagementActions.cpp | 2 +- src/strategy/actions/LfgActions.cpp | 5 +- src/strategy/actions/LootRollAction.cpp | 8 +- src/strategy/actions/MovementActions.cpp | 49 ++++--- src/strategy/actions/MovementActions.h | 1 - src/strategy/actions/NonCombatActions.cpp | 16 +-- src/strategy/actions/QuestAction.cpp | 6 +- src/strategy/actions/QuestAction.h | 4 +- src/strategy/actions/ReleaseSpiritAction.cpp | 2 +- .../actions/RevealGatheringItemAction.cpp | 19 +-- .../actions/ReviveFromCorpseAction.cpp | 2 +- src/strategy/actions/RpgAction.cpp | 1 - src/strategy/actions/SayAction.cpp | 2 +- src/strategy/actions/SetHomeAction.cpp | 2 +- .../actions/SuggestWhatToDoAction.cpp | 3 +- src/strategy/actions/TameAction.cpp | 4 +- src/strategy/actions/TellGlyphsAction.h | 1 - src/strategy/actions/TradeAction.cpp | 2 +- src/strategy/actions/TrainerAction.cpp | 4 +- .../actions/UseMeetingStoneAction.cpp | 23 +--- src/strategy/actions/WorldBuffAction.cpp | 1 - src/strategy/actions/XpGainAction.cpp | 1 - src/strategy/deathknight/DKActions.cpp | 4 +- src/strategy/druid/DruidActions.h | 1 - .../dungeons/DungeonStrategyContext.h | 4 - .../wotlk/WotlkDungeonTriggerContext.h | 1 - .../wotlk/azjolnerub/AzjolNerubActions.cpp | 1 - .../wotlk/azjolnerub/AzjolNerubStrategy.cpp | 1 - .../wotlk/azjolnerub/AzjolNerubStrategy.h | 1 - .../wotlk/azjolnerub/AzjolNerubTriggers.cpp | 1 - .../CullingOfStratholmeActions.cpp | 5 +- .../CullingOfStratholmeStrategy.cpp | 1 - .../CullingOfStratholmeStrategy.h | 1 - .../CullingOfStratholmeTriggers.cpp | 1 - .../draktharonkeep/DrakTharonKeepActions.cpp | 1 - .../draktharonkeep/DrakTharonKeepStrategy.cpp | 1 - .../draktharonkeep/DrakTharonKeepStrategy.h | 1 - .../draktharonkeep/DrakTharonKeepTriggers.cpp | 1 - .../forgeofsouls/ForgeOfSoulsActions.cpp | 4 - .../forgeofsouls/ForgeOfSoulsMultipliers.cpp | 1 - .../forgeofsouls/ForgeOfSoulsMultipliers.h | 1 - .../wotlk/gundrak/GundrakStrategy.cpp | 1 - .../dungeons/wotlk/gundrak/GundrakStrategy.h | 1 - .../HallsOfLightningActions.cpp | 5 +- .../HallsOfLightningMultipliers.cpp | 1 - .../HallsOfLightningStrategy.cpp | 1 - .../HallsOfLightningStrategy.h | 1 - .../hallsofstone/HallsOfStoneStrategy.cpp | 1 - .../wotlk/hallsofstone/HallsOfStoneStrategy.h | 1 - .../dungeons/wotlk/nexus/NexusMultipliers.cpp | 1 - .../dungeons/wotlk/nexus/NexusStrategy.cpp | 1 - .../dungeons/wotlk/nexus/NexusStrategy.h | 1 - .../wotlk/oculus/OculusMultipliers.cpp | 2 +- .../dungeons/wotlk/oculus/OculusStrategy.cpp | 1 - .../dungeons/wotlk/oculus/OculusStrategy.h | 1 - .../wotlk/oldkingdom/OldKingdomActions.cpp | 1 - .../wotlk/oldkingdom/OldKingdomStrategy.cpp | 1 - .../wotlk/oldkingdom/OldKingdomStrategy.h | 1 - .../wotlk/oldkingdom/OldKingdomTriggers.cpp | 1 - .../wotlk/pitofsaron/PitOfSaronActions.cpp | 1 - .../pitofsaron/PitOfSaronMultipliers.cpp | 2 - .../wotlk/pitofsaron/PitOfSaronMultipliers.h | 1 - .../TrialOfTheChampionActions.cpp | 1 - .../TrialOfTheChampionStrategy.cpp | 1 - .../TrialOfTheChampionTriggers.cpp | 2 - .../TrialOfTheChampionTriggers.h | 7 - .../wotlk/utgardekeep/UtgardeKeepStrategy.cpp | 1 - .../wotlk/utgardekeep/UtgardeKeepStrategy.h | 1 - .../UtgardePinnacleStrategy.cpp | 1 - .../utgardepinnacle/UtgardePinnacleStrategy.h | 1 - .../wotlk/violethold/VioletHoldActions.cpp | 1 - .../wotlk/violethold/VioletHoldStrategy.cpp | 1 - .../wotlk/violethold/VioletHoldStrategy.h | 1 - .../wotlk/violethold/VioletHoldTriggers.cpp | 1 - src/strategy/hunter/GenericHunterStrategy.h | 2 - src/strategy/paladin/PaladinActions.cpp | 2 - src/strategy/priest/GenericPriestStrategy.cpp | 1 - src/strategy/raids/aq20/RaidAq20Actions.cpp | 1 - src/strategy/raids/aq20/RaidAq20Triggers.cpp | 1 - .../raids/gruulslair/RaidGruulsLairHelpers.h | 102 +++++++-------- .../raids/icecrown/RaidIccActions.cpp | 120 ++++++++---------- src/strategy/raids/icecrown/RaidIccActions.h | 6 - .../raids/icecrown/RaidIccMultipliers.cpp | 6 +- .../raids/icecrown/RaidIccMultipliers.h | 1 - .../raids/icecrown/RaidIccStrategy.cpp | 1 - .../raids/icecrown/RaidIccTriggers.cpp | 4 +- src/strategy/raids/icecrown/RaidIccTriggers.h | 5 - .../raids/karazhan/RaidKarazhanActions.cpp | 90 ++++++------- .../raids/karazhan/RaidKarazhanHelpers.cpp | 20 +-- .../raids/karazhan/RaidKarazhanHelpers.h | 2 +- .../karazhan/RaidKarazhanMultipliers.cpp | 12 +- .../raids/naxxramas/RaidNaxxMultipliers.cpp | 21 ++- .../raids/naxxramas/RaidNaxxTriggers.h | 3 +- .../raids/obsidiansanctum/RaidOsTriggers.cpp | 5 +- .../raids/ulduar/RaidUlduarActions.cpp | 4 +- .../raids/ulduar/RaidUlduarBossHelper.cpp | 3 +- .../raids/ulduar/RaidUlduarTriggers.cpp | 3 +- src/strategy/rogue/RogueActions.cpp | 1 - src/strategy/rogue/RogueTriggers.h | 3 - .../shaman/ElementalShamanStrategy.cpp | 1 - src/strategy/shaman/ShamanActions.cpp | 8 +- src/strategy/shaman/ShamanActions.h | 1 - src/strategy/shaman/ShamanTriggers.cpp | 54 ++++---- src/strategy/shaman/ShamanTriggers.h | 2 +- src/strategy/triggers/GenericTriggers.h | 2 +- src/strategy/triggers/PvpTriggers.cpp | 1 - src/strategy/triggers/RangeTriggers.cpp | 9 +- src/strategy/triggers/TriggerContext.h | 1 - .../triggers/WorldPacketTriggerContext.h | 1 - src/strategy/values/EnemyPlayerValue.cpp | 2 +- src/strategy/values/HasTotemValue.cpp | 3 +- src/strategy/values/ItemUsageValue.cpp | 10 +- .../values/PossibleRpgTargetsValue.cpp | 22 ++-- src/strategy/warlock/WarlockTriggers.h | 1 - src/strategy/warrior/WarriorActions.cpp | 4 +- src/strategy/warrior/WarriorTriggers.cpp | 1 - src/strategy/warrior/WarriorTriggers.h | 1 - 149 files changed, 451 insertions(+), 565 deletions(-) rename var/.suppress.cppcheck => .suppress.cppcheck (100%) rename apps/{ => codestyle}/codestyle-cpp.py (100%) diff --git a/.github/workflows/codestyle_cpp.yml b/.github/workflows/codestyle_cpp.yml index 0afaf4b795..b868e30b18 100644 --- a/.github/workflows/codestyle_cpp.yml +++ b/.github/workflows/codestyle_cpp.yml @@ -14,7 +14,7 @@ jobs: triage: runs-on: ubuntu-latest name: C++ - if: github.repository == 'mod-playerbots/mod-playerbots' && !github.event.pull_request.draft + if: github.event.pull_request.draft == false steps: - uses: actions/checkout@v4 - name: Setup python diff --git a/var/.suppress.cppcheck b/.suppress.cppcheck similarity index 100% rename from var/.suppress.cppcheck rename to .suppress.cppcheck diff --git a/apps/codestyle-cpp.py b/apps/codestyle/codestyle-cpp.py similarity index 100% rename from apps/codestyle-cpp.py rename to apps/codestyle/codestyle-cpp.py diff --git a/src/AiFactory.cpp b/src/AiFactory.cpp index 3ec1418aab..5d6377f466 100644 --- a/src/AiFactory.cpp +++ b/src/AiFactory.cpp @@ -413,10 +413,12 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa break; } - if (PlayerbotAI::IsTank(player, true)) { + if (PlayerbotAI::IsTank(player, true)) + { engine->addStrategy("tank face", false); } - if (PlayerbotAI::IsMelee(player, true) && PlayerbotAI::IsDps(player, true)) { + if (PlayerbotAI::IsMelee(player, true) && PlayerbotAI::IsDps(player, true)) + { engine->addStrategy("behind", false); } if (PlayerbotAI::IsHeal(player, true)) @@ -706,7 +708,9 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const // { // // nonCombatEngine->addStrategy("travel"); // nonCombatEngine->addStrategy("rpg"); - // } else { + // } + // else + // { // nonCombatEngine->addStrategy("move random"); // } diff --git a/src/BroadcastHelper.cpp b/src/BroadcastHelper.cpp index 85bc52218c..19979d3d9f 100644 --- a/src/BroadcastHelper.cpp +++ b/src/BroadcastHelper.cpp @@ -70,7 +70,7 @@ bool BroadcastHelper::BroadcastToChannelWithGlobalChance(PlayerbotAI* ai, std::s return false; } - for (const auto& pair : toChannels) + for (auto const& pair : toChannels) { uint32 roll = urand(1, 100); uint32 chance = pair.second; @@ -166,7 +166,7 @@ bool BroadcastHelper::BroadcastToChannelWithGlobalChance(PlayerbotAI* ai, std::s return false; } -bool BroadcastHelper::BroadcastLootingItem(PlayerbotAI* ai, Player* bot, const ItemTemplate *proto) +bool BroadcastHelper::BroadcastLootingItem(PlayerbotAI* ai, Player* bot, ItemTemplate const* proto) { if (!sPlayerbotAIConfig->enableBroadcasts) return false; @@ -410,7 +410,6 @@ bool BroadcastHelper::BroadcastQuestUpdateComplete(PlayerbotAI* ai, Player* bot, placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace()); placeholders["%my_level"] = std::to_string(bot->GetLevel()); - return BroadcastToChannelWithGlobalChance( ai, BOT_TEXT2("broadcast_quest_update_complete", placeholders), diff --git a/src/ChatFilter.cpp b/src/ChatFilter.cpp index 9a6b67002b..6025a56274 100644 --- a/src/ChatFilter.cpp +++ b/src/ChatFilter.cpp @@ -373,7 +373,7 @@ class SpecChatFilter : public ChatFilter bool ParseSpecPrefix(const std::string& message, std::string& specPrefix, std::string& rest) { std::string msgLower = ToLower(message); - for (const auto& entry : specTabNames) + for (auto const& entry : specTabNames) { std::string prefix = "@" + entry.second; if (msgLower.find(ToLower(prefix)) == 0) @@ -555,7 +555,7 @@ class AggroByChatFilter : public ChatFilter const float radius = 100.0f; GuidVector npcs = botAI->GetAiObjectContext()->GetValue("nearest npcs")->Get(); bool match = false; - for (const auto& guid : npcs) + for (auto const& guid : npcs) { Creature* c = botAI->GetCreature(guid); if (!c) diff --git a/src/ChatHelper.cpp b/src/ChatHelper.cpp index d1a5050960..bf28874700 100644 --- a/src/ChatHelper.cpp +++ b/src/ChatHelper.cpp @@ -329,9 +329,11 @@ ItemWithRandomProperty ChatHelper::parseItemWithRandomProperty(std::string const while (currentPos < text.length()) { size_t nextColon = text.find(':', currentPos); - if (nextColon == std::string::npos) { + if (nextColon == std::string::npos) + { size_t hTag = text.find("|h", currentPos); - if (hTag != std::string::npos) { + if (hTag != std::string::npos) + { params.push_back(text.substr(currentPos, hTag - currentPos)); } break; @@ -341,7 +343,8 @@ ItemWithRandomProperty ChatHelper::parseItemWithRandomProperty(std::string const currentPos = nextColon + 1; } - if (params.size() >= 6) { + if (params.size() >= 6) + { res.randomPropertyId = atoi(params[5].c_str()); } diff --git a/src/GuildTaskMgr.cpp b/src/GuildTaskMgr.cpp index a689abdb86..37589ebd6b 100644 --- a/src/GuildTaskMgr.cpp +++ b/src/GuildTaskMgr.cpp @@ -1067,7 +1067,7 @@ void GuildTaskMgr::SendCompletionMessage(Player* player, std::string const verb) void GuildTaskMgr::CheckKillTaskInternal(Player* player, Unit* victim) { ObjectGuid::LowType owner = player->GetGUID().GetCounter(); - if (victim->GetTypeId() != TYPEID_UNIT) + if (victim->IsCreature()) return; Creature* creature = reinterpret_cast(victim); diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index f014151ec1..414b4b2275 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -1442,22 +1442,22 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster) switch (mapId) { case 249: - strategyName = "onyxia"; // Onyxia's Lair + strategyName = "onyxia"; // Onyxia's Lair break; case 409: - strategyName = "mc"; // Molten Core + strategyName = "mc"; // Molten Core break; case 469: - strategyName = "bwl"; // Blackwing Lair + strategyName = "bwl"; // Blackwing Lair break; case 509: - strategyName = "aq20"; // Ruins of Ahn'Qiraj + strategyName = "aq20"; // Ruins of Ahn'Qiraj break; case 532: strategyName = "karazhan"; // Karazhan break; case 533: - strategyName = "naxx"; // Naxxramas + strategyName = "naxx"; // Naxxramas break; case 544: strategyName = "magtheridon"; // Magtheridon's Lair @@ -3097,8 +3097,10 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell, SpellCastResult result = spell->CheckCast(true); delete spell; - // if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) { - // if (result != SPELL_FAILED_NOT_READY && result != SPELL_CAST_OK) { + // if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) + // { + // if (result != SPELL_FAILED_NOT_READY && result != SPELL_CAST_OK) + // { // LOG_DEBUG("playerbots", "CanCastSpell - target name: {}, spellid: {}, bot name: {}, result: {}", // target->GetName(), spellid, bot->GetName(), result); // } @@ -3121,7 +3123,6 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell, default: if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) { - // if (result != SPELL_FAILED_NOT_READY && result != SPELL_CAST_OK) { LOG_DEBUG("playerbots", "CanCastSpell Check Failed. - target name: {}, spellid: {}, bot name: {}, result: {}", target->GetName(), spellid, bot->GetName(), result); @@ -3304,7 +3305,8 @@ bool PlayerbotAI::CastSpell(uint32 spellId, Unit* target, Item* itemTarget) if (bot->IsFlying() || bot->HasUnitState(UNIT_STATE_IN_FLIGHT)) { - // if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) { + // if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) + // { // LOG_DEBUG("playerbots", "Spell cast is flying - target name: {}, spellid: {}, bot name: {}}", // target->GetName(), spellId, bot->GetName()); // } @@ -3349,7 +3351,8 @@ bool PlayerbotAI::CastSpell(uint32 spellId, Unit* target, Item* itemTarget) { bot->GetTradeData()->SetSpell(spellId); delete spell; - // if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) { + // if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) + // { // LOG_DEBUG("playerbots", "Spell cast no item - target name: {}, spellid: {}, bot name: {}", // target->GetName(), spellId, bot->GetName()); // } @@ -3418,7 +3421,8 @@ bool PlayerbotAI::CastSpell(uint32 spellId, Unit* target, Item* itemTarget) // spell->m_targets.SetUnitTarget(target); // SpellCastResult spellSuccess = spell->CheckCast(true); - // if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) { + // if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) + // { // LOG_DEBUG("playerbots", "Spell cast result - target name: {}, spellid: {}, bot name: {}, result: {}", // target->GetName(), spellId, bot->GetName(), spellSuccess); // } @@ -3429,7 +3433,8 @@ bool PlayerbotAI::CastSpell(uint32 spellId, Unit* target, Item* itemTarget) if (result != SPELL_CAST_OK) { - // if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) { + // if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) + // { // LOG_DEBUG("playerbots", "Spell cast failed. - target name: {}, spellid: {}, bot name: {}, result: {}", // target->GetName(), spellId, bot->GetName(), result); // } @@ -3496,7 +3501,8 @@ bool PlayerbotAI::CastSpell(uint32 spellId, Unit* target, Item* itemTarget) // { // spell->cancel(); // delete spell; - // if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) { + // if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) + // { // LOG_DEBUG("playerbots", "Spell cast loot - target name: {}, spellid: {}, bot name: {}", // target->GetName(), spellId, bot->GetName()); // } @@ -4727,7 +4733,6 @@ uint32 PlayerbotAI::GetEquipGearScore(Player* player) sum += gearScore[i]; } - if (count) { uint32 res = uint32(sum / count); @@ -5264,7 +5269,7 @@ Item* PlayerbotAI::FindOpenableItem() const return FindItemInInventory( [this](ItemTemplate const* itemTemplate) -> bool { - return (itemTemplate->Flags & ITEM_FLAG_HAS_LOOT) && + return itemTemplate->HasFlag(ITEM_FLAG_HAS_LOOT) && (itemTemplate->LockID == 0 || !this->bot->GetItemByEntry(itemTemplate->ItemId)->IsLocked()); }); } @@ -5415,7 +5420,7 @@ Item* PlayerbotAI::FindOilFor(Item* weapon) const if (prioritizedOils) { - for (const auto& id : *prioritizedOils) + for (auto const& id : *prioritizedOils) { oil = FindConsumable(id); if (oil) diff --git a/src/PlayerbotAI.h b/src/PlayerbotAI.h index b5dacd7642..8c8cff14f2 100644 --- a/src/PlayerbotAI.h +++ b/src/PlayerbotAI.h @@ -530,7 +530,7 @@ class PlayerbotAI : public PlayerbotAIBase Player* GetBot() { return bot; } Player* GetMaster() { return master; } Player* FindNewMaster(); - + // Checks if the bot is really a player. Players always have themselves as master. bool IsRealPlayer() { return master ? (master == bot) : false; } // Bot has a master that is a player. diff --git a/src/PlayerbotMgr.cpp b/src/PlayerbotMgr.cpp index b2b1a0f3c9..6bdbf9b659 100644 --- a/src/PlayerbotMgr.cpp +++ b/src/PlayerbotMgr.cpp @@ -1736,7 +1736,8 @@ PlayerbotAI* PlayerbotsMgr::GetPlayerbotAI(Player* player) { return nullptr; } - // if (player->GetSession()->isLogingOut() || player->IsDuringRemoveFromWorld()) { + // if (player->GetSession()->isLogingOut() || player->IsDuringRemoveFromWorld()) + // { // return nullptr; // } auto itr = _playerbotsAIMap.find(player->GetGUID()); diff --git a/src/RandomItemMgr.cpp b/src/RandomItemMgr.cpp index f99c49590b..a6e143ae14 100644 --- a/src/RandomItemMgr.cpp +++ b/src/RandomItemMgr.cpp @@ -982,7 +982,7 @@ void RandomItemMgr::BuildItemInfoCache() continue; } - if (proto->Flags & ITEM_FLAG_DEPRECATED) + if (proto->HasFlag(ITEM_FLAG_DEPRECATED)) { itemForTest.insert(proto->ItemId); continue; @@ -1072,10 +1072,10 @@ void RandomItemMgr::BuildItemInfoCache() // cacheInfo.team = TEAM_NEUTRAL; // // check faction - // if (proto->Flags2 & ITEM_FLAG2_FACTION_HORDE) + // if (proto->HasFlag2(ITEM_FLAG2_FACTION_HORDE)) // cacheInfo.team = TEAM_HORDE; - // if (proto->Flags2 & ITEM_FLAG2_FACTION_ALLIANCE) + // if (proto->HasFlag2(ITEM_FLAG2_FACTION_ALLIANCE)) // cacheInfo.team = TEAM_ALLIANCE; // if (cacheInfo.team == TEAM_NEUTRAL && proto->AllowableRace > 1 && proto->AllowableRace < 8388607) @@ -1099,7 +1099,7 @@ void RandomItemMgr::BuildItemInfoCache() // // check item source - // if (proto->Flags & ITEM_FLAG_NO_DISENCHANT) + // if (proto->HasFlag(ITEM_FLAG_NO_DISENCHANT)) // { // cacheInfo.source = ITEM_SOURCE_PVP; // LOG_DEBUG("playerbots", "Item: {}, source: PvP Reward", proto->ItemId); @@ -1367,7 +1367,7 @@ uint32 RandomItemMgr::CalculateStatWeight(uint8 playerclass, uint8 spec, ItemTem } // check item spells - for (const auto& spellData : proto->Spells) + for (auto const& spellData : proto->Spells) { // no spell if (!spellData.SpellId) @@ -2374,7 +2374,6 @@ void RandomItemMgr::BuildPotionCache() if (proto->Duration & 0x80000000) continue; - if (proto->AllowableClass != -1) continue; diff --git a/src/RandomPlayerbotMgr.cpp b/src/RandomPlayerbotMgr.cpp index 12713cbb34..abd2b40216 100644 --- a/src/RandomPlayerbotMgr.cpp +++ b/src/RandomPlayerbotMgr.cpp @@ -633,7 +633,7 @@ void RandomPlayerbotMgr::AssignAccountTypes() uint32 existingRndBotAccounts = 0; uint32 existingAddClassAccounts = 0; - for (const auto& [accountId, accountType] : currentAssignments) + for (auto const& [accountId, accountType] : currentAssignments) { if (accountType == 1) existingRndBotAccounts++; else if (accountType == 2) existingAddClassAccounts++; @@ -688,7 +688,7 @@ void RandomPlayerbotMgr::AssignAccountTypes() } // Populate filtered account lists with ALL accounts of each type - for (const auto& [accountId, accountType] : currentAssignments) + for (auto const& [accountId, accountType] : currentAssignments) { if (accountType == 1) rndBotTypeAccounts.push_back(accountId); else if (accountType == 2) addClassTypeAccounts.push_back(accountId); @@ -798,7 +798,7 @@ uint32 RandomPlayerbotMgr::AddRandomBots() std::vector allianceChars; std::vector hordeChars; - for (const auto& charInfo : allCharacters) + for (auto const& charInfo : allCharacters) { if (IsAlliance(charInfo.rRace)) allianceChars.push_back(charInfo); @@ -832,7 +832,7 @@ uint32 RandomPlayerbotMgr::AddRandomBots() }; // PHASE 1: Log-in Alliance bots up to allowedAllianceCount - for (const auto& charInfo : allianceChars) + for (auto const& charInfo : allianceChars) { if (!allowedAllianceCount) break; @@ -845,7 +845,7 @@ uint32 RandomPlayerbotMgr::AddRandomBots() } // PHASE 2: Log-in Horde bots up to maxAllowedBotCount - for (const auto& charInfo : hordeChars) + for (auto const& charInfo : hordeChars) { if (!maxAllowedBotCount) break; @@ -855,7 +855,7 @@ uint32 RandomPlayerbotMgr::AddRandomBots() } // PHASE 3: If maxAllowedBotCount wasn't reached, log-in more Alliance bots - for (const auto& charInfo : allianceChars) + for (auto const& charInfo : allianceChars) { if (!maxAllowedBotCount) break; @@ -1248,7 +1248,7 @@ void RandomPlayerbotMgr::CheckBgQueue() void RandomPlayerbotMgr::LogBattlegroundInfo() { - for (const auto& queueTypePair : BattlegroundData) + for (auto const& queueTypePair : BattlegroundData) { uint8 queueType = queueTypePair.first; @@ -1256,7 +1256,7 @@ void RandomPlayerbotMgr::LogBattlegroundInfo() if (uint8 type = BattlegroundMgr::BGArenaType(queueTypeId)) { - for (const auto& bracketIdPair : queueTypePair.second) + for (auto const& bracketIdPair : queueTypePair.second) { auto& bgInfo = bracketIdPair.second; if (bgInfo.minLevel == 0) @@ -1306,7 +1306,7 @@ void RandomPlayerbotMgr::LogBattlegroundInfo() break; } - for (const auto& bracketIdPair : queueTypePair.second) + for (auto const& bracketIdPair : queueTypePair.second) { auto& bgInfo = bracketIdPair.second; if (bgInfo.minLevel == 0) @@ -1576,7 +1576,7 @@ bool RandomPlayerbotMgr::ProcessBot(Player* bot) { idleBot = true; } - + if (idleBot) { // randomize @@ -1704,7 +1704,6 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector& return; } - PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "RandomTeleportByLocations"); std::shuffle(std::begin(tlocs), std::end(tlocs), RandomEngine::Instance()); @@ -2256,7 +2255,7 @@ void RandomPlayerbotMgr::RandomTeleportForLevel(Player* bot) // Pick a weighted city randomly, then a random banker in that city // then teleport to that banker CityId selectedCity = weightedCities[urand(0, weightedCities.size() - 1)]; - const auto& bankers = cityToBankers.at(selectedCity); + auto const& bankers = cityToBankers.at(selectedCity); uint32 selectedBankerEntry = bankers[urand(0, bankers.size() - 1)]; auto locIt = bankerEntryToLocation.find(selectedBankerEntry); if (locIt != bankerEntryToLocation.end()) diff --git a/src/ServerFacade.cpp b/src/ServerFacade.cpp index 61b9b0d43f..f85525bc31 100644 --- a/src/ServerFacade.cpp +++ b/src/ServerFacade.cpp @@ -56,7 +56,7 @@ Unit* ServerFacade::GetChaseTarget(Unit* target) MovementGenerator* movementGen = target->GetMotionMaster()->top(); if (movementGen && movementGen->GetMovementGeneratorType() == CHASE_MOTION_TYPE) { - if (target->GetTypeId() == TYPEID_PLAYER) + if (target->IsPlayer()) { return static_cast const*>(movementGen)->GetTarget(); } diff --git a/src/TravelMgr.cpp b/src/TravelMgr.cpp index f8df8bfea9..8dfbbe4526 100644 --- a/src/TravelMgr.cpp +++ b/src/TravelMgr.cpp @@ -480,7 +480,7 @@ std::string const WorldPosition::getAreaName(bool fullName, bool zoneName) std::set WorldPosition::getTransports(uint32 entry) { /* - if(!entry) + if (!entry) return getMap()->m_transports; else { @@ -488,7 +488,7 @@ std::set WorldPosition::getTransports(uint32 entry) std::set transports; /* for (auto transport : getMap()->m_transports) - if(transport->GetEntry() == entry) + if (transport->GetEntry() == entry) transports.insert(transport); return transports; @@ -1272,7 +1272,7 @@ std::string const RpgTravelDestination::getTitle() { std::ostringstream out; - if(entry > 0) + if (entry > 0) out << "rpg npc "; out << " " << ChatHelper::FormatWorldEntry(entry); @@ -2076,7 +2076,6 @@ void TravelMgr::LoadQuestTravelTable() continue; } - if (r.role == 0) { container->questGivers.push_back(loc); @@ -2591,7 +2590,7 @@ void TravelMgr::LoadQuestTravelTable() //if (data->displayId == 3015) // pos.setZ(pos.getZ() + 6.0f); - //else if(data->displayId == 3031) + //else if (data->displayId == 3031) // pos.setZ(pos.getZ() - 17.0f); if (prevNode) diff --git a/src/TravelNode.cpp b/src/TravelNode.cpp index 8e88006700..d3c03ed97e 100644 --- a/src/TravelNode.cpp +++ b/src/TravelNode.cpp @@ -180,7 +180,7 @@ uint32 TravelNodePath::getPrice() if (!taxiPath) return 0; - + return taxiPath->price; } @@ -470,7 +470,6 @@ bool TravelNode::cropUselessLinks() for (auto& firstLink : getLinks()) { - TravelNode* firstNode = firstLink.first; float firstLength = firstLink.second.getDistance(); for (auto& secondLink : getLinks()) @@ -1302,7 +1301,7 @@ TravelNodeRoute TravelNodeMap::getRoute(TravelNode* start, TravelNode* goal, Pla return TravelNodeRoute(path); } - for (const auto& link : *currentNode->dataNode->getLinks()) // for each successor n' of n + for (auto const& link : *currentNode->dataNode->getLinks()) // for each successor n' of n { TravelNode* linkNode = link.first; float linkCost = link.second->getCost(bot, currentNode->currentGold); @@ -1351,7 +1350,7 @@ TravelNodeRoute TravelNodeMap::getRoute(WorldPosition startPos, WorldPosition en std::vector newStartPath; std::vector startNodes = m_nodes, endNodes = m_nodes; - if(!startNodes.size() || !endNodes.size()) + if (!startNodes.size() || !endNodes.size()) return TravelNodeRoute(); // Partial sort to get the closest 5 nodes at the begin of the array. @@ -1902,7 +1901,7 @@ void TravelNodeMap::generateTransportNodes() // if (data->displayId == 3015) // pos.setZ(pos.getZ() + 6.0f); - // else if(data->displayId == 3031) + // else if (data->displayId == 3031) // pos.setZ(pos.getZ() - 17.0f); if (prevNode) diff --git a/src/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index af1552a3f9..65ffc444ee 100644 --- a/src/factory/PlayerbotFactory.cpp +++ b/src/factory/PlayerbotFactory.cpp @@ -180,7 +180,7 @@ void PlayerbotFactory::Init() continue; } - if (proto->Flags & ITEM_FLAG_UNIQUE_EQUIPPABLE) + if (proto->HasFlag(ITEM_FLAG_UNIQUE_EQUIPPABLE)) { continue; } @@ -1391,7 +1391,7 @@ bool PlayerbotFactory::CanEquipArmor(ItemTemplate const* proto) // for (uint8 j = 0; j < MAX_ITEM_PROTO_STATS; ++j) // { // // for ItemStatValue != 0 - // if(!proto->ItemStat[j].ItemStatValue) + // if (!proto->ItemStat[j].ItemStatValue) // continue; // AddItemStats(proto->ItemStat[j].ItemStatType, sp, ap, tank); @@ -1600,7 +1600,8 @@ void Shuffle(std::vector& items) // bool noItem = false; // uint32 quality = urand(ITEM_QUALITY_UNCOMMON, ITEM_QUALITY_EPIC); // uint32 attempts = 10; -// if (urand(0, 100) < 100 * sPlayerbotAIConfig->randomGearLoweringChance && quality > ITEM_QUALITY_NORMAL) { +// if (urand(0, 100) < 100 * sPlayerbotAIConfig->randomGearLoweringChance && quality > ITEM_QUALITY_NORMAL) +// { // quality--; // } // // current item; @@ -1705,7 +1706,8 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) if (incremental && !sPlayerbotAIConfig->incrementalGearInit) return; - if (level < 5) { + if (level < 5) + { // original items if (CharStartOutfitEntry const* oEntry = GetCharStartOutfitEntry(bot->getRace(), bot->getClass(), bot->getGender())) { @@ -1734,7 +1736,8 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) continue; } - if (bot->HasItemCount(itemId, count)) { + if (bot->HasItemCount(itemId, count)) + { continue; } @@ -2771,7 +2774,8 @@ void PlayerbotFactory::InitTalents(uint32 specNo) void PlayerbotFactory::InitTalentsByTemplate(uint32 specTab) { - // if (sPlayerbotAIConfig->parsedSpecLinkOrder[bot->getClass()][specNo][80].size() == 0) { + // if (sPlayerbotAIConfig->parsedSpecLinkOrder[bot->getClass()][specNo][80].size() == 0) + // { // return; // } uint32 cls = bot->getClass(); diff --git a/src/factory/StatsWeightCalculator.cpp b/src/factory/StatsWeightCalculator.cpp index fa9e8a83b8..8a3048914e 100644 --- a/src/factory/StatsWeightCalculator.cpp +++ b/src/factory/StatsWeightCalculator.cpp @@ -97,7 +97,7 @@ float StatsWeightCalculator::CalculateItem(uint32 itemId, int32 randomPropertyId // Use player level as effective item level for heirlooms - Quality EPIC // Else - Blend with item quality and level for normal items if (proto->Quality == ITEM_QUALITY_HEIRLOOM) - weight_ *= PlayerbotFactory::CalcMixedGearScore(lvl, ITEM_QUALITY_EPIC); + weight_ *= PlayerbotFactory::CalcMixedGearScore(lvl, ITEM_QUALITY_EPIC); else weight_ *= PlayerbotFactory::CalcMixedGearScore(proto->ItemLevel, proto->Quality); diff --git a/src/strategy/Engine.cpp b/src/strategy/Engine.cpp index c1072ff9f6..2e36e05715 100644 --- a/src/strategy/Engine.cpp +++ b/src/strategy/Engine.cpp @@ -77,7 +77,8 @@ Engine::~Engine(void) // for (std::map::iterator i = strategies.begin(); i != strategies.end(); i++) // { // Strategy* strategy = i->second; - // if (strategy) { + // if (strategy) + // { // delete strategy; // } // } @@ -402,7 +403,6 @@ void Engine::addStrategiesNoInit(std::string first, ...) va_end(vl); } - bool Engine::removeStrategy(std::string const name, bool init) { std::map::iterator i = strategies.find(name); diff --git a/src/strategy/NamedObjectContext.h b/src/strategy/NamedObjectContext.h index af8c4ecec6..b60232a5c3 100644 --- a/src/strategy/NamedObjectContext.h +++ b/src/strategy/NamedObjectContext.h @@ -148,7 +148,7 @@ class SharedNamedObjectContextList void Add(NamedObjectContext* context) { contexts.push_back(context); - for (const auto& iter : context->creators) + for (auto const& iter : context->creators) creators[iter.first] = iter.second; } }; @@ -294,7 +294,7 @@ class NamedObjectFactoryList void Add(NamedObjectFactory* context) { factories.push_back(context); - for (const auto& iter : context->creators) + for (auto const& iter : context->creators) creators[iter.first] = iter.second; } diff --git a/src/strategy/actions/AttackAction.cpp b/src/strategy/actions/AttackAction.cpp index 94c9541830..4ea8694378 100644 --- a/src/strategy/actions/AttackAction.cpp +++ b/src/strategy/actions/AttackAction.cpp @@ -161,17 +161,21 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/) bot->Attack(target, shouldMelee); /* prevent pet dead immediately in group */ - // if (bot->GetMap()->IsDungeon() && bot->GetGroup() && !target->IsInCombat()) { + // if (bot->GetMap()->IsDungeon() && bot->GetGroup() && !target->IsInCombat()) + // { // with_pet = false; // } // if (Pet* pet = bot->GetPet()) // { - // if (with_pet) { + // if (with_pet) + // { // pet->SetReactState(REACT_DEFENSIVE); // pet->SetTarget(target->GetGUID()); // pet->GetCharmInfo()->SetIsCommandAttack(true); // pet->AI()->AttackStart(target); - // } else { + // } + // else + // { // pet->SetReactState(REACT_PASSIVE); // pet->GetCharmInfo()->SetIsCommandFollow(true); // pet->GetCharmInfo()->IsReturning(); diff --git a/src/strategy/actions/AutoMaintenanceOnLevelupAction.cpp b/src/strategy/actions/AutoMaintenanceOnLevelupAction.cpp index b83936daeb..ad6a00c6d6 100644 --- a/src/strategy/actions/AutoMaintenanceOnLevelupAction.cpp +++ b/src/strategy/actions/AutoMaintenanceOnLevelupAction.cpp @@ -176,4 +176,3 @@ void AutoMaintenanceOnLevelupAction::AutoUpgradeEquip() factory.InitEquipment(true); } } - diff --git a/src/strategy/actions/BankAction.cpp b/src/strategy/actions/BankAction.cpp index 19804da874..4d8d6c4d8c 100644 --- a/src/strategy/actions/BankAction.cpp +++ b/src/strategy/actions/BankAction.cpp @@ -17,7 +17,7 @@ bool BankAction::Execute(Event event) for (GuidVector::iterator i = npcs.begin(); i != npcs.end(); i++) { Unit* npc = botAI->GetUnit(*i); - if (!npc || !npc->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_BANKER)) + if (!npc || !npc->HasNpcFlag(UNIT_NPC_FLAG_BANKER)) continue; return ExecuteBank(text, npc); diff --git a/src/strategy/actions/BattleGroundTactics.cpp b/src/strategy/actions/BattleGroundTactics.cpp index 5e9e7daf0c..db5235f515 100644 --- a/src/strategy/actions/BattleGroundTactics.cpp +++ b/src/strategy/actions/BattleGroundTactics.cpp @@ -1894,13 +1894,13 @@ bool BGTactics::selectObjective(bool reset) bool isDefender = role < defendersProhab; bool isAdvanced = !isDefender && role > 8; - const auto& attackObjectives = + auto const& attackObjectives = (team == TEAM_HORDE) ? AV_AttackObjectives_Horde : AV_AttackObjectives_Alliance; - const auto& defendObjectives = + auto const& defendObjectives = (team == TEAM_HORDE) ? AV_DefendObjectives_Horde : AV_DefendObjectives_Alliance; uint32 destroyedNodes = 0; - for (const auto& [nodeId, _] : defendObjectives) + for (auto const& [nodeId, _] : defendObjectives) if (av->GetAVNodeInfo(nodeId).State == POINT_DESTROYED) destroyedNodes++; @@ -2000,7 +2000,7 @@ bool BGTactics::selectObjective(bool reset) std::vector contestedObjectives; std::vector availableObjectives; - for (const auto& [nodeId, goId] : defendObjectives) + for (auto const& [nodeId, goId] : defendObjectives) { const BG_AV_NodeInfo& node = av->GetAVNodeInfo(nodeId); if (node.State == POINT_DESTROYED) @@ -2026,7 +2026,7 @@ bool BGTactics::selectObjective(bool reset) if (!BgObjective) { uint32 towersDown = 0; - for (const auto& [nodeId, _] : attackObjectives) + for (auto const& [nodeId, _] : attackObjectives) if (av->GetAVNodeInfo(nodeId).State == POINT_DESTROYED) towersDown++; @@ -2053,7 +2053,7 @@ bool BGTactics::selectObjective(bool reset) { std::vector candidates; - for (const auto& [nodeId, goId] : attackObjectives) + for (auto const& [nodeId, goId] : attackObjectives) { const BG_AV_NodeInfo& node = av->GetAVNodeInfo(nodeId); GameObject* go = bg->GetBGObject(goId); @@ -2105,13 +2105,13 @@ bool BGTactics::selectObjective(bool reset) Position objPos = BgObjective->GetPosition(); Optional linkedNodeId; - for (const auto& [nodeId, goId] : attackObjectives) + for (auto const& [nodeId, goId] : attackObjectives) if (bg->GetBGObject(goId) == BgObjective) linkedNodeId = nodeId; if (!linkedNodeId) { - for (const auto& [nodeId, goId] : defendObjectives) + for (auto const& [nodeId, goId] : defendObjectives) if (bg->GetBGObject(goId) == BgObjective) linkedNodeId = nodeId; } @@ -2543,7 +2543,7 @@ bool BGTactics::selectObjective(bool reset) float bestDist = FLT_MAX; uint32 bestTrigger = 0; - for (const auto& [nodeId, _, areaTrigger] : EY_AttackObjectives) + for (auto const& [nodeId, _, areaTrigger] : EY_AttackObjectives) { if (!IsOwned(nodeId)) continue; @@ -2610,7 +2610,7 @@ bool BGTactics::selectObjective(bool reset) // --- PRIORITY 2: Nearby unowned contested node --- if (!foundObjective) { - for (const auto& [nodeId, _, __] : EY_AttackObjectives) + for (auto const& [nodeId, _, __] : EY_AttackObjectives) { if (IsOwned(nodeId)) continue; @@ -2711,7 +2711,7 @@ bool BGTactics::selectObjective(bool reset) if (!foundObjective && strategy == EY_STRATEGY_FLAG_FOCUS) { bool ownsAny = false; - for (const auto& [nodeId, _, __] : EY_AttackObjectives) + for (auto const& [nodeId, _, __] : EY_AttackObjectives) { if (IsOwned(nodeId)) { @@ -2739,7 +2739,7 @@ bool BGTactics::selectObjective(bool reset) float bestDist = FLT_MAX; Optional bestNode; - for (const auto& [nodeId, _, __] : EY_AttackObjectives) + for (auto const& [nodeId, _, __] : EY_AttackObjectives) { if (IsOwned(nodeId)) continue; @@ -2974,7 +2974,7 @@ bool BGTactics::selectObjective(bool reset) uint32 len = end(IC_AttackObjectives) - begin(IC_AttackObjectives); for (uint32 i = 0; i < len; i++) { - const auto& objective = + auto const& objective = IC_AttackObjectives[(i + role) % len]; // use role to determine which objective checked first if (isleOfConquestBG->GetICNodePoint(objective.first).nodeState != NODE_STATE_CONTROLLED_H) @@ -3126,7 +3126,7 @@ bool BGTactics::selectObjective(bool reset) uint32 len = end(IC_AttackObjectives) - begin(IC_AttackObjectives); for (uint32 i = 0; i < len; i++) { - const auto& objective = + auto const& objective = IC_AttackObjectives[(i + role) % len]; // use role to determine which objective checked first if (isleOfConquestBG->GetICNodePoint(objective.first).nodeState != NODE_STATE_CONTROLLED_H) diff --git a/src/strategy/actions/CheckMountStateAction.cpp b/src/strategy/actions/CheckMountStateAction.cpp index 7bbcd58efb..0366c56d7d 100644 --- a/src/strategy/actions/CheckMountStateAction.cpp +++ b/src/strategy/actions/CheckMountStateAction.cpp @@ -370,7 +370,7 @@ bool CheckMountStateAction::TryPreferredMount(Player* master) const bool CheckMountStateAction::TryRandomMountFiltered(const std::map>& spells, int32 masterSpeed) const { - for (const auto& pair : spells) + for (auto const& pair : spells) { int32 currentSpeed = pair.first; @@ -378,7 +378,7 @@ bool CheckMountStateAction::TryRandomMountFiltered(const std::map 10) { @@ -673,7 +672,7 @@ bool ChooseTravelTargetAction::SetExploreTarget(TravelTarget* target) //271 south shore //35 booty bay //380 The Barrens The Crossroads - if(((ExploreTravelDestination * )activeTarget)->getAreaId() == 380) + if (((ExploreTravelDestination * )activeTarget)->getAreaId() == 380) { activePoints.push_back(activeTarget->getPoints(true)[0]); } diff --git a/src/strategy/actions/DropQuestAction.cpp b/src/strategy/actions/DropQuestAction.cpp index ddcdaef52b..48e571eb3e 100644 --- a/src/strategy/actions/DropQuestAction.cpp +++ b/src/strategy/actions/DropQuestAction.cpp @@ -58,7 +58,6 @@ bool DropQuestAction::Execute(Event event) return true; } - bool CleanQuestLogAction::Execute(Event event) { Player* requester = event.getOwner() ? event.getOwner() : GetMaster(); @@ -165,7 +164,6 @@ bool CleanQuestLogAction::Execute(Event event) return true; } - void CleanQuestLogAction::DropQuestType(uint8& numQuest, uint8 wantNum, bool isGreen, bool hasProgress, bool isComplete) { std::vector slots; diff --git a/src/strategy/actions/EmoteAction.cpp b/src/strategy/actions/EmoteAction.cpp index 72952e66cc..1770ca2b75 100644 --- a/src/strategy/actions/EmoteAction.cpp +++ b/src/strategy/actions/EmoteAction.cpp @@ -837,8 +837,8 @@ uint32 TalkAction::GetRandomEmote(Unit* unit, bool textEmote) types.push_back(TEXT_EMOTE_TALKEX); types.push_back(TEXT_EMOTE_TALKQ); - if (unit && (unit->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_TRAINER) || - unit->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER))) + if (unit && (unit->HasNpcFlag(UNIT_NPC_FLAG_TRAINER) || + unit->HasNpcFlag(UNIT_NPC_FLAG_QUESTGIVER))) { types.push_back(TEXT_EMOTE_SALUTE); } @@ -864,8 +864,8 @@ uint32 TalkAction::GetRandomEmote(Unit* unit, bool textEmote) types.push_back(EMOTE_ONESHOT_EXCLAMATION); types.push_back(EMOTE_ONESHOT_QUESTION); - if (unit && (unit->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_TRAINER) || - unit->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER))) + if (unit && (unit->HasNpcFlag(UNIT_NPC_FLAG_TRAINER) || + unit->HasNpcFlag(UNIT_NPC_FLAG_QUESTGIVER))) { types.push_back(EMOTE_ONESHOT_SALUTE); } diff --git a/src/strategy/actions/EquipAction.cpp b/src/strategy/actions/EquipAction.cpp index f24d89f7bd..71a2df01e6 100644 --- a/src/strategy/actions/EquipAction.cpp +++ b/src/strategy/actions/EquipAction.cpp @@ -328,7 +328,6 @@ void EquipAction::EquipItem(Item* item) botAI->TellMaster(out); } - bool EquipUpgradesAction::Execute(Event event) { if (!sPlayerbotAIConfig->autoEquipUpgradeLoot && !sRandomPlayerbotMgr->IsRandomBot(bot)) @@ -357,9 +356,12 @@ bool EquipUpgradesAction::Execute(Event event) int32 randomProperty = item->GetItemRandomPropertyId(); uint32 itemId = item->GetTemplate()->ItemId; std::string itemUsageParam; - if (randomProperty != 0) { + if (randomProperty != 0) + { itemUsageParam = std::to_string(itemId) + "," + std::to_string(randomProperty); - } else { + } + else + { itemUsageParam = std::to_string(itemId); } ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", itemUsageParam); @@ -388,9 +390,12 @@ bool EquipUpgradeAction::Execute(Event event) int32 randomProperty = item->GetItemRandomPropertyId(); uint32 itemId = item->GetTemplate()->ItemId; std::string itemUsageParam; - if (randomProperty != 0) { + if (randomProperty != 0) + { itemUsageParam = std::to_string(itemId) + "," + std::to_string(randomProperty); - } else { + } + else + { itemUsageParam = std::to_string(itemId); } ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", itemUsageParam); diff --git a/src/strategy/actions/GenericBuffUtils.cpp b/src/strategy/actions/GenericBuffUtils.cpp index 9e227caf15..1ac71bad4e 100644 --- a/src/strategy/actions/GenericBuffUtils.cpp +++ b/src/strategy/actions/GenericBuffUtils.cpp @@ -61,7 +61,7 @@ namespace ai::buff return false; if (SpellInfo const* info = sSpellMgr->GetSpellInfo(spellId)) - { + { for (int i = 0; i < MAX_SPELL_REAGENTS; ++i) { if (info->Reagent[i] > 0) diff --git a/src/strategy/actions/GenericBuffUtils.h b/src/strategy/actions/GenericBuffUtils.h index d9cfc84213..c893de5976 100644 --- a/src/strategy/actions/GenericBuffUtils.h +++ b/src/strategy/actions/GenericBuffUtils.h @@ -29,7 +29,6 @@ namespace ai::buff // Returns false if the spellId is invalid. bool HasRequiredReagents(Player* bot, uint32 spellId); - // Applies the "switch to group buff" policy if: the bot is in a group of size x+, // the group variant is known/useful, and reagents are available. Otherwise, returns baseName. // If announceOnMissing == true and reagents are missing, calls the 'announce' callback diff --git a/src/strategy/actions/GenericSpellActions.cpp b/src/strategy/actions/GenericSpellActions.cpp index 3a36d9ccd0..b30975ad14 100644 --- a/src/strategy/actions/GenericSpellActions.cpp +++ b/src/strategy/actions/GenericSpellActions.cpp @@ -190,8 +190,8 @@ CastEnchantItemAction::CastEnchantItemAction(PlayerbotAI* botAI, std::string con bool CastEnchantItemAction::isPossible() { - // if (!CastSpellAction::isPossible()) { - + // if (!CastSpellAction::isPossible()) + // { // botAI->TellMasterNoFacing("Impossible: " + spell); // return false; // } @@ -364,7 +364,8 @@ bool UseTrinketAction::UseTrinket(Item* item) for (int i = 0; i < MAX_SPELL_EFFECTS; i++) { const SpellEffectInfo& effectInfo = spellInfo->Effects[i]; - if (effectInfo.Effect == SPELL_EFFECT_APPLY_AURA) { + if (effectInfo.Effect == SPELL_EFFECT_APPLY_AURA) + { applyAura = true; break; } diff --git a/src/strategy/actions/GuildManagementActions.cpp b/src/strategy/actions/GuildManagementActions.cpp index af5a1f0172..4ab6d72c59 100644 --- a/src/strategy/actions/GuildManagementActions.cpp +++ b/src/strategy/actions/GuildManagementActions.cpp @@ -193,7 +193,7 @@ bool GuildManageNearbyAction::Execute(Event event) if (guild->GetMemberSize() > 1000) return false; - if ( (guild->GetRankRights(botMember->GetRankId()) & GR_RIGHT_INVITE) == 0) + if ((guild->GetRankRights(botMember->GetRankId()) & GR_RIGHT_INVITE) == 0) continue; if (player->GetGuildIdInvited()) diff --git a/src/strategy/actions/LfgActions.cpp b/src/strategy/actions/LfgActions.cpp index 406a0ec05e..15f8b92f25 100644 --- a/src/strategy/actions/LfgActions.cpp +++ b/src/strategy/actions/LfgActions.cpp @@ -16,7 +16,6 @@ using namespace lfg; - bool LfgJoinAction::Execute(Event event) { return JoinLFG(); } uint32 LfgJoinAction::GetRoles() @@ -113,7 +112,7 @@ bool LfgJoinAction::JoinLFG() dungeon->TypeID != LFG_TYPE_HEROIC && dungeon->TypeID != LFG_TYPE_RAID)) continue; - const auto& botLevel = bot->GetLevel(); + auto const& botLevel = bot->GetLevel(); /*LFG_TYPE_RANDOM on classic is 15-58 so bot over level 25 will never queue*/ if (dungeon->MinLevel && (botLevel < dungeon->MinLevel || botLevel > dungeon->MaxLevel) || @@ -180,7 +179,6 @@ bool LfgRoleCheckAction::Execute(Event event) // if (currentRoles == newRoles) // return false; - WorldPacket* packet = new WorldPacket(CMSG_LFG_SET_ROLES); *packet << (uint8)newRoles; bot->GetSession()->QueuePacket(packet); @@ -267,7 +265,6 @@ bool LfgAcceptAction::Execute(Event event) return false; } - bool LfgLeaveAction::Execute(Event event) { // Don't leave if lfg strategy enabled diff --git a/src/strategy/actions/LootRollAction.cpp b/src/strategy/actions/LootRollAction.cpp index 3b3a4240ec..912f75fddf 100644 --- a/src/strategy/actions/LootRollAction.cpp +++ b/src/strategy/actions/LootRollAction.cpp @@ -41,9 +41,12 @@ bool LootRollAction::Execute(Event event) continue; std::string itemUsageParam; - if (randomProperty != 0) { + if (randomProperty != 0) + { itemUsageParam = std::to_string(itemId) + "," + std::to_string(randomProperty); - } else { + } + else + { itemUsageParam = std::to_string(itemId); } ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", itemUsageParam); @@ -120,7 +123,6 @@ bool LootRollAction::Execute(Event event) return false; } - RollVote LootRollAction::CalculateRollVote(ItemTemplate const* proto) { std::ostringstream out; diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index 0598a39d19..04a89fa108 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -248,7 +248,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // bot->CastStop(); // botAI->InterruptSpell(); // } - + MotionMaster& mm = *bot->GetMotionMaster(); // No ground pathfinding if the bot/master are flying => allow true 3D (Z) movement auto isFlying = [](Unit* u){ return u && (u->HasUnitMovementFlag(MOVEMENTFLAG_FLYING) || u->IsInFlight()); }; @@ -530,7 +530,8 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // { // AI_VALUE(LastMovement&, "last area trigger").lastAreaTrigger = entry; // } - // else { + // else + // { // LOG_DEBUG("playerbots", "!entry"); // return bot->TeleportTo(movePosition.getMapId(), movePosition.getX(), movePosition.getY(), // movePosition.getZ(), movePosition.getO(), 0); @@ -876,7 +877,8 @@ bool MovementAction::ReachCombatTo(Unit* target, float distance) deltaAngle -= 2.0f * M_PI; // -PI..PI // if target is moving forward and moving far away, predict the position bool behind = fabs(deltaAngle) > M_PI_2; - if (target->HasUnitMovementFlag(MOVEMENTFLAG_FORWARD) && behind) { + if (target->HasUnitMovementFlag(MOVEMENTFLAG_FORWARD) && behind) + { float predictDis = std::min(3.0f, target->GetObjectSize() * 2); tx += cos(target->GetOrientation()) * predictDis; ty += sin(target->GetOrientation()) * predictDis; @@ -1009,7 +1011,8 @@ bool MovementAction::IsMovingAllowed() return false; } - // if (bot->HasUnitMovementFlag(MOVEMENTFLAG_FALLING)) { + // if (bot->HasUnitMovementFlag(MOVEMENTFLAG_FALLING)) + // { // return false; // } return bot->GetMotionMaster()->GetCurrentMovementGeneratorType() != FLIGHT_MOTION_TYPE; @@ -1094,9 +1097,12 @@ void MovementAction::UpdateMovementState() wasMovementRestricted = isCurrentlyRestricted; // Temporary speed increase in group - // if (botAI->HasRealPlayerMaster()) { + // if (botAI->HasRealPlayerMaster()) + // { // bot->SetSpeedRate(MOVE_RUN, 1.1f); - // } else { + // } + // else + // { // bot->SetSpeedRate(MOVE_RUN, 1.0f); // } // check if target is not reachable (from Vmangos) @@ -1105,7 +1111,7 @@ void MovementAction::UpdateMovementState() // { // if (Unit* pTarget = sServerFacade->GetChaseTarget(bot)) // { - // if (!bot->IsWithinMeleeRange(pTarget) && pTarget->GetTypeId() == TYPEID_UNIT) + // if (!bot->IsWithinMeleeRange(pTarget) && pTarget->IsCreature()) // { // float angle = bot->GetAngle(pTarget); // float distance = 5.0f; @@ -1721,7 +1727,8 @@ bool MovementAction::MoveInside(uint32 mapId, float x, float y, float z, float d // float MovementAction::SearchBestGroundZForPath(float x, float y, float z, bool generatePath, float range, bool // normal_only, float step) // { -// if (!generatePath) { +// if (!generatePath) +// { // return z; // } // float min_length = 100000.0f; @@ -1732,10 +1739,12 @@ bool MovementAction::MoveInside(uint32 mapId, float x, float y, float z, float d // modified_z = bot->GetMapWaterOrGroundLevel(x, y, z + delta); // PathGenerator gen(bot); // gen.CalculatePath(x, y, modified_z); -// if (gen.GetPathType() == PATHFIND_NORMAL && gen.getPathLength() < min_length) { +// if (gen.GetPathType() == PATHFIND_NORMAL && gen.getPathLength() < min_length) +// { // min_length = gen.getPathLength(); // current_z = modified_z; -// if (abs(current_z - z) < 0.5f) { +// if (abs(current_z - z) < 0.5f) +// { // return current_z; // } // } @@ -1744,10 +1753,12 @@ bool MovementAction::MoveInside(uint32 mapId, float x, float y, float z, float d // modified_z = bot->GetMapWaterOrGroundLevel(x, y, z + delta); // PathGenerator gen(bot); // gen.CalculatePath(x, y, modified_z); -// if (gen.GetPathType() == PATHFIND_NORMAL && gen.getPathLength() < min_length) { +// if (gen.GetPathType() == PATHFIND_NORMAL && gen.getPathLength() < min_length) +// { // min_length = gen.getPathLength(); // current_z = modified_z; -// if (abs(current_z - z) < 0.5f) { +// if (abs(current_z - z) < 0.5f) +// { // return current_z; // } // } @@ -1756,18 +1767,22 @@ bool MovementAction::MoveInside(uint32 mapId, float x, float y, float z, float d // modified_z = bot->GetMapWaterOrGroundLevel(x, y, z + delta); // PathGenerator gen(bot); // gen.CalculatePath(x, y, modified_z); -// if (gen.GetPathType() == PATHFIND_NORMAL && gen.getPathLength() < min_length) { +// if (gen.GetPathType() == PATHFIND_NORMAL && gen.getPathLength() < min_length) +// { // min_length = gen.getPathLength(); // current_z = modified_z; -// if (abs(current_z - z) < 0.5f) { +// if (abs(current_z - z) < 0.5f) +// { // return current_z; // } // } // } -// if (current_z == INVALID_HEIGHT && normal_only) { +// if (current_z == INVALID_HEIGHT && normal_only) +// { // return INVALID_HEIGHT; // } -// if (current_z == INVALID_HEIGHT && !normal_only) { +// if (current_z == INVALID_HEIGHT && !normal_only) +// { // return z; // } // return current_z; @@ -2818,7 +2833,7 @@ bool MoveAwayFromCreatureAction::Execute(Event event) // Find all creatures with the specified Id std::vector creatures; - for (const auto& guid : targets) + for (auto const& guid : targets) { Unit* unit = botAI->GetUnit(guid); if (unit && (alive && unit->IsAlive()) && unit->GetEntry() == creatureId) diff --git a/src/strategy/actions/MovementActions.h b/src/strategy/actions/MovementActions.h index b5f7a38912..f50e4b9570 100644 --- a/src/strategy/actions/MovementActions.h +++ b/src/strategy/actions/MovementActions.h @@ -119,7 +119,6 @@ class AvoidAoeAction : public MovementAction int moveInterval; }; - class CombatFormationMoveAction : public MovementAction { public: diff --git a/src/strategy/actions/NonCombatActions.cpp b/src/strategy/actions/NonCombatActions.cpp index e810ad27cd..f87aa891dd 100644 --- a/src/strategy/actions/NonCombatActions.cpp +++ b/src/strategy/actions/NonCombatActions.cpp @@ -47,16 +47,16 @@ bool DrinkAction::Execute(Event event) return UseItemAction::Execute(event); } -bool DrinkAction::isUseful() -{ - return UseItemAction::isUseful() && +bool DrinkAction::isUseful() +{ + return UseItemAction::isUseful() && AI_VALUE2(bool, "has mana", "self target") && AI_VALUE2(uint8, "mana", "self target") < 100; } bool DrinkAction::isPossible() { - return !bot->IsInCombat() && + return !bot->IsInCombat() && !bot->IsMounted() && !botAI->HasAnyAuraOf(GetTarget(), "dire bear form", "bear form", "cat form", "travel form", "aquatic form","flight form", "swift flight form", nullptr) && @@ -102,15 +102,15 @@ bool EatAction::Execute(Event event) return UseItemAction::Execute(event); } -bool EatAction::isUseful() -{ - return UseItemAction::isUseful() && +bool EatAction::isUseful() +{ + return UseItemAction::isUseful() && AI_VALUE2(uint8, "health", "self target") < 100; } bool EatAction::isPossible() { - return !bot->IsInCombat() && + return !bot->IsInCombat() && !bot->IsMounted() && !botAI->HasAnyAuraOf(GetTarget(), "dire bear form", "bear form", "cat form", "travel form", "aquatic form","flight form", "swift flight form", nullptr) && diff --git a/src/strategy/actions/QuestAction.cpp b/src/strategy/actions/QuestAction.cpp index 39e8e1ba68..b96c539475 100644 --- a/src/strategy/actions/QuestAction.cpp +++ b/src/strategy/actions/QuestAction.cpp @@ -48,7 +48,7 @@ bool QuestAction::Execute(Event event) // Check the nearest NPCs GuidVector npcs = AI_VALUE(GuidVector, "nearest npcs"); - for (const auto& npc : npcs) + for (auto const& npc : npcs) { Unit* unit = botAI->GetUnit(npc); if (unit && bot->GetDistance(unit) <= INTERACTION_DISTANCE) @@ -59,7 +59,7 @@ bool QuestAction::Execute(Event event) // Checks the nearest game objects GuidVector gos = AI_VALUE(GuidVector, "nearest game objects"); - for (const auto& go : gos) + for (auto const& go : gos) { GameObject* gameobj = botAI->GetGameObject(go); if (gameobj && bot->GetDistance(gameobj) <= INTERACTION_DISTANCE) @@ -359,7 +359,7 @@ bool QuestUpdateAddItemAction::Execute(Event event) uint32 availableItemsCount = botAI->GetInventoryItemsCountWithId(itemId); placeholders["%quest_obj_available"] = std::to_string(availableItemsCount); - for (const auto& pair : botAI->GetCurrentQuestsRequiringItemId(itemId)) + for (auto const& pair : botAI->GetCurrentQuestsRequiringItemId(itemId)) { placeholders["%quest_link"] = chat->FormatQuest(pair.first); uint32 requiredItemsCount = pair.second; diff --git a/src/strategy/actions/QuestAction.h b/src/strategy/actions/QuestAction.h index b9ca10e54b..c19faf9ece 100644 --- a/src/strategy/actions/QuestAction.h +++ b/src/strategy/actions/QuestAction.h @@ -49,7 +49,7 @@ class QuestUpdateAddItemAction : public Action { public: QuestUpdateAddItemAction(PlayerbotAI* ai) : Action(ai, "quest update add item") {} - bool Execute(Event event) override;; + bool Execute(Event event) override; }; class QuestUpdateFailedAction : public Action @@ -70,7 +70,7 @@ class QuestItemPushResultAction : public Action { public: QuestItemPushResultAction(PlayerbotAI* ai) : Action(ai, "quest item push result") {} - bool Execute(Event event) override;; + bool Execute(Event event) override; }; #endif diff --git a/src/strategy/actions/ReleaseSpiritAction.cpp b/src/strategy/actions/ReleaseSpiritAction.cpp index 666e42aba3..b06fc89263 100644 --- a/src/strategy/actions/ReleaseSpiritAction.cpp +++ b/src/strategy/actions/ReleaseSpiritAction.cpp @@ -128,7 +128,7 @@ bool AutoReleaseSpiritAction::HandleBattlegroundSpiritHealer() GuidVector npcs = NearestNpcsValue(botAI, bgRange); Unit* spiritHealer = nullptr; - for (const auto& guid : npcs) + for (auto const& guid : npcs) { Unit* unit = botAI->GetUnit(guid); if (unit && unit->IsFriendlyTo(bot) && unit->IsSpiritService()) diff --git a/src/strategy/actions/RevealGatheringItemAction.cpp b/src/strategy/actions/RevealGatheringItemAction.cpp index 00f1cc1db4..9725dce5ce 100644 --- a/src/strategy/actions/RevealGatheringItemAction.cpp +++ b/src/strategy/actions/RevealGatheringItemAction.cpp @@ -12,24 +12,7 @@ #include "GridNotifiersImpl.h" #include "Playerbots.h" #include "ServerFacade.h" - -class AnyGameObjectInObjectRangeCheck -{ -public: - AnyGameObjectInObjectRangeCheck(WorldObject const* obj, float range) : i_obj(obj), i_range(range) {} - WorldObject const& GetFocusObject() const { return *i_obj; } - bool operator()(GameObject* go) - { - if (go && i_obj->IsWithinDistInMap(go, i_range) && go->isSpawned() && go->GetGOInfo()) - return true; - - return false; - } - -private: - WorldObject const* i_obj; - float i_range; -}; +#include "NearestGameObjects.h" bool RevealGatheringItemAction::Execute(Event event) { diff --git a/src/strategy/actions/ReviveFromCorpseAction.cpp b/src/strategy/actions/ReviveFromCorpseAction.cpp index 83897dc2ab..74237670df 100644 --- a/src/strategy/actions/ReviveFromCorpseAction.cpp +++ b/src/strategy/actions/ReviveFromCorpseAction.cpp @@ -314,7 +314,7 @@ bool SpiritHealerAction::Execute(Event event) for (GuidVector::iterator i = npcs.begin(); i != npcs.end(); i++) { Unit* unit = botAI->GetUnit(*i); - if (unit && unit->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPIRITHEALER)) + if (unit && unit->HasNpcFlag(UNIT_NPC_FLAG_SPIRITHEALER)) { LOG_DEBUG("playerbots", "Bot {} {}:{} <{}> revives at spirit healer", bot->GetGUID().ToString().c_str(), bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(), bot->GetName()); diff --git a/src/strategy/actions/RpgAction.cpp b/src/strategy/actions/RpgAction.cpp index 21b68d6bf8..ae6a50fddb 100644 --- a/src/strategy/actions/RpgAction.cpp +++ b/src/strategy/actions/RpgAction.cpp @@ -50,7 +50,6 @@ bool RpgAction::SetNextRpgAction() std::vector relevances; std::vector triggerNodes; - for (auto& strategy : botAI->GetAiObjectContext()->GetSupportedStrategies()) { if (strategy.find("rpg") == std::string::npos) diff --git a/src/strategy/actions/SayAction.cpp b/src/strategy/actions/SayAction.cpp index 474f37e4ce..00fae65293 100644 --- a/src/strategy/actions/SayAction.cpp +++ b/src/strategy/actions/SayAction.cpp @@ -195,7 +195,7 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32& type, uint32& guid1, uint } ChatChannelSource chatChannelSource = GET_PLAYERBOT_AI(bot)->GetChatChannelSource(bot, type, chanName); - if ( (msg.starts_with("LFG") || msg.starts_with("LFM")) && HandleLFGQuestsReply(bot, chatChannelSource, msg, name)) + if ((msg.starts_with("LFG") || msg.starts_with("LFM")) && HandleLFGQuestsReply(bot, chatChannelSource, msg, name)) { return; } diff --git a/src/strategy/actions/SetHomeAction.cpp b/src/strategy/actions/SetHomeAction.cpp index 36893f4a8c..44f3c7b49f 100644 --- a/src/strategy/actions/SetHomeAction.cpp +++ b/src/strategy/actions/SetHomeAction.cpp @@ -24,7 +24,7 @@ bool SetHomeAction::Execute(Event event) } if (Unit* unit = botAI->GetUnit(selection)) - if (unit->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_INNKEEPER)) + if (unit->HasNpcFlag(UNIT_NPC_FLAG_INNKEEPER)) { if (isRpgAction) { diff --git a/src/strategy/actions/SuggestWhatToDoAction.cpp b/src/strategy/actions/SuggestWhatToDoAction.cpp index 468b1f5077..96cc7f52ac 100644 --- a/src/strategy/actions/SuggestWhatToDoAction.cpp +++ b/src/strategy/actions/SuggestWhatToDoAction.cpp @@ -120,7 +120,8 @@ void SuggestWhatToDoAction::grindMaterials() for (std::map::iterator i = categories.begin(); i != categories.end(); ++i) { - if (urand(0, 10) < 3) { + if (urand(0, 10) < 3) + { std::string name = i->first; double multiplier = i->second; diff --git a/src/strategy/actions/TameAction.cpp b/src/strategy/actions/TameAction.cpp index b414b47806..5b3eda1034 100644 --- a/src/strategy/actions/TameAction.cpp +++ b/src/strategy/actions/TameAction.cpp @@ -81,7 +81,7 @@ bool TameAction::Execute(Event event) std::ostringstream oss; oss << "Available pet families: "; size_t count = 0; - for (const auto& name : normalFamilies) + for (auto const& name : normalFamilies) { if (count++ != 0) oss << ", "; @@ -93,7 +93,7 @@ bool TameAction::Execute(Event event) oss << " | "; oss << "Exotic: "; count = 0; - for (const auto& name : exoticFamilies) + for (auto const& name : exoticFamilies) { if (count++ != 0) oss << ", "; diff --git a/src/strategy/actions/TellGlyphsAction.h b/src/strategy/actions/TellGlyphsAction.h index 6f3e0dace3..77bb5b1ca5 100644 --- a/src/strategy/actions/TellGlyphsAction.h +++ b/src/strategy/actions/TellGlyphsAction.h @@ -18,4 +18,3 @@ class TellGlyphsAction : public Action }; #endif - diff --git a/src/strategy/actions/TradeAction.cpp b/src/strategy/actions/TradeAction.cpp index 9ac7aca2be..ad81d9f93b 100644 --- a/src/strategy/actions/TradeAction.cpp +++ b/src/strategy/actions/TradeAction.cpp @@ -15,7 +15,7 @@ bool TradeAction::Execute(Event event) std::string const text = event.getParam(); // If text starts with any excluded prefix, don't process it further. - for (const auto& prefix : sPlayerbotAIConfig->tradeActionExcludedPrefixes) + for (auto const& prefix : sPlayerbotAIConfig->tradeActionExcludedPrefixes) { if (text.find(prefix) == 0) return false; diff --git a/src/strategy/actions/TrainerAction.cpp b/src/strategy/actions/TrainerAction.cpp index c5509cf8b8..e2e89115b9 100644 --- a/src/strategy/actions/TrainerAction.cpp +++ b/src/strategy/actions/TrainerAction.cpp @@ -172,7 +172,7 @@ bool MaintenanceAction::Execute(Event event) PlayerbotFactory factory(bot, bot->GetLevel()); if (!botAI->IsAlt()) - { + { factory.InitAttunementQuests(); factory.InitBags(false); factory.InitAmmo(); @@ -194,7 +194,7 @@ bool MaintenanceAction::Execute(Event event) if (bot->GetLevel() >= sPlayerbotAIConfig->minEnchantingBotLevel) factory.ApplyEnchantAndGemsNew(); } - else + else { if (sPlayerbotAIConfig->altMaintenanceAttunementQs) factory.InitAttunementQuests(); diff --git a/src/strategy/actions/UseMeetingStoneAction.cpp b/src/strategy/actions/UseMeetingStoneAction.cpp index 24d58aff30..6e902730b8 100644 --- a/src/strategy/actions/UseMeetingStoneAction.cpp +++ b/src/strategy/actions/UseMeetingStoneAction.cpp @@ -9,6 +9,7 @@ #include "Event.h" #include "GridNotifiers.h" #include "GridNotifiersImpl.h" +#include "NearestGameObjects.h" #include "PlayerbotAIConfig.h" #include "Playerbots.h" #include "PositionValue.h" @@ -54,24 +55,6 @@ bool UseMeetingStoneAction::Execute(Event event) return Teleport(master, bot); } -class AnyGameObjectInObjectRangeCheck -{ -public: - AnyGameObjectInObjectRangeCheck(WorldObject const* obj, float range) : i_obj(obj), i_range(range) {} - WorldObject const& GetFocusObject() const { return *i_obj; } - bool operator()(GameObject* go) - { - if (go && i_obj->IsWithinDistInMap(go, i_range) && go->isSpawned() && go->GetGOInfo()) - return true; - - return false; - } - -private: - WorldObject const* i_obj; - float i_range; -}; - bool SummonAction::Execute(Event event) { Player* master = GetMaster(); @@ -134,7 +117,7 @@ bool SummonAction::SummonUsingNpcs(Player* summoner, Player* player) for (Unit* unit : targets) { - if (unit && unit->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_INNKEEPER)) + if (unit && unit->HasNpcFlag(UNIT_NPC_FLAG_INNKEEPER)) { if (!player->HasItemCount(6948, 1, false)) { @@ -242,7 +225,7 @@ bool SummonAction::Teleport(Player* summoner, Player* player) } } - if(summoner != player) + if (summoner != player) botAI->TellError("Not enough place to summon"); return false; } diff --git a/src/strategy/actions/WorldBuffAction.cpp b/src/strategy/actions/WorldBuffAction.cpp index b47083850a..3cd770e4d0 100644 --- a/src/strategy/actions/WorldBuffAction.cpp +++ b/src/strategy/actions/WorldBuffAction.cpp @@ -97,4 +97,3 @@ std::vector WorldBuffAction::NeedWorldBuffs(Unit* unit) return retVec; } - diff --git a/src/strategy/actions/XpGainAction.cpp b/src/strategy/actions/XpGainAction.cpp index 446be3bb6d..4048ce3af8 100644 --- a/src/strategy/actions/XpGainAction.cpp +++ b/src/strategy/actions/XpGainAction.cpp @@ -15,7 +15,6 @@ bool XpGainAction::Execute(Event event) { context->GetValue("death count")->Set(0); - WorldPacket p(event.getPacket()); // (8+4+1+4+8) ObjectGuid guid; // uint32 xpgain; diff --git a/src/strategy/deathknight/DKActions.cpp b/src/strategy/deathknight/DKActions.cpp index 5206103617..2b5ce97706 100644 --- a/src/strategy/deathknight/DKActions.cpp +++ b/src/strategy/deathknight/DKActions.cpp @@ -43,7 +43,7 @@ bool CastRaiseDeadAction::Execute(Event event) return false; } uint32 spellId = AI_VALUE2(uint32, "spell id", spell); - // const SpellInfo *spellInfo = sSpellMgr->GetSpellInfo(spellId); + // SpellInfo const *spellInfo = sSpellMgr->GetSpellInfo(spellId); bot->AddSpellCooldown(spellId, 0, 3 * 60 * 1000); return true; -} \ No newline at end of file +} diff --git a/src/strategy/druid/DruidActions.h b/src/strategy/druid/DruidActions.h index f7a4ef32ce..402073d26d 100644 --- a/src/strategy/druid/DruidActions.h +++ b/src/strategy/druid/DruidActions.h @@ -311,7 +311,6 @@ class CastEnrageAction : public CastBuffSpellAction CastEnrageAction(PlayerbotAI* ai) : CastBuffSpellAction(ai, "enrage") {} }; - class CastRejuvenationOnNotFullAction : public HealPartyMemberAction { public: diff --git a/src/strategy/dungeons/DungeonStrategyContext.h b/src/strategy/dungeons/DungeonStrategyContext.h index ce982b148f..cc606afef8 100644 --- a/src/strategy/dungeons/DungeonStrategyContext.h +++ b/src/strategy/dungeons/DungeonStrategyContext.h @@ -35,8 +35,6 @@ Bronjahm, Devourer of Souls */ - - class DungeonStrategyContext : public NamedObjectContext { public: @@ -84,8 +82,6 @@ class DungeonStrategyContext : public NamedObjectContext static Strategy* wotlk_toc(PlayerbotAI* botAI) { return new WotlkDungeonToCStrategy(botAI); } // NYI from here down static Strategy* wotlk_hor(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } - - }; #endif diff --git a/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h b/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h index 057d25e020..1a3d7a94a3 100644 --- a/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h +++ b/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h @@ -18,5 +18,4 @@ #include "trialofthechampion/TrialOfTheChampionTriggerContext.h" // #include "hallsofreflection/HallsOfReflectionTriggerContext.h" - #endif diff --git a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActions.cpp b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActions.cpp index 2b7e7c9f13..e31ffd00f5 100644 --- a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActions.cpp +++ b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActions.cpp @@ -2,7 +2,6 @@ #include "AzjolNerubActions.h" #include "AzjolNerubStrategy.h" - bool AttackWebWrapAction::isUseful() { return !botAI->IsHeal(bot); } bool AttackWebWrapAction::Execute(Event event) { diff --git a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubStrategy.cpp b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubStrategy.cpp index dcefbae507..1a30e98ce8 100644 --- a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubStrategy.cpp +++ b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubStrategy.cpp @@ -1,7 +1,6 @@ #include "AzjolNerubStrategy.h" #include "AzjolNerubMultipliers.h" - void WotlkDungeonANStrategy::InitTriggers(std::vector &triggers) { // Krik'thir the Gatewatcher diff --git a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubStrategy.h b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubStrategy.h index bf5a7917a2..70196e6e41 100644 --- a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubStrategy.h +++ b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubStrategy.h @@ -5,7 +5,6 @@ #include "AiObjectContext.h" #include "Strategy.h" - class WotlkDungeonANStrategy : public Strategy { public: diff --git a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubTriggers.cpp b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubTriggers.cpp index df52374c4d..f9ef4ff953 100644 --- a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubTriggers.cpp +++ b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubTriggers.cpp @@ -3,7 +3,6 @@ #include "AiObject.h" #include "AiObjectContext.h" - bool KrikthirWebWrapTrigger::IsActive() { if (!botAI->IsDps(bot)) { return false; } diff --git a/src/strategy/dungeons/wotlk/cullingofstratholme/CullingOfStratholmeActions.cpp b/src/strategy/dungeons/wotlk/cullingofstratholme/CullingOfStratholmeActions.cpp index dd857569a0..15121f4a2e 100644 --- a/src/strategy/dungeons/wotlk/cullingofstratholme/CullingOfStratholmeActions.cpp +++ b/src/strategy/dungeons/wotlk/cullingofstratholme/CullingOfStratholmeActions.cpp @@ -2,7 +2,6 @@ #include "CullingOfStratholmeActions.h" #include "CullingOfStratholmeStrategy.h" - bool ExplodeGhoulSpreadAction::Execute(Event event) { Unit* boss = AI_VALUE2(Unit*, "find target", "salramm the fleshcrafter"); @@ -32,7 +31,7 @@ bool EpochStackAction::isUseful() // Hunter bots will try and melee in between ranged attacks, or just melee entirely at 5 as they are in range. // 7.5 or 8.0 solves this for this boss. // Unfortunately at this range the boss will charge. So I guess just don't stack as a hunter.. - // if(bot->getClass() == CLASS_HUNTER) + // if (bot->getClass() == CLASS_HUNTER) // { // return AI_VALUE2(float, "distance", "current target") > 7.5f; // } @@ -45,7 +44,7 @@ bool EpochStackAction::Execute(Event event) if (!boss) { return false; } float maxMovement = 10.0f; - // if(bot->getClass() == CLASS_HUNTER) + // if (bot->getClass() == CLASS_HUNTER) // { // return Move(bot->GetAngle(boss), fmin(bot->GetExactDist2d(boss) - 6.5f, maxMovement)); // } diff --git a/src/strategy/dungeons/wotlk/cullingofstratholme/CullingOfStratholmeStrategy.cpp b/src/strategy/dungeons/wotlk/cullingofstratholme/CullingOfStratholmeStrategy.cpp index b9081ba82c..e9011ee7ff 100644 --- a/src/strategy/dungeons/wotlk/cullingofstratholme/CullingOfStratholmeStrategy.cpp +++ b/src/strategy/dungeons/wotlk/cullingofstratholme/CullingOfStratholmeStrategy.cpp @@ -1,7 +1,6 @@ #include "CullingOfStratholmeStrategy.h" #include "CullingOfStratholmeMultipliers.h" - void WotlkDungeonCoSStrategy::InitTriggers(std::vector &triggers) { // Meathook diff --git a/src/strategy/dungeons/wotlk/cullingofstratholme/CullingOfStratholmeStrategy.h b/src/strategy/dungeons/wotlk/cullingofstratholme/CullingOfStratholmeStrategy.h index 9c687da9c5..bff1267435 100644 --- a/src/strategy/dungeons/wotlk/cullingofstratholme/CullingOfStratholmeStrategy.h +++ b/src/strategy/dungeons/wotlk/cullingofstratholme/CullingOfStratholmeStrategy.h @@ -5,7 +5,6 @@ #include "AiObjectContext.h" #include "Strategy.h" - class WotlkDungeonCoSStrategy : public Strategy { public: diff --git a/src/strategy/dungeons/wotlk/cullingofstratholme/CullingOfStratholmeTriggers.cpp b/src/strategy/dungeons/wotlk/cullingofstratholme/CullingOfStratholmeTriggers.cpp index a2b7dc11cc..cb67ad077f 100644 --- a/src/strategy/dungeons/wotlk/cullingofstratholme/CullingOfStratholmeTriggers.cpp +++ b/src/strategy/dungeons/wotlk/cullingofstratholme/CullingOfStratholmeTriggers.cpp @@ -3,7 +3,6 @@ #include "AiObject.h" #include "AiObjectContext.h" - bool ExplodeGhoulTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "salramm the fleshcrafter"); diff --git a/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepActions.cpp b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepActions.cpp index 0ab72c1bb0..8cbbe49c2a 100644 --- a/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepActions.cpp +++ b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepActions.cpp @@ -2,7 +2,6 @@ #include "DrakTharonKeepActions.h" #include "DrakTharonKeepStrategy.h" - bool CorpseExplodeSpreadAction::Execute(Event event) { Unit* boss = AI_VALUE2(Unit*, "find target", "trollgore"); diff --git a/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepStrategy.cpp b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepStrategy.cpp index 4166f2f5b3..a32d523135 100644 --- a/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepStrategy.cpp +++ b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepStrategy.cpp @@ -1,7 +1,6 @@ #include "DrakTharonKeepStrategy.h" #include "DrakTharonKeepMultipliers.h" - void WotlkDungeonDTKStrategy::InitTriggers(std::vector &triggers) { // Trollgore diff --git a/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepStrategy.h b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepStrategy.h index b819ad3809..41ef0ced6f 100644 --- a/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepStrategy.h +++ b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepStrategy.h @@ -5,7 +5,6 @@ #include "AiObjectContext.h" #include "Strategy.h" - class WotlkDungeonDTKStrategy : public Strategy { public: diff --git a/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepTriggers.cpp b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepTriggers.cpp index 9ed4b19121..406f4d4333 100644 --- a/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepTriggers.cpp +++ b/src/strategy/dungeons/wotlk/draktharonkeep/DrakTharonKeepTriggers.cpp @@ -3,7 +3,6 @@ #include "AiObject.h" #include "AiObjectContext.h" - bool CorpseExplodeTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "trollgore"); diff --git a/src/strategy/dungeons/wotlk/forgeofsouls/ForgeOfSoulsActions.cpp b/src/strategy/dungeons/wotlk/forgeofsouls/ForgeOfSoulsActions.cpp index 3bd21f34d1..e72fe98d5f 100644 --- a/src/strategy/dungeons/wotlk/forgeofsouls/ForgeOfSoulsActions.cpp +++ b/src/strategy/dungeons/wotlk/forgeofsouls/ForgeOfSoulsActions.cpp @@ -17,7 +17,6 @@ bool MoveFromBronjahmAction::Execute(Event event) return false; } - bool AttackCorruptedSoulFragmentAction::Execute(Event event) { Unit* currentTarget = AI_VALUE(Unit*, "current target"); @@ -52,7 +51,6 @@ bool AttackCorruptedSoulFragmentAction::Execute(Event event) return false; } - bool BronjahmGroupPositionAction::Execute(Event event) { Unit* boss = AI_VALUE2(Unit*, "find target", "bronjahm"); @@ -165,5 +163,3 @@ bool DevourerOfSoulsAction::Execute(Event event) return false; } - - diff --git a/src/strategy/dungeons/wotlk/forgeofsouls/ForgeOfSoulsMultipliers.cpp b/src/strategy/dungeons/wotlk/forgeofsouls/ForgeOfSoulsMultipliers.cpp index 7ddcffb86d..7873e7c1d1 100644 --- a/src/strategy/dungeons/wotlk/forgeofsouls/ForgeOfSoulsMultipliers.cpp +++ b/src/strategy/dungeons/wotlk/forgeofsouls/ForgeOfSoulsMultipliers.cpp @@ -6,7 +6,6 @@ #include "ForgeOfSoulsTriggers.h" #include "ForgeOfSoulsActions.h" - float BronjahmMultiplier::GetValue(Action* action) { Unit* boss = AI_VALUE2(Unit *, "find target", "bronjahm"); if (!boss) diff --git a/src/strategy/dungeons/wotlk/forgeofsouls/ForgeOfSoulsMultipliers.h b/src/strategy/dungeons/wotlk/forgeofsouls/ForgeOfSoulsMultipliers.h index 99d7e47b64..1c89bb096b 100644 --- a/src/strategy/dungeons/wotlk/forgeofsouls/ForgeOfSoulsMultipliers.h +++ b/src/strategy/dungeons/wotlk/forgeofsouls/ForgeOfSoulsMultipliers.h @@ -20,5 +20,4 @@ class AttackFragmentMultiplier : public Multiplier float GetValue(Action* action) override; }; - #endif diff --git a/src/strategy/dungeons/wotlk/gundrak/GundrakStrategy.cpp b/src/strategy/dungeons/wotlk/gundrak/GundrakStrategy.cpp index ac7b909d2d..8c5612dc65 100644 --- a/src/strategy/dungeons/wotlk/gundrak/GundrakStrategy.cpp +++ b/src/strategy/dungeons/wotlk/gundrak/GundrakStrategy.cpp @@ -1,7 +1,6 @@ #include "GundrakStrategy.h" #include "GundrakMultipliers.h" - void WotlkDungeonGDStrategy::InitTriggers(std::vector &triggers) { // Moorabi diff --git a/src/strategy/dungeons/wotlk/gundrak/GundrakStrategy.h b/src/strategy/dungeons/wotlk/gundrak/GundrakStrategy.h index c7910c1ca1..47f8617f17 100644 --- a/src/strategy/dungeons/wotlk/gundrak/GundrakStrategy.h +++ b/src/strategy/dungeons/wotlk/gundrak/GundrakStrategy.h @@ -5,7 +5,6 @@ #include "AiObjectContext.h" #include "Strategy.h" - class WotlkDungeonGDStrategy : public Strategy { public: diff --git a/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningActions.cpp b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningActions.cpp index a31699f806..047e0aee13 100644 --- a/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningActions.cpp +++ b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningActions.cpp @@ -126,7 +126,7 @@ bool LokenStackAction::isUseful() // Minimum hunter range is 5, but values too close to this seem to cause issues.. // Hunter bots will try and melee in between ranged attacks, or just melee entirely at 5 as they are in range. // 6.5 or 7.0 solves this for this boss. - if(bot->getClass() == CLASS_HUNTER) + if (bot->getClass() == CLASS_HUNTER) { return AI_VALUE2(float, "distance", "current target") > 6.5f; } @@ -141,7 +141,7 @@ bool LokenStackAction::Execute(Event event) float maxMovement = 10.0f; if (!boss->HasUnitState(UNIT_STATE_CASTING)) { - if(bot->getClass() == CLASS_HUNTER) + if (bot->getClass() == CLASS_HUNTER) { return Move(bot->GetAngle(boss), fmin(bot->GetExactDist2d(boss) - 6.5f, maxMovement)); } @@ -152,7 +152,6 @@ bool LokenStackAction::Execute(Event event) return false; } - bool AvoidLightningNovaAction::Execute(Event event) { Unit* boss = AI_VALUE2(Unit*, "find target", "loken"); diff --git a/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningMultipliers.cpp b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningMultipliers.cpp index fa67682237..3bf9fcfd63 100644 --- a/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningMultipliers.cpp +++ b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningMultipliers.cpp @@ -123,4 +123,3 @@ float LokenMultiplier::GetValue(Action* action) return 1.0f; // Default multiplier value for other cases. } - diff --git a/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningStrategy.cpp b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningStrategy.cpp index dc17c3d8f7..42d30d96d9 100644 --- a/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningStrategy.cpp +++ b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningStrategy.cpp @@ -1,7 +1,6 @@ #include "HallsOfLightningStrategy.h" #include "HallsOfLightningMultipliers.h" - void WotlkDungeonHoLStrategy::InitTriggers(std::vector &triggers) { // General Bjarngrim diff --git a/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningStrategy.h b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningStrategy.h index 8ee265dd47..6463033b3a 100644 --- a/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningStrategy.h +++ b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningStrategy.h @@ -5,7 +5,6 @@ #include "AiObjectContext.h" #include "Strategy.h" - class WotlkDungeonHoLStrategy : public Strategy { public: diff --git a/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneStrategy.cpp b/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneStrategy.cpp index 78d73f4de8..b13d4d6353 100644 --- a/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneStrategy.cpp +++ b/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneStrategy.cpp @@ -1,7 +1,6 @@ #include "HallsOfStoneStrategy.h" #include "HallsOfStoneMultipliers.h" - void WotlkDungeonHoSStrategy::InitTriggers(std::vector &triggers) { // Maiden of Grief diff --git a/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneStrategy.h b/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneStrategy.h index 6208483816..ee26aa2cd2 100644 --- a/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneStrategy.h +++ b/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneStrategy.h @@ -5,7 +5,6 @@ #include "AiObjectContext.h" #include "Strategy.h" - class WotlkDungeonHoSStrategy : public Strategy { public: diff --git a/src/strategy/dungeons/wotlk/nexus/NexusMultipliers.cpp b/src/strategy/dungeons/wotlk/nexus/NexusMultipliers.cpp index 764c9f346c..7adc79b455 100644 --- a/src/strategy/dungeons/wotlk/nexus/NexusMultipliers.cpp +++ b/src/strategy/dungeons/wotlk/nexus/NexusMultipliers.cpp @@ -94,4 +94,3 @@ float OrmorokMultiplier::GetValue(Action* action) } return 1.0f; } - diff --git a/src/strategy/dungeons/wotlk/nexus/NexusStrategy.cpp b/src/strategy/dungeons/wotlk/nexus/NexusStrategy.cpp index e471d3b804..22a85f5027 100644 --- a/src/strategy/dungeons/wotlk/nexus/NexusStrategy.cpp +++ b/src/strategy/dungeons/wotlk/nexus/NexusStrategy.cpp @@ -1,7 +1,6 @@ #include "NexusStrategy.h" #include "NexusMultipliers.h" - void WotlkDungeonNexStrategy::InitTriggers(std::vector &triggers) { // Horde Commander (Alliance N)/Commander Kolurg (Alliance H) diff --git a/src/strategy/dungeons/wotlk/nexus/NexusStrategy.h b/src/strategy/dungeons/wotlk/nexus/NexusStrategy.h index f9ecf42643..c78225b93b 100644 --- a/src/strategy/dungeons/wotlk/nexus/NexusStrategy.h +++ b/src/strategy/dungeons/wotlk/nexus/NexusStrategy.h @@ -5,7 +5,6 @@ #include "AiObjectContext.h" #include "Strategy.h" - class WotlkDungeonNexStrategy : public Strategy { public: diff --git a/src/strategy/dungeons/wotlk/oculus/OculusMultipliers.cpp b/src/strategy/dungeons/wotlk/oculus/OculusMultipliers.cpp index 5789d69649..7be543ee70 100644 --- a/src/strategy/dungeons/wotlk/oculus/OculusMultipliers.cpp +++ b/src/strategy/dungeons/wotlk/oculus/OculusMultipliers.cpp @@ -40,7 +40,7 @@ float OccFlyingMultiplier::GetValue(Action* action) float UromMultiplier::GetValue(Action* action) { - if(GetPhaseByCurrentPosition(bot) < 3) + if (GetPhaseByCurrentPosition(bot) < 3) { Unit* target = action->GetTarget(); if (target && target->GetEntry() == NPC_MAGE_LORD_UROM) diff --git a/src/strategy/dungeons/wotlk/oculus/OculusStrategy.cpp b/src/strategy/dungeons/wotlk/oculus/OculusStrategy.cpp index 1220e56f49..3e98dfa6a4 100644 --- a/src/strategy/dungeons/wotlk/oculus/OculusStrategy.cpp +++ b/src/strategy/dungeons/wotlk/oculus/OculusStrategy.cpp @@ -1,7 +1,6 @@ #include "OculusStrategy.h" #include "OculusMultipliers.h" - void WotlkDungeonOccStrategy::InitTriggers(std::vector &triggers) { // Drakos the Interrogator diff --git a/src/strategy/dungeons/wotlk/oculus/OculusStrategy.h b/src/strategy/dungeons/wotlk/oculus/OculusStrategy.h index a734f2f450..2accfd8f4d 100644 --- a/src/strategy/dungeons/wotlk/oculus/OculusStrategy.h +++ b/src/strategy/dungeons/wotlk/oculus/OculusStrategy.h @@ -5,7 +5,6 @@ #include "AiObjectContext.h" #include "Strategy.h" - class WotlkDungeonOccStrategy : public Strategy { public: diff --git a/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomActions.cpp b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomActions.cpp index 703c7454a5..916bf29200 100644 --- a/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomActions.cpp +++ b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomActions.cpp @@ -2,7 +2,6 @@ #include "OldKingdomActions.h" #include "OldKingdomStrategy.h" - bool AttackNadoxGuardianAction::Execute(Event event) { Unit* target = AI_VALUE2(Unit*, "find target", "ahn'kahar guardian"); diff --git a/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomStrategy.cpp b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomStrategy.cpp index 850fcfcb85..50a38412f7 100644 --- a/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomStrategy.cpp +++ b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomStrategy.cpp @@ -1,7 +1,6 @@ #include "OldKingdomStrategy.h" #include "OldKingdomMultipliers.h" - void WotlkDungeonOKStrategy::InitTriggers(std::vector &triggers) { // Elder Nadox diff --git a/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomStrategy.h b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomStrategy.h index 03c5085ee9..75c475559e 100644 --- a/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomStrategy.h +++ b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomStrategy.h @@ -5,7 +5,6 @@ #include "AiObjectContext.h" #include "Strategy.h" - class WotlkDungeonOKStrategy : public Strategy { public: diff --git a/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomTriggers.cpp b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomTriggers.cpp index 2de9190ce9..6b72f656a9 100644 --- a/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomTriggers.cpp +++ b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomTriggers.cpp @@ -3,7 +3,6 @@ #include "AiObject.h" #include "AiObjectContext.h" - bool NadoxGuardianTrigger::IsActive() { if (botAI->IsHeal(bot)) { return false; } diff --git a/src/strategy/dungeons/wotlk/pitofsaron/PitOfSaronActions.cpp b/src/strategy/dungeons/wotlk/pitofsaron/PitOfSaronActions.cpp index fee0ed6078..f34b98462e 100644 --- a/src/strategy/dungeons/wotlk/pitofsaron/PitOfSaronActions.cpp +++ b/src/strategy/dungeons/wotlk/pitofsaron/PitOfSaronActions.cpp @@ -136,7 +136,6 @@ bool IckAndKrickAction::PoisonNova(bool poisonNova, Unit* boss) return false; } - bool IckAndKrickAction::ExplosiveBarrage(bool explosiveBarrage, Unit* boss) { std::vector orbs; diff --git a/src/strategy/dungeons/wotlk/pitofsaron/PitOfSaronMultipliers.cpp b/src/strategy/dungeons/wotlk/pitofsaron/PitOfSaronMultipliers.cpp index cbe1877800..be36e480f2 100644 --- a/src/strategy/dungeons/wotlk/pitofsaron/PitOfSaronMultipliers.cpp +++ b/src/strategy/dungeons/wotlk/pitofsaron/PitOfSaronMultipliers.cpp @@ -5,8 +5,6 @@ #include "MovementActions.h" #include "PitOfSaronTriggers.h" - - float IckAndKrickMultiplier::GetValue(Action* action) { Unit* boss = AI_VALUE2(Unit*, "find target", "ick"); diff --git a/src/strategy/dungeons/wotlk/pitofsaron/PitOfSaronMultipliers.h b/src/strategy/dungeons/wotlk/pitofsaron/PitOfSaronMultipliers.h index fabf6319a3..bcafc01a43 100644 --- a/src/strategy/dungeons/wotlk/pitofsaron/PitOfSaronMultipliers.h +++ b/src/strategy/dungeons/wotlk/pitofsaron/PitOfSaronMultipliers.h @@ -20,5 +20,4 @@ class GarfrostMultiplier : public Multiplier float GetValue(Action* action) override; }; - #endif diff --git a/src/strategy/dungeons/wotlk/trialofthechampion/TrialOfTheChampionActions.cpp b/src/strategy/dungeons/wotlk/trialofthechampion/TrialOfTheChampionActions.cpp index 0cb1ee0e7b..5dd1ef8bcc 100644 --- a/src/strategy/dungeons/wotlk/trialofthechampion/TrialOfTheChampionActions.cpp +++ b/src/strategy/dungeons/wotlk/trialofthechampion/TrialOfTheChampionActions.cpp @@ -9,7 +9,6 @@ #include "GenericActions.h" #include - bool ToCLanceAction::Execute(Event event) { // If already has lance equipped, do nothing diff --git a/src/strategy/dungeons/wotlk/trialofthechampion/TrialOfTheChampionStrategy.cpp b/src/strategy/dungeons/wotlk/trialofthechampion/TrialOfTheChampionStrategy.cpp index 4c439e18a5..e1f97000c6 100644 --- a/src/strategy/dungeons/wotlk/trialofthechampion/TrialOfTheChampionStrategy.cpp +++ b/src/strategy/dungeons/wotlk/trialofthechampion/TrialOfTheChampionStrategy.cpp @@ -1,7 +1,6 @@ #include "TrialOfTheChampionStrategy.h" #include "TrialOfTheChampionMultipliers.h" - void WotlkDungeonToCStrategy::InitTriggers(std::vector &triggers) { triggers.push_back(new TriggerNode("toc lance", diff --git a/src/strategy/dungeons/wotlk/trialofthechampion/TrialOfTheChampionTriggers.cpp b/src/strategy/dungeons/wotlk/trialofthechampion/TrialOfTheChampionTriggers.cpp index 81cfefec95..6c388382e7 100644 --- a/src/strategy/dungeons/wotlk/trialofthechampion/TrialOfTheChampionTriggers.cpp +++ b/src/strategy/dungeons/wotlk/trialofthechampion/TrialOfTheChampionTriggers.cpp @@ -3,7 +3,6 @@ #include "AiObject.h" #include "AiObjectContext.h" - bool ToCLanceTrigger::IsActive() { if (bot->GetVehicle()) @@ -82,4 +81,3 @@ bool ToCEadricTrigger::IsActive() return true; } - diff --git a/src/strategy/dungeons/wotlk/trialofthechampion/TrialOfTheChampionTriggers.h b/src/strategy/dungeons/wotlk/trialofthechampion/TrialOfTheChampionTriggers.h index c92cd87834..0daaf914a8 100644 --- a/src/strategy/dungeons/wotlk/trialofthechampion/TrialOfTheChampionTriggers.h +++ b/src/strategy/dungeons/wotlk/trialofthechampion/TrialOfTheChampionTriggers.h @@ -16,20 +16,16 @@ enum TocC_IDs //Eadric SPELL_RADIANCE = 66935, - // Objects OBJECT_LANCE_RACK = 196398, - // Items ITEM_LANCE = 46106, - // Vehicles NPC_ARGENT_WARHORSE = 35644, NPC_ARGENT_BATTLEWORG = 36558, - // Horde Champions NPC_MOKRA = 35572, NPC_ERESSEA = 35569, @@ -37,7 +33,6 @@ enum TocC_IDs NPC_ZULTORE = 35570, NPC_VISCERI = 35617, - // Alliance Champions NPC_JACOB = 34705, NPC_AMBROSE = 34702, @@ -45,7 +40,6 @@ enum TocC_IDs NPC_JAELYNE = 34657, NPC_LANA = 34703, - // Grand Champion Minions NPC_IRONFORGE_MINION = 35329, NPC_STORMWIND_MINION = 35328, @@ -58,7 +52,6 @@ enum TocC_IDs NPC_SENJIN_MINION = 35323, NPC_UNDERCITY_MINION = 35327, - // Rest of the bosses and npcs NPC_EADRIC = 35119, NPC_EADRIC_H = 35518, diff --git a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepStrategy.cpp b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepStrategy.cpp index 681083efa1..ee7d102a9b 100644 --- a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepStrategy.cpp +++ b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepStrategy.cpp @@ -1,7 +1,6 @@ #include "UtgardeKeepStrategy.h" #include "UtgardeKeepMultipliers.h" - void WotlkDungeonUKStrategy::InitTriggers(std::vector &triggers) { // Prince Keleseth diff --git a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepStrategy.h b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepStrategy.h index 90873feb01..41ce1e76e6 100644 --- a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepStrategy.h +++ b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepStrategy.h @@ -5,7 +5,6 @@ #include "AiObjectContext.h" #include "Strategy.h" - class WotlkDungeonUKStrategy : public Strategy { public: diff --git a/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleStrategy.cpp b/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleStrategy.cpp index b6e3344936..bcc4399cda 100644 --- a/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleStrategy.cpp +++ b/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleStrategy.cpp @@ -1,7 +1,6 @@ #include "UtgardePinnacleStrategy.h" #include "UtgardePinnacleMultipliers.h" - void WotlkDungeonUPStrategy::InitTriggers(std::vector &triggers) { // Svala Sorrowgrave diff --git a/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleStrategy.h b/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleStrategy.h index e8f45363b9..8ce816f808 100644 --- a/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleStrategy.h +++ b/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleStrategy.h @@ -5,7 +5,6 @@ #include "AiObjectContext.h" #include "Strategy.h" - class WotlkDungeonUPStrategy : public Strategy { public: diff --git a/src/strategy/dungeons/wotlk/violethold/VioletHoldActions.cpp b/src/strategy/dungeons/wotlk/violethold/VioletHoldActions.cpp index 009da3b19d..b590642d8b 100644 --- a/src/strategy/dungeons/wotlk/violethold/VioletHoldActions.cpp +++ b/src/strategy/dungeons/wotlk/violethold/VioletHoldActions.cpp @@ -2,7 +2,6 @@ #include "VioletHoldActions.h" #include "VioletHoldStrategy.h" - bool AttackErekemAction::Execute(Event event) { // Focus boss first, adds after diff --git a/src/strategy/dungeons/wotlk/violethold/VioletHoldStrategy.cpp b/src/strategy/dungeons/wotlk/violethold/VioletHoldStrategy.cpp index 59f2225b52..5b610300f3 100644 --- a/src/strategy/dungeons/wotlk/violethold/VioletHoldStrategy.cpp +++ b/src/strategy/dungeons/wotlk/violethold/VioletHoldStrategy.cpp @@ -1,7 +1,6 @@ #include "VioletHoldStrategy.h" #include "VioletHoldMultipliers.h" - void WotlkDungeonVHStrategy::InitTriggers(std::vector &triggers) { // Erekem diff --git a/src/strategy/dungeons/wotlk/violethold/VioletHoldStrategy.h b/src/strategy/dungeons/wotlk/violethold/VioletHoldStrategy.h index 1670887100..a06f64ce39 100644 --- a/src/strategy/dungeons/wotlk/violethold/VioletHoldStrategy.h +++ b/src/strategy/dungeons/wotlk/violethold/VioletHoldStrategy.h @@ -5,7 +5,6 @@ #include "AiObjectContext.h" #include "Strategy.h" - class WotlkDungeonVHStrategy : public Strategy { public: diff --git a/src/strategy/dungeons/wotlk/violethold/VioletHoldTriggers.cpp b/src/strategy/dungeons/wotlk/violethold/VioletHoldTriggers.cpp index bd2eee765d..87284b0e49 100644 --- a/src/strategy/dungeons/wotlk/violethold/VioletHoldTriggers.cpp +++ b/src/strategy/dungeons/wotlk/violethold/VioletHoldTriggers.cpp @@ -3,7 +3,6 @@ #include "AiObject.h" #include "AiObjectContext.h" - bool ErekemTargetTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "erekem"); diff --git a/src/strategy/hunter/GenericHunterStrategy.h b/src/strategy/hunter/GenericHunterStrategy.h index 0fa9ed69ab..01aef4cecc 100644 --- a/src/strategy/hunter/GenericHunterStrategy.h +++ b/src/strategy/hunter/GenericHunterStrategy.h @@ -21,7 +21,6 @@ class GenericHunterStrategy : public CombatStrategy uint32 GetType() const override { return CombatStrategy::GetType() | STRATEGY_TYPE_RANGED | STRATEGY_TYPE_DPS; } }; - class AoEHunterStrategy : public CombatStrategy { public: @@ -58,5 +57,4 @@ class HunterTrapWeaveStrategy : public Strategy std::string const getName() override { return "trap weave"; } }; - #endif diff --git a/src/strategy/paladin/PaladinActions.cpp b/src/strategy/paladin/PaladinActions.cpp index 7dbf7c91ed..190ecb264a 100644 --- a/src/strategy/paladin/PaladinActions.cpp +++ b/src/strategy/paladin/PaladinActions.cpp @@ -250,7 +250,6 @@ bool CastBlessingOfWisdomOnPartyAction::Execute(Event event) return botAI->CastSpell(castName, target); } - Value* CastBlessingOfSanctuaryOnPartyAction::GetTargetValue() { return context->GetValue( @@ -488,7 +487,6 @@ Unit* CastRighteousDefenseAction::GetTarget() return current_target->GetVictim(); } - bool CastDivineSacrificeAction::isUseful() { return GetTarget() && (GetTarget() != nullptr) && CastSpellAction::isUseful() && diff --git a/src/strategy/priest/GenericPriestStrategy.cpp b/src/strategy/priest/GenericPriestStrategy.cpp index cbd838abdb..acf9d39d86 100644 --- a/src/strategy/priest/GenericPriestStrategy.cpp +++ b/src/strategy/priest/GenericPriestStrategy.cpp @@ -109,4 +109,3 @@ void PriestHealerDpsStrategy::InitTriggers(std::vector& triggers) new NextAction("mind sear", ACTION_DEFAULT + 0.5f), nullptr))); } - diff --git a/src/strategy/raids/aq20/RaidAq20Actions.cpp b/src/strategy/raids/aq20/RaidAq20Actions.cpp index 9b7e40652b..1bf33147f8 100644 --- a/src/strategy/raids/aq20/RaidAq20Actions.cpp +++ b/src/strategy/raids/aq20/RaidAq20Actions.cpp @@ -3,7 +3,6 @@ #include "Playerbots.h" #include "RaidAq20Utils.h" - bool Aq20UseCrystalAction::Execute(Event event) { if (Unit* boss = AI_VALUE2(Unit*, "find target", "ossirian the unscarred")) diff --git a/src/strategy/raids/aq20/RaidAq20Triggers.cpp b/src/strategy/raids/aq20/RaidAq20Triggers.cpp index fbda881a57..1a580d6bd3 100644 --- a/src/strategy/raids/aq20/RaidAq20Triggers.cpp +++ b/src/strategy/raids/aq20/RaidAq20Triggers.cpp @@ -3,7 +3,6 @@ #include "SharedDefines.h" #include "RaidAq20Utils.h" - bool Aq20MoveToCrystalTrigger::IsActive() { if (Unit* boss = AI_VALUE2(Unit*, "find target", "ossirian the unscarred")) diff --git a/src/strategy/raids/gruulslair/RaidGruulsLairHelpers.h b/src/strategy/raids/gruulslair/RaidGruulsLairHelpers.h index aa5a83acb0..8cd01c2e6b 100644 --- a/src/strategy/raids/gruulslair/RaidGruulsLairHelpers.h +++ b/src/strategy/raids/gruulslair/RaidGruulsLairHelpers.h @@ -6,57 +6,57 @@ namespace GruulsLairHelpers { - enum GruulsLairSpells - { - // High King Maulgar - SPELL_WHIRLWIND = 33238, - - // Krosh Firehand - SPELL_SPELL_SHIELD = 33054, - - // Hunter - SPELL_MISDIRECTION = 34477, - - // Warlock - SPELL_BANISH = 18647, // Rank 2 - - // Gruul the Dragonkiller - SPELL_GROUND_SLAM_1 = 33525, - SPELL_GROUND_SLAM_2 = 39187, - }; - - enum GruulsLairNPCs - { - NPC_WILD_FEL_STALKER = 18847, - }; - - bool IsAnyOgreBossAlive(PlayerbotAI* botAI); - void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId); - void MarkTargetWithSquare(Player* bot, Unit* target); - void MarkTargetWithStar(Player* bot, Unit* target); - void MarkTargetWithCircle(Player* bot, Unit* target); - void MarkTargetWithDiamond(Player* bot, Unit* target); - void MarkTargetWithTriangle(Player* bot, Unit* target); - void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target); - bool IsKroshMageTank(PlayerbotAI* botAI, Player* bot); - bool IsKigglerMoonkinTank(PlayerbotAI* botAI, Player* bot); - bool IsPositionSafe(PlayerbotAI* botAI, Player* bot, Position pos); - bool TryGetNewSafePosition(PlayerbotAI* botAI, Player* bot, Position& outPos); - - struct Location - { - float x, y, z; - }; - - namespace GruulsLairLocations - { - extern const Location MaulgarTankPosition; - extern const Location OlmTankPosition; - extern const Location BlindeyeTankPosition; - extern const Location KroshTankPosition; - extern const Location MaulgarRoomCenter; - extern const Location GruulTankPosition; - } + enum GruulsLairSpells + { + // High King Maulgar + SPELL_WHIRLWIND = 33238, + + // Krosh Firehand + SPELL_SPELL_SHIELD = 33054, + + // Hunter + SPELL_MISDIRECTION = 34477, + + // Warlock + SPELL_BANISH = 18647, // Rank 2 + + // Gruul the Dragonkiller + SPELL_GROUND_SLAM_1 = 33525, + SPELL_GROUND_SLAM_2 = 39187, + }; + + enum GruulsLairNPCs + { + NPC_WILD_FEL_STALKER = 18847, + }; + + bool IsAnyOgreBossAlive(PlayerbotAI* botAI); + void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId); + void MarkTargetWithSquare(Player* bot, Unit* target); + void MarkTargetWithStar(Player* bot, Unit* target); + void MarkTargetWithCircle(Player* bot, Unit* target); + void MarkTargetWithDiamond(Player* bot, Unit* target); + void MarkTargetWithTriangle(Player* bot, Unit* target); + void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target); + bool IsKroshMageTank(PlayerbotAI* botAI, Player* bot); + bool IsKigglerMoonkinTank(PlayerbotAI* botAI, Player* bot); + bool IsPositionSafe(PlayerbotAI* botAI, Player* bot, Position pos); + bool TryGetNewSafePosition(PlayerbotAI* botAI, Player* bot, Position& outPos); + + struct Location + { + float x, y, z; + }; + + namespace GruulsLairLocations + { + extern const Location MaulgarTankPosition; + extern const Location OlmTankPosition; + extern const Location BlindeyeTankPosition; + extern const Location KroshTankPosition; + extern const Location MaulgarRoomCenter; + extern const Location GruulTankPosition; + } } #endif diff --git a/src/strategy/raids/icecrown/RaidIccActions.cpp b/src/strategy/raids/icecrown/RaidIccActions.cpp index a1b7100f8b..4f763ecb95 100644 --- a/src/strategy/raids/icecrown/RaidIccActions.cpp +++ b/src/strategy/raids/icecrown/RaidIccActions.cpp @@ -116,7 +116,7 @@ bool IccSpikeAction::HandleSpikeTargeting(Unit* boss) // First check for alive spikes for (const auto entry : spikeEntries) { - for (const auto& guid : spikes) + for (auto const& guid : spikes) { if (Unit* unit = botAI->GetUnit(guid)) { @@ -238,7 +238,7 @@ bool IccRangedPositionLadyDeathwhisperAction::MaintainRangedSpacing() float totalY = 0.0f; int nearbyCount = 0; - for (const auto& memberGuid : members) + for (auto const& memberGuid : members) { Unit* member = botAI->GetUnit(memberGuid); if (!member || !member->IsAlive() || member == bot) @@ -340,7 +340,7 @@ bool IccAddsLadyDeathwhisperAction::Execute(Event event) bool IccAddsLadyDeathwhisperAction::IsTargetedByShade(uint32 shadeEntry) { const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - for (const auto& npcGuid : npcs) + for (auto const& npcGuid : npcs) { Unit* unit = botAI->GetUnit(npcGuid); if (unit && unit->GetEntry() == shadeEntry && unit->GetVictim() == bot) @@ -376,9 +376,9 @@ bool IccAddsLadyDeathwhisperAction::HandleAddTargeting(Unit* boss) bool hasValidAdds = false; // First check for alive adds - for (const auto& entry : addEntriesLady) + for (auto const& entry : addEntriesLady) { - for (const auto& guid : targets) + for (auto const& guid : targets) { Unit* unit = botAI->GetUnit(guid); if (unit && unit->IsAlive() && unit->GetEntry() == entry) @@ -427,7 +427,7 @@ bool IccShadeLadyDeathwhisperAction::Execute(Event event) // Get the nearest hostile NPCs const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - for (const auto& npcGuid : npcs) + for (auto const& npcGuid : npcs) { Unit* shade = botAI->GetUnit(npcGuid); @@ -506,7 +506,7 @@ bool IccRottingFrostGiantTankPositionAction::Execute(Event event) std::map targetCounts; // First, identify all infected bots and their current targets (approximate) - for (const auto& memberGuid : members) + for (auto const& memberGuid : members) { Unit* member = botAI->GetUnit(memberGuid); if (!member || !member->IsAlive() || member == bot) @@ -520,7 +520,7 @@ bool IccRottingFrostGiantTankPositionAction::Execute(Event event) float minDist = 5.0f; // Only count if they're close enough to likely be targeting Unit* likelyTarget = nullptr; - for (const auto& targetGuid : members) + for (auto const& targetGuid : members) { Unit* potentialTarget = botAI->GetUnit(targetGuid); if (!potentialTarget || !potentialTarget->IsAlive() || potentialTarget == member) @@ -551,7 +551,7 @@ bool IccRottingFrostGiantTankPositionAction::Execute(Event event) std::vector> viableTargets; // First try to find ranged, non-infected, non-cured bots - for (const auto& memberGuid : members) + for (auto const& memberGuid : members) { Unit* member = botAI->GetUnit(memberGuid); if (!member || !member->IsAlive() || member == bot) @@ -630,7 +630,7 @@ bool IccRottingFrostGiantTankPositionAction::Execute(Event event) float totalY = 0.0f; int nearbyCount = 0; - for (const auto& memberGuid : members) + for (auto const& memberGuid : members) { Unit* member = botAI->GetUnit(memberGuid); if (!member || !member->IsAlive() || member == bot) @@ -726,7 +726,7 @@ Unit* IccCannonFireAction::FindValidCannonTarget() { const GuidVector attackers = AI_VALUE(GuidVector, "possible targets no los"); - for (const auto& attackerGuid : attackers) + for (auto const& attackerGuid : attackers) { Unit* unit = botAI->GetUnit(attackerGuid); if (!unit) @@ -774,7 +774,7 @@ Unit* IccGunshipEnterCannonAction::FindBestAvailableCannon() Unit* bestVehicle = nullptr; const GuidVector npcs = AI_VALUE(GuidVector, "nearest vehicles"); - for (const auto& npcGuid : npcs) + for (auto const& npcGuid : npcs) { Unit* vehicleBase = botAI->GetUnit(npcGuid); if (!IsValidCannon(vehicleBase, validCannonEntries)) @@ -1006,8 +1006,6 @@ bool IccDbsTankPositionAction::Execute(Event event) ICC_DBS_TANK_POSITION.GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_NORMAL); - - // Early return if this tank has Rune of Blood if (botAI->GetAura("Rune of Blood", bot)) return true; @@ -1041,7 +1039,7 @@ bool IccDbsTankPositionAction::CrowdControlBloodBeasts() bool appliedCC = false; - for (const auto& npc : npcs) + for (auto const& npc : npcs) { Unit* unit = botAI->GetUnit(npc); if (!unit || !unit->IsAlive()) @@ -1143,7 +1141,7 @@ bool IccDbsTankPositionAction::EvadeBloodBeasts() // Get the nearest hostile NPCs const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - for (const auto& npc : npcs) + for (auto const& npc : npcs) { Unit* unit = botAI->GetUnit(npc); if (!unit) @@ -1326,7 +1324,7 @@ bool IccFestergutGroupPositionAction::HasSporesInGroup() { const GuidVector members = AI_VALUE(GuidVector, "group members"); - for (const auto& memberGuid : members) + for (auto const& memberGuid : members) { Unit* unit = botAI->GetUnit(memberGuid); if (unit && unit->HasAura(SPELL_GAS_SPORE)) @@ -1564,7 +1562,7 @@ IccFestergutSporeAction::SporeInfo IccFestergutSporeAction::FindSporedPlayers() SporeInfo info; const GuidVector members = AI_VALUE(GuidVector, "group members"); - for (const auto& memberGuid : members) + for (auto const& memberGuid : members) { Unit* unit = botAI->GetUnit(memberGuid); if (!unit) @@ -1612,7 +1610,7 @@ bool IccFestergutSporeAction::CheckMainTankSpore() { const GuidVector members = AI_VALUE(GuidVector, "group members"); - for (const auto& memberGuid : members) + for (auto const& memberGuid : members) { Unit* unit = botAI->GetUnit(memberGuid); if (!unit) @@ -1702,14 +1700,13 @@ bool IccRotfaceTankPositionAction::HandleAssistTankPositioning(Unit* boss) return HandleBigOozePositioning(boss); } - bool IccRotfaceTankPositionAction::HandleBigOozePositioning(Unit* boss) { // Find all big oozes GuidVector bigOozes = AI_VALUE(GuidVector, "nearest hostile npcs"); std::vector activeBigOozes; - for (const auto& guid : bigOozes) + for (auto const& guid : bigOozes) { Unit* unit = botAI->GetUnit(guid); if (unit && unit->IsAlive() && unit->GetEntry() == NPC_BIG_OOZE && unit->IsVisible()) @@ -1799,7 +1796,7 @@ bool IccRotfaceTankPositionAction::HandleBigOozePositioning(Unit* boss) GuidVector puddles = AI_VALUE(GuidVector, "nearest hostile npcs"); bool isSafeFromPuddles = true; - for (const auto& puddleGuid : puddles) + for (auto const& puddleGuid : puddles) { Unit* puddle = botAI->GetUnit(puddleGuid); if (puddle && botAI->GetAura("Ooze Flood", puddle)) @@ -1827,7 +1824,6 @@ bool IccRotfaceTankPositionAction::HandleBigOozePositioning(Unit* boss) return false; } - bool IccRotfaceGroupPositionAction::Execute(Event event) { Unit* boss = AI_VALUE2(Unit*, "find target", "rotface"); @@ -1837,7 +1833,7 @@ bool IccRotfaceGroupPositionAction::Execute(Event event) const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); bool floodPresent = false; - for (const auto& npc : npcs) + for (auto const& npc : npcs) { Unit* unit = botAI->GetUnit(npc); if (!unit || !botAI->HasAura("Ooze Flood", unit)) @@ -1876,7 +1872,7 @@ bool IccRotfaceGroupPositionAction::HandlePuddleAvoidance(Unit* boss) { const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - for (const auto& npc : npcs) + for (auto const& npc : npcs) { Unit* unit = botAI->GetUnit(npc); if (!unit || !botAI->HasAura("Ooze Flood", unit)) @@ -2062,7 +2058,7 @@ bool IccRotfaceGroupPositionAction::FindAndMoveFromClosestMember(Unit* boss, Uni const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); Unit* puddle = nullptr; - for (const auto& npc : npcs) + for (auto const& npc : npcs) { Unit* unit = botAI->GetUnit(npc); if (!unit || !botAI->HasAura("Ooze Flood", unit)) @@ -2087,11 +2083,11 @@ bool IccRotfaceGroupPositionAction::FindAndMoveFromClosestMember(Unit* boss, Uni float totalY = 0.0f; int nearbyCount = 0; - for (const auto& memberGuid : members) + for (auto const& memberGuid : members) { Unit* member = botAI->GetUnit(memberGuid); if (!member || !member->IsAlive() || member == bot || (smallOoze && smallOoze->GetVictim() == member) || - (member->GetTypeId() == TYPEID_PLAYER && botAI->IsAssistTank(static_cast(member)))) + (member->IsPlayer() && botAI->IsAssistTank(static_cast(member)))) continue; const float distance = bot->GetExactDist2d(member); @@ -2202,7 +2198,7 @@ bool IccRotfaceMoveAwayFromExplosionAction::MoveToRandomSafeLocation() // Ensure the position is at least 30 yards away from any puddle const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - for (const auto& npc : npcs) + for (auto const& npc : npcs) { Unit* puddle = botAI->GetUnit(npc); if (!puddle || !botAI->HasAura("Ooze Flood", puddle)) @@ -2287,7 +2283,7 @@ Unit* IccPutricideGrowingOozePuddleAction::FindClosestThreateningPuddle() float closestDistance = FLT_MAX; float closestSafeDistance = BASE_RADIUS; - for (const auto& npc : npcs) + for (auto const& npc : npcs) { Unit* unit = botAI->GetUnit(npc); if (!unit || unit->GetEntry() != NPC_GROWING_OOZE_PUDDLE) @@ -2402,7 +2398,7 @@ bool IccPutricideGrowingOozePuddleAction::IsPositionTooCloseToOtherPuddles(float static const float STACK_MULTIPLIER = 0.6f; GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - for (const auto& npc : npcs) + for (auto const& npc : npcs) { Unit* unit = botAI->GetUnit(npc); if (!unit || unit == ignorePuddle || unit->GetEntry() != NPC_GROWING_OOZE_PUDDLE) @@ -2445,7 +2441,7 @@ bool IccPutricideVolatileOozeAction::Execute(Event event) // Find all alive oozes std::vector aliveOozes; const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - for (const auto& guid : npcs) + for (auto const& guid : npcs) { Unit* unit = botAI->GetUnit(guid); if (unit && unit->IsAlive() && unit->GetEntry() == ooze->GetEntry()) @@ -2533,7 +2529,6 @@ Unit* IccPutricideVolatileOozeAction::FindAuraTarget() return nullptr; } - bool IccPutricideGasCloudAction::Execute(Event event) { Unit* gasCloud = AI_VALUE2(Unit*, "find target", "gas cloud"); @@ -2560,7 +2555,7 @@ bool IccPutricideGasCloudAction::Execute(Event event) // Find all alive gasCloud std::vector aliveGasCloud; const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - for (const auto& guid : npcs) + for (auto const& guid : npcs) { Unit* unit = botAI->GetUnit(guid); if (unit && unit->IsAlive() && unit->GetEntry() == gasCloud->GetEntry()) @@ -2613,7 +2608,7 @@ bool IccPutricideGasCloudAction::HandleGaseousBloatMovement(Unit* gasCloud) // Gather all choking gas bombs GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); std::vector gasBombs; - for (const auto& guid : npcs) + for (auto const& guid : npcs) { Unit* unit = botAI->GetUnit(guid); if (unit && unit->IsAlive() && unit->GetEntry() == NPC_CHOKING_GAS_BOMB) @@ -3048,7 +3043,6 @@ bool IccPutricideAvoidMalleableGooAction::HandleUnboundPlague(Unit* boss) return false; } - bool IccPutricideAvoidMalleableGooAction::HandleBossPositioning(Unit* boss) { if (botAI->IsTank(bot)) @@ -3128,7 +3122,7 @@ Position IccPutricideAvoidMalleableGooAction::CalculateBossPosition(Unit* boss, bool IccPutricideAvoidMalleableGooAction::HasObstacleBetween(const Position& from, const Position& to) { GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - for (const auto& npc : npcs) + for (auto const& npc : npcs) { Unit* unit = botAI->GetUnit(npc); if (!unit || !unit->IsAlive()) @@ -3261,7 +3255,7 @@ bool IccBpcKelesethTankAction::Execute(Event event) if (boss->GetVictim() == bot) { GuidVector targets = AI_VALUE(GuidVector, "possible targets"); - for (const auto& targetGuid : targets) + for (auto const& targetGuid : targets) { Unit* nucleus = botAI->GetUnit(targetGuid); if (nucleus && nucleus->IsAlive() && nucleus->GetEntry() == NPC_DARK_NUCLEUS) @@ -3392,7 +3386,7 @@ void IccBpcMainTankAction::MarkEmpoweredPrince() Unit* empoweredPrince = nullptr; const GuidVector& targets = AI_VALUE(GuidVector, "possible targets"); - for (const auto& targetGuid : targets) + for (auto const& targetGuid : targets) { Unit* unit = botAI->GetUnit(targetGuid); if (!unit || !unit->IsAlive()) @@ -3478,7 +3472,7 @@ bool IccBpcEmpoweredVortexAction::MaintainRangedSpacing() float totalY = 0.0f; int nearbyCount = 0; - for (const auto& memberGuid : members) + for (auto const& memberGuid : members) { Unit* member = botAI->GetUnit(memberGuid); if (!member || !member->IsAlive() || member == bot) @@ -3557,7 +3551,7 @@ bool IccBpcEmpoweredVortexAction::HandleEmpoweredVortexSpread() float totalY = 0.0f; int nearbyCount = 0; - for (const auto& memberGuid : members) + for (auto const& memberGuid : members) { Unit* member = botAI->GetUnit(memberGuid); if (!member || !member->IsAlive() || member == bot) @@ -3676,7 +3670,7 @@ Unit* IccBpcKineticBombAction::FindOptimalKineticBomb() // Gather all valid kinetic bombs std::vector kineticBombs; - for (const auto& guid : targets) + for (auto const& guid : targets) { Unit* unit = botAI->GetUnit(guid); if (!unit || !unit->IsAlive()) @@ -3764,7 +3758,6 @@ bool IccBpcKineticBombAction::IsBombAlreadyHandled(Unit* bomb, Group* group) return false; } - bool IccBpcBallOfFlameAction::Execute(Event event) { Unit* boss = AI_VALUE2(Unit*, "find target", "prince taldaram"); @@ -3814,7 +3807,7 @@ bool IccBpcBallOfFlameAction::Execute(Event event) { const float SAFE_DIST = 15.0f; GuidVector members = AI_VALUE(GuidVector, "group members"); - for (const auto& memberGuid : members) + for (auto const& memberGuid : members) { Unit* member = botAI->GetUnit(memberGuid); if (!member || !member->IsAlive() || member == bot) @@ -4341,7 +4334,7 @@ bool IccBqlGroupPositionAction::HandleGroupPosition(Unit* boss, Aura* frenzyAura // Gather all ranged and healers, sort by GUID for deterministic assignment std::vector rangedBots; std::vector healers; - for (const auto& guid : members) + for (auto const& guid : members) { Unit* member = botAI->GetUnit(guid); if (!member || !member->IsAlive()) @@ -4457,7 +4450,7 @@ bool IccBqlGroupPositionAction::HandleGroupPosition(Unit* boss, Aura* frenzyAura } // Also spread from swarming shadows GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - for (const auto& npcGuid : npcs) + for (auto const& npcGuid : npcs) { Unit* unit = botAI->GetUnit(npcGuid); if (unit && unit->IsAlive() && unit->GetEntry() == NPC_SWARMING_SHADOWS) @@ -4605,7 +4598,6 @@ bool IccBqlGroupPositionAction::HandleGroupPosition(Unit* boss, Aura* frenzyAura return true; } - return false; } @@ -4798,7 +4790,7 @@ Player* IccBqlVampiricBiteAction::FindBestBiteTarget(Group* group) } // Sort by distance - auto sortByDistance = [](const auto& a, const auto& b) { return a.second < b.second; }; + auto sortByDistance = [](auto const& a, auto const& b) { return a.second < b.second; }; std::sort(dpsTargets.begin(), dpsTargets.end(), sortByDistance); std::sort(healTargets.begin(), healTargets.end(), sortByDistance); @@ -4955,7 +4947,7 @@ bool IccValithriaGroupAction::Execute(Event event) // Tank behavior if (botAI->IsTank(bot)) { - for (const auto& targetGuid : AI_VALUE(GuidVector, "possible targets")) + for (auto const& targetGuid : AI_VALUE(GuidVector, "possible targets")) { if (Unit* unit = botAI->GetUnit(targetGuid)) { @@ -4965,7 +4957,7 @@ bool IccValithriaGroupAction::Execute(Event event) // Skip if unit is already attacking any tank if (Unit* victim = unit->GetVictim()) { - if (victim->GetTypeId() == TYPEID_PLAYER && botAI->IsTank(static_cast(victim))) + if (victim->IsPlayer() && botAI->IsTank(static_cast(victim))) { continue; } @@ -5133,7 +5125,7 @@ bool IccValithriaGroupAction::Handle25ManGroupLogic() } // Sort by GUID for consistent ordering - std::sort(nonHeals.begin(), nonHeals.end(), [](const auto& a, const auto& b) { return a.first < b.first; }); + std::sort(nonHeals.begin(), nonHeals.end(), [](auto const& a, auto const& b) { return a.first < b.first; }); // Assign to groups std::vector group1, group2; @@ -5168,7 +5160,6 @@ bool IccValithriaGroupAction::Handle25ManGroupLogic() if (botAI->IsTank(bot) || botAI->IsDps(bot)) HandleMarkingLogic(inGroup1, inGroup2, group1Pos, group2Pos); - // Movement logic for non-healers if (!botAI->IsHeal(bot)) { @@ -5230,7 +5221,7 @@ bool IccValithriaGroupAction::HandleMarkingLogic(bool inGroup1, bool inGroup2, c for (uint32 entry : addPriority) { - for (const auto& guid : adds) + for (auto const& guid : adds) { if (Unit* unit = botAI->GetUnit(guid)) { @@ -5291,7 +5282,7 @@ bool IccValithriaGroupAction::Handle10ManGroupLogic() for (uint32 entry : addPriority) { - for (const auto& guid : adds) + for (auto const& guid : adds) { if (Unit* unit = botAI->GetUnit(guid)) { @@ -5337,7 +5328,7 @@ bool IccValithriaPortalAction::Execute(Event event) GuidVector npcs = AI_VALUE(GuidVector, "nearest npcs"); std::vector preEffectPortals; std::vector realPortals; - for (const auto& guid : npcs) + for (auto const& guid : npcs) { Creature* c = dynamic_cast(botAI->GetUnit(guid)); if (!c) @@ -5564,7 +5555,7 @@ bool IccValithriaDreamCloudAction::Execute(Event event) // Gather all group members with dream state const GuidVector members = AI_VALUE(GuidVector, "group members"); std::vector dreamBots; - for (const auto& guid : members) + for (auto const& guid : members) { Unit* member = botAI->GetUnit(guid); if (member && member->IsAlive() && member->HasAura(SPELL_DREAM_STATE)) @@ -5615,7 +5606,6 @@ bool IccValithriaDreamCloudAction::Execute(Event event) } } - // All stacked: leader (lowest guid) moves to next cloud, others follow and stack at leader's new position // Find all dream and nightmare clouds GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); @@ -5997,7 +5987,7 @@ bool IccSindragosaGroupPositionAction::HandleNonTankPositioning() for (const auto entry : tombEntries) { - for (const auto& guid : tombGuids) + for (auto const& guid : tombGuids) { if (Unit* unit = botAI->GetUnit(guid)) { @@ -6160,7 +6150,7 @@ void IccSindragosaFrostBeaconAction::HandleSupportActions() if (botAI->IsHeal(bot) && !bot->HasAura(FROST_BEACON_AURA_ID)) { const auto members = AI_VALUE(GuidVector, "group members"); - for (const auto& memberGuid : members) + for (auto const& memberGuid : members) { Unit* member = botAI->GetUnit(memberGuid); if (!member || !member->IsAlive() || !member->HasAura(FROST_BEACON_AURA_ID)) @@ -6266,7 +6256,7 @@ bool IccSindragosaFrostBeaconAction::HandleNonBeaconedPlayer(const Unit* boss) // Collect beaconed players std::vector beaconedPlayers; const auto members = AI_VALUE(GuidVector, "group members"); - for (const auto& memberGuid : members) + for (auto const& memberGuid : members) { Unit* player = botAI->GetUnit(memberGuid); if (player && player->GetGUID() != bot->GetGUID() && player->HasAura(FROST_BEACON_AURA_ID)) @@ -6474,7 +6464,7 @@ bool IccSindragosaMysticBuffetAction::Execute(Event event) for (const auto entry : tombEntries) { - for (const auto& guid : tombGuids) + for (auto const& guid : tombGuids) { if (Unit* unit = botAI->GetUnit(guid)) { @@ -6518,7 +6508,6 @@ bool IccSindragosaMysticBuffetAction::Execute(Event event) return true; } - botAI->Reset(); // Move to LOS2 position return MoveTo(bot->GetMapId(), ICC_SINDRAGOSA_LOS2_POSITION.GetPositionX(), @@ -8062,7 +8051,7 @@ bool IccLichKingAddsAction::HandleSpiritBombAvoidance(Difficulty diff, Unit* ter float minDistAtPos = std::numeric_limits::max(); int bombCountInVicinity = 0; - for (const auto& bombPair : spiritBombs) + for (auto const& bombPair : spiritBombs) { Unit* bomb = bombPair.second; if (!bomb || !bomb->IsAlive()) @@ -8166,7 +8155,7 @@ void IccLichKingAddsAction::HandleSpiritMarkingAndTargeting(Difficulty diff, Uni if (currentMarkedTarget && currentMarkedTarget->IsAlive()) { Unit* spiritTarget = currentMarkedTarget->GetVictim(); - if (spiritTarget && spiritTarget->GetTypeId() == TYPEID_PLAYER) + if (spiritTarget && spiritTarget->IsPlayer()) { if (Group* spiritTargetGroup = spiritTarget->ToPlayer()->GetGroup()) { @@ -8206,7 +8195,7 @@ void IccLichKingAddsAction::HandleSpiritMarkingAndTargeting(Difficulty diff, Uni // Check if this spirit is targeting a group member bool targetingGroupMember = false; Unit* spiritTarget = unit->GetVictim(); - if (spiritTarget && spiritTarget->GetTypeId() == TYPEID_PLAYER) + if (spiritTarget && spiritTarget->IsPlayer()) { if (Group* spiritTargetGroup = spiritTarget->ToPlayer()->GetGroup()) { @@ -9347,4 +9336,3 @@ void IccLichKingAddsAction::HandleVileSpiritMechanics() } } } - diff --git a/src/strategy/raids/icecrown/RaidIccActions.h b/src/strategy/raids/icecrown/RaidIccActions.h index 17cab50424..9ac2a48a04 100644 --- a/src/strategy/raids/icecrown/RaidIccActions.h +++ b/src/strategy/raids/icecrown/RaidIccActions.h @@ -311,8 +311,6 @@ class IccPutricideGrowingOozePuddleAction : public AttackAction Unit* FindClosestThreateningPuddle(); Position CalculateSafeMovePosition(Unit* closestPuddle); bool IsPositionTooCloseToOtherPuddles(float x, float y, Unit* ignorePuddle); - - }; class IccPutricideVolatileOozeAction : public AttackAction @@ -599,7 +597,6 @@ class IccSindragosaTankSwapPositionAction : public AttackAction bool Execute(Event event) override; }; - //LK class IccLichKingShadowTrapAction : public MovementAction { @@ -633,7 +630,6 @@ class IccLichKingWinterAction : public AttackAction void HandleMainTankAddManagement(Unit* boss, const Position* tankPos); void HandleAssistTankAddManagement(Unit* boss, const Position* tankPos); - private: const Position* GetMainTankPosition(); const Position* GetMainTankRangedPosition(); @@ -670,6 +666,4 @@ class IccLichKingAddsAction : public AttackAction void HandleVileSpiritMechanics(); }; - - #endif diff --git a/src/strategy/raids/icecrown/RaidIccMultipliers.cpp b/src/strategy/raids/icecrown/RaidIccMultipliers.cpp index 8c71110305..fc02d56bea 100644 --- a/src/strategy/raids/icecrown/RaidIccMultipliers.cpp +++ b/src/strategy/raids/icecrown/RaidIccMultipliers.cpp @@ -51,7 +51,7 @@ float IccLadyDeathwhisperMultiplier::GetValue(Action* action) if (dynamic_cast(action)) return 1.0f; - for (const auto& npcGuid : npcs) + for (auto const& npcGuid : npcs) { Unit* shade = botAI->GetUnit(npcGuid); @@ -156,7 +156,6 @@ float IccFestergutMultiplier::GetValue(Action* action) if (bot->HasAura(SPELL_GAS_SPORE)) return 0.0f; - return 1.0f; } @@ -375,7 +374,7 @@ float IccBpcAssistMultiplier::GetValue(Action* action) for (const auto entry : bombEntries) { - for (const auto& guid : bombs) + for (auto const& guid : bombs) { if (Unit* unit = botAI->GetUnit(guid)) { @@ -491,7 +490,6 @@ float IccValithriaDreamCloudMultiplier::GetValue(Action* action) Aura* twistedNightmares = botAI->GetAura("Twisted Nightmares", bot); Aura* emeraldVigor = botAI->GetAura("Emerald Vigor", bot); - if (!boss && !bot->HasAura(SPELL_DREAM_STATE)) return 1.0f; diff --git a/src/strategy/raids/icecrown/RaidIccMultipliers.h b/src/strategy/raids/icecrown/RaidIccMultipliers.h index 8eb1f6f14a..69d1ac51ba 100644 --- a/src/strategy/raids/icecrown/RaidIccMultipliers.h +++ b/src/strategy/raids/icecrown/RaidIccMultipliers.h @@ -99,5 +99,4 @@ class IccLichKingAddsMultiplier : public Multiplier virtual float GetValue(Action* action); }; - #endif diff --git a/src/strategy/raids/icecrown/RaidIccStrategy.cpp b/src/strategy/raids/icecrown/RaidIccStrategy.cpp index 6bdb0e6bcf..1acf1e7884 100644 --- a/src/strategy/raids/icecrown/RaidIccStrategy.cpp +++ b/src/strategy/raids/icecrown/RaidIccStrategy.cpp @@ -115,7 +115,6 @@ void RaidIccStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode("icc sister svalna", NextAction::array(0, new NextAction("icc sister svalna", ACTION_RAID + 5), nullptr))); - //VDW triggers.push_back(new TriggerNode("icc valithria group", NextAction::array(0, new NextAction("icc valithria group", ACTION_RAID + 1), nullptr))); diff --git a/src/strategy/raids/icecrown/RaidIccTriggers.cpp b/src/strategy/raids/icecrown/RaidIccTriggers.cpp index 7ea7e845fc..44a2008b1b 100644 --- a/src/strategy/raids/icecrown/RaidIccTriggers.cpp +++ b/src/strategy/raids/icecrown/RaidIccTriggers.cpp @@ -299,7 +299,7 @@ bool IccPutricideGrowingOozePuddleTrigger::IsActive() } const GuidVector& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - for (const auto& npc : npcs) + for (auto const& npc : npcs) { if (Unit* unit = botAI->GetUnit(npc)) { @@ -477,7 +477,7 @@ bool IccBpcKineticBombTrigger::IsActive() for (const auto entry : bombEntries) { - for (const auto& guid : bombs) + for (auto const& guid : bombs) { if (Unit* unit = botAI->GetUnit(guid)) { diff --git a/src/strategy/raids/icecrown/RaidIccTriggers.h b/src/strategy/raids/icecrown/RaidIccTriggers.h index e51f62adc7..cd332e0046 100644 --- a/src/strategy/raids/icecrown/RaidIccTriggers.h +++ b/src/strategy/raids/icecrown/RaidIccTriggers.h @@ -336,7 +336,6 @@ class IccRotfaceGroupPositionTrigger : public Trigger bool IsActive() override; }; - class IccRotfaceMoveAwayFromExplosionTrigger : public Trigger { public: @@ -359,7 +358,6 @@ class IccPutricideGasCloudTrigger : public Trigger bool IsActive() override; }; - class IccPutricideGrowingOozePuddleTrigger : public Trigger { public: @@ -454,7 +452,6 @@ class IccSisterSvalnaTrigger : public Trigger bool IsActive() override; }; - // Valithria Dreamwalker class IccValithriaGroupTrigger : public Trigger @@ -485,7 +482,6 @@ class IccValithriaDreamCloudTrigger : public Trigger bool IsActive() override; }; - //SINDRAGOSA class IccSindragosaGroupPositionTrigger : public Trigger { @@ -550,7 +546,6 @@ class IccSindragosaFrostBombTrigger : public Trigger bool IsActive() override; }; - //LICH KING class IccLichKingShadowTrapTrigger : public Trigger { diff --git a/src/strategy/raids/karazhan/RaidKarazhanActions.cpp b/src/strategy/raids/karazhan/RaidKarazhanActions.cpp index 8ec3ea9143..e45d63f7b2 100644 --- a/src/strategy/raids/karazhan/RaidKarazhanActions.cpp +++ b/src/strategy/raids/karazhan/RaidKarazhanActions.cpp @@ -7,7 +7,7 @@ #include "Playerbots.h" #include "Position.h" -namespace +namespace { // Big Bad Wolf static int currentIndex = 0; @@ -131,7 +131,7 @@ bool KarazhanMaidenOfVirtuePositionRangedAction::Execute(Event event) const GuidVector members = AI_VALUE(GuidVector, "group members"); - for (const auto& memberGuid : members) + for (auto const& memberGuid : members) { Unit* member = botAI->GetUnit(memberGuid); @@ -205,7 +205,7 @@ bool KarazhanBigBadWolfPositionBossAction::isUseful() { Unit* boss = AI_VALUE2(Unit*, "find target", "the big bad wolf"); - return boss && botAI->IsTank(bot) && botAI->HasAggro(boss) && boss->GetVictim() == bot && + return boss && botAI->IsTank(bot) && botAI->HasAggro(boss) && boss->GetVictim() == bot && !bot->HasAura(SPELL_LITTLE_RED_RIDING_HOOD); } @@ -369,16 +369,16 @@ bool KarazhanTerestianIllhoofMarkTargetAction::Execute(Event event) RaidKarazhanHelpers karazhanHelper(botAI); Unit* target = karazhanHelper.GetFirstAliveUnitByEntry(NPC_DEMON_CHAINS); - if (!target || !target->IsAlive()) + if (!target || !target->IsAlive()) { target = karazhanHelper.GetFirstAliveUnitByEntry(NPC_KILREK); - if (!target || !target->IsAlive()) + if (!target || !target->IsAlive()) { target = boss; } } karazhanHelper.MarkTargetWithSkull(target); - + return false; } @@ -420,7 +420,7 @@ bool KarazhanShadeOfAranFlameWreathStopMovementAction::Execute(Event event) } return true; } - + return false; } @@ -430,7 +430,7 @@ bool KarazhanShadeOfAranMarkConjuredElementalAction::Execute(Event event) Unit* boss = AI_VALUE2(Unit*, "find target", "shade of aran"); Unit* target = karazhanHelper.GetFirstAliveUnitByEntry(NPC_CONJURED_ELEMENTAL); - if (!boss || !boss->IsAlive() || + if (!boss || !boss->IsAlive() || !target || !target->IsAlive() || target->HasAura(SPELL_WARLOCK_BANISH)) { return false; @@ -522,12 +522,12 @@ bool KarazhanNetherspiteBlockRedBeamAction::Execute(Event event) lastBeamMoveSideways[botGuid] = !lastBeamMoveSideways[botGuid]; beamMoveTimes[botGuid] = time(nullptr); } - if (!lastBeamMoveSideways[botGuid]) + if (!lastBeamMoveSideways[botGuid]) { - return MoveTo(bot->GetMapId(), beamPos.GetPositionX(), beamPos.GetPositionY(), beamPos.GetPositionZ(), + return MoveTo(bot->GetMapId(), beamPos.GetPositionX(), beamPos.GetPositionY(), beamPos.GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_FORCED); - } - else + } + else { float bx = boss->GetPositionX(); float by = boss->GetPositionY(); @@ -549,7 +549,7 @@ bool KarazhanNetherspiteBlockRedBeamAction::Execute(Event event) float sideY = beamPos.GetPositionY() + perpDy * 3.0f; float sideZ = beamPos.GetPositionZ(); - return MoveTo(bot->GetMapId(), sideX, sideY, sideZ, false, false, false, true, + return MoveTo(bot->GetMapId(), sideX, sideY, sideZ, false, false, false, true, MovementPriority::MOVEMENT_FORCED); } } @@ -602,7 +602,7 @@ bool KarazhanNetherspiteBlockBlueBeamAction::Execute(Event event) "netherspite_beam_leaving_blue", "%player is leaving the blue beam--next blocker up!", ph); bot->Yell(text, LANG_UNIVERSAL); wasBlockingBlueBeam[botGuid] = false; - + return false; } @@ -637,17 +637,17 @@ bool KarazhanNetherspiteBlockBlueBeamAction::Execute(Event event) float bestDist = 150.0f; Position bestPos; bool found = false; - for (float dist = 18.0f; dist <= 30.0f; dist += 0.5f) + for (float dist = 18.0f; dist <= 30.0f; dist += 0.5f) { float candidateX = bx + dx * dist; float candidateY = by + dy * dist; float candidateZ = bz; bool outsideAllVoidZones = true; - for (Unit* voidZone : voidZones) + for (Unit* voidZone : voidZones) { - float voidZoneDist = sqrt(pow(candidateX - voidZone->GetPositionX(), 2) + + float voidZoneDist = sqrt(pow(candidateX - voidZone->GetPositionX(), 2) + pow(candidateY - voidZone->GetPositionY(), 2)); - if (voidZoneDist < 4.0f) + if (voidZoneDist < 4.0f) { outsideAllVoidZones = false; break; @@ -658,14 +658,14 @@ bool KarazhanNetherspiteBlockBlueBeamAction::Execute(Event event) continue; } float distToIdeal = fabs(dist - 18.0f); - if (!found || distToIdeal < bestDist) + if (!found || distToIdeal < bestDist) { bestDist = distToIdeal; bestPos = Position(candidateX, candidateY, candidateZ); found = true; } } - if (found) + if (found) { bot->AttackStop(); bot->InterruptNonMeleeSpells(false); @@ -747,17 +747,17 @@ bool KarazhanNetherspiteBlockGreenBeamAction::Execute(Event event) float bestDist = 150.0f; Position bestPos; bool found = false; - for (float dist = 18.0f; dist <= 30.0f; dist += 0.5f) + for (float dist = 18.0f; dist <= 30.0f; dist += 0.5f) { float candidateX = bx + dx * dist; float candidateY = by + dy * dist; float candidateZ = bz; bool outsideAllVoidZones = true; - for (Unit* voidZone : voidZones) + for (Unit* voidZone : voidZones) { - float voidZoneDist = sqrt(pow(candidateX - voidZone->GetPositionX(), 2) + + float voidZoneDist = sqrt(pow(candidateX - voidZone->GetPositionX(), 2) + pow(candidateY - voidZone->GetPositionY(), 2)); - if (voidZoneDist < 4.0f) + if (voidZoneDist < 4.0f) { outsideAllVoidZones = false; break; @@ -768,19 +768,19 @@ bool KarazhanNetherspiteBlockGreenBeamAction::Execute(Event event) continue; } float distToIdeal = fabs(dist - 18.0f); - if (!found || distToIdeal < bestDist) + if (!found || distToIdeal < bestDist) { bestDist = distToIdeal; bestPos = Position(candidateX, candidateY, candidateZ); found = true; } } - if (found) + if (found) { bot->AttackStop(); bot->InterruptNonMeleeSpells(false); - return MoveTo(bot->GetMapId(), bestPos.GetPositionX(), bestPos.GetPositionY(), bestPos.GetPositionZ(), + return MoveTo(bot->GetMapId(), bestPos.GetPositionX(), bestPos.GetPositionY(), bestPos.GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_FORCED); } @@ -820,7 +820,7 @@ bool KarazhanNetherspiteAvoidBeamAndVoidZoneAction::Execute(Event event) Unit* redPortal = bot->FindNearestCreature(NPC_RED_PORTAL, 150.0f); Unit* bluePortal = bot->FindNearestCreature(NPC_BLUE_PORTAL, 150.0f); Unit* greenPortal = bot->FindNearestCreature(NPC_GREEN_PORTAL, 150.0f); - if (redPortal) + if (redPortal) { float bx = boss->GetPositionX(), by = boss->GetPositionY(); float px = redPortal->GetPositionX(), py = redPortal->GetPositionY(); @@ -828,7 +828,7 @@ bool KarazhanNetherspiteAvoidBeamAndVoidZoneAction::Execute(Event event) float length = sqrt(dx*dx + dy*dy); beams.push_back({redPortal, 0.0f, length}); } - if (bluePortal) + if (bluePortal) { float bx = boss->GetPositionX(), by = boss->GetPositionY(); float px = bluePortal->GetPositionX(), py = bluePortal->GetPositionY(); @@ -836,7 +836,7 @@ bool KarazhanNetherspiteAvoidBeamAndVoidZoneAction::Execute(Event event) float length = sqrt(dx*dx + dy*dy); beams.push_back({bluePortal, 0.0f, length}); } - if (greenPortal) + if (greenPortal) { float bx = boss->GetPositionX(), by = boss->GetPositionY(); float px = greenPortal->GetPositionX(), py = greenPortal->GetPositionY(); @@ -845,7 +845,7 @@ bool KarazhanNetherspiteAvoidBeamAndVoidZoneAction::Execute(Event event) beams.push_back({greenPortal, 0.0f, length}); } bool nearBeam = false; - for (const auto& beam : beams) + for (auto const& beam : beams) { float bx = boss->GetPositionX(), by = boss->GetPositionY(); float px = beam.portal->GetPositionX(), py = beam.portal->GetPositionY(); @@ -888,13 +888,13 @@ bool KarazhanNetherspiteAvoidBeamAndVoidZoneAction::Execute(Event event) continue; } bool tooCloseToBeam = false; - for (const auto& beam : beams) + for (auto const& beam : beams) { float bx = boss->GetPositionX(), by = boss->GetPositionY(); float px = beam.portal->GetPositionX(), py = beam.portal->GetPositionY(); float dx = px - bx, dy = py - by; float length = sqrt(dx*dx + dy*dy); - if (length == 0.0f) + if (length == 0.0f) { continue; } @@ -918,7 +918,7 @@ bool KarazhanNetherspiteAvoidBeamAndVoidZoneAction::Execute(Event event) { continue; } - if (!found || moveDist < bestDist) + if (!found || moveDist < bestDist) { bestCandidate = Position(cx, cy, cz); bestDist = moveDist; @@ -926,17 +926,17 @@ bool KarazhanNetherspiteAvoidBeamAndVoidZoneAction::Execute(Event event) } } } - if (found && karazhanHelper.IsSafePosition(bestCandidate.GetPositionX(), - bestCandidate.GetPositionY(), bestCandidate.GetPositionZ(), + if (found && karazhanHelper.IsSafePosition(bestCandidate.GetPositionX(), + bestCandidate.GetPositionY(), bestCandidate.GetPositionZ(), voidZones, 4.0f)) { bot->AttackStop(); bot->InterruptNonMeleeSpells(false); - return MoveTo(bot->GetMapId(), bestCandidate.GetPositionX(), bestCandidate.GetPositionY(), + return MoveTo(bot->GetMapId(), bestCandidate.GetPositionX(), bestCandidate.GetPositionY(), bestCandidate.GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_COMBAT); } - + return false; } @@ -984,7 +984,7 @@ bool KarazhanNetherspiteBanishPhaseAvoidVoidZoneAction::isUseful() RaidKarazhanHelpers karazhanHelper(botAI); std::vector voidZones = karazhanHelper.GetAllVoidZones(); - for (Unit* vz : voidZones) + for (Unit* vz : voidZones) { if (bot->GetExactDist2d(vz) < 4.0f) { @@ -1056,7 +1056,7 @@ bool KarazhanPrinceMalchezaarNonTankAvoidHazardAction::Execute(Event event) bot->AttackStop(); bot->InterruptNonMeleeSpells(false); - return MoveTo(bot->GetMapId(), bestDestX, bestDestY, bestDestZ, false, false, false, true, + return MoveTo(bot->GetMapId(), bestDestX, bestDestY, bestDestZ, false, false, false, true, MovementPriority::MOVEMENT_FORCED); } @@ -1123,7 +1123,7 @@ bool KarazhanPrinceMalchezaarNonTankAvoidHazardAction::Execute(Event event) bot->AttackStop(); bot->InterruptNonMeleeSpells(false); - return MoveTo(bot->GetMapId(), bestDestX, bestDestY, bestDestZ, false, false, false, true, + return MoveTo(bot->GetMapId(), bestDestX, bestDestY, bestDestZ, false, false, false, true, MovementPriority::MOVEMENT_COMBAT); } } @@ -1135,7 +1135,7 @@ bool KarazhanPrinceMalchezaarNonTankAvoidHazardAction::Execute(Event event) bool KarazhanPrinceMalchezaarNonTankAvoidHazardAction::isUseful() { Unit* boss = AI_VALUE2(Unit*, "find target", "prince malchezaar"); - + return boss && !(botAI->IsTank(bot) && botAI->HasAggro(boss) && boss->GetVictim() == bot); } @@ -1198,7 +1198,7 @@ bool KarazhanPrinceMalchezaarTankAvoidHazardAction::Execute(Event event) if (!destSafe) continue; - bool pathSafe = karazhanHelper.IsStraightPathSafe(Position(bx, by, bz), Position(destX, destY, destZ), + bool pathSafe = karazhanHelper.IsStraightPathSafe(Position(bx, by, bz), Position(destX, destY, destZ), infernals, safeInfernalDistance, stepSize); float moveDist = sqrt(pow(destX - bx, 2) + pow(destY - by, 2)); if (pathSafe && moveDist < bestMoveDist) @@ -1255,7 +1255,7 @@ bool KarazhanPrinceMalchezaarTankAvoidHazardAction::Execute(Event event) bot->AttackStop(); bot->InterruptNonMeleeSpells(false); - return MoveTo(bot->GetMapId(), bestDestX, bestDestY, bestDestZ, false, false, false, true, + return MoveTo(bot->GetMapId(), bestDestX, bestDestY, bestDestZ, false, false, false, true, MovementPriority::MOVEMENT_COMBAT); } } @@ -1266,6 +1266,6 @@ bool KarazhanPrinceMalchezaarTankAvoidHazardAction::Execute(Event event) bool KarazhanPrinceMalchezaarTankAvoidHazardAction::isUseful() { Unit* boss = AI_VALUE2(Unit*, "find target", "prince malchezaar"); - + return boss && botAI->IsTank(bot) && botAI->HasAggro(boss) && boss->GetVictim() == bot; } diff --git a/src/strategy/raids/karazhan/RaidKarazhanHelpers.cpp b/src/strategy/raids/karazhan/RaidKarazhanHelpers.cpp index b4d6d5dfb8..296341746f 100644 --- a/src/strategy/raids/karazhan/RaidKarazhanHelpers.cpp +++ b/src/strategy/raids/karazhan/RaidKarazhanHelpers.cpp @@ -22,11 +22,11 @@ const Position KARAZHAN_MAIDEN_OF_VIRTUE_RANGED_POSITION[8] = }; const Position KARAZHAN_BIG_BAD_WOLF_BOSS_POSITION = Position(-10913.391f, -1773.508f, 90.477f); -const Position KARAZHAN_BIG_BAD_WOLF_RUN_POSITION[4] = +const Position KARAZHAN_BIG_BAD_WOLF_RUN_POSITION[4] = { { -10875.456f, -1779.036f, 90.477f }, { -10872.281f, -1751.638f, 90.477f }, - { -10910.492f, -1747.401f, 90.477f }, + { -10910.492f, -1747.401f, 90.477f }, { -10913.391f, -1773.508f, 90.477f }, }; @@ -68,7 +68,7 @@ Unit* RaidKarazhanHelpers::GetFirstAliveUnitByEntry(uint32 entry) { const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - for (const auto& npcGuid : npcs) + for (auto const& npcGuid : npcs) { Unit* unit = botAI->GetUnit(npcGuid); @@ -253,7 +253,7 @@ std::tuple RaidKarazhanHelpers::GetCurrentBeamBlocker std::vector redBlockers = GetRedBlockers(); if (!redBlockers.empty()) { - auto it = std::find_if(redBlockers.begin(), redBlockers.end(), [](Player* p) + auto it = std::find_if(redBlockers.begin(), redBlockers.end(), [](Player* p) { return p && p->GetGUID() == currentRedBlocker; }); @@ -276,7 +276,7 @@ std::tuple RaidKarazhanHelpers::GetCurrentBeamBlocker std::vector greenBlockers = GetGreenBlockers(); if (!greenBlockers.empty()) { - auto it = std::find_if(greenBlockers.begin(), greenBlockers.end(), [](Player* p) + auto it = std::find_if(greenBlockers.begin(), greenBlockers.end(), [](Player* p) { return p && p->GetGUID() == currentGreenBlocker; }); @@ -299,7 +299,7 @@ std::tuple RaidKarazhanHelpers::GetCurrentBeamBlocker std::vector blueBlockers = GetBlueBlockers(); if (!blueBlockers.empty()) { - auto it = std::find_if(blueBlockers.begin(), blueBlockers.end(), [](Player* p) + auto it = std::find_if(blueBlockers.begin(), blueBlockers.end(), [](Player* p) { return p && p->GetGUID() == currentBlueBlocker; }); @@ -327,7 +327,7 @@ std::vector RaidKarazhanHelpers::GetAllVoidZones() std::vector voidZones; const float radius = 30.0f; const GuidVector npcs = botAI->GetAiObjectContext()->GetValue("nearest npcs")->Get(); - for (const auto& npcGuid : npcs) + for (auto const& npcGuid : npcs) { Unit* unit = botAI->GetUnit(npcGuid); if (!unit || unit->GetEntry() != NPC_VOID_ZONE) @@ -363,7 +363,7 @@ std::vector RaidKarazhanHelpers::GetSpawnedInfernals() const { std::vector infernals; const GuidVector npcs = botAI->GetAiObjectContext()->GetValue("nearest npcs")->Get(); - for (const auto& npcGuid : npcs) + for (auto const& npcGuid : npcs) { Unit* unit = botAI->GetUnit(npcGuid); if (unit && unit->GetEntry() == NPC_NETHERSPITE_INFERNAL) @@ -388,7 +388,7 @@ bool RaidKarazhanHelpers::IsStraightPathSafe(const Position& start, const Positi { return true; } - + for (float checkDist = 0.0f; checkDist <= totalDist; checkDist += stepSize) { float t = checkDist / totalDist; @@ -404,6 +404,6 @@ bool RaidKarazhanHelpers::IsStraightPathSafe(const Position& start, const Positi } } } - + return true; } diff --git a/src/strategy/raids/karazhan/RaidKarazhanHelpers.h b/src/strategy/raids/karazhan/RaidKarazhanHelpers.h index 89d06a950b..d26d8501d1 100644 --- a/src/strategy/raids/karazhan/RaidKarazhanHelpers.h +++ b/src/strategy/raids/karazhan/RaidKarazhanHelpers.h @@ -78,7 +78,7 @@ class RaidKarazhanHelpers : public AiObject bool IsSafePosition (float x, float y, float z, const std::vector& hazards, float hazardRadius); std::vector GetSpawnedInfernals() const; - bool IsStraightPathSafe(const Position& start, const Position& target, + bool IsStraightPathSafe(const Position& start, const Position& target, const std::vector& hazards, float hazardRadius, float stepSize); }; diff --git a/src/strategy/raids/karazhan/RaidKarazhanMultipliers.cpp b/src/strategy/raids/karazhan/RaidKarazhanMultipliers.cpp index ac23560338..f68cbe8b57 100644 --- a/src/strategy/raids/karazhan/RaidKarazhanMultipliers.cpp +++ b/src/strategy/raids/karazhan/RaidKarazhanMultipliers.cpp @@ -40,7 +40,7 @@ float KarazhanBigBadWolfMultiplier::GetValue(Action* action) if (bot->HasAura(SPELL_LITTLE_RED_RIDING_HOOD)) { - if ((dynamic_cast(action) && !dynamic_cast(action)) || + if ((dynamic_cast(action) && !dynamic_cast(action)) || (dynamic_cast(action))) { return 0.0f; @@ -58,7 +58,7 @@ float KarazhanShadeOfAranMultiplier::GetValue(Action* action) return 1.0f; } - if (boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_ARCANE_EXPLOSION)) + if (boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_ARCANE_EXPLOSION)) { if (IsChargeAction(action)) { @@ -120,7 +120,7 @@ float KarazhanNetherspiteBlueAndGreenBeamMultiplier::GetValue(Action* action) Unit* bluePortal = bot->FindNearestCreature(NPC_BLUE_PORTAL, 150.0f); Unit* greenPortal = bot->FindNearestCreature(NPC_GREEN_PORTAL, 150.0f); bool inBeam = false; - for (Unit* portal : {bluePortal, greenPortal}) + for (Unit* portal : {bluePortal, greenPortal}) { if (!portal) { @@ -139,7 +139,7 @@ float KarazhanNetherspiteBlueAndGreenBeamMultiplier::GetValue(Action* action) float t = (botdx * dx + botdy * dy); float beamX = bx + dx * t, beamY = by + dy * t; float distToBeam = sqrt(pow(bot->GetPositionX() - beamX, 2) + pow(bot->GetPositionY() - beamY, 2)); - if (distToBeam < 0.3f && t > 0.0f && t < length) + if (distToBeam < 0.3f && t > 0.0f && t < length) { inBeam = true; break; @@ -149,7 +149,7 @@ float KarazhanNetherspiteBlueAndGreenBeamMultiplier::GetValue(Action* action) { std::vector voidZones = karazhanHelper.GetAllVoidZones(); bool inVoidZone = false; - for (Unit* vz : voidZones) + for (Unit* vz : voidZones) { if (bot->GetExactDist2d(vz) < 4.0f) { @@ -232,7 +232,7 @@ float KarazhanNetherspiteRedBeamMultiplier::GetValue(Action* action) } } } - + return 1.0f; } diff --git a/src/strategy/raids/naxxramas/RaidNaxxMultipliers.cpp b/src/strategy/raids/naxxramas/RaidNaxxMultipliers.cpp index 3b41ec0dc4..2ffe429d1d 100644 --- a/src/strategy/raids/naxxramas/RaidNaxxMultipliers.cpp +++ b/src/strategy/raids/naxxramas/RaidNaxxMultipliers.cpp @@ -140,15 +140,18 @@ float ThaddiusGenericMultiplier::GetValue(Action* action) } // magnetic pull // uint32 curr_timer = eventMap->GetTimer(); - // // if (curr_phase == 2 && bot->GetPositionZ() > 312.5f && dynamic_cast(action)) { + // // if (curr_phase == 2 && bot->GetPositionZ() > 312.5f && dynamic_cast(action)) + // { // if (curr_phase == 2 && (curr_timer % 20000 >= 18000 || curr_timer % 20000 <= 2000) && - // dynamic_cast(action)) { + // dynamic_cast(action)) + // { // // MotionMaster *mm = bot->GetMotionMaster(); // // mm->Clear(); // return 0.0f; // } // thaddius phase - // if (curr_phase == 8 && dynamic_cast(action)) { + // if (curr_phase == 8 && dynamic_cast(action)) + // { // return 0.0f; // } return 1.0f; @@ -263,18 +266,22 @@ float FourhorsemanGenericMultiplier::GetValue(Action* action) // float GothikGenericMultiplier::GetValue(Action* action) // { // Unit* boss = AI_VALUE2(Unit*, "find target", "gothik the harvester"); -// if (!boss) { +// if (!boss) +// { // return 1.0f; // } // BossAI* boss_ai = dynamic_cast(boss->GetAI()); // EventMap* eventMap = boss_botAI->GetEvents(); // uint32 curr_phase = eventMap->GetPhaseMask(); -// if (curr_phase == 1 && (dynamic_cast(action))) { +// if (curr_phase == 1 && (dynamic_cast(action))) +// { // return 0.0f; // } -// if (curr_phase == 1 && (dynamic_cast(action))) { +// if (curr_phase == 1 && (dynamic_cast(action))) +// { // Unit* target = action->GetTarget(); -// if (target == boss) { +// if (target == boss) +// { // return 0.0f; // } // } diff --git a/src/strategy/raids/naxxramas/RaidNaxxTriggers.h b/src/strategy/raids/naxxramas/RaidNaxxTriggers.h index ec10c5ded3..a1f5bc2a8f 100644 --- a/src/strategy/raids/naxxramas/RaidNaxxTriggers.h +++ b/src/strategy/raids/naxxramas/RaidNaxxTriggers.h @@ -178,7 +178,6 @@ class SapphironGroundTrigger : public Trigger SapphironBossHelper helper; }; - class SapphironFlightTrigger : public Trigger { public: @@ -221,4 +220,4 @@ class LoathebTrigger : public Trigger LoathebBossHelper helper; }; -#endif \ No newline at end of file +#endif diff --git a/src/strategy/raids/obsidiansanctum/RaidOsTriggers.cpp b/src/strategy/raids/obsidiansanctum/RaidOsTriggers.cpp index 2deda7e88b..2c3758856d 100644 --- a/src/strategy/raids/obsidiansanctum/RaidOsTriggers.cpp +++ b/src/strategy/raids/obsidiansanctum/RaidOsTriggers.cpp @@ -17,7 +17,6 @@ bool FlameTsunamiTrigger::IsActive() Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); if (!boss) { return false; } - GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); for (auto& npc : npcs) { @@ -39,7 +38,6 @@ bool TwilightFissureTrigger::IsActive() Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); if (!boss) { return false; } - GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); for (auto& npc : npcs) { @@ -98,7 +96,6 @@ bool TwilightPortalEnterTrigger::IsActive() // } // } - // Don't enter portal until drakes are dead if (bot->HasAura(SPELL_POWER_OF_SHADRON) || bot->HasAura(SPELL_POWER_OF_TENEBRON) || @@ -125,4 +122,4 @@ bool TwilightPortalEnterTrigger::IsActive() bool TwilightPortalExitTrigger::IsActive() { return bot->HasAura(SPELL_TWILIGHT_SHIFT) && !AI_VALUE2(Unit*, "find target", "acolyte of shadron"); -} \ No newline at end of file +} diff --git a/src/strategy/raids/ulduar/RaidUlduarActions.cpp b/src/strategy/raids/ulduar/RaidUlduarActions.cpp index 7f7af7bab6..b7c3723bb3 100644 --- a/src/strategy/raids/ulduar/RaidUlduarActions.cpp +++ b/src/strategy/raids/ulduar/RaidUlduarActions.cpp @@ -1001,7 +1001,7 @@ bool RazorscaleHarpoonAction::Execute(Event event) float minDistance = std::numeric_limits::max(); // Find the nearest harpoon that hasn't been fired and is not on cooldown - for (const auto& harpoon : harpoonData) + for (auto const& harpoon : harpoonData) { if (razorscaleHelper.IsHarpoonFired(harpoon.chainSpellId)) continue; @@ -1087,7 +1087,7 @@ bool RazorscaleHarpoonAction::isUseful() const std::vector& harpoonData = razorscaleHelper.GetHarpoonData(); - for (const auto& harpoon : harpoonData) + for (auto const& harpoon : harpoonData) { if (razorscaleHelper.IsHarpoonFired(harpoon.chainSpellId)) continue; diff --git a/src/strategy/raids/ulduar/RaidUlduarBossHelper.cpp b/src/strategy/raids/ulduar/RaidUlduarBossHelper.cpp index b82d70459e..72333a079b 100644 --- a/src/strategy/raids/ulduar/RaidUlduarBossHelper.cpp +++ b/src/strategy/raids/ulduar/RaidUlduarBossHelper.cpp @@ -84,7 +84,7 @@ GameObject* RazorscaleBossHelper::FindNearestHarpoon(float x, float y, float z) GameObject* nearestHarpoon = nullptr; float minDistanceSq = std::numeric_limits::max(); - for (const auto& harpoon : GetHarpoonData()) + for (auto const& harpoon : GetHarpoonData()) { if (GameObject* harpoonGO = bot->FindNearestGameObject(harpoon.gameObjectEntry, 200.0f)) { @@ -158,7 +158,6 @@ bool RazorscaleBossHelper::CanSwapRoles() const return (currentTime - lastSwapTime) >= _roleSwapCooldown; } - void RazorscaleBossHelper::AssignRolesBasedOnHealth() { // Check if enough time has passed since last swap diff --git a/src/strategy/raids/ulduar/RaidUlduarTriggers.cpp b/src/strategy/raids/ulduar/RaidUlduarTriggers.cpp index 4222205268..89ad7ac5fb 100644 --- a/src/strategy/raids/ulduar/RaidUlduarTriggers.cpp +++ b/src/strategy/raids/ulduar/RaidUlduarTriggers.cpp @@ -211,7 +211,7 @@ bool RazorscaleHarpoonAvailableTrigger::IsActive() } // Check each harpoon entry - for (const auto& harpoon : harpoonData) + for (auto const& harpoon : harpoonData) { // Skip harpoons whose chain spell is already active on the boss if (razorscaleHelper.IsHarpoonFired(harpoon.chainSpellId)) @@ -808,7 +808,6 @@ bool ThorimMarkDpsTargetTrigger::IsActive() if (!boss || !boss->IsAlive() || !boss->IsHostileTo(bot)) return false; - if (boss->GetPositionZ() < ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD && (!currentSkullUnit || !currentSkullUnit->IsAlive())) { group->SetTargetIcon(RtiTargetValue::skullIndex, bot->GetGUID(), boss->GetGUID()); diff --git a/src/strategy/rogue/RogueActions.cpp b/src/strategy/rogue/RogueActions.cpp index 8a04a01d0c..3019e0ec86 100644 --- a/src/strategy/rogue/RogueActions.cpp +++ b/src/strategy/rogue/RogueActions.cpp @@ -19,7 +19,6 @@ bool CastStealthAction::isUseful() return true; } - bool CastStealthAction::isPossible() { // do not use with WSG flag or EYE flag diff --git a/src/strategy/rogue/RogueTriggers.h b/src/strategy/rogue/RogueTriggers.h index 4ef826a8c3..d9a184238d 100644 --- a/src/strategy/rogue/RogueTriggers.h +++ b/src/strategy/rogue/RogueTriggers.h @@ -42,7 +42,6 @@ class BladeFuryTrigger : public BoostTrigger BladeFuryTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "blade fury") {} }; - class RuptureTrigger : public DebuffTrigger { public: @@ -127,6 +126,4 @@ class TricksOfTheTradeOnMainTankTrigger : public BuffOnMainTankTrigger TricksOfTheTradeOnMainTankTrigger(PlayerbotAI* ai) : BuffOnMainTankTrigger(ai, "tricks of the trade", true) {} }; - - #endif diff --git a/src/strategy/shaman/ElementalShamanStrategy.cpp b/src/strategy/shaman/ElementalShamanStrategy.cpp index 3e058925ff..350b347219 100644 --- a/src/strategy/shaman/ElementalShamanStrategy.cpp +++ b/src/strategy/shaman/ElementalShamanStrategy.cpp @@ -73,4 +73,3 @@ void ElementalShamanStrategy::InitTriggers(std::vector& triggers) // Range Triggers triggers.push_back(new TriggerNode("enemy is close", NextAction::array(0, new NextAction("thunderstorm", 19.0f), nullptr))); } - diff --git a/src/strategy/shaman/ShamanActions.cpp b/src/strategy/shaman/ShamanActions.cpp index 4915aa2780..e8ed1234bf 100644 --- a/src/strategy/shaman/ShamanActions.cpp +++ b/src/strategy/shaman/ShamanActions.cpp @@ -29,7 +29,7 @@ bool CastFireNovaAction::isUseful() { Unit* target = AI_VALUE(Unit*, "current target"); if (!target) return false; - + Creature* fireTotem = bot->GetMap()->GetCreature(bot->m_SummonSlot[1]); if (!fireTotem) return false; @@ -46,9 +46,9 @@ bool CastCleansingTotemAction::isUseful() } // Will only cast Stoneclaw Totem if low on health and not in a group -bool CastStoneclawTotemAction::isUseful() -{ - return !bot->GetGroup(); +bool CastStoneclawTotemAction::isUseful() +{ + return !bot->GetGroup(); } // Will only cast Lava Burst if Flame Shock is on the target diff --git a/src/strategy/shaman/ShamanActions.h b/src/strategy/shaman/ShamanActions.h index 3af0113436..69f29f0496 100644 --- a/src/strategy/shaman/ShamanActions.h +++ b/src/strategy/shaman/ShamanActions.h @@ -286,7 +286,6 @@ class CastLavaBurstAction : public CastSpellAction bool isUseful() override; }; - // Healing Actions class CastLesserHealingWaveAction : public CastHealingSpellAction diff --git a/src/strategy/shaman/ShamanTriggers.cpp b/src/strategy/shaman/ShamanTriggers.cpp index 574ab638d7..753edb83fd 100644 --- a/src/strategy/shaman/ShamanTriggers.cpp +++ b/src/strategy/shaman/ShamanTriggers.cpp @@ -19,7 +19,7 @@ bool MainHandWeaponNoImbueTrigger::IsActive() Item* const itemForSpell = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND); if (!itemForSpell || itemForSpell->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT)) return false; - + return true; } @@ -28,18 +28,18 @@ bool OffHandWeaponNoImbueTrigger::IsActive() Item* const itemForSpell = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); if (!itemForSpell) return false; - + uint32 invType = itemForSpell->GetTemplate()->InventoryType; bool allowedType = (invType == INVTYPE_WEAPON) || (invType == INVTYPE_WEAPONOFFHAND); if (itemForSpell->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT) || !allowedType) return false; - + return true; } bool ShockTrigger::IsActive() { - return SpellTrigger::IsActive() && + return SpellTrigger::IsActive() && !botAI->HasAura("flame shock", GetTarget(), false, true) && !botAI->HasAura("frost shock", GetTarget(), false, true); } @@ -64,19 +64,19 @@ bool EarthShockExecuteTrigger::IsActive() bool TotemTrigger::IsActive() { - return AI_VALUE(uint8, "attacker count") >= attackerCount && + return AI_VALUE(uint8, "attacker count") >= attackerCount && !AI_VALUE2(bool, "has totem", name) && !botAI->HasAura(name, bot); } -bool WaterWalkingTrigger::IsActive() -{ - return BuffTrigger::IsActive() && AI_VALUE2(bool, "swimming", "self target"); +bool WaterWalkingTrigger::IsActive() +{ + return BuffTrigger::IsActive() && AI_VALUE2(bool, "swimming", "self target"); } -bool WaterBreathingTrigger::IsActive() -{ - return BuffTrigger::IsActive() && AI_VALUE2(bool, "swimming", "self target"); +bool WaterBreathingTrigger::IsActive() +{ + return BuffTrigger::IsActive() && AI_VALUE2(bool, "swimming", "self target"); } bool WaterWalkingOnPartyTrigger::IsActive() @@ -123,7 +123,7 @@ bool SpiritWalkTrigger::IsActive() } } } - + return false; } @@ -138,7 +138,7 @@ bool CallOfTheElementsTrigger::IsActive() int emptyCount = 0; static const uint8 slots[] = { - SUMMON_SLOT_TOTEM_EARTH, SUMMON_SLOT_TOTEM_FIRE, + SUMMON_SLOT_TOTEM_EARTH, SUMMON_SLOT_TOTEM_FIRE, SUMMON_SLOT_TOTEM_WATER, SUMMON_SLOT_TOTEM_AIR }; @@ -210,10 +210,10 @@ bool TotemicRecallTrigger::IsActive() Player* member = ref->GetSource(); if (!member) continue; - + if (member->IsInCombat()) return false; - + Pet* pet = member->GetPet(); if (pet && pet->IsInCombat()) return false; @@ -261,7 +261,7 @@ bool TotemicRecallTrigger::IsActive() } // Find the active totem strategy for this slot, and return the highest-rank spellId the bot knows for it -static uint32 GetRequiredTotemSpellId(PlayerbotAI* ai, const char* strategies[], +static uint32 GetRequiredTotemSpellId(PlayerbotAI* ai, const char* strategies[], const uint32* spellList[], const size_t spellCounts[], size_t numStrategies) { Player* bot = ai->GetBot(); @@ -279,7 +279,7 @@ static uint32 GetRequiredTotemSpellId(PlayerbotAI* ai, const char* strategies[], } } } - + return 0; // No relevant strategy active, or bot doesn't know any rank } @@ -289,11 +289,11 @@ static uint32 GetSummonedTotemSpellId(Player* bot, uint8 slot) ObjectGuid guid = bot->m_SummonSlot[slot]; if (guid.IsEmpty()) return 0; - + Creature* totem = bot->GetMap()->GetCreature(guid); if (!totem) return 0; - + return totem->GetUInt32Value(UNIT_CREATED_BY_SPELL); } @@ -309,7 +309,7 @@ bool NoEarthTotemTrigger::IsActive() if (!guid.IsEmpty()) { totem = bot->GetMap()->GetCreature(guid); - if (totem) + if (totem) { currentSpell = totem->GetUInt32Value(UNIT_CREATED_BY_SPELL); } @@ -320,18 +320,18 @@ bool NoEarthTotemTrigger::IsActive() static const uint32* spells[] = {STRENGTH_OF_EARTH_TOTEM, STONESKIN_TOTEM, TREMOR_TOTEM, EARTHBIND_TOTEM}; static const size_t counts[] = {STRENGTH_OF_EARTH_TOTEM_COUNT, STONESKIN_TOTEM_COUNT, TREMOR_TOTEM_COUNT, EARTHBIND_TOTEM_COUNT}; - + uint32 requiredSpell = GetRequiredTotemSpellId(botAI, names, spells, counts, 4); // EXCEPTION: If Stoneclaw Totem is out and in range, consider the slot "occupied" (do not fire the trigger) - for (size_t i = 0; i < STONECLAW_TOTEM_COUNT; ++i) + for (size_t i = 0; i < STONECLAW_TOTEM_COUNT; ++i) { if (currentSpell == STONECLAW_TOTEM[i] && totem && totem->GetDistance(bot) <= 30.0f) return false; } // If no relevant strategy, only care if the slot is empty or totem is too far away - if (!requiredSpell) + if (!requiredSpell) return guid.IsEmpty() || !totem || totem->GetDistance(bot) > 30.0f; // Fire if slot is empty or wrong totem or totem is too far away @@ -350,7 +350,7 @@ bool NoFireTotemTrigger::IsActive() if (!guid.IsEmpty()) { totem = bot->GetMap()->GetCreature(guid); - if (totem) + if (totem) { currentSpell = totem->GetUInt32Value(UNIT_CREATED_BY_SPELL); } @@ -366,7 +366,7 @@ bool NoFireTotemTrigger::IsActive() uint32 requiredSpell = GetRequiredTotemSpellId(botAI, names, spells, counts, 5); // EXCEPTION: If Fire Elemental is out and in range, consider the slot "occupied" (do not fire the trigger) - for (size_t i = 0; i < FIRE_ELEMENTAL_TOTEM_COUNT; ++i) + for (size_t i = 0; i < FIRE_ELEMENTAL_TOTEM_COUNT; ++i) { if (currentSpell == FIRE_ELEMENTAL_TOTEM[i] && totem && totem->GetDistance(bot) <= 30.0f) return false; @@ -407,14 +407,14 @@ bool NoWaterTotemTrigger::IsActive() uint32 requiredSpell = GetRequiredTotemSpellId(botAI, names, spells, counts, 4); // EXCEPTION: If Mana Tide is out and in range, consider the slot "occupied" (do not fire the trigger) - for (size_t i = 0; i < MANA_TIDE_TOTEM_COUNT; ++i) + for (size_t i = 0; i < MANA_TIDE_TOTEM_COUNT; ++i) { if (currentSpell == MANA_TIDE_TOTEM[i] && totem && totem->GetDistance(bot) <= 30.0f) return false; } // If no relevant strategy, only care if the slot is empty or totem is too far away - if (!requiredSpell) + if (!requiredSpell) { return guid.IsEmpty() || !totem || totem->GetDistance(bot) > 30.0f; } diff --git a/src/strategy/shaman/ShamanTriggers.h b/src/strategy/shaman/ShamanTriggers.h index 7fbaf2fd8b..abf4ed566c 100644 --- a/src/strategy/shaman/ShamanTriggers.h +++ b/src/strategy/shaman/ShamanTriggers.h @@ -374,7 +374,7 @@ class SetTotemTrigger : public Trigger private: uint32 requiredSpellId; uint32 const* totemSpellIds; - size_t totemSpellIdsCount; + size_t totemSpellIdsCount; int actionButtonId; }; diff --git a/src/strategy/triggers/GenericTriggers.h b/src/strategy/triggers/GenericTriggers.h index 5f03f8a134..943e80b742 100644 --- a/src/strategy/triggers/GenericTriggers.h +++ b/src/strategy/triggers/GenericTriggers.h @@ -550,7 +550,7 @@ class MediumManaTrigger : public Trigger bool IsActive() override; }; -BEGIN_TRIGGER(PanicTrigger, Trigger) +BEGIN_TRIGGER(PanicTrigger, Trigger) // cppcheck-suppress unknownMacro std::string const getName() override { return "panic"; } END_TRIGGER() diff --git a/src/strategy/triggers/PvpTriggers.cpp b/src/strategy/triggers/PvpTriggers.cpp index f70ef1d7a3..c837d8b16c 100644 --- a/src/strategy/triggers/PvpTriggers.cpp +++ b/src/strategy/triggers/PvpTriggers.cpp @@ -217,7 +217,6 @@ bool TeamHasFlag::IsActive() return ownTeamHasFlag && !enemyTeamHasFlag; } - bool EnemyTeamHasFlag::IsActive() { if (botAI->GetBot()->InBattleground()) diff --git a/src/strategy/triggers/RangeTriggers.cpp b/src/strategy/triggers/RangeTriggers.cpp index ee1266e607..3c60934fd2 100644 --- a/src/strategy/triggers/RangeTriggers.cpp +++ b/src/strategy/triggers/RangeTriggers.cpp @@ -22,7 +22,8 @@ bool EnemyTooCloseForSpellTrigger::IsActive() return target && (target->GetVictim() != bot || target->isFrozen() || target->HasRootAura()) && target->GetObjectSize() <= 10.0f && target->IsWithinCombatRange(bot, MIN_MELEE_REACH); // Unit* target = AI_VALUE(Unit*, "current target"); - // if (!target) { + // if (!target) + // { // return false; // } @@ -34,7 +35,7 @@ bool EnemyTooCloseForSpellTrigger::IsActive() // bool isRaid = false; // float combatReach = bot->GetCombatReach() + target->GetCombatReach(); // float targetDistance = sServerFacade->GetDistance2d(bot, target) + combatReach; - // if (target->GetTypeId() == TYPEID_UNIT) + // if (target->IsCreature()) // { // Creature* creature = botAI->GetCreature(target->GetGUID()); // if (creature) @@ -80,7 +81,7 @@ bool EnemyTooCloseForAutoShotTrigger::IsActive() // bool isRaid = false; // float combatReach = bot->GetCombatReach() + target->GetCombatReach(); // float targetDistance = sServerFacade->GetDistance2d(bot, target) + combatReach; - // if (target->GetTypeId() == TYPEID_UNIT) + // if (target->IsCreature()) // { // Creature* creature = botAI->GetCreature(target->GetGUID()); // if (creature) @@ -115,7 +116,7 @@ bool EnemyTooCloseForShootTrigger::IsActive() // bool isRaid = false; // float combatReach = bot->GetCombatReach() + target->GetCombatReach(); // float targetDistance = sServerFacade->GetDistance2d(bot, target) + combatReach; - // if (target->GetTypeId() == TYPEID_UNIT) + // if (target->IsCreature()) // { // Creature* creature = botAI->GetCreature(target->GetGUID()); // if (creature) diff --git a/src/strategy/triggers/TriggerContext.h b/src/strategy/triggers/TriggerContext.h index 1323e65344..1c829ae9f6 100644 --- a/src/strategy/triggers/TriggerContext.h +++ b/src/strategy/triggers/TriggerContext.h @@ -53,7 +53,6 @@ class TriggerContext : public NamedObjectContext creators["almost full mana"] = &TriggerContext::AlmostFullMana; creators["enough mana"] = &TriggerContext::EnoughMana; - creators["party member critical health"] = &TriggerContext::PartyMemberCriticalHealth; creators["party member low health"] = &TriggerContext::PartyMemberLowHealth; creators["party member medium health"] = &TriggerContext::PartyMemberMediumHealth; diff --git a/src/strategy/triggers/WorldPacketTriggerContext.h b/src/strategy/triggers/WorldPacketTriggerContext.h index 0f7f4c6e86..1e73095244 100644 --- a/src/strategy/triggers/WorldPacketTriggerContext.h +++ b/src/strategy/triggers/WorldPacketTriggerContext.h @@ -45,7 +45,6 @@ class WorldPacketTriggerContext : public NamedObjectContext creators["quest update complete"] = &WorldPacketTriggerContext::quest_update_complete; creators["questgiver quest details"] = &WorldPacketTriggerContext::questgiver_quest_details; - creators["item push result"] = &WorldPacketTriggerContext::item_push_result; creators["party command"] = &WorldPacketTriggerContext::party_command; creators["taxi done"] = &WorldPacketTriggerContext::taxi_done; diff --git a/src/strategy/values/EnemyPlayerValue.cpp b/src/strategy/values/EnemyPlayerValue.cpp index 9c2a413042..2325c9c09b 100644 --- a/src/strategy/values/EnemyPlayerValue.cpp +++ b/src/strategy/values/EnemyPlayerValue.cpp @@ -83,7 +83,7 @@ Unit* EnemyPlayerValue::Calculate() GuidVector players = AI_VALUE(GuidVector, "nearest enemy players"); float const maxAggroDistance = GetMaxAttackDistance(); - for (const auto& gTarget : players) + for (auto const& gTarget : players) { Unit* pUnit = botAI->GetUnit(gTarget); if (!pUnit) diff --git a/src/strategy/values/HasTotemValue.cpp b/src/strategy/values/HasTotemValue.cpp index 94a51837e0..0968b2e01a 100644 --- a/src/strategy/values/HasTotemValue.cpp +++ b/src/strategy/values/HasTotemValue.cpp @@ -57,7 +57,8 @@ bool HasTotemValue::Calculate() // continue; // Creature* creature = dynamic_cast(unit); -// if (creature->GetOwner() != bot) { +// if (creature->GetOwner() != bot) +// { // continue; // } diff --git a/src/strategy/values/ItemUsageValue.cpp b/src/strategy/values/ItemUsageValue.cpp index 136c74f8a4..3be0db2c1f 100644 --- a/src/strategy/values/ItemUsageValue.cpp +++ b/src/strategy/values/ItemUsageValue.cpp @@ -22,10 +22,13 @@ ItemUsage ItemUsageValue::Calculate() uint32 itemId = 0; uint32 randomPropertyId = 0; size_t pos = qualifier.find(","); - if (pos != std::string::npos) { + if (pos != std::string::npos) + { itemId = atoi(qualifier.substr(0, pos).c_str()); randomPropertyId = atoi(qualifier.substr(pos + 1).c_str()); - } else { + } + else + { itemId = atoi(qualifier.c_str()); } @@ -261,7 +264,7 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto, { needToCheckUnique = true; } - else if (itemProto->Flags & ITEM_FLAG_UNIQUE_EQUIPPABLE) + else if (itemProto->HasFlag(ITEM_FLAG_UNIQUE_EQUIPPABLE)) { needToCheckUnique = true; } @@ -373,7 +376,6 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto, } } - for (uint8 i = 0; i < possibleSlots; i++) { bool shouldEquipInSlot = shouldEquip; diff --git a/src/strategy/values/PossibleRpgTargetsValue.cpp b/src/strategy/values/PossibleRpgTargetsValue.cpp index 1b136c825c..6e78f37f9a 100644 --- a/src/strategy/values/PossibleRpgTargetsValue.cpp +++ b/src/strategy/values/PossibleRpgTargetsValue.cpp @@ -54,18 +54,18 @@ void PossibleRpgTargetsValue::FindUnits(std::list& targets) bool PossibleRpgTargetsValue::AcceptUnit(Unit* unit) { - if (unit->IsHostileTo(bot) || unit->GetTypeId() == TYPEID_PLAYER) + if (unit->IsHostileTo(bot) || unit->IsPlayer()) return false; if (sServerFacade->GetDistance2d(bot, unit) <= sPlayerbotAIConfig->tooCloseDistance) return false; - if (unit->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPIRITHEALER)) + if (unit->HasNpcFlag(UNIT_NPC_FLAG_SPIRITHEALER)) return false; for (uint32 npcFlag : allowedNpcFlags) { - if (unit->HasFlag(UNIT_NPC_FLAGS, npcFlag)) + if (unit->HasNpcFlag(static_cast(npcFlag))) return true; } @@ -82,7 +82,6 @@ bool PossibleRpgTargetsValue::AcceptUnit(Unit* unit) return false; } - std::vector PossibleNewRpgTargetsValue::allowedNpcFlags; PossibleNewRpgTargetsValue::PossibleNewRpgTargetsValue(PlayerbotAI* botAI, float range) @@ -127,11 +126,11 @@ GuidVector PossibleNewRpgTargetsValue::Calculate() guidDistancePairs.push_back({unit->GetGUID(), bot->GetExactDist(unit)}); } // Override to sort by distance - std::sort(guidDistancePairs.begin(), guidDistancePairs.end(), [](const auto& a, const auto& b) { + std::sort(guidDistancePairs.begin(), guidDistancePairs.end(), [](auto const& a, auto const& b) { return a.second < b.second; }); - for (const auto& pair : guidDistancePairs) { + for (auto const& pair : guidDistancePairs) { results.push_back(pair.first); } return results; @@ -146,15 +145,15 @@ void PossibleNewRpgTargetsValue::FindUnits(std::list& targets) bool PossibleNewRpgTargetsValue::AcceptUnit(Unit* unit) { - if (unit->IsHostileTo(bot) || unit->GetTypeId() == TYPEID_PLAYER) + if (unit->IsHostileTo(bot) || unit->IsPlayer()) return false; - if (unit->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPIRITHEALER)) + if (unit->HasNpcFlag(UNIT_NPC_FLAG_SPIRITHEALER)) return false; for (uint32 npcFlag : allowedNpcFlags) { - if (unit->HasFlag(UNIT_NPC_FLAGS, npcFlag)) + if (unit->HasNpcFlag(static_cast(npcFlag))) return true; } @@ -170,7 +169,6 @@ GuidVector PossibleNewRpgGameObjectsValue::Calculate() Acore::GameObjectListSearcher searcher(bot, targets, u_check); Cell::VisitObjects(bot, searcher, range); - std::vector> guidDistancePairs; for (GameObject* go : targets) { @@ -194,11 +192,11 @@ GuidVector PossibleNewRpgGameObjectsValue::Calculate() GuidVector results; // Sort by distance - std::sort(guidDistancePairs.begin(), guidDistancePairs.end(), [](const auto& a, const auto& b) { + std::sort(guidDistancePairs.begin(), guidDistancePairs.end(), [](auto const& a, auto const& b) { return a.second < b.second; }); - for (const auto& pair : guidDistancePairs) { + for (auto const& pair : guidDistancePairs) { results.push_back(pair.first); } return results; diff --git a/src/strategy/warlock/WarlockTriggers.h b/src/strategy/warlock/WarlockTriggers.h index 295b6d895b..8512549012 100644 --- a/src/strategy/warlock/WarlockTriggers.h +++ b/src/strategy/warlock/WarlockTriggers.h @@ -121,7 +121,6 @@ class WrongPetTrigger : public Trigger bool IsActive() override; }; - // CC and Pet Triggers class BanishTrigger : public HasCcTargetTrigger diff --git a/src/strategy/warrior/WarriorActions.cpp b/src/strategy/warrior/WarriorActions.cpp index c679a6b584..9733226a95 100644 --- a/src/strategy/warrior/WarriorActions.cpp +++ b/src/strategy/warrior/WarriorActions.cpp @@ -126,7 +126,7 @@ bool CastRetaliationAction::isUseful() continue; // Check if the attacker is melee-based using unit_class - if (attacker->GetTypeId() == TYPEID_UNIT) + if (attacker->IsCreature()) { Creature* creature = attacker->ToCreature(); if (creature && (creature->IsClass(CLASS_WARRIOR) @@ -136,7 +136,7 @@ bool CastRetaliationAction::isUseful() ++meleeAttackers; } } - else if (attacker->GetTypeId() == TYPEID_PLAYER) + else if (attacker->IsPlayer()) { Player* playerAttacker = attacker->ToPlayer(); if (playerAttacker && botAI->IsMelee(playerAttacker)) // Reuse existing Player melee check diff --git a/src/strategy/warrior/WarriorTriggers.cpp b/src/strategy/warrior/WarriorTriggers.cpp index 4ca2e5b9c7..b561c4815d 100644 --- a/src/strategy/warrior/WarriorTriggers.cpp +++ b/src/strategy/warrior/WarriorTriggers.cpp @@ -119,4 +119,3 @@ bool ShatteringThrowTrigger::IsActive() return false; // No valid targets within range } - diff --git a/src/strategy/warrior/WarriorTriggers.h b/src/strategy/warrior/WarriorTriggers.h index 4e4f4cabf4..563d707698 100644 --- a/src/strategy/warrior/WarriorTriggers.h +++ b/src/strategy/warrior/WarriorTriggers.h @@ -80,7 +80,6 @@ class ShatteringThrowTrigger : public Trigger bool IsActive() override; }; - // class SlamTrigger : public HasAuraTrigger // { // public: From e5bc495dbeff73afba4e00b3485ea83e65103b81 Mon Sep 17 00:00:00 2001 From: kadeshar Date: Fri, 7 Nov 2025 17:49:33 +0100 Subject: [PATCH 20/47] Fixed codestyle fix (#1815) Fix for this changes obraz --- src/GuildTaskMgr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GuildTaskMgr.cpp b/src/GuildTaskMgr.cpp index 37589ebd6b..8fb4711dd7 100644 --- a/src/GuildTaskMgr.cpp +++ b/src/GuildTaskMgr.cpp @@ -1067,7 +1067,7 @@ void GuildTaskMgr::SendCompletionMessage(Player* player, std::string const verb) void GuildTaskMgr::CheckKillTaskInternal(Player* player, Unit* victim) { ObjectGuid::LowType owner = player->GetGUID().GetCounter(); - if (victim->IsCreature()) + if (!victim->IsCreature()) return; Creature* creature = reinterpret_cast(victim); From 9c8ba42c641e857975595a9fc514a970d8857603 Mon Sep 17 00:00:00 2001 From: SaW Date: Tue, 11 Nov 2025 09:10:09 +0100 Subject: [PATCH 21/47] FIX: Battlegrounds - Unset bot's master when current master left BG (#1819) Adds a check for if current master left the BG and group, if so release set master and carry on in BG. This prevents multiple bots in (potentially multiple different) BG's to still consider you as their master when you yourself have left. - Applies when you join BGs with a party of bots. --- src/PlayerbotAI.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 414b4b2275..4292a48b33 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -367,11 +367,19 @@ void PlayerbotAI::UpdateAIGroupAndMaster() { if (!bot) return; + + PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); + if (!botAI) + return; + Group* group = bot->GetGroup(); + + bool IsRandomBot = sRandomPlayerbotMgr->IsRandomBot(bot); + // If bot is not in group verify that for is RandomBot before clearing master and resetting. if (!group) { - if (master && sRandomPlayerbotMgr->IsRandomBot(bot)) + if (master && IsRandomBot) { SetMaster(nullptr); Reset(true); @@ -380,12 +388,10 @@ void PlayerbotAI::UpdateAIGroupAndMaster() return; } - if (bot->InBattleground() && bot->GetBattleground()->GetBgTypeID() != BATTLEGROUND_AV) - return; - - PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); - if (!botAI) - return; + // Bot in BG, but master no longer part of a group: release master + // Exclude alt and addclass bots as they rely on current (real player) master, security-wise. + if (bot->InBattleground() && IsRandomBot && master && !master->GetGroup()) + SetMaster(nullptr); PlayerbotAI* masterBotAI = nullptr; if (master) From a37dd2b9ae64904cb636cdbcd5dd6bd7c83ab418 Mon Sep 17 00:00:00 2001 From: nick <1854029+biship@users.noreply.github.com> Date: Tue, 11 Nov 2025 03:21:13 -0500 Subject: [PATCH 22/47] Clarify random bot timing configuration section and parameter descriptions (#1826) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This update reorganizes and rewrites the random bot timing configuration section for clarity and accuracy. The previous section was mislabeled as "INTERVALS" and lacked precise descriptions. The new version: 1. Renames the header to RANDOM BOT TIMING AND BEHAVIOR 2. Adds concise, standardized comments for each parameter 3. Corrects misleading terminology (not all values are intervals) 4. Documents defaults and actual behavior clearly for easier tuning and maintenance 5. No functional code changes — documentation and readability only. Note, this is derived information from reading the code. Please double check if I have captured each param accurately! --- conf/playerbots.conf.dist | 28 +++++++++++++++++++++++++--- src/PlayerbotAIConfig.cpp | 4 ++-- src/PlayerbotAIConfig.h | 2 +- src/RandomPlayerbotMgr.cpp | 2 +- 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index c38df8f09e..1b9a01dbf3 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -1196,24 +1196,46 @@ AiPlayerbot.FastReactInBG = 1 #################################################################################################### #################################################################################################### -# INTERVALS +# RANDOM BOT TIMING AND BEHAVIOR # # -# All in seconds +# How often (in seconds) the random bot manager runs its main update loop +# Default: 20 AiPlayerbot.RandomBotUpdateInterval = 20 + +# Minimum and maximum seconds before the manager re-evaluates and adjusts total random bot count +# Defaults: 1800 (min), 7200 (max) AiPlayerbot.RandomBotCountChangeMinInterval = 1800 AiPlayerbot.RandomBotCountChangeMaxInterval = 7200 + +# Minimum and maximum seconds a random bot will stay online before logging out +# Defaults: 600 (min), 28800 (max) AiPlayerbot.MinRandomBotInWorldTime = 600 AiPlayerbot.MaxRandomBotInWorldTime = 28800 + +# Minimum and maximum seconds before a bot is eligible for re-randomization (gear, class, talents, etc.) +# Defaults: 7200 (min), 1209600 (max) AiPlayerbot.MinRandomBotRandomizeTime = 7200 AiPlayerbot.MaxRandomBotRandomizeTime = 1209600 + +# Number of bots processed (login, logout, update) per manager update cycle +# Default: 60 AiPlayerbot.RandomBotsPerInterval = 60 + +# Minimum and maximum seconds after death before a bot revives +# Defaults: 60 (min), 300 (max) AiPlayerbot.MinRandomBotReviveTime = 60 AiPlayerbot.MaxRandomBotReviveTime = 300 + +# Minimum and maximum seconds between bot teleports to new areas or zones +# Defaults: 3600 (min), 18000 (max) AiPlayerbot.MinRandomBotTeleportInterval = 3600 AiPlayerbot.MaxRandomBotTeleportInterval = 18000 -AiPlayerbot.PermanantlyInWorldTime = 31104000 + +# Number of seconds bots flagged as permanent stay in the world (31,104,000 ≈ 1 year) +# Default: 31104000 +AiPlayerbot.PermanentlyInWorldTime = 31104000 # # diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index 42ab801fb5..bdafc10c63 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -199,8 +199,8 @@ bool PlayerbotAIConfig::Initialize() maxRandomBotReviveTime = sConfigMgr->GetOption("AiPlayerbot.MaxRandomBotReviveTime", 5 * MINUTE); minRandomBotTeleportInterval = sConfigMgr->GetOption("AiPlayerbot.MinRandomBotTeleportInterval", 1 * HOUR); maxRandomBotTeleportInterval = sConfigMgr->GetOption("AiPlayerbot.MaxRandomBotTeleportInterval", 5 * HOUR); - permanantlyInWorldTime = - sConfigMgr->GetOption("AiPlayerbot.PermanantlyInWorldTime", 1 * YEAR); + permanentlyInWorldTime = + sConfigMgr->GetOption("AiPlayerbot.PermanentlyInWorldTime", 1 * YEAR); randomBotTeleportDistance = sConfigMgr->GetOption("AiPlayerbot.RandomBotTeleportDistance", 100); randomBotsPerInterval = sConfigMgr->GetOption("AiPlayerbot.RandomBotsPerInterval", 60); minRandomBotsPriceChangeInterval = diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index 391b991a21..83a6a20b9a 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -132,7 +132,7 @@ class PlayerbotAIConfig uint32 minRandomBotChangeStrategyTime, maxRandomBotChangeStrategyTime; uint32 minRandomBotReviveTime, maxRandomBotReviveTime; uint32 minRandomBotTeleportInterval, maxRandomBotTeleportInterval; - uint32 permanantlyInWorldTime; + uint32 permanentlyInWorldTime; uint32 minRandomBotPvpTime, maxRandomBotPvpTime; uint32 randomBotsPerInterval; uint32 minRandomBotsPriceChangeInterval, maxRandomBotsPriceChangeInterval; diff --git a/src/RandomPlayerbotMgr.cpp b/src/RandomPlayerbotMgr.cpp index abd2b40216..89a960aecb 100644 --- a/src/RandomPlayerbotMgr.cpp +++ b/src/RandomPlayerbotMgr.cpp @@ -822,7 +822,7 @@ uint32 RandomPlayerbotMgr::AddRandomBots() uint32 add_time = sPlayerbotAIConfig->enablePeriodicOnlineOffline ? urand(sPlayerbotAIConfig->minRandomBotInWorldTime, sPlayerbotAIConfig->maxRandomBotInWorldTime) - : sPlayerbotAIConfig->permanantlyInWorldTime; + : sPlayerbotAIConfig->permanentlyInWorldTime; SetEventValue(charInfo.guid, "add", 1, add_time); SetEventValue(charInfo.guid, "logout", 0, 0); From 0729d14787abc84ac4dc5d143e9c8eedf26c8923 Mon Sep 17 00:00:00 2001 From: privatecore Date: Thu, 13 Nov 2025 00:51:24 +0100 Subject: [PATCH 23/47] Fix PlayerbotAI constructors' members order and wrong type comparison (uint32 -> int32) (#1763) Fix warnings: -Wsign-compare and -Wsign-compare. I would also like to mention that there are issues with the following methods: `IsMainTank` -- in the last loop, it returns the result of comparing the first alive tank it finds with the current bot, which seems odd. `IsBotMainTank` -- it is used in only two places in the code (Yogg-Saron strategy) and has a completely different logic for checking. In the last loop, it only checks for a suitable tank-bot with a lower index (if it's not the bot itself). Additionally, there is a logic issue in the loop: if none of the conditions are met after the first iteration, the method returns `false`. Can someone from the maintainers take a look at this section of the code for possible errors and logic issues? --- src/PlayerbotAI.cpp | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 4292a48b33..44e601bffc 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -106,14 +106,14 @@ void PacketHandlingHelper::AddPacket(WorldPacket const& packet) PlayerbotAI::PlayerbotAI() : PlayerbotAIBase(true), bot(nullptr), + master(nullptr), + accountId(0), aiObjectContext(nullptr), currentEngine(nullptr), + currentState(BOT_STATE_NON_COMBAT), chatHelper(this), chatFilter(this), - accountId(0), - security(nullptr), - master(nullptr), - currentState(BOT_STATE_NON_COMBAT) + security(nullptr) { for (uint8 i = 0; i < BOT_STATE_MAX; i++) engines[i] = nullptr; @@ -128,9 +128,9 @@ PlayerbotAI::PlayerbotAI() PlayerbotAI::PlayerbotAI(Player* bot) : PlayerbotAIBase(true), bot(bot), + master(nullptr), chatHelper(this), chatFilter(this), - master(nullptr), security(bot) // reorder args - whipowill { if (!bot->isTaxiCheater() && HasCheat((BotCheatMask::taxi))) @@ -1801,11 +1801,11 @@ int32 PlayerbotAI::GetAssistTankIndex(Player* player) { return -1; } + int counter = 0; for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { Player* member = ref->GetSource(); - if (!member) { continue; @@ -1815,11 +1815,13 @@ int32 PlayerbotAI::GetAssistTankIndex(Player* player) { return counter; } + if (IsTank(member, true) && group->IsAssistant(member->GetGUID())) { counter++; } } + return 0; } @@ -2132,14 +2134,15 @@ bool PlayerbotAI::IsMainTank(Player* player) break; } } + if (mainTank != ObjectGuid::Empty) { return player->GetGUID() == mainTank; } + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { Player* member = ref->GetSource(); - if (!member) { continue; @@ -2150,6 +2153,7 @@ bool PlayerbotAI::IsMainTank(Player* player) return player->GetGUID() == member->GetGUID(); } } + return false; } @@ -2171,8 +2175,7 @@ bool PlayerbotAI::IsBotMainTank(Player* player) return true; // If no group, consider the bot as main tank } - uint32 botAssistTankIndex = GetAssistTankIndex(player); - + int32 botAssistTankIndex = GetAssistTankIndex(player); if (botAssistTankIndex == -1) { return false; @@ -2186,8 +2189,7 @@ bool PlayerbotAI::IsBotMainTank(Player* player) continue; } - uint32 memberAssistTankIndex = GetAssistTankIndex(member); - + int32 memberAssistTankIndex = GetAssistTankIndex(member); if (memberAssistTankIndex == -1) { continue; From 08c739f9185a88d9118ef9f282f845a89b37acd0 Mon Sep 17 00:00:00 2001 From: Crow Date: Fri, 14 Nov 2025 08:17:43 -0600 Subject: [PATCH 24/47] Align index with section update (#1831) Update to index to reflect change to section title --- conf/playerbots.conf.dist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 1b9a01dbf3..a782479550 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -36,7 +36,7 @@ # RPG STRATEGY # TELEPORTS # BATTLEGROUND & ARENA & PVP -# INTERVALS +# RANDOM BOT TIMING AND BEHAVIOR # PREMADE SPECS # INFORMATION # WARRIOR From cadbcbd447886e1f2361ec7de2a54c0067d2233c Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 15 Nov 2025 09:27:33 +0000 Subject: [PATCH 25/47] fix: Resolved an issue where the open spell was being cast by bots on despawned game objects. (#1842) # Description This addresses the infamous "Possible hacking attempt" error when bots were furiously trying to interact with despawned game objects. The problem was that the game objects were not being as spawned when evaluating what the bot should do. It was only done when spells were being cast on entities. --- src/PlayerbotAI.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 44e601bffc..23d073f54c 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -14,6 +14,7 @@ #include "BudgetValues.h" #include "ChannelMgr.h" #include "CharacterPackets.h" +#include "ChatHelper.h" #include "Common.h" #include "CreatureAIImpl.h" #include "CreatureData.h" @@ -283,6 +284,15 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal) return; } + GameObject* goSpellTarget = currentSpell->m_targets.GetGOTarget(); + + if (goSpellTarget && !goSpellTarget->isSpawned()) + { + InterruptSpell(); + YieldThread(GetReactDelay()); + return; + } + bool isHeal = false; bool isSingleTarget = true; From 6effabfa42a2f793c417dfdfff948736430adae2 Mon Sep 17 00:00:00 2001 From: Alex Dcnh <140754794+Wishmaster117@users.noreply.github.com> Date: Sat, 15 Nov 2025 18:19:16 +0100 Subject: [PATCH 26/47] Core - Fix Bots can pickup the flag in Eye of the Strom instantly, without cast time from the spawn point (#1704) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Eye of the Storm Flag Capture Behavior   ## Previous Behavior - Bots used to interact with the Netherstorm flag the moment they reached interaction range, leading to instant pickups even before they were correctly positioned. - They did not respect the requirement to stand within the capture ring or dismount before channeling, so the interaction finished immediately without the intended delay.   ## Current Behavior - Bots now verify they are inside the Eye of the Storm capture circle and will reposition toward the center flag or base flag until they are within 2.5 yards of the game object before starting the channel. - Once inside the circle they dismount, drop shapeshift forms, and come to a full stop before beginning the channel cast. - When channeling the center flag capture spell, bots keep checking for the ongoing `SPELL_CAPTURE_BANNER` cast and wait for it to finish instead of attempting repeated instant interactions. - They will be interrupted if they receice any damage   These adjustments align the Eye of the Storm flow with the retail mechanics and prevent bots from taking the flag instantly when it spawns. Fixes #1700. --- src/strategy/actions/BattleGroundTactics.cpp | 179 ++++++++++++++++--- 1 file changed, 151 insertions(+), 28 deletions(-) diff --git a/src/strategy/actions/BattleGroundTactics.cpp b/src/strategy/actions/BattleGroundTactics.cpp index db5235f515..827ab01970 100644 --- a/src/strategy/actions/BattleGroundTactics.cpp +++ b/src/strategy/actions/BattleGroundTactics.cpp @@ -4,10 +4,12 @@ */ #include "BattleGroundTactics.h" -#include "BattleGroundJoinAction.h" + +#include #include "ArenaTeam.h" #include "ArenaTeamMgr.h" +#include "BattleGroundJoinAction.h" #include "Battleground.h" #include "BattlegroundAB.h" #include "BattlegroundAV.h" @@ -22,11 +24,12 @@ #include "BattlegroundSA.h" #include "BattlegroundWS.h" #include "Event.h" +#include "GameObject.h" #include "IVMapMgr.h" +#include "PathGenerator.h" #include "Playerbots.h" #include "PositionValue.h" #include "PvpTriggers.h" -#include "PathGenerator.h" #include "ServerFacade.h" #include "Vehicle.h" @@ -1754,7 +1757,7 @@ bool BGTactics::moveToStart(bool force) WS_WAITING_POS_ALLIANCE_2.GetPositionY() + frand(-4.0f, 4.0f), WS_WAITING_POS_ALLIANCE_2.GetPositionZ()); } - else // BB_WSG_WAIT_SPOT_SPAWN + else // BB_WSG_WAIT_SPOT_SPAWN { if (bot->GetTeamId() == TEAM_HORDE) MoveTo(bg->GetMapId(), WS_WAITING_POS_HORDE_3.GetPositionX() + frand(-10.0f, 10.0f), @@ -3365,12 +3368,12 @@ bool BGTactics::resetObjective() return false; // Adjust role-change chance based on battleground type - uint32 oddsToChangeRole = 1; // default low + uint32 oddsToChangeRole = 1; // default low BattlegroundTypeId bgType = bg->GetBgTypeID(); if (bgType == BATTLEGROUND_WS) oddsToChangeRole = 2; - else if (bgType == BATTLEGROUND_EY || bgType == BATTLEGROUND_IC || bgType == BATTLEGROUND_AB) + else if (bgType == BATTLEGROUND_EY || bgType == BATTLEGROUND_IC || bgType == BATTLEGROUND_AB) oddsToChangeRole = 1; else if (bgType == BATTLEGROUND_AV) oddsToChangeRole = 0; @@ -3578,6 +3581,16 @@ bool BGTactics::atFlag(std::vector const& vPaths, std::vector(bg); + if (eyeBg) + eyCenterFlag = eyeBg->GetBGObject(BG_EY_OBJECT_FLAG_NETHERSTORM); + } + // Set up appropriate search ranges and object lists based on BG type switch (bgType) { @@ -3607,27 +3620,82 @@ bool BGTactics::atFlag(std::vector const& vPaths, std::vectorGetCurrentSpell(spellType); + if (!currentSpell || !currentSpell->m_spellInfo || currentSpell->m_spellInfo->Id != SPELL_CAPTURE_BANNER) + return false; + + // If the capture target is no longer available (another bot already captured it), stop channeling + if (GameObject* targetFlag = currentSpell->m_targets.GetGOTarget()) + { + if (!targetFlag->isSpawned() || targetFlag->GetGoState() != GO_STATE_READY) + { + bot->InterruptNonMeleeSpells(true); + resetObjective(); + return false; + } + } + else + { + bot->InterruptNonMeleeSpells(true); + resetObjective(); + return false; + } + + if (bot->IsMounted()) + { + bot->RemoveAurasByType(SPELL_AURA_MOUNTED); + } + + if (bot->IsInDisallowedMountForm()) + { + bot->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT); + } + + if (bot->isMoving()) + { + bot->StopMoving(); + } + + return true; + }; + + // If we are already channeling the capture spell, keep the bot stationary and dismounted + if (keepStationaryWhileCapturing(CURRENT_CHANNELED_SPELL) || keepStationaryWhileCapturing(CURRENT_GENERIC_SPELL)) + return true; + // First identify which flag/base we're trying to interact with GameObject* targetFlag = nullptr; for (ObjectGuid const guid : closeObjects) { GameObject* go = botAI->GetGameObject(guid); if (!go) + { continue; + } + + bool const isEyCenterFlag = eyeBg && eyCenterFlag && eyCenterFlag->GetGUID() == go->GetGUID(); // Check if this object is a valid capture target - std::vector::const_iterator f = find(vFlagIds.begin(), vFlagIds.end(), go->GetEntry()); - if (f == vFlagIds.end()) + std::vector::const_iterator f = std::find(vFlagIds.begin(), vFlagIds.end(), go->GetEntry()); + if (f == vFlagIds.end() && !isEyCenterFlag) + { continue; + } // Verify the object is active and ready if (!go->isSpawned() || go->GetGoState() != GO_STATE_READY) + { continue; + } // Check if we're in range (using double range for enemy detection) float const dist = bot->GetDistance(go); if (flagRange && dist > flagRange * 2.0f) + { continue; + } targetFlag = go; break; @@ -3655,7 +3723,7 @@ bool BGTactics::atFlag(std::vector const& vPaths, std::vector const& vPaths, std::vectorGetUnit(guid)) { - // Check if they're casting the capture spell - if (Spell* spell = pFriend->GetCurrentSpell(CURRENT_GENERIC_SPELL)) + // Check if they're casting or channeling the capture spell + Spell* spell = pFriend->GetCurrentSpell(CURRENT_GENERIC_SPELL); + if (!spell) { - if (spell->m_spellInfo->Id == SPELL_CAPTURE_BANNER) - { - numCapturing++; - capturingPlayer = pFriend; - } + spell = pFriend->GetCurrentSpell(CURRENT_CHANNELED_SPELL); + } + + if (spell && spell->m_spellInfo && spell->m_spellInfo->Id == SPELL_CAPTURE_BANNER) + { + numCapturing++; + capturingPlayer = pFriend; } } } @@ -3704,9 +3775,11 @@ bool BGTactics::atFlag(std::vector const& vPaths, std::vectorGetGUID() == go->GetGUID(); + // Validate this is a capture target - std::vector::const_iterator f = find(vFlagIds.begin(), vFlagIds.end(), go->GetEntry()); - if (f == vFlagIds.end()) + std::vector::const_iterator f = std::find(vFlagIds.begin(), vFlagIds.end(), go->GetEntry()); + if (f == vFlagIds.end() && !isEyCenterFlag) continue; // Check object is active @@ -3722,12 +3795,40 @@ bool BGTactics::atFlag(std::vector const& vPaths, std::vectorGetEntry() == vFlagsWS[bot->GetTeamId()] - : bgType == BATTLEGROUND_EY ? go->GetEntry() == vFlagsEY[0] - : false; + bool isWsBaseFlag = bgType == BATTLEGROUND_WS && go->GetEntry() == vFlagsWS[bot->GetTeamId()]; + bool isEyBaseFlag = bgType == BATTLEGROUND_EY && go->GetEntry() == vFlagsEY[0]; + + // Ensure bots are inside the Eye of the Storm capture circle before casting + if (bgType == BATTLEGROUND_EY) + { + GameObject* captureFlag = (isEyBaseFlag && eyCenterFlag) ? eyCenterFlag : go; + float const requiredRange = 2.5f; + if (!bot->IsWithinDistInMap(captureFlag, requiredRange)) + { + // Stay mounted while relocating to avoid mount/dismount loops + return MoveTo(bot->GetMapId(), captureFlag->GetPositionX(), captureFlag->GetPositionY(), + captureFlag->GetPositionZ()); + } + + // Once inside the circle, dismount and stop before starting the channel + if (bot->IsMounted()) + { + bot->RemoveAurasByType(SPELL_AURA_MOUNTED); + } + + if (bot->IsInDisallowedMountForm()) + { + bot->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT); + } + + if (bot->isMoving()) + { + bot->StopMoving(); + } + } // Don't capture own flag in WSG unless carrying enemy flag - if (atBase && bgType == BATTLEGROUND_WS && + if (isWsBaseFlag && bgType == BATTLEGROUND_WS && !(bot->HasAura(BG_WS_SPELL_WARSONG_FLAG) || bot->HasAura(BG_WS_SPELL_SILVERWING_FLAG))) continue; @@ -3743,7 +3844,7 @@ bool BGTactics::atFlag(std::vector const& vPaths, std::vectorGetObjectSize() + go->GetObjectSize() + 0.1f; return MoveTo(bot->GetMapId(), go->GetPositionX() + (urand(0, 1) ? -moveDist : moveDist), - go->GetPositionY() + (urand(0, 1) ? -moveDist : moveDist), go->GetPositionZ()); + go->GetPositionY() + (urand(0, 1) ? -moveDist : moveDist), go->GetPositionZ()); } // Dismount before capturing @@ -3772,7 +3873,7 @@ bool BGTactics::atFlag(std::vector const& vPaths, std::vectorGetTeamId() == TEAM_HORDE) { @@ -3811,28 +3912,50 @@ bool BGTactics::atFlag(std::vector const& vPaths, std::vectorIsMounted()) + { bot->RemoveAurasByType(SPELL_AURA_MOUNTED); + } if (bot->IsInDisallowedMountForm()) + { bot->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT); + } // Handle center flag differently (requires spell cast) - if (atBase) + if (isEyCenterFlag) { + for (uint8 type = CURRENT_MELEE_SPELL; type <= CURRENT_CHANNELED_SPELL; ++type) + { + if (Spell* currentSpell = bot->GetCurrentSpell(static_cast(type))) + { + // m_spellInfo may be null in some states: protect access + if (currentSpell->m_spellInfo && currentSpell->m_spellInfo->Id == SPELL_CAPTURE_BANNER) + { + bot->StopMoving(); + botAI->SetNextCheckDelay(500); + return true; + } + } + } + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(SPELL_CAPTURE_BANNER); if (!spellInfo) return false; Spell* spell = new Spell(bot, spellInfo, TRIGGERED_NONE); spell->m_targets.SetGOTarget(go); + + bot->StopMoving(); spell->prepare(&spell->m_targets); + botAI->WaitForSpellCast(spell); - //return true; Intended to make a bot cast SPELL_CAPTURE_BANNER and wait for spell finish, but doesn't work and causes infinite loop + resetObjective(); + return true; } // Pick up dropped flag @@ -3849,8 +3972,8 @@ bool BGTactics::atFlag(std::vector const& vPaths, std::vectorGetMapId(), go->GetPositionX(), go->GetPositionY(), go->GetPositionZ()); } } - default: - break; + default: + break; } } From ce2a9904958aa81650aaee593c2d04798f68b61e Mon Sep 17 00:00:00 2001 From: Jay <63983128+Vortikai@users.noreply.github.com> Date: Sat, 15 Nov 2025 23:00:19 +0200 Subject: [PATCH 27/47] README.md edits (#1779) I've noticed some sprawl in the README.md. The following edits have been made around the goal of clarity, brevity, and emphases when needed: - Removed dead link to the Spanish README.md at the top. The README_ES.md file itself is not removed from the repo, but is however out of date and does not seem useful at this time - Removed the addons section. This seems to be covered by the Wiki in the Documentation section unless I'm mistaken? We can add these back if needed - Reorganized installation instructions to emphasize the importance of using the Playerbots branch of AzerothCore for all installations - Moved the platform support from the Frequently Asked Questions section to the installation instructions - Modified punctuation of bulleted list. These can return if it irks someone, but it seems easier to read without them - Added a encouragement to star the project for updates and gain more visibility - Removed the Frequently Asked Questions section. I encourage this to be covered in a wiki page but it can return if deemed necessary. I think a lot of it can be moved to other sections if we really need them in there - Added a "Contributing" section that replaces the Coding Standards section - Coding Standards are included in the Contributing section - Added a second link to the Discord server in the Contributing section - Removed a link to the main Playerbots branch from the introduction section. This is covered and emphasized in the installation section instead - Migrated the encouragements to make bug reports from the introduction, along with the message that this is still in active development, to the contributing section - Reduced redundant language when not necessary (e.g. "As noted above,") - Updated language around custom branch and require branch for uniformity. This will make it more clear to the users about the subject - Removed "Classic" from the "Classic Installation" terminology for being inaccurate. The subject is now known simply as "Cloning the Repositories" while "Docker Installation" still remains distinct. This will make what was formerly known as "Classic Installation" as de facto default - Appended the "Documentation" section with add-on information - Unified all instances of AddOn to the Blizzard spelling without a hyphen following the WoW 3.3.5a client spelling - Appended "AzerothCore" to instances of "branch" to ensure readers they are, in fact, installing AzerothCore here from our required branch --- README.md | 47 ++++++++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 5007b49b82..1008233aa5 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ | 中文 | - Español + Español

@@ -18,25 +18,27 @@ # Playerbots Module -`mod-playerbots` is an [AzerothCore](https://www.azerothcore.org/) module that adds player-like bots to a server. The project is based off [IKE3's Playerbots](https://github.com/ike3/mangosbot) and requires a custom branch of AzerothCore to compile and run: [mod-playerbots/azerothcore-wotlk/tree/Playerbot](https://github.com/mod-playerbots/azerothcore-wotlk/tree/Playerbot). +`mod-playerbots` is an [AzerothCore](https://www.azerothcore.org/) module that adds player-like bots to a server. The project is based off [IKE3's Playerbots](https://github.com/ike3/mangosbot). Features include: -- The ability to log in alt characters as bots, allowing players to interact with their other characters, form parties, level up, and more; -- Random bots that wander through the world, complete quests, and otherwise behave like players, simulating the MMO experience; -- Bots capable of running most raids and battlegrounds; -- Highly configurable settings to define how bots behave; -- Excellent performance, even when running thousands of bots. +- The ability to log in alt characters as bots, allowing players to interact with their other characters, form parties, level up, and more +- Random bots that wander through the world, complete quests, and otherwise behave like players, simulating the MMO experience +- Bots capable of running most raids and battlegrounds +- Highly configurable settings to define how bots behave +- Excellent performance, even when running thousands of bots -**This project is still under development**. If you encounter any errors or experience crashes, we kindly request that you [report them as GitHub issues](https://github.com/mod-playerbots/mod-playerbots/issues/new?template=bug_report.md). Your valuable feedback will help us improve this project collaboratively. - -`mod-playerbots` has a **[Discord server](https://discord.gg/NQm5QShwf9)** where you can discuss the project, ask questions, and get involved in the community! +We also have a **[Discord server](https://discord.gg/NQm5QShwf9)** where you can discuss the project, ask questions, and get involved in the community! ## Installation -### Classic Installation +Supported platforms are Ubuntu, Windows, and macOS. Other Linux distributions may work, but may not receive support. + +**All `mod-playerbots` installations require a custom branch of AzerothCore: [mod-playerbots/azerothcore-wotlk/tree/Playerbot](https://github.com/mod-playerbots/azerothcore-wotlk/tree/Playerbot).** This branch allows the playerbots module to build and function. Updates from the upstream are implemneted regularly to this branch. Instructions for installing this required branch and this module are provided below. + +### Cloning the Repositories -As noted above, `mod-playerbots` requires a custom branch of AzerothCore: [mod-playerbots/azerothcore-wotlk/tree/Playerbot](https://github.com/mod-playerbots/azerothcore-wotlk/tree/Playerbot). To install the module, simply run: +To install both the required branch of AzerothCore and the `mod-playerbots` module from source, run the following: ```bash git clone https://github.com/mod-playerbots/azerothcore-wotlk.git --branch=Playerbot @@ -48,7 +50,7 @@ For more information, refer to the [AzerothCore Installation Guide](https://www. ### Docker Installation -**Docker installation is considered experimental.** To install the module on a Docker installation, run: +Docker installations are considered experimental (unofficial with limited support), and previous Docker experience is recommended. To install the `mod-playerbots` on Docker, first clone the required branch of AzerothCore and this module: ```bash git clone https://github.com/mod-playerbots/azerothcore-wotlk.git --branch=Playerbot @@ -85,24 +87,19 @@ Use `docker compose up -d --build` to build and run the server. For more informa ## Documentation -The [Playerbots Wiki](https://github.com/mod-playerbots/mod-playerbots/wiki) contains an extensive overview of addons, commands, raids with programmed bot strategies, and recommended performance configurations. Please note that documentation may be incomplete or out-of-date in some sections. Contributions are welcome. +The [Playerbots Wiki](https://github.com/mod-playerbots/mod-playerbots/wiki) contains an extensive overview of AddOns, commands, raids with programmed bot strategies, and recommended performance configurations. Please note that documentation may be incomplete or out-of-date in some sections, and contributions are welcome. -## Frequently Asked Questions +Bots are controlled via chat commands. For larger bot groups, this can be cumbersome. Because of this, community members have developed client AddOns to allow controlling bots through the in-game UI. We recommend you check out their projects listed in the [AddOns and Submodules](https://github.com/mod-playerbots/mod-playerbots/wiki/Playerbot-Addons-and-Sub%E2%80%90Modules) page. -- **Why aren't my bots casting spells?** Please make sure that the necessary English DBC file (enUS) is present. -- **What platforms are supported?** We support Ubuntu, Windows, and macOS. Other Linux distros may work, but will not receive support. -- **Why isn't my source compiling?** Please ensure that you are compiling with the required [custom branch of AzerothCore](https://github.com/mod-playerbots/azerothcore-wotlk/tree/Playerbot). Additionally, please [check the build status of our CI](https://github.com/mod-playerbots/mod-playerbots/actions). If the latest build is failing, rever to the last successful commit until we address the issue. +## Contributing -## Code standards -- https://www.azerothcore.org/wiki/cpp-code-standards +This project is still under development. We encourage anyone to make contributions, anything from pull requests to reporting issues. If you encounter any errors or experience crashes, we encourage you [report them as GitHub issues](https://github.com/mod-playerbots/mod-playerbots/issues/new?template=bug_report.md). Your valuable feedback will help us improve this project collaboratively. -## Addons +If you make coding contributions, `mod-playerbots` complies with the [C++ Code Standards](https://www.azerothcore.org/wiki/cpp-code-standards) established by AzerothCore. Each Pull Request must include all test scenarios the author performed, along with their results, to demonstrate that the changes were properly verified. -Typically, bots are controlled via chat commands. For larger bot groups, this can be unwieldy. As an alternative, community members have developed client Add-Ons to allow controlling bots through the in-game UI. We recommend you check out their projects: +We recommend joining the [Discord server](https://discord.gg/NQm5QShwf9) to make your contributions to the project easier, as a lot of active support is carried out through this server. -- [Multibot](https://github.com/Macx-Lio/MultiBot) (by Macx-Lio), which includes English, Chinese, French, German, Korean, Russian, and Spanish support [note: active development is temporarily continuing on a fork in Macx-Lio's absence (https://github.com/Wishmaster117/MultiBot)] -- [Unbot Addon (zh)](https://github.com/liyunfan1223/unbot-addon) (Chinese version by Liyunfan) [note: no longer under active development] -- [Unbot Addon (en)](https://github.com/noisiver/unbot-addon/tree/english) (English version translated by @Revision) [note: no longer under active development] +Please click on the "⭐" button to stay up to date and help us gain more visibility on GitHub! ## Acknowledgements From 610a0323799df7bbae4153033f16290fc64f33d3 Mon Sep 17 00:00:00 2001 From: bashermens <31279994+hermensbas@users.noreply.github.com> Date: Sun, 16 Nov 2025 22:39:46 +0100 Subject: [PATCH 28/47] Bots fly/follow (movePoint core refactor), water walking fixes (#1825) Closer the original solution, i dont wanna drift to much away without really good reason. At this point i still see NPC and bots moving through the levels or even falling out the levels here and there. I verified whether the MovePoint signature changes and related params itself in playerbots has anything todo with it, even when params are hardcoded the behavior remains. It could be deeper problem, but for now it seems core problem. Therefore i dont wanna change to much until the dust has settled a bit in core itself. I havent implemented moveTakeOff or moveLand which are basically responsible for the transitions phases between ground and air visa versa. I have version where i use takeOff for the animation, which means moving vertical. For now i am gonna leave for what it is. PS: also includes additional movement fix for AreaTriggerAction which we missed first time after the core update on movements. @Wishmaster117 Been testing and trying a lot in the background on find solutions and causes. The general solutions remains removed some tweaks, altered code here and there. With the general idea to keep closer to the original code for now at least. Testing: - Class abilities: Slow fall (priest), Flight Form (druid) : Green - BG: Green - Swimming and walking shallow waters: Green - Takeoff and land when following master: Green - Boat and zeppelins: Green - Flymount and ground walking: Green - Water Walking (shaman), Path of Frost (DK): Green - Water Walking (shaman), Path of Frost (DK) transisions; flying, swimming, water walking: Green Skipping pets when group water walking, path of frost, once core pathing changes has settled more i will add it. Which is easy but i dont wanna add more edge cases and code branches for now. --- src/strategy/actions/AreaTriggerAction.cpp | 9 +- .../actions/CheckMountStateAction.cpp | 2 +- src/strategy/actions/MovementActions.cpp | 343 +++++++++--------- src/strategy/actions/MovementActions.h | 46 ++- 4 files changed, 215 insertions(+), 185 deletions(-) diff --git a/src/strategy/actions/AreaTriggerAction.cpp b/src/strategy/actions/AreaTriggerAction.cpp index 298bc686a4..f480277825 100644 --- a/src/strategy/actions/AreaTriggerAction.cpp +++ b/src/strategy/actions/AreaTriggerAction.cpp @@ -40,7 +40,14 @@ bool ReachAreaTriggerAction::Execute(Event event) return true; } - bot->GetMotionMaster()->MovePoint(at->map, at->x, at->y, at->z); + bot->GetMotionMaster()->MovePoint( + /*id*/ at->map, + /*coords*/ at->x, at->y, at->z, + /*forcedMovement*/ FORCED_MOVEMENT_NONE, + /*speed*/ 0.0f, // default speed (not handled here) + /*orientation*/ 0.0f, // keep current orientation of bot + /*generatePath*/ true, // true => terrain path (2d mmap); false => straight spline (3d vmap) + /*forceDestination*/ false); float distance = bot->GetDistance(at->x, at->y, at->z); float delay = 1000.0f * distance / bot->GetSpeed(MOVE_RUN) + sPlayerbotAIConfig->reactDelay; diff --git a/src/strategy/actions/CheckMountStateAction.cpp b/src/strategy/actions/CheckMountStateAction.cpp index 0366c56d7d..bf7a3a169a 100644 --- a/src/strategy/actions/CheckMountStateAction.cpp +++ b/src/strategy/actions/CheckMountStateAction.cpp @@ -81,7 +81,7 @@ bool CheckMountStateAction::isUseful() // to mostly be an issue in tunnels of WSG and AV) float posZ = bot->GetPositionZ(); float groundLevel = bot->GetMapWaterOrGroundLevel(bot->GetPositionX(), bot->GetPositionY(), posZ); - if (!bot->IsMounted() && posZ < groundLevel) + if (!bot->IsMounted() && !bot->HasWaterWalkAura() && posZ < groundLevel) return false; // Not useful when bot does not have mount strat and is not currently mounted diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index 04a89fa108..15e098a521 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -10,6 +10,7 @@ #include #include +#include "Corpse.h" #include "Event.h" #include "FleeManager.h" #include "G3D/Vector3.h" @@ -41,7 +42,6 @@ #include "Unit.h" #include "Vehicle.h" #include "WaypointMovementGenerator.h" -#include "Corpse.h" MovementAction::MovementAction(PlayerbotAI* botAI, std::string const name) : Action(botAI, name) { @@ -192,14 +192,15 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, { return false; } + bool generatePath = !bot->IsFlying() && !bot->isSwimming(); - bool disableMoveSplinePath = sPlayerbotAIConfig->disableMoveSplinePath >= 2 || - (sPlayerbotAIConfig->disableMoveSplinePath == 1 && bot->InBattleground()); + bool disableMoveSplinePath = + sPlayerbotAIConfig->disableMoveSplinePath >= 2 || + (sPlayerbotAIConfig->disableMoveSplinePath == 1 && bot->InBattleground()); if (Vehicle* vehicle = bot->GetVehicle()) { VehicleSeatEntry const* seat = vehicle->GetSeatForPassenger(bot); Unit* vehicleBase = vehicle->GetBase(); - // If the mover (vehicle) can fly, we DO NOT want an mmaps path (2D ground) => disable pathfinding generatePath = !vehicleBase || !vehicleBase->CanFly(); if (!vehicleBase || !seat || !seat->CanControl()) // is passenger and cant move anyway return false; @@ -207,22 +208,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, float distance = vehicleBase->GetExactDist(x, y, z); // use vehicle distance, not bot if (distance > 0.01f) { - MotionMaster& mm = *vehicleBase->GetMotionMaster(); // need to move vehicle, not bot - // Disable ground pathing if the bot/master/vehicle are flying - auto isFlying = [](Unit* u){ return u && (u->HasUnitMovementFlag(MOVEMENTFLAG_FLYING) || u->IsInFlight()); }; - bool allowPathVeh = generatePath; - Unit* masterVeh = botAI ? botAI->GetMaster() : nullptr; - if (isFlying(vehicleBase) || isFlying(bot) || isFlying(masterVeh)) - allowPathVeh = false; - mm.Clear(); - if (!backwards) - { - mm.MovePoint(0, x, y, z, FORCED_MOVEMENT_NONE, 0.0f, 0.0f, allowPathVeh); - } - else - { - mm.MovePointBackwards(0, x, y, z, allowPathVeh); - } + DoMovePoint(vehicleBase, x, y, z, generatePath, backwards); float speed = backwards ? vehicleBase->GetSpeed(MOVE_RUN_BACK) : vehicleBase->GetSpeed(MOVE_RUN); float delay = 1000.0f * (distance / speed); if (lessDelay) @@ -248,23 +234,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // bot->CastStop(); // botAI->InterruptSpell(); // } - - MotionMaster& mm = *bot->GetMotionMaster(); - // No ground pathfinding if the bot/master are flying => allow true 3D (Z) movement - auto isFlying = [](Unit* u){ return u && (u->HasUnitMovementFlag(MOVEMENTFLAG_FLYING) || u->IsInFlight()); }; - bool allowPath = generatePath; - Unit* master = botAI ? botAI->GetMaster() : nullptr; - if (isFlying(bot) || isFlying(master)) - allowPath = false; - mm.Clear(); - if (!backwards) - { - mm.MovePoint(0, x, y, z, FORCED_MOVEMENT_NONE, 0.0f, 0.0f, allowPath); - } - else - { - mm.MovePointBackwards(0, x, y, z, allowPath); - } + DoMovePoint(bot, x, y, z, generatePath, backwards); float delay = 1000.0f * MoveDelay(distance, backwards); if (lessDelay) { @@ -282,9 +252,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, Movement::PointsArray path = SearchForBestPath(x, y, z, modifiedZ, sPlayerbotAIConfig->maxMovementSearchTime, normal_only); if (modifiedZ == INVALID_HEIGHT) - { return false; - } float distance = bot->GetExactDist(x, y, modifiedZ); if (distance > 0.01f) { @@ -296,24 +264,8 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // bot->CastStop(); // botAI->InterruptSpell(); // } - - MotionMaster& mm = *bot->GetMotionMaster(); G3D::Vector3 endP = path.back(); - // No ground pathfinding if the bot/master are flying => allow true 3D (Z) movement - auto isFlying = [](Unit* u){ return u && (u->HasUnitMovementFlag(MOVEMENTFLAG_FLYING) || u->IsInFlight()); }; - bool allowPath = generatePath; - Unit* master = botAI ? botAI->GetMaster() : nullptr; - if (isFlying(bot) || isFlying(master)) - allowPath = false; - mm.Clear(); - if (!backwards) - { - mm.MovePoint(0, x, y, z, FORCED_MOVEMENT_NONE, 0.0f, 0.0f, allowPath); - } - else - { - mm.MovePointBackwards(0, x, y, z, allowPath); - } + DoMovePoint(bot, x, y, z, generatePath, backwards); float delay = 1000.0f * MoveDelay(distance, backwards); if (lessDelay) { @@ -581,9 +533,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // bool goTaxi = bot->ActivateTaxiPathTo({ tEntry->from, tEntry->to }, unit, 1); // if (botAI->HasCheat(BotCheatMask::gold)) - // { // bot->SetMoney(botMoney); - // } // LOG_DEBUG("playerbots", "goTaxi"); // return goTaxi; // } @@ -874,7 +824,7 @@ bool MovementAction::ReachCombatTo(Unit* target, float distance) float deltaAngle = Position::NormalizeOrientation(targetOrientation - target->GetAngle(bot)); if (deltaAngle > M_PI) - deltaAngle -= 2.0f * M_PI; // -PI..PI + deltaAngle -= 2.0f * M_PI; // -PI..PI // if target is moving forward and moving far away, predict the position bool behind = fabs(deltaAngle) > M_PI_2; if (target->HasUnitMovementFlag(MOVEMENTFLAG_FORWARD) && behind) @@ -882,8 +832,8 @@ bool MovementAction::ReachCombatTo(Unit* target, float distance) float predictDis = std::min(3.0f, target->GetObjectSize() * 2); tx += cos(target->GetOrientation()) * predictDis; ty += sin(target->GetOrientation()) * predictDis; - if (!target->GetMap()->CheckCollisionAndGetValidCoords(target, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), - tx, ty, tz)) + if (!target->GetMap()->CheckCollisionAndGetValidCoords(target, target->GetPositionX(), target->GetPositionY(), + target->GetPositionZ(), tx, ty, tz)) { tx = target->GetPositionX(); ty = target->GetPositionY(); @@ -1001,9 +951,8 @@ bool MovementAction::IsMovingAllowed() return false; if (bot->isFrozen() || bot->IsPolymorphed() || (bot->isDead() && !bot->HasPlayerFlag(PLAYER_FLAGS_GHOST)) || - bot->IsBeingTeleported() || bot->HasRootAura() || bot->HasSpiritOfRedemptionAura() || - bot->HasConfuseAura() || bot->IsCharmed() || bot->HasStunAura() || - bot->IsInFlight() || bot->HasUnitState(UNIT_STATE_LOST_CONTROL)) + bot->IsBeingTeleported() || bot->HasRootAura() || bot->HasSpiritOfRedemptionAura() || bot->HasConfuseAura() || + bot->IsCharmed() || bot->HasStunAura() || bot->IsInFlight() || bot->HasUnitState(UNIT_STATE_LOST_CONTROL)) return false; if (bot->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_CONTROLLED) != NULL_MOTION_TYPE) @@ -1022,64 +971,59 @@ bool MovementAction::Follow(Unit* target, float distance) { return Follow(target void MovementAction::UpdateMovementState() { - int8 botInLiquidState = bot->GetLiquidData().Status; - - if (botInLiquidState == LIQUID_MAP_IN_WATER || botInLiquidState == LIQUID_MAP_UNDER_WATER) - { - bot->SetSwim(true); + // state flags + const float gLvlZ = bot->GetMapWaterOrGroundLevel(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ()); + const bool onGround = bot->GetPositionZ() < gLvlZ + 1.f; + const bool wantsToFly = bot->HasIncreaseMountedFlightSpeedAura() || bot->HasFlyAura(); + const auto master = botAI ? botAI->GetMaster() : nullptr; // real or not + const bool masterIsFlying = master && master->HasUnitMovementFlag(MOVEMENTFLAG_FLYING); + const bool isFlying = bot->HasUnitMovementFlag(MOVEMENTFLAG_FLYING); + const auto liquidState = bot->GetLiquidData().Status; // default LIQUID_MAP_NO_WATER + const bool isWaterArea = liquidState != LIQUID_MAP_NO_WATER; + const bool isUnderWater = liquidState == LIQUID_MAP_UNDER_WATER; + const bool isInWater = liquidState == LIQUID_MAP_IN_WATER; + const bool isWaterWalking = bot->HasUnitMovementFlag(MOVEMENTFLAG_WATERWALKING); + const bool isSwimming = bot->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING); + const bool wantsToWaterWalk = bot->HasWaterWalkAura(); + const bool wantsToSwim = isInWater || isUnderWater; + + // handle water state + if (isWaterArea) + { + // water walking + if (wantsToWaterWalk && !isWaterWalking && !isUnderWater && !isFlying) + { + bot->RemoveUnitMovementFlag(MOVEMENTFLAG_SWIMMING); + bot->AddUnitMovementFlag(MOVEMENTFLAG_WATERWALKING); + bot->SendMovementFlagUpdate(); + } + // swimming + else if (wantsToSwim && !isSwimming && !wantsToWaterWalk && !isFlying) + { + bot->RemoveUnitMovementFlag(MOVEMENTFLAG_WATERWALKING); + bot->AddUnitMovementFlag(MOVEMENTFLAG_SWIMMING); + bot->SendMovementFlagUpdate(); + } } else { - bot->SetSwim(false); + // reset flags, if not will inherit incorrect walk speed here and there + // when transistions between land and water. + bot->RemoveUnitMovementFlag(MOVEMENTFLAG_SWIMMING); + bot->RemoveUnitMovementFlag(MOVEMENTFLAG_WATERWALKING); + bot->SendMovementFlagUpdate(); } - bool onGround = bot->GetPositionZ() < - bot->GetMapWaterOrGroundLevel(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ()) + 1.0f; - - // Keep bot->SendMovementFlagUpdate() withing the if statements to not intefere with bot behavior on ground/(shallow) waters - - bool hasFlightAura = bot->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) || bot->HasAuraType(SPELL_AURA_FLY); - if (hasFlightAura) + // handle flying state + if (wantsToFly && !isFlying && masterIsFlying) { - bool changed = false; - if (!bot->HasUnitMovementFlag(MOVEMENTFLAG_CAN_FLY)) - { - bot->AddUnitMovementFlag(MOVEMENTFLAG_CAN_FLY); - changed = true; - } - if (!bot->HasUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY)) - { - bot->AddUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY); - changed = true; - } - if (!bot->HasUnitMovementFlag(MOVEMENTFLAG_FLYING)) - { - bot->AddUnitMovementFlag(MOVEMENTFLAG_FLYING); - changed = true; - } - if (changed) - bot->SendMovementFlagUpdate(); + bot->AddUnitMovementFlag(MOVEMENTFLAG_FLYING); + bot->SendMovementFlagUpdate(); } - else if (!hasFlightAura) + else if ((!wantsToFly || onGround) && isFlying) { - bool changed = false; - if (bot->HasUnitMovementFlag(MOVEMENTFLAG_FLYING)) - { - bot->RemoveUnitMovementFlag(MOVEMENTFLAG_FLYING); - changed = true; - } - if (bot->HasUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY)) - { - bot->RemoveUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY); - changed = true; - } - if (bot->HasUnitMovementFlag(MOVEMENTFLAG_CAN_FLY)) - { - bot->RemoveUnitMovementFlag(MOVEMENTFLAG_CAN_FLY); - changed = true; - } - if (changed) - bot->SendMovementFlagUpdate(); + bot->RemoveUnitMovementFlag(MOVEMENTFLAG_FLYING); + bot->SendMovementFlagUpdate(); } // See if the bot is currently slowed, rooted, or otherwise unable to move @@ -1180,6 +1124,13 @@ bool MovementAction::Follow(Unit* target, float distance, float angle) if (!target) return false; + if (!bot->InBattleground() && sServerFacade->IsDistanceLessOrEqualThan(sServerFacade->GetDistance2d(bot, target), + sPlayerbotAIConfig->followDistance)) + { + // botAI->TellError("No need to follow"); + return false; + } + /* if (!bot->InBattleground() && sServerFacade->IsDistanceLessOrEqualThan(sServerFacade->GetDistance2d(bot, target->GetPositionX(), @@ -1297,17 +1248,21 @@ bool MovementAction::Follow(Unit* target, float distance, float angle) return MoveTo(target, sPlayerbotAIConfig->followDistance); } + if (sServerFacade->IsDistanceLessOrEqualThan(sServerFacade->GetDistance2d(bot, target), + sPlayerbotAIConfig->followDistance)) + { + // botAI->TellError("No need to follow"); + return false; + } + if (target->IsFriendlyTo(bot) && bot->IsMounted() && AI_VALUE(GuidVector, "all targets").empty()) distance += angle; - // Do not cancel follow if the 2D distance is short but the Z still differs (e.g., master above). - float dz1 = fabs(bot->GetPositionZ() - target->GetPositionZ()); - if (!bot->InBattleground() - && sServerFacade->IsDistanceLessOrEqualThan(sServerFacade->GetDistance2d(bot, target), sPlayerbotAIConfig->followDistance) - && dz1 < sPlayerbotAIConfig->contactDistance) + if (!bot->InBattleground() && sServerFacade->IsDistanceLessOrEqualThan(sServerFacade->GetDistance2d(bot, target), + sPlayerbotAIConfig->followDistance)) { // botAI->TellError("No need to follow"); - return false; // truly in range (2D and Z) => no need to move + return false; } bot->HandleEmoteCommand(0); @@ -1388,7 +1343,7 @@ float MovementAction::MoveDelay(float distance, bool backwards) } else { - speed = backwards ? bot->GetSpeed(MOVE_RUN_BACK) :bot->GetSpeed(MOVE_RUN); + speed = backwards ? bot->GetSpeed(MOVE_RUN_BACK) : bot->GetSpeed(MOVE_RUN); } float delay = distance / speed; return delay; @@ -1418,8 +1373,7 @@ void MovementAction::SetNextMovementDelay(float delayMillis) { AI_VALUE(LastMovement&, "last movement") .Set(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), bot->GetOrientation(), - delayMillis, - MovementPriority::MOVEMENT_FORCED); + delayMillis, MovementPriority::MOVEMENT_FORCED); } bool MovementAction::Flee(Unit* target) @@ -1633,7 +1587,8 @@ bool MovementAction::MoveAway(Unit* target, float distance, bool backwards) dz = bot->GetPositionZ(); exact = false; } - if (MoveTo(target->GetMapId(), dx, dy, dz, false, false, true, exact, MovementPriority::MOVEMENT_COMBAT, false, backwards)) + if (MoveTo(target->GetMapId(), dx, dy, dz, false, false, true, exact, MovementPriority::MOVEMENT_COMBAT, false, + backwards)) { return true; } @@ -1655,7 +1610,8 @@ bool MovementAction::MoveAway(Unit* target, float distance, bool backwards) dz = bot->GetPositionZ(); exact = false; } - if (MoveTo(target->GetMapId(), dx, dy, dz, false, false, true, exact, MovementPriority::MOVEMENT_COMBAT, false, backwards)) + if (MoveTo(target->GetMapId(), dx, dy, dz, false, false, true, exact, MovementPriority::MOVEMENT_COMBAT, false, + backwards)) { return true; } @@ -1704,7 +1660,7 @@ bool MovementAction::Move(float angle, float distance) float x = bot->GetPositionX() + cos(angle) * distance; float y = bot->GetPositionY() + sin(angle) * distance; - //TODO do we need GetMapWaterOrGroundLevel() if we're using CheckCollisionAndGetValidCoords() ? + // TODO do we need GetMapWaterOrGroundLevel() if we're using CheckCollisionAndGetValidCoords() ? float z = bot->GetMapWaterOrGroundLevel(x, y, bot->GetPositionZ()); if (z == -100000.0f || z == -200000.0f) z = bot->GetPositionZ(); @@ -1758,9 +1714,7 @@ bool MovementAction::MoveInside(uint32 mapId, float x, float y, float z, float d // min_length = gen.getPathLength(); // current_z = modified_z; // if (abs(current_z - z) < 0.5f) -// { // return current_z; -// } // } // } // for (delta = range / 2 + step; delta <= range; delta += 2) { @@ -1857,6 +1811,59 @@ const Movement::PointsArray MovementAction::SearchForBestPath(float x, float y, return result; } +void MovementAction::DoMovePoint(Unit* unit, float x, float y, float z, bool generatePath, bool backwards) +{ + if (!unit) + return; + + MotionMaster* mm = unit->GetMotionMaster(); + if (!mm) + return; + + // enable flying + if (unit->HasUnitMovementFlag(MOVEMENTFLAG_FLYING)) + { + unit->AddUnitMovementFlag(MOVEMENTFLAG_CAN_FLY); + unit->AddUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY); + } + else + { + unit->RemoveUnitMovementFlag(MOVEMENTFLAG_CAN_FLY); + unit->RemoveUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY); + } + + // enable water walking + if (unit->HasUnitMovementFlag(MOVEMENTFLAG_WATERWALKING)) + { + float gLvlZ = unit->GetMapWaterOrGroundLevel(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ()); + unit->UpdatePosition(unit->GetPositionX(), unit->GetPositionY(), gLvlZ, false); + // z = gLvlZ; do not overwrite Z axex, otherwise you wont be able to steer the bots into swimming when water + // walking. + } + + mm->Clear(); + if (backwards) + { + mm->MovePointBackwards( + /*id*/ 0, + /*coords*/ x, y, z, + /*generatePath*/ generatePath, + /*forceDestination*/ false); + return; + } + else + { + mm->MovePoint( + /*id*/ 0, + /*coords*/ x, y, z, + /*forcedMovement*/ FORCED_MOVEMENT_NONE, + /*speed*/ 0.f, + /*orientation*/ 0.f, + /*generatePath*/ generatePath, // true => terrain path (2d mmap); false => straight spline (3d vmap) + /*forceDestination*/ false); + } +} + bool FleeAction::Execute(Event event) { return MoveAway(AI_VALUE(Unit*, "current target"), sPlayerbotAIConfig->fleeDistance, true); @@ -1937,7 +1944,8 @@ bool AvoidAoeAction::AvoidAuraWithDynamicObj() { return false; } - if (sPlayerbotAIConfig->aoeAvoidSpellWhitelist.find(spellInfo->Id) != sPlayerbotAIConfig->aoeAvoidSpellWhitelist.end()) + if (sPlayerbotAIConfig->aoeAvoidSpellWhitelist.find(spellInfo->Id) != + sPlayerbotAIConfig->aoeAvoidSpellWhitelist.end()) return false; DynamicObject* dynOwner = aura->GetDynobjOwner(); @@ -2002,7 +2010,8 @@ bool AvoidAoeAction::AvoidGameObjectWithDamage() continue; } - if (sPlayerbotAIConfig->aoeAvoidSpellWhitelist.find(spellId) != sPlayerbotAIConfig->aoeAvoidSpellWhitelist.end()) + if (sPlayerbotAIConfig->aoeAvoidSpellWhitelist.find(spellId) != + sPlayerbotAIConfig->aoeAvoidSpellWhitelist.end()) continue; const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId); @@ -2028,7 +2037,8 @@ bool AvoidAoeAction::AvoidGameObjectWithDamage() lastTellTimer = time(NULL); lastMoveTimer = getMSTime(); std::ostringstream out; - out << "I'm avoiding " << name.str() << " (" << spellInfo->Id << ")" << " Radius " << radius << " - [Trap]"; + out << "I'm avoiding " << name.str() << " (" << spellInfo->Id << ")" << " Radius " << radius + << " - [Trap]"; bot->Say(out.str(), LANG_UNIVERSAL); } return true; @@ -2071,7 +2081,8 @@ bool AvoidAoeAction::AvoidUnitWithDamageAura() sSpellMgr->GetSpellInfo(spellInfo->Effects[aurEff->GetEffIndex()].TriggerSpell); if (!triggerSpellInfo) continue; - if (sPlayerbotAIConfig->aoeAvoidSpellWhitelist.find(triggerSpellInfo->Id) != sPlayerbotAIConfig->aoeAvoidSpellWhitelist.end()) + if (sPlayerbotAIConfig->aoeAvoidSpellWhitelist.find(triggerSpellInfo->Id) != + sPlayerbotAIConfig->aoeAvoidSpellWhitelist.end()) return false; for (int j = 0; j < MAX_SPELL_EFFECTS; j++) { @@ -2093,7 +2104,8 @@ bool AvoidAoeAction::AvoidUnitWithDamageAura() lastTellTimer = time(NULL); lastMoveTimer = getMSTime(); std::ostringstream out; - out << "I'm avoiding " << name.str() << " (" << triggerSpellInfo->Id << ")" << " Radius " << radius << " - [Unit Trigger]"; + out << "I'm avoiding " << name.str() << " (" << triggerSpellInfo->Id << ")" + << " Radius " << radius << " - [Unit Trigger]"; bot->Say(out.str(), LANG_UNIVERSAL); } } @@ -2112,7 +2124,8 @@ Position MovementAction::BestPositionForMeleeToFlee(Position pos, float radius) if (currentTarget) { // Normally, move to left or right is the best position - bool isTanking = (!currentTarget->isFrozen() && !currentTarget->HasRootAura()) && (currentTarget->GetVictim() == bot); + bool isTanking = (!currentTarget->isFrozen() + && !currentTarget->HasRootAura()) && (currentTarget->GetVictim() == bot); float angle = bot->GetAngle(currentTarget); float angleLeft = angle + (float)M_PI / 2; float angleRight = angle - (float)M_PI / 2; @@ -2327,8 +2340,7 @@ bool CombatFormationMoveAction::isUseful() bool CombatFormationMoveAction::Execute(Event event) { float dis = AI_VALUE(float, "disperse distance"); - if (dis <= 0.0f || - (!bot->IsInCombat() && botAI->HasStrategy("stay", BotState::BOT_STATE_NON_COMBAT)) || + if (dis <= 0.0f || (!bot->IsInCombat() && botAI->HasStrategy("stay", BotState::BOT_STATE_NON_COMBAT)) || (bot->IsInCombat() && botAI->HasStrategy("stay", BotState::BOT_STATE_COMBAT))) return false; Player* playerToLeave = NearestGroupMember(dis); @@ -2478,7 +2490,7 @@ bool TankFaceAction::Execute(Event event) float deltaAngle = Position::NormalizeOrientation(averageAngle - target->GetAngle(bot)); if (deltaAngle > M_PI) - deltaAngle -= 2.0f * M_PI; // -PI..PI + deltaAngle -= 2.0f * M_PI; // -PI..PI float tolerable = M_PI_2; @@ -2489,12 +2501,13 @@ bool TankFaceAction::Execute(Event event) float goodAngle2 = Position::NormalizeOrientation(averageAngle - M_PI * 3 / 5); // if dist < bot->GetMeleeRange(target) / 2, target will move backward - float dist = std::max(bot->GetExactDist(target), bot->GetMeleeRange(target) / 2) - bot->GetCombatReach() - target->GetCombatReach(); + float dist = std::max(bot->GetExactDist(target), bot->GetMeleeRange(target) / 2) - bot->GetCombatReach() - + target->GetCombatReach(); std::vector availablePos; float x, y, z; target->GetNearPoint(bot, x, y, z, 0.0f, dist, goodAngle1); - if (bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), - x, y, z)) + if (bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(), + bot->GetPositionZ(), x, y, z)) { /// @todo: movement control now is a mess, prepare to rewrite std::list& infoList = AI_VALUE(std::list&, "recently flee info"); @@ -2506,8 +2519,8 @@ bool TankFaceAction::Execute(Event event) } } target->GetNearPoint(bot, x, y, z, 0.0f, dist, goodAngle2); - if (bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), - x, y, z)) + if (bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(), + bot->GetPositionZ(), x, y, z)) { std::list& infoList = AI_VALUE(std::list&, "recently flee info"); Position pos(x, y, z); @@ -2520,13 +2533,15 @@ bool TankFaceAction::Execute(Event event) if (availablePos.empty()) return false; Position nearest = GetNearestPosition(availablePos); - return MoveTo(bot->GetMapId(), nearest.GetPositionX(), nearest.GetPositionY(), nearest.GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_COMBAT); + return MoveTo(bot->GetMapId(), nearest.GetPositionX(), nearest.GetPositionY(), nearest.GetPositionZ(), false, false, + false, true, MovementPriority::MOVEMENT_COMBAT); } bool RearFlankAction::isUseful() { Unit* target = AI_VALUE(Unit*, "current target"); - if (!target) { return false; } + if (!target) + return false; // Need to double the front angle check to account for mirrored angle. bool inFront = target->HasInArc(2.f * minAngle, bot); @@ -2540,7 +2555,8 @@ bool RearFlankAction::isUseful() bool RearFlankAction::Execute(Event event) { Unit* target = AI_VALUE(Unit*, "current target"); - if (!target) { return false; } + if (!target) + return false; float angle = frand(minAngle, maxAngle); float baseDistance = bot->GetMeleeRange(target) * 0.5f; @@ -2559,8 +2575,8 @@ bool RearFlankAction::Execute(Event event) destination = &rightFlank; } - return MoveTo(bot->GetMapId(), destination->GetPositionX(), destination->GetPositionY(), destination->GetPositionZ(), - false, false, false, true, MovementPriority::MOVEMENT_COMBAT); + return MoveTo(bot->GetMapId(), destination->GetPositionX(), destination->GetPositionY(), + destination->GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_COMBAT); } bool DisperseSetAction::Execute(Event event) @@ -2688,9 +2704,8 @@ bool SetFacingTargetAction::isUseful() { return !AI_VALUE2(bool, "facing", "curr bool SetFacingTargetAction::isPossible() { if (bot->isFrozen() || bot->IsPolymorphed() || (bot->isDead() && !bot->HasPlayerFlag(PLAYER_FLAGS_GHOST)) || - bot->IsBeingTeleported() || bot->HasConfuseAura() || bot->IsCharmed() || - bot->HasStunAura() || bot->IsInFlight() || - bot->HasUnitState(UNIT_STATE_LOST_CONTROL)) + bot->IsBeingTeleported() || bot->HasConfuseAura() || bot->IsCharmed() || bot->HasStunAura() || + bot->IsInFlight() || bot->HasUnitState(UNIT_STATE_LOST_CONTROL)) return false; return true; @@ -2710,7 +2725,7 @@ bool SetBehindTargetAction::Execute(Event event) float deltaAngle = Position::NormalizeOrientation(target->GetOrientation() - target->GetAngle(bot)); if (deltaAngle > M_PI) - deltaAngle -= 2.0f * M_PI; // -PI..PI + deltaAngle -= 2.0f * M_PI; // -PI..PI float tolerable = M_PI_2; @@ -2720,12 +2735,13 @@ bool SetBehindTargetAction::Execute(Event event) float goodAngle1 = Position::NormalizeOrientation(target->GetOrientation() + M_PI * 3 / 5); float goodAngle2 = Position::NormalizeOrientation(target->GetOrientation() - M_PI * 3 / 5); - float dist = std::max(bot->GetExactDist(target), bot->GetMeleeRange(target) / 2) - bot->GetCombatReach() - target->GetCombatReach(); + float dist = std::max(bot->GetExactDist(target), bot->GetMeleeRange(target) / 2) - bot->GetCombatReach() - + target->GetCombatReach(); std::vector availablePos; float x, y, z; target->GetNearPoint(bot, x, y, z, 0.0f, dist, goodAngle1); - if (bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), - x, y, z)) + if (bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(), + bot->GetPositionZ(), x, y, z)) { /// @todo: movement control now is a mess, prepare to rewrite std::list& infoList = AI_VALUE(std::list&, "recently flee info"); @@ -2737,8 +2753,8 @@ bool SetBehindTargetAction::Execute(Event event) } } target->GetNearPoint(bot, x, y, z, 0.0f, dist, goodAngle2); - if (bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), - x, y, z)) + if (bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(), + bot->GetPositionZ(), x, y, z)) { std::list& infoList = AI_VALUE(std::list&, "recently flee info"); Position pos(x, y, z); @@ -2751,7 +2767,8 @@ bool SetBehindTargetAction::Execute(Event event) if (availablePos.empty()) return false; Position nearest = GetNearestPosition(availablePos); - return MoveTo(bot->GetMapId(), nearest.GetPositionX(), nearest.GetPositionY(), nearest.GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_COMBAT); + return MoveTo(bot->GetMapId(), nearest.GetPositionX(), nearest.GetPositionY(), nearest.GetPositionZ(), false, false, + false, true, MovementPriority::MOVEMENT_COMBAT); } bool MoveOutOfCollisionAction::Execute(Event event) @@ -2822,7 +2839,7 @@ bool MoveFromGroupAction::Execute(Event event) { float distance = atoi(event.getParam().c_str()); if (!distance) - distance = 20.0f; // flee distance from config is too small for this + distance = 20.0f; // flee distance from config is too small for this return MoveFromGroup(distance); } @@ -2905,10 +2922,7 @@ bool MoveAwayFromCreatureAction::Execute(Event event) return false; } -bool MoveAwayFromCreatureAction::isPossible() -{ - return bot->CanFreeMove(); -} +bool MoveAwayFromCreatureAction::isPossible() { return bot->CanFreeMove(); } bool MoveAwayFromPlayerWithDebuffAction::Execute(Event event) { @@ -2995,7 +3009,4 @@ bool MoveAwayFromPlayerWithDebuffAction::Execute(Event event) return false; } -bool MoveAwayFromPlayerWithDebuffAction::isPossible() -{ - return bot->CanFreeMove(); -} +bool MoveAwayFromPlayerWithDebuffAction::isPossible() { return bot->CanFreeMove(); } diff --git a/src/strategy/actions/MovementActions.h b/src/strategy/actions/MovementActions.h index f50e4b9570..d12bcd555c 100644 --- a/src/strategy/actions/MovementActions.h +++ b/src/strategy/actions/MovementActions.h @@ -29,12 +29,17 @@ class MovementAction : public Action protected: bool JumpTo(uint32 mapId, float x, float y, float z, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL); - bool MoveNear(uint32 mapId, float x, float y, float z, float distance = sPlayerbotAIConfig->contactDistance, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL); + bool MoveNear(uint32 mapId, float x, float y, float z, float distance = sPlayerbotAIConfig->contactDistance, + MovementPriority priority = MovementPriority::MOVEMENT_NORMAL); bool MoveToLOS(WorldObject* target, bool ranged = false); bool MoveTo(uint32 mapId, float x, float y, float z, bool idle = false, bool react = false, - bool normal_only = false, bool exact_waypoint = false, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL, bool lessDelay = false, bool backwards = false); - bool MoveTo(WorldObject* target, float distance = 0.0f, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL); - bool MoveNear(WorldObject* target, float distance = sPlayerbotAIConfig->contactDistance, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL); + bool normal_only = false, bool exact_waypoint = false, + MovementPriority priority = MovementPriority::MOVEMENT_NORMAL, bool lessDelay = false, + bool backwards = false); + bool MoveTo(WorldObject* target, float distance = 0.0f, + MovementPriority priority = MovementPriority::MOVEMENT_NORMAL); + bool MoveNear(WorldObject* target, float distance = sPlayerbotAIConfig->contactDistance, + MovementPriority priority = MovementPriority::MOVEMENT_NORMAL); float GetFollowAngle(); bool Follow(Unit* target, float distance = sPlayerbotAIConfig->followDistance); bool Follow(Unit* target, float distance, float angle); @@ -51,10 +56,11 @@ class MovementAction : public Action bool Flee(Unit* target); void ClearIdleState(); void UpdateMovementState(); - bool MoveAway(Unit* target, float distance = sPlayerbotAIConfig -> fleeDistance, bool backwards = false); + bool MoveAway(Unit* target, float distance = sPlayerbotAIConfig->fleeDistance, bool backwards = false); bool MoveFromGroup(float distance); bool Move(float angle, float distance); - bool MoveInside(uint32 mapId, float x, float y, float z, float distance = sPlayerbotAIConfig->followDistance, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL); + bool MoveInside(uint32 mapId, float x, float y, float z, float distance = sPlayerbotAIConfig->followDistance, + MovementPriority priority = MovementPriority::MOVEMENT_NORMAL); void CreateWp(Player* wpOwner, float x, float y, float z, float o, uint32 entry, bool important = false); Position BestPositionForMeleeToFlee(Position pos, float radius); Position BestPositionForRangedToFlee(Position pos, float radius); @@ -74,6 +80,7 @@ class MovementAction : public Action const Movement::PointsArray SearchForBestPath(float x, float y, float z, float& modified_z, int maxSearchCount = 5, bool normal_only = false, float step = 8.0f); bool wasMovementRestricted = false; + void DoMovePoint(Unit* unit, float x, float y, float z, bool generatePath, bool backwards); }; class FleeAction : public MovementAction @@ -149,17 +156,18 @@ class TankFaceAction : public CombatFormationMoveAction class RearFlankAction : public MovementAction { -// 90 degree minimum angle prevents any frontal cleaves/breaths and avoids parry-hasting the boss. -// 120 degree maximum angle leaves a 120 degree symmetrical cone at the tail end which is usually enough to avoid tail swipes. -// Some dragons or mobs may have different danger zone angles, override if needed. + // 90 degree minimum angle prevents any frontal cleaves/breaths and avoids parry-hasting the boss. + // 120 degree maximum angle leaves a 120 degree symmetrical cone at the tail end which is usually enough to avoid + // tail swipes. Some dragons or mobs may have different danger zone angles, override if needed. public: - RearFlankAction(PlayerbotAI* botAI, float distance = 0.0f, float minAngle = ANGLE_90_DEG, float maxAngle = ANGLE_120_DEG) + RearFlankAction(PlayerbotAI* botAI, float distance = 0.0f, float minAngle = ANGLE_90_DEG, + float maxAngle = ANGLE_120_DEG) : MovementAction(botAI, "rear flank") - { - this->distance = distance; - this->minAngle = minAngle; - this->maxAngle = maxAngle; - } + { + this->distance = distance; + this->minAngle = minAngle; + this->maxAngle = maxAngle; + } bool Execute(Event event) override; bool isUseful() override; @@ -297,7 +305,9 @@ class MoveAwayFromCreatureAction : public MovementAction { public: MoveAwayFromCreatureAction(PlayerbotAI* botAI, std::string name, uint32 creatureId, float range, bool alive = true) - : MovementAction(botAI, name), creatureId(creatureId), range(range), alive(alive) {} + : MovementAction(botAI, name), creatureId(creatureId), range(range), alive(alive) + { + } bool Execute(Event event) override; bool isPossible() override; @@ -312,7 +322,9 @@ class MoveAwayFromPlayerWithDebuffAction : public MovementAction { public: MoveAwayFromPlayerWithDebuffAction(PlayerbotAI* botAI, std::string name, uint32 spellId, float range) - : MovementAction(botAI, name), spellId(spellId), range(range) {} + : MovementAction(botAI, name), spellId(spellId), range(range) + { + } bool Execute(Event event) override; bool isPossible() override; From 05057ae9b5019f3582d09c952a1a75513624c373 Mon Sep 17 00:00:00 2001 From: Tecc Date: Sun, 16 Nov 2025 22:49:12 +0100 Subject: [PATCH 29/47] feat: Improve bot mount behavior to faster close distance between bot and master (#1760) # Fix bot mount behavior when master dismounts ## Summary Improves bot mount/dismount logic to ensure better coordination with the master player. The bot now remains mounted when closing distance to a recently dismounted master and mounts up to assist the master in combat. The changes have been tested using the testcases described in the second half of the PR description, which provide some directions on how this PR can be tested and verified. Closes: #1660 ## Changes - Add masterInCombat variable to track master combat state - Modify target-based logic to consider master combat state - Change mount priority when bot is not in combat to favor master-following - Remove combatReach from distance calculations (duplicate) ## Implementation Added two methods: - `StayMountedToCloseDistance()` - prevents premature dismounting when the master dismounts - `ShouldMountToCloseDistance()` - determines when to mount for master assistance, even if master is not mounted at this time Modified Execute() method: - Target-based shouldMount/shouldDismount considers the master's combat state - Combat assistance logic separated from general following - Mount when master in combat, but the bot is not Distance logic: - Combat: dismount at CalculateDismountDistance() (18-25 yards) - Non-combat: dismount at 10 yards - Mount threshold: 21+ yards ## Result - Bots mount to assist masters in combat - Bots stay mounted longer during travel - Different dismount distances based on context - Existing mount selection logic unchanged # Mount Behavior Testing ## Prerequisites 1. Add a test bot: `.playerbots bot add ` or `.playerbots bot addclass ` 2. Test in a level-appropriate outdoor area (mobs should not die with one hit, as this makes testing combat assistance impossible) 3. Both master and bot must have mounts available ## Test 1: Combat Assistance Mounting **Objective**: Verify bot mounts to assist the master in combat **Detailed Steps**: 1. Position both master and bot dismounted 2. Command bot to stay: `/w stay` 3. Move master 35+ yards away from bot 4. Target a nearby mob and attack the mob 5. Command bot to assist: `/w follow` **Expected Results**: - Bot immediately mounts up (within 1-2 seconds cast time) - Bot rides toward master while mounted - Bot dismounts at ~18-25 yards from master/mob - Bot engages in combat to assist **Failure Indicators**: - Bot runs dismounted (old behavior) - Bot never mounts despite distance - Bot mounts but doesn't dismount at proper assist range ## Test 2: Non-Combat Dismount Distance **Objective**: Verify bot stays mounted longer during peaceful travel **Detailed Steps**: 1. Both master and bot start dismounted 2. Mount up: use any mount 3. Verify bot also mounts: `/w follow` 4. Travel together for 10+ seconds to establish following 5. While moving, dismount master but continue running 6. Observe bot behavior as you move away **Expected Results**: - Bot stays mounted while master runs dismounted - Bot only dismounts when within ~10 yards of master ## Test 3: Target Interference Prevention **Objective**: Verify target selection doesn't prevent mounting when master needs help **Detailed Steps**: 1. Position bot 35+ yards from master: `/w stay` then move away 2. Find a mob visible to both master and bot 3. Target the mob (do not attack): click on mob to select it 4. Verify bot can see the target: `/w attack` (bot should respond about target) 5. Cancel bot attack: `/w follow` 6. Now attack the mob yourself (master enters combat) 7. Observe bot behavior immediately after master engages **Expected Results**: - Bot mounts up despite having the same target selected - Bot rides toward combat area while mounted - Bot dismounts at assist range (~18-25 yards) - Target selection does not prevent proper mount behavior **Failure Indicators**: - Bot runs dismounted toward target (target interference) - Bot doesn't mount because it's "focused on target" ## Test 4: Basic Mount Following **Objective**: Verify fundamental mount matching still works **Detailed Steps**: 1. Start both master and bot dismounted 2. Ensure bot is following: `/w follow` 3. Mount up on any available mount 4. Wait 2-3 seconds and observe bot 5. Test different mount speeds if available (60%, 100%, 280% speed) 6. Test dismounting and remounting **Expected Results**: - Bot mounts within 2-3 seconds of master mounting - Bot uses appropriate mount speed to match master - Bot dismounts when master dismounts (basic following) - No regression in basic mount following behavior ## Test 5: Distance-Based Mounting Threshold **Objective**: Verify bot mounts at efficient distance threshold (21+ yards) **Detailed Steps**: 1. Both master and bot start dismounted in safe area (no mobs) 2. Command bot to stay: `/w stay` 3. Record starting position: `.gps` 4. Walk exactly 15 yards away (short distance) 5. Command bot to follow: `/w follow` 6. Observe: bot should run dismounted (distance too short) 7. Command bot to stay again: `/w stay` 8. Walk to 25+ yards away from bot 9. Record new position: `.gps` 10. Command bot to follow: `/w follow` **Expected Results**: - At 15 yards: Bot runs dismounted (inefficient to mount) - At 25+ yards: Bot mounts up immediately (efficient distance) - Distance threshold should be ~21 yards based on mount cast time efficiency **Distance Calculation**: Use coordinates from `.gps` to verify exact distances ## Test 6: Druid Form Integration (Druid Master Required) **Objective**: Verify druid form compatibility with mount system **Detailed Steps**: 1. Master must be druid with travel form available 2. Start both dismounted: `/w follow` 3. Shift master to travel form 4. Observe bot response (should mount or shift if druid) 5. Travel together for 10+ seconds 6. Shift master to normal form while moving 7. Observe bot dismount behavior 8. If available, test flight form in appropriate zones **Expected Results**: - Druid bot: matches master's form (travel form) - Non-druid bot: uses equivalent mount speed - Form changes trigger appropriate bot responses - Speed matching works between forms and mounts ## Test 7: Battleground Mount Behavior **Objective**: Verify mount behavior works in PvP environments **Detailed Steps**: 1. Queue for battleground with bot in party 2. Enter battleground together 3. Test basic mount following in BG 4. Test flag-carrying restrictions (WSG/EotS) 5. Test mounting during BG combat scenarios **Expected Results**: - Bot mounts appropriately in battlegrounds - Flag carrying prevents mounting - Combat assistance mounting still works in PvP --- .../actions/CheckMountStateAction.cpp | 64 +++++++++++++++++-- src/strategy/actions/CheckMountStateAction.h | 2 + 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/src/strategy/actions/CheckMountStateAction.cpp b/src/strategy/actions/CheckMountStateAction.cpp index bf7a3a169a..fb67057d35 100644 --- a/src/strategy/actions/CheckMountStateAction.cpp +++ b/src/strategy/actions/CheckMountStateAction.cpp @@ -122,18 +122,21 @@ bool CheckMountStateAction::Execute(Event /*event*/) bool shouldMount = false; Unit* currentTarget = AI_VALUE(Unit*, "current target"); - if (currentTarget) + bool masterInCombat = master && master->IsInCombat(); + + if (currentTarget && (bot->IsInCombat() || masterInCombat)) { + // Use target-based logic if bot is in combat OR master is in combat and needs assistance float dismountDistance = CalculateDismountDistance(); float mountDistance = CalculateMountDistance(); - float combatReach = bot->GetCombatReach() + currentTarget->GetCombatReach(); float distanceToTarget = bot->GetExactDist(currentTarget); - shouldDismount = (distanceToTarget <= dismountDistance + combatReach); - shouldMount = (distanceToTarget > mountDistance + combatReach); + shouldDismount = (distanceToTarget <= dismountDistance); + shouldMount = (distanceToTarget > mountDistance); } else { + // If neither bot nor master is in combat, prioritize master-following shouldMount = true; } @@ -160,10 +163,19 @@ bool CheckMountStateAction::Execute(Event /*event*/) else if (ShouldDismountForMaster(master) && bot->IsMounted()) { + // If master dismounted, stay mounted until close enough to assist + if (StayMountedToCloseDistance()) + return false; + Dismount(); return true; } + // Mount up to close distance to master if beneficial - allow mounting even if master is in combat + // as long as the bot itself is not in combat and has no attackers + else if (!bot->IsMounted() && noAttackers && !bot->IsInCombat() && ShouldMountToCloseDistance()) + return Mount(); + return false; } @@ -397,6 +409,50 @@ bool CheckMountStateAction::TryRandomMountFiltered(const std::mapGetDistance2d(bot, master); + + // If master is in combat, dismount at combat assist range to help immediately + if (master->IsInCombat()) + { + float assistRange = CalculateDismountDistance(); + return distToMaster > assistRange; + } + + // If master is not in combat, use smaller proximity range for general following + float masterProximityRange = 10.0f; // Close enough to be near master but not attack range + return distToMaster > masterProximityRange; +} + +bool CheckMountStateAction::ShouldMountToCloseDistance() const +{ + // Mount up to close distance to master if beneficial + // Uses the same logic as CalculateMountDistance() which already considers the 2-second mount cast time + // This handles cases where master is in combat but bot isn't, and bot needs to mount to reach master + + if (!master) + return false; + + // Only mount to close distance when actively following + if (!botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT)) + return false; + + float distToMaster = sServerFacade->GetDistance2d(bot, master); + float mountDistance = CalculateMountDistance(); + + // Mount if distance is greater than the calculated mount distance threshold + return distToMaster > mountDistance; +} + float CheckMountStateAction::CalculateDismountDistance() const { // Warrior bots should dismount far enough to charge (because it's important for generating some initial rage), diff --git a/src/strategy/actions/CheckMountStateAction.h b/src/strategy/actions/CheckMountStateAction.h index 9a21838e17..3278cf930d 100644 --- a/src/strategy/actions/CheckMountStateAction.h +++ b/src/strategy/actions/CheckMountStateAction.h @@ -60,6 +60,8 @@ class CheckMountStateAction : public UseItemAction bool TryPreferredMount(Player* master) const; uint32 GetMountType(Player* master) const; bool TryRandomMountFiltered(const std::map>& spells, int32 masterSpeed) const; + bool StayMountedToCloseDistance() const; + bool ShouldMountToCloseDistance() const; }; #endif From e88c1b779b8a3b392f4be08eee7d4c5646439b57 Mon Sep 17 00:00:00 2001 From: Jay <63983128+Vortikai@users.noreply.github.com> Date: Tue, 18 Nov 2025 19:08:16 +0200 Subject: [PATCH 30/47] Minor README.md corrections (#1849) - Removed double "is" in acknowledgements - Made the acknowledgements section more grammatical by using "based on" instead of "based off" - Corrected misspelling of "implemented" - Standardized instances of "Playerbots" to `mod-playerbots` or properly capitalized `Playerbots` - Minor change of "for the continued contributions" to "for their continued contributions" in the Acknowledgements section --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1008233aa5..3924765ab6 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ We also have a **[Discord server](https://discord.gg/NQm5QShwf9)** where you can Supported platforms are Ubuntu, Windows, and macOS. Other Linux distributions may work, but may not receive support. -**All `mod-playerbots` installations require a custom branch of AzerothCore: [mod-playerbots/azerothcore-wotlk/tree/Playerbot](https://github.com/mod-playerbots/azerothcore-wotlk/tree/Playerbot).** This branch allows the playerbots module to build and function. Updates from the upstream are implemneted regularly to this branch. Instructions for installing this required branch and this module are provided below. +**All `mod-playerbots` installations require a custom branch of AzerothCore: [mod-playerbots/azerothcore-wotlk/tree/Playerbot](https://github.com/mod-playerbots/azerothcore-wotlk/tree/Playerbot).** This branch allows the `mod-playerbots` module to build and function. Updates from the upstream are implemented regularly to this branch. Instructions for installing this required branch and this module are provided below. ### Cloning the Repositories @@ -50,7 +50,7 @@ For more information, refer to the [AzerothCore Installation Guide](https://www. ### Docker Installation -Docker installations are considered experimental (unofficial with limited support), and previous Docker experience is recommended. To install the `mod-playerbots` on Docker, first clone the required branch of AzerothCore and this module: +Docker installations are considered experimental (unofficial with limited support), and previous Docker experience is recommended. To install `mod-playerbots` on Docker, first clone the required branch of AzerothCore and this module: ```bash git clone https://github.com/mod-playerbots/azerothcore-wotlk.git --branch=Playerbot @@ -103,7 +103,7 @@ Please click on the "⭐" button to stay up to date and help us gain more visibi ## Acknowledgements -`mod-playerbots` is is based off [ZhengPeiRu21/mod-playerbots](https://github.com/ZhengPeiRu21/mod-playerbots) and [celguar/mangosbot-bots](https://github.com/celguar/mangosbot-bots). We extend our gratitude to [@ZhengPeiRu21](https://github.com/ZhengPeiRu21) and [@celguar](https://github.com/celguar) for the continued efforts in maintaining the module. +`mod-playerbots` is based on [ZhengPeiRu21/mod-playerbots](https://github.com/ZhengPeiRu21/mod-playerbots) and [celguar/mangosbot-bots](https://github.com/celguar/mangosbot-bots). We extend our gratitude to [@ZhengPeiRu21](https://github.com/ZhengPeiRu21) and [@celguar](https://github.com/celguar) for their continued efforts in maintaining the module. Also, a thank you to the many contributors who've helped build this project: From bb5ed37cd3270fc81759e05b3b491670dab59a6d Mon Sep 17 00:00:00 2001 From: SaW Date: Tue, 18 Nov 2025 18:48:52 +0100 Subject: [PATCH 31/47] Update codestyle_cpp.yml for review events and concurrency (#1836) Include ready_for_review type, to run when draft is converted to ready. Include concurrency; cancels obsolete tasks on new commits. --- .github/workflows/codestyle_cpp.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/codestyle_cpp.yml b/.github/workflows/codestyle_cpp.yml index b868e30b18..065732890f 100644 --- a/.github/workflows/codestyle_cpp.yml +++ b/.github/workflows/codestyle_cpp.yml @@ -5,11 +5,16 @@ on: - opened - reopened - synchronize + - ready_for_review paths: - src/** - "!README.md" - "!docs/**" +concurrency: + group: "codestyle-cppcheck-${{ github.event.pull_request.number }}" + cancel-in-progress: true + jobs: triage: runs-on: ubuntu-latest From 27311b734dd49c3a8c2f2a6923618b4c6eb5f3fe Mon Sep 17 00:00:00 2001 From: SaW Date: Tue, 18 Nov 2025 20:06:24 +0100 Subject: [PATCH 32/47] Revert "feat: Improve bot mount behavior to faster close distance between bot and master" (#1855) Reverts mod-playerbots/mod-playerbots#1760 This, as it is causing issues in BG, where bots just don't dismount and just stand there instead. image --- .../actions/CheckMountStateAction.cpp | 64 ++----------------- src/strategy/actions/CheckMountStateAction.h | 2 - 2 files changed, 4 insertions(+), 62 deletions(-) diff --git a/src/strategy/actions/CheckMountStateAction.cpp b/src/strategy/actions/CheckMountStateAction.cpp index fb67057d35..bf7a3a169a 100644 --- a/src/strategy/actions/CheckMountStateAction.cpp +++ b/src/strategy/actions/CheckMountStateAction.cpp @@ -122,21 +122,18 @@ bool CheckMountStateAction::Execute(Event /*event*/) bool shouldMount = false; Unit* currentTarget = AI_VALUE(Unit*, "current target"); - bool masterInCombat = master && master->IsInCombat(); - - if (currentTarget && (bot->IsInCombat() || masterInCombat)) + if (currentTarget) { - // Use target-based logic if bot is in combat OR master is in combat and needs assistance float dismountDistance = CalculateDismountDistance(); float mountDistance = CalculateMountDistance(); + float combatReach = bot->GetCombatReach() + currentTarget->GetCombatReach(); float distanceToTarget = bot->GetExactDist(currentTarget); - shouldDismount = (distanceToTarget <= dismountDistance); - shouldMount = (distanceToTarget > mountDistance); + shouldDismount = (distanceToTarget <= dismountDistance + combatReach); + shouldMount = (distanceToTarget > mountDistance + combatReach); } else { - // If neither bot nor master is in combat, prioritize master-following shouldMount = true; } @@ -163,19 +160,10 @@ bool CheckMountStateAction::Execute(Event /*event*/) else if (ShouldDismountForMaster(master) && bot->IsMounted()) { - // If master dismounted, stay mounted until close enough to assist - if (StayMountedToCloseDistance()) - return false; - Dismount(); return true; } - // Mount up to close distance to master if beneficial - allow mounting even if master is in combat - // as long as the bot itself is not in combat and has no attackers - else if (!bot->IsMounted() && noAttackers && !bot->IsInCombat() && ShouldMountToCloseDistance()) - return Mount(); - return false; } @@ -409,50 +397,6 @@ bool CheckMountStateAction::TryRandomMountFiltered(const std::mapGetDistance2d(bot, master); - - // If master is in combat, dismount at combat assist range to help immediately - if (master->IsInCombat()) - { - float assistRange = CalculateDismountDistance(); - return distToMaster > assistRange; - } - - // If master is not in combat, use smaller proximity range for general following - float masterProximityRange = 10.0f; // Close enough to be near master but not attack range - return distToMaster > masterProximityRange; -} - -bool CheckMountStateAction::ShouldMountToCloseDistance() const -{ - // Mount up to close distance to master if beneficial - // Uses the same logic as CalculateMountDistance() which already considers the 2-second mount cast time - // This handles cases where master is in combat but bot isn't, and bot needs to mount to reach master - - if (!master) - return false; - - // Only mount to close distance when actively following - if (!botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT)) - return false; - - float distToMaster = sServerFacade->GetDistance2d(bot, master); - float mountDistance = CalculateMountDistance(); - - // Mount if distance is greater than the calculated mount distance threshold - return distToMaster > mountDistance; -} - float CheckMountStateAction::CalculateDismountDistance() const { // Warrior bots should dismount far enough to charge (because it's important for generating some initial rage), diff --git a/src/strategy/actions/CheckMountStateAction.h b/src/strategy/actions/CheckMountStateAction.h index 3278cf930d..9a21838e17 100644 --- a/src/strategy/actions/CheckMountStateAction.h +++ b/src/strategy/actions/CheckMountStateAction.h @@ -60,8 +60,6 @@ class CheckMountStateAction : public UseItemAction bool TryPreferredMount(Player* master) const; uint32 GetMountType(Player* master) const; bool TryRandomMountFiltered(const std::map>& spells, int32 masterSpeed) const; - bool StayMountedToCloseDistance() const; - bool ShouldMountToCloseDistance() const; }; #endif From 8e03371147807b885eb578ada558642fa0cbf8bc Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Wed, 19 Nov 2025 17:00:59 -0300 Subject: [PATCH 33/47] Balance-Druid-improve-Starfall-usage-and-add-CC-safety (#1713) - Move Starfall from default actions to AOE strategy only - Require 2+ enemies for Starfall usage (prevents single-target casting) - Add CC safety: avoid casting Starfall near current CC targets - Prioritize Starfall over Hurricane in medium AOE situations - Remove Starfall from pull/opener rotation to prevent early single-target usage This prevents Balance druids from wasting Starfall on single targets and breaking crowd control effects in group content. --- src/strategy/druid/DruidActions.cpp | 29 +++++++++++++++++++++++++++++ src/strategy/druid/DruidActions.h | 2 ++ 2 files changed, 31 insertions(+) diff --git a/src/strategy/druid/DruidActions.cpp b/src/strategy/druid/DruidActions.cpp index b5a493dbb5..217db82508 100644 --- a/src/strategy/druid/DruidActions.cpp +++ b/src/strategy/druid/DruidActions.cpp @@ -7,6 +7,9 @@ #include "Event.h" #include "Playerbots.h" +#include "ServerFacade.h" +#include "AoeValues.h" +#include "TargetValue.h" NextAction** CastAbolishPoisonAction::getAlternatives() { @@ -30,6 +33,32 @@ bool CastEntanglingRootsCcAction::Execute(Event event) { return botAI->CastSpell Value* CastHibernateCcAction::GetTargetValue() { return context->GetValue("cc target", "hibernate"); } bool CastHibernateCcAction::Execute(Event event) { return botAI->CastSpell("hibernate", GetTarget()); } +bool CastStarfallAction::isUseful() +{ + if (!CastSpellAction::isUseful()) + return false; + + // Avoid breaking CC + WorldLocation aoePos = *context->GetValue("aoe position"); + Unit* ccTarget = context->GetValue("current cc target")->Get(); + if (ccTarget && ccTarget->IsAlive()) + { + float dist2d = sServerFacade->GetDistance2d(ccTarget, aoePos.GetPositionX(), aoePos.GetPositionY()); + if (sServerFacade->IsDistanceLessOrEqualThan(dist2d, sPlayerbotAIConfig->aoeRadius)) + return false; + } + + // Avoid single-target usage on initial pull + uint8 aoeCount = *context->GetValue("aoe count"); + if (aoeCount < 2) + { + Unit* target = context->GetValue("current target")->Get(); + if (!target || (!botAI->HasAura("moonfire", target) && !botAI->HasAura("insect swarm", target))) + return false; + } + + return true; +} NextAction** CastReviveAction::getPrerequisites() { diff --git a/src/strategy/druid/DruidActions.h b/src/strategy/druid/DruidActions.h index 402073d26d..d0af6e5a49 100644 --- a/src/strategy/druid/DruidActions.h +++ b/src/strategy/druid/DruidActions.h @@ -144,6 +144,8 @@ class CastStarfallAction : public CastSpellAction { public: CastStarfallAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "starfall") {} + + bool isUseful() override; }; class CastHurricaneAction : public CastSpellAction From 0b1b0eaeccdbf1fee18311ce3389eb6cfc6be595 Mon Sep 17 00:00:00 2001 From: Alex Dcnh <140754794+Wishmaster117@users.noreply.github.com> Date: Fri, 21 Nov 2025 13:18:14 +0100 Subject: [PATCH 34/47] Core - Fix RTSC SeeSpellAction crash on malformed WorldPacket (#1841) ## Summary This PR fixes the Crash 1 Source from Issue [#1840](https://github.com/mod-playerbots/mod-playerbots/issues/1840) posted in a @Regrad posted logs, in `SeeSpellAction::Execute` when an RTSC "see spell" event arrives with an empty or malformed `WorldPacket`. In that case the code used to read from the packet without any validation, causing a `ByteBufferException` and a crash in the map thread. ## Fix - Reset the packet read position and check that the RTSC header (castCount + spellId + castFlags) fits into the packet before reading. - Wrap `SpellCastTargets::Read` in a `try { } catch (ByteBufferException const&) { }` block so truncated RTSC payloads are handled gracefully. - Check that `targets.GetDst()` is not `nullptr` before accessing its position. For valid RTSC packets the behavior is unchanged; malformed packets are now safely ignored instead of crashing the server. ## Testing - Sent bots to multiple locations using RTSC and verified they still move as before. - Reproduced the previous crash scenario with malformed RTSC packets: the worldserver no longer crashes and the event is simply ignored. --------- Co-authored-by: bash Co-authored-by: bashermens <31279994+hermensbas@users.noreply.github.com> --- src/strategy/actions/SeeSpellAction.cpp | 42 ++++++++++++++++++++----- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/src/strategy/actions/SeeSpellAction.cpp b/src/strategy/actions/SeeSpellAction.cpp index 8b90adece0..f42dcade64 100644 --- a/src/strategy/actions/SeeSpellAction.cpp +++ b/src/strategy/actions/SeeSpellAction.cpp @@ -12,6 +12,7 @@ #include "RTSCValues.h" #include "RtscAction.h" #include "PositionValue.h" +#include "ByteBuffer.h" Creature* SeeSpellAction::CreateWps(Player* wpOwner, float x, float y, float z, float o, uint32 entry, Creature* lastWp, bool important) @@ -31,27 +32,52 @@ Creature* SeeSpellAction::CreateWps(Player* wpOwner, float x, float y, float z, bool SeeSpellAction::Execute(Event event) { - WorldPacket p(event.getPacket()); // + // RTSC packet data + WorldPacket p(event.getPacket()); + uint8 castCount; uint32 spellId; - uint8 castCount, castFlags; - Player* master = botAI->GetMaster(); + uint8 castFlags; - p.rpos(0); - p >> castCount >> spellId >> castFlags; + // check RTSC header size = castCount (uint8) + spellId (uint32) + castFlags (uint8) + uint32 const rtscHeaderSize = sizeof(uint8) + sizeof(uint32) + sizeof(uint8); + if (p.size() < rtscHeaderSize) + { + LOG_WARN("playerbots", "SeeSpellAction: Corrupt RTSC packet size={}, expected>={}", p.size(), rtscHeaderSize); + return false; + } + Player* master = botAI->GetMaster(); if (!master) return false; + // read RTSC packet data + p.rpos(0); // set read position to start + p >> castCount >> spellId >> castFlags; + // if (!botAI->HasStrategy("RTSC", botAI->GetState())) // return false; if (spellId != RTSC_MOVE_SPELL) return false; - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); - + // should not throw exception,just defensive measure to prevent any crashes when core function breaks. SpellCastTargets targets; - targets.Read(p, botAI->GetMaster()); + try + { + targets.Read(p, master); + if (!targets.GetDst()) + { + // do not dereference a null destination; ignore malformed RTSC packets instead of crashing + LOG_WARN("playerbots", "SeeSpellAction: (malformed) RTSC payload does not contain full targets data"); + return false; + } + } + catch (ByteBufferException const&) + { + // ignore malformed RTSC packets instead of crashing + LOG_WARN("playerbots", "SeeSpellAction: Failed deserialization (malformed) RTSC payload"); + return false; + } WorldPosition spellPosition(master->GetMapId(), targets.GetDst()->_position); SET_AI_VALUE(WorldPosition, "see spell location", spellPosition); From 0c1700c1176222ea422b99270e636baf8c9ad10a Mon Sep 17 00:00:00 2001 From: Alex Dcnh <140754794+Wishmaster117@users.noreply.github.com> Date: Fri, 21 Nov 2025 15:56:03 +0100 Subject: [PATCH 35/47] CORE - Improved language detection for bots (#1784) I've had this problem for a long time, my bots only speak English even though I'm playing on a French client. I suppose this must be the case for some other people who do not have a large number of players with the same local client. If we use French DBCs, the bots bug because they only recognize US DBCs. From what I understand, the language is chosen as follows: On load, the module reads the entire `ai_playerbot_texts` table and stores each text variant in a dictionary indexed by the locale ID: the `text` column remains the default value (English), and the `text_loc1` to `text_loc8` columns fill slots 1 through 8. Whenever a real player connects, the module increments a counter for that player's DBC locale using `AddLocalePriority(player->GetSession()->GetSessionDbcLocale())`. When a bot needs a text, `GetLocalePriority()` returns the most frequently used locale index among currently connected players. The corresponding string is then retrieved. if the box is empty, we fall back to the English version (text[0]). ### This PR improve language detection. **Summary** - log both the client DBC locale and the account database locale when a player logs in - fall back to the account locale when the client reports enUS but the account is configured for another locale - keep the existing vote-based selection so bots always speak the majority language among connected players **Therefore, the original behavior is maintained. Bots still choose the most represented language among connected players (the counter is simply more efficient by prioritizing the account's locale when it differs from the client's English). For example, if more English-speaking players are connected, the language will revert to English, as the bots always share the majority locale.** --- src/PlayerbotMgr.cpp | 22 ++++++++++++++++++++-- src/PlayerbotTextMgr.cpp | 13 ++++++++----- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/PlayerbotMgr.cpp b/src/PlayerbotMgr.cpp index 6bdbf9b659..fe4a714f72 100644 --- a/src/PlayerbotMgr.cpp +++ b/src/PlayerbotMgr.cpp @@ -1602,8 +1602,26 @@ void PlayerbotMgr::OnBotLoginInternal(Player* const bot) void PlayerbotMgr::OnPlayerLogin(Player* player) { + if (!player) + return; + + WorldSession* session = player->GetSession(); + if (!session) + { + LOG_WARN("playerbots", "Unable to register locale priority for player {} because the session is missing", player->GetName()); + return; + } + + // DB locale (source of bot text translation) + LocaleConstant const databaseLocale = session->GetSessionDbLocaleIndex(); + + // For bot texts (DB-driven), prefer the database locale with a safe fallback. + LocaleConstant usedLocale = databaseLocale; + if (usedLocale >= MAX_LOCALES) + usedLocale = LOCALE_enUS; // fallback + // set locale priority for bot texts - sPlayerbotTextMgr->AddLocalePriority(player->GetSession()->GetSessionDbcLocale()); + sPlayerbotTextMgr->AddLocalePriority(usedLocale); if (sPlayerbotAIConfig->selfBotLevel > 2) HandlePlayerbotCommand("self", player); @@ -1611,7 +1629,7 @@ void PlayerbotMgr::OnPlayerLogin(Player* player) if (!sPlayerbotAIConfig->botAutologin) return; - uint32 accountId = player->GetSession()->GetAccountId(); + uint32 accountId = session->GetAccountId(); QueryResult results = CharacterDatabase.Query("SELECT name FROM characters WHERE account = {}", accountId); if (results) { diff --git a/src/PlayerbotTextMgr.cpp b/src/PlayerbotTextMgr.cpp index 3caa96a738..1dce9a29a5 100644 --- a/src/PlayerbotTextMgr.cpp +++ b/src/PlayerbotTextMgr.cpp @@ -190,26 +190,29 @@ bool PlayerbotTextMgr::GetBotText(std::string name, std::string& text, std::map< void PlayerbotTextMgr::AddLocalePriority(uint32 locale) { - if (!locale) + if (locale >= MAX_LOCALES) + { + LOG_WARN("playerbots", "Ignoring locale {} for bot texts because it exceeds MAX_LOCALES ({})", locale, MAX_LOCALES - 1); return; + } botTextLocalePriority[locale]++; } uint32 PlayerbotTextMgr::GetLocalePriority() { - uint32 topLocale = 0; - // if no real players online, reset top locale - if (!sWorldSessionMgr->GetActiveSessionCount()) + uint32 const activeSessions = sWorldSessionMgr->GetActiveSessionCount(); + if (!activeSessions) { ResetLocalePriority(); return 0; } + uint32 topLocale = 0; for (uint8 i = 0; i < MAX_LOCALES; ++i) { - if (botTextLocalePriority[i] > topLocale) + if (botTextLocalePriority[i] > botTextLocalePriority[topLocale]) topLocale = i; } From d97870facdf4c51b9fdcb2fc338c94ea6dfaaf9c Mon Sep 17 00:00:00 2001 From: bashermens <31279994+hermensbas@users.noreply.github.com> Date: Fri, 21 Nov 2025 20:45:23 +0100 Subject: [PATCH 36/47] fix: warning updating movement flags while rooted (#1858) fixes https://github.com/mod-playerbots/mod-playerbots/issues/1854 ----- Also includes fixes for: ----- * Bots swimming with waterWalk kept switching between swimming and walking, as result jittering effect swimming under water when water walking active * Bots flying close above water they would land on water and start walking, now they stay flying unless on solid ground they will land and start walking by design ----- Moved all flag setting to updateMovementState: * So all movement flag are handled in updateMovementState which also contains the restricted movement logic. * Handle restricted movement logic and preventing SendMovementFlagUpdate while being restricted. ----- Known issue when flying the following bots feel a bit jittering, wont touch for now at least till core movement changes quirks has been dealt with. The current code is the extended version of what is originally was before core merge with refactored movements. Once the core movement refactors are settled a bit more i would like to revisit this code; as i would expect more imperative code and less manual flag setting e.g. bot->SetWaterWalking, SetGravitiy..SetCanFly etc. --- src/strategy/actions/MovementActions.cpp | 148 +++++++++++------------ 1 file changed, 74 insertions(+), 74 deletions(-) diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index 15e098a521..7078557d1e 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -971,73 +971,86 @@ bool MovementAction::Follow(Unit* target, float distance) { return Follow(target void MovementAction::UpdateMovementState() { - // state flags - const float gLvlZ = bot->GetMapWaterOrGroundLevel(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ()); - const bool onGround = bot->GetPositionZ() < gLvlZ + 1.f; - const bool wantsToFly = bot->HasIncreaseMountedFlightSpeedAura() || bot->HasFlyAura(); - const auto master = botAI ? botAI->GetMaster() : nullptr; // real or not - const bool masterIsFlying = master && master->HasUnitMovementFlag(MOVEMENTFLAG_FLYING); - const bool isFlying = bot->HasUnitMovementFlag(MOVEMENTFLAG_FLYING); - const auto liquidState = bot->GetLiquidData().Status; // default LIQUID_MAP_NO_WATER - const bool isWaterArea = liquidState != LIQUID_MAP_NO_WATER; - const bool isUnderWater = liquidState == LIQUID_MAP_UNDER_WATER; - const bool isInWater = liquidState == LIQUID_MAP_IN_WATER; - const bool isWaterWalking = bot->HasUnitMovementFlag(MOVEMENTFLAG_WATERWALKING); - const bool isSwimming = bot->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING); - const bool wantsToWaterWalk = bot->HasWaterWalkAura(); - const bool wantsToSwim = isInWater || isUnderWater; - - // handle water state - if (isWaterArea) - { - // water walking - if (wantsToWaterWalk && !isWaterWalking && !isUnderWater && !isFlying) - { - bot->RemoveUnitMovementFlag(MOVEMENTFLAG_SWIMMING); - bot->AddUnitMovementFlag(MOVEMENTFLAG_WATERWALKING); - bot->SendMovementFlagUpdate(); + const bool isCurrentlyRestricted = // see if the bot is currently slowed, rooted, or otherwise unable to move + bot->isFrozen() || + bot->IsPolymorphed() || + bot->HasRootAura() || + bot->HasStunAura() || + bot->HasConfuseAura() || + bot->HasUnitState(UNIT_STATE_LOST_CONTROL); + + // no update movement flags while movement is current restricted. + if (!isCurrentlyRestricted && bot->IsAlive()) + { + // state flags + const auto master = botAI ? botAI->GetMaster() : nullptr; // real player or not + const bool masterIsFlying = master ? master->HasUnitMovementFlag(MOVEMENTFLAG_FLYING) : true; + const bool masterIsSwimming = master ? master->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING) : true; + const auto liquidState = bot->GetLiquidData().Status; // default LIQUID_MAP_NO_WATER + const float gZ = bot->GetMapWaterOrGroundLevel(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ()); + const bool wantsToFly = bot->HasIncreaseMountedFlightSpeedAura() || bot->HasFlyAura(); + const bool isFlying = bot->HasUnitMovementFlag(MOVEMENTFLAG_FLYING); + const bool isWaterArea = liquidState != LIQUID_MAP_NO_WATER; + const bool isUnderWater = liquidState == LIQUID_MAP_UNDER_WATER; + const bool isInWater = liquidState == LIQUID_MAP_IN_WATER; + const bool isWaterWalking = bot->HasUnitMovementFlag(MOVEMENTFLAG_WATERWALKING); + const bool isSwimming = bot->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING); + const bool wantsToWaterWalk = bot->HasWaterWalkAura(); + const bool wantsToSwim = isInWater || isUnderWater; + const bool onGroundZ = (bot->GetPositionZ() < gZ + 1.f) && !isWaterArea; + bool movementFlagsUpdated = false; + + // handle water state + if (isWaterArea && !isFlying) + { + // water walking + if (wantsToWaterWalk && !isWaterWalking && !masterIsSwimming) + { + bot->RemoveUnitMovementFlag(MOVEMENTFLAG_SWIMMING); + bot->AddUnitMovementFlag(MOVEMENTFLAG_WATERWALKING); + movementFlagsUpdated = true; + } + // swimming + else if (wantsToSwim && !isSwimming && masterIsSwimming) + { + bot->RemoveUnitMovementFlag(MOVEMENTFLAG_WATERWALKING); + bot->AddUnitMovementFlag(MOVEMENTFLAG_SWIMMING); + movementFlagsUpdated = true; + } } - // swimming - else if (wantsToSwim && !isSwimming && !wantsToWaterWalk && !isFlying) + else if (isSwimming || isWaterWalking) { + // reset water flags + bot->RemoveUnitMovementFlag(MOVEMENTFLAG_SWIMMING); bot->RemoveUnitMovementFlag(MOVEMENTFLAG_WATERWALKING); - bot->AddUnitMovementFlag(MOVEMENTFLAG_SWIMMING); - bot->SendMovementFlagUpdate(); + movementFlagsUpdated = true; } - } - else - { - // reset flags, if not will inherit incorrect walk speed here and there - // when transistions between land and water. - bot->RemoveUnitMovementFlag(MOVEMENTFLAG_SWIMMING); - bot->RemoveUnitMovementFlag(MOVEMENTFLAG_WATERWALKING); - bot->SendMovementFlagUpdate(); - } - // handle flying state - if (wantsToFly && !isFlying && masterIsFlying) - { - bot->AddUnitMovementFlag(MOVEMENTFLAG_FLYING); - bot->SendMovementFlagUpdate(); - } - else if ((!wantsToFly || onGround) && isFlying) - { - bot->RemoveUnitMovementFlag(MOVEMENTFLAG_FLYING); - bot->SendMovementFlagUpdate(); - } + // handle flying state + if (wantsToFly && !isFlying && masterIsFlying) + { + bot->AddUnitMovementFlag(MOVEMENTFLAG_CAN_FLY); + bot->AddUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY); + bot->AddUnitMovementFlag(MOVEMENTFLAG_FLYING); + movementFlagsUpdated = true; + } + else if ((!wantsToFly || onGroundZ) && isFlying) + { + bot->RemoveUnitMovementFlag(MOVEMENTFLAG_CAN_FLY); + bot->RemoveUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY); + bot->RemoveUnitMovementFlag(MOVEMENTFLAG_FLYING); + movementFlagsUpdated = true; + } - // See if the bot is currently slowed, rooted, or otherwise unable to move - bool isCurrentlyRestricted = bot->isFrozen() || bot->IsPolymorphed() || bot->HasRootAura() || bot->HasStunAura() || - bot->HasConfuseAura() || bot->HasUnitState(UNIT_STATE_LOST_CONTROL); + // detect if movement restrictions have been lifted, CC just ended. + if (wasMovementRestricted) + movementFlagsUpdated = true; // refresh movement state to ensure animations play correctly - // Detect if movement restrictions have been lifted - if (wasMovementRestricted && !isCurrentlyRestricted && bot->IsAlive()) - { - // CC just ended - refresh movement state to ensure animations play correctly - bot->SendMovementFlagUpdate(); + if (movementFlagsUpdated) + bot->SendMovementFlagUpdate(); } - // Save current state for the next check + // Save current state for the next check wasMovementRestricted = isCurrentlyRestricted; // Temporary speed increase in group @@ -1820,25 +1833,12 @@ void MovementAction::DoMovePoint(Unit* unit, float x, float y, float z, bool gen if (!mm) return; - // enable flying - if (unit->HasUnitMovementFlag(MOVEMENTFLAG_FLYING)) - { - unit->AddUnitMovementFlag(MOVEMENTFLAG_CAN_FLY); - unit->AddUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY); - } - else - { - unit->RemoveUnitMovementFlag(MOVEMENTFLAG_CAN_FLY); - unit->RemoveUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY); - } - // enable water walking if (unit->HasUnitMovementFlag(MOVEMENTFLAG_WATERWALKING)) { - float gLvlZ = unit->GetMapWaterOrGroundLevel(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ()); - unit->UpdatePosition(unit->GetPositionX(), unit->GetPositionY(), gLvlZ, false); - // z = gLvlZ; do not overwrite Z axex, otherwise you wont be able to steer the bots into swimming when water - // walking. + float gZ = unit->GetMapWaterOrGroundLevel(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ()); + unit->UpdatePosition(unit->GetPositionX(), unit->GetPositionY(), gZ, false); + // z = gZ; no overwrite Z axe otherwise you cant steer the bots into swimming when water walking. } mm->Clear(); From 10213d838177bd6ccf7ad3a5264e160d837bba5d Mon Sep 17 00:00:00 2001 From: blinkysc <37940565+blinkysc@users.noreply.github.com> Date: Fri, 21 Nov 2025 14:55:55 -0600 Subject: [PATCH 37/47] Add thread safety for group operations (#1816) Fixes crashes and race conditions when bots perform group/guild/arena operations by moving thread-unsafe code to world thread. Potentially fixes #1124 ## Changes - Added operation queue system that runs in world thread - Group operations (invite, remove, convert to raid, set leader) now queued - Arena formation refactored to use queue - Guild operations changed to use packet queueing ## Testing Set `MapUpdate.Threads` > 1 in worldserver.conf to enable multiple map threads, then test: - Group formation and disbanding - Arena team formation - Guild operations (invite, promote, demote, remove) - Run with TSAN cmake ../ \ -DCMAKE_CXX_FLAGS="-fsanitize=thread -g -O1" \ -DCMAKE_C_FLAGS="-fsanitize=thread -g -O1" \ -DCMAKE_EXE_LINKER_FLAGS="-fsanitize=thread" \ -DCMAKE_INSTALL_PREFIX=/path/to/install \ -DCMAKE_BUILD_TYPE=RelWithDebInfo build export TSAN_OPTIONS="log_path=tsan_report:halt_on_error=0:second_deadlock_stack=1" ./worldserver The crashes/race conditions should no longer occur with concurrent map threads. ## New Files - `PlayerbotOperation.h` - Base class defining the operation interface (Execute, IsValid, GetPriority) - `PlayerbotOperations.h` - Concrete implementations: GroupInviteOperation, GroupRemoveMemberOperation, GroupConvertToRaidOperation, GroupSetLeaderOperation, ArenaGroupFormationOperation - `PlayerbotWorldThreadProcessor.h/cpp` - Singleton processor with mutex-protected queue, processes operations in WorldScript::OnUpdate hook, handles batch processing and validation --------- Co-authored-by: blinkysc Co-authored-by: SaW Co-authored-by: bash --- src/PlayerbotAIConfig.cpp | 4 +- src/PlayerbotMgr.cpp | 35 +- src/PlayerbotOperation.h | 93 ++++ src/PlayerbotOperations.h | 500 ++++++++++++++++++ src/PlayerbotWorldThreadProcessor.cpp | 217 ++++++++ src/PlayerbotWorldThreadProcessor.h | 142 +++++ src/Playerbots.cpp | 14 +- .../actions/GuildManagementActions.cpp | 32 +- src/strategy/actions/GuildManagementActions.h | 6 +- src/strategy/actions/InviteToGroupAction.cpp | 20 +- .../actions/PassLeadershipToMasterAction.cpp | 7 +- 11 files changed, 1015 insertions(+), 55 deletions(-) create mode 100644 src/PlayerbotOperation.h create mode 100644 src/PlayerbotOperations.h create mode 100644 src/PlayerbotWorldThreadProcessor.cpp create mode 100644 src/PlayerbotWorldThreadProcessor.h diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index bdafc10c63..db69387e6f 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -725,8 +725,8 @@ std::string const PlayerbotAIConfig::GetTimestampStr() // HH hour (2 digits 00-23) // MM minutes (2 digits 00-59) // SS seconds (2 digits 00-59) - char buf[20]; - snprintf(buf, 20, "%04d-%02d-%02d %02d-%02d-%02d", aTm->tm_year + 1900, aTm->tm_mon + 1, aTm->tm_mday, aTm->tm_hour, + char buf[32]; + snprintf(buf, sizeof(buf), "%04d-%02d-%02d %02d-%02d-%02d", aTm->tm_year + 1900, aTm->tm_mon + 1, aTm->tm_mday, aTm->tm_hour, aTm->tm_min, aTm->tm_sec); return std::string(buf); } diff --git a/src/PlayerbotMgr.cpp b/src/PlayerbotMgr.cpp index fe4a714f72..d96851dafc 100644 --- a/src/PlayerbotMgr.cpp +++ b/src/PlayerbotMgr.cpp @@ -27,7 +27,9 @@ #include "PlayerbotAIConfig.h" #include "PlayerbotDbStore.h" #include "PlayerbotFactory.h" +#include "PlayerbotOperations.h" #include "PlayerbotSecurity.h" +#include "PlayerbotWorldThreadProcessor.h" #include "Playerbots.h" #include "RandomPlayerbotMgr.h" #include "SharedDefines.h" @@ -85,7 +87,6 @@ class PlayerbotLoginQueryHolder : public LoginQueryHolder void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId) { - // bot is loading if (botLoading.find(playerGuid) != botLoading.end()) return; @@ -195,7 +196,9 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con } sRandomPlayerbotMgr->OnPlayerLogin(bot); - OnBotLogin(bot); + + auto op = std::make_unique(bot->GetGUID(), this); + sPlayerbotWorldProcessor->QueueOperation(std::move(op)); botLoading.erase(holder.GetGuid()); } @@ -316,11 +319,9 @@ void PlayerbotHolder::LogoutPlayerBot(ObjectGuid guid) if (!botAI) return; - Group* group = bot->GetGroup(); - if (group && !bot->InBattleground() && !bot->InBattlegroundQueue() && botAI->HasActivePlayerMaster()) - { - sPlayerbotDbStore->Save(botAI); - } + // Queue group cleanup operation for world thread + auto cleanupOp = std::make_unique(guid); + sPlayerbotWorldProcessor->QueueOperation(std::move(cleanupOp)); LOG_DEBUG("playerbots", "Bot {} logging out", bot->GetName().c_str()); bot->SaveToDB(false, false); @@ -549,6 +550,7 @@ void PlayerbotHolder::OnBotLogin(Player* const bot) botAI->TellMaster("Hello!", PLAYERBOT_SECURITY_TALK); + // Queue group operations for world thread if (master && master->GetGroup() && !group) { Group* mgroup = master->GetGroup(); @@ -556,24 +558,29 @@ void PlayerbotHolder::OnBotLogin(Player* const bot) { if (!mgroup->isRaidGroup() && !mgroup->isLFGGroup() && !mgroup->isBGGroup() && !mgroup->isBFGroup()) { - mgroup->ConvertToRaid(); + // Queue ConvertToRaid operation + auto convertOp = std::make_unique(master->GetGUID()); + sPlayerbotWorldProcessor->QueueOperation(std::move(convertOp)); } if (mgroup->isRaidGroup()) { - mgroup->AddMember(bot); + // Queue AddMember operation + auto addOp = std::make_unique(master->GetGUID(), bot->GetGUID()); + sPlayerbotWorldProcessor->QueueOperation(std::move(addOp)); } } else { - mgroup->AddMember(bot); + // Queue AddMember operation + auto addOp = std::make_unique(master->GetGUID(), bot->GetGUID()); + sPlayerbotWorldProcessor->QueueOperation(std::move(addOp)); } } else if (master && !group) { - Group* newGroup = new Group(); - newGroup->Create(master); - sGroupMgr->AddGroup(newGroup); - newGroup->AddMember(bot); + // Queue group creation and AddMember operation + auto inviteOp = std::make_unique(master->GetGUID(), bot->GetGUID()); + sPlayerbotWorldProcessor->QueueOperation(std::move(inviteOp)); } // if (master) // { diff --git a/src/PlayerbotOperation.h b/src/PlayerbotOperation.h new file mode 100644 index 0000000000..6ac303d323 --- /dev/null +++ b/src/PlayerbotOperation.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_OPERATION_H +#define _PLAYERBOT_OPERATION_H + +#include "Common.h" +#include "ObjectGuid.h" +#include + +/** + * @brief Base class for thread-unsafe operations that must be executed in the world thread + * + * PlayerbotOperation represents an operation that needs to be deferred from a map thread + * to the world thread for safe execution. Examples include group modifications, LFG operations, + * guild operations, etc. + * + * Thread Safety: + * - The constructor and data members must be thread-safe (use copies, not pointers) + * - Execute() is called in the world thread and can safely perform thread-unsafe operations + * - Subclasses must not store raw pointers to (core/world thread) game object (use ObjectGuid instead) + */ +class PlayerbotOperation +{ +public: + virtual ~PlayerbotOperation() = default; + + /** + * @brief Execute this operation in the world thread + * + * This method is called by PlayerbotWorldThreadProcessor::Update() which runs in the world thread. + * It's safe to perform any thread-unsafe operation here (Group, LFG, Guild, etc.) + * + * @return true if operation succeeded, false if it failed + */ + virtual bool Execute() = 0; + + /** + * @brief Get the bot GUID this operation is for (optional) + * + * Used for logging and debugging purposes. + * + * @return ObjectGuid of the bot, or ObjectGuid::Empty if not applicable + */ + virtual ObjectGuid GetBotGuid() const { return ObjectGuid::Empty; } + + /** + * @brief Get the operation priority (higher = more urgent) + * + * Priority levels: + * - 100: Critical (crash prevention, cleanup operations) + * - 50: High (player-facing operations like group invites) + * - 10: Normal (background operations) + * - 0: Low (statistics, logging) + * + * @return Priority value (0-100) + */ + virtual uint32 GetPriority() const { return 10; } + + /** + * @brief Get a human-readable name for this operation + * + * Used for logging and debugging. + * + * @return Operation name + */ + virtual std::string GetName() const { return "Unknown Operation"; } + + /** + * @brief Check if this operation is still valid + * + * Called before Execute() to check if the operation should still be executed. + * For example, if a bot logged out, group invite operations for that bot can be skipped. + * + * @return true if operation should be executed, false to skip + */ + virtual bool IsValid() const { return true; } +}; + +/** + * @brief Comparison operator for priority queue (higher priority first) + */ +struct PlayerbotOperationComparator +{ + bool operator()(const std::unique_ptr& a, const std::unique_ptr& b) const + { + return a->GetPriority() < b->GetPriority(); // Lower priority goes to back of queue + } +}; + +#endif diff --git a/src/PlayerbotOperations.h b/src/PlayerbotOperations.h new file mode 100644 index 0000000000..d7c2b47bb8 --- /dev/null +++ b/src/PlayerbotOperations.h @@ -0,0 +1,500 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_OPERATIONS_H +#define _PLAYERBOT_OPERATIONS_H + +#include "Group.h" +#include "GroupMgr.h" +#include "GuildMgr.h" +#include "ObjectAccessor.h" +#include "PlayerbotOperation.h" +#include "Player.h" +#include "PlayerbotAI.h" +#include "PlayerbotMgr.h" +#include "PlayerbotDbStore.h" +#include "RandomPlayerbotMgr.h" + +// Group invite operation +class GroupInviteOperation : public PlayerbotOperation +{ +public: + GroupInviteOperation(ObjectGuid botGuid, ObjectGuid targetGuid) + : m_botGuid(botGuid), m_targetGuid(targetGuid) + { + } + + bool Execute() override + { + Player* bot = ObjectAccessor::FindPlayer(m_botGuid); + Player* target = ObjectAccessor::FindPlayer(m_targetGuid); + + if (!bot || !target) + { + LOG_DEBUG("playerbots", "GroupInviteOperation: Bot or target not found"); + return false; + } + + // Check if target is already in a group + if (target->GetGroup()) + { + LOG_DEBUG("playerbots", "GroupInviteOperation: Target {} is already in a group", target->GetName()); + return false; + } + + Group* group = bot->GetGroup(); + + // Create group if bot doesn't have one + if (!group) + { + group = new Group; + if (!group->Create(bot)) + { + delete group; + LOG_ERROR("playerbots", "GroupInviteOperation: Failed to create group for bot {}", bot->GetName()); + return false; + } + sGroupMgr->AddGroup(group); + LOG_DEBUG("playerbots", "GroupInviteOperation: Created new group for bot {}", bot->GetName()); + } + + // Convert to raid if needed (more than 5 members) + if (!group->isRaidGroup() && group->GetMembersCount() >= 5) + { + group->ConvertToRaid(); + LOG_DEBUG("playerbots", "GroupInviteOperation: Converted group to raid"); + } + + // Add member to group + if (group->AddMember(target)) + { + LOG_DEBUG("playerbots", "GroupInviteOperation: Successfully added {} to group", target->GetName()); + return true; + } + else + { + LOG_ERROR("playerbots", "GroupInviteOperation: Failed to add {} to group", target->GetName()); + return false; + } + } + + ObjectGuid GetBotGuid() const override { return m_botGuid; } + + uint32 GetPriority() const override { return 50; } // High priority (player-facing) + + std::string GetName() const override { return "GroupInvite"; } + + bool IsValid() const override + { + // Check if bot still exists and is online + Player* bot = ObjectAccessor::FindPlayer(m_botGuid); + Player* target = ObjectAccessor::FindPlayer(m_targetGuid); + return bot && target; + } + +private: + ObjectGuid m_botGuid; + ObjectGuid m_targetGuid; +}; + +// Remove member from group +class GroupRemoveMemberOperation : public PlayerbotOperation +{ +public: + GroupRemoveMemberOperation(ObjectGuid botGuid, ObjectGuid targetGuid) + : m_botGuid(botGuid), m_targetGuid(targetGuid) + { + } + + bool Execute() override + { + Player* bot = ObjectAccessor::FindPlayer(m_botGuid); + Player* target = ObjectAccessor::FindPlayer(m_targetGuid); + + if (!bot || !target) + return false; + + Group* group = bot->GetGroup(); + if (!group) + { + LOG_DEBUG("playerbots", "GroupRemoveMemberOperation: Bot is not in a group"); + return false; + } + + if (!group->IsMember(target->GetGUID())) + { + LOG_DEBUG("playerbots", "GroupRemoveMemberOperation: Target is not in bot's group"); + return false; + } + + group->RemoveMember(target->GetGUID()); + LOG_DEBUG("playerbots", "GroupRemoveMemberOperation: Removed {} from group", target->GetName()); + return true; + } + + ObjectGuid GetBotGuid() const override { return m_botGuid; } + + uint32 GetPriority() const override { return 50; } + + std::string GetName() const override { return "GroupRemoveMember"; } + + bool IsValid() const override + { + Player* bot = ObjectAccessor::FindPlayer(m_botGuid); + return bot != nullptr; + } + +private: + ObjectGuid m_botGuid; + ObjectGuid m_targetGuid; +}; + +// Convert group to raid +class GroupConvertToRaidOperation : public PlayerbotOperation +{ +public: + GroupConvertToRaidOperation(ObjectGuid botGuid) : m_botGuid(botGuid) {} + + bool Execute() override + { + Player* bot = ObjectAccessor::FindPlayer(m_botGuid); + if (!bot) + return false; + + Group* group = bot->GetGroup(); + if (!group) + { + LOG_DEBUG("playerbots", "GroupConvertToRaidOperation: Bot is not in a group"); + return false; + } + + if (group->isRaidGroup()) + { + LOG_DEBUG("playerbots", "GroupConvertToRaidOperation: Group is already a raid"); + return true; // Success - already in desired state + } + + group->ConvertToRaid(); + LOG_DEBUG("playerbots", "GroupConvertToRaidOperation: Converted group to raid"); + return true; + } + + ObjectGuid GetBotGuid() const override { return m_botGuid; } + + uint32 GetPriority() const override { return 50; } + + std::string GetName() const override { return "GroupConvertToRaid"; } + + bool IsValid() const override + { + Player* bot = ObjectAccessor::FindPlayer(m_botGuid); + return bot != nullptr; + } + +private: + ObjectGuid m_botGuid; +}; + +// Set group leader +class GroupSetLeaderOperation : public PlayerbotOperation +{ +public: + GroupSetLeaderOperation(ObjectGuid botGuid, ObjectGuid newLeaderGuid) + : m_botGuid(botGuid), m_newLeaderGuid(newLeaderGuid) + { + } + + bool Execute() override + { + Player* bot = ObjectAccessor::FindPlayer(m_botGuid); + Player* newLeader = ObjectAccessor::FindPlayer(m_newLeaderGuid); + + if (!bot || !newLeader) + return false; + + Group* group = bot->GetGroup(); + if (!group) + { + LOG_DEBUG("playerbots", "GroupSetLeaderOperation: Bot is not in a group"); + return false; + } + + if (!group->IsMember(newLeader->GetGUID())) + { + LOG_DEBUG("playerbots", "GroupSetLeaderOperation: New leader is not in the group"); + return false; + } + + group->ChangeLeader(newLeader->GetGUID()); + LOG_DEBUG("playerbots", "GroupSetLeaderOperation: Changed leader to {}", newLeader->GetName()); + return true; + } + + ObjectGuid GetBotGuid() const override { return m_botGuid; } + + uint32 GetPriority() const override { return 50; } + + std::string GetName() const override { return "GroupSetLeader"; } + + bool IsValid() const override + { + Player* bot = ObjectAccessor::FindPlayer(m_botGuid); + Player* newLeader = ObjectAccessor::FindPlayer(m_newLeaderGuid); + return bot && newLeader; + } + +private: + ObjectGuid m_botGuid; + ObjectGuid m_newLeaderGuid; +}; + +// Form arena group +class ArenaGroupFormationOperation : public PlayerbotOperation +{ +public: + ArenaGroupFormationOperation(ObjectGuid leaderGuid, std::vector memberGuids, + uint32 requiredSize, uint32 arenaTeamId, std::string arenaTeamName) + : m_leaderGuid(leaderGuid), m_memberGuids(memberGuids), + m_requiredSize(requiredSize), m_arenaTeamId(arenaTeamId), m_arenaTeamName(arenaTeamName) + { + } + + bool Execute() override + { + Player* leader = ObjectAccessor::FindPlayer(m_leaderGuid); + if (!leader) + { + LOG_ERROR("playerbots", "ArenaGroupFormationOperation: Leader not found"); + return false; + } + + // Step 1: Remove all members from their existing groups + for (const ObjectGuid& memberGuid : m_memberGuids) + { + Player* member = ObjectAccessor::FindPlayer(memberGuid); + if (!member) + continue; + + Group* memberGroup = member->GetGroup(); + if (memberGroup) + { + memberGroup->RemoveMember(memberGuid); + LOG_DEBUG("playerbots", "ArenaGroupFormationOperation: Removed {} from their existing group", + member->GetName()); + } + } + + // Step 2: Disband leader's existing group + Group* leaderGroup = leader->GetGroup(); + if (leaderGroup) + { + leaderGroup->Disband(true); + LOG_DEBUG("playerbots", "ArenaGroupFormationOperation: Disbanded leader's existing group"); + } + + // Step 3: Create new group with leader + Group* newGroup = new Group(); + if (!newGroup->Create(leader)) + { + delete newGroup; + LOG_ERROR("playerbots", "ArenaGroupFormationOperation: Failed to create arena group for leader {}", + leader->GetName()); + return false; + } + + sGroupMgr->AddGroup(newGroup); + LOG_DEBUG("playerbots", "ArenaGroupFormationOperation: Created new arena group with leader {}", + leader->GetName()); + + // Step 4: Add members to the new group + uint32 addedMembers = 0; + for (const ObjectGuid& memberGuid : m_memberGuids) + { + Player* member = ObjectAccessor::FindPlayer(memberGuid); + if (!member) + { + LOG_DEBUG("playerbots", "ArenaGroupFormationOperation: Member {} not found, skipping", + memberGuid.ToString()); + continue; + } + + if (member->GetLevel() < 70) + { + LOG_DEBUG("playerbots", "ArenaGroupFormationOperation: Member {} is below level 70, skipping", + member->GetName()); + continue; + } + + if (newGroup->AddMember(member)) + { + addedMembers++; + LOG_DEBUG("playerbots", "ArenaGroupFormationOperation: Added {} to arena group", + member->GetName()); + } + else + LOG_ERROR("playerbots", "ArenaGroupFormationOperation: Failed to add {} to arena group", + member->GetName()); + } + + if (addedMembers == 0) + { + LOG_ERROR("playerbots", "ArenaGroupFormationOperation: No members were added to the arena group"); + newGroup->Disband(); + return false; + } + + // Step 5: Teleport members to leader and reset AI + for (const ObjectGuid& memberGuid : m_memberGuids) + { + Player* member = ObjectAccessor::FindPlayer(memberGuid); + if (!member || !newGroup->IsMember(memberGuid)) + continue; + + PlayerbotAI* memberBotAI = sPlayerbotsMgr->GetPlayerbotAI(member); + if (memberBotAI) + memberBotAI->Reset(); + + member->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP); + member->TeleportTo(leader->GetMapId(), leader->GetPositionX(), leader->GetPositionY(), + leader->GetPositionZ(), 0); + + LOG_DEBUG("playerbots", "ArenaGroupFormationOperation: Teleported {} to leader", member->GetName()); + } + + // Check if we have enough members + if (newGroup->GetMembersCount() < m_requiredSize) + { + LOG_INFO("playerbots", "Team #{} <{}> Group is not ready for match (not enough members: {}/{})", + m_arenaTeamId, m_arenaTeamName, newGroup->GetMembersCount(), m_requiredSize); + newGroup->Disband(); + return false; + } + + LOG_INFO("playerbots", "Team #{} <{}> Group is ready for match with {} members", + m_arenaTeamId, m_arenaTeamName, newGroup->GetMembersCount()); + return true; + } + + ObjectGuid GetBotGuid() const override { return m_leaderGuid; } + + uint32 GetPriority() const override { return 60; } // Very high priority (arena/BG operations) + + std::string GetName() const override { return "ArenaGroupFormation"; } + + bool IsValid() const override + { + Player* leader = ObjectAccessor::FindPlayer(m_leaderGuid); + return leader != nullptr; + } + +private: + ObjectGuid m_leaderGuid; + std::vector m_memberGuids; + uint32 m_requiredSize; + uint32 m_arenaTeamId; + std::string m_arenaTeamName; +}; + +// Bot logout group cleanup operation +class BotLogoutGroupCleanupOperation : public PlayerbotOperation +{ +public: + BotLogoutGroupCleanupOperation(ObjectGuid botGuid) : m_botGuid(botGuid) {} + + bool Execute() override + { + Player* bot = ObjectAccessor::FindPlayer(m_botGuid); + if (!bot) + return false; + + PlayerbotAI* botAI = sPlayerbotsMgr->GetPlayerbotAI(bot); + if (!botAI) + return false; + + Group* group = bot->GetGroup(); + if (group && !bot->InBattleground() && !bot->InBattlegroundQueue() && botAI->HasActivePlayerMaster()) + sPlayerbotDbStore->Save(botAI); + + return true; + } + + ObjectGuid GetBotGuid() const override { return m_botGuid; } + uint32 GetPriority() const override { return 70; } + std::string GetName() const override { return "BotLogoutGroupCleanup"; } + + bool IsValid() const override + { + Player* bot = ObjectAccessor::FindPlayer(m_botGuid); + return bot != nullptr; + } + +private: + ObjectGuid m_botGuid; +}; + +// Add player bot operation (for logging in bots from map threads) +class AddPlayerBotOperation : public PlayerbotOperation +{ +public: + AddPlayerBotOperation(ObjectGuid botGuid, uint32 masterAccountId) + : m_botGuid(botGuid), m_masterAccountId(masterAccountId) + { + } + + bool Execute() override + { + sRandomPlayerbotMgr->AddPlayerBot(m_botGuid, m_masterAccountId); + return true; + } + + ObjectGuid GetBotGuid() const override { return m_botGuid; } + + uint32 GetPriority() const override { return 50; } // High priority + + std::string GetName() const override { return "AddPlayerBot"; } + + bool IsValid() const override + { + return !ObjectAccessor::FindConnectedPlayer(m_botGuid); + } + +private: + ObjectGuid m_botGuid; + uint32 m_masterAccountId; +}; + +class OnBotLoginOperation : public PlayerbotOperation +{ +public: + OnBotLoginOperation(ObjectGuid botGuid, PlayerbotHolder* holder) + : m_botGuid(botGuid), m_holder(holder) + { + } + + bool Execute() override + { + Player* bot = ObjectAccessor::FindConnectedPlayer(m_botGuid); + if (!bot || !m_holder) + return false; + + m_holder->OnBotLogin(bot); + return true; + } + + ObjectGuid GetBotGuid() const override { return m_botGuid; } + uint32 GetPriority() const override { return 100; } + std::string GetName() const override { return "OnBotLogin"; } + + bool IsValid() const override + { + return ObjectAccessor::FindConnectedPlayer(m_botGuid) != nullptr; + } + +private: + ObjectGuid m_botGuid; + PlayerbotHolder* m_holder; +}; + +#endif diff --git a/src/PlayerbotWorldThreadProcessor.cpp b/src/PlayerbotWorldThreadProcessor.cpp new file mode 100644 index 0000000000..c776eb1207 --- /dev/null +++ b/src/PlayerbotWorldThreadProcessor.cpp @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include "PlayerbotWorldThreadProcessor.h" + +#include "Log.h" +#include "PlayerbotAIConfig.h" + +#include + +PlayerbotWorldThreadProcessor::PlayerbotWorldThreadProcessor() + : m_enabled(true), m_maxQueueSize(10000), m_batchSize(100), m_queueWarningThreshold(80), + m_timeSinceLastUpdate(0), m_updateInterval(50) // Process at least every 50ms +{ + LOG_INFO("playerbots", "PlayerbotWorldThreadProcessor initialized"); +} + +PlayerbotWorldThreadProcessor::~PlayerbotWorldThreadProcessor() { ClearQueue(); } + +PlayerbotWorldThreadProcessor* PlayerbotWorldThreadProcessor::instance() +{ + static PlayerbotWorldThreadProcessor instance; + return &instance; +} + +void PlayerbotWorldThreadProcessor::Update(uint32 diff) +{ + if (!m_enabled) + return; + + // Accumulate time + m_timeSinceLastUpdate += diff; + + // Don't process too frequently to reduce overhead + if (m_timeSinceLastUpdate < m_updateInterval) + return; + + m_timeSinceLastUpdate = 0; + + // Check queue health (warn if getting full) + CheckQueueHealth(); + + // Process a batch of operations + ProcessBatch(); +} + +bool PlayerbotWorldThreadProcessor::QueueOperation(std::unique_ptr operation) +{ + if (!operation) + { + LOG_ERROR("playerbots", "Attempted to queue null operation"); + return false; + } + + std::lock_guard lock(m_queueMutex); + + // Check if queue is full + if (m_operationQueue.size() >= m_maxQueueSize) + { + LOG_ERROR("playerbots", + "PlayerbotWorldThreadProcessor queue is full ({} operations). Dropping operation: {}", + m_maxQueueSize, operation->GetName()); + + std::lock_guard statsLock(m_statsMutex); + m_stats.totalOperationsSkipped++; + return false; + } + + // Queue the operation + m_operationQueue.push(std::move(operation)); + + // Update statistics + { + std::lock_guard statsLock(m_statsMutex); + m_stats.currentQueueSize = static_cast(m_operationQueue.size()); + m_stats.maxQueueSize = std::max(m_stats.maxQueueSize, m_stats.currentQueueSize); + } + + return true; +} + +void PlayerbotWorldThreadProcessor::ProcessBatch() +{ + // Extract a batch of operations from the queue + std::vector> batch; + batch.reserve(m_batchSize); + + { + std::lock_guard lock(m_queueMutex); + + // Extract up to batchSize operations + while (!m_operationQueue.empty() && batch.size() < m_batchSize) + { + batch.push_back(std::move(m_operationQueue.front())); + m_operationQueue.pop(); + } + + // Update current queue size stat + std::lock_guard statsLock(m_statsMutex); + m_stats.currentQueueSize = static_cast(m_operationQueue.size()); + } + + // Execute operations outside of lock to avoid blocking queue + uint32 totalExecutionTime = 0; + for (auto& operation : batch) + { + if (!operation) + continue; + + try + { + // Check if operation is still valid + if (!operation->IsValid()) + { + LOG_DEBUG("playerbots", "Skipping invalid operation: {}", operation->GetName()); + + std::lock_guard statsLock(m_statsMutex); + m_stats.totalOperationsSkipped++; + continue; + } + + // Time the execution + uint32 startTime = getMSTime(); + + // Execute the operation + bool success = operation->Execute(); + + uint32 executionTime = GetMSTimeDiffToNow(startTime); + totalExecutionTime += executionTime; + + // Log slow operations + if (executionTime > 100) + LOG_WARN("playerbots", "Slow operation: {} took {}ms", operation->GetName(), executionTime); + + // Update statistics + std::lock_guard statsLock(m_statsMutex); + if (success) + m_stats.totalOperationsProcessed++; + else + { + m_stats.totalOperationsFailed++; + LOG_DEBUG("playerbots", "Operation failed: {}", operation->GetName()); + } + } + catch (std::exception const& e) + { + LOG_ERROR("playerbots", "Exception in operation {}: {}", operation->GetName(), e.what()); + + std::lock_guard statsLock(m_statsMutex); + m_stats.totalOperationsFailed++; + } + catch (...) + { + LOG_ERROR("playerbots", "Unknown exception in operation {}", operation->GetName()); + + std::lock_guard statsLock(m_statsMutex); + m_stats.totalOperationsFailed++; + } + } + + // Update average execution time + if (!batch.empty()) + { + std::lock_guard statsLock(m_statsMutex); + uint32 avgTime = totalExecutionTime / static_cast(batch.size()); + // Exponential moving average + m_stats.averageExecutionTimeMs = + (m_stats.averageExecutionTimeMs * 9 + avgTime) / 10; // 90% old, 10% new + } +} + +void PlayerbotWorldThreadProcessor::CheckQueueHealth() +{ + uint32 queueSize = GetQueueSize(); + uint32 threshold = (m_maxQueueSize * m_queueWarningThreshold) / 100; + + if (queueSize >= threshold) + { + LOG_WARN("playerbots", + "PlayerbotWorldThreadProcessor queue is {}% full ({}/{}). " + "Consider increasing update frequency or batch size.", + (queueSize * 100) / m_maxQueueSize, queueSize, m_maxQueueSize); + } +} + +uint32 PlayerbotWorldThreadProcessor::GetQueueSize() const +{ + std::lock_guard lock(m_queueMutex); + return static_cast(m_operationQueue.size()); +} + +void PlayerbotWorldThreadProcessor::ClearQueue() +{ + std::lock_guard lock(m_queueMutex); + + uint32 cleared = static_cast(m_operationQueue.size()); + if (cleared > 0) + LOG_INFO("playerbots", "Clearing {} queued operations", cleared); + + // Clear the queue + while (!m_operationQueue.empty()) + { + m_operationQueue.pop(); + } + + // Reset queue size stat + std::lock_guard statsLock(m_statsMutex); + m_stats.currentQueueSize = 0; +} + +PlayerbotWorldThreadProcessor::Statistics PlayerbotWorldThreadProcessor::GetStatistics() const +{ + std::lock_guard statsLock(m_statsMutex); + return m_stats; // Return a copy +} diff --git a/src/PlayerbotWorldThreadProcessor.h b/src/PlayerbotWorldThreadProcessor.h new file mode 100644 index 0000000000..e37d2b5ba5 --- /dev/null +++ b/src/PlayerbotWorldThreadProcessor.h @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_WORLD_THREAD_PROCESSOR_H +#define _PLAYERBOT_WORLD_THREAD_PROCESSOR_H + +#include "Common.h" +#include "PlayerbotOperation.h" + +#include +#include +#include + +/** + * @brief Processes thread-unsafe bot operations in the world thread + * + * The PlayerbotWorldThreadProcessor manages a queue of operations that must be executed + * in the world thread rather than map threads. This ensures thread safety for operations + * like group modifications, LFG, guilds, battlegrounds, etc. + * + * Architecture: + * - Map threads queue operations via QueueOperation() + * - World thread processes operations via Update() (called from WorldScript::OnUpdate) + * - Operations are processed in priority order + * - Thread-safe queue protected by mutex + * + * Usage: + * auto op = std::make_unique(botGuid, params); + * sPlayerbotWorldProcessor->QueueOperation(std::move(op)); + */ +class PlayerbotWorldThreadProcessor +{ +public: + PlayerbotWorldThreadProcessor(); + ~PlayerbotWorldThreadProcessor(); + + static PlayerbotWorldThreadProcessor* instance(); + + /** + * @brief Update and process queued operations (called from world thread) + * + * This method should be called from WorldScript::OnUpdate hook, which runs in the world thread. + * It processes a batch of queued operations. + * + * @param diff Time since last update in milliseconds + */ + void Update(uint32 diff); + + /** + * @brief Queue an operation for execution in the world thread + * + * Thread-safe method that can be called from any thread (typically map threads). + * The operation will be executed later during Update(). + * + * @param operation Unique pointer to the operation (ownership is transferred) + * @return true if operation was queued, false if queue is full + */ + bool QueueOperation(std::unique_ptr operation); + + /** + * @brief Get current queue size + * + * Thread-safe method for monitoring queue size. + * + * @return Number of operations waiting to be processed + */ + uint32 GetQueueSize() const; + + /** + * @brief Clear all queued operations + * + * Used during shutdown or emergency situations. + */ + void ClearQueue(); + + /** + * @brief Get statistics about operation processing + */ + struct Statistics + { + uint64 totalOperationsProcessed = 0; + uint64 totalOperationsFailed = 0; + uint64 totalOperationsSkipped = 0; + uint32 currentQueueSize = 0; + uint32 maxQueueSize = 0; + uint32 averageExecutionTimeMs = 0; + }; + + Statistics GetStatistics() const; + + /** + * @brief Enable/disable operation processing + * + * When disabled, operations are still queued but not processed. + * Useful for testing or temporary suspension. + * + * @param enabled true to enable processing, false to disable + */ + void SetEnabled(bool enabled) { m_enabled = enabled; } + + bool IsEnabled() const { return m_enabled; } + +private: + /** + * @brief Process a single batch of operations + * + * Extracts operations from queue and executes them. + * Called internally by Update(). + */ + void ProcessBatch(); + + /** + * @brief Check if queue is approaching capacity + * + * Logs warning if queue is getting full. + */ + void CheckQueueHealth(); + + // Thread-safe queue + mutable std::mutex m_queueMutex; + std::queue> m_operationQueue; + + // Configuration + bool m_enabled; + uint32 m_maxQueueSize; // Maximum operations in queue + uint32 m_batchSize; // Operations to process per Update() + uint32 m_queueWarningThreshold; // Warn when queue reaches this percentage + + // Statistics + mutable std::mutex m_statsMutex; + Statistics m_stats; + + // Timing + uint32 m_timeSinceLastUpdate; + uint32 m_updateInterval; // Minimum ms between updates +}; + +#define sPlayerbotWorldProcessor PlayerbotWorldThreadProcessor::instance() + +#endif diff --git a/src/Playerbots.cpp b/src/Playerbots.cpp index a7217d7bc4..136d4e6166 100644 --- a/src/Playerbots.cpp +++ b/src/Playerbots.cpp @@ -25,6 +25,7 @@ #include "Metric.h" #include "PlayerScript.h" #include "PlayerbotAIConfig.h" +#include "PlayerbotWorldThreadProcessor.h" #include "RandomPlayerbotMgr.h" #include "ScriptMgr.h" #include "cs_playerbots.h" @@ -300,7 +301,8 @@ class PlayerbotsWorldScript : public WorldScript { public: PlayerbotsWorldScript() : WorldScript("PlayerbotsWorldScript", { - WORLDHOOK_ON_BEFORE_WORLD_INITIALIZED + WORLDHOOK_ON_BEFORE_WORLD_INITIALIZED, + WORLDHOOK_ON_UPDATE }) {} void OnBeforeWorldInitialized() override @@ -329,6 +331,13 @@ class PlayerbotsWorldScript : public WorldScript LOG_INFO("server.loading", ">> Loaded playerbots config in {} ms", GetMSTimeDiffToNow(oldMSTime)); LOG_INFO("server.loading", " "); + LOG_INFO("server.loading", "Playerbots World Thread Processor initialized"); + } + + void OnUpdate(uint32 diff) override + { + sPlayerbotWorldProcessor->Update(diff); + sRandomPlayerbotMgr->UpdateAI(diff); // World thread only } }; @@ -390,8 +399,7 @@ class PlayerbotsScript : public PlayerbotScript void OnPlayerbotUpdate(uint32 diff) override { - sRandomPlayerbotMgr->UpdateAI(diff); - sRandomPlayerbotMgr->UpdateSessions(); + sRandomPlayerbotMgr->UpdateSessions(); // Per-bot updates only } void OnPlayerbotUpdateSessions(Player* player) override diff --git a/src/strategy/actions/GuildManagementActions.cpp b/src/strategy/actions/GuildManagementActions.cpp index 4ab6d72c59..f00a955e7c 100644 --- a/src/strategy/actions/GuildManagementActions.cpp +++ b/src/strategy/actions/GuildManagementActions.cpp @@ -58,6 +58,14 @@ Player* GuidManageAction::GetPlayer(Event event) return nullptr; } +void GuidManageAction::SendPacket(WorldPacket const& packet) +{ + // make a heap copy because QueuePacket takes ownership + WorldPacket* data = new WorldPacket(packet); + + bot->GetSession()->QueuePacket(data); +} + bool GuidManageAction::Execute(Event event) { Player* player = GetPlayer(event); @@ -84,12 +92,6 @@ bool GuildInviteAction::isUseful() return bot->GetGuildId() && sGuildMgr->GetGuildById(bot->GetGuildId())->HasRankRight(bot, GR_RIGHT_INVITE); } -void GuildInviteAction::SendPacket(WorldPacket packet) -{ - WorldPackets::Guild::GuildInviteByName data = WorldPacket(packet); - bot->GetSession()->HandleGuildInviteOpcode(data); -} - bool GuildInviteAction::PlayerIsValid(Player* member) { return !member->GetGuildId() && (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) || @@ -101,12 +103,6 @@ bool GuildPromoteAction::isUseful() return bot->GetGuildId() && sGuildMgr->GetGuildById(bot->GetGuildId())->HasRankRight(bot, GR_RIGHT_PROMOTE); } -void GuildPromoteAction::SendPacket(WorldPacket packet) -{ - WorldPackets::Guild::GuildPromoteMember data = WorldPacket(packet); - bot->GetSession()->HandleGuildPromoteOpcode(data); -} - bool GuildPromoteAction::PlayerIsValid(Player* member) { return member->GetGuildId() == bot->GetGuildId() && GetRankId(bot) < GetRankId(member) - 1; @@ -117,12 +113,6 @@ bool GuildDemoteAction::isUseful() return bot->GetGuildId() && sGuildMgr->GetGuildById(bot->GetGuildId())->HasRankRight(bot, GR_RIGHT_DEMOTE); } -void GuildDemoteAction::SendPacket(WorldPacket packet) -{ - WorldPackets::Guild::GuildDemoteMember data = WorldPacket(packet); - bot->GetSession()->HandleGuildDemoteOpcode(data); -} - bool GuildDemoteAction::PlayerIsValid(Player* member) { return member->GetGuildId() == bot->GetGuildId() && GetRankId(bot) < GetRankId(member); @@ -133,12 +123,6 @@ bool GuildRemoveAction::isUseful() return bot->GetGuildId() && sGuildMgr->GetGuildById(bot->GetGuildId())->HasRankRight(bot, GR_RIGHT_REMOVE); } -void GuildRemoveAction::SendPacket(WorldPacket packet) -{ - WorldPackets::Guild::GuildOfficerRemoveMember data = WorldPacket(packet); - bot->GetSession()->HandleGuildRemoveOpcode(data); -} - bool GuildRemoveAction::PlayerIsValid(Player* member) { return member->GetGuildId() == bot->GetGuildId() && GetRankId(bot) < GetRankId(member); diff --git a/src/strategy/actions/GuildManagementActions.h b/src/strategy/actions/GuildManagementActions.h index bf46d1b741..b1d363e89a 100644 --- a/src/strategy/actions/GuildManagementActions.h +++ b/src/strategy/actions/GuildManagementActions.h @@ -25,7 +25,7 @@ class GuidManageAction : public Action bool isUseful() override { return false; } protected: - virtual void SendPacket(WorldPacket data){}; + virtual void SendPacket(WorldPacket const& packet); virtual Player* GetPlayer(Event event); virtual bool PlayerIsValid(Player* member); virtual uint8 GetRankId(Player* member); @@ -44,7 +44,6 @@ class GuildInviteAction : public GuidManageAction bool isUseful() override; protected: - void SendPacket(WorldPacket data) override; bool PlayerIsValid(Player* member) override; }; @@ -59,7 +58,6 @@ class GuildPromoteAction : public GuidManageAction bool isUseful() override; protected: - void SendPacket(WorldPacket data) override; bool PlayerIsValid(Player* member) override; }; @@ -74,7 +72,6 @@ class GuildDemoteAction : public GuidManageAction bool isUseful() override; protected: - void SendPacket(WorldPacket data) override; bool PlayerIsValid(Player* member) override; }; @@ -89,7 +86,6 @@ class GuildRemoveAction : public GuidManageAction bool isUseful() override; protected: - void SendPacket(WorldPacket data) override; bool PlayerIsValid(Player* member) override; }; diff --git a/src/strategy/actions/InviteToGroupAction.cpp b/src/strategy/actions/InviteToGroupAction.cpp index 7af26210c6..4d0b4df7b9 100644 --- a/src/strategy/actions/InviteToGroupAction.cpp +++ b/src/strategy/actions/InviteToGroupAction.cpp @@ -9,7 +9,9 @@ #include "Event.h" #include "GuildMgr.h" #include "Log.h" +#include "PlayerbotOperations.h" #include "Playerbots.h" +#include "PlayerbotWorldThreadProcessor.h" #include "ServerFacade.h" bool InviteToGroupAction::Invite(Player* inviter, Player* player) @@ -27,7 +29,10 @@ bool InviteToGroupAction::Invite(Player* inviter, Player* player) { if (GET_PLAYERBOT_AI(player) && !GET_PLAYERBOT_AI(player)->IsRealPlayer()) if (!group->isRaidGroup() && group->GetMembersCount() > 4) - group->ConvertToRaid(); + { + auto convertOp = std::make_unique(inviter->GetGUID()); + sPlayerbotWorldProcessor->QueueOperation(std::move(convertOp)); + } } WorldPacket p; @@ -89,7 +94,10 @@ bool InviteNearbyToGroupAction::Execute(Event event) // When inviting the 5th member of the group convert to raid for future invites. if (group && botAI->GetGrouperType() > GrouperType::LEADER_5 && !group->isRaidGroup() && bot->GetGroup()->GetMembersCount() > 3) - group->ConvertToRaid(); + { + auto convertOp = std::make_unique(bot->GetGUID()); + sPlayerbotWorldProcessor->QueueOperation(std::move(convertOp)); + } if (sPlayerbotAIConfig->inviteChat && sRandomPlayerbotMgr->IsRandomBot(bot)) { @@ -221,7 +229,8 @@ bool InviteGuildToGroupAction::Execute(Event event) if (group && botAI->GetGrouperType() > GrouperType::LEADER_5 && !group->isRaidGroup() && bot->GetGroup()->GetMembersCount() > 3) { - group->ConvertToRaid(); + auto convertOp = std::make_unique(bot->GetGUID()); + sPlayerbotWorldProcessor->QueueOperation(std::move(convertOp)); } if (sPlayerbotAIConfig->inviteChat && @@ -362,7 +371,10 @@ bool LfgAction::Execute(Event event) if (param.empty() || param == "5" || group->isRaidGroup()) return false; // Group or raid is full so stop trying. else - group->ConvertToRaid(); // We want a raid but are in a group so convert and continue. + { + auto convertOp = std::make_unique(requester->GetGUID()); + sPlayerbotWorldProcessor->QueueOperation(std::move(convertOp)); + } } Group::MemberSlotList const& groupSlot = group->GetMemberSlots(); diff --git a/src/strategy/actions/PassLeadershipToMasterAction.cpp b/src/strategy/actions/PassLeadershipToMasterAction.cpp index ceb1fbbcf2..87890c1c57 100644 --- a/src/strategy/actions/PassLeadershipToMasterAction.cpp +++ b/src/strategy/actions/PassLeadershipToMasterAction.cpp @@ -6,16 +6,17 @@ #include "PassLeadershipToMasterAction.h" #include "Event.h" +#include "PlayerbotOperations.h" #include "Playerbots.h" +#include "PlayerbotWorldThreadProcessor.h" bool PassLeadershipToMasterAction::Execute(Event event) { if (Player* master = GetMaster()) if (master && master != bot && bot->GetGroup() && bot->GetGroup()->IsMember(master->GetGUID())) { - WorldPacket p(SMSG_GROUP_SET_LEADER, 8); - p << master->GetGUID(); - bot->GetSession()->HandleGroupSetLeaderOpcode(p); + auto setLeaderOp = std::make_unique(bot->GetGUID(), master->GetGUID()); + sPlayerbotWorldProcessor->QueueOperation(std::move(setLeaderOp)); if (!message.empty()) botAI->TellMasterNoFacing(message); From cf743a186a41ce37947e9ef0f4b4dd838c4c0144 Mon Sep 17 00:00:00 2001 From: Crow Date: Sun, 23 Nov 2025 03:06:19 -0600 Subject: [PATCH 38/47] Fix Wrong Misdirection Spell ID for Gruul's Lair and Magtheridon Strategies (#1867) Lol oops. Confirmed with logs/in-game that the prior one was wrong (and thus always returning false) and current one is correct. --- src/strategy/raids/gruulslair/RaidGruulsLairHelpers.h | 2 +- src/strategy/raids/magtheridon/RaidMagtheridonHelpers.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/strategy/raids/gruulslair/RaidGruulsLairHelpers.h b/src/strategy/raids/gruulslair/RaidGruulsLairHelpers.h index 8cd01c2e6b..c7becc8362 100644 --- a/src/strategy/raids/gruulslair/RaidGruulsLairHelpers.h +++ b/src/strategy/raids/gruulslair/RaidGruulsLairHelpers.h @@ -15,7 +15,7 @@ namespace GruulsLairHelpers SPELL_SPELL_SHIELD = 33054, // Hunter - SPELL_MISDIRECTION = 34477, + SPELL_MISDIRECTION = 35079, // Warlock SPELL_BANISH = 18647, // Rank 2 diff --git a/src/strategy/raids/magtheridon/RaidMagtheridonHelpers.h b/src/strategy/raids/magtheridon/RaidMagtheridonHelpers.h index 21fd9d459c..80c3a47e09 100644 --- a/src/strategy/raids/magtheridon/RaidMagtheridonHelpers.h +++ b/src/strategy/raids/magtheridon/RaidMagtheridonHelpers.h @@ -24,7 +24,7 @@ namespace MagtheridonHelpers SPELL_FEAR = 6215, // Hunter - SPELL_MISDIRECTION = 34477, + SPELL_MISDIRECTION = 35079, }; enum MagtheridonNPCs From 2424f73bc487a7520a16ffb2dd67316209174485 Mon Sep 17 00:00:00 2001 From: Keleborn <22352763+Celandriel@users.noreply.github.com> Date: Sun, 23 Nov 2025 11:45:31 -0800 Subject: [PATCH 39/47] Core Merge PR - Replace OnPlayerChat with OnPlayerCanUseChat (#1838) First stab at getting this working. Im not sure if Im missing something, but it seemed to be a pretty simple change overall. Based on testing the bots do respond to commands via whisper and group. Edit: Relevant PR this addresses. https://github.com/azerothcore/azerothcore-wotlk/commit/50f8f145d224037be9a29c4390c5088816639868#diff-baadebd8cd1117ca48225f316a5ab3fd5fd55b20963394d302341147183db067 --- src/Playerbots.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Playerbots.cpp b/src/Playerbots.cpp index 136d4e6166..ecaaa5839a 100644 --- a/src/Playerbots.cpp +++ b/src/Playerbots.cpp @@ -82,12 +82,12 @@ class PlayerbotsPlayerScript : public PlayerScript PlayerbotsPlayerScript() : PlayerScript("PlayerbotsPlayerScript", { PLAYERHOOK_ON_LOGIN, PLAYERHOOK_ON_AFTER_UPDATE, - PLAYERHOOK_ON_CHAT, - PLAYERHOOK_ON_CHAT_WITH_CHANNEL, - PLAYERHOOK_ON_CHAT_WITH_GROUP, PLAYERHOOK_ON_BEFORE_CRITERIA_PROGRESS, PLAYERHOOK_ON_BEFORE_ACHI_COMPLETE, PLAYERHOOK_CAN_PLAYER_USE_PRIVATE_CHAT, + PLAYERHOOK_CAN_PLAYER_USE_GROUP_CHAT, + PLAYERHOOK_CAN_PLAYER_USE_GUILD_CHAT, + PLAYERHOOK_CAN_PLAYER_USE_CHANNEL_CHAT, PLAYERHOOK_ON_GIVE_EXP, PLAYERHOOK_ON_BEFORE_TELEPORT }) {} @@ -163,15 +163,12 @@ class PlayerbotsPlayerScript : public PlayerScript if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(receiver)) { botAI->HandleCommand(type, msg, player); - - return false; } } - return true; } - void OnPlayerChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Group* group) override + bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Group* group) override { for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) { @@ -183,9 +180,10 @@ class PlayerbotsPlayerScript : public PlayerScript } } } + return true; } - void OnPlayerChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg) override + bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Guild* guild) override { if (type == CHAT_MSG_GUILD) { @@ -204,9 +202,10 @@ class PlayerbotsPlayerScript : public PlayerScript } } } + return true; } - void OnPlayerChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Channel* channel) override + bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Channel* channel) override { if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player)) { @@ -217,6 +216,7 @@ class PlayerbotsPlayerScript : public PlayerScript } sRandomPlayerbotMgr->HandleCommand(type, msg, player); + return true; } bool OnPlayerBeforeAchievementComplete(Player* player, AchievementEntry const* achievement) override From d5dbc4ddd70c021175451f55befbcb1411361b38 Mon Sep 17 00:00:00 2001 From: Keleborn <22352763+Celandriel@users.noreply.github.com> Date: Mon, 24 Nov 2025 12:49:55 -0800 Subject: [PATCH 40/47] Hotfix: prevent server crash when whisper 'logout' (#1874) Temp Hotfix to resolve #1870. --- src/Playerbots.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Playerbots.cpp b/src/Playerbots.cpp index ecaaa5839a..ed4e482636 100644 --- a/src/Playerbots.cpp +++ b/src/Playerbots.cpp @@ -163,6 +163,12 @@ class PlayerbotsPlayerScript : public PlayerScript if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(receiver)) { botAI->HandleCommand(type, msg, player); + + // hotfix; otherwise the server will crash when whispering logout + // https://github.com/mod-playerbots/mod-playerbots/pull/1838 + // TODO: find the root cause and solve it. (does not happen in party chat) + if (msg == "logout") + return false; } } return true; From 38e2d8584b8772894439e07314facd98345ff823 Mon Sep 17 00:00:00 2001 From: Crow Date: Fri, 28 Nov 2025 12:00:12 -0600 Subject: [PATCH 41/47] Add missing break in ApplyInstanceStrategies (#1887) Well, I hope nobody has tried Magtheridon lately. It looks like this got inadvertently deleted during the merge. --- src/PlayerbotAI.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 23d073f54c..3748b84e02 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -1477,6 +1477,7 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster) break; case 544: strategyName = "magtheridon"; // Magtheridon's Lair + break; case 565: strategyName = "gruulslair"; // Gruul's Lair break; From 52c3e966416ffe881acd5ff24a375debbbeddde9 Mon Sep 17 00:00:00 2001 From: Keleborn <22352763+Celandriel@users.noreply.github.com> Date: Wed, 3 Dec 2025 04:25:01 -0800 Subject: [PATCH 42/47] Rename groupmaster to groupleader and related variables. (#1875) Fix the naming conventions. Master should be reserved to identify a bots Master. groupleaders are not necessarily group masters and it should be clear what the bot is looking for. (In most solo cases leader=master) --- src/PlayerbotAI.cpp | 6 +-- src/PlayerbotAI.h | 2 +- src/PlayerbotSecurity.cpp | 4 +- src/RandomPlayerbotMgr.cpp | 6 +-- src/strategy/actions/ActionContext.h | 4 +- .../actions/ChooseRpgTargetAction.cpp | 16 +++---- .../actions/ChooseTravelTargetAction.cpp | 2 +- src/strategy/actions/FollowActions.cpp | 16 +++---- src/strategy/actions/FollowActions.h | 4 +- src/strategy/actions/InviteToGroupAction.cpp | 2 +- src/strategy/actions/LeaveGroupAction.cpp | 22 +++++----- .../actions/MoveToTravelTargetAction.cpp | 2 +- src/strategy/actions/MovementActions.cpp | 4 +- .../actions/RandomBotUpdateAction.cpp | 6 +-- src/strategy/actions/ReleaseSpiritAction.cpp | 8 ++-- src/strategy/actions/ResetInstancesAction.cpp | 2 +- .../actions/ReviveFromCorpseAction.cpp | 42 +++++++++---------- src/strategy/actions/RewardAction.cpp | 4 +- src/strategy/actions/RpgSubActions.cpp | 2 +- src/strategy/actions/SecurityCheckAction.cpp | 4 +- src/strategy/actions/TellMasterAction.cpp | 2 +- src/strategy/actions/TravelAction.cpp | 2 +- src/strategy/rogue/RogueTriggers.cpp | 4 +- src/strategy/triggers/RangeTriggers.cpp | 2 +- src/strategy/values/Formations.cpp | 2 +- ...erTargetValue.cpp => GroupLeaderValue.cpp} | 4 +- ...MasterTargetValue.h => GroupLeaderValue.h} | 8 ++-- src/strategy/values/GroupValues.cpp | 14 +++---- src/strategy/values/ValueContext.h | 6 +-- 29 files changed, 101 insertions(+), 101 deletions(-) rename src/strategy/values/{MasterTargetValue.cpp => GroupLeaderValue.cpp} (70%) rename src/strategy/values/{MasterTargetValue.h => GroupLeaderValue.h} (57%) diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 3748b84e02..7419574bd2 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -420,7 +420,7 @@ void PlayerbotAI::UpdateAIGroupAndMaster() { botAI->ChangeStrategy("+follow", BOT_STATE_NON_COMBAT); - if (botAI->GetMaster() == botAI->GetGroupMaster()) + if (botAI->GetMaster() == botAI->GetGroupLeader()) botAI->TellMaster("Hello, I follow you!"); else botAI->TellMaster(!urand(0, 2) ? "Hello!" : "Hi!"); @@ -4093,7 +4093,7 @@ Player* PlayerbotAI::FindNewMaster() if (!group) return nullptr; - Player* groupLeader = GetGroupMaster(); + Player* groupLeader = GetGroupLeader(); PlayerbotAI* leaderBotAI = GET_PLAYERBOT_AI(groupLeader); if (!leaderBotAI || leaderBotAI->IsRealPlayer()) return groupLeader; @@ -4144,7 +4144,7 @@ bool PlayerbotAI::HasActivePlayerMaster() { return master && !GET_PLAYERBOT_AI(m bool PlayerbotAI::IsAlt() { return HasRealPlayerMaster() && !sRandomPlayerbotMgr->IsRandomBot(bot); } -Player* PlayerbotAI::GetGroupMaster() +Player* PlayerbotAI::GetGroupLeader() { if (!bot->InBattleground()) if (Group* group = bot->GetGroup()) diff --git a/src/PlayerbotAI.h b/src/PlayerbotAI.h index 8c8cff14f2..ad7641b278 100644 --- a/src/PlayerbotAI.h +++ b/src/PlayerbotAI.h @@ -540,7 +540,7 @@ class PlayerbotAI : public PlayerbotAIBase // Get the group leader or the master of the bot. // Checks if the bot is summoned as alt of a player bool IsAlt(); - Player* GetGroupMaster(); + Player* GetGroupLeader(); // Returns a semi-random (cycling) number that is fixed for each bot. uint32 GetFixedBotNumer(uint32 maxNum = 100, float cyclePerMin = 1); GrouperType GetGrouperType(); diff --git a/src/PlayerbotSecurity.cpp b/src/PlayerbotSecurity.cpp index 45e8598989..822f40e6d4 100644 --- a/src/PlayerbotSecurity.cpp +++ b/src/PlayerbotSecurity.cpp @@ -251,9 +251,9 @@ bool PlayerbotSecurity::CheckLevelFor(PlayerbotSecurityLevel level, bool silent, out << "I am currently leading a group. I can invite you if you want."; break; case PLAYERBOT_DENY_NOT_LEADER: - if (botAI->GetGroupMaster()) + if (botAI->GetGroupLeader()) { - out << "I am in a group with " << botAI->GetGroupMaster()->GetName() + out << "I am in a group with " << botAI->GetGroupLeader()->GetName() << ". You can ask him for invite."; } else diff --git a/src/RandomPlayerbotMgr.cpp b/src/RandomPlayerbotMgr.cpp index 89a960aecb..1f94e35e13 100644 --- a/src/RandomPlayerbotMgr.cpp +++ b/src/RandomPlayerbotMgr.cpp @@ -1480,10 +1480,10 @@ bool RandomPlayerbotMgr::ProcessBot(uint32 bot) if (!sRandomPlayerbotMgr->IsRandomBot(player)) update = false; - if (player->GetGroup() && botAI->GetGroupMaster()) + if (player->GetGroup() && botAI->GetGroupLeader()) { - PlayerbotAI* groupMasterBotAI = GET_PLAYERBOT_AI(botAI->GetGroupMaster()); - if (!groupMasterBotAI || groupMasterBotAI->IsRealPlayer()) + PlayerbotAI* groupLeaderBotAI = GET_PLAYERBOT_AI(botAI->GetGroupLeader()); + if (!groupLeaderBotAI || groupLeaderBotAI->IsRealPlayer()) { update = false; } diff --git a/src/strategy/actions/ActionContext.h b/src/strategy/actions/ActionContext.h index cebd7615a7..09c9145f93 100644 --- a/src/strategy/actions/ActionContext.h +++ b/src/strategy/actions/ActionContext.h @@ -121,7 +121,7 @@ class ActionContext : public NamedObjectContext creators["shoot"] = &ActionContext::shoot; creators["follow"] = &ActionContext::follow; creators["move from group"] = &ActionContext::move_from_group; - creators["flee to master"] = &ActionContext::flee_to_master; + creators["flee to group leader"] = &ActionContext::flee_to_group_leader; creators["runaway"] = &ActionContext::runaway; creators["stay"] = &ActionContext::stay; creators["sit"] = &ActionContext::sit; @@ -318,7 +318,7 @@ class ActionContext : public NamedObjectContext static Action* runaway(PlayerbotAI* botAI) { return new RunAwayAction(botAI); } static Action* follow(PlayerbotAI* botAI) { return new FollowAction(botAI); } static Action* move_from_group(PlayerbotAI* botAI) { return new MoveFromGroupAction(botAI); } - static Action* flee_to_master(PlayerbotAI* botAI) { return new FleeToMasterAction(botAI); } + static Action* flee_to_group_leader(PlayerbotAI* botAI) { return new FleeToGroupLeaderAction(botAI); } static Action* add_gathering_loot(PlayerbotAI* botAI) { return new AddGatheringLootAction(botAI); } static Action* add_loot(PlayerbotAI* botAI) { return new AddLootAction(botAI); } static Action* add_all_loot(PlayerbotAI* botAI) { return new AddAllLootAction(botAI); } diff --git a/src/strategy/actions/ChooseRpgTargetAction.cpp b/src/strategy/actions/ChooseRpgTargetAction.cpp index 20151658cc..0d441e9b67 100644 --- a/src/strategy/actions/ChooseRpgTargetAction.cpp +++ b/src/strategy/actions/ChooseRpgTargetAction.cpp @@ -311,7 +311,7 @@ bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldObject* target) bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldPosition pos) { PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); - Player* gmaster = botAI->GetGroupMaster(); + Player* groupLeader = botAI->GetGroupLeader(); Player* realMaster = botAI->GetMaster(); AiObjectContext* context = botAI->GetAiObjectContext(); @@ -327,30 +327,30 @@ bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldPosition pos) return false; } - if (!gmaster || bot == gmaster) + if (!groupLeader || bot == groupLeader) return true; if (!botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT)) return true; - if (bot->GetDistance(gmaster) > sPlayerbotAIConfig->rpgDistance * 2) + if (bot->GetDistance(groupLeader) > sPlayerbotAIConfig->rpgDistance * 2) return false; Formation* formation = AI_VALUE(Formation*, "formation"); - float distance = gmaster->GetDistance2d(pos.getX(), pos.getY()); + float distance = groupLeader->GetDistance2d(pos.getX(), pos.getY()); if (!botAI->HasActivePlayerMaster() && distance < 50.0f) { - Player* player = gmaster; - if (gmaster && !gmaster->isMoving() || + Player* player = groupLeader; + if (groupLeader && !groupLeader->isMoving() || PAI_VALUE(WorldPosition, "last long move").distance(pos) < sPlayerbotAIConfig->reactDistance) return true; } - if ((inDungeon || !gmaster->HasPlayerFlag(PLAYER_FLAGS_RESTING)) && realMaster == gmaster && distance > 5.0f) + if ((inDungeon || !groupLeader->HasPlayerFlag(PLAYER_FLAGS_RESTING)) && realMaster == groupLeader && distance > 5.0f) return false; - if (!gmaster->isMoving() && distance < 25.0f) + if (!groupLeader->isMoving() && distance < 25.0f) return true; if (distance < formation->GetMaxDistance()) diff --git a/src/strategy/actions/ChooseTravelTargetAction.cpp b/src/strategy/actions/ChooseTravelTargetAction.cpp index 3843f35876..cf6dddd40c 100644 --- a/src/strategy/actions/ChooseTravelTargetAction.cpp +++ b/src/strategy/actions/ChooseTravelTargetAction.cpp @@ -180,7 +180,7 @@ void ChooseTravelTargetAction::getNewTarget(TravelTarget* newTarget, TravelTarge void ChooseTravelTargetAction::setNewTarget(TravelTarget* newTarget, TravelTarget* oldTarget) { // Tell the master where we are going. - if (!bot->GetGroup() || (botAI->GetGroupMaster() == bot)) + if (!bot->GetGroup() || (botAI->GetGroupLeader() == bot)) ReportTravelTarget(newTarget, oldTarget); // If we are heading to a creature/npc clear it from the ignore list. diff --git a/src/strategy/actions/FollowActions.cpp b/src/strategy/actions/FollowActions.cpp index f4a66bf9e7..d168e8cc93 100644 --- a/src/strategy/actions/FollowActions.cpp +++ b/src/strategy/actions/FollowActions.cpp @@ -70,7 +70,7 @@ bool FollowAction::isUseful() if (!target.empty()) fTarget = AI_VALUE(Unit*, target); else - fTarget = AI_VALUE(Unit*, "master target"); + fTarget = AI_VALUE(Unit*, "group leader"); if (fTarget) { @@ -114,9 +114,9 @@ bool FollowAction::CanDeadFollow(Unit* target) return true; } -bool FleeToMasterAction::Execute(Event event) +bool FleeToGroupLeaderAction::Execute(Event event) { - Unit* fTarget = AI_VALUE(Unit*, "master target"); + Unit* fTarget = AI_VALUE(Unit*, "group leader"); bool canFollow = Follow(fTarget); if (!canFollow) { @@ -146,22 +146,22 @@ bool FleeToMasterAction::Execute(Event event) return true; } -bool FleeToMasterAction::isUseful() +bool FleeToGroupLeaderAction::isUseful() { - if (!botAI->GetGroupMaster()) + if (!botAI->GetGroupLeader()) return false; - if (botAI->GetGroupMaster() == bot) + if (botAI->GetGroupLeader() == bot) return false; Unit* target = AI_VALUE(Unit*, "current target"); - if (target && botAI->GetGroupMaster()->GetTarget() == target->GetGUID()) + if (target && botAI->GetGroupLeader()->GetTarget() == target->GetGUID()) return false; if (!botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT)) return false; - Unit* fTarget = AI_VALUE(Unit*, "master target"); + Unit* fTarget = AI_VALUE(Unit*, "group leader"); if (!CanDeadFollow(fTarget)) return false; diff --git a/src/strategy/actions/FollowActions.h b/src/strategy/actions/FollowActions.h index 5468e34131..9331bbf556 100644 --- a/src/strategy/actions/FollowActions.h +++ b/src/strategy/actions/FollowActions.h @@ -20,10 +20,10 @@ class FollowAction : public MovementAction bool CanDeadFollow(Unit* target); }; -class FleeToMasterAction : public FollowAction +class FleeToGroupLeaderAction : public FollowAction { public: - FleeToMasterAction(PlayerbotAI* botAI) : FollowAction(botAI, "flee to master") {} + FleeToGroupLeaderAction(PlayerbotAI* botAI) : FollowAction(botAI, "flee to group leader") {} bool Execute(Event event) override; bool isUseful() override; diff --git a/src/strategy/actions/InviteToGroupAction.cpp b/src/strategy/actions/InviteToGroupAction.cpp index 4d0b4df7b9..bec515fefb 100644 --- a/src/strategy/actions/InviteToGroupAction.cpp +++ b/src/strategy/actions/InviteToGroupAction.cpp @@ -141,7 +141,7 @@ bool InviteNearbyToGroupAction::isUseful() if (group->isRaidGroup() && group->IsFull()) return false; - if (botAI->GetGroupMaster() != bot) + if (botAI->GetGroupLeader() != bot) return false; uint32 memberCount = group->GetMembersCount(); diff --git a/src/strategy/actions/LeaveGroupAction.cpp b/src/strategy/actions/LeaveGroupAction.cpp index 039c476bf5..a279c9426c 100644 --- a/src/strategy/actions/LeaveGroupAction.cpp +++ b/src/strategy/actions/LeaveGroupAction.cpp @@ -109,22 +109,22 @@ bool LeaveFarAwayAction::isUseful() if (!bot->GetGroup()) return false; - Player* master = botAI->GetGroupMaster(); + Player* groupLeader = botAI->GetGroupLeader(); Player* trueMaster = botAI->GetMaster(); - if (!master || (bot == master && !botAI->IsRealPlayer())) + if (!groupLeader || (bot == groupLeader && !botAI->IsRealPlayer())) return false; - PlayerbotAI* masterBotAI = nullptr; - if (master) - masterBotAI = GET_PLAYERBOT_AI(master); - if (master && !masterBotAI) + PlayerbotAI* groupLeaderBotAI = nullptr; + if (groupLeader) + groupLeaderBotAI = GET_PLAYERBOT_AI(groupLeader); + if (groupLeader && !groupLeaderBotAI) return false; if (trueMaster && !GET_PLAYERBOT_AI(trueMaster)) return false; if (botAI->IsAlt() && - (!masterBotAI || masterBotAI->IsRealPlayer())) // Don't leave group when alt grouped with player master. + (!groupLeaderBotAI || groupLeaderBotAI->IsRealPlayer())) // Don't leave group when alt grouped with player groupLeader. return false; if (botAI->GetGrouperType() == GrouperType::SOLO) @@ -138,19 +138,19 @@ bool LeaveFarAwayAction::isUseful() if (dCount > 4 && !botAI->HasRealPlayerMaster()) return true; - if (bot->GetGuildId() == master->GetGuildId()) + if (bot->GetGuildId() == groupLeader->GetGuildId()) { - if (bot->GetLevel() > master->GetLevel() + 5) + if (bot->GetLevel() > groupLeader->GetLevel() + 5) { if (AI_VALUE(bool, "should get money")) return false; } } - if (abs(int32(master->GetLevel() - bot->GetLevel())) > 4) + if (abs(int32(groupLeader->GetLevel() - bot->GetLevel())) > 4) return true; - if (bot->GetMapId() != master->GetMapId() || bot->GetDistance2d(master) >= 2 * sPlayerbotAIConfig->rpgDistance) + if (bot->GetMapId() != groupLeader->GetMapId() || bot->GetDistance2d(groupLeader) >= 2 * sPlayerbotAIConfig->rpgDistance) { return true; } diff --git a/src/strategy/actions/MoveToTravelTargetAction.cpp b/src/strategy/actions/MoveToTravelTargetAction.cpp index a4dec2caac..ed60339b82 100644 --- a/src/strategy/actions/MoveToTravelTargetAction.cpp +++ b/src/strategy/actions/MoveToTravelTargetAction.cpp @@ -18,7 +18,7 @@ bool MoveToTravelTargetAction::Execute(Event event) WorldLocation location = *target->getPosition(); Group* group = bot->GetGroup(); - if (group && !urand(0, 1) && bot == botAI->GetGroupMaster() && !bot->IsInCombat()) + if (group && !urand(0, 1) && bot == botAI->GetGroupLeader() && !bot->IsInCombat()) { for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index 7078557d1e..f66638a7a1 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -1102,7 +1102,7 @@ void MovementAction::UpdateMovementState() // { // if (Unit* pTarget = sServerFacade->GetChaseTarget(bot)) // { - // if (pTarget != botAI->GetGroupMaster()) + // if (pTarget != botAI->GetGroupLeader()) // return; // if (!bot->IsWithinMeleeRange(pTarget)) @@ -2663,7 +2663,7 @@ bool DisperseSetAction::Execute(Event event) return true; } -bool RunAwayAction::Execute(Event event) { return Flee(AI_VALUE(Unit*, "master target")); } +bool RunAwayAction::Execute(Event event) { return Flee(AI_VALUE(Unit*, "group leader")); } bool MoveToLootAction::Execute(Event event) { diff --git a/src/strategy/actions/RandomBotUpdateAction.cpp b/src/strategy/actions/RandomBotUpdateAction.cpp index 1536cd6b92..61a8360e59 100644 --- a/src/strategy/actions/RandomBotUpdateAction.cpp +++ b/src/strategy/actions/RandomBotUpdateAction.cpp @@ -13,10 +13,10 @@ bool RandomBotUpdateAction::Execute(Event event) if (!sRandomPlayerbotMgr->IsRandomBot(bot)) return false; - if (bot->GetGroup() && botAI->GetGroupMaster()) + if (bot->GetGroup() && botAI->GetGroupLeader()) { - PlayerbotAI* groupMasterBotAI = GET_PLAYERBOT_AI(botAI->GetGroupMaster()); - if (!groupMasterBotAI || groupMasterBotAI->IsRealPlayer()) + PlayerbotAI* groupLeaderBotAI = GET_PLAYERBOT_AI(botAI->GetGroupLeader()); + if (!groupLeaderBotAI || groupLeaderBotAI->IsRealPlayer()) return true; } diff --git a/src/strategy/actions/ReleaseSpiritAction.cpp b/src/strategy/actions/ReleaseSpiritAction.cpp index b06fc89263..17fcc42a0b 100644 --- a/src/strategy/actions/ReleaseSpiritAction.cpp +++ b/src/strategy/actions/ReleaseSpiritAction.cpp @@ -168,15 +168,15 @@ bool AutoReleaseSpiritAction::ShouldAutoRelease() const if (!bot->GetGroup()) return true; - Player* groupMaster = botAI->GetGroupMaster(); - if (!groupMaster || groupMaster == bot) + Player* groupLeader = botAI->GetGroupLeader(); + if (!groupLeader || groupLeader == bot) return true; if (!botAI->HasActivePlayerMaster()) return true; if (botAI->HasActivePlayerMaster() && - groupMaster->GetMapId() == bot->GetMapId() && + groupLeader->GetMapId() == bot->GetMapId() && bot->GetMap() && (bot->GetMap()->IsRaid() || bot->GetMap()->IsDungeon())) { @@ -184,7 +184,7 @@ bool AutoReleaseSpiritAction::ShouldAutoRelease() const } return sServerFacade->IsDistanceGreaterThan( - AI_VALUE2(float, "distance", "master target"), + AI_VALUE2(float, "distance", "group leader"), sPlayerbotAIConfig->sightDistance); } diff --git a/src/strategy/actions/ResetInstancesAction.cpp b/src/strategy/actions/ResetInstancesAction.cpp index 1c0a6f2480..cce5eef1e3 100644 --- a/src/strategy/actions/ResetInstancesAction.cpp +++ b/src/strategy/actions/ResetInstancesAction.cpp @@ -16,4 +16,4 @@ bool ResetInstancesAction::Execute(Event event) return true; } -bool ResetInstancesAction::isUseful() { return botAI->GetGroupMaster() == bot; }; +bool ResetInstancesAction::isUseful() { return botAI->GetGroupLeader() == bot; }; diff --git a/src/strategy/actions/ReviveFromCorpseAction.cpp b/src/strategy/actions/ReviveFromCorpseAction.cpp index 74237670df..ce0b1fa05d 100644 --- a/src/strategy/actions/ReviveFromCorpseAction.cpp +++ b/src/strategy/actions/ReviveFromCorpseAction.cpp @@ -17,14 +17,14 @@ bool ReviveFromCorpseAction::Execute(Event event) { - Player* master = botAI->GetGroupMaster(); + Player* groupLeader = botAI->GetGroupLeader(); Corpse* corpse = bot->GetCorpse(); - // follow master when master revives + // follow group Leader when group Leader revives WorldPacket& p = event.getPacket(); - if (!p.empty() && p.GetOpcode() == CMSG_RECLAIM_CORPSE && master && !corpse && bot->IsAlive()) + if (!p.empty() && p.GetOpcode() == CMSG_RECLAIM_CORPSE && groupLeader && !corpse && bot->IsAlive()) { - if (sServerFacade->IsDistanceLessThan(AI_VALUE2(float, "distance", "master target"), + if (sServerFacade->IsDistanceLessThan(AI_VALUE2(float, "distance", "group leader"), sPlayerbotAIConfig->farDistance)) { if (!botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT)) @@ -43,10 +43,10 @@ bool ReviveFromCorpseAction::Execute(Event event) // time(nullptr)) // return false; - if (master) + if (groupLeader) { - if (!GET_PLAYERBOT_AI(master) && master->isDead() && master->GetCorpse() && - sServerFacade->IsDistanceLessThan(AI_VALUE2(float, "distance", "master target"), + if (!GET_PLAYERBOT_AI(groupLeader) && groupLeader->isDead() && groupLeader->GetCorpse() && + sServerFacade->IsDistanceLessThan(AI_VALUE2(float, "distance", "group leader"), sPlayerbotAIConfig->farDistance)) return false; } @@ -79,15 +79,15 @@ bool FindCorpseAction::Execute(Event event) if (bot->InBattleground()) return false; - Player* master = botAI->GetGroupMaster(); + Player* groupLeader = botAI->GetGroupLeader(); Corpse* corpse = bot->GetCorpse(); if (!corpse) return false; - // if (master) + // if (groupLeader) // { - // if (!GET_PLAYERBOT_AI(master) && - // sServerFacade->IsDistanceLessThan(AI_VALUE2(float, "distance", "master target"), + // if (!GET_PLAYERBOT_AI(groupLeader) && + // sServerFacade->IsDistanceLessThan(AI_VALUE2(float, "distance", "group leader"), // sPlayerbotAIConfig->farDistance)) return false; // } @@ -110,20 +110,20 @@ bool FindCorpseAction::Execute(Event event) WorldPosition botPos(bot); WorldPosition corpsePos(corpse); WorldPosition moveToPos = corpsePos; - WorldPosition masterPos(master); + WorldPosition leaderPos(groupLeader); float reclaimDist = CORPSE_RECLAIM_RADIUS - 5.0f; float corpseDist = botPos.distance(corpsePos); int64 deadTime = time(nullptr) - corpse->GetGhostTime(); - bool moveToMaster = master && master != bot && masterPos.fDist(corpsePos) < reclaimDist; + bool moveToLeader = groupLeader && groupLeader != bot && leaderPos.fDist(corpsePos) < reclaimDist; // Should we ressurect? If so, return false. if (corpseDist < reclaimDist) { - if (moveToMaster) // We are near master. + if (moveToLeader) // We are near group leader. { - if (botPos.fDist(masterPos) < sPlayerbotAIConfig->spellDistance) + if (botPos.fDist(leaderPos) < sPlayerbotAIConfig->spellDistance) return false; } else if (deadTime > 8 * MINUTE) // We have walked too long already. @@ -140,8 +140,8 @@ bool FindCorpseAction::Execute(Event event) // If we are getting close move to a save ressurrection spot instead of just the corpse. if (corpseDist < sPlayerbotAIConfig->reactDistance) { - if (moveToMaster) - moveToPos = masterPos; + if (moveToLeader) + moveToPos = leaderPos; else { FleeManager manager(bot, reclaimDist, 0.0, urand(0, 1), moveToPos); @@ -215,12 +215,12 @@ GraveyardStruct const* SpiritHealerAction::GetGrave(bool startZone) if (!startZone && ClosestGrave) return ClosestGrave; - if (botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT) && botAI->GetGroupMaster() && botAI->GetGroupMaster() != bot) + if (botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT) && botAI->GetGroupLeader() && botAI->GetGroupLeader() != bot) { - Player* master = botAI->GetGroupMaster(); - if (master && master != bot) + Player* groupLeader = botAI->GetGroupLeader(); + if (groupLeader && groupLeader != bot) { - ClosestGrave = sGraveyard->GetClosestGraveyard(master, bot->GetTeamId()); + ClosestGrave = sGraveyard->GetClosestGraveyard(groupLeader, bot->GetTeamId()); if (ClosestGrave) return ClosestGrave; diff --git a/src/strategy/actions/RewardAction.cpp b/src/strategy/actions/RewardAction.cpp index 1022166d0f..8fe1c6ff72 100644 --- a/src/strategy/actions/RewardAction.cpp +++ b/src/strategy/actions/RewardAction.cpp @@ -35,8 +35,8 @@ bool RewardAction::Execute(Event event) return true; } - Unit* mtar = AI_VALUE(Unit*, "master target"); - if (mtar && Reward(itemId, mtar)) + Unit* groupLeaderUnit = AI_VALUE(Unit*, "group leader"); + if (groupLeaderUnit && Reward(itemId, groupLeaderUnit)) return true; botAI->TellError("Cannot talk to quest giver"); diff --git a/src/strategy/actions/RpgSubActions.cpp b/src/strategy/actions/RpgSubActions.cpp index 784f6bbb20..aa4269fa92 100644 --- a/src/strategy/actions/RpgSubActions.cpp +++ b/src/strategy/actions/RpgSubActions.cpp @@ -76,7 +76,7 @@ void RpgHelper::setFacing(GuidPosition guidPosition) void RpgHelper::setDelay(bool waitForGroup) { - if (!botAI->HasRealPlayerMaster() || (waitForGroup && botAI->GetGroupMaster() == bot && bot->GetGroup())) + if (!botAI->HasRealPlayerMaster() || (waitForGroup && botAI->GetGroupLeader() == bot && bot->GetGroup())) botAI->SetNextCheckDelay(sPlayerbotAIConfig->rpgDelay); else botAI->SetNextCheckDelay(sPlayerbotAIConfig->rpgDelay / 5); diff --git a/src/strategy/actions/SecurityCheckAction.cpp b/src/strategy/actions/SecurityCheckAction.cpp index 1320eb04a6..c47a6e52cb 100644 --- a/src/strategy/actions/SecurityCheckAction.cpp +++ b/src/strategy/actions/SecurityCheckAction.cpp @@ -22,8 +22,8 @@ bool SecurityCheckAction::Execute(Event event) ItemQualities threshold = group->GetLootThreshold(); if (method == MASTER_LOOT || method == FREE_FOR_ALL || threshold > ITEM_QUALITY_UNCOMMON) { - if ((botAI->GetGroupMaster()->GetSession()->GetSecurity() == SEC_PLAYER) && - (!bot->GetGuildId() || bot->GetGuildId() != botAI->GetGroupMaster()->GetGuildId())) + if ((botAI->GetGroupLeader()->GetSession()->GetSecurity() == SEC_PLAYER) && + (!bot->GetGuildId() || bot->GetGuildId() != botAI->GetGroupLeader()->GetGuildId())) { botAI->TellError("I will play with this loot type only if I'm in your guild :/"); botAI->ChangeStrategy("+passive,+stay", BOT_STATE_NON_COMBAT); diff --git a/src/strategy/actions/TellMasterAction.cpp b/src/strategy/actions/TellMasterAction.cpp index 701f93d773..4b8d96d561 100644 --- a/src/strategy/actions/TellMasterAction.cpp +++ b/src/strategy/actions/TellMasterAction.cpp @@ -22,7 +22,7 @@ bool OutOfReactRangeAction::Execute(Event event) bool OutOfReactRangeAction::isUseful() { - bool canFollow = Follow(AI_VALUE(Unit*, "master target")); + bool canFollow = Follow(AI_VALUE(Unit*, "group leader")); if (!canFollow) { return false; diff --git a/src/strategy/actions/TravelAction.cpp b/src/strategy/actions/TravelAction.cpp index 5804fd7d24..f99f8b29d3 100644 --- a/src/strategy/actions/TravelAction.cpp +++ b/src/strategy/actions/TravelAction.cpp @@ -64,7 +64,7 @@ bool MoveToDarkPortalAction::Execute(Event event) { if (bot->GetGroup()) if (bot->GetGroup()->GetLeaderGUID() != bot->GetGUID() && - !GET_PLAYERBOT_AI(GET_PLAYERBOT_AI(bot)->GetGroupMaster())) + !GET_PLAYERBOT_AI(GET_PLAYERBOT_AI(bot)->GetGroupLeader())) return false; if (bot->GetLevel() > 57) diff --git a/src/strategy/rogue/RogueTriggers.cpp b/src/strategy/rogue/RogueTriggers.cpp index 16aada0164..fd9901552e 100644 --- a/src/strategy/rogue/RogueTriggers.cpp +++ b/src/strategy/rogue/RogueTriggers.cpp @@ -22,8 +22,8 @@ bool UnstealthTrigger::IsActive() return botAI->HasAura("stealth", bot) && !AI_VALUE(uint8, "attacker count") && (AI_VALUE2(bool, "moving", "self target") && ((botAI->GetMaster() && - sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "master target"), 10.0f) && - AI_VALUE2(bool, "moving", "master target")) || + sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "group leader"), 10.0f) && + AI_VALUE2(bool, "moving", "group leader")) || !AI_VALUE(uint8, "attacker count"))); } diff --git a/src/strategy/triggers/RangeTriggers.cpp b/src/strategy/triggers/RangeTriggers.cpp index 3c60934fd2..af29f984d1 100644 --- a/src/strategy/triggers/RangeTriggers.cpp +++ b/src/strategy/triggers/RangeTriggers.cpp @@ -213,7 +213,7 @@ PartyMemberToHealOutOfSpellRangeTrigger::PartyMemberToHealOutOfSpellRangeTrigger bool FarFromMasterTrigger::IsActive() { - return sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "master target"), distance); + return sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "group leader"), distance); } bool TooCloseToCreatureTrigger::TooCloseToCreature(uint32 creatureId, float range, bool alive) diff --git a/src/strategy/values/Formations.cpp b/src/strategy/values/Formations.cpp index 21c43f402a..30c738252b 100644 --- a/src/strategy/values/Formations.cpp +++ b/src/strategy/values/Formations.cpp @@ -62,7 +62,7 @@ class MeleeFormation : public FollowFormation public: MeleeFormation(PlayerbotAI* botAI) : FollowFormation(botAI, "melee") {} - std::string const GetTargetName() override { return "master target"; } + std::string const GetTargetName() override { return "group leader"; } }; class QueueFormation : public FollowFormation diff --git a/src/strategy/values/MasterTargetValue.cpp b/src/strategy/values/GroupLeaderValue.cpp similarity index 70% rename from src/strategy/values/MasterTargetValue.cpp rename to src/strategy/values/GroupLeaderValue.cpp index 6fddb6d99b..18ed007648 100644 --- a/src/strategy/values/MasterTargetValue.cpp +++ b/src/strategy/values/GroupLeaderValue.cpp @@ -3,8 +3,8 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ -#include "MasterTargetValue.h" +#include "GroupLeaderValue.h" #include "Playerbots.h" -Unit* MasterTargetValue::Calculate() { return botAI->GetGroupMaster(); } +Unit* GroupLeaderValue::Calculate() { return botAI->GetGroupLeader(); } diff --git a/src/strategy/values/MasterTargetValue.h b/src/strategy/values/GroupLeaderValue.h similarity index 57% rename from src/strategy/values/MasterTargetValue.h rename to src/strategy/values/GroupLeaderValue.h index a9f8f781df..a359620437 100644 --- a/src/strategy/values/MasterTargetValue.h +++ b/src/strategy/values/GroupLeaderValue.h @@ -3,18 +3,18 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ -#ifndef _PLAYERBOT_MASTERTARGETVALUE_H -#define _PLAYERBOT_MASTERTARGETVALUE_H +#ifndef _PLAYERBOT_GROUPLEADERVALUE_H +#define _PLAYERBOT_GROUPLEADERVALUE_H #include "Value.h" class PlayerbotAI; class Unit; -class MasterTargetValue : public UnitCalculatedValue +class GroupLeaderValue : public UnitCalculatedValue { public: - MasterTargetValue(PlayerbotAI* botAI, std::string const name = "master target") : UnitCalculatedValue(botAI, name) + GroupLeaderValue(PlayerbotAI* botAI, std::string const name = "group leader") : UnitCalculatedValue(botAI, name) { } diff --git a/src/strategy/values/GroupValues.cpp b/src/strategy/values/GroupValues.cpp index f9cb21028e..01049a7969 100644 --- a/src/strategy/values/GroupValues.cpp +++ b/src/strategy/values/GroupValues.cpp @@ -28,7 +28,7 @@ GuidVector GroupMembersValue::Calculate() bool IsFollowingPartyValue::Calculate() { - if (botAI->GetGroupMaster() == bot) + if (botAI->GetGroupLeader() == bot) return true; if (botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT)) @@ -39,15 +39,15 @@ bool IsFollowingPartyValue::Calculate() bool IsNearLeaderValue::Calculate() { - Player* groupMaster = botAI->GetGroupMaster(); + Player* groupLeader = botAI->GetGroupLeader(); - if (!groupMaster) + if (!groupLeader) return false; - if (groupMaster == bot) + if (groupLeader == bot) return true; - return sServerFacade->GetDistance2d(bot, botAI->GetGroupMaster()) < sPlayerbotAIConfig->sightDistance; + return sServerFacade->GetDistance2d(bot, botAI->GetGroupLeader()) < sPlayerbotAIConfig->sightDistance; } bool BoolANDValue::Calculate() @@ -154,8 +154,8 @@ bool GroupReadyValue::Calculate() // We only wait for members that are in range otherwise we might be waiting for bots stuck in dead loops // forever. - if (botAI->GetGroupMaster() && - sServerFacade->GetDistance2d(member, botAI->GetGroupMaster()) > sPlayerbotAIConfig->sightDistance) + if (botAI->GetGroupLeader() && + sServerFacade->GetDistance2d(member, botAI->GetGroupLeader()) > sPlayerbotAIConfig->sightDistance) continue; if (member->GetHealthPct() < sPlayerbotAIConfig->almostFullHealth) diff --git a/src/strategy/values/ValueContext.h b/src/strategy/values/ValueContext.h index b8e79d6e5a..adf03be8ea 100644 --- a/src/strategy/values/ValueContext.h +++ b/src/strategy/values/ValueContext.h @@ -30,6 +30,7 @@ #include "Formations.h" #include "GrindTargetValue.h" #include "GroupValues.h" +#include "GroupLeaderValue.h" #include "GuildValues.h" #include "HasAvailableLootValue.h" #include "HasTotemValue.h" @@ -51,7 +52,6 @@ #include "LootStrategyValue.h" #include "MaintenanceValues.h" #include "ManaSaveLevelValue.h" -#include "MasterTargetValue.h" #include "NearestAdsValue.h" #include "NearestCorpsesValue.h" #include "NearestFriendlyPlayersValue.h" @@ -130,7 +130,7 @@ class ValueContext : public NamedObjectContext creators["party member to resurrect"] = &ValueContext::party_member_to_resurrect; creators["current target"] = &ValueContext::current_target; creators["self target"] = &ValueContext::self_target; - creators["master target"] = &ValueContext::master; + creators["group leader"] = &ValueContext::group_leader; creators["line target"] = &ValueContext::line_target; creators["tank target"] = &ValueContext::tank_target; creators["dps target"] = &ValueContext::dps_target; @@ -439,7 +439,7 @@ class ValueContext : public NamedObjectContext static UntypedValue* current_target(PlayerbotAI* botAI) { return new CurrentTargetValue(botAI); } static UntypedValue* old_target(PlayerbotAI* botAI) { return new CurrentTargetValue(botAI); } static UntypedValue* self_target(PlayerbotAI* botAI) { return new SelfTargetValue(botAI); } - static UntypedValue* master(PlayerbotAI* botAI) { return new MasterTargetValue(botAI); } + static UntypedValue* group_leader(PlayerbotAI* botAI) { return new GroupLeaderValue(botAI); } static UntypedValue* line_target(PlayerbotAI* botAI) { return new LineTargetValue(botAI); } static UntypedValue* tank_target(PlayerbotAI* botAI) { return new TankTargetValue(botAI); } static UntypedValue* dps_target(PlayerbotAI* botAI) { return new DpsTargetValue(botAI); } From 353c29dfc44dd9779dea1348d8b10eeeb1580efe Mon Sep 17 00:00:00 2001 From: Keleborn <22352763+Celandriel@users.noreply.github.com> Date: Mon, 8 Dec 2025 03:25:40 -0800 Subject: [PATCH 43/47] Bug: Fix bots leaving LFG groups before master (#1876) I removed bots checking if they should leave group every tick, and will rely on the LeaveGroupFarAway action. I also increased the timer from 5 seconds to 20 seconds. No need to check this that often. --- src/PlayerbotAI.cpp | 6 ++---- src/PlayerbotAI.h | 2 +- src/strategy/generic/GroupStrategy.cpp | 5 ++--- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 7419574bd2..7975807f88 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -365,7 +365,7 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal) } // Update the bot's group status (moved to helper function) - UpdateAIGroupAndMaster(); + UpdateAIGroupMaster(); // Update internal AI UpdateAIInternal(elapsed, minimal); @@ -373,7 +373,7 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal) } // Helper function for UpdateAI to check group membership and handle removal if necessary -void PlayerbotAI::UpdateAIGroupAndMaster() +void PlayerbotAI::UpdateAIGroupMaster() { if (!bot) return; @@ -431,8 +431,6 @@ void PlayerbotAI::UpdateAIGroupAndMaster() botAI->ChangeStrategy("-follow", BOT_STATE_NON_COMBAT); } } - else if (!newMaster && !bot->InBattleground()) - LeaveOrDisbandGroup(); } } diff --git a/src/PlayerbotAI.h b/src/PlayerbotAI.h index ad7641b278..7252cd7720 100644 --- a/src/PlayerbotAI.h +++ b/src/PlayerbotAI.h @@ -612,7 +612,7 @@ class PlayerbotAI : public PlayerbotAIBase static void _fillGearScoreData(Player* player, Item* item, std::vector* gearScore, uint32& twoHandScore, bool mixed = false); bool IsTellAllowed(PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL); - void UpdateAIGroupAndMaster(); + void UpdateAIGroupMaster(); Item* FindItemInInventory(std::function checkItem) const; void HandleCommands(); void HandleCommand(uint32 type, const std::string& text, Player& fromPlayer, const uint32 lang = LANG_UNIVERSAL); diff --git a/src/strategy/generic/GroupStrategy.cpp b/src/strategy/generic/GroupStrategy.cpp index 9406e8409d..9cfba6e2fc 100644 --- a/src/strategy/generic/GroupStrategy.cpp +++ b/src/strategy/generic/GroupStrategy.cpp @@ -11,7 +11,6 @@ void GroupStrategy::InitTriggers(std::vector& triggers) { triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("invite nearby", 4.0f), nullptr))); triggers.push_back(new TriggerNode("random", NextAction::array(0, new NextAction("invite guild", 4.0f), nullptr))); - triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("leave far away", 4.0f), nullptr))); - triggers.push_back( - new TriggerNode("seldom", NextAction::array(0, new NextAction("reset instances", 1.0f), nullptr))); + triggers.push_back(new TriggerNode("random", NextAction::array(0, new NextAction("leave far away", 4.0f), nullptr))); + triggers.push_back(new TriggerNode("seldom", NextAction::array(0, new NextAction("reset instances", 1.0f), nullptr))); } From e5b27910539d0ea741e9e2a4341de4aaed6e44ac Mon Sep 17 00:00:00 2001 From: HennyWilly <5954598+HennyWilly@users.noreply.github.com> Date: Mon, 8 Dec 2025 12:29:07 +0100 Subject: [PATCH 44/47] Improve Molten Core Strategy (#1852) This is my first attempt of implementing playerbot strategies. A team of 40 can steamroll Molten Core relatively easy, even with lower item levels. Regardless, this PR adds resistance triggers and actions to mitigate some damage. Additionally, improvements were made for the encounters with Garr, Baron Geddon, Shazzrah and Golemagg. A short summary per boss is listed below. All planned features are included, but feedback is of course appreciated. ### Lucifron - Shadow resistance: mitigate damage from [Impending Doom](https://www.wowhead.com/classic/spell=19702/impending-doom) and [Shadow Shock](https://www.wowhead.com/classic/spell=19460/shadow-shock). ### Magmadar - Fire resistance: mitigate damage from [Lava Bomb](https://www.wowhead.com/classic/spell=19411/lava-bomb) and [Magma Spit](https://www.wowhead.com/classic/spell=19450/magma-spit). - Like King Dred and the fraction commander (Nexus), this fight might profit from an anti-fear strategy in order to counter [Panic](https://www.wowhead.com/classic/spell=19408/panic). Not implemented here. ### Gehennas - Shadow resistance: mitigate damage from [Shadow Bolt](https://www.wowhead.com/classic/spell=19728/shadow-bolt) and increase the chance to resist [Gehennas' Curse](https://www.wowhead.com/classic/spell=19716/gehennas-curse). ### Garr - Fire resistance: mitigate damage from the Firesworn adds ([Immolate](https://www.wowhead.com/classic/spell=20294/immolate) and [Eruption](https://www.wowhead.com/classic/spell=19497/eruption)). - Disabled dps aoe abilities via multiplier. This one is important because multiple exploding adds at once might delete bots rather quick... ### Baron Geddon - Refactored the existing strategy. - Fire resistance: mitigate damage from [Ignite Mana](https://www.wowhead.com/classic/spell=19659/ignite-mana), [Inferno](https://www.wowhead.com/classic/spell=19695/inferno) and [Living Bomb](https://www.wowhead.com/classic/spell=20475/living-bomb). - Better Inferno handling: Before moving away, bots stop attacking and interrupt their spells. Additionally, the new multiplier prevents bots from running back to Geddon while Inferno is still active. ### Shazzrah - Ranged bots now position themselves in a sweet spot that prevents them from getting hit with [Arcane Explosion](https://www.wowhead.com/classic/spell=19712/arcane-explosion) but still close enough to dps and heal. ### Sulfuron Harbinger - Fire resistance: mitigate damage from [Hand of Ragnaros](https://www.wowhead.com/classic/spell=19780/hand-of-ragnaros) and [Immolate](https://www.wowhead.com/classic/spell=20294/immolate). To be fair, this one is quite negligible... ### Golemagg - Fire resistance: mitigate damage from [Magma Splash](https://www.wowhead.com/classic/spell=13880/magma-splash) and [Pyroblast](https://www.wowhead.com/classic/spell=20228/pyroblast). - Disabled dps aoe abilities via multiplier. Kind of a preference on my side. Otherwise, the Core Ragers spam emotes about not wanting to die. ### Majordomo Executus - Shadow resistance: mitigate damage from [Aegis of Ragnaros](https://www.wowhead.com/classic/spell=20620/aegis-of-ragnaros), [Shadow Shock](https://www.wowhead.com/classic/spell=20603/shadow-shock) and [Shadow Bolt](https://www.wowhead.com/classic/spell=21077/shadow-bolt). This one is also negligible, TBF. ### Ragnaros - Fire resistance: mitigate damage from [Wrath of Ragnaros](https://www.wowhead.com/classic/spell=20566/wrath-of-ragnaros) and [Lava Burst](https://www.wowhead.com/classic/spell=21158/lava-burst). --- src/PlayerbotAI.cpp | 10 +- src/PlayerbotAI.h | 2 +- src/strategy/raids/RaidStrategyContext.h | 4 +- .../raids/moltencore/RaidMcActionContext.h | 32 ++- .../raids/moltencore/RaidMcActions.cpp | 214 ++++++++++++++++-- src/strategy/raids/moltencore/RaidMcActions.h | 49 +++- src/strategy/raids/moltencore/RaidMcHelpers.h | 22 ++ .../raids/moltencore/RaidMcMultipliers.cpp | 117 ++++++++++ .../raids/moltencore/RaidMcMultipliers.h | 27 +++ .../raids/moltencore/RaidMcStrategy.cpp | 70 +++++- .../raids/moltencore/RaidMcStrategy.h | 8 +- .../raids/moltencore/RaidMcTriggerContext.h | 30 ++- .../raids/moltencore/RaidMcTriggers.cpp | 36 ++- .../raids/moltencore/RaidMcTriggers.h | 28 +++ 14 files changed, 601 insertions(+), 48 deletions(-) create mode 100644 src/strategy/raids/moltencore/RaidMcHelpers.h create mode 100644 src/strategy/raids/moltencore/RaidMcMultipliers.cpp create mode 100644 src/strategy/raids/moltencore/RaidMcMultipliers.h diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 7975807f88..2ef6860a2a 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -1459,7 +1459,7 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster) strategyName = "onyxia"; // Onyxia's Lair break; case 409: - strategyName = "mc"; // Molten Core + strategyName = "moltencore"; // Molten Core break; case 469: strategyName = "bwl"; // Blackwing Lair @@ -2247,7 +2247,7 @@ uint32 PlayerbotAI::GetGroupTankNum(Player* player) bool PlayerbotAI::IsAssistTank(Player* player) { return IsTank(player) && !IsMainTank(player); } -bool PlayerbotAI::IsAssistTankOfIndex(Player* player, int index) +bool PlayerbotAI::IsAssistTankOfIndex(Player* player, int index, bool ignoreDeadPlayers) { Group* group = player->GetGroup(); if (!group) @@ -2264,6 +2264,9 @@ bool PlayerbotAI::IsAssistTankOfIndex(Player* player, int index) continue; } + if (ignoreDeadPlayers && !member->IsAlive()) + continue; + if (group->IsAssistant(member->GetGUID()) && IsAssistTank(member)) { if (index == counter) @@ -2283,6 +2286,9 @@ bool PlayerbotAI::IsAssistTankOfIndex(Player* player, int index) continue; } + if (ignoreDeadPlayers && !member->IsAlive()) + continue; + if (!group->IsAssistant(member->GetGUID()) && IsAssistTank(member)) { if (index == counter) diff --git a/src/PlayerbotAI.h b/src/PlayerbotAI.h index 7252cd7720..3b9eaab1ea 100644 --- a/src/PlayerbotAI.h +++ b/src/PlayerbotAI.h @@ -428,7 +428,7 @@ class PlayerbotAI : public PlayerbotAIBase static bool IsMainTank(Player* player); static uint32 GetGroupTankNum(Player* player); static bool IsAssistTank(Player* player); - static bool IsAssistTankOfIndex(Player* player, int index); + static bool IsAssistTankOfIndex(Player* player, int index, bool ignoreDeadPlayers = false); static bool IsHealAssistantOfIndex(Player* player, int index); static bool IsRangedDpsAssistantOfIndex(Player* player, int index); bool HasAggro(Unit* unit); diff --git a/src/strategy/raids/RaidStrategyContext.h b/src/strategy/raids/RaidStrategyContext.h index f16fa72546..ee2bac3270 100644 --- a/src/strategy/raids/RaidStrategyContext.h +++ b/src/strategy/raids/RaidStrategyContext.h @@ -22,7 +22,7 @@ class RaidStrategyContext : public NamedObjectContext RaidStrategyContext() : NamedObjectContext(false, true) { creators["aq20"] = &RaidStrategyContext::aq20; - creators["mc"] = &RaidStrategyContext::mc; + creators["moltencore"] = &RaidStrategyContext::moltencore; creators["bwl"] = &RaidStrategyContext::bwl; creators["karazhan"] = &RaidStrategyContext::karazhan; creators["magtheridon"] = &RaidStrategyContext::magtheridon; @@ -38,7 +38,7 @@ class RaidStrategyContext : public NamedObjectContext private: static Strategy* aq20(PlayerbotAI* botAI) { return new RaidAq20Strategy(botAI); } - static Strategy* mc(PlayerbotAI* botAI) { return new RaidMcStrategy(botAI); } + static Strategy* moltencore(PlayerbotAI* botAI) { return new RaidMcStrategy(botAI); } static Strategy* bwl(PlayerbotAI* botAI) { return new RaidBwlStrategy(botAI); } static Strategy* karazhan(PlayerbotAI* botAI) { return new RaidKarazhanStrategy(botAI); } static Strategy* magtheridon(PlayerbotAI* botAI) { return new RaidMagtheridonStrategy(botAI); } diff --git a/src/strategy/raids/moltencore/RaidMcActionContext.h b/src/strategy/raids/moltencore/RaidMcActionContext.h index 45e6e056cc..79a4a95a85 100644 --- a/src/strategy/raids/moltencore/RaidMcActionContext.h +++ b/src/strategy/raids/moltencore/RaidMcActionContext.h @@ -10,13 +10,39 @@ class RaidMcActionContext : public NamedObjectContext public: RaidMcActionContext() { - creators["mc check should move from group"] = &RaidMcActionContext::check_should_move_from_group; + creators["mc lucifron shadow resistance"] = &RaidMcActionContext::lucifron_shadow_resistance; + creators["mc magmadar fire resistance"] = &RaidMcActionContext::magmadar_fire_resistance; + creators["mc gehennas shadow resistance"] = &RaidMcActionContext::gehennas_shadow_resistance; + creators["mc garr fire resistance"] = &RaidMcActionContext::garr_fire_resistance; + creators["mc baron geddon fire resistance"] = &RaidMcActionContext::baron_geddon_fire_resistance; + creators["mc move from group"] = &RaidMcActionContext::check_should_move_from_group; creators["mc move from baron geddon"] = &RaidMcActionContext::move_from_baron_geddon; + creators["mc shazzrah move away"] = &RaidMcActionContext::shazzrah_move_away; + creators["mc sulfuron harbinger fire resistance"] = &RaidMcActionContext::sulfuron_harbinger_fire_resistance; + creators["mc golemagg fire resistance"] = &RaidMcActionContext::golemagg_fire_resistance; + creators["mc golemagg mark boss"] = &RaidMcActionContext::golemagg_mark_boss; + creators["mc golemagg main tank attack golemagg"] = &RaidMcActionContext::golemagg_main_tank_attack_golemagg; + creators["mc golemagg assist tank attack core rager"] = &RaidMcActionContext::golemagg_assist_tank_attack_core_rager; + creators["mc majordomo shadow resistance"] = &RaidMcActionContext::majordomo_shadow_resistance; + creators["mc ragnaros fire resistance"] = &RaidMcActionContext::ragnaros_fire_resistance; } private: - static Action* check_should_move_from_group(PlayerbotAI* ai) { return new McCheckShouldMoveFromGroupAction(ai); } - static Action* move_from_baron_geddon(PlayerbotAI* ai) { return new McMoveFromBaronGeddonAction(ai); } + static Action* lucifron_shadow_resistance(PlayerbotAI* botAI) { return new BossShadowResistanceAction(botAI, "lucifron"); } + static Action* magmadar_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceAction(botAI, "magmadar"); } + static Action* gehennas_shadow_resistance(PlayerbotAI* botAI) { return new BossShadowResistanceAction(botAI, "gehennas"); } + static Action* garr_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceAction(botAI, "garr"); } + static Action* baron_geddon_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceAction(botAI, "baron geddon"); } + static Action* check_should_move_from_group(PlayerbotAI* botAI) { return new McMoveFromGroupAction(botAI); } + static Action* move_from_baron_geddon(PlayerbotAI* botAI) { return new McMoveFromBaronGeddonAction(botAI); } + static Action* shazzrah_move_away(PlayerbotAI* botAI) { return new McShazzrahMoveAwayAction(botAI); } + static Action* sulfuron_harbinger_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceAction(botAI, "sulfuron harbinger"); } + static Action* golemagg_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceAction(botAI, "golemagg the incinerator"); } + static Action* golemagg_mark_boss(PlayerbotAI* botAI) { return new McGolemaggMarkBossAction(botAI); } + static Action* golemagg_main_tank_attack_golemagg(PlayerbotAI* botAI) { return new McGolemaggMainTankAttackGolemaggAction(botAI); } + static Action* golemagg_assist_tank_attack_core_rager(PlayerbotAI* botAI) { return new McGolemaggAssistTankAttackCoreRagerAction(botAI); } + static Action* majordomo_shadow_resistance(PlayerbotAI* botAI) { return new BossShadowResistanceAction(botAI, "majordomo executus"); } + static Action* ragnaros_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceAction(botAI, "ragnaros"); } }; #endif diff --git a/src/strategy/raids/moltencore/RaidMcActions.cpp b/src/strategy/raids/moltencore/RaidMcActions.cpp index b82a073c40..b18c8b8534 100644 --- a/src/strategy/raids/moltencore/RaidMcActions.cpp +++ b/src/strategy/raids/moltencore/RaidMcActions.cpp @@ -1,43 +1,215 @@ #include "RaidMcActions.h" #include "Playerbots.h" +#include "RtiTargetValue.h" +#include "RaidMcTriggers.h" +#include "RaidMcHelpers.h" -bool McCheckShouldMoveFromGroupAction::Execute(Event event) +static constexpr float LIVING_BOMB_DISTANCE = 20.0f; +static constexpr float INFERNO_DISTANCE = 20.0f; + +// don't get hit by Arcane Explosion but still be in casting range +static constexpr float ARCANE_EXPLOSION_DISTANCE = 26.0f; + +// dedicated tank positions; prevents assist tanks from positioning Core Ragers on steep walls on pull +static const Position GOLEMAGG_TANK_POSITION{795.7308, -994.8848, -207.18661}; +static const Position CORE_RAGER_TANK_POSITION{846.6453, -1019.0639, -198.9819}; + +static constexpr float GOLEMAGGS_TRUST_DISTANCE = 30.0f; +static constexpr float CORE_RAGER_STEP_DISTANCE = 5.0f; + +using namespace MoltenCoreHelpers; + +bool McMoveFromGroupAction::Execute(Event event) +{ + return MoveFromGroup(LIVING_BOMB_DISTANCE); +} + +bool McMoveFromBaronGeddonAction::Execute(Event event) +{ + if (Unit* boss = AI_VALUE2(Unit*, "find target", "baron geddon")) + { + float distToTravel = INFERNO_DISTANCE - bot->GetDistance2d(boss); + if (distToTravel > 0) + { + // Stop current spell first + bot->AttackStop(); + bot->InterruptNonMeleeSpells(false); + + return MoveAway(boss, distToTravel); + } + } + return false; +} + +bool McShazzrahMoveAwayAction::Execute(Event event) +{ + if (Unit* boss = AI_VALUE2(Unit*, "find target", "shazzrah")) + { + float distToTravel = ARCANE_EXPLOSION_DISTANCE - bot->GetDistance2d(boss); + if (distToTravel > 0) + return MoveAway(boss, distToTravel); + } + return false; +} + +bool McGolemaggMarkBossAction::Execute(Event event) { - if (bot->HasAura(20475)) // barron geddon's living bomb + if (Unit* boss = AI_VALUE2(Unit*, "find target", "golemagg the incinerator")) { - if (!botAI->HasStrategy("move from group", BotState::BOT_STATE_COMBAT)) + if (Group* group = bot->GetGroup()) { - // add/remove from both for now as it will make it more obvious to - // player if this strat remains on after fight somehow - botAI->ChangeStrategy("+move from group", BOT_STATE_NON_COMBAT); - botAI->ChangeStrategy("+move from group", BOT_STATE_COMBAT); - return true; + ObjectGuid currentSkullGuid = group->GetTargetIcon(RtiTargetValue::skullIndex); + if (currentSkullGuid.IsEmpty() || currentSkullGuid != boss->GetGUID()) + { + group->SetTargetIcon(RtiTargetValue::skullIndex, bot->GetGUID(), boss->GetGUID()); + return true; + } } } - else if (botAI->HasStrategy("move from group", BotState::BOT_STATE_COMBAT)) + return false; +} + +bool McGolemaggTankAction::MoveUnitToPosition(Unit* target, const Position& tankPosition, float maxDistance, + float stepDistance) +{ + if (bot->GetVictim() != target) + return Attack(target); + if (target->GetVictim() == bot) { - // add/remove from both for now as it will make it more obvious to - // player if this strat remains on after fight somehow - botAI->ChangeStrategy("-move from group", BOT_STATE_NON_COMBAT); - botAI->ChangeStrategy("-move from group", BOT_STATE_COMBAT); + float distanceToTankPosition = bot->GetExactDist2d(tankPosition.GetPositionX(), tankPosition.GetPositionY()); + if (distanceToTankPosition > maxDistance) + { + float dX = tankPosition.GetPositionX() - bot->GetPositionX(); + float dY = tankPosition.GetPositionY() - bot->GetPositionY(); + float dist = sqrt(dX * dX + dY * dY); + float moveX = bot->GetPositionX() + (dX / dist) * stepDistance; + float moveY = bot->GetPositionY() + (dY / dist) * stepDistance; + return MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), false, false, + false, false, MovementPriority::MOVEMENT_COMBAT, true, + true); + } + } + else if (botAI->DoSpecificAction("taunt spell", Event(), true)) return true; + return false; +} + +bool McGolemaggTankAction::FindCoreRagers(Unit*& coreRager1, Unit*& coreRager2) const +{ + coreRager1 = coreRager2 = nullptr; + for (auto const& target : AI_VALUE(GuidVector, "possible targets no los")) + { + Unit* unit = botAI->GetUnit(target); + if (unit && unit->IsAlive() && unit->GetEntry() == NPC_CORE_RAGER) + { + if (coreRager1 == nullptr) + coreRager1 = unit; + else if (coreRager2 == nullptr) + { + coreRager2 = unit; + break; // There should be no third Core Rager. + } + } + } + return coreRager1 != nullptr && coreRager2 != nullptr; +} + +bool McGolemaggMainTankAttackGolemaggAction::Execute(Event event) +{ + // At this point, we know we are not the last living tank in the group. + if (Unit* boss = AI_VALUE2(Unit*, "find target", "golemagg the incinerator")) + { + Unit* coreRager1; + Unit* coreRager2; + if (!FindCoreRagers(coreRager1, coreRager2)) + return false; // safety check + + // We only need to move if the Core Ragers still have Golemagg's Trust + if (coreRager1->HasAura(SPELL_GOLEMAGGS_TRUST) || coreRager2->HasAura(SPELL_GOLEMAGGS_TRUST)) + return MoveUnitToPosition(boss, GOLEMAGG_TANK_POSITION, boss->GetCombatReach()); } return false; } -bool McMoveFromBaronGeddonAction::Execute(Event event) +bool McGolemaggAssistTankAttackCoreRagerAction::Execute(Event event) { - const float radius = 25.0f; // more than should be needed but bots keep trying to run back in - if (Unit* boss = AI_VALUE2(Unit*, "find target", "baron geddon")) + Unit* boss = AI_VALUE2(Unit*, "find target", "golemagg the incinerator"); + if (!boss) + return false; + + // Step 0: Filter additional assist tanks. We only need 2. + bool isFirstAssistTank = PlayerbotAI::IsAssistTankOfIndex(bot, 0, true); + bool isSecondAssistTank = PlayerbotAI::IsAssistTankOfIndex(bot, 1, true); + if (!isFirstAssistTank && !isSecondAssistTank) + return Attack(boss); + + // Step 1: Find both Core Ragers + Unit* coreRager1; + Unit* coreRager2; + if (!FindCoreRagers(coreRager1, coreRager2)) + return false; // safety check + + // Step 2: Assign Core Rager to bot + Unit* myCoreRager = nullptr; + Unit* otherCoreRager = nullptr; + if (isFirstAssistTank) { - long distToTravel = radius - bot->GetDistance(boss); - if (distToTravel > 0) + myCoreRager = coreRager1; + otherCoreRager = coreRager2; + } + else // isSecondAssistTank is always true here + { + myCoreRager = coreRager2; + otherCoreRager = coreRager1; + } + + // Step 3: Select the right target + if (myCoreRager->GetVictim() != bot) + { + // Step 3.1: My Core Rager isn't attacking me. Attack until it does. + if (bot->GetVictim() != myCoreRager) + return Attack(myCoreRager); + return botAI->DoSpecificAction("taunt spell", event, true); + } + + Unit* otherCoreRagerVictim = otherCoreRager->GetVictim(); + if (otherCoreRagerVictim) // Core Rager victim can be NULL + { + // Step 3.2: Check if the other Core Rager isn't attacking its assist tank. + Player* otherCoreRagerPlayerVictim = otherCoreRagerVictim->ToPlayer(); + if (otherCoreRagerPlayerVictim && + !PlayerbotAI::IsAssistTankOfIndex(otherCoreRagerPlayerVictim, 0, true) && + !PlayerbotAI::IsAssistTankOfIndex(otherCoreRagerPlayerVictim, 1, true)) { - // float angle = bot->GetAngle(boss) + M_PI; - // return Move(angle, distToTravel); - return MoveAway(boss, distToTravel); + // Assume we are the only assist tank or the other assist tank is dead => pick up other Core Rager! + if (bot->GetVictim() != otherCoreRager) + return Attack(otherCoreRager); + return botAI->DoSpecificAction("taunt spell", event, true); } } + + if (bot->GetVictim() != myCoreRager) + return Attack(myCoreRager); // Step 3.3: Attack our Core Rager in case we previously switched in 3.2. + + // Step 4: Prevent Golemagg's Trust on Core Ragers + if (myCoreRager->HasAura(SPELL_GOLEMAGGS_TRUST) || + (otherCoreRagerVictim == bot && otherCoreRager->HasAura(SPELL_GOLEMAGGS_TRUST))) + { + // Step 4.1: Move Core Ragers to dedicated tank position (only if Golemagg is far enough away from said position) + float bossDistanceToCoreRagerTankPosition = boss->GetExactDist2d( + CORE_RAGER_TANK_POSITION.GetPositionX(), CORE_RAGER_TANK_POSITION.GetPositionY()); + if (bossDistanceToCoreRagerTankPosition > GOLEMAGGS_TRUST_DISTANCE) + { + float distanceToTankPosition = bot->GetExactDist2d(CORE_RAGER_TANK_POSITION.GetPositionX(), + CORE_RAGER_TANK_POSITION.GetPositionY()); + if (distanceToTankPosition > CORE_RAGER_STEP_DISTANCE) + return MoveUnitToPosition(myCoreRager, CORE_RAGER_TANK_POSITION, CORE_RAGER_STEP_DISTANCE); + } + + // Step 4.2: if boss is too close to tank position, or we are already there, move away from Golemagg to try to out-range Golemagg's Trust + return MoveAway(boss, CORE_RAGER_STEP_DISTANCE, true); + } + return false; } diff --git a/src/strategy/raids/moltencore/RaidMcActions.h b/src/strategy/raids/moltencore/RaidMcActions.h index 6ff18513ff..680b311d3c 100644 --- a/src/strategy/raids/moltencore/RaidMcActions.h +++ b/src/strategy/raids/moltencore/RaidMcActions.h @@ -1,15 +1,16 @@ #ifndef _PLAYERBOT_RAIDMCACTIONS_H #define _PLAYERBOT_RAIDMCACTIONS_H +#include "AttackAction.h" #include "MovementActions.h" #include "PlayerbotAI.h" #include "Playerbots.h" -class McCheckShouldMoveFromGroupAction : public Action +class McMoveFromGroupAction : public MovementAction { public: - McCheckShouldMoveFromGroupAction(PlayerbotAI* botAI, std::string const name = "mc check should move from group") - : Action(botAI, name) {} + McMoveFromGroupAction(PlayerbotAI* botAI, std::string const name = "mc move from group") + : MovementAction(botAI, name) {} bool Execute(Event event) override; }; @@ -21,4 +22,46 @@ class McMoveFromBaronGeddonAction : public MovementAction bool Execute(Event event) override; }; +class McShazzrahMoveAwayAction : public MovementAction +{ +public: + McShazzrahMoveAwayAction(PlayerbotAI* botAI, std::string const name = "mc shazzrah move away") + : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class McGolemaggMarkBossAction : public Action +{ +public: + McGolemaggMarkBossAction(PlayerbotAI* botAI, std::string const name = "mc golemagg mark boss") + : Action(botAI, name) {}; + bool Execute(Event event) override; +}; + +class McGolemaggTankAction : public AttackAction +{ +public: + McGolemaggTankAction(PlayerbotAI* botAI, std::string const name) + : AttackAction(botAI, name) {} +protected: + bool MoveUnitToPosition(Unit* target, const Position& tankPosition, float maxDistance, float stepDistance = 3.0f); + bool FindCoreRagers(Unit*& coreRager1, Unit*& coreRager2) const; +}; + +class McGolemaggMainTankAttackGolemaggAction : public McGolemaggTankAction +{ +public: + McGolemaggMainTankAttackGolemaggAction(PlayerbotAI* botAI, std::string const name = "mc golemagg main tank attack golemagg") + : McGolemaggTankAction(botAI, name) {}; + bool Execute(Event event) override; +}; + +class McGolemaggAssistTankAttackCoreRagerAction : public McGolemaggTankAction +{ +public: + McGolemaggAssistTankAttackCoreRagerAction(PlayerbotAI* botAI, std::string const name = "mc golemagg assist tank attack core rager") + : McGolemaggTankAction(botAI, name) {}; + bool Execute(Event event) override; +}; + #endif diff --git a/src/strategy/raids/moltencore/RaidMcHelpers.h b/src/strategy/raids/moltencore/RaidMcHelpers.h new file mode 100644 index 0000000000..5dc821246d --- /dev/null +++ b/src/strategy/raids/moltencore/RaidMcHelpers.h @@ -0,0 +1,22 @@ +#ifndef _PLAYERBOT_RAIDMCHELPERS_H +#define _PLAYERBOT_RAIDMCHELPERS_H + +namespace MoltenCoreHelpers +{ +enum MoltenCoreNPCs +{ + // Golemagg + NPC_CORE_RAGER = 11672, +}; +enum MoltenCoreSpells +{ + // Baron Geddon + SPELL_INFERNO = 19695, + SPELL_LIVING_BOMB = 20475, + + // Golemagg + SPELL_GOLEMAGGS_TRUST = 20553, +}; +} + +#endif diff --git a/src/strategy/raids/moltencore/RaidMcMultipliers.cpp b/src/strategy/raids/moltencore/RaidMcMultipliers.cpp new file mode 100644 index 0000000000..d1ee936b0d --- /dev/null +++ b/src/strategy/raids/moltencore/RaidMcMultipliers.cpp @@ -0,0 +1,117 @@ +#include "RaidMcMultipliers.h" + +#include "Playerbots.h" +#include "ChooseTargetActions.h" +#include "GenericSpellActions.h" +#include "DruidActions.h" +#include "HunterActions.h" +#include "PaladinActions.h" +#include "ShamanActions.h" +#include "WarriorActions.h" +#include "DKActions.h" +#include "RaidMcActions.h" +#include "RaidMcHelpers.h" + +using namespace MoltenCoreHelpers; + +static bool IsDpsBotWithAoeAction(Player* bot, Action* action) +{ + if (PlayerbotAI::IsDps(bot)) + { + if (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action)) + return true; + + if (auto castSpellAction = dynamic_cast(action)) + { + if (castSpellAction->getThreatType() == Action::ActionThreatType::Aoe) + return true; + } + } + return false; +} + +float GarrDisableDpsAoeMultiplier::GetValue(Action* action) +{ + if (AI_VALUE2(Unit*, "find target", "garr")) + { + if (IsDpsBotWithAoeAction(bot, action)) + return 0.0f; + } + return 1.0f; +} + +static bool IsAllowedGeddonMovementAction(Action* action) +{ + if (dynamic_cast(action) && + !dynamic_cast(action) && + !dynamic_cast(action)) + return false; + + if (dynamic_cast(action)) + return false; + + return true; +} + +float BaronGeddonAbilityMultiplier::GetValue(Action* action) +{ + if (Unit* boss = AI_VALUE2(Unit*, "find target", "baron geddon")) + { + if (boss->HasAura(SPELL_INFERNO)) + { + if (!IsAllowedGeddonMovementAction(action)) + return 0.0f; + } + } + + // No check for Baron Geddon, because bots may have the bomb even after Geddon died. + if (bot->HasAura(SPELL_LIVING_BOMB)) + { + if (!IsAllowedGeddonMovementAction(action)) + return 0.0f; + } + + return 1.0f; +} + +static bool IsSingleLivingTankInGroup(Player* bot) +{ + if (Group* group = bot->GetGroup()) + { + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || !member->IsAlive() || member == bot) + continue; + if (PlayerbotAI::IsTank(member)) + return false; + } + } + return true; +} + +float GolemaggMultiplier::GetValue(Action* action) +{ + if (AI_VALUE2(Unit*, "find target", "golemagg the incinerator")) + { + if (PlayerbotAI::IsTank(bot) && IsSingleLivingTankInGroup(bot)) + { + // Only one tank => Pick up Golemagg and the two Core Ragers + if (dynamic_cast(action) || + dynamic_cast(action)) + return 0.0f; + } + if (PlayerbotAI::IsAssistTank(bot)) + { + // The first two assist tanks manage the Core Ragers. The remaining assist tanks attack the boss. + if (dynamic_cast(action)) + return 0.0f; + } + if (IsDpsBotWithAoeAction(bot, action)) + return 0.0f; + } + return 1.0f; +} diff --git a/src/strategy/raids/moltencore/RaidMcMultipliers.h b/src/strategy/raids/moltencore/RaidMcMultipliers.h new file mode 100644 index 0000000000..56dc7a5f80 --- /dev/null +++ b/src/strategy/raids/moltencore/RaidMcMultipliers.h @@ -0,0 +1,27 @@ +#ifndef _PLAYERBOT_RAIDMCMULTIPLIERS_H +#define _PLAYERBOT_RAIDMCMULTIPLIERS_H + +#include "Multiplier.h" + +class GarrDisableDpsAoeMultiplier : public Multiplier +{ +public: + GarrDisableDpsAoeMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "garr disable dps aoe multiplier") {} + float GetValue(Action* action) override; +}; + +class BaronGeddonAbilityMultiplier : public Multiplier +{ +public: + BaronGeddonAbilityMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "baron geddon ability multiplier") {} + float GetValue(Action* action) override; +}; + +class GolemaggMultiplier : public Multiplier +{ +public: + GolemaggMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "golemagg multiplier") {} + float GetValue(Action* action) override; +}; + +#endif diff --git a/src/strategy/raids/moltencore/RaidMcStrategy.cpp b/src/strategy/raids/moltencore/RaidMcStrategy.cpp index 67793de906..c6a5042eb5 100644 --- a/src/strategy/raids/moltencore/RaidMcStrategy.cpp +++ b/src/strategy/raids/moltencore/RaidMcStrategy.cpp @@ -1,13 +1,81 @@ #include "RaidMcStrategy.h" +#include "RaidMcMultipliers.h" #include "Strategy.h" void RaidMcStrategy::InitTriggers(std::vector& triggers) { + // Lucifron + triggers.push_back( + new TriggerNode("mc lucifron shadow resistance", + NextAction::array(0, new NextAction("mc lucifron shadow resistance", ACTION_RAID), nullptr))); + + // Magmadar + // TODO: Fear ward / tremor totem, or general anti-fear strat development. Same as King Dred (Drak'Tharon) and faction commander (Nexus). + triggers.push_back( + new TriggerNode("mc magmadar fire resistance", + NextAction::array(0, new NextAction("mc magmadar fire resistance", ACTION_RAID), nullptr))); + + // Gehennas + triggers.push_back( + new TriggerNode("mc gehennas shadow resistance", + NextAction::array(0, new NextAction("mc gehennas shadow resistance", ACTION_RAID), nullptr))); + + // Garr + triggers.push_back( + new TriggerNode("mc garr fire resistance", + NextAction::array(0, new NextAction("mc garr fire resistance", ACTION_RAID), nullptr))); + + // Baron Geddon + triggers.push_back( + new TriggerNode("mc baron geddon fire resistance", + NextAction::array(0, new NextAction("mc baron geddon fire resistance", ACTION_RAID), nullptr))); triggers.push_back( new TriggerNode("mc living bomb debuff", - NextAction::array(0, new NextAction("mc check should move from group", ACTION_RAID), nullptr))); + NextAction::array(0, new NextAction("mc move from group", ACTION_RAID), nullptr))); triggers.push_back( new TriggerNode("mc baron geddon inferno", NextAction::array(0, new NextAction("mc move from baron geddon", ACTION_RAID), nullptr))); + + // Shazzrah + triggers.push_back( + new TriggerNode("mc shazzrah ranged", + NextAction::array(0, new NextAction("mc shazzrah move away", ACTION_RAID), nullptr))); + + // Sulfuron Harbinger + // Alternatively, shadow resistance is also possible. + triggers.push_back( + new TriggerNode("mc sulfuron harbinger fire resistance", + NextAction::array(0, new NextAction("mc sulfuron harbinger fire resistance", ACTION_RAID), nullptr))); + + // Golemagg the Incinerator + triggers.push_back( + new TriggerNode("mc golemagg fire resistance", + NextAction::array(0, new NextAction("mc golemagg fire resistance", ACTION_RAID), nullptr))); + triggers.push_back( + new TriggerNode("mc golemagg mark boss", + NextAction::array(0, new NextAction("mc golemagg mark boss", ACTION_RAID), nullptr))); + triggers.push_back( + new TriggerNode("mc golemagg is main tank", + NextAction::array(0, new NextAction("mc golemagg main tank attack golemagg", ACTION_RAID), nullptr))); + triggers.push_back( + new TriggerNode("mc golemagg is assist tank", + NextAction::array(0, new NextAction("mc golemagg assist tank attack core rager", ACTION_RAID), nullptr))); + + // Majordomo Executus + triggers.push_back( + new TriggerNode("mc majordomo shadow resistance", + NextAction::array(0, new NextAction("mc majordomo shadow resistance", ACTION_RAID), nullptr))); + + // Ragnaros + triggers.push_back( + new TriggerNode("mc ragnaros fire resistance", + NextAction::array(0, new NextAction("mc ragnaros fire resistance", ACTION_RAID), nullptr))); +} + +void RaidMcStrategy::InitMultipliers(std::vector& multipliers) +{ + multipliers.push_back(new GarrDisableDpsAoeMultiplier(botAI)); + multipliers.push_back(new BaronGeddonAbilityMultiplier(botAI)); + multipliers.push_back(new GolemaggMultiplier(botAI)); } diff --git a/src/strategy/raids/moltencore/RaidMcStrategy.h b/src/strategy/raids/moltencore/RaidMcStrategy.h index 82b7d8f25e..45b503e933 100644 --- a/src/strategy/raids/moltencore/RaidMcStrategy.h +++ b/src/strategy/raids/moltencore/RaidMcStrategy.h @@ -8,10 +8,10 @@ class RaidMcStrategy : public Strategy { public: - RaidMcStrategy(PlayerbotAI* ai) : Strategy(ai) {} - virtual std::string const getName() override { return "mc"; } - virtual void InitTriggers(std::vector& triggers) override; - // virtual void InitMultipliers(std::vector &multipliers) override; + RaidMcStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} + std::string const getName() override { return "moltencore"; } + void InitTriggers(std::vector& triggers) override; + void InitMultipliers(std::vector &multipliers) override; }; #endif diff --git a/src/strategy/raids/moltencore/RaidMcTriggerContext.h b/src/strategy/raids/moltencore/RaidMcTriggerContext.h index 93fe6df0f8..b74958919c 100644 --- a/src/strategy/raids/moltencore/RaidMcTriggerContext.h +++ b/src/strategy/raids/moltencore/RaidMcTriggerContext.h @@ -10,13 +10,39 @@ class RaidMcTriggerContext : public NamedObjectContext public: RaidMcTriggerContext() { + creators["mc lucifron shadow resistance"] = &RaidMcTriggerContext::lucifron_shadow_resistance; + creators["mc magmadar fire resistance"] = &RaidMcTriggerContext::magmadar_fire_resistance; + creators["mc gehennas shadow resistance"] = &RaidMcTriggerContext::gehennas_shadow_resistance; + creators["mc garr fire resistance"] = &RaidMcTriggerContext::garr_fire_resistance; + creators["mc baron geddon fire resistance"] = &RaidMcTriggerContext::baron_geddon_fire_resistance; creators["mc living bomb debuff"] = &RaidMcTriggerContext::living_bomb_debuff; creators["mc baron geddon inferno"] = &RaidMcTriggerContext::baron_geddon_inferno; + creators["mc shazzrah ranged"] = &RaidMcTriggerContext::shazzrah_ranged; + creators["mc sulfuron harbinger fire resistance"] = &RaidMcTriggerContext::sulfuron_harbinger_fire_resistance; + creators["mc golemagg fire resistance"] = &RaidMcTriggerContext::golemagg_fire_resistance; + creators["mc golemagg mark boss"] = &RaidMcTriggerContext::golemagg_mark_boss; + creators["mc golemagg is main tank"] = &RaidMcTriggerContext::golemagg_is_main_tank; + creators["mc golemagg is assist tank"] = &RaidMcTriggerContext::golemagg_is_assist_tank; + creators["mc majordomo shadow resistance"] = &RaidMcTriggerContext::majordomo_shadow_resistance; + creators["mc ragnaros fire resistance"] = &RaidMcTriggerContext::ragnaros_fire_resistance; } private: - static Trigger* living_bomb_debuff(PlayerbotAI* ai) { return new McLivingBombDebuffTrigger(ai); } - static Trigger* baron_geddon_inferno(PlayerbotAI* ai) { return new McBaronGeddonInfernoTrigger(ai); } + static Trigger* lucifron_shadow_resistance(PlayerbotAI* botAI) { return new BossShadowResistanceTrigger(botAI, "lucifron"); } + static Trigger* magmadar_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceTrigger(botAI, "magmadar"); } + static Trigger* gehennas_shadow_resistance(PlayerbotAI* botAI) { return new BossShadowResistanceTrigger(botAI, "gehennas"); } + static Trigger* garr_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceTrigger(botAI, "garr"); } + static Trigger* baron_geddon_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceTrigger(botAI, "baron geddon"); } + static Trigger* living_bomb_debuff(PlayerbotAI* botAI) { return new McLivingBombDebuffTrigger(botAI); } + static Trigger* baron_geddon_inferno(PlayerbotAI* botAI) { return new McBaronGeddonInfernoTrigger(botAI); } + static Trigger* shazzrah_ranged(PlayerbotAI* botAI) { return new McShazzrahRangedTrigger(botAI); } + static Trigger* sulfuron_harbinger_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceTrigger(botAI, "sulfuron harbinger"); } + static Trigger* golemagg_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceTrigger(botAI, "golemagg the incinerator"); } + static Trigger* golemagg_mark_boss(PlayerbotAI* botAI) { return new McGolemaggMarkBossTrigger(botAI); } + static Trigger* golemagg_is_main_tank(PlayerbotAI* botAI) { return new McGolemaggIsMainTankTrigger(botAI); } + static Trigger* golemagg_is_assist_tank(PlayerbotAI* botAI) { return new McGolemaggIsAssistTankTrigger(botAI); } + static Trigger* majordomo_shadow_resistance(PlayerbotAI* botAI) { return new BossShadowResistanceTrigger(botAI, "majordomo executus"); } + static Trigger* ragnaros_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceTrigger(botAI, "ragnaros"); } }; #endif diff --git a/src/strategy/raids/moltencore/RaidMcTriggers.cpp b/src/strategy/raids/moltencore/RaidMcTriggers.cpp index f77b70f20e..834d703d33 100644 --- a/src/strategy/raids/moltencore/RaidMcTriggers.cpp +++ b/src/strategy/raids/moltencore/RaidMcTriggers.cpp @@ -1,22 +1,40 @@ #include "RaidMcTriggers.h" #include "SharedDefines.h" +#include "RaidMcHelpers.h" + +using namespace MoltenCoreHelpers; bool McLivingBombDebuffTrigger::IsActive() { - // if bot has barron geddon's living bomb, we need to add strat, otherwise we need to remove - // only do when fighting baron geddon (to avoid modifying strat set by player outside this fight) - if (Unit* boss = AI_VALUE2(Unit*, "find target", "baron geddon")) - { - if (boss->IsInCombat()) - return bot->HasAura(20475) != botAI->HasStrategy("move from group", BotState::BOT_STATE_COMBAT); - } - return false; + // No check for Baron Geddon, because bots may have the bomb even after Geddon died. + return bot->HasAura(SPELL_LIVING_BOMB); } bool McBaronGeddonInfernoTrigger::IsActive() { if (Unit* boss = AI_VALUE2(Unit*, "find target", "baron geddon")) - return boss->HasAura(19695); + return boss->HasAura(SPELL_INFERNO); return false; } + +bool McShazzrahRangedTrigger::IsActive() +{ + return AI_VALUE2(Unit*, "find target", "shazzrah") && PlayerbotAI::IsRanged(bot); +} + +bool McGolemaggMarkBossTrigger::IsActive() +{ + // any tank may mark the boss + return AI_VALUE2(Unit*, "find target", "golemagg the incinerator") && PlayerbotAI::IsTank(bot); +} + +bool McGolemaggIsMainTankTrigger::IsActive() +{ + return AI_VALUE2(Unit*, "find target", "golemagg the incinerator") && PlayerbotAI::IsMainTank(bot); +} + +bool McGolemaggIsAssistTankTrigger::IsActive() +{ + return AI_VALUE2(Unit*, "find target", "golemagg the incinerator") && PlayerbotAI::IsAssistTank(bot); +} diff --git a/src/strategy/raids/moltencore/RaidMcTriggers.h b/src/strategy/raids/moltencore/RaidMcTriggers.h index 9d2fb985a2..4cd84a2df2 100644 --- a/src/strategy/raids/moltencore/RaidMcTriggers.h +++ b/src/strategy/raids/moltencore/RaidMcTriggers.h @@ -19,4 +19,32 @@ class McBaronGeddonInfernoTrigger : public Trigger bool IsActive() override; }; +class McShazzrahRangedTrigger : public Trigger +{ +public: + McShazzrahRangedTrigger(PlayerbotAI* botAI) : Trigger(botAI, "mc shazzrah ranged") {} + bool IsActive() override; +}; + +class McGolemaggMarkBossTrigger : public Trigger +{ +public: + McGolemaggMarkBossTrigger(PlayerbotAI* botAI) : Trigger(botAI, "mc golemagg mark boss") {} + bool IsActive() override; +}; + +class McGolemaggIsMainTankTrigger : public Trigger +{ +public: + McGolemaggIsMainTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "mc golemagg is main tank") {} + bool IsActive() override; +}; + +class McGolemaggIsAssistTankTrigger : public Trigger +{ +public: + McGolemaggIsAssistTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "mc golemagg is assist tank") {} + bool IsActive() override; +}; + #endif From dde16674c3725b2be1ee393a2d6cfb0481dee962 Mon Sep 17 00:00:00 2001 From: NoxMax <50133316+NoxMax@users.noreply.github.com> Date: Mon, 8 Dec 2025 04:34:16 -0700 Subject: [PATCH 45/47] Fix: Stop pets from fighting in PVP prohibited zones (#1829) Stripped down version of #1818. No new features. Refactors IsPossibleTarget in AttackersValue.cpp to a better style and makes sure pets don't attack in prohibited zones. Testing: Confirmed that aggro pets no longer attack in PVP prohibited areas, but still do outside them. Zim'Torga in Zul'Drak is a good example to test this (ID 4323). Lookout for death knights with a Risen Ally (uncontrolled and naturally aggro) now they respect PVP prohibition like their master. Note: If you manually teleport a bot that is in mid combat to a PVP prohibited area, its aggro pet might still attack, because its master is still in combat strategy. Otherwise the pet will not attack if its master has switched to non-combat. --- ...11_25_00_ai_playerbot_pet_action_texts.sql | 255 ++++++++++++++++++ src/strategy/actions/AttackAction.cpp | 7 +- src/strategy/actions/PetsAction.cpp | 97 +++++-- src/strategy/values/AttackersValue.cpp | 137 +++++++--- 4 files changed, 427 insertions(+), 69 deletions(-) create mode 100644 data/sql/playerbots/updates/2025_11_25_00_ai_playerbot_pet_action_texts.sql diff --git a/data/sql/playerbots/updates/2025_11_25_00_ai_playerbot_pet_action_texts.sql b/data/sql/playerbots/updates/2025_11_25_00_ai_playerbot_pet_action_texts.sql new file mode 100644 index 0000000000..b7c6c96826 --- /dev/null +++ b/data/sql/playerbots/updates/2025_11_25_00_ai_playerbot_pet_action_texts.sql @@ -0,0 +1,255 @@ +DELETE FROM ai_playerbot_texts WHERE name IN ( + 'pet_usage_error', + 'pet_no_pet_error', + 'pet_stance_report', + 'pet_no_target_error', + 'pet_target_dead_error', + 'pet_invalid_target_error', + 'pet_pvp_prohibited_error', + 'pet_attack_success', + 'pet_attack_failed', + 'pet_follow_success', + 'pet_stay_success', + 'pet_unknown_command_error', + 'pet_stance_set_success', + 'pet_type_pet', + 'pet_type_guardian', + 'pet_stance_aggressive', + 'pet_stance_defensive', + 'pet_stance_passive', + 'pet_stance_unknown' +); + +DELETE FROM ai_playerbot_texts_chance WHERE name IN ( + 'pet_usage_error', + 'pet_no_pet_error', + 'pet_stance_report', + 'pet_no_target_error', + 'pet_target_dead_error', + 'pet_invalid_target_error', + 'pet_pvp_prohibited_error', + 'pet_attack_success', + 'pet_attack_failed', + 'pet_follow_success', + 'pet_stay_success', + 'pet_unknown_command_error', + 'pet_stance_set_success', + 'pet_type_pet', + 'pet_type_guardian', + 'pet_stance_aggressive', + 'pet_stance_defensive', + 'pet_stance_passive', + 'pet_stance_unknown' +); + +INSERT INTO ai_playerbot_texts (id, name, text, say_type, reply_type, text_loc1, text_loc2, text_loc3, text_loc4, text_loc5, text_loc6, text_loc7, text_loc8) VALUES +(1717, 'pet_usage_error', "Usage: pet ", 0, 0, +"사용법: pet ", +"Utilisation: pet ", +"Verwendung: pet ", +"用法: pet ", +"用法: pet ", +"Uso: pet ", +"Uso: pet ", +"Использование: pet "), + +(1718, 'pet_no_pet_error', "You have no pet or guardian pet.", 0, 0, +"펫이나 수호자 펫이 없습니다.", +"Vous n'avez pas de familier ou gardien.", +"Du hast kein Tier oder Wächter.", +"你没有宠物或守护者宠物。", +"你沒有寵物或守護者寵物。", +"No tienes mascota o mascota guardián.", +"No tienes mascota o mascota guardián.", +"У вас нет питомца или защитника."), + +(1719, 'pet_stance_report', "Current stance of %type \"%name\": %stance.", 0, 0, +"%type \"%name\"의 현재 태세: %stance.", +"Position actuelle du %type \"%name\": %stance.", +"Aktuelle Haltung des %type \"%name\": %stance.", +"%type \"%name\" 的当前姿态: %stance。", +"%type \"%name\" 的當前姿態: %stance。", +"Postura actual del %type \"%name\": %stance.", +"Postura actual del %type \"%name\": %stance.", +"Текущая позиция %type \"%name\": %stance."), + +(1720, 'pet_no_target_error', "No valid target selected by master.", 0, 0, +"주인이 유효한 대상을 선택하지 않았습니다.", +"Aucune cible valide sélectionnée par le maître.", +"Kein gültiges Ziel vom Meister ausgewählt.", +"主人未选择有效目标。", +"主人未選擇有效目標。", +"No hay objetivo válido seleccionado por el maestro.", +"No hay objetivo válido seleccionado por el maestro.", +"Хозяин не выбрал действительную цель."), + +(1721, 'pet_target_dead_error', "Target is not alive.", 0, 0, +"대상이 살아있지 않습니다.", +"La cible n'est pas vivante.", +"Das Ziel ist nicht am Leben.", +"目标未存活。", +"目標未存活。", +"El objetivo no está vivo.", +"El objetivo no está vivo.", +"Цель не жива."), + +(1722, 'pet_invalid_target_error', "Target is not a valid attack target for the bot.", 0, 0, +"대상이 봇에게 유효한 공격 대상이 아닙니다.", +"La cible n'est pas une cible d'attaque valide pour le bot.", +"Das Ziel ist kein gültiges Angriffsziel für den Bot.", +"目标不是机器人的有效攻击目标。", +"目標不是機器人的有效攻擊目標。", +"El objetivo no es un objetivo de ataque válido para el bot.", +"El objetivo no es un objetivo de ataque válido para el bot.", +"Цель не является допустимой целью атаки для бота."), + +(1723, 'pet_pvp_prohibited_error', "I cannot command my pet to attack players in PvP prohibited areas.", 0, 0, +"PvP 금지 지역에서는 펫에게 플레이어 공격 명령을 내릴 수 없습니다.", +"Je ne peux pas commander à mon familier d'attaquer des joueurs dans les zones où le PvP est interdit.", +"Ich kann meinem Tier nicht befehlen, Spieler in PvP-verbotenen Gebieten anzugreifen.", +"我不能命令我的宠物在禁止PvP的区域攻击玩家。", +"我不能命令我的寵物在禁止PvP的區域攻擊玩家。", +"No puedo ordenar a mi mascota atacar jugadores en áreas donde el PvP está prohibido.", +"No puedo ordenar a mi mascota atacar jugadores en áreas donde el PvP está prohibido.", +"Я не могу приказать своему питомцу атаковать игроков в зонах, где PvP запрещено."), + +(1724, 'pet_attack_success', "Pet commanded to attack your target.", 0, 0, +"펫이 당신의 대상을 공격하도록 명령했습니다.", +"Le familier a reçu l'ordre d'attaquer votre cible.", +"Tier wurde befohlen, dein Ziel anzugreifen.", +"宠物已命令攻击你的目标。", +"寵物已命令攻擊你的目標。", +"Mascota ordenada a atacar tu objetivo.", +"Mascota ordenada a atacar tu objetivo.", +"Питомцу приказано атаковать вашу цель."), + +(1725, 'pet_attack_failed', "Pet did not attack. (Already attacking or unable to attack target)", 0, 0, +"펫이 공격하지 않았습니다. (이미 공격 중이거나 대상 공격 불가)", +"Le familier n'a pas attaqué. (Attaque déjà en cours ou impossible d'attaquer la cible)", +"Tier hat nicht angegriffen. (Greift bereits an oder kann Ziel nicht angreifen)", +"宠物未攻击。(已在攻击或无法攻击目标)", +"寵物未攻擊。(已在攻擊或無法攻擊目標)", +"La mascota no atacó. (Ya está atacando o no puede atacar al objetivo)", +"La mascota no atacó. (Ya está atacando o no puede atacar al objetivo)", +"Питомец не атаковал. (Уже атакует или не может атаковать цель)"), + +(1726, 'pet_follow_success', "Pet commanded to follow.", 0, 0, +"펫이 따라오도록 명령했습니다.", +"Le familier a reçu l'ordre de suivre.", +"Tier wurde befohlen zu folgen.", +"宠物已命令跟随。", +"寵物已命令跟隨。", +"Mascota ordenada a seguir.", +"Mascota ordenada a seguir.", +"Питомцу приказано следовать."), + +(1727, 'pet_stay_success', "Pet commanded to stay.", 0, 0, +"펫이 머물도록 명령했습니다.", +"Le familier a reçu l'ordre de rester.", +"Tier wurde befohlen zu bleiben.", +"宠物已命令停留。", +"寵物已命令停留。", +"Mascota ordenada a quedarse.", +"Mascota ordenada a quedarse.", +"Питомцу приказано остаться."), + +(1728, 'pet_unknown_command_error', "Unknown pet command: %param. Use: pet ", 0, 0, +"알 수 없는 펫 명령: %param. 사용법: pet ", +"Commande de familier inconnue: %param. Utilisation: pet ", +"Unbekannter Tierbefehl: %param. Verwendung: pet ", +"未知宠物命令: %param。用法: pet ", +"未知寵物命令: %param。用法: pet ", +"Comando de mascota desconocido: %param. Uso: pet ", +"Comando de mascota desconocido: %param. Uso: pet ", +"Неизвестная команда питомца: %param. Использование: pet "), + +(1729, 'pet_stance_set_success', "Pet stance set to %stance.", 0, 0, +"펫 태세가 %stance(으)로 설정되었습니다.", +"Position du familier définie sur %stance.", +"Tierhaltung auf %stance gesetzt.", +"宠物姿态设置为 %stance。", +"寵物姿態設置為 %stance。", +"Postura de mascota establecida en %stance.", +"Postura de mascota establecida en %stance.", +"Позиция питомца установлена на %stance."), + +(1730, 'pet_type_pet', "pet", 0, 0, +"펫", +"familier", +"Tier", +"宠物", +"寵物", +"mascota", +"mascota", +"питомец"), + +(1731, 'pet_type_guardian', "guardian", 0, 0, +"수호자", +"gardien", +"Wächter", +"守护者", +"守護者", +"guardián", +"guardián", +"защитник"), + +(1732, 'pet_stance_aggressive', "aggressive", 0, 0, +"공격적", +"agressif", +"aggressiv", +"进攻", +"進攻", +"agresivo", +"agresivo", +"агрессивная"), + +(1733, 'pet_stance_defensive', "defensive", 0, 0, +"방어적", +"défensif", +"defensiv", +"防御", +"防禦", +"defensivo", +"defensivo", +"защитная"), + +(1734, 'pet_stance_passive', "passive", 0, 0, +"수동적", +"passif", +"passiv", +"被动", +"被動", +"pasivo", +"pasivo", +"пассивная"), + +(1735, 'pet_stance_unknown', "unknown", 0, 0, +"알 수 없음", +"inconnu", +"unbekannt", +"未知", +"未知", +"desconocido", +"desconocido", +"неизвестная"); + +INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES +('pet_usage_error', 100), +('pet_no_pet_error', 100), +('pet_stance_report', 100), +('pet_no_target_error', 100), +('pet_target_dead_error', 100), +('pet_invalid_target_error', 100), +('pet_pvp_prohibited_error', 100), +('pet_attack_success', 100), +('pet_attack_failed', 100), +('pet_follow_success', 100), +('pet_stay_success', 100), +('pet_unknown_command_error', 100), +('pet_stance_set_success', 100), +('pet_type_pet', 100), +('pet_type_guardian', 100), +('pet_stance_aggressive', 100), +('pet_stance_defensive', 100), +('pet_stance_passive', 100), +('pet_stance_unknown', 100); \ No newline at end of file diff --git a/src/strategy/actions/AttackAction.cpp b/src/strategy/actions/AttackAction.cpp index 4ea8694378..38cb96835f 100644 --- a/src/strategy/actions/AttackAction.cpp +++ b/src/strategy/actions/AttackAction.cpp @@ -84,9 +84,10 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/) return false; } - if ((sPlayerbotAIConfig->IsInPvpProhibitedZone(bot->GetZoneId()) || - sPlayerbotAIConfig->IsInPvpProhibitedArea(bot->GetAreaId())) - && (target->IsPlayer() || target->IsPet())) + // Check if bot OR target is in prohibited zone/area + if ((target->IsPlayer() || target->IsPet()) && + (sPlayerbotAIConfig->IsPvpProhibited(bot->GetZoneId(), bot->GetAreaId()) || + sPlayerbotAIConfig->IsPvpProhibited(target->GetZoneId(), target->GetAreaId()))) { if (verbose) botAI->TellError("I cannot attack other players in PvP prohibited areas."); diff --git a/src/strategy/actions/PetsAction.cpp b/src/strategy/actions/PetsAction.cpp index 945a883672..9e9e3172c4 100644 --- a/src/strategy/actions/PetsAction.cpp +++ b/src/strategy/actions/PetsAction.cpp @@ -25,7 +25,9 @@ bool PetsAction::Execute(Event event) if (param.empty()) { // If no parameter is provided, show usage instructions and return. - botAI->TellError("Usage: pet "); + std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( + "pet_usage_error", "Usage: pet ", {}); + botAI->TellError(text); return false; } @@ -52,7 +54,9 @@ bool PetsAction::Execute(Event event) // If no pets or guardians are found, notify and return. if (targets.empty()) { - botAI->TellError("You have no pet or guardian pet."); + std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( + "pet_no_pet_error", "You have no pet or guardian pet.", {}); + botAI->TellError(text); return false; } @@ -63,42 +67,54 @@ bool PetsAction::Execute(Event event) if (param == "aggressive") { react = REACT_AGGRESSIVE; - stanceText = "aggressive"; + stanceText = sPlayerbotTextMgr->GetBotTextOrDefault( + "pet_stance_aggressive", "aggressive", {}); } else if (param == "defensive") { react = REACT_DEFENSIVE; - stanceText = "defensive"; + stanceText = sPlayerbotTextMgr->GetBotTextOrDefault( + "pet_stance_defensive", "defensive", {}); } else if (param == "passive") { react = REACT_PASSIVE; - stanceText = "passive"; + stanceText = sPlayerbotTextMgr->GetBotTextOrDefault( + "pet_stance_passive", "passive", {}); } // The "stance" command simply reports the current stance of each pet/guardian. else if (param == "stance") { for (Creature* target : targets) { - std::string type = target->IsPet() ? "pet" : "guardian"; + std::string type = target->IsPet() ? + sPlayerbotTextMgr->GetBotTextOrDefault("pet_type_pet", "pet", {}) : + sPlayerbotTextMgr->GetBotTextOrDefault("pet_type_guardian", "guardian", {}); std::string name = target->GetName(); std::string stance; switch (target->GetReactState()) { case REACT_AGGRESSIVE: - stance = "aggressive"; + stance = sPlayerbotTextMgr->GetBotTextOrDefault( + "pet_stance_aggressive", "aggressive", {}); break; case REACT_DEFENSIVE: - stance = "defensive"; + stance = sPlayerbotTextMgr->GetBotTextOrDefault( + "pet_stance_defensive", "defensive", {}); break; case REACT_PASSIVE: - stance = "passive"; + stance = sPlayerbotTextMgr->GetBotTextOrDefault( + "pet_stance_passive", "passive", {}); break; default: - stance = "unknown"; + stance = sPlayerbotTextMgr->GetBotTextOrDefault( + "pet_stance_unknown", "unknown", {}); break; } - botAI->TellMaster("Current stance of " + type + " \"" + name + "\": " + stance + "."); + std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( + "pet_stance_report", "Current stance of %type \"%name\": %stance.", + {{"type", type}, {"name", name}, {"stance", stance}}); + botAI->TellMaster(text); } return true; } @@ -121,17 +137,31 @@ bool PetsAction::Execute(Event event) // If no valid target is selected, show an error and return. if (!targetUnit) { - botAI->TellError("No valid target selected by master."); + std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( + "pet_no_target_error", "No valid target selected by master.", {}); + botAI->TellError(text); return false; } if (!targetUnit->IsAlive()) { - botAI->TellError("Target is not alive."); + std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( + "pet_target_dead_error", "Target is not alive.", {}); + botAI->TellError(text); return false; } if (!bot->IsValidAttackTarget(targetUnit)) { - botAI->TellError("Target is not a valid attack target for the bot."); + std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( + "pet_invalid_target_error", "Target is not a valid attack target for the bot.", {}); + botAI->TellError(text); + return false; + } + if (sPlayerbotAIConfig->IsPvpProhibited(bot->GetZoneId(), bot->GetAreaId()) + && (targetUnit->IsPlayer() || targetUnit->IsPet())) + { + std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( + "pet_pvp_prohibited_error", "I cannot command my pet to attack players in PvP prohibited areas.", {}); + botAI->TellError(text); return false; } @@ -182,9 +212,17 @@ bool PetsAction::Execute(Event event) } // Inform the master if the command succeeded or failed. if (didAttack && sPlayerbotAIConfig->petChatCommandDebug == 1) - botAI->TellMaster("Pet commanded to attack your target."); + { + std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( + "pet_attack_success", "Pet commanded to attack your target.", {}); + botAI->TellMaster(text); + } else if (!didAttack) - botAI->TellError("Pet did not attack. (Already attacking or unable to attack target)"); + { + std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( + "pet_attack_failed", "Pet did not attack. (Already attacking or unable to attack target)", {}); + botAI->TellError(text); + } return didAttack; } // The "follow" command makes all pets/guardians follow the bot. @@ -192,7 +230,11 @@ bool PetsAction::Execute(Event event) { botAI->PetFollow(); if (sPlayerbotAIConfig->petChatCommandDebug == 1) - botAI->TellMaster("Pet commanded to follow."); + { + std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( + "pet_follow_success", "Pet commanded to follow.", {}); + botAI->TellMaster(text); + } return true; } // The "stay" command causes all pets/guardians to stop and stay in place. @@ -229,14 +271,20 @@ bool PetsAction::Execute(Event event) } } if (sPlayerbotAIConfig->petChatCommandDebug == 1) - botAI->TellMaster("Pet commanded to stay."); + { + std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( + "pet_stay_success", "Pet commanded to stay.", {}); + botAI->TellMaster(text); + } return true; } // Unknown command: show usage instructions and return. else { - botAI->TellError("Unknown pet command: " + param + - ". Use: pet "); + std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( + "pet_unknown_command_error", "Unknown pet command: %param. Use: pet ", + {{"param", param}}); + botAI->TellError(text); return false; } @@ -251,7 +299,12 @@ bool PetsAction::Execute(Event event) // Inform the master of the new stance if debug is enabled. if (sPlayerbotAIConfig->petChatCommandDebug == 1) - botAI->TellMaster("Pet stance set to " + stanceText + "."); + { + std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( + "pet_stance_set_success", "Pet stance set to %stance.", + {{"stance", stanceText}}); + botAI->TellMaster(text); + } return true; -} +} \ No newline at end of file diff --git a/src/strategy/values/AttackersValue.cpp b/src/strategy/values/AttackersValue.cpp index 745369a3b5..d89f1f8da5 100644 --- a/src/strategy/values/AttackersValue.cpp +++ b/src/strategy/values/AttackersValue.cpp @@ -107,7 +107,6 @@ void AttackersValue::AddAttackersOf(Player* player, std::unordered_set& t { ThreatMgr* threatMgr = ref->GetSource(); Unit* attacker = threatMgr->GetOwner(); - Unit* victim = attacker->GetVictim(); if (player->IsValidAttackTarget(attacker) && player->GetDistance2d(attacker) < sPlayerbotAIConfig->sightDistance) @@ -142,57 +141,107 @@ bool AttackersValue::hasRealThreat(Unit* attacker) (attacker->GetThreatMgr().getCurrentVictim() || dynamic_cast(attacker)); } -bool AttackersValue::IsPossibleTarget(Unit* attacker, Player* bot, float range) +bool AttackersValue::IsPossibleTarget(Unit* attacker, Player* bot, float /*range*/) { - Creature* c = attacker->ToCreature(); - bool rti = false; - if (attacker && bot->GetGroup()) - rti = bot->GetGroup()->GetTargetIcon(7) == attacker->GetGUID(); - PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); + if (!botAI) + return false; + + // Basic check + if (!attacker) + return false; + + // bool inCannon = botAI->IsInVehicle(false, true); + // bool enemy = botAI->GetAiObjectContext()->GetValue("enemy player target")->Get(); + + // Validity checks + if (!attacker->IsVisible() || !attacker->IsInWorld() || attacker->GetMapId() != bot->GetMapId()) + return false; - bool leaderHasThreat = false; - if (attacker && bot->GetGroup() && botAI->GetMaster()) - leaderHasThreat = attacker->GetThreatMgr().GetThreat(botAI->GetMaster()); + if (attacker->isDead() || attacker->HasSpiritOfRedemptionAura()) + return false; - bool isMemberBotGroup = false; - if (bot->GetGroup() && botAI->GetMaster()) + // Flag checks + if (attacker->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NON_ATTACKABLE_2)) + return false; + + if (attacker->HasUnitFlag(UNIT_FLAG_IMMUNE_TO_PC) || attacker->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE)) + return false; + + // Relationship checks + if (attacker->IsFriendlyTo(bot)) + return false; + + // Critter exception + if (attacker->GetCreatureType() == CREATURE_TYPE_CRITTER && !attacker->IsInCombat()) + return false; + + // Visibility check + if (!bot->CanSeeOrDetect(attacker)) + return false; + + // PvP prohibition checks + if ((attacker->GetGUID().IsPlayer() || attacker->GetGUID().IsPet()) && + (sPlayerbotAIConfig->IsPvpProhibited(attacker->GetZoneId(), attacker->GetAreaId()) || + sPlayerbotAIConfig->IsPvpProhibited(bot->GetZoneId(), bot->GetAreaId()))) { - PlayerbotAI* masterBotAI = GET_PLAYERBOT_AI(botAI->GetMaster()); - if (masterBotAI && !masterBotAI->IsRealPlayer()) - isMemberBotGroup = true; + // This will stop aggresive pets from starting an attack. + // This will stop currently attacking pets from continuing their attack. + // This will first require the bot to change from a combat strat. It will + // not be reached if the bot only switches targets, including NPC targets. + for (Unit::ControlSet::const_iterator itr = bot->m_Controlled.begin(); + itr != bot->m_Controlled.end(); ++itr) + { + Creature* creature = dynamic_cast(*itr); + if (creature && creature->GetVictim() == attacker) + { + creature->AttackStop(); + if (CharmInfo* charmInfo = creature->GetCharmInfo()) + charmInfo->SetIsCommandAttack(false); + } + } + + return false; } - // bool inCannon = botAI->IsInVehicle(false, true); - // bool enemy = botAI->GetAiObjectContext()->GetValue("enemy player target")->Get(); + // Unflagged player check + if (attacker->IsPlayer() && !attacker->IsPvP() && !attacker->IsFFAPvP() && + (!bot->duel || bot->duel->Opponent != attacker)) + return false; + + // Creature-specific checks + Creature* c = attacker->ToCreature(); + if (c) + { + if (c->IsInEvadeMode()) + return false; + + bool leaderHasThreat = false; + if (bot->GetGroup() && botAI->GetMaster()) + leaderHasThreat = attacker->GetThreatMgr().GetThreat(botAI->GetMaster()); + + bool isMemberBotGroup = false; + if (bot->GetGroup() && botAI->GetMaster()) + { + PlayerbotAI* masterBotAI = GET_PLAYERBOT_AI(botAI->GetMaster()); + if (masterBotAI && !masterBotAI->IsRealPlayer()) + isMemberBotGroup = true; + } + + bool canAttack = (!isMemberBotGroup && botAI->HasStrategy("attack tagged", BOT_STATE_NON_COMBAT)) || + leaderHasThreat || + (!c->hasLootRecipient() && + (!c->GetVictim() || + (c->GetVictim() && + ((!c->GetVictim()->IsPlayer() || bot->IsInSameGroupWith(c->GetVictim()->ToPlayer())) || + (botAI->GetMaster() && c->GetVictim() == botAI->GetMaster()))))) || + c->isTappedBy(bot); + + if (!canAttack) + return false; + } - return attacker && attacker->IsVisible() && attacker->IsInWorld() && attacker->GetMapId() == bot->GetMapId() && - !attacker->isDead() && - !attacker->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NON_ATTACKABLE_2) && - // (inCannon || !attacker->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) && - // attacker->CanSeeOrDetect(bot) && - // !(attacker->HasUnitState(UNIT_STATE_STUNNED) && botAI->HasAura("shackle undead", attacker)) && - // !((attacker->IsPolymorphed() || botAI->HasAura("sap", attacker) || /*attacker->IsCharmed() ||*/ - // attacker->isFeared()) && !rti) && - /*!sServerFacade->IsInRoots(attacker) &&*/ - !attacker->IsFriendlyTo(bot) && !attacker->HasSpiritOfRedemptionAura() && - // !(attacker->GetGUID().IsPet() && enemy) && - !(attacker->GetCreatureType() == CREATURE_TYPE_CRITTER && !attacker->IsInCombat()) && - !attacker->HasUnitFlag(UNIT_FLAG_IMMUNE_TO_PC) && !attacker->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE) && - bot->CanSeeOrDetect(attacker) && - !(sPlayerbotAIConfig->IsPvpProhibited(attacker->GetZoneId(), attacker->GetAreaId()) && - (attacker->GetGUID().IsPlayer() || attacker->GetGUID().IsPet())) && - !(attacker->IsPlayer() && !attacker->IsPvP() && !attacker->IsFFAPvP() && - (!bot->duel || bot->duel->Opponent != attacker)) && - (!c || - (!c->IsInEvadeMode() && - ((!isMemberBotGroup && botAI->HasStrategy("attack tagged", BOT_STATE_NON_COMBAT)) || leaderHasThreat || - (!c->hasLootRecipient() && - (!c->GetVictim() || - (c->GetVictim() && - ((!c->GetVictim()->IsPlayer() || bot->IsInSameGroupWith(c->GetVictim()->ToPlayer())) || - (botAI->GetMaster() && c->GetVictim() == botAI->GetMaster()))))) || - c->isTappedBy(bot)))); + return true; } bool AttackersValue::IsValidTarget(Unit* attacker, Player* bot) From bb569b4d3987b7f15e702620eedb22d2ed5cc269 Mon Sep 17 00:00:00 2001 From: Tecc Date: Mon, 8 Dec 2025 12:35:06 +0100 Subject: [PATCH 46/47] Fix: Arena - PersonalRating and MMR issue for bot teams (#1789) # Fix: Arena PersonalRating and MMR issue for bot teams ## Problem Bot arena teams are created with artificial random ratings (1000-2000 range), but when bots join these teams, their personal ratings and matchmaker ratings (MMR) use default config values instead of being adjusted to match the team's artificial rating. This causes matchmaking issues since the system uses personal ratings for queue calculations. ## Root Cause The issue occurred because `SetRatingForAll()` was called during team creation but only affected the captain. When additional bots were added later via `AddMember()`, they received default values from `CONFIG_ARENA_START_PERSONAL_RATING` and `CONFIG_ARENA_START_MATCHMAKER_RATING` instead of values appropriate for the team's artificial rating. ## Solution After bots are added to arena teams, the fix: 1. Uses `SetRatingForAll()` to align all personal ratings with team rating 2. Adjusts matchmaker ratings based on team context vs default configuration 3. Saves changes to both database tables with proper data types ## Impact - Personal ratings now match team ratings for artificial bot teams - MMR values are adjusted for artificial bot team ratings instead of using default config values - Arena matchmaking functions correctly for bot teams with random ratings - Only affects new arena team assignments after deployment - Existing player teams and normal config behavior are unaffected ## Manual Database Update For existing installations, the provided SQL script could be used to fix bot teams created before this patch. ### Update personal rating ```sql UPDATE arena_team_member atm JOIN arena_team at ON atm.arenaTeamId = at.arenaTeamId JOIN characters c ON atm.guid = c.guid JOIN auth.account a ON c.account = a.id SET atm.personalRating = at.rating WHERE a.username LIKE 'rndbot%' AND atm.personalRating != at.rating; ``` ### Update MMR for existing entries ```sql UPDATE character_arena_stats cas JOIN characters c ON cas.guid = c.guid JOIN auth.account a ON c.account = a.id JOIN arena_team_member atm ON cas.guid = atm.guid JOIN arena_team at ON atm.arenaTeamId = at.arenaTeamId SET cas.matchMakerRating = GREATEST(at.rating, 1500), -- Use team rating or 1500 minimum cas.maxMMR = GREATEST(cas.maxMMR, cas.matchMakerRating) -- Update maxMMR if needed WHERE a.username LIKE '%rndbot%' AND ( -- Update if MMR doesn't match team context (at.rating > 1500 AND cas.matchMakerRating < at.rating) OR (at.rating <= 1500 AND cas.matchMakerRating != 1500) OR cas.matchMakerRating IS NULL ) AND ( -- Map arena team type to character_arena_stats slot (at.type = 2 AND cas.slot = 0) OR -- 2v2 teams use slot 0 (at.type = 3 AND cas.slot = 1) OR -- 3v3 teams use slot 1 (at.type = 5 AND cas.slot = 2) -- 5v5 teams use slot 2 ); ``` ### Insert missing MMR records for bots without character_arena_stats entries ```sql INSERT INTO character_arena_stats (guid, slot, matchMakerRating, maxMMR) SELECT atm.guid, CASE WHEN at.type = 2 THEN 0 -- 2v2 -> slot 0 WHEN at.type = 3 THEN 1 -- 3v3 -> slot 1 WHEN at.type = 5 THEN 2 -- 5v5 -> slot 2 ELSE 0 END as slot, GREATEST(at.rating, 1500) as matchMakerRating, GREATEST(at.rating, 1500) as maxMMR FROM arena_team_member atm JOIN arena_team at ON atm.arenaTeamId = at.arenaTeamId JOIN characters c ON atm.guid = c.guid JOIN auth.account a ON c.account = a.id WHERE a.username LIKE '%rndbot%' AND NOT EXISTS ( SELECT 1 FROM character_arena_stats cas2 WHERE cas2.guid = atm.guid AND cas2.slot = CASE WHEN at.type = 2 THEN 0 WHEN at.type = 3 THEN 1 WHEN at.type = 5 THEN 2 ELSE 0 END ) AND at.rating > 0; ``` ## Related issues Fixes: #1787 Fixes: #1800 ## Verification Queries ### Query 1: Check personal rating alignment ```sql SELECT 'Personal Rating Check' as check_type, COUNT(*) as total_bot_members, SUM(CASE WHEN atm.personalRating = at.rating THEN 1 ELSE 0 END) as correct_ratings, SUM(CASE WHEN atm.personalRating != at.rating THEN 1 ELSE 0 END) as incorrect_ratings, ROUND(AVG(at.rating), 2) as avg_team_rating, ROUND(AVG(atm.personalRating), 2) as avg_personal_rating FROM arena_team_member atm JOIN arena_team at ON atm.arenaTeamId = at.arenaTeamId JOIN characters c ON atm.guid = c.guid JOIN auth.account a ON c.account = a.id WHERE a.username LIKE '%rndbot%'; ``` ### Query 2: Check MMR alignment ```sql SELECT 'MMR Alignment Check' as check_type, COUNT(*) as total_mmr_records, SUM(CASE WHEN at.rating > 1500 AND cas.matchMakerRating >= at.rating THEN 1 WHEN at.rating <= 1500 AND cas.matchMakerRating = 1500 THEN 1 ELSE 0 END) as correct_mmr, SUM(CASE WHEN at.rating > 1500 AND cas.matchMakerRating < at.rating THEN 1 WHEN at.rating <= 1500 AND cas.matchMakerRating != 1500 THEN 1 ELSE 0 END) as incorrect_mmr, ROUND(AVG(at.rating), 2) as avg_team_rating, ROUND(AVG(cas.matchMakerRating), 2) as avg_mmr, ROUND(AVG(cas.maxMMR), 2) as avg_max_mmr FROM arena_team_member atm JOIN arena_team at ON atm.arenaTeamId = at.arenaTeamId JOIN characters c ON atm.guid = c.guid JOIN auth.account a ON c.account = a.id JOIN character_arena_stats cas ON atm.guid = cas.guid WHERE a.username LIKE '%rndbot%' AND ( (at.type = 2 AND cas.slot = 0) OR (at.type = 3 AND cas.slot = 1) OR (at.type = 5 AND cas.slot = 2) ); ``` ### Query 3: Detailed team-by-team analysis ```sql SELECT at.arenaTeamId, at.name as team_name, at.type as team_type, at.rating as team_rating, COUNT(atm.guid) as member_count, GROUP_CONCAT(DISTINCT atm.personalRating) as personal_ratings, GROUP_CONCAT(DISTINCT cas.matchMakerRating) as mmr_values, CASE WHEN COUNT(DISTINCT atm.personalRating) = 1 AND MIN(atm.personalRating) = at.rating THEN 'OK' ELSE 'MISMATCH' END as personal_rating_status, CASE WHEN COUNT(DISTINCT cas.matchMakerRating) = 1 AND ( (at.rating > 1500 AND MIN(cas.matchMakerRating) >= at.rating) OR (at.rating <= 1500 AND MIN(cas.matchMakerRating) = 1500) ) THEN 'OK' ELSE 'MISMATCH' END as mmr_status FROM arena_team at JOIN arena_team_member atm ON at.arenaTeamId = atm.arenaTeamId JOIN characters c ON atm.guid = c.guid JOIN auth.account a ON c.account = a.id LEFT JOIN character_arena_stats cas ON atm.guid = cas.guid AND cas.slot = CASE WHEN at.type = 2 THEN 0 WHEN at.type = 3 THEN 1 WHEN at.type = 5 THEN 2 ELSE 0 END WHERE a.username LIKE '%rndbot%' GROUP BY at.arenaTeamId, at.name, at.type, at.rating ORDER BY at.rating DESC; ``` --- src/factory/PlayerbotFactory.cpp | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index 65ffc444ee..50a216c970 100644 --- a/src/factory/PlayerbotFactory.cpp +++ b/src/factory/PlayerbotFactory.cpp @@ -4099,6 +4099,7 @@ void PlayerbotFactory::InitImmersive() void PlayerbotFactory::InitArenaTeam() { + if (!sPlayerbotAIConfig->IsInRandomAccountList(bot->GetSession()->GetAccountId())) return; @@ -4185,10 +4186,34 @@ void PlayerbotFactory::InitArenaTeam() if (botcaptain && botcaptain->GetTeamId() == bot->GetTeamId()) // need? { + // Add bot to arena team arenateam->AddMember(bot->GetGUID()); - arenateam->SaveToDB(); + + // Only synchronize ratings once the team is full (avoid redundant work) + // The captain was added with incorrect ratings when the team was created, + // so we fix everyone's ratings once the roster is complete + if (arenateam->GetMembersSize() >= (uint32)arenateam->GetType()) + { + uint32 teamRating = arenateam->GetRating(); + + // Use SetRatingForAll to align all members with team rating + arenateam->SetRatingForAll(teamRating); + + // For bot-only teams, keep MMR synchronized with team rating + // This ensures matchmaking reflects the artificial team strength (1000-2000 range) + // instead of being influenced by the global CONFIG_ARENA_START_MATCHMAKER_RATING + for (auto& member : arenateam->GetMembers()) + { + // Set MMR to match personal rating (which already matches team rating) + member.MatchMakerRating = member.PersonalRating; + member.MaxMMR = std::max(member.MaxMMR, member.PersonalRating); + } + // Force save all member data to database + arenateam->SaveToDB(true); + } } } + arenateams.erase(arenateams.begin() + index); } From 910b8a9c53bed15e4504bd7de965db68e6f011ef Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Tue, 9 Dec 2025 18:29:57 +0000 Subject: [PATCH 47/47] fix: Made bots roll in a more reasonable time on group loots. (#1857) # Description This PR changes the way loot rolls are being evaluated. It puts a maximum priority on the loot action so it does not hang for so long. --- src/PlayerbotAI.cpp | 3 --- src/strategy/generic/WorldPacketHandlerStrategy.cpp | 4 +--- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 2ef6860a2a..c1878632c6 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -1384,9 +1384,6 @@ void PlayerbotAI::DoNextAction(bool min) else if (bot->isAFK()) bot->ToggleAFK(); - Group* group = bot->GetGroup(); - PlayerbotAI* masterBotAI = nullptr; - if (master && master->IsInWorld()) { float distance = sServerFacade->GetDistance2d(bot, master); diff --git a/src/strategy/generic/WorldPacketHandlerStrategy.cpp b/src/strategy/generic/WorldPacketHandlerStrategy.cpp index 69b7685e2f..97b1ba1ba8 100644 --- a/src/strategy/generic/WorldPacketHandlerStrategy.cpp +++ b/src/strategy/generic/WorldPacketHandlerStrategy.cpp @@ -5,8 +5,6 @@ #include "WorldPacketHandlerStrategy.h" -#include "Playerbots.h" - void WorldPacketHandlerStrategy::InitTriggers(std::vector& triggers) { PassTroughStrategy::InitTriggers(triggers); @@ -69,7 +67,7 @@ void WorldPacketHandlerStrategy::InitTriggers(std::vector& trigger triggers.push_back(new TriggerNode("questgiver quest details", NextAction::array(0, new NextAction("turn in query quest", relevance), nullptr))); // loot roll - triggers.push_back(new TriggerNode("very often", NextAction::array(0, new NextAction("loot roll", 10.0f), nullptr))); + triggers.push_back(new TriggerNode("very often", NextAction::array(0, new NextAction("loot roll", relevance), nullptr))); } WorldPacketHandlerStrategy::WorldPacketHandlerStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI)