diff --git a/.github/workflows/core_build.yml b/.github/workflows/core_build.yml index fe4802e5..4711f923 100644 --- a/.github/workflows/core_build.yml +++ b/.github/workflows/core_build.yml @@ -50,18 +50,17 @@ jobs: path: 'modules/mod-playerbots' - name: Cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: - path: | - /var/cache/apt - /var/lib/apt - key: ccache:${{ matrix.os }}:${{ github.ref }}:${{ github.sha }} + path: ${{ github.workspace }}/var/ccache + key: ccache:${{ matrix.os }}:${{ matrix.c_compiler }}:${{ github.ref }}:${{ github.sha }} restore-keys: | - ccache:${{ matrix.os }}:${{ github.ref }} + ccache:${{ matrix.os }}:${{ matrix.c_compiler }}:${{ github.ref }} + ccache:${{ matrix.os }}:${{ matrix.c_compiler }} ccache:${{ matrix.os }} - name: Install Requirements - run: sudo apt-get update && sudo apt-get install git cmake make gcc g++ clang libmysqlclient-dev libssl-dev libbz2-dev libreadline-dev libncurses-dev mysql-server libboost-all-dev + run: sudo apt-get update && sudo apt-get install ccache git cmake make gcc g++ clang libmysqlclient-dev libssl-dev libbz2-dev libreadline-dev libncurses-dev mysql-server libboost-all-dev - name: Configure CMake run: > diff --git a/.github/workflows/windows_build.yml b/.github/workflows/windows_build.yml index d6829895..48d33ba2 100644 --- a/.github/workflows/windows_build.yml +++ b/.github/workflows/windows_build.yml @@ -26,14 +26,8 @@ jobs: with: repository: 'liyunfan1223/mod-playerbots' path: 'modules/mod-playerbots' - - name: Cache - uses: actions/cache@v3 - with: - path: C:\ProgramData\chocolatey\cache - key: ccache:${{ matrix.os }}:${{ github.ref }}:${{ github.sha }} - restore-keys: | - ccache:${{ matrix.os }}:${{ github.ref }} - ccache:${{ matrix.os }} + - name: ccache + uses: hendrikmuhs/ccache-action@v1.2.13 - name: Configure OS shell: bash env: diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index d10f0348..496ea5d7 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -125,12 +125,12 @@ AiPlayerbot.BotAutologin = 0 # Default: 0 (disabled) AiPlayerbot.AllowPlayerBots = 0 -# Bots will be summoned to player when accept group invitation -AiPlayerbot.SummonWhenGroup = 1 - # Allow/deny bots from your guild AiPlayerbot.AllowGuildBots = 1 +# Bots will be summoned to player when accept group invitation +AiPlayerbot.SummonWhenGroup = 1 + # Added following config # Selfbot permission level (0 = disabled, 1 = gm only (default), 2 = all players, 3 = activate on login) AiPlayerbot.SelfBotLevel = 1 @@ -172,8 +172,8 @@ AiPlayerbot.AllowSummonWhenMasterIsDead = 1 # default: 1 (always) AiPlayerbot.AllowSummonWhenBotIsDead = 1 -# Enable/Disable reviving the bots when summoning them -# default: 1 (enable) +# Enable/Disable reviving the bots when summoning them (0 = disable, 1 = disable in combat, 2 = enable) +# default: 1 (disable in combat) AiPlayerbot.ReviveBotWhenSummoned = 1 # Enable/Disable bot repair gear when summon (0 = no, 1 = yes) @@ -541,7 +541,7 @@ AiPlayerbot.EquipmentPersistence = 0 # default: 80 AiPlayerbot.EquipmentPersistenceLevel = 80 -# Bot automatically upgrade equipments on levelup +# Randombots automatically upgrade equipments on levelup # Default: 1 (enabled) AiPlayerbot.AutoUpgradeEquip = 1 @@ -578,15 +578,15 @@ AiPlayerbot.RandomBotQuestItems = "6948,5175,5176,5177,5178,16309,12382,13704,11 # # -# Bots automatically learn classquest reward spells on levelup +# Randombots automatically learn classquest reward spells on levelup # Default: 0 (disabled) AiPlayerbot.AutoLearnQuestSpells = 0 -# Bots automatically learn trainable spells on levelup +# Randombots automatically learn trainable spells on levelup # Default: 1 (enabled) AiPlayerbot.AutoLearnTrainerSpells = 1 -# Bot automatically picks talent points on levelup +# Randombots automatically picks talent points on levelup # Default: 1 (enabled) AiPlayerbot.AutoPickTalents = 1 @@ -635,7 +635,6 @@ AiPlayerbot.RandomBotTeleLowerLevel = 3 AiPlayerbot.RandomBotTeleHigherLevel = 1 # Bots automatically teleport to another place for leveling on levelup -# Only for random bots # Default: 1 (enabled) AiPlayerbot.AutoTeleportForLevel = 1 @@ -900,8 +899,8 @@ AiPlayerbot.PremadeSpecLink.8.1.60 = -0055030011302231053120321341 AiPlayerbot.PremadeSpecLink.8.1.80 = 23000503110003-0055030011302331053120321351 AiPlayerbot.PremadeSpecName.8.2 = frost pve AiPlayerbot.PremadeSpecGlyph.8.2 = 42742,43339,50045,43364,43361,42751 -AiPlayerbot.PremadeSpecLink.8.2.60 = --3533103310203100232102231151 -AiPlayerbot.PremadeSpecLink.8.2.80 = 23002322010203--0533003313203100232112231151 +AiPlayerbot.PremadeSpecLink.8.2.60 = --0533030313203100030152231151 +AiPlayerbot.PremadeSpecLink.8.2.80 = 23002303110003--0533030313203100030152231351 # # @@ -1239,6 +1238,19 @@ AiPlayerbot.CommandPrefix = "" # Separator for bot chat commands AiPlayerbot.CommandSeparator = "\\\\" +# Enable playerbot talk (say / yell / general chatting / lfg) +AiPlayerbot.RandomBotTalk = 0 + +# Enable playerbot emote +AiPlayerbot.RandomBotEmote = 0 + +# Enable dungeon suggestions for random bots +AiPlayerbot.RandomBotSuggestDungeons = 1 + +# Bots greet to the players +AiPlayerbot.EnableGreet = 0 + + # # # @@ -1277,9 +1289,6 @@ AiPlayerbot.RandomBotLoginAtStartup = 1 # Guild Task system AiPlayerbot.EnableGuildTasks = 0 -# Enable dungeon suggestions for random bots -AiPlayerbot.RandomBotSuggestDungeons = 1 - # Enable dungeon suggestions in lower case randomly AiPlayerbot.SuggestDungeonsInLowerCaseRandomly = 0 @@ -1290,7 +1299,7 @@ AiPlayerbot.RandomBotGuildCount = 20 AiPlayerbot.DeleteRandomBotGuilds = 0 # Chance bot chooses RPG (Teleport to random camp for their level) instead of grinding -AiPlayerbot.RandomBotRpgChance = 0.20 #unused now +AiPlayerbot.RandomBotRpgChance = 0.20 # Set randombots movement speed to walking anywhere AiPlayerbot.RandombotsWalkingRPG = 0 @@ -1298,9 +1307,6 @@ AiPlayerbot.RandombotsWalkingRPG = 0 # Set randombots movement speed to walking only inside buildings AiPlayerbot.RandombotsWalkingRPG.InDoors = 0 -# Bots greet to the players -AiPlayerbot.EnableGreet = 0 - # Specify percent of active bots # The default is 10. With 10% of all bots going active or inactive each minute. AiPlayerbot.BotActiveAlone = 100 diff --git a/sql/playerbots/base/playerbots_text.sql b/sql/playerbots/base/playerbots_text.sql index 73d1967e..e4a3c6bf 100644 --- a/sql/playerbots/base/playerbots_text.sql +++ b/sql/playerbots/base/playerbots_text.sql @@ -18,27 +18,27 @@ CREATE TABLE IF NOT EXISTS `ai_playerbot_texts` ( /*!40000 ALTER TABLE `ai_playerbot_texts` DISABLE KEYS */; 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 - (1, 'suggest_instance', 'Anyone wants %instance?', 0, 0, '', 'Quelqu\'un fait %instance ?', '', '', '', '', '', ''), - (2, 'suggest_instance', 'Any groups for %instance?', 0, 0, '', 'Des groupes pour %instance ?', '', '', '', '', '', ''), - (3, 'suggest_instance', 'Need help for %instance?', 0, 0, '', 'Besoin d\'aide pour %instance ?', '', '', '', '', '', ''), - (4, 'suggest_instance', 'LFD: %instance.', 0, 0, '', 'Cherche à faire : %instance', '', '', '', '', '', ''), - (5, 'suggest_instance', 'Anyone needs %role for %instance?', 0, 0, '', 'Quelqu\'un a besoin de %role pour %instance ?', '', '', '', '', '', ''), - (6, 'suggest_instance', 'Missing %role for %instance?', 0, 0, '', '%role manquant pour %instance ?', '', '', '', '', '', ''), - (7, 'suggest_instance', 'Can be a %role for %instance.', 0, 0, '', 'Peut être un %role pour %instance', '', '', '', '', '', ''), - (8, 'suggest_instance', 'Need help with %instance?', 0, 0, '', 'Besoin d\'aide avec %instance ?', '', '', '', '', '', ''), - (9, 'suggest_instance', 'Need %role help with %instance?', 0, 0, '', 'Besoin d\'aide %role avec %instance ?', '', '', '', '', '', ''), - (10, 'suggest_instance', 'Anyone needs gear from %instance?', 0, 0, '', 'Quelqu\'un a besoin d\'équipement de %instance ?', '', '', '', '', '', ''), - (11, 'suggest_instance', 'A little grind in %instance?', 0, 0, '', 'Un peu de grind dans %instance ?', '', '', '', '', '', ''), - (12, 'suggest_instance', 'WTR %instance', 0, 0, '', 'Cherche à rejoindre %instance', '', '', '', '', '', ''), - (13, 'suggest_instance', 'Need help for %instance.', 0, 0, '', 'Besoin d\'aide pour %instance', '', '', '', '', '', ''), - (14, 'suggest_instance', 'Wanna run %instance.', 0, 0, '', 'Je veux faire %instance', '', '', '', '', '', ''), - (15, 'suggest_instance', '%role looks for %instance.', 0, 0, '', '%role recherche %instance', '', '', '', '', '', ''), - (16, 'suggest_instance', 'What about %instance?', 0, 0, '', 'Et %instance ?', '', '', '', '', '', ''), - (17, 'suggest_instance', 'Who wants to farm %instance?', 0, 0, '', 'Qui veut exploiter %instance ?', '', '', '', '', '', ''), - (18, 'suggest_instance', 'Go in %instance?', 0, 0, '', 'Aller à %instance ?', '', '', '', '', '', ''), - (19, 'suggest_instance', 'Looking for %instance.', 0, 0, '', 'À la recherche de %instance', '', '', '', '', '', ''), + (1, 'suggest_instance', 'Anyone wants %instance?', 0, 0, '', 'Quelqu\'un fait %instance ?', '', '', '', '', '', ''), + (2, 'suggest_instance', 'Any groups for %instance?', 0, 0, '', 'Des groupes pour %instance ?', '', '', '', '', '', ''), + (3, 'suggest_instance', 'Need help for %instance?', 0, 0, '', 'Besoin d\'aide pour %instance ?', '', '', '', '', '', ''), + (4, 'suggest_instance', 'LFD: %instance.', 0, 0, '', 'Cherche à faire : %instance', '', '', '', '', '', ''), + (5, 'suggest_instance', 'Anyone needs %role for %instance?',0, 0, '', 'Quelqu\'un a besoin de %role pour %instance ?', '', '', '', '', '', ''), + (6, 'suggest_instance', 'Missing %role for %instance?', 0, 0, '', '%role manquant pour %instance ?', '', '', '', '', '', ''), + (7, 'suggest_instance', 'Can be a %role for %instance.', 0, 0, '', '%role disponible pour %instance', '', '', '', '', '', ''), + (8, 'suggest_instance', 'Need help with %instance?', 0, 0, '', 'Besoin d\'aide avec %instance ?', '', '', '', '', '', ''), + (9, 'suggest_instance', 'Need %role help with %instance?', 0, 0, '', 'Besoin d\'aide %role avec %instance ?', '', '', '', '', '', ''), + (10, 'suggest_instance', 'Anyone needs gear from %instance?',0, 0, '', 'Quelqu\'un a besoin d\'équipement de %instance ?', '', '', '', '', '', ''), + (11, 'suggest_instance', 'A little grind in %instance?', 0, 0, '', 'Un peu de grind dans %instance ?', '', '', '', '', '', ''), + (12, 'suggest_instance', 'WTR %instance', 0, 0, '', 'Cherche à rejoindre %instance', '', '', '', '', '', ''), + (13, 'suggest_instance', 'Need help for %instance.', 0, 0, '', 'Besoin d\'aide pour %instance', '', '', '', '', '', ''), + (14, 'suggest_instance', 'Wanna run %instance.', 0, 0, '', 'Je veux faire %instance', '', '', '', '', '', ''), + (15, 'suggest_instance', '%role looks for %instance.', 0, 0, '', '%role recherche %instance', '', '', '', '', '', ''), + (16, 'suggest_instance', 'What about %instance?', 0, 0, '', 'Et %instance ?', '', '', '', '', '', ''), + (17, 'suggest_instance', 'Who wants to farm %instance?', 0, 0, '', 'Qui veut exploiter %instance ?', '', '', '', '', '', ''), + (18, 'suggest_instance', 'Go in %instance?', 0, 0, '', 'Aller à %instance ?', '', '', '', '', '', ''), + (19, 'suggest_instance', 'Looking for %instance.', 0, 0, '', 'À la recherche de %instance', '', '', '', '', '', ''), (20, 'suggest_instance', 'Need help with %instance quests?', 0, 0, '', 'Besoin d\'aide pour les quêtes %instance ?', '', '', '', '', '', ''), - (21, 'suggest_instance', 'Wanna quest in %instance.', 0, 0, '', 'Je veux une quête dans %instance', '', '', '', '', '', ''), + (21, 'suggest_instance', 'Wanna quest in %instance.', 0, 0, '', 'Je veux une quête dans %instance', '', '', '', '', '', ''), (22, 'suggest_instance', 'Anyone with quests in %instance?', 0, 0, '', 'Quelqu\'un a des quêtes dans %instance ?', '', '', '', '', '', ''), (23, 'suggest_instance', 'Could help with quests in %instance.', 0, 0, '', 'Peux aider pour les quêtes dans %instance', '', '', '', '', '', ''), (24, 'suggest_instance', '%role: any place in group for %instance?', 0, 0, '', '%role : n\'importe quel rôle dans le groupe pour %instance ?', '', '', '', '', '', ''), @@ -47,62 +47,62 @@ INSERT INTO `ai_playerbot_texts` (`id`, `name`, `text`, `say_type`, `reply_type` (27, 'suggest_instance', 'Is there any point being %role in %instance?', 0, 0, '', 'Y a-t-il un intérêt à être %role dans %instance ?', '', '', '', '', '', ''), (28, 'suggest_instance', 'It is really worth to go to %instance?', 0, 0, '', 'Cela vaut-il vraiment la peine d\'aller dans %instance ?', '', '', '', '', '', ''), (29, 'suggest_instance', 'Anybody needs more people for %instance?', 0, 0, '', 'Quelqu\'un a besoin de plus de personnes pour %instance ?', '', '', '', '', '', ''), - (30, 'suggest_instance', '%instance bosses drop good gear. Wanna run?', 0, 0, '', 'Les boss %instance drop du bon équipement. Tu veux courir ?', '', '', '', '', '', ''), - (31, 'suggest_instance', 'What about %instance?', 0, 0, '', 'Et %instance ?', '', '', '', '', '', ''), - (32, 'suggest_instance', 'Anybody needs %role?', 0, 0, '', 'Quelqu\'un a besoin de %role ?', '', '', '', '', '', ''), - (33, 'suggest_instance', 'Anyone needs %role?', 0, 0, '', 'Quelqu\'un a besoin de %role ?', '', '', '', '', '', ''), - (34, 'suggest_instance', 'Who wants %instance?', 0, 0, '', 'Qui veut %instance ?', '', '', '', '', '', ''), + (30, 'suggest_instance', '%instance bosses drop good gear. Wanna run?', 0, 0, '', 'Les boss %instance drop du bon équipement. Tu veux nous rejoindre ?', '', '', '', '', '', ''), + (31, 'suggest_instance', 'What about %instance?', 0, 0, '', 'Et %instance ?', '', '', '', '', '', ''), + (32, 'suggest_instance', 'Anybody needs %role?', 0, 0, '', 'Quelqu\'un a besoin de %role ?', '', '', '', '', '', ''), + (33, 'suggest_instance', 'Anyone needs %role?', 0, 0, '', 'Quelqu\'un a besoin de %role ?', '', '', '', '', '', ''), + (34, 'suggest_instance', 'Who wants %instance?', 0, 0, '', 'Qui veut %instance ?', '', '', '', '', '', ''), (35, 'suggest_instance', 'Can anybody summon me at %instance?', 0, 0, '', 'Quelqu\'un peut-il me TP à %instance ?', '', '', '', '', '', ''), - (36, 'suggest_instance', 'Meet me in %instance', 0, 0, '', 'Rencontrez-moi dans %instance', '', '', '', '', '', ''), - (37, 'suggest_instance', 'Wanna quick %instance run', 0, 0, '', 'Je veux faire rapidement %instance', '', '', '', '', '', ''), - (38, 'suggest_instance', 'Wanna full %instance run', 0, 0, '', 'Je veux faire %instance complètement', '', '', '', '', '', ''), + (36, 'suggest_instance', 'Meet me in %instance', 0, 0, '', 'Rencontrez-moi dans %instance', '', '', '', '', '', ''), + (37, 'suggest_instance', 'Wanna quick %instance run', 0, 0, '', 'Je veux faire rapidement %instance', '', '', '', '', '', ''), + (38, 'suggest_instance', 'Wanna full %instance run', 0, 0, '', 'Je veux faire %instance complètement', '', '', '', '', '', ''), (39, 'suggest_instance', 'How many times were you in %instance?', 0, 0, '', 'Combien de fois avez-vous été dans %instance ?', '', '', '', '', '', ''), - (40, 'suggest_instance', 'Another %instance run?', 0, 0, '', 'Une autre exécution de %instance ?', '', '', '', '', '', ''), + (40, 'suggest_instance', 'Another %instance run?', 0, 0, '', 'Une autre exécution de %instance ?', '', '', '', '', '', ''), (41, 'suggest_instance', 'Wiped in %instance? Take me instead!', 0, 0, '', 'Effacé dans %instance ? Prends-moi plutôt !', '', '', '', '', '', ''), - (42, 'suggest_instance', 'Take me in %instance please.', 0, 0, '', 'Prenez-moi dans %instance s\'il vous plaît.', '', '', '', '', '', ''), - (43, 'suggest_instance', 'Quick %instance run?', 0, 0, '', 'Faire rapidement %instance ?', '', '', '', '', '', ''), - (44, 'suggest_instance', 'Full %instance run?', 0, 0, '', 'Clean complet dans %instance ?', '', '', '', '', '', ''), + (42, 'suggest_instance', 'Take me in %instance please.', 0, 0, '', 'Prenez-moi dans %instance s\'il vous plaît.', '', '', '', '', '', ''), + (43, 'suggest_instance', 'Quick %instance run?', 0, 0, '', 'Faire rapidement %instance ?', '', '', '', '', '', ''), + (44, 'suggest_instance', 'Full %instance run?', 0, 0, '', 'Clean complet dans %instance ?', '', '', '', '', '', ''), (45, 'suggest_instance', 'Who can take %role to %instance?', 0, 0, '', 'Qui peut prendre %role à %instance ?', '', '', '', '', '', ''), - (46, 'suggest_quest', 'Need help with %quest?', 0, 0, '', 'Besoin d\'aide avec %quest ?', '', '', '', '', '', ''), - (47, 'suggest_quest', 'Anyone wants to share %quest?', 0, 0, '', 'Quelqu\'un veut partager %quest ?', '', '', '', '', '', ''), - (48, 'suggest_quest', 'Anyone doing %quest?', 0, 0, '', 'Quelqu\'un fait %quest ?', '', '', '', '', '', ''), - (49, 'suggest_quest', 'Wanna do %quest.', 0, 0, '', 'Je veux faire %quest', '', '', '', '', '', ''), - (50, 'suggest_trade', 'Anyone to farm %category?', 0, 0, '', 'Quelqu\'un pour farm %category ?', '', '', '', '', '', ''), + (46, 'suggest_quest', 'Need help with %quest?', 0, 0, '', 'Besoin d\'aide avec %quest ?', '', '', '', '', '', ''), + (47, 'suggest_quest', 'Anyone wants to share %quest?', 0, 0, '', 'Quelqu\'un veut partager %quest ?', '', '', '', '', '', ''), + (48, 'suggest_quest', 'Anyone doing %quest?', 0, 0, '', 'Quelqu\'un fait %quest ?', '', '', '', '', '', ''), + (49, 'suggest_quest', 'Wanna do %quest.', 0, 0, '', 'Je veux faire %quest', '', '', '', '', '', ''), + (50, 'suggest_trade', 'Anyone to farm %category?', 0, 0, '', 'Quelqu\'un pour farm %category ?', '', '', '', '', '', ''), (51, 'suggest_trade', 'Looking for help farming %category.', 0, 0, '', 'Vous cherchez de l\'aide pour farmer %category', '', '', '', '', '', ''), - (52, 'suggest_trade', 'Damn %category are so expensive!', 0, 0, '', 'Putain %category sont si chers!', '', '', '', '', '', ''), - (53, 'suggest_trade', 'Wanna %category.', 0, 0, '', 'Je veux %category', '', '', '', '', '', ''), - (54, 'suggest_trade', 'Need help with %category.', 0, 0, '', 'Besoin d\'aide avec %category', '', '', '', '', '', ''), - (55, 'suggest_trade', 'WTB %category.', 0, 0, '', 'Cherche à acheter %category', '', '', '', '', '', ''), - (56, 'suggest_trade', 'Anyone interested in %category?', 0, 0, '', 'Quiconque est intéressé par %category?', '', '', '', '', '', ''), - (57, 'suggest_trade', 'WTS %category.', 0, 0, '', 'Cherche à vendre %category', '', '', '', '', '', ''), + (52, 'suggest_trade', 'Damn %category are so expensive!', 0, 0, '', 'Putain %category sont si chers!', '', '', '', '', '', ''), + (53, 'suggest_trade', 'Wanna %category.', 0, 0, '', 'Je veux %category', '', '', '', '', '', ''), + (54, 'suggest_trade', 'Need help with %category.', 0, 0, '', 'Besoin d\'aide avec %category', '', '', '', '', '', ''), + (55, 'suggest_trade', 'WTB %category.', 0, 0, '', 'Cherche à acheter %category', '', '', '', '', '', ''), + (56, 'suggest_trade', 'Anyone interested in %category?', 0, 0, '', 'Quiconque est intéressé par %category?', '', '', '', '', '', ''), + (57, 'suggest_trade', 'WTS %category.', 0, 0, '', 'Cherche à vendre %category', '', '', '', '', '', ''), (58, 'suggest_trade', 'I am selling %category cheaper than AH.', 0, 0, '', 'Je vends %category moins cher que l\'hôtel des ventes.', '', '', '', '', '', ''), - (59, 'suggest_trade', 'Who wants to farm %category?', 0, 0, '', 'Qui veut farmer %category ?', '', '', '', '', '', ''), - (60, 'suggest_trade', 'Wanna farm %category.', 0, 0, '', 'Je veux farmer %category', '', '', '', '', '', ''), + (59, 'suggest_trade', 'Who wants to farm %category?', 0, 0, '', 'Qui veut farmer %category ?', '', '', '', '', '', ''), + (60, 'suggest_trade', 'Wanna farm %category.', 0, 0, '', 'Je veux farmer %category', '', '', '', '', '', ''), (61, 'suggest_trade', 'Looking for party after %category.', 0, 0, '', 'Recherche de partie après %category', '', '', '', '', '', ''), - (62, 'suggest_trade', 'Any %category are appreciated.', 0, 0, '', 'Toute %category est appréciée.', '', '', '', '', '', ''), - (63, 'suggest_trade', 'Buying anything of %category.', 0, 0, '', 'Acheter quoi que ce soit de %category', '', '', '', '', '', ''), + (62, 'suggest_trade', 'Any %category are appreciated.', 0, 0, '', 'Toute %category est appréciée.', '', '', '', '', '', ''), + (63, 'suggest_trade', 'Buying anything of %category.', 0, 0, '', 'Acheter quoi que ce soit de %category', '', '', '', '', '', ''), (64, 'suggest_trade', 'Wow, anybody is farming %category!', 0, 0, '', 'Wow, quelqu\'un farm %category !', '', '', '', '', '', ''), (65, 'suggest_trade', '%category are selling mad in the AH.', 0, 0, '', '%category se vendent follement dans l\'hôtel des ventes.', '', '', '', '', '', ''), - (66, 'suggest_trade', 'AH is hot for %category.', 0, 0, '', 'Hôtel des ventes est chaud pour %category', '', '', '', '', '', ''), - (67, 'suggest_trade', '%category are on the market.', 0, 0, '', '%category sont sur le marché.', '', '', '', '', '', ''), - (68, 'suggest_trade', 'Wanna trade some %category.', 0, 0, '', 'Je veux échanger une %category', '', '', '', '', '', ''), - (69, 'suggest_trade', 'Need more %category.', 0, 0, '', 'Besoin de plus de %category', '', '', '', '', '', ''), - (70, 'suggest_trade', 'Anybody can spare some %category?', 0, 0, '', 'Quelqu\'un peut-il épargner une %category ?', '', '', '', '', '', ''), - (71, 'suggest_trade', 'Who wants %category?', 0, 0, '', 'Qui veut %category ?', '', '', '', '', '', ''), - (72, 'suggest_trade', 'Some %category please?', 0, 0, '', 'Une %category s\'il vous plaît ?', '', '', '', '', '', ''), + (66, 'suggest_trade', 'AH is hot for %category.', 0, 0, '', 'Hôtel des ventes est chaud pour %category', '', '', '', '', '', ''), + (67, 'suggest_trade', '%category are on the market.', 0, 0, '', '%category sont sur le marché.', '', '', '', '', '', ''), + (68, 'suggest_trade', 'Wanna trade some %category.', 0, 0, '', 'Je veux échanger une %category', '', '', '', '', '', ''), + (69, 'suggest_trade', 'Need more %category.', 0, 0, '', 'Besoin de plus de %category', '', '', '', '', '', ''), + (70, 'suggest_trade', 'Anybody can spare some %category?', 0, 0, '', 'Quelqu\'un peut-il épargner une %category ?', '', '', '', '', '', ''), + (71, 'suggest_trade', 'Who wants %category?', 0, 0, '', 'Qui veut %category ?', '', '', '', '', '', ''), + (72, 'suggest_trade', 'Some %category please?', 0, 0, '', 'Une %category s\'il vous plaît ?', '', '', '', '', '', ''), (73, 'suggest_trade', 'I should have got skill for %category.', 0, 0, '', 'J\'aurais dû avoir une compétence pour %category', '', '', '', '', '', ''), - (74, 'suggest_trade', 'I am dying for %category.', 0, 0, '', 'Je meurs d\'envie pour %category', '', '', '', '', '', ''), - (75, 'suggest_trade', 'People are killing for %category.', 0, 0, '', 'Les gens tuent pour %category', '', '', '', '', '', ''), - (76, 'suggest_trade', '%category is a great bargain!', 0, 0, '', '%category est une bonne affaire!', '', '', '', '', '', ''), - (77, 'suggest_trade', 'Everybody is mad for %category!', 0, 0, '', 'Tout le monde est fou de %category !', '', '', '', '', '', ''), + (74, 'suggest_trade', 'I am dying for %category.', 0, 0, '', 'Je meurs d\'envie pour %category', '', '', '', '', '', ''), + (75, 'suggest_trade', 'People are killing for %category.', 0, 0, '', 'Les gens tuent pour %category', '', '', '', '', '', ''), + (76, 'suggest_trade', '%category is a great bargain!', 0, 0, '', '%category est une bonne affaire!', '', '', '', '', '', ''), + (77, 'suggest_trade', 'Everybody is mad for %category!', 0, 0, '', 'Tout le monde est fou de %category !', '', '', '', '', '', ''), (78, 'suggest_trade', 'Where is the best place to farm for %category?', 0, 0, '', 'Quel est le meilleur endroit pour farmer pour %category ?', '', '', '', '', '', ''), - (79, 'suggest_trade', 'I am all set for %category.', 0, 0, '', 'Je suis prêt pour %category', '', '', '', '', '', ''), - (80, 'suggest_trade', 'Is it good to sell %category?', 0, 0, '', 'Est-il bon de vendre %category ?', '', '', '', '', '', ''), + (79, 'suggest_trade', 'I am all set for %category.', 0, 0, '', 'Je suis prêt pour %category', '', '', '', '', '', ''), + (80, 'suggest_trade', 'Is it good to sell %category?', 0, 0, '', 'Est-il bon de vendre %category ?', '', '', '', '', '', ''), (81, 'suggest_trade', 'I\'d probably keep all my %category with me.', 0, 0, '', 'Je garderais probablement toute ma %category avec moi.', '', '', '', '', '', ''), (82, 'suggest_trade', 'Need group? Maybe to farm some %category?', 0, 0, '', 'Besoin de groupe ? Peut-être pour farmer une certaine %category ?', '', '', '', '', '', ''), (83, 'suggest_trade', 'I am still thinking about %category.', 0, 0, '', 'Je pense toujours à %category', '', '', '', '', '', ''), (84, 'suggest_trade', 'I heard about %category already, but my pockets are empty.', 0, 0, '', 'J\'ai déjà entendu parler de %category , mais mes poches sont vides.', '', '', '', '', '', ''), - (85, 'suggest_trade', 'LFG for %category', 0, 0, '', 'Cherche un groupe pour %category', '', '', '', '', '', ''), + (85, 'suggest_trade', 'LFG for %category', 0, 0, '', 'Cherche un groupe pour %category', '', '', '', '', '', ''), (86, 'suggest_trade', 'Would selling %category make me rich?', 0, 0, '', 'Vendre %category me rendrait-il riche?', '', '', '', '', '', ''), (87, 'suggest_trade', 'OK. I an farming %category tomorrow.', 0, 0, '', 'D\'ACCORD. Je farm %category demain.', '', '', '', '', '', ''), (88, 'suggest_trade', 'Everyone is talking about %category.', 0, 0, '', 'Tout le monde parle de %category', '', '', '', '', '', ''), @@ -518,41 +518,41 @@ INSERT INTO `ai_playerbot_texts` (`id`, `name`, `text`, `say_type`, `reply_type` (498, 'no ammo', 'That\'s it! No !', 0, 0, '', 'C\'est ça! Pas de !', '', '', '', '', '', ''), (499, 'no ammo', 'And you have my bow... Oops, no !', 0, 0, '', 'Et vous avez mon arc... Oups, pas de !', '', '', '', '', '', ''), (500, 'no ammo', 'Need ammo!', 0, 0, '', 'Besoin de munitions!', '', '', '', '', '', ''), - (501, 'hello', 'Hello', 0, 0, '', '', '', '', '', '', '', 'Привет'), - (502, 'hello', 'Hello!', 0, 0, '', '', '', '', '', '', '', 'Привет!'), - (503, 'hello', 'Hi', 0, 0, '', '', '', '', '', '', '', 'Прив'), - (504, 'hello', 'Hi!', 0, 0, '', '', '', '', '', '', '', 'Прив!'), - (505, 'hello', 'Hello there!', 0, 0, '', '', '', '', '', '', '', 'Здарова!'), - (506, 'hello_follow', 'Hello, I follow you!', 0, 0, '', '', '', '', '', '', '', 'Привет, иду за тобой!'), - (507, 'hello_follow', 'Hello, lead the way!', 0, 0, '', '', '', '', '', '', '', 'Привет, веди!'), - (508, 'hello_follow', 'Hi, lead the way!', 0, 0, '', '', '', '', '', '', '', 'Прив, веди!'), - (509, 'logout_cancel', 'Logout cancelled!', 0, 0, '', '', '', '', '', '', '', 'Логаут отменен!'), - (510, 'logout_start', 'I\'m logging out!', 0, 0, '', '', '', '', '', '', '', 'Я выхожу из игры!'), - (511, 'goodbye', 'Goodbye!', 0, 0, '', '', '', '', '', '', '', 'Пока!'), - (512, 'goodbye', 'Bye bye!', 0, 0, '', '', '', '', '', '', '', 'Пока пока!'), - (513, 'goodbye', 'See you later!', 0, 0, '', '', '', '', '', '', '', 'Увидимся позже!'), - (514, 'quest_accept', 'Quest accepted', 0, 0, '', '', '', '', '', '', '', 'Квест взят'), - (515, 'quest_remove', 'Quest removed', 0, 0, '', '', '', '', '', '', '', 'Квест отменен'), - (516, 'quest_cant_take', 'I can\'t take this quest', 0, 0, '', '', '', '', '', '', '', 'Я не могу взять этот квест'), - (517, 'following', 'Following', 0, 0, '', '', '', '', '', '', '', 'Следую'), - (518, 'staying', 'Staying', 0, 0, '', '', '', '', '', '', '', 'Стою'), - (519, 'fleeing', 'Fleeing', 0, 0, '', '', '', '', '', '', '', 'Убегаю'), - (520, 'fleeing_far', 'I won\'t flee with you, you are too far away', 0, 0, '', '', '', '', '', '', '', 'Я с тобой не побегу, ты слишком далеко'), - (521, 'grinding', 'Grinding', 0, 0, '', '', '', '', '', '', '', 'Гриндю'), - (522, 'attacking', 'Attacking', 0, 0, '', '', '', '', '', '', '', 'Атакую'), - (523, 'wait_travel_close', 'I am close, wait for me!', 0, 0, '', '', '', '', '', '', '', 'Я тут рядом, подожди!'), - (524, 'wait_travel_close', 'I\'m not far, please wait!', 0, 0, '', '', '', '', '', '', '', 'Я недалеко, погодите!'), - (525, 'wait_travel_medium', 'I\'m heading to your location', 0, 0, '', '', '', '', '', '', '', 'Двигаюсь к тебе'), - (526, 'wait_travel_medium', 'I\'m coming to you', 0, 0, '', '', '', '', '', '', '', 'Иду к тебе'), - (527, 'wait_travel_far', 'I\'m traveling to your location', 0, 0, '', '', '', '', '', '', '', 'Направляюсь к тебе'), - (528, 'wait_travel_far', 'I\'m trying to get to you', 0, 0, '', '', '', '', '', '', '', 'Пытаюсь до тебя добраться'), - (529, 'error_far', 'It is too far away', 0, 0, '', '', '', '', '', '', '', 'Слишком далеко'), - (530, 'error_water', 'It is under water', 0, 0, '', '', '', '', '', '', '', 'Это под водой'), - (531, 'error_cant_go', 'I can\'t go there', 0, 0, '', '', '', '', '', '', '', 'Я не могу туда пройти'), - (532, 'error_guild', 'I\'m not in your guild!', 0, 0, '', '', '', '', '', '', '', 'Я не в твоей гильдии'), - (533, 'error_gbank_found', 'Can not find a guild bank nearby', 0, 0, '', '', '', '', '', '', '', 'Не могу найти гильд банк рядом'), + (501, 'hello', 'Hello', 0, 0, '', 'Salut', '', '', '', '', '', 'Привет'), + (502, 'hello', 'Hello!', 0, 0, '', 'Salut !', '', '', '', '', '', 'Привет!'), + (503, 'hello', 'Hi', 0, 0, '', 'Bonjour', '', '', '', '', '', 'Прив'), + (504, 'hello', 'Hi!', 0, 0, '', 'Bonjour !', '', '', '', '', '', 'Прив!'), + (505, 'hello', 'Hello there!', 0, 0, '', 'Salut par ici !', '', '', '', '', '', 'Здарова!'), + (506, 'hello_follow', 'Hello, I follow you!', 0, 0, '', 'Salut, je te suis !', '', '', '', '', '', 'Привет, иду за тобой!'), + (507, 'hello_follow', 'Hello, lead the way!', 0, 0, '', 'Salut, après toi !', '', '', '', '', '', 'Привет, веди!'), + (508, 'hello_follow', 'Hi, lead the way!', 0, 0, '', 'Bonjour, après vous !', '', '', '', '', '', 'Прив, веди!'), + (509, 'logout_cancel', 'Logout cancelled!', 0, 0, '', 'Logout annulé', '', '', '', '', '', 'Логаут отменен!'), + (510, 'logout_start', 'I\'m logging out!', 0, 0, '', 'Je déconnecte', '', '', '', '', '', 'Я выхожу из игры!'), + (511, 'goodbye', 'Goodbye!', 0, 0, '', 'Au revoir', '', '', '', '', '', 'Пока!'), + (512, 'goodbye', 'Bye bye!', 0, 0, '', 'Salut salut++', '', '', '', '', '', 'Пока пока!'), + (513, 'goodbye', 'See you later!', 0, 0, '', 'A plus tard', '', '', '', '', '', 'Увидимся позже!'), + (514, 'quest_accept', 'Quest accepted', 0, 0, '', 'Quête accepté', '', '', '', '', '', 'Квест взят'), + (515, 'quest_remove', 'Quest removed', 0, 0, '', 'Quête supprimé', '', '', '', '', '', 'Квест отменен'), + (516, 'quest_cant_take', 'I can\'t take this quest', 0, 0, '', 'Je ne peut pas prendre la quête', '', '', '', '', '', 'Я не могу взять этот квест'), + (517, 'following', 'Following', 0, 0, '', 'Je vous suis', '', '', '', '', '', 'Следую'), + (518, 'staying', 'Staying', 0, 0, '', 'Je reste en position', '', '', '', '', '', 'Стою'), + (519, 'fleeing', 'Fleeing', 0, 0, '', 'Au secours !!', '', '', '', '', '', 'Убегаю'), + (520, 'fleeing_far', 'I won\'t flee with you, you are too far away', 0, 0, '', 'Tu es trop loin je ne peut pas fuir avec toi', '', '', '', '', '', 'Я с тобой не побегу, ты слишком далеко'), + (521, 'grinding', 'Grinding', 0, 0, '', 'Je grind', '', '', '', '', '', 'Гриндю'), + (522, 'attacking', 'Attacking', 0, 0, '', 'J\'attack', '', '', '', '', '', 'Атакую'), + (523, 'wait_travel_close', 'I am close, wait for me!', 0, 0, '', 'Attends moi je suis proche', '', '', '', '', '', 'Я тут рядом, подожди!'), + (524, 'wait_travel_close', 'I\'m not far, please wait!', 0, 0, '', 'Je suis à côté s\'il te plait attends moi !', '', '', '', '', '', 'Я недалеко, погодите!'), + (525, 'wait_travel_medium', 'I\'m heading to your location', 0, 0, '', 'J\'arrive sur ta position', '', '', '', '', '', 'Двигаюсь к тебе'), + (526, 'wait_travel_medium', 'I\'m coming to you', 0, 0, '', 'Je viens à toi', '', '', '', '', '', 'Иду к тебе'), + (527, 'wait_travel_far', 'I\'m traveling to your location', 0, 0, '', 'Je voyage jusqu\'à toi', '', '', '', '', '', 'Направляюсь к тебе'), + (528, 'wait_travel_far', 'I\'m trying to get to you', 0, 0, '', 'J\'essai de vous rejoindre', '', '', '', '', '', 'Пытаюсь до тебя добраться'), + (529, 'error_far', 'It is too far away', 0, 0, '', 'C\'est trop loin', '', '', '', '', '', 'Слишком далеко'), + (530, 'error_water', 'It is under water', 0, 0, '', 'C\'est sous l\'eau', '', '', '', '', '', 'Это под водой'), + (531, 'error_cant_go', 'I can\'t go there', 0, 0, '', 'Je ne peut pas atteindre cette destination', '', '', '', '', '', 'Я не могу туда пройти'), + (532, 'error_guild', 'I\'m not in your guild!', 0, 0, '', 'Je ne fais pas parti de ta guilde', '', '', '', '', '', 'Я не в твоей гильдии'), + (533, 'error_gbank_found', 'Can not find a guild bank nearby', 0, 0, '', 'Il n\'y a pas de coffre de guilde à proximité', '', '', '', '', '', 'Не могу найти гильд банк рядом'), (534, 'error_cant_put', 'I can\'t put ', 0, 0, '', '', '', '', '', '', '', 'Я не могу положить'), - (535, 'error_gbank_rights', 'I have no rights to put items in the first guild bank tab', 0, 0, '', '', '', '', '', '', '', 'Нет прав чтобы класть вещи в первую вкладку гильд банка'), + (535, 'error_gbank_rights', 'I have no rights to put items in the first guild bank tab', 0, 0, '', 'Je n\'ai pas les droits de dépôt à la banque', '', '', '', '', '', 'Нет прав чтобы класть вещи в первую вкладку гильд банка'), (536, 'gbank_put', ' put to guild bank', 0, 0, '', '', '', '', '', '', '', ' теперь в гильд банке'); /*!40000 ALTER TABLE `ai_playerbot_texts` ENABLE KEYS */; diff --git a/src/AiFactory.cpp b/src/AiFactory.cpp index 3a5ce21b..db551d1a 100644 --- a/src/AiFactory.cpp +++ b/src/AiFactory.cpp @@ -264,7 +264,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa if (!player->InBattleground()) { - engine->addStrategies("racials", "chat", "default", "cast time", "duel", "boost", nullptr); + engine->addStrategies("racials", "chat", "default", "cast time", "duel", "boost", "emote", nullptr); } if (sPlayerbotAIConfig->autoSaveMana) { @@ -548,12 +548,12 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const if (!player->InBattleground()) { nonCombatEngine->addStrategies("nc", "food", "chat", "follow", - "default", "quest", "loot", "gather", "duel", "buff", "mount", nullptr); + "default", "quest", "loot", "gather", "duel", "buff", "mount", "emote", nullptr); } if (sPlayerbotAIConfig->autoSaveMana) { nonCombatEngine->addStrategy("auto save mana"); } - if ((facade->IsRealPlayer() || sRandomPlayerbotMgr->IsRandomBot(player)) && !player->InBattleground()) + if ((sRandomPlayerbotMgr->IsRandomBot(player)) && !player->InBattleground()) { Player* master = facade->GetMaster(); @@ -636,7 +636,7 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const // Battleground switch if (player->InBattleground() && player->GetBattleground()) { - nonCombatEngine->addStrategies("nc", "chat", "default", "buff", "food", "mount", "pvp", "dps assist", "attack tagged", nullptr); + nonCombatEngine->addStrategies("nc", "chat", "default", "buff", "food", "mount", "pvp", "dps assist", "attack tagged", "emote", nullptr); nonCombatEngine->removeStrategy("custom::say"); nonCombatEngine->removeStrategy("travel"); nonCombatEngine->removeStrategy("rpg"); diff --git a/src/ChatHelper.cpp b/src/ChatHelper.cpp index cec5d696..92daa117 100644 --- a/src/ChatHelper.cpp +++ b/src/ChatHelper.cpp @@ -288,7 +288,7 @@ std::string const ChatHelper::FormatQuest(Quest const* quest) std::string const ChatHelper::FormatGameobject(GameObject* go) { std::ostringstream out; - out << "|cFFFFFF00|Hfound:" << go->GetGUID().GetRawValue() << ":" << go->GetEntry() << ":" << "|h[" << go->GetGOInfo()->name << "]|h|r"; + out << "|cFFFFFF00|Hfound:" << go->GetGUID().GetRawValue() << ":" << go->GetEntry() << ":" << "|h[" << go->GetNameForLocaleIdx(LOCALE_enUS) << "]|h|r"; return out.str(); } @@ -296,7 +296,7 @@ std::string const ChatHelper::FormatWorldobject(WorldObject* wo) { std::ostringstream out; out << "|cFFFFFF00|Hfound:" << wo->GetGUID().GetRawValue() << ":" << wo->GetEntry() << ":" << "|h["; - out << (wo->ToGameObject() ? ((GameObject*)wo)->GetGOInfo()->name : wo->GetName()) << "]|h|r"; + out << (wo->ToGameObject() ? ((GameObject*)wo)->GetNameForLocaleIdx(LOCALE_enUS) : wo->GetNameForLocaleIdx(LOCALE_enUS)) << "]|h|r"; return out.str(); } @@ -336,6 +336,8 @@ std::string const ChatHelper::FormatItem(ItemTemplate const* proto, uint32 count char color[32]; sprintf(color, "%x", ItemQualityColors[proto->Quality]); + // const std::string &name = sObjectMgr->GetItemLocale(proto->ItemId)->Name[LOCALE_enUS]; + std::ostringstream out; out << "|c" << color << "|Hitem:" << proto->ItemId << ":0:0:0:0:0:0:0" << "|h[" << proto->Name1 diff --git a/src/LootObjectStack.cpp b/src/LootObjectStack.cpp index acda2e2f..5b792865 100644 --- a/src/LootObjectStack.cpp +++ b/src/LootObjectStack.cpp @@ -96,7 +96,7 @@ void LootObject::Refresh(Player* bot, ObjectGuid lootGUID) if (IsNeededForQuest(bot, itemId)) { - this->guid = guid; + this->guid = lootGUID; return; } isQuestItemOnly |= itemId > 0; diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 6ed537e2..fa59ff11 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -191,7 +191,7 @@ PlayerbotAI::~PlayerbotAI() delete aiObjectContext; if (bot) - sPlayerbotsMgr->RemovePlayerBotData(bot->GetGUID()); + sPlayerbotsMgr->RemovePlayerBotData(bot->GetGUID(), true); } void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal) @@ -625,10 +625,15 @@ void PlayerbotAI::HandleCommand(uint32 type, std::string const text, Player* fro return; } - if (!IsAllowedCommand(filtered) && !GetSecurity()->CheckLevelFor(PLAYERBOT_SECURITY_ALLOW_ALL, type != CHAT_MSG_WHISPER, fromPlayer)) + if (!IsAllowedCommand(filtered) && + (master != fromPlayer || !GetSecurity()->CheckLevelFor(PLAYERBOT_SECURITY_ALLOW_ALL, type != CHAT_MSG_WHISPER, fromPlayer))) return; - if (type == CHAT_MSG_RAID_WARNING && filtered.find(bot->GetName()) != std::string::npos && filtered.find("award") == std::string::npos) + if (!IsAllowedCommand(filtered) && master != fromPlayer) + return; + + if (type == CHAT_MSG_RAID_WARNING && filtered.find(bot->GetName()) != std::string::npos && + filtered.find("award") == std::string::npos) { ChatCommandHolder cmd("warning", fromPlayer, type); chatCommands.push(cmd); diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index b28aeeba..a859c9dd 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -140,7 +140,10 @@ bool PlayerbotAIConfig::Initialize() minRandomBotsPriceChangeInterval = sConfigMgr->GetOption("AiPlayerbot.MinRandomBotsPriceChangeInterval", 2 * HOUR); maxRandomBotsPriceChangeInterval = sConfigMgr->GetOption("AiPlayerbot.MaxRandomBotsPriceChangeInterval", 48 * HOUR); randomBotJoinLfg = sConfigMgr->GetOption("AiPlayerbot.RandomBotJoinLfg", true); + randomBotTalk = sConfigMgr->GetOption("AiPlayerbot.RandomBotTalk", false); + randomBotEmote = sConfigMgr->GetOption("AiPlayerbot.RandomBotEmote", false); randomBotSuggestDungeons = sConfigMgr->GetOption("AiPlayerbot.RandomBotSuggestDungeons", true); + randomBotGuildTalk = sConfigMgr->GetOption("AiPlayerbot.RandomBotGuildTalk", false); suggestDungeonsInLowerCaseRandomly = sConfigMgr->GetOption("AiPlayerbot.SuggestDungeonsInLowerCaseRandomly", false); randomBotJoinBG = sConfigMgr->GetOption("AiPlayerbot.RandomBotJoinBG", true); randomBotAutoJoinBG = sConfigMgr->GetOption("AiPlayerbot.RandomBotAutoJoinBG", false); @@ -279,7 +282,7 @@ bool PlayerbotAIConfig::Initialize() allowSummonInCombat = sConfigMgr->GetOption("AiPlayerbot.AllowSummonInCombat", true); allowSummonWhenMasterIsDead = sConfigMgr->GetOption("AiPlayerbot.AllowSummonWhenMasterIsDead", true); allowSummonWhenBotIsDead = sConfigMgr->GetOption("AiPlayerbot.AllowSummonWhenBotIsDead", true); - reviveBotWhenSummoned = sConfigMgr->GetOption("AiPlayerbot.ReviveBotWhenSummoned", true); + reviveBotWhenSummoned = sConfigMgr->GetOption("AiPlayerbot.ReviveBotWhenSummoned", 1); botRepairWhenSummon = sConfigMgr->GetOption("AiPlayerbot.BotRepairWhenSummon", true); autoInitOnly = sConfigMgr->GetOption("AiPlayerbot.AutoInitOnly", false); autoInitEquipLevelLimitRatio = sConfigMgr->GetOption("AiPlayerbot.AutoInitEquipLevelLimitRatio", 1.0); diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index 32bd1c59..129daee3 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -95,7 +95,10 @@ class PlayerbotAIConfig uint32 randomBotsPerInterval; uint32 minRandomBotsPriceChangeInterval, maxRandomBotsPriceChangeInterval; bool randomBotJoinLfg; + bool randomBotTalk; + bool randomBotEmote; bool randomBotSuggestDungeons; + bool randomBotGuildTalk; bool suggestDungeonsInLowerCaseRandomly; bool randomBotJoinBG; bool randomBotAutoJoinBG; @@ -220,7 +223,7 @@ class PlayerbotAIConfig bool allowSummonInCombat; bool allowSummonWhenMasterIsDead; bool allowSummonWhenBotIsDead; - bool reviveBotWhenSummoned; + int reviveBotWhenSummoned; bool botRepairWhenSummon; bool autoInitOnly; float autoInitEquipLevelLimitRatio; diff --git a/src/PlayerbotFactory.cpp b/src/PlayerbotFactory.cpp index fcd392de..e8360991 100644 --- a/src/PlayerbotFactory.cpp +++ b/src/PlayerbotFactory.cpp @@ -136,6 +136,9 @@ void PlayerbotFactory::Init() continue; } ItemTemplate const* proto = sObjectMgr->GetItemTemplate(gemId); + if (proto->Flags & ITEM_FLAG_UNIQUE_EQUIPPABLE) { // unique gem + continue; + } if (!proto || !sGemPropertiesStore.LookupEntry(proto->GemProperties)) { continue; } @@ -644,6 +647,7 @@ void PlayerbotFactory::InitPetTalents() spells_row.erase(spells_row.begin() + index); } } + bot->SendTalentsInfoData(true); } void PlayerbotFactory::InitPet() @@ -870,6 +874,7 @@ void PlayerbotFactory::InitTalentsTree(bool increment/*false*/, bool use_templat if (bot->GetFreeTalentPoints()) InitTalents((specTab + 1) % 3); } + bot->SendTalentsInfoData(false); } void PlayerbotFactory::InitTalentsBySpecNo(Player* bot, int specNo, bool reset) @@ -933,6 +938,7 @@ void PlayerbotFactory::InitTalentsBySpecNo(Player* bot, int specNo, bool reset) break; } } + bot->SendTalentsInfoData(false); } void PlayerbotFactory::InitTalentsByParsedSpecLink(Player* bot, std::vector> parsedSpecLink, bool reset) @@ -983,6 +989,7 @@ void PlayerbotFactory::InitTalentsByParsedSpecLink(Player* bot, std::vectorSendTalentsInfoData(false); } class DestroyItemsVisitor : public IterateItemsVisitor @@ -1734,7 +1741,7 @@ void PlayerbotFactory::InitBags(bool destroyOld) bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true); } if (old_bag) { - return; + continue; } Item* newItem = bot->EquipNewItem(dest, newItemId, true); if (newItem) @@ -2567,7 +2574,7 @@ void PlayerbotFactory::InitMounts() break; case RACE_NIGHTELF: slow = { 10789, 8394, 10793 }; - fast = { 24252, 63637, 22723 }; + fast = { 23219, 23220, 63637 }; break; case RACE_UNDEAD_PLAYER: slow = { 17463, 17464, 17462 }; @@ -2616,6 +2623,16 @@ void PlayerbotFactory::InitMounts() for (uint32 type = 0; type < 4; type++) { + bool hasMount = false; + for (uint32 &spell : mounts[bot->getRace()][type]) { + if (bot->HasSpell(spell)) { + hasMount = true; + break; + } + } + if (hasMount) + continue; + if (bot->GetLevel() < secondmount && type == 1) continue; @@ -2844,7 +2861,29 @@ void PlayerbotFactory::InitGlyphs(bool increment) if (!increment) { for (uint32 slotIndex = 0; slotIndex < MAX_GLYPH_SLOT_INDEX; ++slotIndex) { - bot->SetGlyph(slotIndex, 0, true); + uint32 glyph = bot->GetGlyph(slotIndex); + if (GlyphPropertiesEntry const* glyphEntry = sGlyphPropertiesStore.LookupEntry(glyph)) + { + bot->RemoveAurasDueToSpell(glyphEntry->SpellId); + + // Removed any triggered auras + Unit::AuraMap& ownedAuras = bot->GetOwnedAuras(); + for (Unit::AuraMap::iterator iter = ownedAuras.begin(); iter != ownedAuras.end();) + { + Aura* aura = iter->second; + if (SpellInfo const* triggeredByAuraSpellInfo = aura->GetTriggeredByAuraSpellInfo()) + { + if (triggeredByAuraSpellInfo->Id == glyphEntry->SpellId) + { + bot->RemoveOwnedAura(iter); + continue; + } + } + ++iter; + } + + bot->SetGlyph(slotIndex, 0, true); + } } } @@ -2943,6 +2982,8 @@ void PlayerbotFactory::InitGlyphs(bool increment) if (!glyph) { continue; } + GlyphPropertiesEntry const* glyphEntry = sGlyphPropertiesStore.LookupEntry(glyph); + bot->CastSpell(bot, glyphEntry->SpellId, TriggerCastFlags(TRIGGERED_FULL_MASK & ~(TRIGGERED_IGNORE_SHAPESHIFT | TRIGGERED_IGNORE_CASTER_AURASTATE))); bot->SetGlyph(realSlot, glyph, true); chosen.insert(glyph); } else { @@ -2976,13 +3017,16 @@ void PlayerbotFactory::InitGlyphs(bool increment) continue; chosen.insert(id); - + GlyphPropertiesEntry const* glyphEntry = sGlyphPropertiesStore.LookupEntry(id); + bot->CastSpell(bot, glyphEntry->SpellId, TriggerCastFlags(TRIGGERED_FULL_MASK & ~(TRIGGERED_IGNORE_SHAPESHIFT | TRIGGERED_IGNORE_CASTER_AURASTATE))); + bot->SetGlyph(realSlot, id, true); found = true; break; } } } + bot->SendTalentsInfoData(false); } void PlayerbotFactory::CancelAuras() diff --git a/src/PlayerbotMgr.cpp b/src/PlayerbotMgr.cpp index 1f1a272d..1774b93e 100644 --- a/src/PlayerbotMgr.cpp +++ b/src/PlayerbotMgr.cpp @@ -85,7 +85,8 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con uint32 botAccountId = holder.GetAccountId(); - WorldSession* botSession = new WorldSession(botAccountId, "", nullptr, SEC_PLAYER, EXPANSION_WRATH_OF_THE_LICH_KING, time_t(0), LOCALE_enUS, 0, false, false, 0, true); + // At login DBC locale should be what the server is set to use by default (as spells etc are hardcoded to ENUS this allows channels to work as intended) + WorldSession* botSession = new WorldSession(botAccountId, "", nullptr, SEC_PLAYER, EXPANSION_WRATH_OF_THE_LICH_KING, time_t(0), sWorld->GetDefaultDbcLocale(), 0, false, false, 0, true); botSession->HandlePlayerLoginFromDB(holder); // will delete lqh @@ -525,7 +526,7 @@ void PlayerbotHolder::OnBotLogin(Player* const bot) // join standard channels AreaTableEntry const* current_zone = sAreaTableStore.LookupEntry(bot->GetAreaId()); ChannelMgr* cMgr = ChannelMgr::forTeam(bot->GetTeamId()); - std::string current_zone_name = current_zone ? current_zone->area_name[0] : ""; + std::string current_zone_name = current_zone ? current_zone->area_name[sWorld->GetDefaultDbcLocale()] : ""; if (current_zone && cMgr) { @@ -544,13 +545,13 @@ void PlayerbotHolder::OnBotLogin(Player* const bot) Channel* new_channel = nullptr; if (isLfg) { - std::string lfgChannelName = channel->pattern[0]; + std::string lfgChannelName = channel->pattern[sWorld->GetDefaultDbcLocale()]; new_channel = cMgr->GetJoinChannel("LookingForGroup", channel->ChannelID); } else { char new_channel_name_buf[100]; - snprintf(new_channel_name_buf, 100, channel->pattern[0], current_zone_name.c_str()); + snprintf(new_channel_name_buf, 100, channel->pattern[sWorld->GetDefaultDbcLocale()], current_zone_name.c_str()); new_channel = cMgr->GetJoinChannel(new_channel_name_buf, channel->ChannelID); } if (new_channel && new_channel->GetName().length() > 0) @@ -736,7 +737,7 @@ bool PlayerbotMgr::HandlePlayerbotMgrCommand(ChatHandler* handler, char const* a for (std::vector::iterator i = messages.begin(); i != messages.end(); ++i) { - handler->PSendSysMessage("%s", i->c_str()); + handler->PSendSysMessage("{}", i->c_str()); } return true; @@ -748,8 +749,8 @@ std::vector PlayerbotHolder::HandlePlayerbotCommand(char const* arg if (!*args) { - messages.push_back("usage: list/reload/tweak/self or add/init/remove PLAYERNAME"); - messages.push_back(" addclass CLASSNAME"); + messages.push_back("usage: list/reload/tweak/self or add/init/remove PLAYERNAME\n"); + messages.push_back("usage: addclass CLASSNAME"); return messages; } @@ -842,7 +843,7 @@ std::vector PlayerbotHolder::HandlePlayerbotCommand(char const* arg if (GET_PLAYERBOT_AI(master)) { messages.push_back("Disable player botAI"); - DisablePlayerBot(master->GetGUID()); + delete GET_PLAYERBOT_AI(master); } else if (sPlayerbotAIConfig->selfBotLevel == 0) messages.push_back("Self-bot is disabled"); @@ -851,7 +852,8 @@ std::vector PlayerbotHolder::HandlePlayerbotCommand(char const* arg else { messages.push_back("Enable player botAI"); - OnBotLogin(master); + sPlayerbotsMgr->AddPlayerbotData(master, true); + GET_PLAYERBOT_AI(master)->SetMaster(master); } return messages; @@ -938,20 +940,22 @@ std::vector PlayerbotHolder::HandlePlayerbotCommand(char const* arg race_limit = "2, 5, 6, 8, 10"; break; } + uint32 maxAccountId = sPlayerbotAIConfig->randomBotAccounts.back(); // find a bot fit conditions and not in any guild QueryResult results = CharacterDatabase.Query("SELECT guid FROM characters " "WHERE name IN (SELECT name FROM playerbots_names) AND class = '{}' AND online = 0 AND race IN ({}) AND guid NOT IN ( SELECT guid FROM guild_member ) " - "ORDER BY account DESC LIMIT 1", claz, race_limit); + "AND account <= {} " + "ORDER BY account DESC LIMIT 1", claz, race_limit, maxAccountId); if (results) { Field* fields = results->Fetch(); ObjectGuid guid = ObjectGuid(HighGuid::Player, fields[0].Get()); AddPlayerBot(guid, master->GetSession()->GetAccountId()); - messages.push_back("addclass " + std::string(charname) + " ok"); + messages.push_back("Add class " + std::string(charname)); return messages; } - messages.push_back("addclass failed."); + messages.push_back("Add class failed."); return messages; } @@ -1214,7 +1218,7 @@ PlayerbotMgr::PlayerbotMgr(Player* const master) : PlayerbotHolder(), master(ma PlayerbotMgr::~PlayerbotMgr() { if (master) - sPlayerbotsMgr->RemovePlayerBotData(master->GetGUID()); + sPlayerbotsMgr->RemovePlayerBotData(master->GetGUID(), false); } void PlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/) @@ -1427,31 +1431,45 @@ void PlayerbotsMgr::AddPlayerbotData(Player* player, bool isBotAI) return; } // If the guid already exists in the map, remove it - std::unordered_map::iterator itr = _playerbotsMap.find(player->GetGUID()); - if (itr != _playerbotsMap.end()) - { - _playerbotsMap.erase(itr); - } + if (!isBotAI) { + std::unordered_map::iterator itr = _playerbotsMgrMap.find(player->GetGUID()); + if (itr != _playerbotsMgrMap.end()) + { + _playerbotsMgrMap.erase(itr); + } PlayerbotMgr* playerbotMgr = new PlayerbotMgr(player); - ASSERT(_playerbotsMap.emplace(player->GetGUID(), playerbotMgr).second); + ASSERT(_playerbotsMgrMap.emplace(player->GetGUID(), playerbotMgr).second); playerbotMgr->OnPlayerLogin(player); } else { + std::unordered_map::iterator itr = _playerbotsAIMap.find(player->GetGUID()); + if (itr != _playerbotsAIMap.end()) + { + _playerbotsAIMap.erase(itr); + } PlayerbotAI* botAI = new PlayerbotAI(player); - ASSERT(_playerbotsMap.emplace(player->GetGUID(), botAI).second); + ASSERT(_playerbotsAIMap.emplace(player->GetGUID(), botAI).second); } } -void PlayerbotsMgr::RemovePlayerBotData(ObjectGuid const& guid) +void PlayerbotsMgr::RemovePlayerBotData(ObjectGuid const& guid, bool is_AI) { - std::unordered_map::iterator itr = _playerbotsMap.find(guid); - if (itr != _playerbotsMap.end()) - { - _playerbotsMap.erase(itr); + if (is_AI) { + std::unordered_map::iterator itr = _playerbotsAIMap.find(guid); + if (itr != _playerbotsAIMap.end()) + { + _playerbotsAIMap.erase(itr); + } + } else { + std::unordered_map::iterator itr = _playerbotsMgrMap.find(guid); + if (itr != _playerbotsMgrMap.end()) + { + _playerbotsMgrMap.erase(itr); + } } } @@ -1464,8 +1482,8 @@ PlayerbotAI* PlayerbotsMgr::GetPlayerbotAI(Player* player) // if (player->GetSession()->isLogingOut() || player->IsDuringRemoveFromWorld()) { // return nullptr; // } - auto itr = _playerbotsMap.find(player->GetGUID()); - if (itr != _playerbotsMap.end()) + auto itr = _playerbotsAIMap.find(player->GetGUID()); + if (itr != _playerbotsAIMap.end()) { if (itr->second->IsBotAI()) return reinterpret_cast(itr->second); @@ -1480,8 +1498,8 @@ PlayerbotMgr* PlayerbotsMgr::GetPlayerbotMgr(Player* player) { return nullptr; } - auto itr = _playerbotsMap.find(player->GetGUID()); - if (itr != _playerbotsMap.end()) + auto itr = _playerbotsMgrMap.find(player->GetGUID()); + if (itr != _playerbotsMgrMap.end()) { if (!itr->second->IsBotAI()) return reinterpret_cast(itr->second); diff --git a/src/PlayerbotMgr.h b/src/PlayerbotMgr.h index d430d414..c8297c0a 100644 --- a/src/PlayerbotMgr.h +++ b/src/PlayerbotMgr.h @@ -99,13 +99,14 @@ class PlayerbotsMgr } void AddPlayerbotData(Player* player, bool isBotAI); - void RemovePlayerBotData(ObjectGuid const& guid); + void RemovePlayerBotData(ObjectGuid const& guid, bool is_AI); PlayerbotAI* GetPlayerbotAI(Player* player); PlayerbotMgr* GetPlayerbotMgr(Player* player); private: - std::unordered_map _playerbotsMap; + std::unordered_map _playerbotsAIMap; + std::unordered_map _playerbotsMgrMap; }; #define sPlayerbotsMgr PlayerbotsMgr::instance() diff --git a/src/PlayerbotTextMgr.cpp b/src/PlayerbotTextMgr.cpp index acfaada8..7041185c 100644 --- a/src/PlayerbotTextMgr.cpp +++ b/src/PlayerbotTextMgr.cpp @@ -28,12 +28,13 @@ void PlayerbotTextMgr::LoadBotTexts() Field* fields = result->Fetch(); std::string name = fields[0].Get(); text[0] = fields[1].Get(); - uint32 sayType = fields[2].Get(); - uint32 replyType = fields[3].Get(); + uint8 sayType = fields[2].Get(); + uint8 replyType = fields[3].Get(); for (uint8 i = 1; i < MAX_LOCALES; ++i) { text[i] = fields[i + 3].Get(); } + botTexts[name].push_back(BotTextEntry(name, text, sayType, replyType)); ++count; } @@ -191,6 +192,7 @@ uint32 PlayerbotTextMgr::GetLocalePriority() if (botTextLocalePriority[i] > topLocale) topLocale = i; } + return topLocale; } diff --git a/src/Playerbots.cpp b/src/Playerbots.cpp index 6b6462cc..ad519e13 100644 --- a/src/Playerbots.cpp +++ b/src/Playerbots.cpp @@ -299,7 +299,7 @@ class PlayerbotsScript : public PlayerbotScript { botAI->HandleBotOutgoingPacket(*packet); } - else if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player)) + if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player)) { playerbotMgr->HandleMasterOutgoingPacket(*packet); } diff --git a/src/Playerbots.h b/src/Playerbots.h index 7e678af3..2b467366 100644 --- a/src/Playerbots.h +++ b/src/Playerbots.h @@ -14,6 +14,7 @@ #include "RandomPlayerbotMgr.h" #include "SharedValueContext.h" #include "Spell.h" +#include "SpellMgr.h" #include "TravelNode.h" std::vector split(std::string const s, char delim); diff --git a/src/RandomItemMgr.cpp b/src/RandomItemMgr.cpp index c3b01ef5..1e1a18ab 100644 --- a/src/RandomItemMgr.cpp +++ b/src/RandomItemMgr.cpp @@ -2211,8 +2211,8 @@ void RandomItemMgr::BuildAmmoCache() { for (uint32 subClass = ITEM_SUBCLASS_ARROW; subClass <= ITEM_SUBCLASS_BULLET; subClass++) { - QueryResult results = WorldDatabase.Query("SELECT entry, Flags FROM item_template WHERE class = {} AND subclass = {} AND RequiredLevel <= {} AND stackable = 1000 " - "ORDER BY RequiredLevel DESC", ITEM_CLASS_PROJECTILE, subClass, level); + QueryResult results = WorldDatabase.Query("SELECT entry, Flags FROM item_template WHERE class = {} AND subclass = {} AND RequiredLevel <= {} " + "ORDER BY stackable DESC, RequiredLevel DESC", ITEM_CLASS_PROJECTILE, subClass, level); if (!results) continue; do { diff --git a/src/RandomPlayerbotFactory.cpp b/src/RandomPlayerbotFactory.cpp index e1fec407..b1dfae8b 100644 --- a/src/RandomPlayerbotFactory.cpp +++ b/src/RandomPlayerbotFactory.cpp @@ -375,7 +375,7 @@ void RandomPlayerbotFactory::CreateRandomBots() LOG_INFO("playerbots", "Creating random bot accounts..."); std::vector> account_creations; - bool account_creation = false; + int account_creation = 0; for (uint32 accountNumber = 0; accountNumber < sPlayerbotAIConfig->randomBotAccountCount; ++accountNumber) { std::ostringstream out; @@ -389,7 +389,7 @@ void RandomPlayerbotFactory::CreateRandomBots() { continue; } - account_creation = true; + account_creation++; std::string password = ""; if (sPlayerbotAIConfig->randomBotRandomPassword) { @@ -408,6 +408,7 @@ void RandomPlayerbotFactory::CreateRandomBots() if (account_creation) { /* wait for async accounts create to make character create correctly, same as account delete */ + LOG_INFO("playerbots", "Waiting for {} accounts loading into database...", account_creation); std::this_thread::sleep_for(10ms * sPlayerbotAIConfig->randomBotAccountCount); } @@ -418,7 +419,7 @@ void RandomPlayerbotFactory::CreateRandomBots() std::unordered_map> names; std::vector> playerBots; std::vector sessionBots; - bool bot_creation = false; + int bot_creation = 0; for (uint32 accountNumber = 0; accountNumber < sPlayerbotAIConfig->randomBotAccountCount; ++accountNumber) { std::ostringstream out; @@ -441,7 +442,6 @@ void RandomPlayerbotFactory::CreateRandomBots() { continue; } - bot_creation = true; LOG_INFO("playerbots", "Creating random bot characters for account: [{}/{}]", accountNumber + 1, sPlayerbotAIConfig->randomBotAccountCount); RandomPlayerbotFactory factory(accountId); @@ -462,19 +462,23 @@ void RandomPlayerbotFactory::CreateRandomBots() continue; } - if (cls != 10) + if (cls != 10) { if (Player* playerBot = factory.CreateRandomBot(session, cls, names)) { 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); } + } } } if (bot_creation) { - LOG_INFO("playerbots", "Waiting for {} characters loading into database...", totalCharCount); + LOG_INFO("playerbots", "Waiting for {} characters loading into database...", bot_creation); /* wait for characters load into database, or characters will fail to loggin */ std::this_thread::sleep_for(10s); } diff --git a/src/RandomPlayerbotMgr.cpp b/src/RandomPlayerbotMgr.cpp index 5e6891e5..1a424d5e 100644 --- a/src/RandomPlayerbotMgr.cpp +++ b/src/RandomPlayerbotMgr.cpp @@ -1228,8 +1228,8 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector& if (bot->GetLevel() <= 18 && (loc.GetMapId() != pInfo->mapId || dis > 10000.0f)) { continue; } - LocaleConstant locale = sWorld->GetDefaultDbcLocale(); - + + const LocaleConstant& locale = sWorld->GetDefaultDbcLocale(); LOG_INFO("playerbots", "Random teleporting bot {} (level {}) to Map: {} ({}) Zone: {} ({}) Area: {} ({}) {},{},{} ({}/{} locations)", bot->GetName().c_str(), bot->GetLevel(), map->GetId(), map->GetMapName(), @@ -1501,7 +1501,6 @@ void RandomPlayerbotMgr::Randomize(Player* bot) else { RandomizeFirst(bot); } - RandomTeleportForLevel(bot); } void RandomPlayerbotMgr::IncreaseLevel(Player* bot) @@ -1590,6 +1589,8 @@ void RandomPlayerbotMgr::RandomizeFirst(Player* bot) if (pmo) pmo->finish(); + + RandomTeleportForLevel(bot); } void RandomPlayerbotMgr::RandomizeMin(Player* bot) diff --git a/src/strategy/Engine.cpp b/src/strategy/Engine.cpp index cef411c6..c4ca7394 100644 --- a/src/strategy/Engine.cpp +++ b/src/strategy/Engine.cpp @@ -172,6 +172,7 @@ bool Engine::DoNextAction(Unit* unit, uint32 depth, bool minimal) if (!action) { + //LOG_ERROR("playerbots", "Action: {} - is UNKNOWN - c:{} l:{}", actionNode->getName().c_str(), botAI->GetBot()->getClass(), botAI->GetBot()->GetLevel()); LogAction("A:%s - UNKNOWN", actionNode->getName().c_str()); } else if (action->isUseful()) diff --git a/src/strategy/actions/AcceptInvitationAction.cpp b/src/strategy/actions/AcceptInvitationAction.cpp index ce2ea360..5bd261c1 100644 --- a/src/strategy/actions/AcceptInvitationAction.cpp +++ b/src/strategy/actions/AcceptInvitationAction.cpp @@ -4,17 +4,24 @@ #include "AcceptInvitationAction.h" #include "Event.h" +#include "ObjectAccessor.h" #include "PlayerbotAIConfig.h" #include "Playerbots.h" #include "PlayerbotSecurity.h" +#include "WorldPacket.h" bool AcceptInvitationAction::Execute(Event event) { Group* grp = bot->GetGroupInvite(); if (!grp) return false; + WorldPacket packet = event.getPacket(); + uint8 flag; + std::string name; + packet >> flag >> name; - Player* inviter = ObjectAccessor::FindPlayer(grp->GetLeaderGUID()); + // Player* inviter = ObjectAccessor::FindPlayer(grp->GetLeaderGUID()); + Player* inviter = ObjectAccessor::FindPlayerByName(name, true); if (!inviter) return false; diff --git a/src/strategy/actions/ActionContext.h b/src/strategy/actions/ActionContext.h index ea8b3362..e7caf4e2 100644 --- a/src/strategy/actions/ActionContext.h +++ b/src/strategy/actions/ActionContext.h @@ -123,6 +123,7 @@ class ActionContext : public NamedObjectContext creators["talk"] = &ActionContext::talk; creators["suggest what to do"] = &ActionContext::suggest_what_to_do; creators["suggest trade"] = &ActionContext::suggest_trade; + creators["suggest dungeon"] = &ActionContext::suggest_dungeon; creators["return"] = &ActionContext::_return; creators["move to loot"] = &ActionContext::move_to_loot; creators["open loot"] = &ActionContext::open_loot; @@ -280,6 +281,7 @@ class ActionContext : public NamedObjectContext static Action* talk(PlayerbotAI* botAI) { return new TalkAction(botAI); } static Action* suggest_what_to_do(PlayerbotAI* botAI) { return new SuggestWhatToDoAction(botAI); } static Action* suggest_trade(PlayerbotAI* botAI) { return new SuggestTradeAction(botAI); } + static Action* suggest_dungeon(PlayerbotAI* botAI) { return new SuggestDungeonAction(botAI); } static Action* attack_anything(PlayerbotAI* botAI) { return new AttackAnythingAction(botAI); } static Action* attack_least_hp_target(PlayerbotAI* botAI) { return new AttackLeastHpTargetAction(botAI); } static Action* attack_enemy_player(PlayerbotAI* botAI) { return new AttackEnemyPlayerAction(botAI); } diff --git a/src/strategy/actions/AutoLearnSpellAction.cpp b/src/strategy/actions/AutoLearnSpellAction.cpp index 3d5230ed..24205a64 100644 --- a/src/strategy/actions/AutoLearnSpellAction.cpp +++ b/src/strategy/actions/AutoLearnSpellAction.cpp @@ -6,6 +6,7 @@ #include "Event.h" #include "PlayerbotFactory.h" #include "Playerbots.h" +#include "GuildMgr.h" bool AutoLearnSpellAction::Execute(Event event) { @@ -27,7 +28,6 @@ bool AutoLearnSpellAction::Execute(Event event) return true; } - void AutoLearnSpellAction::LearnSpells(std::ostringstream* out) { if (sPlayerbotAIConfig->autoLearnTrainerSpells && sRandomPlayerbotMgr->IsRandomBot(bot))// || (!botAI->GetMaster() && sRandomPlayerbotMgr->IsRandomBot(bot))) @@ -35,6 +35,22 @@ void AutoLearnSpellAction::LearnSpells(std::ostringstream* out) if (sPlayerbotAIConfig->autoLearnQuestSpells && sRandomPlayerbotMgr->IsRandomBot(bot))// || (!botAI->GetMaster() && sRandomPlayerbotMgr->IsRandomBot(bot))) LearnQuestSpells(out); + + if (sPlayerbotAIConfig->randomBotGuildTalk) + { + Guild* guild = sGuildMgr->GetGuildById(bot->GetGuildId()); + if (guild) + { + std::string toSay = ""; + + if (urand(0, 3)) + toSay = "Ding !"; + else + toSay = "Yay level " + std::to_string(bot->GetLevel()) + " !"; + + guild->BroadcastToGuild(bot->GetSession(), false, toSay, LANG_UNIVERSAL); + } + } } void AutoLearnSpellAction::LearnTrainerSpells(std::ostringstream* out) diff --git a/src/strategy/actions/BattleGroundTactics.cpp b/src/strategy/actions/BattleGroundTactics.cpp index 403c50fe..2ec22b81 100644 --- a/src/strategy/actions/BattleGroundTactics.cpp +++ b/src/strategy/actions/BattleGroundTactics.cpp @@ -2386,11 +2386,11 @@ static std::pair AV_AllianceDefendObjectives[] = static uint32 AB_AttackObjectives[] = { // Attack - { BG_AB_NODE_STABLES }, - { BG_AB_NODE_BLACKSMITH }, - { BG_AB_NODE_FARM }, - { BG_AB_NODE_LUMBER_MILL }, - { BG_AB_NODE_GOLD_MINE } + BG_AB_NODE_STABLES, + BG_AB_NODE_BLACKSMITH, + BG_AB_NODE_FARM, + BG_AB_NODE_LUMBER_MILL, + BG_AB_NODE_GOLD_MINE }; static std::tuple EY_AttackObjectives[] = diff --git a/src/strategy/actions/EmoteAction.cpp b/src/strategy/actions/EmoteAction.cpp index b13a7a12..8fe57374 100644 --- a/src/strategy/actions/EmoteAction.cpp +++ b/src/strategy/actions/EmoteAction.cpp @@ -447,7 +447,7 @@ bool EmoteActionBase::ReceiveEmote(Player* source, uint32 emote, bool verbal) case TEXT_EMOTE_RASP: emoteId = EMOTE_ONESHOT_RUDE; textEmote = TEXT_EMOTE_RASP; - emoteText = "Right back at you, bub!", LANG_UNIVERSAL; + emoteText = "Right back at you, bub!"; // , LANG_UNIVERSAL; break; case TEXT_EMOTE_ROAR: case TEXT_EMOTE_THREATEN: @@ -671,9 +671,9 @@ bool EmoteAction::Execute(Event event) if (pSource && (pSource->GetGUID() != bot->GetGUID()) && ((urand(0, 1) && bot->HasInArc(static_cast(M_PI), pSource, 10.0f)) || (namlen > 1 && strstri(bot->GetName().c_str(), nam.c_str())))) { - LOG_INFO("playerbots", "Bot {} {}:{} <{}> received SMSG_TEXT_EMOTE {} from player {} <{}>", + /*LOG_INFO("playerbots", "Bot {} {}:{} <{}> received SMSG_TEXT_EMOTE {} from player {} <{}>", bot->GetGUID().ToString().c_str(), bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(), - bot->GetName(), text_emote, pSource->GetGUID().ToString().c_str(), pSource->GetName()); + bot->GetName(), text_emote, pSource->GetGUID().ToString().c_str(), pSource->GetName());*/ emote = text_emote; } @@ -693,9 +693,9 @@ bool EmoteAction::Execute(Event event) if ((pSource->GetGUID() != bot->GetGUID()) && (pSource->GetTarget() == bot->GetGUID() || (urand(0, 1) && bot->HasInArc(static_cast(M_PI), pSource, 10.0f)))) { - LOG_INFO("playerbots", "Bot {} {}:{} <{}> received SMSG_EMOTE {} from player {} <{}>", + /*LOG_INFO("playerbots", "Bot {} {}:{} <{}> received SMSG_EMOTE {} from player {} <{}>", bot->GetGUID().ToString().c_str(), bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(), bot->GetName(), - emoteId, pSource->GetGUID().ToString().c_str(), pSource->GetName()); + emoteId, pSource->GetGUID().ToString().c_str(), pSource->GetName());*/ std::vector types; for (int32 i = sEmotesTextStore.GetNumRows(); i >= 0; --i) diff --git a/src/strategy/actions/GenericActions.cpp b/src/strategy/actions/GenericActions.cpp index 5e292369..9b16ee7a 100644 --- a/src/strategy/actions/GenericActions.cpp +++ b/src/strategy/actions/GenericActions.cpp @@ -20,6 +20,7 @@ bool TogglePetSpellAutoCastAction::Execute(Event event) { if (!pet) { return false; } + bool toggled = false; for (PetSpellMap::const_iterator itr = pet->m_spells.begin(); itr != pet->m_spells.end(); ++itr) { if(itr->second.state == PETSPELL_REMOVED) @@ -29,17 +30,29 @@ bool TogglePetSpellAutoCastAction::Execute(Event event) { const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId); if (spellInfo->IsPassive()) continue; - + + bool shouldApply = true; // imp's spell, felhunte's intelligence, ghoul's leap, cat stealth if (spellId == 4511 || spellId == 1742 || spellId == 54424 || spellId == 57564 || spellId == 57565 || spellId == 57566 || spellId == 57567 || spellId == 47482 || spellId == 24450) { - pet->ToggleAutocast(spellInfo, false); - } else { - pet->ToggleAutocast(spellInfo, true); + shouldApply = false; + } + bool isAutoCast = false; + for (unsigned int &m_autospell : pet->m_autospells) + { + if (m_autospell == spellId) + { + isAutoCast = true; + break; + } + } + if (shouldApply != isAutoCast) { + pet->ToggleAutocast(spellInfo, shouldApply); + toggled = true; } } - return true; + return toggled; } bool PetAttackAction::Execute(Event event) diff --git a/src/strategy/actions/InviteToGroupAction.cpp b/src/strategy/actions/InviteToGroupAction.cpp index 3e7fb0bc..58094fc6 100644 --- a/src/strategy/actions/InviteToGroupAction.cpp +++ b/src/strategy/actions/InviteToGroupAction.cpp @@ -19,7 +19,7 @@ bool InviteToGroupAction::Execute(Event event) bool InviteToGroupAction::Invite(Player* player) { - if (!player) + if (!player || !player->IsInWorld()) return false; if (!GET_PLAYERBOT_AI(player) && !botAI->GetSecurity()->CheckLevelFor(PLAYERBOT_SECURITY_INVITE, true, player)) diff --git a/src/strategy/actions/LfgActions.cpp b/src/strategy/actions/LfgActions.cpp index 45f3b16c..d7e7b865 100644 --- a/src/strategy/actions/LfgActions.cpp +++ b/src/strategy/actions/LfgActions.cpp @@ -103,14 +103,13 @@ bool LfgJoinAction::JoinLFG() if (!dungeon || (dungeon->TypeID != LFG_TYPE_RANDOM && dungeon->TypeID != LFG_TYPE_DUNGEON && dungeon->TypeID != LFG_TYPE_HEROIC && dungeon->TypeID != LFG_TYPE_RAID)) continue; - uint32 botLevel = bot->GetLevel(); - if (dungeon->MinLevel && botLevel < dungeon->MinLevel) - continue; + const auto& botLevel = bot->GetLevel(); - if (dungeon->MinLevel && botLevel > dungeon->MinLevel + 10) - continue; - - if (dungeon->MaxLevel && botLevel > dungeon->MaxLevel) + /*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) + || + (botLevel > dungeon->MinLevel + 10 && dungeon->TypeID == LFG_TYPE_DUNGEON) + ) continue; selected.push_back(dungeon->ID); @@ -126,41 +125,8 @@ bool LfgJoinAction::JoinLFG() bool many = list.size() > 1; LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(*list.begin()); - /*for (uint32 i = 0; i < sLFGDungeonStore.GetNumRows(); ++i) - { - LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(i); - if (!dungeon || (dungeon->type != LFG_TYPE_RANDOM_DUNGEON && dungeon->type != LFG_TYPE_DUNGEON && dungeon->type != LFG_TYPE_HEROIC_DUNGEON && - dungeon->type != LFG_TYPE_RAID)) - continue; - - uint32 botLevel = bot->GetLevel(); - if (dungeon->MinLevel && botLevel < dungeon->MinLevel) - continue; - - if (dungeon->MinLevel && botLevel > dungeon->MinLevel + 10) - continue; - - if (dungeon->MaxLevel && botLevel > dungeon->MaxLevel) - continue; - - if (heroic && !dungeon->difficulty) - continue; - - if (rbotAId && dungeon->type != LFG_TYPE_RAID) - continue; - - if (!random && !rbotAId && !heroic && dungeon->type != LFG_TYPE_DUNGEON) - continue; - - list.insert(dungeon); - } - - if (list.empty() && !random) - return false;*/ - // check role for console msg std::string _roles = "multiple roles"; - uint32 roleMask = GetRoles(); if (roleMask & PLAYER_ROLE_TANK) _roles = "TANK"; @@ -175,70 +141,8 @@ bool LfgJoinAction::JoinLFG() bot->GetGUID().ToString().c_str(), bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(), bot->GetName().c_str(), _roles, many ? "several dungeons" : dungeon->Name[0]); - /*if (lfgState->IsSingleRole()) - { - if (lfgState->HasRole(ROLE_TANK)) - _roles = "TANK"; - if (lfgState->HasRole(ROLE_HEALER)) - _roles = "HEAL"; - if (lfgState->HasRole(ROLE_DAMAGE)) - _roles = "DPS"; - }*/ - - /*LFGDungeonEntry const* dungeon; - - if(!random) - dungeon = *list.begin(); - - bool many = list.size() > 1; - - if (random) - { - LFGDungeonSet randList = sLFGMgr->GetRandomDungeonsForPlayer(bot); - for (LFGDungeonSet::const_iterator itr = randList.begin(); itr != randList.end(); ++itr) - { - LFGDungeonEntry const* dungeon = *itr; - - if (!dungeon) - continue; - - idx.push_back(dungeon->ID); - } - if (idx.empty()) - return false; - - // choose random dungeon - dungeon = sLFGDungeonStore.LookupEntry(idx[urand(0, idx.size() - 1)]); - list.insert(dungeon); - - pState->SetType(LFG_TYPE_RANDOM_DUNGEON); - LOG_INFO("playerbots", "Bot {} {}:{} <{}>: queues LFG, Random Dungeon as {} ({})", - bot->GetGUID().ToString().c_str(), bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(), bot->GetName().c_str(), _roles, dungeon->Name[0]); - return true; - } - else if (heroic) - { - pState->SetType(LFG_TYPE_HEROIC_DUNGEON); - LOG_INFO("playerbots", "Bot {} {}:{} <{}>: queues LFG, Heroic Dungeon as {} ({})", - bot->GetGUID().ToString().c_str(), bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", - bot->GetLevel(), bot->GetName().c_str(), _roles, many ? "several dungeons" : dungeon->Name[0]); - } - else if (rbotAId) - { - pState->SetType(LFG_TYPE_RAID); - LOG_INFO("playerbots", "Bot {} {}:{} <{}>: queues LFG, RbotAId as {} ({})", - bot->GetGUID().ToString().c_str(), bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(), bot->GetName().c_str(), _roles, many ? "several dungeons" : dungeon->Name[0]); - } - else - { - pState->SetType(LFG_TYPE_DUNGEON); - LOG_INFO("playerbots", "Bot {} {}:{} <{}>: queues LFG, Dungeon as {} ({})", - bot->GetGUID().ToString().c_str(), bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(), bot->GetName().c_str(), _roles, many ? "several dungeons" : dungeon->Name[0]); - }*/ - // Set RbotAId Browser comment std::string const _gs = std::to_string(botAI->GetEquipGearScore(bot, false, false)); - sLFGMgr->JoinLfg(bot, roleMask, list, _gs); return true; diff --git a/src/strategy/actions/LootAction.cpp b/src/strategy/actions/LootAction.cpp index f0526328..10ae1ccc 100644 --- a/src/strategy/actions/LootAction.cpp +++ b/src/strategy/actions/LootAction.cpp @@ -12,6 +12,7 @@ #include "PlayerbotAIConfig.h" #include "Playerbots.h" #include "ServerFacade.h" +#include "GuildMgr.h" bool LootAction::Execute(Event event) { @@ -416,6 +417,23 @@ bool StoreLootAction::Execute(Event event) if (proto->Quality >= ITEM_QUALITY_RARE && !urand(0, 1) && botAI->HasStrategy("emote", BOT_STATE_NON_COMBAT)) botAI->PlayEmote(TEXT_EMOTE_CHEER); + if (sPlayerbotAIConfig->randomBotGuildTalk && bot->GetGuildId() && urand(0, 10) && proto->Quality >= ITEM_QUALITY_RARE) + { + Guild* guild = sGuildMgr->GetGuildById(bot->GetGuildId()); + + if (guild) + { + std::string toSay = ""; + + if (urand(0, 3)) + toSay = "Yay I looted " + chat->FormatItem(proto) + " !"; + else + toSay = "Guess who got a " + chat->FormatItem(proto) + " ? Me !"; + + guild->BroadcastToGuild(bot->GetSession(), false, toSay, LANG_UNIVERSAL); + } + } + // std::ostringstream out; // out << "Looting " << chat->FormatItem(proto); // botAI->TellMasterNoFacing(out.str()); diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index 3de2c96f..e2288195 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -859,6 +859,21 @@ void MovementAction::UpdateMovementState() if (bot->IsFlying()) bot->UpdateSpeed(MOVE_FLIGHT, true); + + Transport* newTransport = bot->GetMap()->GetTransportForPos(bot->GetPhaseMask(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), bot); + if (newTransport != bot->GetTransport()) + { + LOG_DEBUG("playerbots", "Bot {} is on a transport", bot->GetName()); + + if (bot->GetTransport()) + bot->GetTransport()->RemovePassenger(bot, true); + + if (newTransport) + newTransport->AddPassenger(bot, true); + + bot->StopMovingOnCurrentPos(); + } + // Temporary speed increase in group //if (botAI->HasRealPlayerMaster()) //bot->SetSpeedRate(MOVE_RUN, 1.1f); @@ -1565,7 +1580,7 @@ bool AvoidAoeAction::AvoidAuraWithDynamicObj() return false; } std::ostringstream name; - name << spellInfo->SpellName[sWorld->GetDefaultDbcLocale()]; // << "] (aura)"; + name << spellInfo->SpellName[LOCALE_enUS]; // << "] (aura)"; if (FleePosition(dynOwner->GetPosition(), radius)) { if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) { lastTellTimer = time(NULL); @@ -1605,7 +1620,7 @@ bool AvoidAoeAction::AvoidGameObjectWithDamage() continue; } const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId); - if (spellInfo->IsPositive()) { + if (!spellInfo || spellInfo->IsPositive()) { continue; } float radius = (float)goInfo->trap.diameter / 2; @@ -1623,7 +1638,7 @@ bool AvoidAoeAction::AvoidGameObjectWithDamage() continue; } std::ostringstream name; - name << spellInfo->SpellName[sWorld->GetDefaultDbcLocale()]; // << "] (object)"; + name << spellInfo->SpellName[LOCALE_enUS]; // << "] (object)"; if (FleePosition(go->GetPosition(), radius)) { if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) { lastTellTimer = time(NULL); @@ -1672,7 +1687,7 @@ bool AvoidAoeAction::AvoidUnitWithDamageAura() break; } std::ostringstream name; - name << triggerSpellInfo->SpellName[sWorld->GetDefaultDbcLocale()]; //<< "] (unit)"; + name << triggerSpellInfo->SpellName[LOCALE_enUS]; //<< "] (unit)"; if (FleePosition(unit->GetPosition(), radius)) { if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) { lastTellTimer = time(NULL); diff --git a/src/strategy/actions/SayAction.cpp b/src/strategy/actions/SayAction.cpp index 02f03499..2bcca877 100644 --- a/src/strategy/actions/SayAction.cpp +++ b/src/strategy/actions/SayAction.cpp @@ -10,6 +10,16 @@ #include "GuildMgr.h" #include +static const std::unordered_set noReplyMsgs = { + "join", "leave", "follow", "attack", "pull", "flee", "reset", "reset ai", + "all ?", "talents", "talents list", "talents auto", "talk", "stay", "stats", + "who", "items", "leave", "join", "repair", "summon", "nc ?", "co ?", "de ?", + "dead ?", "follow", "los", "guard", "do accept invitation", "stats", "react ?", + "reset strats", "home", +}; +static const std::unordered_set noReplyMsgParts = { "+", "-","@" , "follow target", "focus heal", "cast ", "accept [", "e [", "destroy [", "go zone" }; +static const std::unordered_set noReplyMsgStarts = { "e ", "accept ", "cast ", "destroy " }; + SayAction::SayAction(PlayerbotAI* botAI) : Action(botAI, "say"), Qualified() { } @@ -105,6 +115,9 @@ bool SayAction::isUseful() if (!botAI->AllowActivity()) return false; + if (botAI->HasStrategy("silent", BotState::BOT_STATE_NON_COMBAT)) + return false; + time_t lastSaid = AI_VALUE2(time_t, "last said", qualifier); return (time(nullptr) - lastSaid) > 30; } @@ -114,6 +127,35 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32 type, uint32 guid1, uint32 ChatReplyType replyType = REPLY_NOT_UNDERSTAND; // default not understand std::string respondsText = ""; + // if we're just commanding bots around, don't respond... + // first one is for exact word matches + if (noReplyMsgs.find(msg) != noReplyMsgs.end()) { + /*std::ostringstream out; + out << "DEBUG ChatReplyDo decided to ignore exact blocklist match" << msg; + bot->Say(out.str(), LANG_UNIVERSAL);*/ + return; + } + + // second one is for partial matches like + or - where we change strats + if (std::any_of(noReplyMsgParts.begin(), noReplyMsgParts.end(), [&msg](const std::string& part) { return msg.find(part) != std::string::npos; })) { + /*std::ostringstream out; + out << "DEBUG ChatReplyDo decided to ignore partial blocklist match" << msg; + bot->Say(out.str(), LANG_UNIVERSAL);*/ + return; + } + + if (std::any_of(noReplyMsgStarts.begin(), noReplyMsgStarts.end(), [&msg](const std::string& start) { + return msg.find(start) == 0; // Check if the start matches the beginning of msg + })) { + /*std::ostringstream out; + out << "DEBUG ChatReplyDo decided to ignore start blocklist match" << msg; + bot->Say(out.str(), LANG_UNIVERSAL);*/ + return; + } + + ObjectGuid receiver = sCharacterCache->GetCharacterGuidByName(name); + Player* plr = ObjectAccessor::FindPlayer(receiver); + // Chat Logic int32 verb_pos = -1; int32 verb_type = -1; @@ -149,18 +191,16 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32 type, uint32 guid1, uint32 // Responds for (uint32 i = 0; i < 8; i++) { -// // blame gm with chat tag -// if (Player* plr = sObjectMgr->GetPlayer(ObjectGuid(HIGHGUID_PLAYER, guid1))) -// { -// if (plr->isGMChat()) -// { -// replyType = REPLY_ADMIN_ABUSE; -// found = true; -// break; -// } -// } -// - if (word[i] == "hi" || word[i] == "hey" || word[i] == "hello" || word[i] == "wazzup") + // blame gm with chat tag + if (plr && plr->isGMChat()) + { + replyType = REPLY_ADMIN_ABUSE; + found = true; + break; + } + + if (word[i] == "hi" || word[i] == "hey" || word[i] == "hello" || word[i] == "wazzup" + || word[i] == "salut" || word[i] == "plop" || word[i] == "yo") { replyType = REPLY_HELLO; found = true; @@ -169,22 +209,25 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32 type, uint32 guid1, uint32 if (verb_type < 4) { - if (word[i] == "am" || word[i] == "are" || word[i] == "is") + if (word[i] == "am" || word[i] == "are" || word[i] == "is" || word[i] == "suis" || word[i] == "as" || word[i] == "est" + || word[i] == "dois" || word[i] == "doit") { verb_pos = i; verb_type = 2; // present + if (verb_pos == 0) + is_quest = 1; } - else if (word[i] == "will") + else if (word[i] == "will" || word[i] == "vais" || word[i] == "sera") { verb_pos = i; verb_type = 3; // future } - else if (word[i] == "was" || word[i] == "were") + else if (word[i] == "was" || word[i] == "were" || word[i] == "été" || word[i] == "ai" || word[i] == "eu" || word[i] == "étions" || word[i] == "etion" ) { verb_pos = i; verb_type = 1; // past } - else if (word[i] == "shut" || word[i] == "noob") + else if (word[i] == "shut" || word[i] == "noob" || word[i] == "tg") { if (msg.find(bot->GetName()) == std::string::npos) { @@ -600,22 +643,20 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32 type, uint32 guid1, uint32 { if (type == CHAT_MSG_WHISPER) { - ObjectGuid receiver = sCharacterCache->GetCharacterGuidByName(name); - if (!receiver || !receiver.IsPlayer() || !ObjectAccessor::FindPlayer(receiver)) + if (plr) { - return; - } - if (bot->GetTeamId() == TEAM_ALLIANCE) - { - bot->Whisper(c, LANG_COMMON, ObjectAccessor::FindPlayer(receiver)); - } - else - { - bot->Whisper(c, LANG_ORCISH, ObjectAccessor::FindPlayer(receiver)); + if (bot->GetTeamId() == TEAM_ALLIANCE) + { + bot->Whisper(c, LANG_COMMON, plr); + } + else + { + bot->Whisper(c, LANG_ORCISH, plr); + } } } - if (type == CHAT_MSG_SAY) + else if (type == CHAT_MSG_SAY) { if (bot->GetTeamId() == TEAM_ALLIANCE) bot->Say(respondsText, LANG_COMMON); @@ -623,7 +664,7 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32 type, uint32 guid1, uint32 bot->Say(respondsText, LANG_ORCISH); } - if (type == CHAT_MSG_YELL) + else if (type == CHAT_MSG_YELL) { if (bot->GetTeamId() == TEAM_ALLIANCE) bot->Yell(respondsText, LANG_COMMON); @@ -631,9 +672,9 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32 type, uint32 guid1, uint32 bot->Yell(respondsText, LANG_ORCISH); } - if (type == CHAT_MSG_GUILD) + else if (type == CHAT_MSG_GUILD) { - if (!bot->GetGuildId()) + if (!bot->GetGuildId() || !sPlayerbotAIConfig->randomBotGuildTalk) return; Guild* guild = sGuildMgr->GetGuildById(bot->GetGuildId()); @@ -645,4 +686,4 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32 type, uint32 guid1, uint32 } GET_PLAYERBOT_AI(bot)->GetAiObjectContext()->GetValue("last said", "chat")->Set(time(nullptr) + urand(5, 25)); } -} \ No newline at end of file +} diff --git a/src/strategy/actions/SellAction.cpp b/src/strategy/actions/SellAction.cpp index cafe9192..9bcf8dac 100644 --- a/src/strategy/actions/SellAction.cpp +++ b/src/strategy/actions/SellAction.cpp @@ -74,7 +74,7 @@ bool SellAction::Execute(Event event) return true; } - if (text == "all") + if (text != "") { std::vector items = parseItems(text, ITERATE_ITEMS_IN_BAGS); for (Item *item : items) @@ -84,7 +84,7 @@ bool SellAction::Execute(Event event) return true; } - botAI->TellError("Usage: s gray/*/vendor/all"); + botAI->TellError("Usage: s gray/*/vendor/[item link]"); return false; } diff --git a/src/strategy/actions/SuggestWhatToDoAction.cpp b/src/strategy/actions/SuggestWhatToDoAction.cpp index 3c797193..391978f1 100644 --- a/src/strategy/actions/SuggestWhatToDoAction.cpp +++ b/src/strategy/actions/SuggestWhatToDoAction.cpp @@ -3,6 +3,7 @@ */ #include "SuggestWhatToDoAction.h" +#include "ServerFacade.h" #include "ChannelMgr.h" #include "Event.h" #include "ItemVisitors.h" @@ -11,24 +12,47 @@ #include "Playerbots.h" #include "PlayerbotTextMgr.h" #include "GuildMgr.h" +#include "Config.h" -std::map SuggestWhatToDoAction::instances; +#include + +enum eTalkType +{ + /*0x18*/ General = ChannelFlags::CHANNEL_FLAG_GENERAL | ChannelFlags::CHANNEL_FLAG_NOT_LFG, + /*0x3C*/ Trade = ChannelFlags::CHANNEL_FLAG_CITY | ChannelFlags::CHANNEL_FLAG_GENERAL | ChannelFlags::CHANNEL_FLAG_NOT_LFG | ChannelFlags::CHANNEL_FLAG_TRADE, + /*0x18*/ LocalDefence = ChannelFlags::CHANNEL_FLAG_GENERAL | ChannelFlags::CHANNEL_FLAG_NOT_LFG, + /*x038*/ GuildRecruitment = ChannelFlags::CHANNEL_FLAG_CITY | ChannelFlags::CHANNEL_FLAG_GENERAL | ChannelFlags::CHANNEL_FLAG_NOT_LFG, + /*0x50*/ LookingForGroup = ChannelFlags::CHANNEL_FLAG_LFG | ChannelFlags::CHANNEL_FLAG_GENERAL +}; + +std::map SuggestDungeonAction::instances; std::map SuggestWhatToDoAction::factions; -SuggestWhatToDoAction::SuggestWhatToDoAction(PlayerbotAI* botAI, std::string const name) : InventoryAction(botAI, name) +SuggestWhatToDoAction::SuggestWhatToDoAction(PlayerbotAI* botAI, std::string const name) + : InventoryAction{ botAI, name } + , _dbc_locale{ sWorld->GetDefaultDbcLocale() } { - suggestions.push_back(&SuggestWhatToDoAction::specificQuest); - suggestions.push_back(&SuggestWhatToDoAction::grindReputation); - suggestions.push_back(&SuggestWhatToDoAction::something); + suggestions.push_back(std::bind(&SuggestWhatToDoAction::specificQuest, this)); + suggestions.push_back(std::bind(&SuggestWhatToDoAction::grindReputation, this)); + suggestions.push_back(std::bind(&SuggestWhatToDoAction::something, this)); + suggestions.push_back(std::bind(&SuggestWhatToDoAction::grindMaterials, this)); } -bool SuggestWhatToDoAction::Execute(Event event) +bool SuggestWhatToDoAction::isUseful() { if (!sRandomPlayerbotMgr->IsRandomBot(bot) || bot->GetGroup() || bot->GetInstanceId()) return false; + std::string qualifier = "suggest what to do"; + time_t lastSaid = AI_VALUE2(time_t, "last said", qualifier); + return (time(0) - lastSaid) > 30; +} + +bool SuggestWhatToDoAction::Execute(Event event) +{ uint32 index = rand() % suggestions.size(); - (this->*suggestions[index])(); + auto fnct_ptr = suggestions[index]; + fnct_ptr(); std::string const qualifier = "suggest what to do"; time_t lastSaid = AI_VALUE2(time_t, "last said", qualifier); @@ -37,73 +61,6 @@ bool SuggestWhatToDoAction::Execute(Event event) return true; } -void SuggestWhatToDoAction::instance() -{ - if (instances.empty()) - { - instances["Ragefire Chasm"] = 15; - instances["Deadmines"] = 18; - instances["Wailing Caverns"] = 18; - instances["Shadowfang Keep"] = 25; - instances["Blackfathom Deeps"] = 20; - instances["Stockade"] = 20; - instances["Gnomeregan"] = 35; - instances["Razorfen Kraul"] = 35; - instances["Maraudon"] = 50; - instances["Scarlet Monestery"] = 40; - instances["Uldaman"] = 45; - instances["Dire Maul"] = 58; - instances["Scholomance"] = 59; - instances["Razorfen Downs"] = 40; - instances["Strathholme"] = 59; - instances["Zul'Farrak"] = 45; - instances["Blackrock Depths"] = 55; - instances["Temple of Atal'Hakkar"] = 55; - instances["Lower Blackrock Spire"] = 57; - - instances["Hellfire Citidel"] = 65; - instances["Coilfang Reservoir"] = 65; - instances["Auchindoun"] = 65; - instances["Cavens of Time"] = 68; - instances["Tempest Keep"] = 69; - instances["Magister's Terrace"] = 70; - - instances["Utgarde Keep"] = 75; - instances["The Nexus"] = 75; - instances["Ahn'kahet: The Old Kingdom"] = 75; - instances["Azjol-Nerub"] = 75; - instances["Drak'Tharon Keep"] = 75; - instances["Violet Hold"] = 80; - instances["Gundrak"] = 77; - instances["Halls of Stone"] = 77; - instances["Halls of Lightning"] = 77; - instances["Oculus"] = 77; - instances["Utgarde Pinnacle"] = 77; - instances["Trial of the Champion"] = 80; - instances["Forge of Souls"] = 80; - instances["Pit of Saron"] = 80; - instances["Halls of Reflection"] = 80; - } - - std::vector allowedInstances; - for (auto & instance : instances) - { - if (bot->GetLevel() >= instance.second) allowedInstances.push_back(instance.first); - } - - if (allowedInstances.empty()) return; - - std::map placeholders; - placeholders["%role"] = ChatHelper::FormatClass(bot, AiFactory::GetPlayerSpecTab(bot)); - - std::ostringstream itemout; - //itemout << "|c00b000b0" << allowedInstances[urand(0, allowedInstances.size() - 1)] << "|r"; - itemout << allowedInstances[urand(0, allowedInstances.size() - 1)]; - placeholders["%instance"] = itemout.str(); - - spam(BOT_TEXT2("suggest_instance", placeholders), urand(0, 1) ? 0x50 : 0, urand(0, 2), urand(0, 2)); -} - std::vector SuggestWhatToDoAction::GetIncompletedQuests() { std::vector result; @@ -136,7 +93,52 @@ void SuggestWhatToDoAction::specificQuest() placeholders["%role"] = chat->FormatClass(bot, AiFactory::GetPlayerSpecTab(bot)); placeholders["%quest"] = chat->FormatQuest(quest); - spam(BOT_TEXT2("suggest_quest", placeholders), urand(0, 1) ? 0x18 : 0, urand(0, 2), urand(0, 2)); + spam(BOT_TEXT2("suggest_quest", placeholders), urand(0, 1) ? eTalkType::General : 0, !urand(0, 2), !urand(0, 3)); +} + +void SuggestWhatToDoAction::grindMaterials() +{ + /*if (bot->GetLevel() <= 5) + return; + + auto result = CharacterDatabase.Query("SELECT distinct category, multiplier FROM ahbot_category where category not in ('other', 'quest', 'trade', 'reagent') and multiplier > 3 order by multiplier desc limit 10"); + if (!result) + return; + + std::map categories; + do + { + Field* fields = result->Fetch(); + categories[fields[0].Get()] = fields[1].Get(); + } while (result->NextRow()); + + for (std::map::iterator i = categories.begin(); i != categories.end(); ++i) + { + if (urand(0, 10) < 3) { + std::string name = i->first; + double multiplier = i->second; + + for (int j = 0; j < ahbot::CategoryList::instance.size(); j++) + { + ahbot::Category* category = ahbot::CategoryList::instance[j]; + if (name == category->GetName()) + { + std::string item = category->GetLabel(); + transform(item.begin(), item.end(), item.begin(), ::tolower); + std::ostringstream itemout; + itemout << "|c0000b000" << item << "|r"; + item = itemout.str(); + + std::map placeholders; + placeholders["%role"] = chat->formatClass(bot, AiFactory::GetPlayerSpecTab(bot)); + placeholders["%category"] = item; + + spam(BOT_TEXT2("suggest_trade", placeholders), urand(0, 1) ? 0x3C : 0x18, !urand(0, 2), !urand(0, 3)); + return; + } + } + } + }*/ } void SuggestWhatToDoAction::grindReputation() @@ -185,12 +187,11 @@ void SuggestWhatToDoAction::grindReputation() levels.push_back("exalted"); std::vector allowedFactions; - for (std::map::iterator i = factions.begin(); i != factions.end(); ++i) + for (const auto& i : factions) { - if (bot->GetLevel() >= i->second) - allowedFactions.push_back(i->first); + if (bot->GetLevel() >= i.second) + allowedFactions.push_back(i.first); } - if (allowedFactions.empty()) return; @@ -207,7 +208,7 @@ void SuggestWhatToDoAction::grindReputation() itemout << allowedFactions[urand(0, allowedFactions.size() - 1)]; placeholders["%faction"] = itemout.str(); - spam(BOT_TEXT2("suggest_faction", placeholders), 0x18, true); + spam(BOT_TEXT2("suggest_faction", placeholders), eTalkType::General, true); } void SuggestWhatToDoAction::something() @@ -221,10 +222,10 @@ void SuggestWhatToDoAction::something() std::ostringstream out; // out << "|cffb04040" << entry->area_name[0] << "|r"; - out << entry->area_name[0]; + out << entry->area_name[_dbc_locale]; placeholders["%zone"] = out.str(); - spam(BOT_TEXT2("suggest_something", placeholders), urand(0, 1) ? 0x18 : 0, urand(0, 2), urand(0, 2)); + spam(BOT_TEXT2("suggest_something", placeholders), urand(0, 1) ? eTalkType::General : 0, !urand(0, 2), !urand(0, 3)); } void SuggestWhatToDoAction::spam(std::string msg, uint8 flags, bool worldChat, bool guild) @@ -237,58 +238,65 @@ void SuggestWhatToDoAction::spam(std::string msg, uint8 flags, bool worldChat, b if (!cMgr) return; + AreaTableEntry const* zone = sAreaTableStore.LookupEntry(bot->GetMap()->GetZoneId(bot->GetPhaseMask(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ())); + if (!zone) return; + /*AreaTableEntry const* area = sAreaTableStore.LookupEntry(bot->GetMap()->GetAreaId(bot->GetPhaseMask(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ())); + if (!area) return;*/ for (uint32 i = 0; i < sChatChannelsStore.GetNumRows(); ++i) { ChatChannelsEntry const* channel = sChatChannelsStore.LookupEntry(i); if (!channel) continue; - for (AreaTableEntry const* current_zone : sAreaTableStore) + // combine full channel name + char channelName[100]; + Channel* chn = nullptr; + if (channel->ChannelID == 24 || (channel->flags & CHANNEL_DBC_FLAG_LFG) != 0) { - if (!current_zone) - continue; + std::string chanName = channel->pattern[_dbc_locale]; + chn = cMgr->GetChannel(chanName, bot); + } + else + { + snprintf(channelName, 100, channel->pattern[_dbc_locale], zone->area_name[_dbc_locale]); + chn = cMgr->GetChannel(channelName, bot); + } + if (!chn) + continue; + // skip world chat here + if (chn->GetName() == "World") + continue; - // combine full channel name - char channelName[100]; - Channel* chn = nullptr; - if ((channel->flags & CHANNEL_DBC_FLAG_LFG) != 0) - { - std::string chanName = channel->pattern[0]; - chn = cMgr->GetChannel(chanName, bot); - } - else - { - snprintf(channelName, 100, channel->pattern[0], current_zone->area_name[0]); - chn = cMgr->GetChannel(channelName, bot); - } - if (!chn) - continue; - // skip world chat here - if (chn->GetName() == "World") - continue; + if (flags != 0 && chn->GetFlags() != flags) + continue; - if (flags != 0 && chn->GetFlags() != flags) - continue; + // skip local defense and universal defense + if (chn->GetChannelId() == 22 || chn->GetChannelId() == 23) + continue; - // skip local defense - //if (chn->GetFlags() == 0x18) - // continue; - - // no filter, pick several options - if (flags == CHANNEL_FLAG_NONE) - { - channelNames.push_back(chn->GetName()); - } - else - chn->Say(bot->GetGUID(), msg.c_str(), LANG_UNIVERSAL); + // no filter, pick several options + if (flags == CHANNEL_FLAG_NONE) + { + channelNames.push_back(chn->GetName()); + } + else + { + if (!bot->IsInChannel(chn)) + chn->JoinChannel(bot, ""); + chn->Say(bot->GetGUID(), msg.c_str(), LANG_UNIVERSAL); } if (!channelNames.empty()) { std::string randomName = channelNames[urand(0, channelNames.size() - 1)]; if (Channel* chn = cMgr->GetChannel(randomName, bot)) + { + if (!bot->IsInChannel(chn)) + chn->JoinChannel(bot, ""); + chn->Say(bot->GetGUID(), msg.c_str(), LANG_UNIVERSAL); + } } if (worldChat) @@ -296,13 +304,13 @@ void SuggestWhatToDoAction::spam(std::string msg, uint8 flags, bool worldChat, b if (Channel* worldChannel = cMgr->GetChannel("World", bot)) worldChannel->Say(bot->GetGUID(), msg.c_str(), LANG_UNIVERSAL); } + } - if (guild && bot->GetGuildId()) - { - Guild* guild = sGuildMgr->GetGuildById(bot->GetGuildId()); - if (guild) - guild->BroadcastToGuild(bot->GetSession(), false, msg.c_str(), LANG_UNIVERSAL); - } + if (sPlayerbotAIConfig->randomBotGuildTalk && guild && bot->GetGuildId()) + { + Guild* guild = sGuildMgr->GetGuildById(bot->GetGuildId()); + if (guild) + guild->BroadcastToGuild(bot->GetSession(), false, msg.c_str(), LANG_UNIVERSAL); } } @@ -337,15 +345,88 @@ class FindTradeItemsVisitor : public IterateItemsVisitor uint32 quality; }; +SuggestDungeonAction::SuggestDungeonAction(PlayerbotAI* botAI) : SuggestWhatToDoAction(botAI, "suggest dungeon") +{ +} + +bool SuggestDungeonAction::Execute(Event event) +{ + // TODO: use sPlayerbotDungeonSuggestionMgr + + if (!sPlayerbotAIConfig->randomBotSuggestDungeons || bot->GetGroup()) return false; + + if (instances.empty()) + { + instances["Ragefire Chasm"] = 15; + instances["Deadmines"] = 18; + instances["Wailing Caverns"] = 18; + instances["Shadowfang Keep"] = 25; + instances["Blackfathom Deeps"] = 20; + instances["Stockade"] = 20; + instances["Gnomeregan"] = 35; + instances["Razorfen Kraul"] = 35; + instances["Maraudon"] = 50; + instances["Scarlet Monestery"] = 40; + instances["Uldaman"] = 45; + instances["Dire Maul"] = 58; + instances["Scholomance"] = 59; + instances["Razorfen Downs"] = 40; + instances["Strathholme"] = 59; + instances["Zul'Farrak"] = 45; + instances["Blackrock Depths"] = 55; + instances["Temple of Atal'Hakkar"] = 55; + instances["Lower Blackrock Spire"] = 57; + + instances["Hellfire Citidel"] = 65; + instances["Coilfang Reservoir"] = 65; + instances["Auchindoun"] = 65; + instances["Cavens of Time"] = 68; + instances["Tempest Keep"] = 69; + instances["Magister's Terrace"] = 70; + + instances["Utgarde Keep"] = 75; + instances["The Nexus"] = 75; + instances["Ahn'kahet: The Old Kingdom"] = 75; + instances["Azjol-Nerub"] = 75; + instances["Drak'Tharon Keep"] = 75; + instances["Violet Hold"] = 80; + instances["Gundrak"] = 77; + instances["Halls of Stone"] = 77; + instances["Halls of Lightning"] = 77; + instances["Oculus"] = 77; + instances["Utgarde Pinnacle"] = 77; + instances["Trial of the Champion"] = 80; + instances["Forge of Souls"] = 80; + instances["Pit of Saron"] = 80; + instances["Halls of Reflection"] = 80; + } + + std::vector allowedInstances; + for (const auto& instance : instances) + { + if (bot->GetLevel() >= instance.second) + allowedInstances.push_back(instance.first); + } + if (allowedInstances.empty()) return false; + + std::map placeholders; + placeholders["%role"] = ChatHelper::FormatClass(bot, AiFactory::GetPlayerSpecTab(bot)); + + std::ostringstream itemout; + //itemout << "|c00b000b0" << allowedInstances[urand(0, allowedInstances.size() - 1)] << "|r"; + itemout << allowedInstances[urand(0, allowedInstances.size() - 1)]; + placeholders["%instance"] = itemout.str(); + + spam(BOT_TEXT2("suggest_instance", placeholders), urand(0, 1) ? eTalkType::LookingForGroup : 0, !urand(0, 2), !urand(0, 3)); + return true; +} + SuggestTradeAction::SuggestTradeAction(PlayerbotAI* botAI) : SuggestWhatToDoAction(botAI, "suggest trade") { } bool SuggestTradeAction::Execute(Event event) { - if (!sRandomPlayerbotMgr->IsRandomBot(bot) || bot->GetGroup() || bot->GetInstanceId()) - return false; - uint32 quality = urand(0, 100); if (quality > 95) quality = ITEM_QUALITY_LEGENDARY; @@ -400,13 +481,7 @@ bool SuggestTradeAction::Execute(Event event) placeholders["%item"] = chat->FormatItem(proto, count); placeholders["%gold"] = chat->formatMoney(price); - spam(BOT_TEXT2("suggest_sell", placeholders), urand(0, 1) ? 0x3C : 0, urand(0, 1), urand(0, 5)); + + spam(BOT_TEXT2("suggest_sell", placeholders), urand(0, 1) ? eTalkType::Trade : 0, !urand(0, 2), urand(0, 5)); return true; } - -bool SuggestWhatToDoAction::isUseful() -{ - std::string const qualifier = "suggest what to do"; - time_t lastSaid = AI_VALUE2(time_t, "last said", qualifier); - return (time(nullptr) - lastSaid) > 30; -} diff --git a/src/strategy/actions/SuggestWhatToDoAction.h b/src/strategy/actions/SuggestWhatToDoAction.h index 4bff50cf..46391ff2 100644 --- a/src/strategy/actions/SuggestWhatToDoAction.h +++ b/src/strategy/actions/SuggestWhatToDoAction.h @@ -18,19 +18,19 @@ class SuggestWhatToDoAction : public InventoryAction bool isUseful() override; protected: - typedef void (SuggestWhatToDoAction::*Suggestion)(); + using Suggestion = std::function; std::vector suggestions; - void instance(); void specificQuest(); void grindReputation(); + void grindMaterials(); void something(); void spam(std::string msg, uint8 flags = 0, bool worldChat = false, bool guild = false); std::vector GetIncompletedQuests(); private: - static std::map instances; static std::map factions; + const int32_t _dbc_locale; }; class SuggestTradeAction : public SuggestWhatToDoAction @@ -42,4 +42,15 @@ class SuggestTradeAction : public SuggestWhatToDoAction bool isUseful() override { return true; } }; +class SuggestDungeonAction : public SuggestWhatToDoAction +{ + public: + SuggestDungeonAction(PlayerbotAI* botAI); + + bool Execute(Event event) override; + bool isUseful() override { return true; } + private: + static std::map instances; +}; + #endif diff --git a/src/strategy/actions/TellLosAction.cpp b/src/strategy/actions/TellLosAction.cpp index 8c729cf3..8917d2ed 100644 --- a/src/strategy/actions/TellLosAction.cpp +++ b/src/strategy/actions/TellLosAction.cpp @@ -6,6 +6,7 @@ #include "Event.h" #include "ChatHelper.h" #include "Playerbots.h" +#include "World.h" bool TellLosAction::Execute(Event event) { @@ -52,7 +53,7 @@ void TellLosAction::ListUnits(std::string const title, GuidVector units) for (ObjectGuid const guid : units) { if (Unit* unit = botAI->GetUnit(guid)) { - botAI->TellMaster(unit->GetName()); + botAI->TellMaster(unit->GetNameForLocaleIdx(sWorld->GetDefaultDbcLocale())); } } @@ -137,4 +138,4 @@ bool TellExpectedDpsAction::Execute(Event event) float dps = AI_VALUE(float, "expected group dps"); botAI->TellMaster("Expected Group DPS: " + std::to_string(dps)); return true; -} \ No newline at end of file +} diff --git a/src/strategy/actions/TrainerAction.cpp b/src/strategy/actions/TrainerAction.cpp index fb994ae5..6f13d41f 100644 --- a/src/strategy/actions/TrainerAction.cpp +++ b/src/strategy/actions/TrainerAction.cpp @@ -172,6 +172,7 @@ bool MaintenanceAction::Execute(Event event) factory.ApplyEnchantAndGemsNew(); } bot->DurabilityRepairAll(false, 1.0f, false); + bot->SendTalentsInfoData(false); return true; } @@ -181,6 +182,7 @@ bool RemoveGlyphAction::Execute(Event event) { bot->SetGlyph(slotIndex, 0, true); } + bot->SendTalentsInfoData(false); return true; } diff --git a/src/strategy/actions/UseMeetingStoneAction.cpp b/src/strategy/actions/UseMeetingStoneAction.cpp index 5cf93e14..3ef3b0b1 100644 --- a/src/strategy/actions/UseMeetingStoneAction.cpp +++ b/src/strategy/actions/UseMeetingStoneAction.cpp @@ -164,15 +164,15 @@ bool SummonAction::SummonUsingNpcs(Player* summoner, Player* player) bool SummonAction::Teleport(Player* summoner, Player* player) { Player* master = GetMaster(); - if (master->GetMap() && master->GetMap()->IsDungeon()) { - InstanceMap* map = master->GetMap()->ToInstanceMap(); - if (map) { - if (map->CannotEnter(player) == Map::CANNOT_ENTER_MAX_PLAYERS) { - botAI->TellError("I can not enter this dungeon"); - return false; - } - } - } + // if (master->GetMap() && master->GetMap()->IsDungeon()) { + // InstanceMap* map = master->GetMap()->ToInstanceMap(); + // if (map) { + // if (map->CannotEnter(player, true) == Map::CANNOT_ENTER_MAX_PLAYERS) { + // botAI->TellError("I can not enter this dungeon"); + // return false; + // } + // } + // } if (!summoner->IsBeingTeleported() && !player->IsBeingTeleported()) { float followAngle = GetFollowAngle(); @@ -206,7 +206,8 @@ bool SummonAction::Teleport(Player* summoner, Player* player) return false; } - if (bot->isDead() && sPlayerbotAIConfig->reviveBotWhenSummoned) + bool revive = sPlayerbotAIConfig->reviveBotWhenSummoned == 2 || (sPlayerbotAIConfig->reviveBotWhenSummoned == 1 && !master->IsInCombat() && master->IsAlive()); + if (bot->isDead() && revive) { bot->ResurrectPlayer(1.0f, false); botAI->TellMasterNoFacing("I live, again!"); diff --git a/src/strategy/actions/XpGainAction.cpp b/src/strategy/actions/XpGainAction.cpp index 6dcca4a4..8dc98ca6 100644 --- a/src/strategy/actions/XpGainAction.cpp +++ b/src/strategy/actions/XpGainAction.cpp @@ -6,6 +6,7 @@ #include "Event.h" #include "PlayerbotAIConfig.h" #include "Playerbots.h" +#include "GuildMgr.h" bool XpGainAction::Execute(Event event) { @@ -32,6 +33,26 @@ bool XpGainAction::Execute(Event event) p >> groupBonus; // 8 group bonus } + if (sPlayerbotAIConfig->randomBotGuildTalk && bot->GetGuildId() && urand(0, 10)) + { + Creature* creature = botAI->GetCreature(guid); + if (creature && (creature->isElite() || creature->isWorldBoss() || creature->GetLevel() > 61 || creature->GetLevel() > bot->GetLevel() + 4)) + { + Guild* guild = sGuildMgr->GetGuildById(bot->GetGuildId()); + if (guild) + { + std::string toSay = ""; + + if (urand(0, 3)) + toSay = "Wow I just killed " + creature->GetName() + " !"; + else + toSay = "Awesome that " + creature->GetName() + " went down quickly !"; + + guild->BroadcastToGuild(bot->GetSession(), false, toSay, LANG_UNIVERSAL); + } + } + } + Unit* victim = nullptr; if (guid) victim = botAI->GetUnit(guid); diff --git a/src/strategy/deathknight/UnholyDKStrategy.cpp b/src/strategy/deathknight/UnholyDKStrategy.cpp index 5bc5f500..8edab828 100644 --- a/src/strategy/deathknight/UnholyDKStrategy.cpp +++ b/src/strategy/deathknight/UnholyDKStrategy.cpp @@ -72,12 +72,12 @@ UnholyDKStrategy::UnholyDKStrategy(PlayerbotAI* botAI) : GenericDKStrategy(botAI NextAction** UnholyDKStrategy::getDefaultActions() { return NextAction::array(0, - new NextAction("death and decay", ACTION_DEFAULT + 1.0f), - new NextAction("scourge strike", ACTION_DEFAULT + 0.8f), - new NextAction("horn of winter", ACTION_DEFAULT + 0.6f), - new NextAction("summon gargoyle", ACTION_DEFAULT + 0.4f), + new NextAction("death and decay", ACTION_DEFAULT + 0.5f), + new NextAction("horn of winter", ACTION_DEFAULT + 0.4f), + new NextAction("summon gargoyle", ACTION_DEFAULT + 0.3f), new NextAction("death coil", ACTION_DEFAULT + 0.2f), - new NextAction("melee", ACTION_DEFAULT), + new NextAction("scourge strike", ACTION_NORMAL + 0.1f), + new NextAction("melee", ACTION_DEFAULT), nullptr); } @@ -91,8 +91,8 @@ void UnholyDKStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode("no desolation", NextAction::array(0, new NextAction("blood strike", ACTION_HIGH + 4), nullptr))); triggers.push_back(new TriggerNode("death and decay cooldown", NextAction::array(0, - new NextAction("ghoul frenzy", ACTION_DEFAULT + 5.0f), - new NextAction("scourge strike", ACTION_DEFAULT + 4.0f), + new NextAction("ghoul frenzy", ACTION_NORMAL + 5.0f), + new NextAction("scourge strike", ACTION_NORMAL + 4.0f), new NextAction("blood boil", ACTION_NORMAL + 3.0f), new NextAction("icy touch", ACTION_NORMAL + 2.0f), new NextAction("plague strike", ACTION_NORMAL + 1.0f), diff --git a/src/strategy/generic/EmoteStrategy.cpp b/src/strategy/generic/EmoteStrategy.cpp index a008f48e..d3b72fa3 100644 --- a/src/strategy/generic/EmoteStrategy.cpp +++ b/src/strategy/generic/EmoteStrategy.cpp @@ -7,22 +7,24 @@ void EmoteStrategy::InitTriggers(std::vector& triggers) { - triggers.push_back(new TriggerNode("seldom", NextAction::array(0, new NextAction("emote", 1.0f), nullptr))); - triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("suggest what to do", 1.0f), nullptr))); - triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("suggest trade", 1.0f), nullptr))); - - if (sPlayerbotAIConfig->randomBotSuggestDungeons) + if (sPlayerbotAIConfig->randomBotEmote) { - triggers.push_back(new TriggerNode("random", NextAction::array(0, new NextAction("suggest dungeon", 1.0f), nullptr))); + triggers.push_back(new TriggerNode("seldom", NextAction::array(0, new NextAction("emote", 1.0f), nullptr))); + triggers.push_back(new TriggerNode("receive text emote", NextAction::array(0, new NextAction("emote", 10.0f), nullptr))); + triggers.push_back(new TriggerNode("receive emote", NextAction::array(0, new NextAction("emote", 10.0f), nullptr))); + } + + if (sPlayerbotAIConfig->randomBotTalk) + { + triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("suggest what to do", 10.0f), + new NextAction("suggest dungeon", 3.0f), + new NextAction("suggest trade", 3.0f), + new NextAction("talk", 1.0f), + nullptr))); } if (sPlayerbotAIConfig->enableGreet) - { triggers.push_back(new TriggerNode("new player nearby", NextAction::array(0, new NextAction("greet", 1.0f), nullptr))); - } - triggers.push_back(new TriggerNode("seldom", NextAction::array(0, new NextAction("talk", 1.0f), nullptr))); - triggers.push_back(new TriggerNode("receive text emote", NextAction::array(0, new NextAction("emote", 10.0f), nullptr))); - triggers.push_back(new TriggerNode("receive emote", NextAction::array(0, new NextAction("emote", 10.0f), nullptr))); triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("rpg mount anim", 1.0f), nullptr))); } diff --git a/src/strategy/hunter/HunterTriggers.h b/src/strategy/hunter/HunterTriggers.h index b64c4b65..6896fd32 100644 --- a/src/strategy/hunter/HunterTriggers.h +++ b/src/strategy/hunter/HunterTriggers.h @@ -88,10 +88,10 @@ class FreezingTrapTrigger : public HasCcTargetTrigger FreezingTrapTrigger(PlayerbotAI* botAI) : HasCcTargetTrigger(botAI, "freezing trap") { } }; -class RapidFireTrigger : public BuffTrigger +class RapidFireTrigger : public BoostTrigger { public: - RapidFireTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "rapid fire") { } + RapidFireTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "rapid fire") { } }; class TrueshotAuraTrigger : public BuffTrigger diff --git a/src/strategy/mage/GenericMageStrategy.cpp b/src/strategy/mage/GenericMageStrategy.cpp index 682ddfb4..8c772ab4 100644 --- a/src/strategy/mage/GenericMageStrategy.cpp +++ b/src/strategy/mage/GenericMageStrategy.cpp @@ -180,7 +180,7 @@ void MageBoostStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode("icy veins", NextAction::array(0, new NextAction("icy veins", 50.0f), nullptr))); triggers.push_back(new TriggerNode("presence of mind", NextAction::array(0, new NextAction("presence of mind", 42.0f), nullptr))); // triggers.push_back(new TriggerNode("arcane power", NextAction::array(0, new NextAction("arcane power", 41.0f), nullptr))); - // triggers.push_back(new TriggerNode("mirror image", NextAction::array(0, new NextAction("mirror image", 41.0f), nullptr))); + triggers.push_back(new TriggerNode("mirror image", NextAction::array(0, new NextAction("mirror image", 41.0f), nullptr))); } void MageCcStrategy::InitTriggers(std::vector& triggers) diff --git a/src/strategy/shaman/ShamanActions.h b/src/strategy/shaman/ShamanActions.h index 2db469fb..1b8d0101 100644 --- a/src/strategy/shaman/ShamanActions.h +++ b/src/strategy/shaman/ShamanActions.h @@ -5,9 +5,7 @@ #ifndef _PLAYERBOT_SHAMANACTIONS_H #define _PLAYERBOT_SHAMANACTIONS_H -#include "Define.h" #include "GenericSpellActions.h" -#include "Playerbots.h" #include "SharedDefines.h" class PlayerbotAI; @@ -362,31 +360,11 @@ class CastWindShearOnEnemyHealerAction : public CastSpellOnEnemyHealerAction CastWindShearOnEnemyHealerAction(PlayerbotAI* botAI) : CastSpellOnEnemyHealerAction(botAI, "wind shear") { } }; -// class CastCurePoisonAction : public CastCureSpellAction -// { -// public: -// CastCurePoisonAction(PlayerbotAI* botAI) : CastCureSpellAction(botAI, "cure poison") { } -// }; +CURE_ACTION(CastCurePoisonActionSham, "cure disease"); +CURE_PARTY_ACTION(CastCurePoisonOnPartyActionSham, "cure poison", DISPEL_POISON); -// class CastCurePoisonOnPartyAction : public CurePartyMemberAction -// { -// public: -// CastCurePoisonOnPartyAction(PlayerbotAI* botAI) : CurePartyMemberAction(botAI, "cure poison", DISPEL_POISON) { } -// }; - -// class CastCureDiseaseAction : public CastCureSpellAction -// { -// public: -// CastCureDiseaseAction(PlayerbotAI* botAI) : CastCureSpellAction(botAI, "cure disease") { } -// }; - -// class CastCureDiseaseOnPartyAction : public CurePartyMemberAction -// { -// public: -// CastCureDiseaseOnPartyAction(PlayerbotAI* botAI) : CurePartyMemberAction(botAI, "cure disease", DISPEL_DISEASE) { } - -// std::string const getName() override { return "cure disease on party"; } -// }; +CURE_ACTION(CastCureDiseaseActionSham, "cure disease"); +CURE_PARTY_ACTION(CastCureDiseaseOnPartyActionSham, "cure disease", DISPEL_DISEASE); class CastLavaBurstAction : public CastSpellAction { diff --git a/src/strategy/shaman/ShamanAiObjectContext.cpp b/src/strategy/shaman/ShamanAiObjectContext.cpp index cee208c2..f7ac2223 100644 --- a/src/strategy/shaman/ShamanAiObjectContext.cpp +++ b/src/strategy/shaman/ShamanAiObjectContext.cpp @@ -213,10 +213,10 @@ class ShamanAiObjectContextInternal : public NamedObjectContext creators["heroism"] = &ShamanAiObjectContextInternal::heroism; creators["bloodlust"] = &ShamanAiObjectContextInternal::bloodlust; creators["elemental mastery"] = &ShamanAiObjectContextInternal::elemental_mastery; - // creators["cure disease"] = &ShamanAiObjectContextInternal::cure_disease; - // creators["cure disease on party"] = &ShamanAiObjectContextInternal::cure_disease_on_party; - // creators["cure poison"] = &ShamanAiObjectContextInternal::cure_poison; - // creators["cure poison on party"] = &ShamanAiObjectContextInternal::cure_poison_on_party; + creators["cure disease"] = &ShamanAiObjectContextInternal::cure_disease; + creators["cure disease on party"] = &ShamanAiObjectContextInternal::cure_disease_on_party; + creators["cure poison"] = &ShamanAiObjectContextInternal::cure_poison; + creators["cure poison on party"] = &ShamanAiObjectContextInternal::cure_poison_on_party; creators["lava burst"] = &ShamanAiObjectContextInternal::lava_burst; creators["earth shield on main tank"] = &ShamanAiObjectContextInternal::earth_shield_on_main_tank; creators["fire elemental totem"] = &ShamanAiObjectContextInternal::fire_elemental_totem; @@ -277,10 +277,10 @@ class ShamanAiObjectContextInternal : public NamedObjectContext static Action* lava_lash(PlayerbotAI* botAI) { return new CastLavaLashAction(botAI); } static Action* ancestral_spirit(PlayerbotAI* botAI) { return new CastAncestralSpiritAction(botAI); } static Action* wind_shear_on_enemy_healer(PlayerbotAI* botAI) { return new CastWindShearOnEnemyHealerAction(botAI); } - // static Action* cure_poison(PlayerbotAI* botAI) { return new CastCurePoisonAction(botAI); } - // static Action* cure_poison_on_party(PlayerbotAI* botAI) { return new CastCurePoisonOnPartyAction(botAI); } - // static Action* cure_disease(PlayerbotAI* botAI) { return new CastCureDiseaseAction(botAI); } - // static Action* cure_disease_on_party(PlayerbotAI* botAI) { return new CastCureDiseaseOnPartyAction(botAI); } + static Action* cure_poison(PlayerbotAI* botAI) { return new CastCurePoisonActionSham(botAI); } + static Action* cure_poison_on_party(PlayerbotAI* botAI) { return new CastCurePoisonOnPartyActionSham(botAI); } + static Action* cure_disease(PlayerbotAI* botAI) { return new CastCureDiseaseActionSham(botAI); } + static Action* cure_disease_on_party(PlayerbotAI* botAI) { return new CastCureDiseaseOnPartyActionSham(botAI); } static Action* lava_burst(PlayerbotAI* ai) { return new CastLavaBurstAction(ai); } static Action* earth_shield_on_main_tank(PlayerbotAI* ai) { return new CastEarthShieldOnMainTankAction(ai); } static Action* totem_of_wrath(PlayerbotAI* ai) { return new CastTotemOfWrathAction(ai); } diff --git a/src/strategy/values/GrindTargetValue.cpp b/src/strategy/values/GrindTargetValue.cpp index ae8fffdb..0be03904 100644 --- a/src/strategy/values/GrindTargetValue.cpp +++ b/src/strategy/values/GrindTargetValue.cpp @@ -160,7 +160,7 @@ bool GrindTargetValue::needForQuest(Unit* target) { QuestStatusData* questStatus = sTravelMgr->getQuestStatus(bot, questId); - if (questTemplate->GetQuestLevel() > bot->GetLevel()) + if (questTemplate->GetQuestLevel() > bot->GetLevel()+5) continue; for (int j = 0; j < QUEST_OBJECTIVES_COUNT; j++) diff --git a/src/strategy/values/SpellIdValue.cpp b/src/strategy/values/SpellIdValue.cpp index 0e7eef38..8cb8198f 100644 --- a/src/strategy/values/SpellIdValue.cpp +++ b/src/strategy/values/SpellIdValue.cpp @@ -6,6 +6,7 @@ #include "ChatHelper.h" #include "Playerbots.h" #include "Vehicle.h" +#include "World.h" SpellIdValue::SpellIdValue(PlayerbotAI* botAI) : CalculatedValue(botAI, "spell id", 20 * 1000) { @@ -34,7 +35,7 @@ uint32 SpellIdValue::Calculate() char firstSymbol = tolower(namepart[0]); int spellLength = wnamepart.length(); - LocaleConstant loc = bot->GetSession()->GetSessionDbcLocale(); + LocaleConstant loc = LOCALE_enUS; std::set spellIds; for (PlayerSpellMap::iterator itr = bot->GetSpellMap().begin(); itr != bot->GetSpellMap().end(); ++itr) @@ -189,7 +190,7 @@ uint32 VehicleSpellIdValue::Calculate() char firstSymbol = tolower(namepart[0]); int spellLength = wnamepart.length(); - int loc = bot->GetSession()->GetSessionDbcLocale(); + const int loc = LocaleConstant::LOCALE_enUS; Creature* creature = vehicleBase->ToCreature(); for (uint32 x = 0; x < MAX_CREATURE_SPELLS; ++x)