diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 19c31aca..f0c278d1 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -23,6 +23,12 @@ AiPlayerbot.EnableGuildTasks = 1 # Enable LFG for random bots AiPlayerbot.RandomBotJoinLfg = 1 +# Enable dungeon suggestions for random bots +AiPlayerbot.RandomBotSuggestDungeons = 1 + +# Enable dungeon suggestions in lower case randomly +AiPlayerbot.SuggestDungeonsInLowerCaseRandomly = 0 + # Enable BG/Arena for random Bots AiPlayerbot.RandomBotJoinBG = 1 diff --git a/playerbots_dungeon_suggestion_definition.sql b/playerbots_dungeon_suggestion_definition.sql new file mode 100644 index 00000000..6f532e09 --- /dev/null +++ b/playerbots_dungeon_suggestion_definition.sql @@ -0,0 +1,124 @@ +USE acore_playerbots; + +DROP TABLE IF EXISTS `playerbots_dungeon_suggestion_definition`; +CREATE TABLE `playerbots_dungeon_suggestion_definition` ( + `id` TINYINT(3) NOT NULL AUTO_INCREMENT UNIQUE, + `slug` VARCHAR(255) NOT NULL, + `name` VARCHAR(255) NOT NULL, + `expansion` TINYINT(1) NOT NULL, + `difficulty` TINYINT(1) NOT NULL, + `min_level` TINYINT(3), + `max_level` TINYINT(3), + `comment` VARCHAR(255), + + PRIMARY KEY (`id`) +) +ENGINE=MyISAM +DEFAULT CHARSET=utf8 +ROW_FORMAT=FIXED +COMMENT='Playerbot dungeon suggestion definitions'; + +INSERT INTO `playerbots_dungeon_suggestion_definition` VALUES + +-- == Vanilla == + +(NULL, 'rfc' , 'Ragefire Chasm' , 0, 0, 15, 21, NULL), +(NULL, 'dm' , 'Deadmines' , 0, 0, 15, 23, NULL), +(NULL, 'wc' , 'Wailing Caverns' , 0, 0, 17, 27, NULL), +(NULL, 'sfk' , 'Shadowfang Keep' , 0, 0, 18, 24, NULL), +(NULL, 'bfd' , 'Blackfathom Deeps' , 0, 0, 20, 27, NULL), +(NULL, 'stocks' , 'Stormwind Stockade' , 0, 0, 22, 30, NULL), +(NULL, 'gnomer' , 'Gnomeregan' , 0, 0, 26, 36, NULL), +(NULL, 'sm-gy' , 'Scarlet Monastery: Graveyard', 0, 0, 26, 36, NULL), +(NULL, 'sm-lib' , 'Scarlet Monastery: Library' , 0, 0, 29, 39, NULL), +(NULL, 'rfk' , 'Razorfen Kraul' , 0, 0, 32, 35, NULL), +(NULL, 'sm-armory' , 'Scarlet Monastery: Armory' , 0, 0, 32, 42, NULL), +(NULL, 'sm-cath' , 'Scarlet Monastery: Cathedral', 0, 0, 35, 45, NULL), +(NULL, 'rfd' , 'Razorfen Downs' , 0, 0, 42, 45, NULL), +(NULL, 'ulda' , 'Uldaman' , 0, 0, 42, 52, NULL), +(NULL, 'mara' , 'Maraudon' , 0, 0, 42, 52, NULL), +(NULL, 'zf' , 'Zul''Farrak' , 0, 0, 46, 56, NULL), +(NULL, 'st' , 'Temple of Atal''Hakkar' , 0, 0, 50, 60, NULL), +(NULL, 'brd' , 'Blackrock Depths' , 0, 0, 52, 60, NULL), +(NULL, 'dm-w' , 'Dire Maul: West' , 0, 0, 55, 60, NULL), +(NULL, 'dm-e' , 'Dire Maul: East' , 0, 0, 55, 60, NULL), +(NULL, 'dm-n' , 'Dire Maul: North' , 0, 0, 55, 60, NULL), +(NULL, 'lbrs' , 'Lower Blackrock Spire' , 0, 0, 55, 60, NULL), +(NULL, 'scholo' , 'Scholomance' , 0, 0, 58, 60, NULL), +(NULL, 'strat' , 'Stratholme' , 0, 0, 58, 60, NULL), + +-- == The Burning Crusade == + +(NULL, 'hfr' , 'Hellfire Ramparts' , 1, 0, 59, 67, NULL), +(NULL, 'bf' , 'The Blood Furnace' , 1, 0, 60, 68, NULL), +(NULL, 'sp' , 'The Slave Pens' , 1, 0, 61, 69, NULL), +(NULL, 'ub' , 'The Underbog' , 1, 0, 62, 70, NULL), +(NULL, 'mt' , 'Mana-Tombs' , 1, 0, 63, 70, NULL), +(NULL, 'ac' , 'Auchenai Crypts' , 1, 0, 64, 70, NULL), +(NULL, 'seth', 'Sethekk Halls' , 1, 0, 66, 70, NULL), +(NULL, 'oh' , 'Old Hillsbrad Foothills', 1, 0, 66, 70, NULL), +(NULL, 'bm' , 'The Black Morass' , 1, 0, 68, 70, NULL), +(NULL, 'mech', 'The Mechanar' , 1, 0, 68, 70, NULL), +(NULL, 'bot' , 'The Botanica' , 1, 0, 69, 70, NULL), +(NULL, 'arc' , 'The Arcatraz' , 1, 0, 69, 70, NULL), +(NULL, 'sh' , 'The Shattered Halls' , 1, 0, 69, 70, NULL), +(NULL, 'sv' , 'The Steamvault' , 1, 0, 69, 70, NULL), +(NULL, 'sl' , 'Shadow Labyrinth' , 1, 0, 69, 70, NULL), +(NULL, 'mgt' , 'Magister''s Terrace' , 1, 0, 70, 70, NULL), + +-- == The Burning Crusade (Heroic) == + +(NULL, 'hfr' , 'Hellfire Ramparts' , 1, 1, 70, 70, NULL), +(NULL, 'bf' , 'The Blood Furnace' , 1, 1, 70, 70, NULL), +(NULL, 'sp' , 'The Slave Pens' , 1, 1, 70, 70, NULL), +(NULL, 'ub' , 'The Underbog' , 1, 1, 70, 70, NULL), +(NULL, 'mt' , 'Mana-Tombs' , 1, 1, 70, 70, NULL), +(NULL, 'ac' , 'Auchenai Crypts' , 1, 1, 70, 70, NULL), +(NULL, 'seth', 'Sethekk Halls' , 1, 1, 70, 70, NULL), +(NULL, 'oh' , 'Old Hillsbrad Foothills', 1, 1, 70, 70, NULL), +(NULL, 'bm' , 'The Black Morass' , 1, 1, 70, 70, NULL), +(NULL, 'mech', 'The Mechanar' , 1, 1, 70, 70, NULL), +(NULL, 'bot' , 'The Botanica' , 1, 1, 70, 70, NULL), +(NULL, 'arc' , 'The Arcatraz' , 1, 1, 70, 70, NULL), +(NULL, 'sh' , 'The Shattered Halls' , 1, 1, 70, 70, NULL), +(NULL, 'sv' , 'The Steamvault' , 1, 1, 70, 70, NULL), +(NULL, 'sl' , 'Shadow Labyrinth' , 1, 1, 70, 70, NULL), +(NULL, 'mgt' , 'Magister''s Terrace' , 1, 1, 70, 70, NULL), + +-- == Wrath of the Lich King == + +(NULL, 'uk' , 'Utgarde Keep' , 2, 0, 70, 72, NULL), +(NULL, 'nexus' , 'The Nexus' , 2, 0, 71, 73, NULL), +(NULL, 'an' , 'Azjol-Nerub' , 2, 0, 72, 74, NULL), +(NULL, 'ak' , 'Ahn''kahet: The Old Kingdom', 2, 0, 73, 75, NULL), +(NULL, 'dtk' , 'Drak''Tharon Keep' , 2, 0, 74, 76, NULL), +(NULL, 'vh' , 'Violet Hold' , 2, 0, 75, 77, NULL), +(NULL, 'gd' , 'Gundrak' , 2, 0, 76, 78, NULL), +(NULL, 'hos' , 'Halls of Stone' , 2, 0, 77, 79, NULL), +(NULL, 'hol' , 'Halls of Lightning' , 2, 0, 80, 80, NULL), +(NULL, 'cos' , 'The Culling of Stratholme' , 2, 0, 80, 80, NULL), +(NULL, 'oculus', 'The Oculus' , 2, 0, 80, 80, NULL), +(NULL, 'up' , 'Utgarde Pinnacle' , 2, 0, 80, 80, NULL), +(NULL, 'toc' , 'Trial of the Champion' , 2, 0, 80, 80, NULL), +(NULL, 'fos' , 'Forge of Souls' , 2, 0, 80, 80, NULL), +(NULL, 'pos' , 'Pit of Saron' , 2, 0, 80, 80, NULL), +(NULL, 'hor' , 'Halls of Reflection' , 2, 0, 80, 80, NULL), + +-- == Wrath of the Lich King (Heroic) == + +(NULL, 'uk' , 'Utgarde Keep' , 2, 1, 80, 80, NULL), +(NULL, 'nexus' , 'The Nexus' , 2, 1, 80, 80, NULL), +(NULL, 'an' , 'Azjol-Nerub' , 2, 1, 80, 80, NULL), +(NULL, 'ak' , 'Ahn''kahet: The Old Kingdom', 2, 1, 80, 80, NULL), +(NULL, 'dtk' , 'Drak''Tharon Keep' , 2, 1, 80, 80, NULL), +(NULL, 'vh' , 'Violet Hold' , 2, 1, 80, 80, NULL), +(NULL, 'gd' , 'Gundrak' , 2, 1, 80, 80, NULL), +(NULL, 'hos' , 'Halls of Stone' , 2, 1, 80, 80, NULL), +(NULL, 'hol' , 'Halls of Lightning' , 2, 1, 80, 80, NULL), +(NULL, 'cos' , 'The Culling of Stratholme' , 2, 1, 80, 80, NULL), +(NULL, 'oculus', 'The Oculus' , 2, 1, 80, 80, NULL), +(NULL, 'up' , 'Utgarde Pinnacle' , 2, 1, 80, 80, NULL), +(NULL, 'toc' , 'Trial of the Champion' , 2, 1, 80, 80, NULL), +(NULL, 'fos' , 'Forge of Souls' , 2, 1, 80, 80, NULL), +(NULL, 'pos' , 'Pit of Saron' , 2, 1, 80, 80, NULL), +(NULL, 'hor' , 'Halls of Reflection' , 2, 1, 80, 80, NULL); diff --git a/sql/playerbots/base/playerbots_dungeon_suggestion_abbrevation.sql b/sql/playerbots/base/playerbots_dungeon_suggestion_abbrevation.sql new file mode 100644 index 00000000..cca6372a --- /dev/null +++ b/sql/playerbots/base/playerbots_dungeon_suggestion_abbrevation.sql @@ -0,0 +1,26 @@ +USE acore_playerbots; + +DROP TABLE IF EXISTS `playerbots_dungeon_suggestion_abbrevation`; +CREATE TABLE `playerbots_dungeon_suggestion_abbrevation` ( + `id` TINYINT(3) NOT NULL AUTO_INCREMENT UNIQUE, + `definition_slug` VARCHAR(255) NOT NULL, + `abbrevation` VARCHAR(16) NOT NULL, + + PRIMARY KEY (`id`), + KEY `definition_slug` (`definition_slug`) +) +ENGINE=MyISAM +DEFAULT CHARSET=utf8 +ROW_FORMAT=FIXED +COMMENT='Playerbot dungeon suggestion abbrevations'; + +-- = Example = + +-- INSERT INTO `playerbots_dungeon_suggestion_abbrevation` VALUES + +-- (NULL, 'rfc', 'Ragefire'), +-- (NULL, 'rfc', 'RFC'), +-- (NULL, 'st', 'Sunken Temple'), +-- (NULL, 'st', 'ST'), +-- (NULL, 'stocks', 'Stockades'); +-- (NULL, 'stocks', 'Stockade'); diff --git a/sql/playerbots/base/playerbots_dungeon_suggestion_strategy.sql b/sql/playerbots/base/playerbots_dungeon_suggestion_strategy.sql new file mode 100644 index 00000000..98147e28 --- /dev/null +++ b/sql/playerbots/base/playerbots_dungeon_suggestion_strategy.sql @@ -0,0 +1,34 @@ +USE acore_playerbots; + +DROP TABLE IF EXISTS `playerbots_dungeon_suggestion_strategy`; +CREATE TABLE `playerbots_dungeon_suggestion_strategy` ( + `id` TINYINT(3) NOT NULL AUTO_INCREMENT UNIQUE, + `definition_slug` VARCHAR(255) NOT NULL, + `strategy` VARCHAR(255) NOT NULL, + `difficulty` TINYINT(1) NOT NULL, + + PRIMARY KEY (`id`), + KEY `definition_slug` (`definition_slug`) +) +ENGINE=MyISAM +DEFAULT CHARSET=utf8 +ROW_FORMAT=FIXED +COMMENT='Playerbot dungeon suggestion strategies'; + +-- = Example = + +-- INSERT INTO `playerbots_dungeon_suggestion_strategy` VALUES + +-- (NULL, 'mara-scepter', 'Scepter run', 0), +-- (NULL, 'mara-princess', 'Princess run', 0), +-- (NULL, 'zf', 'Mallet run', 0), +-- (NULL, 'brd', 'Arena run', 0), +-- (NULL, 'brd', 'Attunement run', 0), +-- (NULL, 'brd', 'Lava run', 0), +-- (NULL, 'brd', 'Full run', 0), +-- (NULL, 'strat', 'Undead', 0), +-- (NULL, 'strat', 'UD', 0), +-- (NULL, 'strat', 'Living', 0), +-- (NULL, 'strat', 'Live', 0), +-- (NULL, 'seth', 'Anzu run', 1), +-- (NULL, 'cos', 'Timed run', 1); diff --git a/sql/playerbots/updates/pending_db_playerbots/rev_1651613158228212658.sql b/sql/playerbots/updates/pending_db_playerbots/rev_1651613158228212658.sql new file mode 100644 index 00000000..1e98662f --- /dev/null +++ b/sql/playerbots/updates/pending_db_playerbots/rev_1651613158228212658.sql @@ -0,0 +1,187 @@ +USE acore_playerbots; + +INSERT INTO `version_db_playerbots` (`sql_rev`) VALUES ('1651613158228212658'); + +DELETE FROM `playerbots_text` WHERE `key` = 'suggest_instance'; +INSERT INTO `playerbots_text` VALUES + +(NULL, 'suggest_dungeon', 'Anyone wants %dungeon?'), +(NULL, 'suggest_dungeon', 'Any groups for %dungeon?'), +(NULL, 'suggest_dungeon', 'Need help for %dungeon?'), +(NULL, 'suggest_dungeon', 'LFD: %dungeon.'), +(NULL, 'suggest_dungeon_role', 'Anyone needs %role for %dungeon?'), +(NULL, 'suggest_dungeon_role', 'Missing %role for %dungeon?'), +(NULL, 'suggest_dungeon_role', 'Can be a %role for %dungeon.'), +(NULL, 'suggest_dungeon', 'Need help with %dungeon?'), +(NULL, 'suggest_dungeon_role', 'Need %role help with %dungeon?'), +(NULL, 'suggest_dungeon', 'Anyone needs gear from %dungeon?'), +(NULL, 'suggest_dungeon', 'A little grind in %dungeon?'), +(NULL, 'suggest_dungeon', 'WTR %dungeon'), +(NULL, 'suggest_dungeon', 'Need help for %dungeon.'), +(NULL, 'suggest_dungeon', 'Wanna run %dungeon.'), +(NULL, 'suggest_dungeon_role', '%role looks for %dungeon.'), +(NULL, 'suggest_dungeon', 'What about %dungeon?'), +(NULL, 'suggest_dungeon', 'Who wants to farm %dungeon?'), +(NULL, 'suggest_dungeon', 'Go in %dungeon?'), +(NULL, 'suggest_dungeon', 'Looking for %dungeon.'), +(NULL, 'suggest_dungeon', 'Need help with %dungeon quests?'), +(NULL, 'suggest_dungeon', 'Wanna quest in %dungeon.'), +(NULL, 'suggest_dungeon', 'Anyone with quests in %dungeon?'), +(NULL, 'suggest_dungeon', 'Could help with quests in %dungeon.'), +(NULL, 'suggest_dungeon_role', '%role: any place in group for %dungeon?'), +(NULL, 'suggest_dungeon', 'Does anybody still run %dungeon this days?'), +(NULL, 'suggest_dungeon_role', '%dungeon: anyone wants to take a %role?'), +(NULL, 'suggest_dungeon_role', 'Is there any point being %role in %dungeon?'), +(NULL, 'suggest_dungeon', 'It is really worth to go to %dungeon?'), +(NULL, 'suggest_dungeon', 'Anybody needs more people for %dungeon?'), +(NULL, 'suggest_dungeon', '%dungeon bosses drop good gear. Wanna run?'), +(NULL, 'suggest_dungeon', 'What about %dungeon?'), +(NULL, 'suggest_dungeon_role', 'Anybody needs %role?'), +(NULL, 'suggest_dungeon_role', 'Anyone needs %role?'), +(NULL, 'suggest_dungeon', 'Who wants %dungeon?'), +(NULL, 'suggest_dungeon', 'Can anybody summon me at %dungeon?'), +(NULL, 'suggest_dungeon', 'Meet me in %dungeon'), +(NULL, 'suggest_dungeon', 'Wanna quick %dungeon run'), +(NULL, 'suggest_dungeon', 'Wanna full %dungeon run'), +(NULL, 'suggest_dungeon', 'How many times were you in %dungeon?'), +(NULL, 'suggest_dungeon', 'Another %dungeon run?'), +(NULL, 'suggest_dungeon', 'Wiped in %dungeon? Take me instead!'), +(NULL, 'suggest_dungeon', 'Take me in %dungeon please.'), +(NULL, 'suggest_dungeon', 'Quick %dungeon run?'), +(NULL, 'suggest_dungeon', 'Full %dungeon run?'), +(NULL, 'suggest_dungeon_role', 'Who can take %role to %dungeon?'); + +CREATE TABLE `playerbots_dungeon_suggestion_definition` ( + `id` TINYINT(3) NOT NULL AUTO_INCREMENT UNIQUE, + `slug` VARCHAR(255) NOT NULL, + `name` VARCHAR(255) NOT NULL, + `expansion` TINYINT(1) NOT NULL, + `difficulty` TINYINT(1) NOT NULL, + `min_level` TINYINT(3), + `max_level` TINYINT(3), + `comment` VARCHAR(255), + + PRIMARY KEY (`id`) +) +ENGINE=MyISAM +DEFAULT CHARSET=utf8 +ROW_FORMAT=FIXED +COMMENT='Playerbot dungeon suggestion definitions'; + +INSERT INTO `playerbots_dungeon_suggestion_definition` VALUES + +(NULL, 'rfc' , 'Ragefire Chasm' , 0, 0, 15, 21, NULL), +(NULL, 'dm' , 'Deadmines' , 0, 0, 15, 23, NULL), +(NULL, 'wc' , 'Wailing Caverns' , 0, 0, 17, 27, NULL), +(NULL, 'sfk' , 'Shadowfang Keep' , 0, 0, 18, 24, NULL), +(NULL, 'bfd' , 'Blackfathom Deeps' , 0, 0, 20, 27, NULL), +(NULL, 'stocks' , 'Stormwind Stockade' , 0, 0, 22, 30, NULL), +(NULL, 'gnomer' , 'Gnomeregan' , 0, 0, 26, 36, NULL), +(NULL, 'sm-gy' , 'Scarlet Monastery: Graveyard', 0, 0, 26, 36, NULL), +(NULL, 'sm-lib' , 'Scarlet Monastery: Library' , 0, 0, 29, 39, NULL), +(NULL, 'rfk' , 'Razorfen Kraul' , 0, 0, 32, 35, NULL), +(NULL, 'sm-armory' , 'Scarlet Monastery: Armory' , 0, 0, 32, 42, NULL), +(NULL, 'sm-cath' , 'Scarlet Monastery: Cathedral', 0, 0, 35, 45, NULL), +(NULL, 'rfd' , 'Razorfen Downs' , 0, 0, 42, 45, NULL), +(NULL, 'ulda' , 'Uldaman' , 0, 0, 42, 52, NULL), +(NULL, 'mara' , 'Maraudon' , 0, 0, 42, 52, NULL), +(NULL, 'zf' , 'Zul''Farrak' , 0, 0, 46, 56, NULL), +(NULL, 'st' , 'Temple of Atal''Hakkar' , 0, 0, 50, 60, NULL), +(NULL, 'brd' , 'Blackrock Depths' , 0, 0, 52, 60, NULL), +(NULL, 'dm-w' , 'Dire Maul: West' , 0, 0, 55, 60, NULL), +(NULL, 'dm-e' , 'Dire Maul: East' , 0, 0, 55, 60, NULL), +(NULL, 'dm-n' , 'Dire Maul: North' , 0, 0, 55, 60, NULL), +(NULL, 'lbrs' , 'Lower Blackrock Spire' , 0, 0, 55, 60, NULL), +(NULL, 'scholo' , 'Scholomance' , 0, 0, 58, 60, NULL), +(NULL, 'strat' , 'Stratholme' , 0, 0, 58, 60, NULL), +(NULL, 'hfr' , 'Hellfire Ramparts' , 1, 0, 59, 67, NULL), +(NULL, 'bf' , 'The Blood Furnace' , 1, 0, 60, 68, NULL), +(NULL, 'sp' , 'The Slave Pens' , 1, 0, 61, 69, NULL), +(NULL, 'ub' , 'The Underbog' , 1, 0, 62, 70, NULL), +(NULL, 'mt' , 'Mana-Tombs' , 1, 0, 63, 70, NULL), +(NULL, 'ac' , 'Auchenai Crypts' , 1, 0, 64, 70, NULL), +(NULL, 'seth', 'Sethekk Halls' , 1, 0, 66, 70, NULL), +(NULL, 'oh' , 'Old Hillsbrad Foothills', 1, 0, 66, 70, NULL), +(NULL, 'bm' , 'The Black Morass' , 1, 0, 68, 70, NULL), +(NULL, 'mech', 'The Mechanar' , 1, 0, 68, 70, NULL), +(NULL, 'bot' , 'The Botanica' , 1, 0, 69, 70, NULL), +(NULL, 'arc' , 'The Arcatraz' , 1, 0, 69, 70, NULL), +(NULL, 'sh' , 'The Shattered Halls' , 1, 0, 69, 70, NULL), +(NULL, 'sv' , 'The Steamvault' , 1, 0, 69, 70, NULL), +(NULL, 'sl' , 'Shadow Labyrinth' , 1, 0, 69, 70, NULL), +(NULL, 'mgt' , 'Magister''s Terrace' , 1, 0, 70, 70, NULL), +(NULL, 'hfr' , 'Hellfire Ramparts' , 1, 1, 70, 70, NULL), +(NULL, 'bf' , 'The Blood Furnace' , 1, 1, 70, 70, NULL), +(NULL, 'sp' , 'The Slave Pens' , 1, 1, 70, 70, NULL), +(NULL, 'ub' , 'The Underbog' , 1, 1, 70, 70, NULL), +(NULL, 'mt' , 'Mana-Tombs' , 1, 1, 70, 70, NULL), +(NULL, 'ac' , 'Auchenai Crypts' , 1, 1, 70, 70, NULL), +(NULL, 'seth', 'Sethekk Halls' , 1, 1, 70, 70, NULL), +(NULL, 'oh' , 'Old Hillsbrad Foothills', 1, 1, 70, 70, NULL), +(NULL, 'bm' , 'The Black Morass' , 1, 1, 70, 70, NULL), +(NULL, 'mech', 'The Mechanar' , 1, 1, 70, 70, NULL), +(NULL, 'bot' , 'The Botanica' , 1, 1, 70, 70, NULL), +(NULL, 'arc' , 'The Arcatraz' , 1, 1, 70, 70, NULL), +(NULL, 'sh' , 'The Shattered Halls' , 1, 1, 70, 70, NULL), +(NULL, 'sv' , 'The Steamvault' , 1, 1, 70, 70, NULL), +(NULL, 'sl' , 'Shadow Labyrinth' , 1, 1, 70, 70, NULL), +(NULL, 'mgt' , 'Magister''s Terrace' , 1, 1, 70, 70, NULL), +(NULL, 'uk' , 'Utgarde Keep' , 2, 0, 70, 72, NULL), +(NULL, 'nexus' , 'The Nexus' , 2, 0, 71, 73, NULL), +(NULL, 'an' , 'Azjol-Nerub' , 2, 0, 72, 74, NULL), +(NULL, 'ak' , 'Ahn''kahet: The Old Kingdom', 2, 0, 73, 75, NULL), +(NULL, 'dtk' , 'Drak''Tharon Keep' , 2, 0, 74, 76, NULL), +(NULL, 'vh' , 'Violet Hold' , 2, 0, 75, 77, NULL), +(NULL, 'gd' , 'Gundrak' , 2, 0, 76, 78, NULL), +(NULL, 'hos' , 'Halls of Stone' , 2, 0, 77, 79, NULL), +(NULL, 'hol' , 'Halls of Lightning' , 2, 0, 80, 80, NULL), +(NULL, 'cos' , 'The Culling of Stratholme' , 2, 0, 80, 80, NULL), +(NULL, 'oculus', 'The Oculus' , 2, 0, 80, 80, NULL), +(NULL, 'up' , 'Utgarde Pinnacle' , 2, 0, 80, 80, NULL), +(NULL, 'toc' , 'Trial of the Champion' , 2, 0, 80, 80, NULL), +(NULL, 'fos' , 'Forge of Souls' , 2, 0, 80, 80, NULL), +(NULL, 'pos' , 'Pit of Saron' , 2, 0, 80, 80, NULL), +(NULL, 'hor' , 'Halls of Reflection' , 2, 0, 80, 80, NULL), +(NULL, 'uk' , 'Utgarde Keep' , 2, 1, 80, 80, NULL), +(NULL, 'nexus' , 'The Nexus' , 2, 1, 80, 80, NULL), +(NULL, 'an' , 'Azjol-Nerub' , 2, 1, 80, 80, NULL), +(NULL, 'ak' , 'Ahn''kahet: The Old Kingdom', 2, 1, 80, 80, NULL), +(NULL, 'dtk' , 'Drak''Tharon Keep' , 2, 1, 80, 80, NULL), +(NULL, 'vh' , 'Violet Hold' , 2, 1, 80, 80, NULL), +(NULL, 'gd' , 'Gundrak' , 2, 1, 80, 80, NULL), +(NULL, 'hos' , 'Halls of Stone' , 2, 1, 80, 80, NULL), +(NULL, 'hol' , 'Halls of Lightning' , 2, 1, 80, 80, NULL), +(NULL, 'cos' , 'The Culling of Stratholme' , 2, 1, 80, 80, NULL), +(NULL, 'oculus', 'The Oculus' , 2, 1, 80, 80, NULL), +(NULL, 'up' , 'Utgarde Pinnacle' , 2, 1, 80, 80, NULL), +(NULL, 'toc' , 'Trial of the Champion' , 2, 1, 80, 80, NULL), +(NULL, 'fos' , 'Forge of Souls' , 2, 1, 80, 80, NULL), +(NULL, 'pos' , 'Pit of Saron' , 2, 1, 80, 80, NULL), +(NULL, 'hor' , 'Halls of Reflection' , 2, 1, 80, 80, NULL); + +CREATE TABLE `playerbots_dungeon_suggestion_abbrevation` ( + `id` TINYINT(3) NOT NULL AUTO_INCREMENT UNIQUE, + `definition_slug` VARCHAR(255) NOT NULL, + `abbrevation` VARCHAR(16) NOT NULL, + + PRIMARY KEY (`id`), + KEY `definition_slug` (`definition_slug`) +) +ENGINE=MyISAM +DEFAULT CHARSET=utf8 +ROW_FORMAT=FIXED +COMMENT='Playerbot dungeon suggestion abbrevations'; + +CREATE TABLE `playerbots_dungeon_suggestion_strategy` ( + `id` TINYINT(3) NOT NULL AUTO_INCREMENT UNIQUE, + `definition_slug` VARCHAR(255) NOT NULL, + `strategy` VARCHAR(255) NOT NULL, + `difficulty` TINYINT(1) NOT NULL, + + PRIMARY KEY (`id`), + KEY `definition_slug` (`definition_slug`) +) +ENGINE=MyISAM +DEFAULT CHARSET=utf8 +ROW_FORMAT=FIXED +COMMENT='Playerbot dungeon suggestion strategies'; diff --git a/src/PlaceholderHelper.cpp b/src/PlaceholderHelper.cpp new file mode 100644 index 00000000..c51a4303 --- /dev/null +++ b/src/PlaceholderHelper.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + */ + +#include "PlaceholderHelper.h" +#include "AiFactory.h" +#include "Playerbots.h" +#include "PlayerbotTextMgr.h" +#include "Util.h" + +void PlaceholderHelper::MapDungeon( + PlaceholderMap& placeholders, + DungeonSuggestion const* dungeonSuggestion, + Player* bot +) +{ + std::ostringstream out; + Insertion insertion = {out, dungeonSuggestion}; + InsertDungeonName(insertion); + InsertDungeonStrategy(insertion); + InsertDifficulty(insertion, bot); + + placeholders["%dungeon"] = out.str(); +} + + +void PlaceholderHelper::MapRole(PlaceholderMap& placeholders, Player* bot) +{ + BotRoles const role = AiFactory::GetPlayerRoles(bot); + std::string roleText; + switch (role) + { + case BOT_ROLE_TANK: + roleText = "Tank"; + break; + case BOT_ROLE_HEALER: + roleText = "Healer"; + break; + case BOT_ROLE_DPS: + roleText = "DPS"; + break; + case BOT_ROLE_NONE: + default: + return; + } + + bool const hasRole = !roleText.empty(); + if (hasRole) + { + placeholders["%role"] = roleText; + } +} + +void PlaceholderHelper::InsertDungeonName(Insertion& insertion) +{ + std::string name = insertion.dungeonSuggestion->name; + bool const hasAbbrevation = !insertion.dungeonSuggestion->abbrevation.empty(); + if (hasAbbrevation) + { + name = insertion.dungeonSuggestion->abbrevation; + } + + insertion.out << "|c00b000b0" << name << "|r"; +} + +void PlaceholderHelper::InsertDungeonStrategy(Insertion& insertion) +{ + bool const hasStrategy = !insertion.dungeonSuggestion->strategy.empty(); + bool const isRandomlyUsingStrategy = urand(0, 1); + if (hasStrategy && isRandomlyUsingStrategy) + { + std::string strategy = insertion.dungeonSuggestion->strategy; + insertion.out << " " + strategy; + } +} + +void PlaceholderHelper::InsertDifficulty(Insertion& insertion, Player* bot) +{ + bool const hasHeroic = insertion.dungeonSuggestion->difficulty == DUNGEON_DIFFICULTY_HEROIC; + std::string difficultyText; + if (hasHeroic) + { + bool const isRandomlyNormal = urand(0, 1); + bool const isRandomlyHeroic = urand(0, 1); + std::vector normalAbbrevations = { "Normal", "N" }; + std::vector heroicAbbrevations = { "Heroic", "HC", "H"}; + uint32 const randomAbbrevationIndex = urand(0, 1); + if (isRandomlyNormal) + { + difficultyText = normalAbbrevations[randomAbbrevationIndex]; + } + else if (isRandomlyHeroic) + { + difficultyText = heroicAbbrevations[randomAbbrevationIndex]; + } + + insertion.out << " " << difficultyText; + } +} diff --git a/src/PlaceholderHelper.h b/src/PlaceholderHelper.h new file mode 100644 index 00000000..75a85e36 --- /dev/null +++ b/src/PlaceholderHelper.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_PLACEHOLDERHELPER_H +#define _PLAYERBOT_PLACEHOLDERHELPER_H + +#include "Common.h" +#include "Player.h" +#include "PlayerbotDungeonSuggestionMgr.h" + +#include + +typedef std::map PlaceholderMap; + +class PlaceholderHelper +{ + public: + static void MapRole(PlaceholderMap& placeholders, Player* bot); + static void MapDungeon( + PlaceholderMap& placeholders, + DungeonSuggestion const* dungeonSuggestion, + Player* bot + ); + + private: + struct Insertion + { + std::ostringstream& out; + DungeonSuggestion const* dungeonSuggestion; + }; + + static void InsertDungeonName(Insertion& insertion); + static void InsertDungeonStrategy(Insertion& insertion); + static void InsertDifficulty(Insertion& insertion, Player* bot); +}; + +#endif diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index ed8db0f5..590ce58b 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -9,6 +9,7 @@ #include "RandomItemMgr.h" #include "RandomPlayerbotFactory.h" #include "Talentspec.h" +#include "PlayerbotDungeonSuggestionMgr.h" #include @@ -124,6 +125,8 @@ bool PlayerbotAIConfig::Initialize() minRandomBotsPriceChangeInterval = sConfigMgr->GetIntDefault("AiPlayerbot.MinRandomBotsPriceChangeInterval", 2 * HOUR); maxRandomBotsPriceChangeInterval = sConfigMgr->GetIntDefault("AiPlayerbot.MaxRandomBotsPriceChangeInterval", 48 * HOUR); randomBotJoinLfg = sConfigMgr->GetBoolDefault("AiPlayerbot.RandomBotJoinLfg", true); + randomBotSuggestDungeons = sConfigMgr->GetBoolDefault("AiPlayerbot.RandomBotSuggestDungeons", true); + suggestDungeonsInLowerCaseRandomly = sConfigMgr->GetBoolDefault("AiPlayerbot.SuggestDungeonsInLowerCaseRandomly", false); randomBotJoinBG = sConfigMgr->GetBoolDefault("AiPlayerbot.RandomBotJoinBG", true); logInGroupOnly = sConfigMgr->GetBoolDefault("AiPlayerbot.LogInGroupOnly", true); logValuesPerTick = sConfigMgr->GetBoolDefault("AiPlayerbot.LogValuesPerTick", false); @@ -315,6 +318,11 @@ bool PlayerbotAIConfig::Initialize() if (sPlayerbotAIConfig->randomBotJoinBG) sRandomPlayerbotMgr->LoadBattleMastersCache(); + if (sPlayerbotAIConfig->randomBotSuggestDungeons) + { + sPlayerbotDungeonSuggestionMgr->LoadDungeonSuggestions(); + } + LOG_INFO("server.loading", "---------------------------------------"); LOG_INFO("server.loading", " AI Playerbots initialized "); LOG_INFO("server.loading", "---------------------------------------"); diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index 22000c98..50774299 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -70,6 +70,8 @@ class PlayerbotAIConfig uint32 randomBotsPerInterval; uint32 minRandomBotsPriceChangeInterval, maxRandomBotsPriceChangeInterval; bool randomBotJoinLfg; + bool randomBotSuggestDungeons; + bool suggestDungeonsInLowerCaseRandomly; bool randomBotJoinBG; bool randomBotLoginAtStartup; uint32 randomBotTeleLevel; diff --git a/src/PlayerbotDungeonSuggestionMgr.cpp b/src/PlayerbotDungeonSuggestionMgr.cpp new file mode 100644 index 00000000..f240ab41 --- /dev/null +++ b/src/PlayerbotDungeonSuggestionMgr.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + */ + +#include "PlayerbotDungeonSuggestionMgr.h" +#include "Playerbots.h" + +std::vector const PlayerbotDungeonSuggestionMgr::GetDungeonSuggestions() +{ + return m_dungeonSuggestions; +} + +void PlayerbotDungeonSuggestionMgr::LoadDungeonSuggestions() +{ + LOG_INFO("server.loading", "Loading playerbots dungeon suggestions..."); + uint32 oldMSTime = getMSTime(); + + uint32 count = 0; + auto statement = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_SEL_DUNGEON_SUGGESTION); + uint8 const expansion = sWorld->getIntConfig(CONFIG_EXPANSION); + statement->SetData(0, expansion); + + PreparedQueryResult result = PlayerbotsDatabase.Query(statement); + if (result) + { + do + { + Field* fields = result->Fetch(); + std::string const name = fields[0].Get(); + uint8 const difficulty = fields[1].Get(); + uint8 const min_level = fields[2].Get(); + uint8 const max_level = fields[3].Get(); + std::string const abbrevation = fields[4].Get(); + std::string const strategy = fields[5].Get(); + + DungeonSuggestion const row = + { + name, + static_cast(difficulty), + min_level, + max_level, + abbrevation, + strategy + }; + + m_dungeonSuggestions.push_back(row); + ++count; + } + while (result->NextRow()); + } + + LOG_INFO("server.loading", "{} playerbots dungeon suggestions loaded in {} ms", + count, GetMSTimeDiffToNow(oldMSTime)); +} diff --git a/src/PlayerbotDungeonSuggestionMgr.h b/src/PlayerbotDungeonSuggestionMgr.h new file mode 100644 index 00000000..65ecce4a --- /dev/null +++ b/src/PlayerbotDungeonSuggestionMgr.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_PLAYERBOTDUNGEONSUGGESTIONMGR_H +#define _PLAYERBOT_PLAYERBOTDUNGEONSUGGESTIONMGR_H + +#include "Common.h" +#include "DBCEnums.h" + +#include +#include + +struct DungeonSuggestion +{ + std::string name; + Difficulty difficulty; + uint8 min_level; + uint8 max_level; + std::string abbrevation; + std::string strategy; +}; + +class PlayerbotDungeonSuggestionMgr +{ + public: + PlayerbotDungeonSuggestionMgr() { }; + ~PlayerbotDungeonSuggestionMgr() { }; + static PlayerbotDungeonSuggestionMgr* instance() + { + static PlayerbotDungeonSuggestionMgr instance; + return &instance; + } + + void LoadDungeonSuggestions(); + std::vector const GetDungeonSuggestions(); + + private: + std::vector m_dungeonSuggestions; +}; + +#define sPlayerbotDungeonSuggestionMgr PlayerbotDungeonSuggestionMgr::instance() + +#endif diff --git a/src/strategy/actions/ActionContext.h b/src/strategy/actions/ActionContext.h index 33fcc436..ea5c7196 100644 --- a/src/strategy/actions/ActionContext.h +++ b/src/strategy/actions/ActionContext.h @@ -53,6 +53,7 @@ #include "SayAction.h" #include "StayActions.h" #include "SuggestWhatToDoAction.h" +#include "SuggestDungeonAction.h" #include "TravelAction.h" #include "XpGainAction.h" #include "VehicleActions.h" @@ -117,6 +118,7 @@ class ActionContext : public NamedObjectContext creators["emote"] = &ActionContext::emote; creators["talk"] = &ActionContext::talk; creators["suggest what to do"] = &ActionContext::suggest_what_to_do; + creators["suggest dungeon"] = &ActionContext::suggest_dungeon; creators["suggest trade"] = &ActionContext::suggest_trade; creators["return"] = &ActionContext::_return; creators["move to loot"] = &ActionContext::move_to_loot; @@ -264,6 +266,7 @@ class ActionContext : public NamedObjectContext static Action* emote(PlayerbotAI* botAI) { return new EmoteAction(botAI); } static Action* talk(PlayerbotAI* botAI) { return new TalkAction(botAI); } static Action* suggest_what_to_do(PlayerbotAI* botAI) { return new SuggestWhatToDoAction(botAI); } + static Action* suggest_dungeon(PlayerbotAI* botAI) { return new SuggestDungeonAction(botAI); } static Action* suggest_trade(PlayerbotAI* botAI) { return new SuggestTradeAction(botAI); } static Action* attack_anything(PlayerbotAI* botAI) { return new AttackAnythingAction(botAI); } static Action* attack_least_hp_target(PlayerbotAI* botAI) { return new AttackLeastHpTargetAction(botAI); } diff --git a/src/strategy/actions/SuggestDungeonAction.cpp b/src/strategy/actions/SuggestDungeonAction.cpp new file mode 100644 index 00000000..817dc545 --- /dev/null +++ b/src/strategy/actions/SuggestDungeonAction.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + */ + +#include "SuggestDungeonAction.h" +#include "AiFactory.h" +#include "Player.h" +#include "Playerbots.h" +#include "PlayerbotTextMgr.h" +#include "PlayerbotDungeonSuggestionMgr.h" + +DungeonSuggestions SuggestDungeonAction::m_dungeonSuggestions; + +SuggestDungeonAction::SuggestDungeonAction(PlayerbotAI* botAI) : + SuggestWhatToDoAction(botAI, "suggest instance") +{ + if (m_dungeonSuggestions.empty()) + { + m_dungeonSuggestions = sPlayerbotDungeonSuggestionMgr->GetDungeonSuggestions(); + } +} + +bool SuggestDungeonAction::Execute(Event event) +{ + bool const isRealPlayer = !sRandomPlayerbotMgr->IsRandomBot(bot); + bool const isInGroup = bot->GetGroup(); + bool const isInInstance = bot->GetInstanceId(); + if (isRealPlayer || isInGroup || isInInstance) + { + return false; + } + + DungeonSuggestions const dungeonSuggestions = GetDungeonSuggestionsEligibleFor(bot); + if (dungeonSuggestions.empty()) + { + return false; + } + + uint32 const randomDungeonIndex = urand(0, dungeonSuggestions.size() - 1); + DungeonSuggestion const* dungeonSuggestion = &dungeonSuggestions[randomDungeonIndex]; + PlaceholderMap const placeholders = MapPlaceholders(bot, dungeonSuggestion); + std::string playerbotsTextKey = PlayerbotsTextKeyByMapKey(placeholders); + std::string message = sPlayerbotTextMgr->Format(playerbotsTextKey, placeholders); + bool isRandomlyLowerCase = sPlayerbotAIConfig->suggestDungeonsInLowerCaseRandomly + ? urand(0, 1) + : false; + spam(message, 1, isRandomlyLowerCase); + + return true; +} + +DungeonSuggestions const SuggestDungeonAction::GetDungeonSuggestionsEligibleFor(Player* bot) +{ + DungeonSuggestions dungeonSuggestionsEligibleFor; + for (DungeonSuggestions::const_iterator i = m_dungeonSuggestions.begin(); + i != m_dungeonSuggestions.end(); ++i) + { + uint8 const level = bot->getLevel(); + bool const isEligible = level >= i->min_level && level <= i->max_level; + if (isEligible) + { + dungeonSuggestionsEligibleFor.push_back(*i); + } + } + + return dungeonSuggestionsEligibleFor; +} + +PlaceholderMap SuggestDungeonAction::MapPlaceholders( + Player* bot, + DungeonSuggestion const* dungeonSuggestion +) +{ + PlaceholderMap placeholders; + bool const isRandomlyMappingRole = urand(0, 1); + if (isRandomlyMappingRole) + { + PlaceholderHelper::MapRole(placeholders, bot); + } + PlaceholderHelper::MapDungeon(placeholders, dungeonSuggestion, bot); + + return placeholders; +} + +std::string SuggestDungeonAction::PlayerbotsTextKeyByMapKey(PlaceholderMap const& placeholders) +{ + bool const isRoleMapped = placeholders.find("%role") != placeholders.end(); + std::string playerbotsTextKey = "suggest_dungeon"; + if (isRoleMapped) + { + playerbotsTextKey = "suggest_dungeon_role"; + } + + return playerbotsTextKey; +} diff --git a/src/strategy/actions/SuggestDungeonAction.h b/src/strategy/actions/SuggestDungeonAction.h new file mode 100644 index 00000000..aacaa37c --- /dev/null +++ b/src/strategy/actions/SuggestDungeonAction.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_SUGGESTINSTANCEACTION_H +#define _PLAYERBOT_SUGGESTINSTANCEACTION_H + +#include "SuggestWhatToDoAction.h" +#include "PlayerbotDungeonSuggestionMgr.h" +#include "PlaceholderHelper.h" + +typedef std::vector DungeonSuggestions; + +class SuggestDungeonAction : public SuggestWhatToDoAction +{ + public: + SuggestDungeonAction(PlayerbotAI* botAI); + + bool Execute(Event event) override; + bool isUseful() override { return true; } + + private: + static DungeonSuggestions m_dungeonSuggestions; + + DungeonSuggestions const GetDungeonSuggestionsEligibleFor(Player* bot); + PlaceholderMap MapPlaceholders( + Player* bot, + DungeonSuggestion const* dungeonSuggestion + ); + std::string PlayerbotsTextKeyByMapKey(PlaceholderMap const& placeholders); +}; + +#endif diff --git a/src/strategy/actions/SuggestWhatToDoAction.cpp b/src/strategy/actions/SuggestWhatToDoAction.cpp index a32d59b2..29576b1e 100644 --- a/src/strategy/actions/SuggestWhatToDoAction.cpp +++ b/src/strategy/actions/SuggestWhatToDoAction.cpp @@ -11,12 +11,10 @@ #include "Playerbots.h" #include "PlayerbotTextMgr.h" -std::map SuggestWhatToDoAction::instances; std::map SuggestWhatToDoAction::factions; SuggestWhatToDoAction::SuggestWhatToDoAction(PlayerbotAI* botAI, std::string const name) : InventoryAction(botAI, name) { - suggestions.push_back(&SuggestWhatToDoAction::instance); suggestions.push_back(&SuggestWhatToDoAction::specificQuest); suggestions.push_back(&SuggestWhatToDoAction::grindReputation); suggestions.push_back(&SuggestWhatToDoAction::something); @@ -37,74 +35,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 Monastery"] = 40; - instances["Uldaman"] = 45; - instances["Dire Maul"] = 58; - instances["Scholomance"] = 59; - instances["Razorfen Downs"] = 40; - instances["Stratholme"] = 59; - instances["Zul'Farrak"] = 45; - instances["Blackrock Depths"] = 55; - instances["Temple of Atal'Hakkar"] = 55; - instances["Lower Blackrock Spire"] = 57; - - instances["Hellfire Citadel"] = 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 (std::map::iterator i = instances.begin(); i != instances.end(); ++i) - { - if (bot->getLevel() >= i->second) - allowedInstances.push_back(i->first); - } - - if (allowedInstances.empty()) - return; - - std::map placeholders; - placeholders["%role"] = chat->FormatClass(bot, AiFactory::GetPlayerSpecTab(bot)); - - std::ostringstream itemout; - itemout << "|c00b000b0" << allowedInstances[urand(0, allowedInstances.size() - 1)] << "|r"; - placeholders["%instance"] = itemout.str(); - - spam(sPlayerbotTextMgr->Format("suggest_instance", placeholders)); -} - std::vector SuggestWhatToDoAction::GetIncompletedQuests() { std::vector result; @@ -226,7 +156,11 @@ void SuggestWhatToDoAction::something() spam(sPlayerbotTextMgr->Format("suggest_something", placeholders)); } -void SuggestWhatToDoAction::spam(std::string const msg, uint32 channelId) +void SuggestWhatToDoAction::spam( + std::string msg, + uint32 channelId, + bool const isLowerCase +) { std::set said; for (uint32 i = 0; i < sChatChannelsStore.GetNumRows(); ++i) @@ -248,6 +182,10 @@ void SuggestWhatToDoAction::spam(std::string const msg, uint32 channelId) if (Channel* chn = cMgr->GetJoinChannel(channelName, channel->ChannelID)) { chn->JoinChannel(bot, ""); + if (isLowerCase) + { + strToLower(msg); + } chn->Say(bot->GetGUID(), msg.c_str(), LANG_UNIVERSAL); } } diff --git a/src/strategy/actions/SuggestWhatToDoAction.h b/src/strategy/actions/SuggestWhatToDoAction.h index 822aa76d..d5f08b00 100644 --- a/src/strategy/actions/SuggestWhatToDoAction.h +++ b/src/strategy/actions/SuggestWhatToDoAction.h @@ -20,11 +20,14 @@ class SuggestWhatToDoAction : public InventoryAction protected: typedef void (SuggestWhatToDoAction::*Suggestion)(); std::vector suggestions; - void instance(); void specificQuest(); void grindReputation(); void something(); - void spam(std::string const msg, uint32 channelId = 1); + void spam( + std::string msg, + uint32 channelId = 1, + bool const isLowerCase = false + ); std::vector GetIncompletedQuests(); diff --git a/src/strategy/generic/EmoteStrategy.cpp b/src/strategy/generic/EmoteStrategy.cpp index a55a979d..331ce368 100644 --- a/src/strategy/generic/EmoteStrategy.cpp +++ b/src/strategy/generic/EmoteStrategy.cpp @@ -11,6 +11,11 @@ void EmoteStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode("seldom", NextAction::array(0, new NextAction("suggest what to do", 1.0f), nullptr))); triggers.push_back(new TriggerNode("seldom", NextAction::array(0, new NextAction("suggest trade", 1.0f), nullptr))); + if (sPlayerbotAIConfig->randomBotSuggestDungeons) + { + triggers.push_back(new TriggerNode("random", NextAction::array(0, new NextAction("suggest dungeon", 1.0f), nullptr))); + } + if (sPlayerbotAIConfig->enableGreet) { triggers.push_back(new TriggerNode("new player nearby", NextAction::array(0, new NextAction("greet", 1.0f), nullptr)));