Refactor instance suggestions

This commit is contained in:
qudzy
2022-05-03 23:48:07 +02:00
parent fe59158e7d
commit ed35a2ae67
17 changed files with 772 additions and 73 deletions

View File

@@ -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

View File

@@ -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);

View File

@@ -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');

View File

@@ -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);

View File

@@ -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';

99
src/PlaceholderHelper.cpp Normal file
View File

@@ -0,0 +1,99 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, 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<std::string> normalAbbrevations = { "Normal", "N" };
std::vector<std::string> heroicAbbrevations = { "Heroic", "HC", "H"};
uint32 const randomAbbrevationIndex = urand(0, 1);
if (isRandomlyNormal)
{
difficultyText = normalAbbrevations[randomAbbrevationIndex];
}
else if (isRandomlyHeroic)
{
difficultyText = heroicAbbrevations[randomAbbrevationIndex];
}
insertion.out << " " << difficultyText;
}
}

38
src/PlaceholderHelper.h Normal file
View File

@@ -0,0 +1,38 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, 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 <map>
typedef std::map<std::string, std::string> 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

View File

@@ -9,6 +9,7 @@
#include "RandomItemMgr.h"
#include "RandomPlayerbotFactory.h"
#include "Talentspec.h"
#include "PlayerbotDungeonSuggestionMgr.h"
#include <iostream>
@@ -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", "---------------------------------------");

View File

@@ -70,6 +70,8 @@ class PlayerbotAIConfig
uint32 randomBotsPerInterval;
uint32 minRandomBotsPriceChangeInterval, maxRandomBotsPriceChangeInterval;
bool randomBotJoinLfg;
bool randomBotSuggestDungeons;
bool suggestDungeonsInLowerCaseRandomly;
bool randomBotJoinBG;
bool randomBotLoginAtStartup;
uint32 randomBotTeleLevel;

View File

@@ -0,0 +1,54 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, 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<DungeonSuggestion> 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<std::string>();
uint8 const difficulty = fields[1].Get<uint8>();
uint8 const min_level = fields[2].Get<uint8>();
uint8 const max_level = fields[3].Get<uint8>();
std::string const abbrevation = fields[4].Get<std::string>();
std::string const strategy = fields[5].Get<std::string>();
DungeonSuggestion const row =
{
name,
static_cast<Difficulty>(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));
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, 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 <map>
#include <vector>
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<DungeonSuggestion> const GetDungeonSuggestions();
private:
std::vector<DungeonSuggestion> m_dungeonSuggestions;
};
#define sPlayerbotDungeonSuggestionMgr PlayerbotDungeonSuggestionMgr::instance()
#endif

View File

@@ -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<Action>
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<Action>
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); }

View File

@@ -0,0 +1,95 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, 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;
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, 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<DungeonSuggestion> 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

View File

@@ -11,12 +11,10 @@
#include "Playerbots.h"
#include "PlayerbotTextMgr.h"
std::map<std::string, uint8> SuggestWhatToDoAction::instances;
std::map<std::string, uint8> 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<std::string> allowedInstances;
for (std::map<std::string, uint8>::iterator i = instances.begin(); i != instances.end(); ++i)
{
if (bot->getLevel() >= i->second)
allowedInstances.push_back(i->first);
}
if (allowedInstances.empty())
return;
std::map<std::string, std::string> 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<uint32> SuggestWhatToDoAction::GetIncompletedQuests()
{
std::vector<uint32> 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<std::string> 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);
}
}

View File

@@ -20,11 +20,14 @@ class SuggestWhatToDoAction : public InventoryAction
protected:
typedef void (SuggestWhatToDoAction::*Suggestion)();
std::vector<Suggestion> 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<uint32> GetIncompletedQuests();

View File

@@ -11,6 +11,11 @@ void EmoteStrategy::InitTriggers(std::vector<TriggerNode*>& 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)));