diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index bd2f4476a..3a6c82157 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -581,10 +581,10 @@ AiPlayerbot.AutoGearScoreLimit = 0 # Default: food, taxi, and raid are enabled AiPlayerbot.BotCheats = "food,taxi,raid" -# List of attunement quests (comma-separated list of quest IDs) that are automatically completed for all bots. +# List of attunement quests (comma-separated list of quest IDs) that are automatically completed for all bots. # While mod-playerbots does not restore removed attunement requirements, other mods, such as mod-individual-progression, may do so. # This is meant to exclude bots from such requirements. -# +# # Default: # Caverns of Time - Part 1 # - 10279, To The Master's Lair @@ -651,9 +651,14 @@ AiPlayerbot.BotTaxiGapJitterMs = 100 #################################################################################################### # PROFESSIONS -# Note: Random bots currently do not get professions # +# Percentage of randombots in each class bucket that receive a class-matching +# weighted profession combination. The remaining randombots use the weighted +# random sane-pair profession pool. +# Default: 30 +AiPlayerbot.ClassMatchingProfessionChance = 30 + # Automatically adds the 'master fishing' strategy to bots that have the fishing skill when the bots master fishes. # Default: 1 (Enabled) AiPlayerbot.EnableFishingWithMaster = 1 @@ -801,6 +806,27 @@ AiPlayerbot.RandomGearQualityLimit = 3 # Default: 0 (no limit) AiPlayerbot.RandomGearScoreLimit = 0 +# Prefer armor of the class's ideal type: apply 3x score multiplier to class-appropriate armor. +# When enabled, Warriors strongly prefer plate, Shamans prefer mail, etc. +# A truly superior item can still win (no hard filtering), but same-quality +# armor of the preferred type will score 3x higher and be equipped instead. +# +# ARMOR TYPE PREFERENCES: +# Plate: Warriors, Paladins, Death Knights +# Mail: Hunters, Shamans +# Leather: Rogues, Druids +# Cloth: Priests, Mages, Warlocks +# +# Default: 0 (disabled) +AiPlayerbot.PreferClassArmorType = 0 + + +# When enabled, bots prefer spec-appropriate weapons based on speed and weapon type during autogear. +# Examples: Arms Warriors favor slow 2H axes/polearms (Axe Specialization), Combat Rogues +# favor a slow MH with a fast OH, and Enhancement Shamans favor synchronized slow 1H weapons. +# Default: 0 (disabled) +AiPlayerbot.PreferredSpecWeapons = 0 + # If disabled, random bots can only upgrade equipment through looting and quests # Default: 1 (enabled) AiPlayerbot.IncrementalGearInit = 1 @@ -1325,7 +1351,7 @@ AiPlayerbot.DeleteRandomBotArenaTeams = 0 AiPlayerbot.PvpProhibitedZoneIds = "2255,656,2361,2362,2363,976,35,2268,3425,392,541,1446,3828,3712,3738,3565,3539,3623,4152,3988,4658,4284,4418,4436,4275,4323,4395,3703,4298,3951" # PvP Restricted Areas (bots don't pvp) -AiPlayerbot.PvpProhibitedAreaIds = "976,35,392,2268,4161,4010,4317,4312,3649,3887,3958,3724,4080,3938,3754,3786,3973" +AiPlayerbot.PvpProhibitedAreaIds = "976,35,392,2268,4161,4010,4317,4312,3649,3887,3958,3724,4080,3938,3754,3786,3973,4085,4086,4087,4088" # Improve reaction speeds in battlegrounds and arenas (may cause lag) AiPlayerbot.FastReactInBG = 1 @@ -1803,10 +1829,10 @@ AiPlayerbot.PremadeSpecLink.11.6.80 = 05320021--230033312031500531353013251 # Requires sending the command "nc +worldbuff" in chat to a bot (or a group of bots) to enable # Each entry in the matrix should be formatted as follows: Entry:FactionID,ClassID,SpecID,MinimumLevel,MaximumLevel:SpellID1,SpellID2,etc.; # FactionID may be set to 0 for the entry to apply buffs to bots of either faction -# The default entries create a cross-faction list of level 80 buffs for each implemented pve spec from the "Premade Specs" section +# The default entries create a cross-faction level 60-69 Vanilla buffs, level 70-79 TBC buffs, and level 80 buffs for each implemented pve spec from the "Premade Specs" section # The default entries may be deleted or modified, and new custom entries may be added -AiPlayerbot.WorldBuffMatrix = # WARRIOR ARMS 1:0,1,0,80,80:53760,57358; # WARRIOR FURY 2:0,1,1,80,80:53760,57358; # WARRIOR PROTECTION 3:0,1,2,80,80:53758,57356; # PALADIN HOLY 4:0,2,0,80,80:53749,57332,60347; # PALADIN PROTECTION 5:0,2,1,80,80:53758,57356; # PALADIN RETRIBUTION 6:0,2,2,80,80:53760,57371; # HUNTER BEAST 7:0,3,0,80,80:53760,57325; # HUNTER MARKSMANSHIP 8:0,3,1,80,80:53760,57358; # HUNTER SURVIVAL 9:0,3,2,80,80:53760,57367; # ROGUE ASSASSINATION 10:0,4,0,80,80:53760,57325; # ROGUE COMBAT 11:0,4,1,80,80:53760,57358; # ROGUE SUBTLETY 12:0,4,2,80,80:53760,57367; # PRIEST DISCIPLINE 13:0,5,0,80,80:53755,57327; # PRIEST HOLY 14:0,5,1,80,80:53755,57327; # PRIEST SHADOW 15:0,5,2,80,80:53755,57327; # DEATH KNIGHT BLOOD 16:0,6,0,80,80:53758,57356; # DEATH KNIGHT FROST 17:0,6,1,80,80:53760,57358; # DEATH KNIGHT UNHOLY 18:0,6,2,80,80:53760,57358; # DEATH KNIGHT BLOOD DPS 19:0,6,3,80,80:53760,57371; # SHAMAN ELEMENTAL 20:0,7,0,80,80:53755,57327; # SHAMAN ENHANCEMENT 21:0,7,1,80,80:53760,57325; # SHAMAN RESTORATION 22:0,7,2,80,80:53755,57327; # MAGE ARCANE 23:0,8,0,80,80:53755,57327; # MAGE FIRE 24:0,8,1,80,80:53755,57327; # MAGE FROST 25:0,8,2,80,80:53755,57327; # WARLOCK AFFLICTION 26:0,9,0,80,80:53755,57327; # WARLOCK DEMONOLOGY 27:0,9,1,80,80:53755,57327; # WARLOCK DESTRUCTION 28:0,9,2,80,80:53755,57327; # DRUID BALANCE 29:0,11,0,80,80:53755,57327; # DRUID FERAL BEAR 30:0,11,1,80,80:53749,53763,57367; # DRUID RESTORATION 31:0,11,2,80,80:54212,57334; # DRUID FERAL CAT 32:0,11,3,80,80:53760,57358 +AiPlayerbot.WorldBuffMatrix = # WARRIOR ARMS 1:0,1,0,80,80:53760,57358; # WARRIOR FURY 2:0,1,1,80,80:53760,57358; # WARRIOR PROTECTION 3:0,1,2,80,80:53758,57356; # PALADIN HOLY 4:0,2,0,80,80:53749,57332,60347; # PALADIN PROTECTION 5:0,2,1,80,80:53758,57356; # PALADIN RETRIBUTION 6:0,2,2,80,80:53760,57371; # HUNTER BEAST 7:0,3,0,80,80:53760,57325; # HUNTER MARKSMANSHIP 8:0,3,1,80,80:53760,57358; # HUNTER SURVIVAL 9:0,3,2,80,80:53760,57367; # ROGUE ASSASSINATION 10:0,4,0,80,80:53760,57325; # ROGUE COMBAT 11:0,4,1,80,80:53760,57358; # ROGUE SUBTLETY 12:0,4,2,80,80:53760,57367; # PRIEST DISCIPLINE 13:0,5,0,80,80:53755,57327; # PRIEST HOLY 14:0,5,1,80,80:53755,57327; # PRIEST SHADOW 15:0,5,2,80,80:53755,57327; # DEATH KNIGHT BLOOD 16:0,6,0,80,80:53758,57356; # DEATH KNIGHT FROST 17:0,6,1,80,80:53760,57358; # DEATH KNIGHT UNHOLY 18:0,6,2,80,80:53760,57358; # DEATH KNIGHT BLOOD DPS 19:0,6,3,80,80:53760,57371; # SHAMAN ELEMENTAL 20:0,7,0,80,80:53755,57327; # SHAMAN ENHANCEMENT 21:0,7,1,80,80:53760,57325; # SHAMAN RESTORATION 22:0,7,2,80,80:53755,57327; # MAGE ARCANE 23:0,8,0,80,80:53755,57327; # MAGE FIRE 24:0,8,1,80,80:53755,57327; # MAGE FROST 25:0,8,2,80,80:53755,57327; # WARLOCK AFFLICTION 26:0,9,0,80,80:53755,57327; # WARLOCK DEMONOLOGY 27:0,9,1,80,80:53755,57327; # WARLOCK DESTRUCTION 28:0,9,2,80,80:53755,57327; # DRUID BALANCE 29:0,11,0,80,80:53755,57327; # DRUID FERAL BEAR 30:0,11,1,80,80:53749,53763,57367; # DRUID RESTORATION 31:0,11,2,80,80:54212,57334; # DRUID FERAL CAT 32:0,11,3,80,80:53760,57358; # WARRIOR ARMS TBC 33:0,1,0,70,79:28520,33256; # WARRIOR FURY TBC 34:0,1,1,70,79:28520,33256; # WARRIOR PROTECTION TBC 35:0,1,2,70,79:28518,33257; # PALADIN HOLY TBC 36:0,2,0,70,79:28491,39627,33263; # PALADIN PROTECTION TBC 37:0,2,1,70,79:28518,33257; # PALADIN RETRIBUTION TBC 38:0,2,2,70,79:28520,33256; # HUNTER BEAST TBC 39:0,3,0,70,79:28520,33261; # HUNTER MARKSMANSHIP TBC 40:0,3,1,70,79:28520,33261; # HUNTER SURVIVAL TBC 41:0,3,2,70,79:28520,33261; # ROGUE ASSASSINATION TBC 42:0,4,0,70,79:28520,33261; # ROGUE COMBAT TBC 43:0,4,1,70,79:28520,33261; # ROGUE SUBTLETY TBC 44:0,4,2,70,79:28520,33261; # PRIEST DISCIPLINE TBC 45:0,5,0,70,79:28491,39627,33263; # PRIEST HOLY TBC 46:0,5,1,70,79:28491,39627,33263; # PRIEST SHADOW TBC 47:0,5,2,70,79:28540,33263; # SHAMAN ELEMENTAL TBC 48:0,7,0,70,79:28521,33263; # SHAMAN ENHANCEMENT TBC 49:0,7,1,70,79:28520,33261; # SHAMAN RESTORATION TBC 50:0,7,2,70,79:28491,39627,33263; # MAGE ARCANE TBC 51:0,8,0,70,79:28521,33263; # MAGE FIRE TBC 52:0,8,1,70,79:28540,33263; # MAGE FROST TBC 53:0,8,2,70,79:28540,33263; # WARLOCK AFFLICTION TBC 54:0,9,0,70,79:28540,33263; # WARLOCK DEMONOLOGY TBC 55:0,9,1,70,79:28540,33263; # WARLOCK DESTRUCTION TBC 56:0,9,2,70,79:28540,33263; # DRUID BALANCE TBC 57:0,11,0,70,79:28521,33263; # DRUID FERAL BEAR TBC 58:0,11,1,70,79:28518,33257; # DRUID RESTORATION TBC 59:0,11,2,70,79:28491,39627,33263; # DRUID FERAL CAT TBC 60:0,11,3,70,79:28520,33261; # WARRIOR ARMS VANILLA 61:0,1,0,60,69:17538,24799; # WARRIOR FURY VANILLA 62:0,1,1,60,69:17538,24799; # WARRIOR PROTECTION VANILLA 63:0,1,2,60,69:17626,25661; # PALADIN HOLY VANILLA 64:0,2,0,60,69:17627,18194; # PALADIN PROTECTION VANILLA 65:0,2,1,60,69:17626,25661; # PALADIN RETRIBUTION VANILLA 66:0,2,2,60,69:17628,24799; # HUNTER BEAST VANILLA 67:0,3,0,60,69:17538,18192; # HUNTER MARKSMANSHIP VANILLA 68:0,3,1,60,69:17538,18192; # HUNTER SURVIVAL VANILLA 69:0,3,2,60,69:17538,18192; # ROGUE ASSASSINATION VANILLA 70:0,4,0,60,69:17538,18192; # ROGUE COMBAT VANILLA 71:0,4,1,60,69:17538,18192; # ROGUE SUBTLETY VANILLA 72:0,4,2,60,69:17538,18192; # PRIEST DISCIPLINE VANILLA 73:0,5,0,60,69:17628,18194; # PRIEST HOLY VANILLA 74:0,5,1,60,69:17627,18194; # PRIEST SHADOW VANILLA 75:0,5,2,60,69:17628,18194; # SHAMAN ELEMENTAL VANILLA 76:0,7,0,60,69:17628,18194; # SHAMAN ENHANCEMENT VANILLA 77:0,7,1,60,69:17538,24799; # SHAMAN RESTORATION VANILLA 78:0,7,2,60,69:17627,18194; # MAGE ARCANE VANILLA 79:0,8,0,60,69:17628,18194; # MAGE FIRE VANILLA 80:0,8,1,60,69:17628,18194; # MAGE FROST VANILLA 81:0,8,2,60,69:17628,18194; # WARLOCK AFFLICTION VANILLA 82:0,9,0,60,69:17628,25661; # WARLOCK DEMONOLOGY VANILLA 83:0,9,1,60,69:17628,25661; # WARLOCK DESTRUCTION VANILLA 84:0,9,2,60,69:17628,25661; # DRUID BALANCE VANILLA 85:0,11,0,60,69:17628,18194; # DRUID FERAL BEAR VANILLA 86:0,11,1,60,69:17626,25661; # DRUID RESTORATION VANILLA 87:0,11,2,60,69:17627,18194; # DRUID FERAL CAT VANILLA 88:0,11,3,60,69:17538,24799 # # @@ -1837,12 +1863,24 @@ AiPlayerbot.WorldBuffMatrix = # WARRIOR ARMS 1:0,1,0,80,80:53760,57358; # WARRIO # # +# arms pve AiPlayerbot.RandomClassSpecProb.1.0 = 20 AiPlayerbot.RandomClassSpecIndex.1.0 = 0 +# fury pve AiPlayerbot.RandomClassSpecProb.1.1 = 40 AiPlayerbot.RandomClassSpecIndex.1.1 = 1 +# prot pve AiPlayerbot.RandomClassSpecProb.1.2 = 40 AiPlayerbot.RandomClassSpecIndex.1.2 = 2 +# arms pvp +AiPlayerbot.RandomClassSpecProb.1.3 = 0 +AiPlayerbot.RandomClassSpecIndex.1.3 = 3 +# fury pvp +AiPlayerbot.RandomClassSpecProb.1.4 = 0 +AiPlayerbot.RandomClassSpecIndex.1.4 = 4 +# prot pvp +AiPlayerbot.RandomClassSpecProb.1.5 = 0 +AiPlayerbot.RandomClassSpecIndex.1.5 = 5 # # @@ -1854,12 +1892,24 @@ AiPlayerbot.RandomClassSpecIndex.1.2 = 2 # # +# holy pve AiPlayerbot.RandomClassSpecProb.2.0 = 30 AiPlayerbot.RandomClassSpecIndex.2.0 = 0 +# prot pve AiPlayerbot.RandomClassSpecProb.2.1 = 40 AiPlayerbot.RandomClassSpecIndex.2.1 = 1 +# ret pve AiPlayerbot.RandomClassSpecProb.2.2 = 30 AiPlayerbot.RandomClassSpecIndex.2.2 = 2 +# holy pvp +AiPlayerbot.RandomClassSpecProb.2.3 = 0 +AiPlayerbot.RandomClassSpecIndex.2.3 = 3 +# prot pvp +AiPlayerbot.RandomClassSpecProb.2.4 = 0 +AiPlayerbot.RandomClassSpecIndex.2.4 = 4 +# ret pvp +AiPlayerbot.RandomClassSpecProb.2.5 = 0 +AiPlayerbot.RandomClassSpecIndex.2.5 = 5 # # @@ -1871,12 +1921,24 @@ AiPlayerbot.RandomClassSpecIndex.2.2 = 2 # # +# bm pve AiPlayerbot.RandomClassSpecProb.3.0 = 33 AiPlayerbot.RandomClassSpecIndex.3.0 = 0 +# mm pve AiPlayerbot.RandomClassSpecProb.3.1 = 33 AiPlayerbot.RandomClassSpecIndex.3.1 = 1 +# surv pve AiPlayerbot.RandomClassSpecProb.3.2 = 33 AiPlayerbot.RandomClassSpecIndex.3.2 = 2 +# bm pvp +AiPlayerbot.RandomClassSpecProb.3.3 = 0 +AiPlayerbot.RandomClassSpecIndex.3.3 = 3 +# mm pvp +AiPlayerbot.RandomClassSpecProb.3.4 = 0 +AiPlayerbot.RandomClassSpecIndex.3.4 = 4 +# surv pvp +AiPlayerbot.RandomClassSpecProb.3.5 = 0 +AiPlayerbot.RandomClassSpecIndex.3.5 = 5 # # @@ -1888,12 +1950,24 @@ AiPlayerbot.RandomClassSpecIndex.3.2 = 2 # # +# as pve AiPlayerbot.RandomClassSpecProb.4.0 = 45 AiPlayerbot.RandomClassSpecIndex.4.0 = 0 +# combat pve AiPlayerbot.RandomClassSpecProb.4.1 = 45 AiPlayerbot.RandomClassSpecIndex.4.1 = 1 +# subtlety pve AiPlayerbot.RandomClassSpecProb.4.2 = 10 AiPlayerbot.RandomClassSpecIndex.4.2 = 2 +# as pvp +AiPlayerbot.RandomClassSpecProb.4.3 = 0 +AiPlayerbot.RandomClassSpecIndex.4.3 = 3 +# combat pvp +AiPlayerbot.RandomClassSpecProb.4.4 = 0 +AiPlayerbot.RandomClassSpecIndex.4.4 = 4 +# subtlety pvp +AiPlayerbot.RandomClassSpecProb.4.5 = 0 +AiPlayerbot.RandomClassSpecIndex.4.5 = 5 # # @@ -1905,12 +1979,24 @@ AiPlayerbot.RandomClassSpecIndex.4.2 = 2 # # +# disc pve AiPlayerbot.RandomClassSpecProb.5.0 = 40 AiPlayerbot.RandomClassSpecIndex.5.0 = 0 +# holy pve AiPlayerbot.RandomClassSpecProb.5.1 = 35 AiPlayerbot.RandomClassSpecIndex.5.1 = 1 +# shadow pve AiPlayerbot.RandomClassSpecProb.5.2 = 25 AiPlayerbot.RandomClassSpecIndex.5.2 = 2 +# disc pvp +AiPlayerbot.RandomClassSpecProb.5.3 = 0 +AiPlayerbot.RandomClassSpecIndex.5.3 = 3 +# holy pvp +AiPlayerbot.RandomClassSpecProb.5.4 = 0 +AiPlayerbot.RandomClassSpecIndex.5.4 = 4 +# shadow pvp +AiPlayerbot.RandomClassSpecProb.5.5 = 0 +AiPlayerbot.RandomClassSpecIndex.5.5 = 5 # # @@ -1922,12 +2008,27 @@ AiPlayerbot.RandomClassSpecIndex.5.2 = 2 # # +# blood pve AiPlayerbot.RandomClassSpecProb.6.0 = 30 AiPlayerbot.RandomClassSpecIndex.6.0 = 0 +# frost pve AiPlayerbot.RandomClassSpecProb.6.1 = 40 AiPlayerbot.RandomClassSpecIndex.6.1 = 1 +# unholy pve AiPlayerbot.RandomClassSpecProb.6.2 = 30 AiPlayerbot.RandomClassSpecIndex.6.2 = 2 +# double aura blood pve +AiPlayerbot.RandomClassSpecProb.6.3 = 0 +AiPlayerbot.RandomClassSpecIndex.6.3 = 3 +# blood pvp +AiPlayerbot.RandomClassSpecProb.6.4 = 0 +AiPlayerbot.RandomClassSpecIndex.6.4 = 4 +# frost pvp +AiPlayerbot.RandomClassSpecProb.6.5 = 0 +AiPlayerbot.RandomClassSpecIndex.6.5 = 5 +# unholy pvp +AiPlayerbot.RandomClassSpecProb.6.6 = 0 +AiPlayerbot.RandomClassSpecIndex.6.6 = 6 # # @@ -1939,12 +2040,24 @@ AiPlayerbot.RandomClassSpecIndex.6.2 = 2 # # +# ele pve AiPlayerbot.RandomClassSpecProb.7.0 = 33 AiPlayerbot.RandomClassSpecIndex.7.0 = 0 +# enh pve AiPlayerbot.RandomClassSpecProb.7.1 = 33 AiPlayerbot.RandomClassSpecIndex.7.1 = 1 +# resto pve AiPlayerbot.RandomClassSpecProb.7.2 = 33 AiPlayerbot.RandomClassSpecIndex.7.2 = 2 +# ele pvp +AiPlayerbot.RandomClassSpecProb.7.3 = 0 +AiPlayerbot.RandomClassSpecIndex.7.3 = 3 +# enh pvp +AiPlayerbot.RandomClassSpecProb.7.4 = 0 +AiPlayerbot.RandomClassSpecIndex.7.4 = 4 +# resto pvp +AiPlayerbot.RandomClassSpecProb.7.5 = 0 +AiPlayerbot.RandomClassSpecIndex.7.5 = 5 # # @@ -1956,12 +2069,27 @@ AiPlayerbot.RandomClassSpecIndex.7.2 = 2 # # +# arcane pve AiPlayerbot.RandomClassSpecProb.8.0 = 30 AiPlayerbot.RandomClassSpecIndex.8.0 = 0 +# fire pve AiPlayerbot.RandomClassSpecProb.8.1 = 30 AiPlayerbot.RandomClassSpecIndex.8.1 = 1 +# frost pve AiPlayerbot.RandomClassSpecProb.8.2 = 40 AiPlayerbot.RandomClassSpecIndex.8.2 = 2 +# frostfire pve +AiPlayerbot.RandomClassSpecProb.8.3 = 0 +AiPlayerbot.RandomClassSpecIndex.8.3 = 3 +# arcane pvp +AiPlayerbot.RandomClassSpecProb.8.4 = 0 +AiPlayerbot.RandomClassSpecIndex.8.4 = 4 +# fire pvp +AiPlayerbot.RandomClassSpecProb.8.5 = 0 +AiPlayerbot.RandomClassSpecIndex.8.5 = 5 +# frost pvp +AiPlayerbot.RandomClassSpecProb.8.6 = 0 +AiPlayerbot.RandomClassSpecIndex.8.6 = 6 # # @@ -1973,12 +2101,24 @@ AiPlayerbot.RandomClassSpecIndex.8.2 = 2 # # +# affli pve AiPlayerbot.RandomClassSpecProb.9.0 = 33 AiPlayerbot.RandomClassSpecIndex.9.0 = 0 +# demo pve AiPlayerbot.RandomClassSpecProb.9.1 = 34 AiPlayerbot.RandomClassSpecIndex.9.1 = 1 +# destro pve AiPlayerbot.RandomClassSpecProb.9.2 = 33 AiPlayerbot.RandomClassSpecIndex.9.2 = 2 +# affli pvp +AiPlayerbot.RandomClassSpecProb.9.3 = 0 +AiPlayerbot.RandomClassSpecIndex.9.3 = 3 +# demo pvp +AiPlayerbot.RandomClassSpecProb.9.4 = 0 +AiPlayerbot.RandomClassSpecIndex.9.4 = 4 +# destro pvp +AiPlayerbot.RandomClassSpecProb.9.5 = 0 +AiPlayerbot.RandomClassSpecIndex.9.5 = 5 # # @@ -1990,14 +2130,27 @@ AiPlayerbot.RandomClassSpecIndex.9.2 = 2 # # +# balance pve AiPlayerbot.RandomClassSpecProb.11.0 = 20 AiPlayerbot.RandomClassSpecIndex.11.0 = 0 +# bear pve AiPlayerbot.RandomClassSpecProb.11.1 = 25 AiPlayerbot.RandomClassSpecIndex.11.1 = 1 +# resto pve AiPlayerbot.RandomClassSpecProb.11.2 = 35 AiPlayerbot.RandomClassSpecIndex.11.2 = 2 +# cat pve AiPlayerbot.RandomClassSpecProb.11.3 = 20 AiPlayerbot.RandomClassSpecIndex.11.3 = 3 +# balance pvp +AiPlayerbot.RandomClassSpecProb.11.4 = 0 +AiPlayerbot.RandomClassSpecIndex.11.4 = 4 +# cat pvp +AiPlayerbot.RandomClassSpecProb.11.5 = 0 +AiPlayerbot.RandomClassSpecIndex.11.5 = 5 +# resto pvp +AiPlayerbot.RandomClassSpecProb.11.6 = 0 +AiPlayerbot.RandomClassSpecIndex.11.6 = 6 # # diff --git a/data/sql/playerbots/updates/2026_04_12_00_ai_playerbot_pull_texts.sql b/data/sql/playerbots/updates/2026_04_12_00_ai_playerbot_pull_texts.sql new file mode 100644 index 000000000..4e9c17583 --- /dev/null +++ b/data/sql/playerbots/updates/2026_04_12_00_ai_playerbot_pull_texts.sql @@ -0,0 +1,102 @@ +-- ######################################################### +-- Playerbots - Add pull command texts +-- Localized for all WotLK locales (koKR, frFR, deDE, zhCN, +-- zhTW, esES, esMX, ruRU) +-- ######################################################### + +DELETE FROM ai_playerbot_texts WHERE name IN ( + 'pull_no_target_error', + 'pull_target_too_far_error', + 'pull_invalid_target_error', + 'pull_action_unavailable_error' +); +DELETE FROM ai_playerbot_texts_chance WHERE name IN ( + 'pull_no_target_error', + 'pull_target_too_far_error', + 'pull_invalid_target_error', + 'pull_action_unavailable_error' +); + +-- pull_no_target_error +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 ( + 1755, + 'pull_no_target_error', + 'You have no target', + 0, 0, + '대상이 없습니다', + 'Vous n''avez pas de cible', + 'Du hast kein Ziel', + '你没有目标', + '你沒有目標', + 'No tienes objetivo', + 'No tienes objetivo', + 'У вас нет цели'); + +INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES ('pull_no_target_error', 100); + +-- pull_target_too_far_error +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 ( + 1756, + 'pull_target_too_far_error', + 'The target is too far away', + 0, 0, + '대상이 너무 멀리 있습니다', + 'La cible est trop loin', + 'Das Ziel ist zu weit entfernt', + '目标太远了', + '目標太遠了', + 'El objetivo está demasiado lejos', + 'El objetivo está demasiado lejos', + 'Цель слишком далеко'); + +INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES ('pull_target_too_far_error', 100); + +-- pull_invalid_target_error +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 ( + 1757, + 'pull_invalid_target_error', + 'The target can''t be pulled', + 0, 0, + '해당 대상은 풀링할 수 없습니다', + 'La cible ne peut pas être attirée', + 'Das Ziel kann nicht gepullt werden', + '该目标无法被拉怪', + '該目標無法被拉怪', + 'No se puede hacer pull al objetivo', + 'No se puede hacer pull al objetivo', + 'Эту цель нельзя пуллить'); + +INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES ('pull_invalid_target_error', 100); + +-- pull_action_unavailable_error: %action_name is replaced with the configured pull action +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 ( + 1758, + 'pull_action_unavailable_error', + 'Can''t perform pull action ''%action_name''', + 0, 0, + '''%action_name'' 풀 액션을 수행할 수 없습니다', + 'Impossible d''effectuer l''action d''engagement ''%action_name''', + 'Die Pull-Aktion ''%action_name'' kann nicht ausgeführt werden', + '无法执行拉怪动作“%action_name”', + '無法執行拉怪動作「%action_name」', + 'No se puede realizar la acción de pull ''%action_name''', + 'No se puede realizar la acción de pull ''%action_name''', + 'Невозможно выполнить действие пула ''%action_name'''); + +INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES ('pull_action_unavailable_error', 100); diff --git a/src/Ai/Base/ActionContext.h b/src/Ai/Base/ActionContext.h index 79b5b4985..3026cfd50 100644 --- a/src/Ai/Base/ActionContext.h +++ b/src/Ai/Base/ActionContext.h @@ -45,6 +45,7 @@ #include "NonCombatActions.h" #include "OutfitAction.h" #include "PositionAction.h" +#include "PullActions.h" #include "DropQuestAction.h" #include "RandomBotUpdateAction.h" #include "ReachTargetActions.h" @@ -105,6 +106,13 @@ public: creators["shoot"] = &ActionContext::shoot; creators["lifeblood"] = &ActionContext::lifeblood; creators["arcane torrent"] = &ActionContext::arcane_torrent; + creators["pull my target"] = &ActionContext::pull_my_target; + creators["pull rti target"] = &ActionContext::pull_rti_target; + creators["pull start"] = &ActionContext::pull_start; + creators["pull action"] = &ActionContext::pull_action; + creators["pull end"] = &ActionContext::pull_end; + creators["return to pull position"] = &ActionContext::return_to_pull_position; + creators["reach pull"] = &ActionContext::reach_pull; creators["end pull"] = &ActionContext::end_pull; creators["healthstone"] = &ActionContext::healthstone; creators["healing potion"] = &ActionContext::healing_potion; @@ -313,6 +321,13 @@ private: static Action* gift_of_the_naaru(PlayerbotAI* botAI) { return new CastGiftOfTheNaaruAction(botAI); } static Action* lifeblood(PlayerbotAI* botAI) { return new CastLifeBloodAction(botAI); } static Action* arcane_torrent(PlayerbotAI* botAI) { return new CastArcaneTorrentAction(botAI); } + static Action* pull_my_target(PlayerbotAI* botAI) { return new PullMyTargetAction(botAI); } + static Action* pull_rti_target(PlayerbotAI* botAI) { return new PullRtiTargetAction(botAI); } + static Action* pull_start(PlayerbotAI* botAI) { return new PullStartAction(botAI); } + static Action* pull_action(PlayerbotAI* botAI) { return new PullAction(botAI); } + static Action* pull_end(PlayerbotAI* botAI) { return new PullEndAction(botAI); } + static Action* return_to_pull_position(PlayerbotAI* botAI) { return new ReturnToPullPositionAction(botAI); } + static Action* reach_pull(PlayerbotAI* botAI) { return new ReachPullAction(botAI); } static Action* mana_tap(PlayerbotAI* botAI) { return new CastManaTapAction(botAI); } static Action* end_pull(PlayerbotAI* botAI) { return new ChangeCombatStrategyAction(botAI, "-pull"); } static Action* cancel_channel(PlayerbotAI* botAI) { return new CancelChannelAction(botAI); } diff --git a/src/Ai/Base/Actions/AttackAction.cpp b/src/Ai/Base/Actions/AttackAction.cpp index 96bf5c4d3..a537dd218 100644 --- a/src/Ai/Base/Actions/AttackAction.cpp +++ b/src/Ai/Base/Actions/AttackAction.cpp @@ -53,22 +53,6 @@ bool AttackMyTargetAction::Execute(Event /*event*/) bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/) { - Unit* oldTarget = context->GetValue("current target")->Get(); - bool shouldMelee = bot->IsWithinMeleeRange(target) || botAI->IsMelee(bot); - - bool sameTarget = oldTarget == target && bot->GetVictim() == target; - bool inCombat = botAI->GetState() == BOT_STATE_COMBAT; - bool sameAttackMode = bot->HasUnitState(UNIT_STATE_MELEE_ATTACKING) == shouldMelee; - - if (bot->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE || - bot->HasUnitState(UNIT_STATE_IN_FLIGHT)) - { - if (verbose) - botAI->TellError("I cannot attack in flight"); - - return false; - } - if (!target) { if (verbose) @@ -85,6 +69,15 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/) return false; } + if (bot->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE || + bot->HasUnitState(UNIT_STATE_IN_FLIGHT)) + { + if (verbose) + botAI->TellError("I cannot attack in flight"); + + return false; + } + // Check if bot OR target is in prohibited zone/area (skip for duels) if ((target->IsPlayer() || target->IsPet()) && (!bot->duel || bot->duel->Opponent != target) && @@ -121,6 +114,18 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/) return false; } + // Infantry attacks are not allowed from vehicles drivers. + // Check is needed to stop some auto-attack situations. + if (botAI->IsInVehicle() && !botAI->IsInVehicle(false, false, true)) + return false; + + Unit* oldTarget = context->GetValue("current target")->Get(); + bool shouldMelee = bot->IsWithinMeleeRange(target) || botAI->IsMelee(bot); + + bool sameTarget = oldTarget == target && bot->GetVictim() == target; + bool inCombat = botAI->GetState() == BOT_STATE_COMBAT; + bool sameAttackMode = bot->HasUnitState(UNIT_STATE_MELEE_ATTACKING) == shouldMelee; + if (sameTarget && inCombat && sameAttackMode) { if (verbose) @@ -146,8 +151,7 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/) ObjectGuid guid = target->GetGUID(); bot->SetSelection(target->GetGUID()); - context->GetValue("old target")->Set(oldTarget); - + context->GetValue("old target")->Set(oldTarget); context->GetValue("current target")->Set(target); context->GetValue("available loot")->Get()->Add(guid); diff --git a/src/Ai/Base/Actions/AutoMaintenanceOnLevelupAction.cpp b/src/Ai/Base/Actions/AutoMaintenanceOnLevelupAction.cpp index 72433c15f..75152d87b 100644 --- a/src/Ai/Base/Actions/AutoMaintenanceOnLevelupAction.cpp +++ b/src/Ai/Base/Actions/AutoMaintenanceOnLevelupAction.cpp @@ -73,7 +73,7 @@ void AutoMaintenanceOnLevelupAction::LearnSpells(std::ostringstream* out) LearnQuestSpells(out); } -void AutoMaintenanceOnLevelupAction::LearnTrainerSpells(std::ostringstream* out) +void AutoMaintenanceOnLevelupAction::LearnTrainerSpells(std::ostringstream* /*out*/) { PlayerbotFactory factory(bot, bot->GetLevel()); factory.InitSkills(); diff --git a/src/Ai/Base/Actions/BankAction.cpp b/src/Ai/Base/Actions/BankAction.cpp index 4d8d6c4d8..5a4975f77 100644 --- a/src/Ai/Base/Actions/BankAction.cpp +++ b/src/Ai/Base/Actions/BankAction.cpp @@ -27,7 +27,7 @@ bool BankAction::Execute(Event event) return false; } -bool BankAction::ExecuteBank(std::string const text, Unit* bank) +bool BankAction::ExecuteBank(std::string const text, Unit* /*bank*/) { if (text.empty() || text == "?") { diff --git a/src/Ai/Base/Actions/BattleGroundJoinAction.cpp b/src/Ai/Base/Actions/BattleGroundJoinAction.cpp index fdc13120f..ab897a1b2 100644 --- a/src/Ai/Base/Actions/BattleGroundJoinAction.cpp +++ b/src/Ai/Base/Actions/BattleGroundJoinAction.cpp @@ -534,21 +534,18 @@ bool BGJoinAction::JoinQueue(uint32 type) botAI->GetAiObjectContext()->GetValue("bg type")->Set(0); + WorldPacket* packet = nullptr; if (!isArena) { - WorldPacket* packet = new WorldPacket(CMSG_BATTLEMASTER_JOIN, 20); + packet = new WorldPacket(CMSG_BATTLEMASTER_JOIN, 20); *packet << bot->GetGUID() << bgTypeId_ << instanceId << joinAsGroup; - /// FIX race condition - // bot->GetSession()->HandleBattlemasterJoinOpcode(packet); - bot->GetSession()->QueuePacket(packet); } else { - WorldPacket arena_packet(CMSG_BATTLEMASTER_JOIN_ARENA, 20); - arena_packet << unit->GetGUID() << arenaslot << asGroup << uint8(isRated); - bot->GetSession()->HandleBattlemasterJoinArena(arena_packet); + packet = new WorldPacket(CMSG_BATTLEMASTER_JOIN_ARENA, 20); + *packet << unit->GetGUID() << arenaslot << asGroup << uint8(isRated); } - + bot->GetSession()->QueuePacket(packet); return true; } diff --git a/src/Ai/Base/Actions/CastCustomSpellAction.h b/src/Ai/Base/Actions/CastCustomSpellAction.h index ed53b18a5..6cfc1e689 100644 --- a/src/Ai/Base/Actions/CastCustomSpellAction.h +++ b/src/Ai/Base/Actions/CastCustomSpellAction.h @@ -21,7 +21,7 @@ public: } bool Execute(Event event) override; - virtual std::string const castString(WorldObject* target) { return "cast"; } + virtual std::string const castString(WorldObject* /*target*/) { return "cast"; } protected: bool ncCast = false; @@ -49,7 +49,7 @@ public: bool isUseful() override { return false; } virtual bool AcceptSpell(SpellInfo const* spellInfo); - virtual uint32 GetSpellPriority(SpellInfo const* spellInfo) { return 1; } + virtual uint32 GetSpellPriority(SpellInfo const* /*spellInfo*/) { return 1; } virtual bool castSpell(uint32 spellId, WorldObject* wo); bool Execute(Event event) override; diff --git a/src/Ai/Base/Actions/ChatShortcutActions.cpp b/src/Ai/Base/Actions/ChatShortcutActions.cpp index 0caeb5e0d..b715cb2bb 100644 --- a/src/Ai/Base/Actions/ChatShortcutActions.cpp +++ b/src/Ai/Base/Actions/ChatShortcutActions.cpp @@ -80,7 +80,7 @@ bool FollowChatShortcutAction::Execute(Event /*event*/) true, priority); } - if (Pet* pet = bot->GetPet()) + if (bot->GetPet()) botAI->PetFollow(); if (moved) diff --git a/src/Ai/Base/Actions/ChooseRpgTargetAction.cpp b/src/Ai/Base/Actions/ChooseRpgTargetAction.cpp index 76e6fe05f..8d15ae9be 100644 --- a/src/Ai/Base/Actions/ChooseRpgTargetAction.cpp +++ b/src/Ai/Base/Actions/ChooseRpgTargetAction.cpp @@ -116,6 +116,7 @@ bool ChooseRpgTargetAction::Execute(Event /*event*/) GuidPosition masterRpgTarget; if (master && master != bot && GET_PLAYERBOT_AI(master) && master->GetMapId() == bot->GetMapId() && !master->IsBeingTeleported()) { + //TODO Implement Player* player = botAI->GetMaster(); //GuidPosition masterRpgTarget = PAI_VALUE(GuidPosition, "rpg target"); //not used, line marked for removal. } diff --git a/src/Ai/Base/Actions/DropQuestAction.cpp b/src/Ai/Base/Actions/DropQuestAction.cpp index b3cba9c56..f6712abf3 100644 --- a/src/Ai/Base/Actions/DropQuestAction.cpp +++ b/src/Ai/Base/Actions/DropQuestAction.cpp @@ -62,31 +62,16 @@ bool CleanQuestLogAction::Execute(Event event) { Player* requester = event.getOwner() ? event.getOwner() : GetMaster(); if (!requester) - { - botAI->TellMaster("No event owner detected"); return false; - } if (!sPlayerbotAIConfig.dropObsoleteQuests) - { return false; - } // Only output this message if "debug rpg" strategy is enabled if (botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT)) - { botAI->TellMaster("Clean Quest Log command received, removing grey/trivial quests..."); - } uint8 botLevel = bot->GetLevel(); // Get bot's level - uint8 numQuest = 0; - for (uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot) - { - if (bot->GetQuestSlotQuestId(slot)) - { - numQuest++; - } - } for (uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot) { @@ -101,34 +86,24 @@ bool CleanQuestLogAction::Execute(Event event) // Determine if quest is trivial by comparing levels int32 questLevel = quest->GetQuestLevel(); if (questLevel == -1) // For scaling quests, default to bot level - { questLevel = botLevel; - } // Set the level difference for when a quest becomes trivial // This was determined by using the Lua code the client uses int32 trivialLevel = 5; if (botLevel >= 40) - { trivialLevel = 8; - } else if (botLevel >= 30) - { trivialLevel = 7; - } else if (botLevel >= 20) - { trivialLevel = 6; - } // Check if the quest is trivial (grey) for the bot if ((botLevel - questLevel) > trivialLevel) { // Output only if "debug rpg" strategy is enabled if (botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT)) - { botAI->TellMaster("Quest [ " + quest->GetTitle() + " ] will be removed because it is trivial (grey)."); - } // Remove quest botAI->rpgStatistic.questDropped++; @@ -137,8 +112,6 @@ bool CleanQuestLogAction::Execute(Event event) bot->SetQuestStatus(questId, QUEST_STATUS_NONE); bot->RemoveRewardedQuest(questId); - numQuest--; - if (botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT)) { const std::string text_quest = ChatHelper::FormatQuest(quest); @@ -147,17 +120,13 @@ bool CleanQuestLogAction::Execute(Event event) } if (botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT)) - { botAI->TellMaster("Quest [ " + quest->GetTitle() + " ] has been removed."); - } } else { // Only output if "debug rpg" strategy is enabled if (botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT)) - { botAI->TellMaster("Quest [ " + quest->GetTitle() + " ] is not trivial and will be kept."); - } } } @@ -174,7 +143,6 @@ void CleanQuestLogAction::DropQuestType(uint8& numQuest, uint8 wantNum, bool isG { std::random_device rd; std::mt19937 g(rd()); - std::shuffle(slots.begin(), slots.end(), g); } @@ -200,8 +168,10 @@ void CleanQuestLogAction::DropQuestType(uint8& numQuest, uint8 wantNum, bool isG bot->GetLevel() <= bot->GetQuestLevel(quest) + uint32(lowLevelDiff)) // Quest is not gray { if (bot->GetLevel() + 5 > bot->GetQuestLevel(quest)) // Quest is not red + { if (!isGreen) continue; + } } else // Quest is gray { diff --git a/src/Ai/Base/Actions/FollowActions.cpp b/src/Ai/Base/Actions/FollowActions.cpp index 1100ab460..12335e855 100644 --- a/src/Ai/Base/Actions/FollowActions.cpp +++ b/src/Ai/Base/Actions/FollowActions.cpp @@ -93,8 +93,8 @@ bool FollowAction::Execute(Event /*event*/) ? MovementPriority::MOVEMENT_COMBAT : MovementPriority::MOVEMENT_NORMAL; - bool const movingAllowed = IsMovingAllowed(mapId, destX, destY, destZ); - bool const dupMove = IsDuplicateMove(mapId, destX, destY, destZ); + bool const movingAllowed = IsMovingAllowed(); + bool const dupMove = IsDuplicateMove(destX, destY, destZ); bool const waiting = IsWaitingForLastMove(priority); if (movingAllowed && !dupMove && !waiting) diff --git a/src/Ai/Base/Actions/GenericSpellActions.cpp b/src/Ai/Base/Actions/GenericSpellActions.cpp index 41911f62d..c81aca214 100644 --- a/src/Ai/Base/Actions/GenericSpellActions.cpp +++ b/src/Ai/Base/Actions/GenericSpellActions.cpp @@ -273,7 +273,7 @@ bool BuffOnPartyAction::Execute(Event /*event*/) } // End greater buff fix -CastShootAction::CastShootAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "shoot") +CastShootAction::CastShootAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "shoot"), shootSpellId(0) { if (Item* const pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED)) { @@ -283,17 +283,40 @@ CastShootAction::CastShootAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "s { case ITEM_SUBCLASS_WEAPON_GUN: spell += " gun"; + shootSpellId = 3018; break; case ITEM_SUBCLASS_WEAPON_BOW: spell += " bow"; + shootSpellId = 3018; break; case ITEM_SUBCLASS_WEAPON_CROSSBOW: spell += " crossbow"; + shootSpellId = 3018; + break; + case ITEM_SUBCLASS_WEAPON_THROWN: + spell = "throw"; + shootSpellId = 2764; break; } } } +bool CastShootAction::isPossible() +{ + if (shootSpellId) + return botAI->CanCastSpell(shootSpellId, GetTarget(), false); + + return CastSpellAction::isPossible(); +} + +bool CastShootAction::Execute(Event /*event*/) +{ + if (shootSpellId) + return botAI->CastSpell(shootSpellId, GetTarget()); + + return botAI->CastSpell(spell, GetTarget()); +} + Value* CastDebuffSpellOnAttackerAction::GetTargetValue() { return context->GetValue("attacker without aura", spell); diff --git a/src/Ai/Base/Actions/GenericSpellActions.h b/src/Ai/Base/Actions/GenericSpellActions.h index dc0785713..e9dacb7d2 100644 --- a/src/Ai/Base/Actions/GenericSpellActions.h +++ b/src/Ai/Base/Actions/GenericSpellActions.h @@ -253,7 +253,12 @@ class CastShootAction : public CastSpellAction public: CastShootAction(PlayerbotAI* botAI); + bool isPossible() override; + bool Execute(Event event) override; ActionThreatType getThreatType() override { return ActionThreatType::None; } + +private: + uint32 shootSpellId; }; class CastLifeBloodAction : public CastHealingSpellAction diff --git a/src/Ai/Base/Actions/GuildBankAction.cpp b/src/Ai/Base/Actions/GuildBankAction.cpp index 9693556d7..81fb0f4bd 100644 --- a/src/Ai/Base/Actions/GuildBankAction.cpp +++ b/src/Ai/Base/Actions/GuildBankAction.cpp @@ -53,7 +53,7 @@ bool GuildBankAction::Execute(std::string const text, GameObject* bank) return result; } -bool GuildBankAction::MoveFromCharToBank(Item* item, GameObject* bank) +bool GuildBankAction::MoveFromCharToBank(Item* item, GameObject* /*bank*/) { uint32 playerSlot = item->GetSlot(); uint32 playerBag = item->GetBagSlot(); diff --git a/src/Ai/Base/Actions/MailAction.cpp b/src/Ai/Base/Actions/MailAction.cpp index 6fd07e1e9..80f6002a3 100644 --- a/src/Ai/Base/Actions/MailAction.cpp +++ b/src/Ai/Base/Actions/MailAction.cpp @@ -78,7 +78,7 @@ private: class TakeMailProcessor : public MailProcessor { public: - bool Process(uint32 index, Mail* mail, PlayerbotAI* botAI) override + bool Process(uint32 /*index*/, Mail* mail, PlayerbotAI* botAI) override { Player* bot = botAI->GetBot(); if (!CheckBagSpace(bot)) @@ -104,7 +104,7 @@ public: { std::vector guids; for (MailItemInfoVec::iterator i = mail->items.begin(); i != mail->items.end(); ++i) - if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(i->item_template)) + if (sObjectMgr->GetItemTemplate(i->item_template)) guids.push_back(i->item_guid); for (std::vector::iterator i = guids.begin(); i != guids.end(); ++i) @@ -157,7 +157,7 @@ private: class DeleteMailProcessor : public MailProcessor { public: - bool Process(uint32 index, Mail* mail, PlayerbotAI* botAI) override + bool Process(uint32 /*index*/, Mail* mail, PlayerbotAI* botAI) override { std::ostringstream out; out << "|cffffffff" << mail->subject << "|cffff0000 deleted"; @@ -172,7 +172,7 @@ public: class ReadMailProcessor : public MailProcessor { public: - bool Process(uint32 index, Mail* mail, PlayerbotAI* botAI) override + bool Process(uint32 /*index*/, Mail* mail, PlayerbotAI* botAI) override { std::ostringstream out, body; out << "|cffffffff" << mail->subject; diff --git a/src/Ai/Base/Actions/MovementActions.cpp b/src/Ai/Base/Actions/MovementActions.cpp index c57894e31..6e04d881f 100644 --- a/src/Ai/Base/Actions/MovementActions.cpp +++ b/src/Ai/Base/Actions/MovementActions.cpp @@ -67,10 +67,10 @@ void MovementAction::CreateWp(Player* wpOwner, float x, float y, float z, float bool MovementAction::JumpTo(uint32 mapId, float x, float y, float z, MovementPriority priority) { UpdateMovementState(); - if (!IsMovingAllowed(mapId, x, y, z)) + if (!IsMovingAllowed()) return false; - if (IsDuplicateMove(mapId, x, y, z)) + if (IsDuplicateMove(x, y, z)) return false; if (IsWaitingForLastMove(priority)) @@ -173,11 +173,11 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, bool exact_waypoint, MovementPriority priority, bool lessDelay, bool backwards) { UpdateMovementState(); - if (!IsMovingAllowed(mapId, x, y, z)) + if (!IsMovingAllowed()) { return false; } - if (IsDuplicateMove(mapId, x, y, z)) + if (IsDuplicateMove(x, y, z)) { return false; } @@ -899,20 +899,7 @@ bool MovementAction::IsMovingAllowed(WorldObject* target) return IsMovingAllowed(); } -bool MovementAction::IsMovingAllowed(uint32 mapId, float x, float y, float z) -{ - // removed sqrt as means distance limit was effectively 22500 (ReactDistance�) - // leaving it commented incase we find ReactDistance limit causes problems - // float distance = sqrt(bot->GetDistance(x, y, z)); - - // Remove react distance limit - // if (!bot->InBattleground()) - // return false; - - return IsMovingAllowed(); -} - -bool MovementAction::IsDuplicateMove(uint32 mapId, float x, float y, float z) +bool MovementAction::IsDuplicateMove(float x, float y, float z) { LastMovement& lastMove = *context->GetValue("last movement"); @@ -1288,7 +1275,7 @@ bool MovementAction::Follow(Unit* target, float distance, float angle) return true; } -bool MovementAction::ChaseTo(WorldObject* obj, float distance, float angle) +bool MovementAction::ChaseTo(WorldObject* obj, float distance) { if (!IsMovingAllowed()) { @@ -1874,7 +1861,7 @@ bool FleeAction::isUseful() bool FleeWithPetAction::Execute(Event /*event*/) { - if (Pet* pet = bot->GetPet()) + if (bot->GetPet()) botAI->PetFollow(); return Flee(AI_VALUE(Unit*, "current target")); diff --git a/src/Ai/Base/Actions/MovementActions.h b/src/Ai/Base/Actions/MovementActions.h index 5ed96dcfa..29b50880c 100644 --- a/src/Ai/Base/Actions/MovementActions.h +++ b/src/Ai/Base/Actions/MovementActions.h @@ -57,14 +57,13 @@ protected: float GetFollowAngle(); bool Follow(Unit* target, float distance = sPlayerbotAIConfig.followDistance); bool Follow(Unit* target, float distance, float angle); - bool ChaseTo(WorldObject* obj, float distance = 0.0f, float angle = 0.0f); + bool ChaseTo(WorldObject* obj, float distance = 0.0f); bool ReachCombatTo(Unit* target, float distance = 0.0f); float MoveDelay(float distance, bool backwards = false); void WaitForReach(float distance); void SetNextMovementDelay(float delayMillis); bool IsMovingAllowed(WorldObject* target); - bool IsMovingAllowed(uint32 mapId, float x, float y, float z); - bool IsDuplicateMove(uint32 mapId, float x, float y, float z); + bool IsDuplicateMove(float x, float y, float z); bool IsWaitingForLastMove(MovementPriority priority); bool IsMovingAllowed(); bool Flee(Unit* target); diff --git a/src/Ai/Base/Actions/PullActions.cpp b/src/Ai/Base/Actions/PullActions.cpp new file mode 100644 index 000000000..805abd362 --- /dev/null +++ b/src/Ai/Base/Actions/PullActions.cpp @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include "AttackersValue.h" +#include "CreatureAI.h" +#include "Playerbots.h" +#include "PlayerbotTextMgr.h" +#include "PositionValue.h" +#include "PullActions.h" +#include "PullStrategy.h" +#include "RtiTargetValue.h" +#include + +namespace +{ +float GetPullReachDistance(Player* bot, Unit* target, PullStrategy const* strategy) +{ + if (!bot || !target || !strategy) + return 0.0f; + + float const combatDistance = bot->GetCombatReach() + target->GetCombatReach(); + return std::max(0.0f, strategy->GetRange() - combatDistance); +} + +bool IsWithinPullRange(Player* bot, Unit* target, PullStrategy const* strategy) +{ + return bot && target && strategy && bot->GetExactDist(target) <= strategy->GetRange(); +} +} + +bool PullRequestAction::Execute(Event event) +{ + PullStrategy* strategy = PullStrategy::Get(botAI); + if (!strategy) + return false; + + if (!botAI->IsTank(bot)) + return false; + + Unit* target = GetPullTarget(event); + if (!target || !target->IsInWorld()) + { + std::string const text = PlayerbotTextMgr::instance().GetBotTextOrDefault( + "pull_no_target_error", "You have no target", {}); + botAI->TellError(text); + return false; + } + + float const maxPullDistance = sPlayerbotAIConfig.reactDistance * 3.0f; + if (target->GetMapId() != bot->GetMapId() || bot->GetDistance(target) > maxPullDistance) + { + std::string const text = PlayerbotTextMgr::instance().GetBotTextOrDefault( + "pull_target_too_far_error", "The target is too far away", {}); + botAI->TellError(text); + return false; + } + + if (!AttackersValue::IsPossibleTarget(target, bot)) + { + std::string const text = PlayerbotTextMgr::instance().GetBotTextOrDefault( + "pull_invalid_target_error", "The target can't be pulled", {}); + botAI->TellError(text); + return false; + } + + if (!strategy->CanDoPullAction(target)) + { + std::string const actionName = strategy->GetPullActionName(); + std::string const text = PlayerbotTextMgr::instance().GetBotTextOrDefault( + "pull_action_unavailable_error", + "Can't perform pull action '%action_name'", + {{"%action_name", actionName}}); + botAI->TellError(text); + return false; + } + + PositionMap& posMap = AI_VALUE(PositionMap&, "position"); + PositionInfo pullPosition = posMap["pull"]; + pullPosition.Set(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), bot->GetMapId()); + posMap["pull"] = pullPosition; + + strategy->RequestPull(target); + context->GetValue("current target")->Set(target); + botAI->ChangeEngine(BOT_STATE_COMBAT); + botAI->SetNextCheckDelay(sPlayerbotAIConfig.reactDelay); + return true; +} + +Unit* PullMyTargetAction::GetPullTarget(Event event) +{ + Player* requester = event.getOwner() ? event.getOwner() : GetMaster(); + if (event.GetSource() == "attack anything") + return botAI->GetCreature(event.getObject()); + + return requester ? requester->GetSelectedUnit() : nullptr; +} + +Unit* PullRtiTargetAction::GetPullTarget(Event /*event*/) +{ + Unit* rtiTarget = AI_VALUE(Unit*, "rti target"); + if (rtiTarget) + return rtiTarget; + + Group* group = bot->GetGroup(); + if (!group) + return nullptr; + + std::string const rti = AI_VALUE(std::string, "rti"); + int32 const index = RtiTargetValue::GetRtiIndex(rti); + if (index < 0) + return nullptr; + + ObjectGuid const guid = group->GetTargetIcon(index); + return guid.IsEmpty() ? nullptr : botAI->GetUnit(guid); +} + +bool PullStartAction::Execute(Event event) +{ + PullStrategy* strategy = PullStrategy::Get(botAI); + if (!strategy) + return false; + + Unit* target = strategy->GetTarget(); + if (!target) + return false; + + std::string const preActionName = strategy->GetPreActionName(); + if (!preActionName.empty() && !botAI->DoSpecificAction(preActionName, event, true)) + return false; + + if (Pet* pet = bot->GetPet()) + { + Creature* creature = pet->ToCreature(); + if (creature) + { + strategy->SetPetReactState(creature->GetReactState()); + creature->SetReactState(REACT_PASSIVE); + } + } + + strategy->OnPullStarted(); + return true; +} + +PullAction::PullAction(PlayerbotAI* botAI, std::string const name) : CastSpellAction(botAI, name) { InitPullAction(); } + +Unit* PullAction::GetTarget() +{ + PullStrategy* strategy = PullStrategy::Get(botAI); + if (!strategy) + return nullptr; + + return strategy->GetTarget(); +} + +std::vector PullAction::getPrerequisites() +{ + PullStrategy* strategy = PullStrategy::Get(botAI); + Unit* target = strategy ? strategy->GetTarget() : nullptr; + if (!strategy || !target) + return {}; + + return IsWithinPullRange(bot, target, strategy) ? std::vector{} + : std::vector{ NextAction("reach pull", ACTION_MOVE) }; +} + +bool PullAction::Execute(Event event) +{ + InitPullAction(); + + PullStrategy* strategy = PullStrategy::Get(botAI); + if (!strategy) + return false; + + Unit* target = strategy->GetTarget(); + if (!target || !target->IsInWorld()) + return false; + + if (target->IsInCombat()) + return false; + + if (!IsWithinPullRange(bot, target, strategy)) + { + strategy->RequestPull(target, false); + return false; + } + + if (bot->isMoving()) + { + bot->StopMoving(); + strategy->RequestPull(target, false); + return false; + } + + context->GetValue("current target")->Set(target); + if (!botAI->DoSpecificAction(strategy->GetPullActionName(), event, true)) + return false; + + return true; +} + +bool PullAction::isPossible() +{ + InitPullAction(); + + PullStrategy* strategy = PullStrategy::Get(botAI); + if (!strategy) + return false; + + Unit* target = strategy->GetTarget(); + std::string const spellName = strategy->GetSpellName(); + if (!target || !target->IsInWorld() || target->GetMapId() != bot->GetMapId() || spellName.empty()) + return false; + + return true; +} + +void PullAction::InitPullAction() +{ + PullStrategy* strategy = PullStrategy::Get(botAI); + if (!strategy) + return; + + std::string const spellName = strategy->GetSpellName(); + if (spellName.empty()) + return; + + spell = spellName; + + bool isShoot = (spellName == "shoot" || spellName == "shoot bow" || + spellName == "shoot gun" || spellName == "shoot crossbow" || + spellName == "throw"); + range = botAI->GetRange(isShoot ? "shoot" : "spell"); +} + +bool PullEndAction::Execute(Event /*event*/) +{ + PullStrategy* strategy = PullStrategy::Get(botAI); + if (!strategy) + return false; + + Unit* pullTarget = strategy->GetTarget(); + + if (!strategy->HasPullStarted() && !strategy->IsPullPendingToStart() && !strategy->HasTarget()) + return false; + + if (Pet* pet = bot->GetPet()) + { + Creature* creature = pet->ToCreature(); + if (creature) + creature->SetReactState(strategy->GetPetReactState()); + } + + PositionMap& posMap = AI_VALUE(PositionMap&, "position"); + PositionInfo pullPosition = posMap["pull"]; + if (pullPosition.isSet()) + posMap.erase("pull"); + + if (pullTarget && context->GetValue("current target")->Get() == pullTarget) + context->GetValue("current target")->Set(nullptr); + + strategy->OnPullEnded(); + return true; +} + +bool ReturnToPullPositionAction::Execute(Event /*event*/) +{ + PositionInfo pullPosition = AI_VALUE(PositionMap&, "position")["pull"]; + if (!pullPosition.isSet() || pullPosition.mapId != bot->GetMapId()) + return false; + + return MoveTo(pullPosition.mapId, pullPosition.x, pullPosition.y, pullPosition.z, + false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true); +} + +bool ReturnToPullPositionAction::isUseful() +{ + PullStrategy* strategy = PullStrategy::Get(botAI); + Unit* target = strategy ? strategy->GetTarget() : nullptr; + if (!strategy || !target || !target->IsInCombat()) + return false; + + PositionInfo pullPosition = AI_VALUE(PositionMap&, "position")["pull"]; + return pullPosition.isSet() && pullPosition.mapId == bot->GetMapId() && + bot->GetDistance(pullPosition.x, pullPosition.y, pullPosition.z) > sPlayerbotAIConfig.followDistance; +} + +bool ReachPullAction::Execute(Event /*event*/) +{ + Unit* target = GetTarget(); + PullStrategy* strategy = PullStrategy::Get(botAI); + if (!target || !strategy) + return false; + + float const reachDistance = GetPullReachDistance(bot, target, strategy); + return ReachCombatTo(target, reachDistance); +} + +bool ReachPullAction::isUseful() +{ + if (botAI->HasStrategy("stay", botAI->GetState())) + return false; + + if (bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL) != nullptr) + return false; + + PullStrategy* strategy = PullStrategy::Get(botAI); + Unit* target = strategy ? strategy->GetTarget() : nullptr; + return target && !IsWithinPullRange(bot, target, strategy); +} + +Unit* ReachPullAction::GetTarget() +{ + PullStrategy* strategy = PullStrategy::Get(botAI); + if (!strategy) + return nullptr; + + return strategy->GetTarget(); +} diff --git a/src/Ai/Base/Actions/PullActions.h b/src/Ai/Base/Actions/PullActions.h new file mode 100644 index 000000000..299dd4a1e --- /dev/null +++ b/src/Ai/Base/Actions/PullActions.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_PULLACTIONS_H +#define _PLAYERBOT_PULLACTIONS_H + +#include "GenericSpellActions.h" +#include "ReachTargetActions.h" + +class PullRequestAction : public Action +{ +public: + PullRequestAction(PlayerbotAI* botAI, std::string const name) : Action(botAI, name) {} + + bool Execute(Event event) override; + +protected: + virtual Unit* GetPullTarget(Event event) = 0; +}; + +class PullMyTargetAction : public PullRequestAction +{ +public: + PullMyTargetAction(PlayerbotAI* botAI) : PullRequestAction(botAI, "pull my target") {} + +private: + Unit* GetPullTarget(Event event) override; +}; + +class PullRtiTargetAction : public PullRequestAction +{ +public: + PullRtiTargetAction(PlayerbotAI* botAI) : PullRequestAction(botAI, "pull rti target") {} + +private: + Unit* GetPullTarget(Event event) override; +}; + +class PullStartAction : public Action +{ +public: + PullStartAction(PlayerbotAI* botAI, std::string const name = "pull start") : Action(botAI, name) {} + + bool Execute(Event event) override; +}; + +class PullAction : public CastSpellAction +{ +public: + PullAction(PlayerbotAI* botAI, std::string const name = "pull action"); + + bool Execute(Event event) override; + bool isPossible() override; + std::vector getPrerequisites() override; + Unit* GetTarget() override; + +private: + void InitPullAction(); +}; + +class PullEndAction : public Action +{ +public: + PullEndAction(PlayerbotAI* botAI, std::string const name = "pull end") : Action(botAI, name) {} + + bool Execute(Event event) override; +}; + +class ReachPullAction : public ReachTargetAction +{ +public: + ReachPullAction(PlayerbotAI* botAI) : ReachTargetAction(botAI, "reach pull", botAI->GetRange("spell")) {} + + bool Execute(Event event) override; + bool isUseful() override; + Unit* GetTarget() override; +}; + +class ReturnToPullPositionAction : public MovementAction +{ +public: + ReturnToPullPositionAction(PlayerbotAI* botAI) : MovementAction(botAI, "return to pull position") {} + + bool Execute(Event event) override; + bool isUseful() override; +}; + +#endif diff --git a/src/Ai/Base/Actions/ReadyCheckAction.cpp b/src/Ai/Base/Actions/ReadyCheckAction.cpp index 1c510d69d..625deb8a4 100644 --- a/src/Ai/Base/Actions/ReadyCheckAction.cpp +++ b/src/Ai/Base/Actions/ReadyCheckAction.cpp @@ -45,7 +45,7 @@ std::once_flag ReadyChecker::initFlag; class HealthChecker : public ReadyChecker { public: - bool Check(PlayerbotAI* botAI, AiObjectContext* context) override + bool Check(PlayerbotAI* /*botAI*/, AiObjectContext* context) override { return AI_VALUE2(uint8, "health", "self target") > sPlayerbotAIConfig.almostFullHealth; } @@ -56,7 +56,7 @@ public: class ManaChecker : public ReadyChecker { public: - bool Check(PlayerbotAI* botAI, AiObjectContext* context) override + bool Check(PlayerbotAI* /*botAI*/, AiObjectContext* context) override { return !AI_VALUE2(bool, "has mana", "self target") || AI_VALUE2(uint8, "mana", "self target") > sPlayerbotAIConfig.mediumHealth; @@ -68,7 +68,7 @@ public: class DistanceChecker : public ReadyChecker { public: - bool Check(PlayerbotAI* botAI, AiObjectContext* context) override + bool Check(PlayerbotAI* botAI, AiObjectContext* /*context*/) override { Player* bot = botAI->GetBot(); if (Player* master = botAI->GetMaster()) @@ -90,7 +90,7 @@ public: class HunterChecker : public ReadyChecker { public: - bool Check(PlayerbotAI* botAI, AiObjectContext* context) override + bool Check(PlayerbotAI* botAI, AiObjectContext* /*context*/) override { Player* bot = botAI->GetBot(); if (bot->getClass() == CLASS_HUNTER) @@ -126,7 +126,7 @@ class ItemCountChecker : public ReadyChecker public: ItemCountChecker(std::string const item, std::string const name) : item(item), name(name) {} - bool Check(PlayerbotAI* botAI, AiObjectContext* context) override + bool Check(PlayerbotAI* /*botAI*/, AiObjectContext* context) override { return AI_VALUE2(uint32, "item count", item) > 0; } @@ -225,4 +225,4 @@ bool ReadyCheckAction::ReadyCheck() return true; } -bool FinishReadyCheckAction::Execute(Event event) { return ReadyCheck(); } +bool FinishReadyCheckAction::Execute(Event /*event*/) { return ReadyCheck(); } diff --git a/src/Ai/Base/Actions/ReleaseSpiritAction.cpp b/src/Ai/Base/Actions/ReleaseSpiritAction.cpp index 9a923d4fd..19bb870db 100644 --- a/src/Ai/Base/Actions/ReleaseSpiritAction.cpp +++ b/src/Ai/Base/Actions/ReleaseSpiritAction.cpp @@ -65,7 +65,7 @@ void ReleaseSpiritAction::IncrementDeathCount() const } } -void ReleaseSpiritAction::LogRelease(const std::string& releaseMsg, bool isAutoRelease) const +void ReleaseSpiritAction::LogRelease(const std::string& releaseMsg) const { const std::string teamPrefix = bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H"; @@ -82,13 +82,13 @@ bool AutoReleaseSpiritAction::Execute(Event /*event*/) { IncrementDeathCount(); bot->DurabilityRepairAll(false, 1.0f, false); - LogRelease("auto released", true); + LogRelease("auto released"); WorldPacket packet(CMSG_REPOP_REQUEST); packet << uint8(0); bot->GetSession()->HandleRepopRequestOpcode(packet); - LogRelease("releases spirit", true); + LogRelease("releases spirit"); if (bot->InBattleground()) { diff --git a/src/Ai/Base/Actions/ReleaseSpiritAction.h b/src/Ai/Base/Actions/ReleaseSpiritAction.h index 57851214a..af5be1da8 100644 --- a/src/Ai/Base/Actions/ReleaseSpiritAction.h +++ b/src/Ai/Base/Actions/ReleaseSpiritAction.h @@ -18,7 +18,7 @@ public: : Action(botAI, name) {} bool Execute(Event event) override; - void LogRelease(const std::string& releaseType, bool isAutoRelease = false) const; + void LogRelease(const std::string& releaseType) const; protected: void IncrementDeathCount() const; diff --git a/src/Ai/Base/Actions/ReviveFromCorpseAction.cpp b/src/Ai/Base/Actions/ReviveFromCorpseAction.cpp index 0051a5a49..2599fb1cf 100644 --- a/src/Ai/Base/Actions/ReviveFromCorpseAction.cpp +++ b/src/Ai/Base/Actions/ReviveFromCorpseAction.cpp @@ -251,9 +251,9 @@ GraveyardStruct const* SpiritHealerAction::GetGrave(bool startZone) std::vector races; if (bot->GetTeamId() == TEAM_ALLIANCE) - races = {RACE_HUMAN, RACE_DWARF, RACE_GNOME, RACE_NIGHTELF}; + races = {RACE_HUMAN, RACE_DWARF, RACE_GNOME, RACE_NIGHTELF, RACE_DRAENEI}; else - races = {RACE_ORC, RACE_TROLL, RACE_TAUREN, RACE_UNDEAD_PLAYER}; + races = {RACE_ORC, RACE_TROLL, RACE_TAUREN, RACE_UNDEAD_PLAYER, RACE_BLOODELF}; float graveDistance = -1; diff --git a/src/Ai/Base/Actions/SayAction.cpp b/src/Ai/Base/Actions/SayAction.cpp index 9580fe228..1e8af7605 100644 --- a/src/Ai/Base/Actions/SayAction.cpp +++ b/src/Ai/Base/Actions/SayAction.cpp @@ -154,7 +154,7 @@ bool SayAction::isUseful() return (time(nullptr) - lastSaid) > 30; } -void ChatReplyAction::ChatReplyDo(Player* bot, uint32& type, uint32& guid1, uint32& guid2, std::string& msg, std::string& chanName, std::string& name) +void ChatReplyAction::ChatReplyDo(Player* bot, uint32& type, uint32& guid1, std::string& msg, std::string& chanName, std::string& name) { std::string respondsText = ""; @@ -205,14 +205,14 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32& type, uint32& guid1, uint if (msg.starts_with(sPlayerbotAIConfig.toxicLinksPrefix) && (GET_PLAYERBOT_AI(bot)->GetChatHelper()->ExtractAllItemIds(msg).size() > 0 || GET_PLAYERBOT_AI(bot)->GetChatHelper()->ExtractAllQuestIds(msg).size() > 0)) { - HandleToxicLinksReply(bot, chatChannelSource, msg, name); + HandleToxicLinksReply(bot, chatChannelSource); return; } //thunderfury if (GET_PLAYERBOT_AI(bot)->GetChatHelper()->ExtractAllItemIds(msg).count(19019)) { - HandleThunderfuryReply(bot, chatChannelSource, msg, name); + HandleThunderfuryReply(bot, chatChannelSource); return; } @@ -220,7 +220,7 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32& type, uint32& guid1, uint SendGeneralResponse(bot, chatChannelSource, messageRepy, name); } -bool ChatReplyAction::HandleThunderfuryReply(Player* bot, ChatChannelSource chatChannelSource, std::string& msg, std::string& name) +bool ChatReplyAction::HandleThunderfuryReply(Player* bot, ChatChannelSource chatChannelSource) { std::map placeholders; const auto thunderfury = sObjectMgr->GetItemTemplate(19019); @@ -248,7 +248,7 @@ bool ChatReplyAction::HandleThunderfuryReply(Player* bot, ChatChannelSource chat return true; } -bool ChatReplyAction::HandleToxicLinksReply(Player* bot, ChatChannelSource chatChannelSource, std::string& msg, std::string& name) +bool ChatReplyAction::HandleToxicLinksReply(Player* bot, ChatChannelSource chatChannelSource) { //quests std::vector incompleteQuests; diff --git a/src/Ai/Base/Actions/SayAction.h b/src/Ai/Base/Actions/SayAction.h index 5bf9a8f04..cae5ee444 100644 --- a/src/Ai/Base/Actions/SayAction.h +++ b/src/Ai/Base/Actions/SayAction.h @@ -29,12 +29,12 @@ class ChatReplyAction : public Action { public: ChatReplyAction(PlayerbotAI* ai) : Action(ai, "chat message") {} - virtual bool Execute(Event event) { return true; } + virtual bool Execute(Event /*event*/) { return true; } bool isUseful() { return true; } - static void ChatReplyDo(Player* bot, uint32& type, uint32& guid1, uint32& guid2, std::string& msg, std::string& chanName, std::string& name); - static bool HandleThunderfuryReply(Player* bot, ChatChannelSource chatChannelSource, std::string& msg, std::string& name); - static bool HandleToxicLinksReply(Player* bot, ChatChannelSource chatChannelSource, std::string& msg, std::string& name); + static void ChatReplyDo(Player* bot, uint32& type, uint32& guid1, std::string& msg, std::string& chanName, std::string& name); + static bool HandleThunderfuryReply(Player* bot, ChatChannelSource chatChannelSource); + static bool HandleToxicLinksReply(Player* bot, ChatChannelSource chatChannelSource); static bool HandleWTBItemsReply(Player* bot, ChatChannelSource chatChannelSource, std::string& msg, std::string& name); static bool HandleLFGQuestsReply(Player* bot, ChatChannelSource chatChannelSource, std::string& msg, std::string& name); static bool SendGeneralResponse(Player* bot, ChatChannelSource chatChannelSource, std::string& responseMessage, std::string& name); diff --git a/src/Ai/Base/Actions/SeeSpellAction.cpp b/src/Ai/Base/Actions/SeeSpellAction.cpp index f7d7eab23..d93b56b51 100644 --- a/src/Ai/Base/Actions/SeeSpellAction.cpp +++ b/src/Ai/Base/Actions/SeeSpellAction.cpp @@ -15,7 +15,7 @@ std::set const FISHING_SPELLS = {7620, 7731, 7732, 18248, 33095, 51294}; -Creature* SeeSpellAction::CreateWps(Player* wpOwner, float x, float y, float z, float o, uint32 entry, Creature* lastWp, +Creature* SeeSpellAction::CreateWps(Player* wpOwner, float x, float y, float z, float o, uint32 entry, Creature* /*lastWp*/, bool important) { float dist = wpOwner->GetDistance(x, y, z); diff --git a/src/Ai/Base/Actions/UseMeetingStoneAction.cpp b/src/Ai/Base/Actions/UseMeetingStoneAction.cpp index 0862be68f..d6032acb1 100644 --- a/src/Ai/Base/Actions/UseMeetingStoneAction.cpp +++ b/src/Ai/Base/Actions/UseMeetingStoneAction.cpp @@ -61,7 +61,7 @@ bool SummonAction::Execute(Event /*event*/) if (!master) return false; - if (Pet* pet = bot->GetPet()) + if (bot->GetPet()) botAI->PetFollow(); if (master->GetSession()->GetSecurity() >= SEC_PLAYER) diff --git a/src/Ai/Base/ChatActionContext.h b/src/Ai/Base/ChatActionContext.h index 6f11fb33c..af51c23ae 100644 --- a/src/Ai/Base/ChatActionContext.h +++ b/src/Ai/Base/ChatActionContext.h @@ -43,6 +43,7 @@ #include "NewRpgAction.h" #include "PassLeadershipToMasterAction.h" #include "PositionAction.h" +#include "PullActions.h" #include "QueryItemUsageAction.h" #include "QueryQuestAction.h" #include "RangeAction.h" @@ -138,6 +139,8 @@ public: creators["autogear"] = &ChatActionContext::autogear; creators["equip upgrade"] = &ChatActionContext::equip_upgrade; creators["attack my target"] = &ChatActionContext::attack_my_target; + creators["pull my target"] = &ChatActionContext::pull_my_target; + creators["pull rti target"] = &ChatActionContext::pull_rti_target; creators["chat"] = &ChatActionContext::chat; creators["home"] = &ChatActionContext::home; creators["destroy"] = &ChatActionContext::destroy; @@ -250,6 +253,8 @@ private: static Action* home(PlayerbotAI* botAI) { return new SetHomeAction(botAI); } static Action* chat(PlayerbotAI* botAI) { return new ChangeChatAction(botAI); } static Action* attack_my_target(PlayerbotAI* botAI) { return new AttackMyTargetAction(botAI); } + static Action* pull_my_target(PlayerbotAI* botAI) { return new PullMyTargetAction(botAI); } + static Action* pull_rti_target(PlayerbotAI* botAI) { return new PullRtiTargetAction(botAI); } static Action* trainer(PlayerbotAI* botAI) { return new TrainerAction(botAI); } static Action* maintenance(PlayerbotAI* botAI) { return new MaintenanceAction(botAI); } static Action* remove_glyph(PlayerbotAI* botAI) { return new RemoveGlyphAction(botAI); } diff --git a/src/Ai/Base/ChatTriggerContext.h b/src/Ai/Base/ChatTriggerContext.h index 40316bd62..7742a9305 100644 --- a/src/Ai/Base/ChatTriggerContext.h +++ b/src/Ai/Base/ChatTriggerContext.h @@ -66,6 +66,9 @@ public: creators["autogear"] = &ChatTriggerContext::autogear; creators["equip upgrade"] = &ChatTriggerContext::equip_upgrade; creators["attack"] = &ChatTriggerContext::attack; + creators["pull"] = &ChatTriggerContext::pull; + creators["pull back"] = &ChatTriggerContext::pull_back; + creators["pull rti"] = &ChatTriggerContext::pull_rti; creators["chat"] = &ChatTriggerContext::chat; creators["accept"] = &ChatTriggerContext::accept; creators["home"] = &ChatTriggerContext::home; @@ -209,6 +212,9 @@ private: static Trigger* accept(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "accept"); } static Trigger* chat(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "chat"); } static Trigger* attack(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "attack"); } + static Trigger* pull(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "pull"); } + static Trigger* pull_back(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "pull back"); } + static Trigger* pull_rti(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "pull rti"); } static Trigger* trainer(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "trainer"); } static Trigger* maintenance(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "maintenance"); } static Trigger* remove_glyph(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "remove glyph"); } diff --git a/src/Ai/Base/Strategy/ChatCommandHandlerStrategy.cpp b/src/Ai/Base/Strategy/ChatCommandHandlerStrategy.cpp index 77a8d9d0b..8d5449ef3 100644 --- a/src/Ai/Base/Strategy/ChatCommandHandlerStrategy.cpp +++ b/src/Ai/Base/Strategy/ChatCommandHandlerStrategy.cpp @@ -11,7 +11,7 @@ public: ChatCommandActionNodeFactoryInternal() { creators["tank attack chat shortcut"] = &tank_attack_chat_shortcut; } private: - static ActionNode* tank_attack_chat_shortcut(PlayerbotAI* botAI) + static ActionNode* tank_attack_chat_shortcut(PlayerbotAI* /*botAI*/) { return new ActionNode("tank attack chat shortcut", /*P*/ {}, @@ -81,6 +81,12 @@ void ChatCommandHandlerStrategy::InitTriggers(std::vector& trigger new TriggerNode("attackers", { NextAction("tell attackers", relevance) })); triggers.push_back( new TriggerNode("target", { NextAction("tell target", relevance) })); + triggers.push_back( + new TriggerNode("pull", { NextAction("pull my target", relevance) })); + triggers.push_back( + new TriggerNode("pull back", { NextAction("pull my target", relevance) })); + triggers.push_back( + new TriggerNode("pull rti", { NextAction("pull rti target", relevance) })); triggers.push_back( new TriggerNode("ready", { NextAction("ready check", relevance) })); triggers.push_back( diff --git a/src/Ai/Base/Strategy/CombatStrategy.cpp b/src/Ai/Base/Strategy/CombatStrategy.cpp index 62972d4b1..f7e82102e 100644 --- a/src/Ai/Base/Strategy/CombatStrategy.cpp +++ b/src/Ai/Base/Strategy/CombatStrategy.cpp @@ -64,11 +64,11 @@ std::vector AvoidAoeStrategy::getDefaultActions() }; } -void AvoidAoeStrategy::InitTriggers(std::vector& triggers) +void AvoidAoeStrategy::InitTriggers(std::vector& /*triggers*/) { } -void AvoidAoeStrategy::InitMultipliers(std::vector& multipliers) +void AvoidAoeStrategy::InitMultipliers(std::vector& /*multipliers*/) { } @@ -81,7 +81,7 @@ std::vector TankFaceStrategy::getDefaultActions() }; } -void TankFaceStrategy::InitTriggers(std::vector& triggers) +void TankFaceStrategy::InitTriggers(std::vector& /*triggers*/) { } diff --git a/src/Ai/Base/Strategy/DuelStrategy.cpp b/src/Ai/Base/Strategy/DuelStrategy.cpp index c7f7e9d23..2a36a3cc6 100644 --- a/src/Ai/Base/Strategy/DuelStrategy.cpp +++ b/src/Ai/Base/Strategy/DuelStrategy.cpp @@ -17,6 +17,6 @@ void DuelStrategy::InitTriggers(std::vector& triggers) DuelStrategy::DuelStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI) {} -void StartDuelStrategy::InitTriggers(std::vector& triggers) {} +void StartDuelStrategy::InitTriggers(std::vector& /*triggers*/) {} StartDuelStrategy::StartDuelStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} diff --git a/src/Ai/Base/Strategy/FollowMasterStrategy.cpp b/src/Ai/Base/Strategy/FollowMasterStrategy.cpp index 6701fdcac..5e836cf62 100644 --- a/src/Ai/Base/Strategy/FollowMasterStrategy.cpp +++ b/src/Ai/Base/Strategy/FollowMasterStrategy.cpp @@ -12,6 +12,6 @@ std::vector FollowMasterStrategy::getDefaultActions() }; } -void FollowMasterStrategy::InitTriggers(std::vector& triggers) +void FollowMasterStrategy::InitTriggers(std::vector& /*triggers*/) { } diff --git a/src/Ai/Base/Strategy/GuardStrategy.cpp b/src/Ai/Base/Strategy/GuardStrategy.cpp index 96017365f..914cacf03 100644 --- a/src/Ai/Base/Strategy/GuardStrategy.cpp +++ b/src/Ai/Base/Strategy/GuardStrategy.cpp @@ -12,4 +12,4 @@ std::vector GuardStrategy::getDefaultActions() }; } -void GuardStrategy::InitTriggers(std::vector& triggers) {} +void GuardStrategy::InitTriggers(std::vector& /*triggers*/) {} diff --git a/src/Ai/Base/Strategy/MaintenanceStrategy.cpp b/src/Ai/Base/Strategy/MaintenanceStrategy.cpp index 855555edd..428d09a00 100644 --- a/src/Ai/Base/Strategy/MaintenanceStrategy.cpp +++ b/src/Ai/Base/Strategy/MaintenanceStrategy.cpp @@ -13,7 +13,7 @@ void MaintenanceStrategy::InitTriggers(std::vector& triggers) { triggers.push_back( new TriggerNode( - "random", + "seldom", { NextAction("clean quest log", 6.0f) } diff --git a/src/Ai/Base/Strategy/NonCombatStrategy.cpp b/src/Ai/Base/Strategy/NonCombatStrategy.cpp index cb32233f4..05163208a 100644 --- a/src/Ai/Base/Strategy/NonCombatStrategy.cpp +++ b/src/Ai/Base/Strategy/NonCombatStrategy.cpp @@ -17,7 +17,7 @@ void CollisionStrategy::InitTriggers(std::vector& triggers) new TriggerNode("collision", { NextAction("move out of collision", 2.0f) })); } -void MountStrategy::InitTriggers(std::vector& triggers) +void MountStrategy::InitTriggers(std::vector& /*triggers*/) { } diff --git a/src/Ai/Base/Strategy/PullStrategy.cpp b/src/Ai/Base/Strategy/PullStrategy.cpp index d0c7c9eac..1237f4aa8 100644 --- a/src/Ai/Base/Strategy/PullStrategy.cpp +++ b/src/Ai/Base/Strategy/PullStrategy.cpp @@ -5,8 +5,188 @@ #include "PullStrategy.h" +#include "AiObjectContext.h" #include "PassiveMultiplier.h" +#include "Player.h" +#include "PlayerbotAI.h" #include "Playerbots.h" +#include "SpellMgr.h" + +class PullStrategyActionNodeFactory : public NamedObjectFactory +{ +public: + PullStrategyActionNodeFactory() + { + creators["pull start"] = &pull_start; + } + +private: + static ActionNode* pull_start(PlayerbotAI* /*botAI*/) + { + return new ActionNode("pull start", {}, {}, { NextAction("pull action", ACTION_NORMAL) }); + } +}; + +PullStrategy::PullStrategy(PlayerbotAI* botAI, std::string const action, std::string const preAction) + : Strategy(botAI), action(action), preAction(preAction) +{ + actionNodeFactories.Add(new PullStrategyActionNodeFactory()); +} + +PullStrategy* PullStrategy::Get(PlayerbotAI* botAI) +{ + if (!botAI) + return nullptr; + + if (PullStrategy* strategy = dynamic_cast(botAI->GetStrategy("pull", BOT_STATE_NON_COMBAT))) + { + if (strategy->IsPullPendingToStart() || strategy->HasPullStarted() || strategy->HasTarget()) + return strategy; + } + + return dynamic_cast(botAI->GetStrategy("pull", BOT_STATE_COMBAT)); +} + +Unit* PullStrategy::GetTarget() const +{ + ObjectGuid const guid = botAI->GetAiObjectContext()->GetValue("pull strategy target")->Get(); + if (guid.IsEmpty()) + return nullptr; + + Unit* target = botAI->GetUnit(guid); + Player* bot = botAI->GetBot(); + if (!bot || !target || !target->IsAlive() || !target->IsInWorld() || + target->GetMapId() != bot->GetMapId()) + return nullptr; + + return target; +} + +bool PullStrategy::HasTarget() const { return GetTarget() != nullptr; } + +void PullStrategy::SetTarget(Unit* target) +{ + botAI->GetAiObjectContext()->GetValue("pull strategy target")->Set(target ? target->GetGUID() : ObjectGuid::Empty); +} + +std::string PullStrategy::GetPullActionName() const +{ + return action; +} + +std::string PullStrategy::GetSpellName() const +{ + Player* bot = botAI->GetBot(); + std::string spellName = GetPullActionName(); + if (!bot || spellName != "shoot") + return spellName; + + Item* equippedWeapon = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED); + if (!equippedWeapon) + return spellName; + + ItemTemplate const* itemTemplate = equippedWeapon->GetTemplate(); + if (!itemTemplate) + return spellName; + + switch (itemTemplate->SubClass) + { + case ITEM_SUBCLASS_WEAPON_THROWN: + return "throw"; + case ITEM_SUBCLASS_WEAPON_GUN: + return "shoot gun"; + case ITEM_SUBCLASS_WEAPON_BOW: + return "shoot bow"; + case ITEM_SUBCLASS_WEAPON_CROSSBOW: + return "shoot crossbow"; + default: + return spellName; + } +} + +float PullStrategy::GetRange() const +{ + Player* bot = botAI->GetBot(); + std::string const spellName = GetSpellName(); + if (bot && !spellName.empty()) + { + uint32 const spellId = botAI->GetAiObjectContext()->GetValue("spell id", spellName)->Get(); + if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId)) + return bot->GetSpellMaxRangeForTarget(GetTarget(), spellInfo) - CONTACT_DISTANCE; + } + + return (action == "shoot" ? botAI->GetRange("shoot") : botAI->GetRange("spell")) - CONTACT_DISTANCE; +} + +std::string PullStrategy::GetPreActionName() const +{ + return preAction; +} + +bool PullStrategy::CanDoPullAction(Unit* target) +{ + Player* bot = botAI->GetBot(); + if (!bot || !target) + return false; + + if (!target->IsInWorld() || target->GetMapId() != bot->GetMapId()) + return false; + + if (bot->getClass() != CLASS_DRUID && bot->getClass() != CLASS_PALADIN && + GetPullActionName() == "shoot" && !bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED)) + { + return false; + } + + std::string const spellName = GetSpellName(); + if (spellName.empty()) + return false; + + return true; +} + +void PullStrategy::RequestPull(Unit* target, bool resetTime) +{ + SetTarget(target); + pendingToStart = true; + if (resetTime) + pullStartTime = time(nullptr); +} + +void PullStrategy::OnPullStarted() { pendingToStart = false; } + +void PullStrategy::OnPullEnded() +{ + pullStartTime = 0; + pendingToStart = false; + SetTarget(nullptr); +} + +PullMultiplier::PullMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "pull") {} + +float PullMultiplier::GetValue(Action* action) +{ + PullStrategy const* strategy = PullStrategy::Get(botAI); + if (!strategy || !strategy->HasTarget() || !action) + return 1.0f; + + if (!strategy->IsPullPendingToStart() && !strategy->HasPullStarted()) + return 1.0f; + + std::string const actionName = action->getName(); + if (actionName == "pull my target" || + actionName == "pull rti target" || + actionName == "reach pull" || + actionName == "pull start" || + actionName == "pull action" || + actionName == "return to pull position" || + actionName == "pull end" || + actionName == "follow" || + actionName == "set facing") + return 1.0f; + + return 0.0f; +} class MagePullMultiplier : public PassiveMultiplier { @@ -24,8 +204,16 @@ float MagePullMultiplier::GetValue(Action* action) if (!action) return 1.0f; + PullStrategy const* strategy = PullStrategy::Get(botAI); + if (!strategy || !strategy->HasTarget()) + return 1.0f; + std::string const name = action->getName(); - if (actionName == name || name == "reach spell" || name == "change strategy") + if (actionName == name || name == "pull action" || name == "pull start" || name == "pull end" || + name == "pull my target" || name == "pull rti target" || + name == "reach spell" || name == "reach pull" || + name == "return to pull position" || name == "follow" || + name == "set facing" || name == "change strategy") return 1.0f; return PassiveMultiplier::GetValue(action); @@ -34,18 +222,32 @@ float MagePullMultiplier::GetValue(Action* action) std::vector PullStrategy::getDefaultActions() { return { - NextAction(action, 105.0f), - NextAction("follow", 104.0f), - NextAction("end pull", 103.0f), + NextAction("pull action", 105.0f), }; } -void PullStrategy::InitTriggers(std::vector& triggers) { CombatStrategy::InitTriggers(triggers); } +void PullStrategy::InitTriggers(std::vector& triggers) +{ + triggers.push_back(new TriggerNode( + "pull start", + { + NextAction("pull start", 106.0f), + NextAction("pull action", ACTION_MOVE) + } + )); + + triggers.push_back(new TriggerNode( + "pull end", + { + NextAction("pull end", 107.0f) + } + )); +} void PullStrategy::InitMultipliers(std::vector& multipliers) { + multipliers.push_back(new PullMultiplier(botAI)); multipliers.push_back(new MagePullMultiplier(botAI, action)); - CombatStrategy::InitMultipliers(multipliers); } void PossibleAddsStrategy::InitTriggers(std::vector& triggers) @@ -61,3 +263,15 @@ void PossibleAddsStrategy::InitTriggers(std::vector& triggers) ) ); } + +void PullBackStrategy::InitTriggers(std::vector& triggers) +{ + Strategy::InitTriggers(triggers); + + triggers.push_back(new TriggerNode( + "return to pull position", + { + NextAction("return to pull position", ACTION_MOVE + 5.0f) + } + )); +} diff --git a/src/Ai/Base/Strategy/PullStrategy.h b/src/Ai/Base/Strategy/PullStrategy.h index bdd7332f3..428699c56 100644 --- a/src/Ai/Base/Strategy/PullStrategy.h +++ b/src/Ai/Base/Strategy/PullStrategy.h @@ -6,22 +6,65 @@ #ifndef _PLAYERBOT_PULLSTRATEGY_H #define _PLAYERBOT_PULLSTRATEGY_H -#include "CombatStrategy.h" +#include "Strategy.h" + +class Action; +class Multiplier; +class Unit; class PlayerbotAI; -class PullStrategy : public CombatStrategy +class PullStrategy : public Strategy { public: - PullStrategy(PlayerbotAI* botAI, std::string const action) : CombatStrategy(botAI), action(action) {} + PullStrategy(PlayerbotAI* botAI, std::string const action, std::string const preAction = ""); void InitTriggers(std::vector& triggers) override; void InitMultipliers(std::vector& multipliers) override; std::string const getName() override { return "pull"; } std::vector getDefaultActions() override; + uint32 GetType() const override { return STRATEGY_TYPE_COMBAT | STRATEGY_TYPE_NONCOMBAT; } + + static PullStrategy* Get(PlayerbotAI* botAI); + static uint8 GetMaxPullTime() { return 15; } + + time_t GetPullStartTime() const { return pullStartTime; } + bool IsPullPendingToStart() const { return pendingToStart; } + bool HasPullStarted() const { return pullStartTime > 0; } + + bool CanDoPullAction(Unit* target); + Unit* GetTarget() const; + bool HasTarget() const; + + virtual std::string GetPullActionName() const; + std::string GetSpellName() const; + float GetRange() const; + virtual std::string GetPreActionName() const; + + void RequestPull(Unit* target, bool resetTime = true); + void OnPullStarted(); + void OnPullEnded(); + + ReactStates GetPetReactState() const { return petReactState; } + void SetPetReactState(ReactStates reactState) { petReactState = reactState; } + +private: + void SetTarget(Unit* target); private: std::string const action; + std::string const preAction; + bool pendingToStart = false; + time_t pullStartTime = 0; + ReactStates petReactState = REACT_DEFENSIVE; +}; + +class PullMultiplier : public Multiplier +{ +public: + PullMultiplier(PlayerbotAI* botAI); + + float GetValue(Action* action) override; }; class PossibleAddsStrategy : public Strategy @@ -33,4 +76,13 @@ public: std::string const getName() override { return "adds"; } }; +class PullBackStrategy : public Strategy +{ +public: + PullBackStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} + + void InitTriggers(std::vector& triggers) override; + std::string const getName() override { return "pull back"; } +}; + #endif diff --git a/src/Ai/Base/Strategy/RTSCStrategy.cpp b/src/Ai/Base/Strategy/RTSCStrategy.cpp index 525338c15..75d07706b 100644 --- a/src/Ai/Base/Strategy/RTSCStrategy.cpp +++ b/src/Ai/Base/Strategy/RTSCStrategy.cpp @@ -7,4 +7,4 @@ RTSCStrategy::RTSCStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} -void RTSCStrategy::InitTriggers(std::vector& triggers) {} +void RTSCStrategy::InitTriggers(std::vector& /*triggers*/) {} diff --git a/src/Ai/Base/Strategy/RacialsStrategy.cpp b/src/Ai/Base/Strategy/RacialsStrategy.cpp index ae45cdaaf..b5a84bbce 100644 --- a/src/Ai/Base/Strategy/RacialsStrategy.cpp +++ b/src/Ai/Base/Strategy/RacialsStrategy.cpp @@ -11,7 +11,7 @@ public: RacialsStrategyActionNodeFactory() { creators["lifeblood"] = &lifeblood; } private: - static ActionNode* lifeblood(PlayerbotAI* botAI) + static ActionNode* lifeblood(PlayerbotAI* /*botAI*/) { return new ActionNode("lifeblood", /*P*/ {}, diff --git a/src/Ai/Base/Strategy/UsePotionsStrategy.cpp b/src/Ai/Base/Strategy/UsePotionsStrategy.cpp index 55827fbd2..27f690cf1 100644 --- a/src/Ai/Base/Strategy/UsePotionsStrategy.cpp +++ b/src/Ai/Base/Strategy/UsePotionsStrategy.cpp @@ -11,7 +11,7 @@ public: UsePotionsStrategyActionNodeFactory() { creators["healthstone"] = &healthstone; } private: - static ActionNode* healthstone(PlayerbotAI* botAI) + static ActionNode* healthstone(PlayerbotAI* /*botAI*/) { return new ActionNode("healthstone", /*P*/ {}, diff --git a/src/Ai/Base/Strategy/WaitForAttackStrategy.cpp b/src/Ai/Base/Strategy/WaitForAttackStrategy.cpp index a38140512..21950f00c 100644 --- a/src/Ai/Base/Strategy/WaitForAttackStrategy.cpp +++ b/src/Ai/Base/Strategy/WaitForAttackStrategy.cpp @@ -82,6 +82,7 @@ float WaitForAttackMultiplier::GetValue(Action* action) actionName != "set facing" && actionName != "pull my target" && actionName != "pull rti target" && + actionName != "reach pull" && actionName != "pull start" && actionName != "pull action" && actionName != "pull end") diff --git a/src/Ai/Base/StrategyContext.h b/src/Ai/Base/StrategyContext.h index 5386e872b..8dab9c40d 100644 --- a/src/Ai/Base/StrategyContext.h +++ b/src/Ai/Base/StrategyContext.h @@ -95,6 +95,7 @@ public: creators["sit"] = &StrategyContext::sit; creators["mark rti"] = &StrategyContext::mark_rti; creators["adds"] = &StrategyContext::possible_adds; + creators["pull back"] = &StrategyContext::pull_back; creators["close"] = &StrategyContext::close; creators["ranged"] = &StrategyContext::ranged; creators["behind"] = &StrategyContext::behind; @@ -171,6 +172,7 @@ private: static Strategy* map_full(PlayerbotAI* botAI) { return new MapFullStrategy(botAI); } static Strategy* sit(PlayerbotAI* botAI) { return new SitStrategy(botAI); } static Strategy* possible_adds(PlayerbotAI* botAI) { return new PossibleAddsStrategy(botAI); } + static Strategy* pull_back(PlayerbotAI* botAI) { return new PullBackStrategy(botAI); } static Strategy* mount(PlayerbotAI* botAI) { return new MountStrategy(botAI); } static Strategy* bg(PlayerbotAI* botAI) { return new BGStrategy(botAI); } static Strategy* battleground(PlayerbotAI* botAI) { return new BattlegroundStrategy(botAI); } diff --git a/src/Ai/Base/Trigger/PullTriggers.cpp b/src/Ai/Base/Trigger/PullTriggers.cpp new file mode 100644 index 000000000..4d23b3896 --- /dev/null +++ b/src/Ai/Base/Trigger/PullTriggers.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include "PullTriggers.h" + +#include "PositionValue.h" +#include "Player.h" +#include "PlayerbotAI.h" +#include "Playerbots.h" +#include "PullStrategy.h" + +bool PullStartTrigger::IsActive() +{ + PullStrategy const* strategy = PullStrategy::Get(botAI); + return strategy && strategy->IsPullPendingToStart(); +} + +bool PullEndTrigger::IsActive() +{ + PullStrategy const* strategy = PullStrategy::Get(botAI); + + if (!strategy || !strategy->HasPullStarted()) + return false; + + Unit* target = strategy->GetTarget(); + if (!target || !target->IsInWorld() || !target->IsAlive()) + return true; + + time_t const secondsSincePullStarted = time(nullptr) - strategy->GetPullStartTime(); + if (secondsSincePullStarted >= PullStrategy::GetMaxPullTime()) + return true; + + float distanceToPullTarget = bot->GetDistance(target); + if (distanceToPullTarget > ATTACK_DISTANCE && !target->IsNonMeleeSpellCast(false, false, true) && + (!botAI->IsRanged(bot) || distanceToPullTarget > botAI->GetRange("spell"))) + return false; + + if (!botAI->HasStrategy("pull back", BOT_STATE_COMBAT)) + return true; + + PositionInfo pullPosition = AI_VALUE(PositionMap&, "position")["pull"]; + if (!pullPosition.isSet() || pullPosition.mapId != bot->GetMapId()) + return true; + + return bot->GetDistance(pullPosition.x, pullPosition.y, pullPosition.z) <= botAI->GetRange("follow"); +} + +bool ReturnToPullPositionTrigger::IsActive() +{ + PullStrategy const* strategy = PullStrategy::Get(botAI); + + Unit* target = strategy ? strategy->GetTarget() : nullptr; + if (!strategy || !strategy->HasPullStarted() || !target || !target->IsInCombat() || + !botAI->HasStrategy("pull back", BOT_STATE_COMBAT)) + return false; + + PositionInfo pullPosition = AI_VALUE(PositionMap&, "position")["pull"]; + return pullPosition.isSet() && pullPosition.mapId == bot->GetMapId() && + bot->GetDistance(pullPosition.x, pullPosition.y, pullPosition.z) > sPlayerbotAIConfig.followDistance; +} diff --git a/src/Ai/Base/Trigger/PullTriggers.h b/src/Ai/Base/Trigger/PullTriggers.h new file mode 100644 index 000000000..d56036177 --- /dev/null +++ b/src/Ai/Base/Trigger/PullTriggers.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_PULLTRIGGERS_H +#define _PLAYERBOT_PULLTRIGGERS_H + +#include "Trigger.h" + +class PullStartTrigger : public Trigger +{ +public: + PullStartTrigger(PlayerbotAI* botAI, std::string const name = "pull start") : Trigger(botAI, name) {} + + bool IsActive() override; +}; + +class PullEndTrigger : public Trigger +{ +public: + PullEndTrigger(PlayerbotAI* botAI, std::string const name = "pull end") : Trigger(botAI, name) {} + + bool IsActive() override; +}; + +class ReturnToPullPositionTrigger : public Trigger +{ +public: + ReturnToPullPositionTrigger(PlayerbotAI* botAI) : Trigger(botAI, "return to pull position") {} + + bool IsActive() override; +}; + +#endif diff --git a/src/Ai/Base/TriggerContext.h b/src/Ai/Base/TriggerContext.h index bfdddecf7..54edbb017 100644 --- a/src/Ai/Base/TriggerContext.h +++ b/src/Ai/Base/TriggerContext.h @@ -16,6 +16,7 @@ #include "NewRpgStrategy.h" #include "NewRpgTriggers.h" #include "PvpTriggers.h" +#include "PullTriggers.h" #include "RpgTriggers.h" #include "RtiTriggers.h" #include "StuckTriggers.h" @@ -129,6 +130,9 @@ public: creators["has attackers"] = &TriggerContext::has_attackers; creators["no possible targets"] = &TriggerContext::no_possible_targets; creators["possible adds"] = &TriggerContext::possible_adds; + creators["pull start"] = &TriggerContext::pull_start; + creators["pull end"] = &TriggerContext::pull_end; + creators["return to pull position"] = &TriggerContext::return_to_pull_position; creators["no drink"] = &TriggerContext::no_drink; creators["no food"] = &TriggerContext::no_food; @@ -280,6 +284,9 @@ private: static Trigger* swimming(PlayerbotAI* botAI) { return new IsSwimmingTrigger(botAI); } static Trigger* no_possible_targets(PlayerbotAI* botAI) { return new NoPossibleTargetsTrigger(botAI); } static Trigger* possible_adds(PlayerbotAI* botAI) { return new PossibleAddsTrigger(botAI); } + static Trigger* pull_start(PlayerbotAI* botAI) { return new PullStartTrigger(botAI); } + static Trigger* pull_end(PlayerbotAI* botAI) { return new PullEndTrigger(botAI); } + static Trigger* return_to_pull_position(PlayerbotAI* botAI) { return new ReturnToPullPositionTrigger(botAI); } static Trigger* can_loot(PlayerbotAI* botAI) { return new CanLootTrigger(botAI); } static Trigger* far_from_loot_target(PlayerbotAI* botAI) { return new FarFromCurrentLootTrigger(botAI); } static Trigger* far_from_master(PlayerbotAI* botAI) { return new FarFromMasterTrigger(botAI); } diff --git a/src/Ai/Base/Value/Arrow.cpp b/src/Ai/Base/Value/Arrow.cpp index c993275f5..1a4c4ff88 100644 --- a/src/Ai/Base/Value/Arrow.cpp +++ b/src/Ai/Base/Value/Arrow.cpp @@ -19,6 +19,7 @@ WorldLocation ArrowFormation::GetLocationInternal() uint32 tankLines = 1 + tanks.Size() / 6; uint32 meleeLines = 1 + melee.Size() / 6; uint32 rangedLines = 1 + ranged.Size() / 6; + //TODO Implement Healer Lines uint32 healerLines = 1 + healers.Size() / 6; float offset = 0.f; @@ -147,7 +148,7 @@ UnitPosition MultiLineUnitPlacer::Place(FormationUnit* unit, uint32 index, uint3 return placer.Place(unit, indexInLine, lineSize); } -UnitPosition SingleLineUnitPlacer::Place(FormationUnit* unit, uint32 index, uint32 count) +UnitPosition SingleLineUnitPlacer::Place(FormationUnit* /*unit*/, uint32 index, uint32 count) { float angle = orientation - M_PI / 2.0f; float x = cos(angle) * sPlayerbotAIConfig.followDistance * ((float)index - (float)count / 2); diff --git a/src/Ai/Base/Value/CcTargetValue.cpp b/src/Ai/Base/Value/CcTargetValue.cpp index a8de7a10e..4903a59f3 100644 --- a/src/Ai/Base/Value/CcTargetValue.cpp +++ b/src/Ai/Base/Value/CcTargetValue.cpp @@ -20,7 +20,7 @@ public: } public: - void CheckAttacker(Unit* creature, ThreatManager* threatMgr) override + void CheckAttacker(Unit* creature, ThreatManager* /*threatMgr*/) override { Player* bot = botAI->GetBot(); if (!botAI->CanCastSpell(spell, creature)) diff --git a/src/Ai/Base/Value/CurrentCcTargetValue.cpp b/src/Ai/Base/Value/CurrentCcTargetValue.cpp index b095c09d7..39fb7edd2 100644 --- a/src/Ai/Base/Value/CurrentCcTargetValue.cpp +++ b/src/Ai/Base/Value/CurrentCcTargetValue.cpp @@ -13,7 +13,7 @@ public: { } - void CheckAttacker(Unit* attacker, ThreatManager* threatMgr) override + void CheckAttacker(Unit* attacker, ThreatManager* /*threatMgr*/) override { if (botAI->HasAura(spell, attacker)) result = attacker; diff --git a/src/Ai/Base/Value/DpsTargetValue.cpp b/src/Ai/Base/Value/DpsTargetValue.cpp index 010e47191..7099a13fe 100644 --- a/src/Ai/Base/Value/DpsTargetValue.cpp +++ b/src/Ai/Base/Value/DpsTargetValue.cpp @@ -50,7 +50,7 @@ public: result = nullptr; } - void CheckAttacker(Unit* attacker, ThreatManager* threatMgr) override + void CheckAttacker(Unit* attacker, ThreatManager* /*threatMgr*/) override { if (Group* group = botAI->GetBot()->GetGroup()) { diff --git a/src/Ai/Base/Value/ItemCountValue.cpp b/src/Ai/Base/Value/ItemCountValue.cpp index 9ea7da2e0..46ebc1dfa 100644 --- a/src/Ai/Base/Value/ItemCountValue.cpp +++ b/src/Ai/Base/Value/ItemCountValue.cpp @@ -11,8 +11,6 @@ std::vector InventoryItemValueBase::Find(std::string const qualifier) { std::vector result; - Player* bot = InventoryAction::botAI->GetBot(); - std::vector items = InventoryAction::parseItems(qualifier); for (Item* item : items) result.push_back(item); diff --git a/src/Ai/Base/Value/ItemCountValue.h b/src/Ai/Base/Value/ItemCountValue.h index 6f7c593b2..7e47565fc 100644 --- a/src/Ai/Base/Value/ItemCountValue.h +++ b/src/Ai/Base/Value/ItemCountValue.h @@ -17,7 +17,7 @@ class InventoryItemValueBase : public InventoryAction public: InventoryItemValueBase(PlayerbotAI* botAI) : InventoryAction(botAI, "empty") {} - bool Execute(Event event) override { return false; } + bool Execute(Event /*event*/) override { return false; } protected: std::vector Find(std::string const qualifier); diff --git a/src/Ai/Base/Value/ItemForSpellValue.cpp b/src/Ai/Base/Value/ItemForSpellValue.cpp index e14549da4..2cefe9062 100644 --- a/src/Ai/Base/Value/ItemForSpellValue.cpp +++ b/src/Ai/Base/Value/ItemForSpellValue.cpp @@ -48,23 +48,6 @@ Item* ItemForSpellValue::Calculate() } } - // Workaround as some spells have no item mask (e.g. shaman weapon enhancements) - if (!strcmpi(spellInfo->SpellName[0], "rockbiter weapon") || - !strcmpi(spellInfo->SpellName[0], "flametongue weapon") || - !strcmpi(spellInfo->SpellName[0], "earthliving weapon") || - !strcmpi(spellInfo->SpellName[0], "frostbrand weapon") || !strcmpi(spellInfo->SpellName[0], "windfury weapon")) - { - itemForSpell = GetItemFitsToSpellRequirements(EQUIPMENT_SLOT_MAINHAND, spellInfo); - if (itemForSpell && itemForSpell->GetTemplate()->Class == ITEM_CLASS_WEAPON) - return itemForSpell; - - itemForSpell = GetItemFitsToSpellRequirements(EQUIPMENT_SLOT_OFFHAND, spellInfo); - if (itemForSpell && itemForSpell->GetTemplate()->Class == ITEM_CLASS_WEAPON) - return itemForSpell; - - return nullptr; - } - if (!(spellInfo->Targets & TARGET_FLAG_ITEM)) return nullptr; diff --git a/src/Ai/Base/Value/ItemUsageValue.cpp b/src/Ai/Base/Value/ItemUsageValue.cpp index b651af956..c3d976f0f 100644 --- a/src/Ai/Base/Value/ItemUsageValue.cpp +++ b/src/Ai/Base/Value/ItemUsageValue.cpp @@ -234,6 +234,11 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto, calculator.SetItemSetBonus(false); calculator.SetOverflowPenalty(false); + // Apply PvP weights if the bot is specced for PvP + bool isPvp = sRandomPlayerbotMgr.IsSpecPvp(bot->GetGUID().GetCounter(), bot->getClass()); + if (isPvp) + calculator.SetPvpSpec(true); + float itemScore = calculator.CalculateItem(itemProto->ItemId, randomPropertyId); if (itemScore) @@ -864,8 +869,6 @@ bool ItemUsageValue::SpellGivesSkillUp(uint32 spellId, Player* bot) { uint32 SkillValue = bot->GetPureSkillValue(skill->SkillLine); - uint32 craft_skill_gain = sWorld->getIntConfig(CONFIG_SKILL_GAIN_CRAFTING); - if (SkillGainChance(SkillValue, skill->TrivialSkillLineRankHigh, (skill->TrivialSkillLineRankHigh + skill->TrivialSkillLineRankLow) / 2, skill->TrivialSkillLineRankLow) > 0) diff --git a/src/Ai/Base/Value/LeastHpTargetValue.cpp b/src/Ai/Base/Value/LeastHpTargetValue.cpp index c185628fa..d8579f0dd 100644 --- a/src/Ai/Base/Value/LeastHpTargetValue.cpp +++ b/src/Ai/Base/Value/LeastHpTargetValue.cpp @@ -13,7 +13,7 @@ class FindLeastHpTargetStrategy : public FindNonCcTargetStrategy public: FindLeastHpTargetStrategy(PlayerbotAI* botAI) : FindNonCcTargetStrategy(botAI), minHealth(0) {} - void CheckAttacker(Unit* attacker, ThreatManager* threatMgr) override + void CheckAttacker(Unit* attacker, ThreatManager* /*threatMgr*/) override { if (IsCcTarget(attacker)) return; diff --git a/src/Ai/Base/Value/LootStrategyValue.cpp b/src/Ai/Base/Value/LootStrategyValue.cpp index 6a4f9b9b4..3b0b81a19 100644 --- a/src/Ai/Base/Value/LootStrategyValue.cpp +++ b/src/Ai/Base/Value/LootStrategyValue.cpp @@ -60,7 +60,7 @@ public: class AllLootStrategy : public LootStrategy { public: - bool CanLoot(ItemTemplate const* proto, AiObjectContext* context) override { return true; } + bool CanLoot(ItemTemplate const* /*proto*/, AiObjectContext* /*context*/) override { return true; } std::string const GetName() override { return "all"; } }; diff --git a/src/Ai/Base/Value/NearestCorpsesValue.cpp b/src/Ai/Base/Value/NearestCorpsesValue.cpp index 6091c3241..99b42662c 100644 --- a/src/Ai/Base/Value/NearestCorpsesValue.cpp +++ b/src/Ai/Base/Value/NearestCorpsesValue.cpp @@ -28,4 +28,4 @@ void NearestCorpsesValue::FindUnits(std::list& targets) Cell::VisitObjects(bot, searcher, range); } -bool NearestCorpsesValue::AcceptUnit(Unit* unit) { return true; } +bool NearestCorpsesValue::AcceptUnit(Unit* /*unit*/) { return true; } diff --git a/src/Ai/Base/Value/PartyMemberValue.cpp b/src/Ai/Base/Value/PartyMemberValue.cpp index 53a93b2b4..b4f44fdf4 100644 --- a/src/Ai/Base/Value/PartyMemberValue.cpp +++ b/src/Ai/Base/Value/PartyMemberValue.cpp @@ -27,7 +27,7 @@ Unit* PartyMemberValue::FindPartyMember(std::vector* party, FindPlayerP return nullptr; } -Unit* PartyMemberValue::FindPartyMember(FindPlayerPredicate& predicate, bool ignoreOutOfGroup) +Unit* PartyMemberValue::FindPartyMember(FindPlayerPredicate& predicate, bool /*ignoreOutOfGroup*/) { Player* master = GetMaster(); // GuidVector nearestPlayers; diff --git a/src/Ai/Base/Value/SpellCastUsefulValue.cpp b/src/Ai/Base/Value/SpellCastUsefulValue.cpp index 9fa85b3a1..6841335d5 100644 --- a/src/Ai/Base/Value/SpellCastUsefulValue.cpp +++ b/src/Ai/Base/Value/SpellCastUsefulValue.cpp @@ -40,13 +40,13 @@ bool SpellCastUsefulValue::Calculate() return false; } - // TODO: workaround - if (qualifier == "windfury weapon" || qualifier == "flametongue weapon" || qualifier == "frostbrand weapon" || - qualifier == "rockbiter weapon" || qualifier == "earthliving weapon" || qualifier == "spellstone") + if (qualifier == "windfury weapon" || qualifier == "flametongue weapon" || + qualifier == "frostbrand weapon" || qualifier == "rockbiter weapon" || + qualifier == "earthliving weapon" || qualifier == "spellstone") { - if (Item* item = AI_VALUE2(Item*, "item for spell", spellid)) - if (item->IsInWorld() && item->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT)) - return false; + if (Item* item = AI_VALUE2(Item*, "item for spell", spellid); + item && item->IsInWorld() && item->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT)) + return false; } std::set& skipSpells = AI_VALUE(std::set&, "skip spells list"); diff --git a/src/Ai/Base/Value/TankTargetValue.cpp b/src/Ai/Base/Value/TankTargetValue.cpp index 80def1cf9..74a4046a3 100644 --- a/src/Ai/Base/Value/TankTargetValue.cpp +++ b/src/Ai/Base/Value/TankTargetValue.cpp @@ -49,7 +49,7 @@ class FindTankTargetSmartStrategy : public FindTargetStrategy public: FindTankTargetSmartStrategy(PlayerbotAI* botAI) : FindTargetStrategy(botAI) {} - void CheckAttacker(Unit* attacker, ThreatManager* threatMgr) override + void CheckAttacker(Unit* attacker, ThreatManager* /*threatMgr*/) override { if (Group* group = botAI->GetBot()->GetGroup()) { diff --git a/src/Ai/Base/Value/TargetValue.cpp b/src/Ai/Base/Value/TargetValue.cpp index 19578daf4..9b8fde5bd 100644 --- a/src/Ai/Base/Value/TargetValue.cpp +++ b/src/Ai/Base/Value/TargetValue.cpp @@ -161,7 +161,7 @@ Unit* FindTargetValue::Calculate() return nullptr; } -void FindBossTargetStrategy::CheckAttacker(Unit* attacker, ThreatManager* threatManager) +void FindBossTargetStrategy::CheckAttacker(Unit* attacker, ThreatManager* /*threatManager*/) { UnitAI* unitAI = attacker->GetAI(); BossAI* bossAI = dynamic_cast(unitAI); diff --git a/src/Ai/Base/Value/TargetValue.h b/src/Ai/Base/Value/TargetValue.h index 7d766578a..94fcbdf8a 100644 --- a/src/Ai/Base/Value/TargetValue.h +++ b/src/Ai/Base/Value/TargetValue.h @@ -116,6 +116,15 @@ public: } }; +class PullStrategyTargetValue : public ManualSetValue +{ +public: + PullStrategyTargetValue(PlayerbotAI* botAI, std::string const name = "pull strategy target") + : ManualSetValue(botAI, ObjectGuid::Empty, name) + { + } +}; + class FindTargetValue : public UnitCalculatedValue, public Qualified { public: diff --git a/src/Ai/Base/ValueContext.h b/src/Ai/Base/ValueContext.h index 77d25e060..bac5fd835 100644 --- a/src/Ai/Base/ValueContext.h +++ b/src/Ai/Base/ValueContext.h @@ -241,6 +241,7 @@ public: creators["travel target"] = &ValueContext::travel_target; creators["talk target"] = &ValueContext::talk_target; creators["pull target"] = &ValueContext::pull_target; + creators["pull strategy target"] = &ValueContext::pull_strategy_target; creators["focus heal targets"] = &ValueContext::focus_heal_targets; creators["group"] = &ValueContext::group; creators["range"] = &ValueContext::range; @@ -498,6 +499,7 @@ private: static UntypedValue* next_rpg_action(PlayerbotAI* botAI) { return new NextRpgActionValue(botAI); } static UntypedValue* travel_target(PlayerbotAI* botAI) { return new TravelTargetValue(botAI); } static UntypedValue* pull_target(PlayerbotAI* botAI) { return new PullTargetValue(botAI); } + static UntypedValue* pull_strategy_target(PlayerbotAI* botAI) { return new PullStrategyTargetValue(botAI); } static UntypedValue* focus_heal_targets(PlayerbotAI* botAI) { return new FocusHealTargetValue(botAI); } static UntypedValue* bg_master(PlayerbotAI* botAI) { return new BgMasterValue(botAI); } diff --git a/src/Ai/Class/Dk/DKAiObjectContext.cpp b/src/Ai/Class/Dk/DKAiObjectContext.cpp index 9a8271aa7..85f8bc8cd 100644 --- a/src/Ai/Class/Dk/DKAiObjectContext.cpp +++ b/src/Ai/Class/Dk/DKAiObjectContext.cpp @@ -8,11 +8,11 @@ #include "BloodDKStrategy.h" #include "DKActions.h" #include "DKTriggers.h" +#include "DeathKnightPullStrategy.h" #include "FrostDKStrategy.h" #include "GenericDKNonCombatStrategy.h" #include "GenericTriggers.h" #include "Playerbots.h" -#include "PullStrategy.h" #include "UnholyDKStrategy.h" class DeathKnightStrategyFactoryInternal : public NamedObjectContext @@ -28,7 +28,7 @@ public: private: static Strategy* nc(PlayerbotAI* botAI) { return new GenericDKNonCombatStrategy(botAI); } - static Strategy* pull(PlayerbotAI* botAI) { return new PullStrategy(botAI, "icy touch"); } + static Strategy* pull(PlayerbotAI* botAI) { return new DeathKnightPullStrategy(botAI); } static Strategy* frost_aoe(PlayerbotAI* botAI) { return new FrostDKAoeStrategy(botAI); } static Strategy* unholy_aoe(PlayerbotAI* botAI) { return new UnholyDKAoeStrategy(botAI); } }; diff --git a/src/Ai/Class/Dk/Strategy/DeathKnightPullStrategy.cpp b/src/Ai/Class/Dk/Strategy/DeathKnightPullStrategy.cpp new file mode 100644 index 000000000..be643b50c --- /dev/null +++ b/src/Ai/Class/Dk/Strategy/DeathKnightPullStrategy.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include "DeathKnightPullStrategy.h" + +#include "AiObjectContext.h" +#include "Player.h" +#include "PlayerbotAI.h" +#include "Playerbots.h" + +std::string DeathKnightPullStrategy::GetPullActionName() const +{ + Player* bot = botAI->GetBot(); + Unit* target = GetTarget(); + if (!bot || !target || + (!botAI->HasStrategy("blood", BOT_STATE_COMBAT) && !botAI->HasStrategy("blood", BOT_STATE_NON_COMBAT))) + { + return PullStrategy::GetPullActionName(); + } + + uint32 const deathGripSpellId = botAI->GetAiObjectContext()->GetValue("spell id", "death grip")->Get(); + if (deathGripSpellId && bot->HasSpell(deathGripSpellId) && + botAI->CanCastSpell(deathGripSpellId, target)) + { + return "death grip"; + } + + uint32 const icyTouchSpellId = botAI->GetAiObjectContext()->GetValue("spell id", "icy touch")->Get(); + if (!icyTouchSpellId || !bot->HasSpell(icyTouchSpellId) || + !botAI->CanCastSpell(icyTouchSpellId, target)) + { + uint32 const darkCommandSpellId = botAI->GetAiObjectContext()->GetValue("spell id", "dark command")->Get(); + if (darkCommandSpellId && bot->HasSpell(darkCommandSpellId) && + botAI->CanCastSpell(darkCommandSpellId, target)) + { + return "dark command"; + } + } + + return PullStrategy::GetPullActionName(); +} diff --git a/src/Ai/Class/Dk/Strategy/DeathKnightPullStrategy.h b/src/Ai/Class/Dk/Strategy/DeathKnightPullStrategy.h new file mode 100644 index 000000000..ce80c69f6 --- /dev/null +++ b/src/Ai/Class/Dk/Strategy/DeathKnightPullStrategy.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_DEATH_KNIGHT_PULL_STRATEGY_H +#define _PLAYERBOT_DEATH_KNIGHT_PULL_STRATEGY_H + +#include "PullStrategy.h" + +class DeathKnightPullStrategy : public PullStrategy +{ +public: + DeathKnightPullStrategy(PlayerbotAI* botAI) : PullStrategy(botAI, "icy touch") {} + + std::string GetPullActionName() const override; +}; + +#endif diff --git a/src/Ai/Class/Dk/Strategy/GenericDKNonCombatStrategy.cpp b/src/Ai/Class/Dk/Strategy/GenericDKNonCombatStrategy.cpp index 0d3a43b79..28179d74e 100644 --- a/src/Ai/Class/Dk/Strategy/GenericDKNonCombatStrategy.cpp +++ b/src/Ai/Class/Dk/Strategy/GenericDKNonCombatStrategy.cpp @@ -47,7 +47,7 @@ void GenericDKNonCombatStrategy::InitTriggers(std::vector& trigger new TriggerNode("bone shield", { NextAction("bone shield", 21.0f) })); } -void DKBuffDpsStrategy::InitTriggers(std::vector& triggers) +void DKBuffDpsStrategy::InitTriggers(std::vector& /*triggers*/) { } diff --git a/src/Ai/Class/Druid/DruidAiObjectContext.cpp b/src/Ai/Class/Druid/DruidAiObjectContext.cpp index 3d9086cff..4d74d1db3 100644 --- a/src/Ai/Class/Druid/DruidAiObjectContext.cpp +++ b/src/Ai/Class/Druid/DruidAiObjectContext.cpp @@ -19,6 +19,7 @@ #include "MeleeDruidStrategy.h" #include "OffhealDruidCatStrategy.h" #include "Playerbots.h" +#include "DruidPullStrategy.h" class DruidStrategyFactoryInternal : public NamedObjectContext { @@ -26,6 +27,7 @@ public: DruidStrategyFactoryInternal() { creators["nc"] = &DruidStrategyFactoryInternal::nc; + creators["pull"] = &DruidStrategyFactoryInternal::pull; creators["cat aoe"] = &DruidStrategyFactoryInternal::cat_aoe; creators["caster aoe"] = &DruidStrategyFactoryInternal::caster_aoe; creators["caster debuff"] = &DruidStrategyFactoryInternal::caster_debuff; @@ -40,6 +42,7 @@ public: private: static Strategy* nc(PlayerbotAI* botAI) { return new GenericDruidNonCombatStrategy(botAI); } + static Strategy* pull(PlayerbotAI* botAI) { return new DruidPullStrategy(botAI); } static Strategy* cat_aoe(PlayerbotAI* botAI) { return new CatAoeDruidStrategy(botAI); } static Strategy* caster_aoe(PlayerbotAI* botAI) { return new CasterDruidAoeStrategy(botAI); } static Strategy* caster_debuff(PlayerbotAI* botAI) { return new CasterDruidDebuffStrategy(botAI); } diff --git a/src/Ai/Class/Druid/Strategy/CatDpsDruidStrategy.cpp b/src/Ai/Class/Druid/Strategy/CatDpsDruidStrategy.cpp index b1a4685b1..f7a76b0fc 100644 --- a/src/Ai/Class/Druid/Strategy/CatDpsDruidStrategy.cpp +++ b/src/Ai/Class/Druid/Strategy/CatDpsDruidStrategy.cpp @@ -311,4 +311,4 @@ void CatDpsDruidStrategy::InitTriggers(std::vector& triggers) ); } -void CatAoeDruidStrategy::InitTriggers(std::vector& triggers) {} +void CatAoeDruidStrategy::InitTriggers(std::vector& /*triggers*/) {} diff --git a/src/Ai/Class/Druid/Strategy/DruidPullStrategy.cpp b/src/Ai/Class/Druid/Strategy/DruidPullStrategy.cpp new file mode 100644 index 000000000..dc72b9e82 --- /dev/null +++ b/src/Ai/Class/Druid/Strategy/DruidPullStrategy.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include "DruidPullStrategy.h" + +#include "AiObjectContext.h" +#include "Player.h" +#include "PlayerbotAI.h" +#include "Playerbots.h" + +std::string DruidPullStrategy::GetPullActionName() const +{ + Player* bot = botAI->GetBot(); + std::string actionName = PullStrategy::GetPullActionName(); + if (!bot) + return actionName; + + uint32 const faerieFireFeralId = botAI->GetAiObjectContext()->GetValue("spell id", "faerie fire (feral)")->Get(); + if (faerieFireFeralId && bot->HasSpell(faerieFireFeralId) && + (botAI->HasStrategy("bear", BOT_STATE_COMBAT) || botAI->HasStrategy("cat", BOT_STATE_COMBAT))) + { + actionName = "faerie fire (feral)"; + } + + Unit* target = GetTarget(); + uint32 const faerieFireSpellId = botAI->GetAiObjectContext()->GetValue("spell id", actionName)->Get(); + if (target && (!faerieFireSpellId || !bot->HasSpell(faerieFireSpellId) || + !botAI->CanCastSpell(faerieFireSpellId, target))) + { + uint32 const growlSpellId = botAI->GetAiObjectContext()->GetValue("spell id", "growl")->Get(); + if (growlSpellId && bot->HasSpell(growlSpellId) && botAI->CanCastSpell(growlSpellId, target)) + return "growl"; + } + + return actionName; +} + +std::string DruidPullStrategy::GetPreActionName() const +{ + if (GetPullActionName() == "faerie fire") + return ""; + + return PullStrategy::GetPreActionName(); +} diff --git a/src/Ai/Class/Druid/Strategy/DruidPullStrategy.h b/src/Ai/Class/Druid/Strategy/DruidPullStrategy.h new file mode 100644 index 000000000..9a52f262a --- /dev/null +++ b/src/Ai/Class/Druid/Strategy/DruidPullStrategy.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_DRUID_PULL_STRATEGY_H +#define _PLAYERBOT_DRUID_PULL_STRATEGY_H + +#include "PullStrategy.h" + +class DruidPullStrategy : public PullStrategy +{ +public: + DruidPullStrategy(PlayerbotAI* botAI) : PullStrategy(botAI, "faerie fire", "dire bear form") {} + + std::string GetPullActionName() const override; + std::string GetPreActionName() const override; +}; + +#endif diff --git a/src/Ai/Class/Hunter/Action/HunterActions.h b/src/Ai/Class/Hunter/Action/HunterActions.h index a67f17780..4c7c0851b 100644 --- a/src/Ai/Class/Hunter/Action/HunterActions.h +++ b/src/Ai/Class/Hunter/Action/HunterActions.h @@ -392,7 +392,7 @@ class CastExplosiveShotRank4Action : public CastExplosiveShotBaseAction public: CastExplosiveShotRank4Action(PlayerbotAI* botAI) : CastExplosiveShotBaseAction(botAI) {} - bool Execute(Event event) override + bool Execute(Event /*event*/) override { return botAI->CastSpell(60053, GetTarget()); } @@ -412,7 +412,7 @@ class CastExplosiveShotRank3Action : public CastExplosiveShotBaseAction public: CastExplosiveShotRank3Action(PlayerbotAI* botAI) : CastExplosiveShotBaseAction(botAI) {} - bool Execute(Event event) override + bool Execute(Event /*event*/) override { return botAI->CastSpell(60052, GetTarget()); } @@ -432,7 +432,7 @@ class CastExplosiveShotRank2Action : public CastExplosiveShotBaseAction public: CastExplosiveShotRank2Action(PlayerbotAI* botAI) : CastExplosiveShotBaseAction(botAI) {} - bool Execute(Event event) override + bool Execute(Event /*event*/) override { return botAI->CastSpell(60051, GetTarget()); } @@ -452,7 +452,7 @@ class CastExplosiveShotRank1Action : public CastExplosiveShotBaseAction public: CastExplosiveShotRank1Action(PlayerbotAI* botAI) : CastExplosiveShotBaseAction(botAI) {} - bool Execute(Event event) override + bool Execute(Event /*event*/) override { return botAI->CastSpell(53301, GetTarget()); } diff --git a/src/Ai/Class/Paladin/PaladinAiObjectContext.cpp b/src/Ai/Class/Paladin/PaladinAiObjectContext.cpp index 7edbf5c8f..a58ede3f8 100644 --- a/src/Ai/Class/Paladin/PaladinAiObjectContext.cpp +++ b/src/Ai/Class/Paladin/PaladinAiObjectContext.cpp @@ -12,6 +12,7 @@ #include "OffhealRetPaladinStrategy.h" #include "PaladinActions.h" #include "PaladinBuffStrategies.h" +#include "PaladinPullStrategy.h" #include "PaladinTriggers.h" #include "Playerbots.h" #include "TankPaladinStrategy.h" @@ -22,6 +23,7 @@ public: PaladinStrategyFactoryInternal() { creators["nc"] = &PaladinStrategyFactoryInternal::nc; + creators["pull"] = &PaladinStrategyFactoryInternal::pull; creators["cure"] = &PaladinStrategyFactoryInternal::cure; creators["boost"] = &PaladinStrategyFactoryInternal::boost; creators["cc"] = &PaladinStrategyFactoryInternal::cc; @@ -31,6 +33,7 @@ public: private: static Strategy* nc(PlayerbotAI* botAI) { return new GenericPaladinNonCombatStrategy(botAI); } + static Strategy* pull(PlayerbotAI* botAI) { return new PaladinPullStrategy(botAI); } static Strategy* cure(PlayerbotAI* botAI) { return new PaladinCureStrategy(botAI); } static Strategy* boost(PlayerbotAI* botAI) { return new PaladinBoostStrategy(botAI); } static Strategy* cc(PlayerbotAI* botAI) { return new PaladinCcStrategy(botAI); } diff --git a/src/Ai/Class/Paladin/Strategy/GenericPaladinStrategy.cpp b/src/Ai/Class/Paladin/Strategy/GenericPaladinStrategy.cpp index c4edc28fd..315b4a96f 100644 --- a/src/Ai/Class/Paladin/Strategy/GenericPaladinStrategy.cpp +++ b/src/Ai/Class/Paladin/Strategy/GenericPaladinStrategy.cpp @@ -54,7 +54,7 @@ void PaladinCureStrategy::InitTriggers(std::vector& triggers) { NextAction("cleanse magic on party", ACTION_DISPEL + 1) })); } -void PaladinBoostStrategy::InitTriggers(std::vector& triggers) +void PaladinBoostStrategy::InitTriggers(std::vector& /*triggers*/) { // triggers.push_back(new TriggerNode("divine favor", { NextAction("divine favor", diff --git a/src/Ai/Class/Paladin/Strategy/PaladinPullStrategy.cpp b/src/Ai/Class/Paladin/Strategy/PaladinPullStrategy.cpp new file mode 100644 index 000000000..ba0381b5a --- /dev/null +++ b/src/Ai/Class/Paladin/Strategy/PaladinPullStrategy.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include "PaladinPullStrategy.h" + +#include "AiObjectContext.h" +#include "Player.h" +#include "PlayerbotAI.h" +#include "Playerbots.h" + +std::string PaladinPullStrategy::GetPullActionName() const +{ + Player* bot = botAI->GetBot(); + Unit* target = GetTarget(); + if (!bot || !target || + (!botAI->HasStrategy("tank", BOT_STATE_COMBAT) && !botAI->HasStrategy("tank", BOT_STATE_NON_COMBAT))) + { + return PullStrategy::GetPullActionName(); + } + + uint32 const avengersShieldSpellId = botAI->GetAiObjectContext()->GetValue("spell id", "avenger's shield")->Get(); + if (avengersShieldSpellId && bot->HasSpell(avengersShieldSpellId) && + botAI->CanCastSpell(avengersShieldSpellId, target)) + { + return "avenger's shield"; + } + + uint32 const handOfReckoningSpellId = botAI->GetAiObjectContext()->GetValue("spell id", "hand of reckoning")->Get(); + if (handOfReckoningSpellId && bot->HasSpell(handOfReckoningSpellId) && + botAI->CanCastSpell(handOfReckoningSpellId, target)) + { + return "hand of reckoning"; + } + + return PullStrategy::GetPullActionName(); +} + +std::string PaladinPullStrategy::GetPreActionName() const +{ + if (botAI->HasStrategy("tank", BOT_STATE_COMBAT) || botAI->HasStrategy("tank", BOT_STATE_NON_COMBAT)) + return ""; + + return PullStrategy::GetPreActionName(); +} diff --git a/src/Ai/Class/Paladin/Strategy/PaladinPullStrategy.h b/src/Ai/Class/Paladin/Strategy/PaladinPullStrategy.h new file mode 100644 index 000000000..43d014ec7 --- /dev/null +++ b/src/Ai/Class/Paladin/Strategy/PaladinPullStrategy.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_PALADIN_PULL_STRATEGY_H +#define _PLAYERBOT_PALADIN_PULL_STRATEGY_H + +#include "PullStrategy.h" + +class PaladinPullStrategy : public PullStrategy +{ +public: + PaladinPullStrategy(PlayerbotAI* botAI) : PullStrategy(botAI, "judgement", "seal of righteousness") {} + + std::string GetPullActionName() const override; + std::string GetPreActionName() const override; +}; + +#endif diff --git a/src/Ai/Class/Warlock/Strategy/GenericWarlockStrategy.cpp b/src/Ai/Class/Warlock/Strategy/GenericWarlockStrategy.cpp index 9c85f7861..d5e06b54c 100644 --- a/src/Ai/Class/Warlock/Strategy/GenericWarlockStrategy.cpp +++ b/src/Ai/Class/Warlock/Strategy/GenericWarlockStrategy.cpp @@ -124,12 +124,12 @@ void AoEWarlockStrategy::InitTriggers(std::vector& triggers) ); } -void WarlockBoostStrategy::InitTriggers(std::vector& triggers) +void WarlockBoostStrategy::InitTriggers(std::vector& /*triggers*/) { // Placeholder for future boost triggers } -void WarlockPetStrategy::InitTriggers(std::vector& triggers) +void WarlockPetStrategy::InitTriggers(std::vector& /*triggers*/) { // Placeholder for future pet triggers } diff --git a/src/Ai/Class/Warlock/Strategy/TankWarlockStrategy.cpp b/src/Ai/Class/Warlock/Strategy/TankWarlockStrategy.cpp index 5c922af90..ce10e6b9c 100644 --- a/src/Ai/Class/Warlock/Strategy/TankWarlockStrategy.cpp +++ b/src/Ai/Class/Warlock/Strategy/TankWarlockStrategy.cpp @@ -41,6 +41,6 @@ std::vector TankWarlockStrategy::getDefaultActions() }; } -void TankWarlockStrategy::InitTriggers(std::vector& triggers) +void TankWarlockStrategy::InitTriggers(std::vector& /*triggers*/) { } diff --git a/src/Ai/Class/Warrior/Strategy/ArmsWarriorStrategy.cpp b/src/Ai/Class/Warrior/Strategy/ArmsWarriorStrategy.cpp index 6d6e0b655..bbae89d20 100644 --- a/src/Ai/Class/Warrior/Strategy/ArmsWarriorStrategy.cpp +++ b/src/Ai/Class/Warrior/Strategy/ArmsWarriorStrategy.cpp @@ -21,7 +21,7 @@ public: } private: - static ActionNode* charge(PlayerbotAI* botAI) + static ActionNode* charge(PlayerbotAI* /*botAI*/) { return new ActionNode( "charge", @@ -31,7 +31,7 @@ private: ); } - static ActionNode* death_wish(PlayerbotAI* botAI) + static ActionNode* death_wish(PlayerbotAI* /*botAI*/) { return new ActionNode( "death wish", @@ -41,7 +41,7 @@ private: ); } - static ActionNode* piercing_howl(PlayerbotAI* botAI) + static ActionNode* piercing_howl(PlayerbotAI* /*botAI*/) { return new ActionNode( "piercing howl", @@ -51,7 +51,7 @@ private: ); } - static ActionNode* mocking_blow(PlayerbotAI* botAI) + static ActionNode* mocking_blow(PlayerbotAI* /*botAI*/) { return new ActionNode( "mocking blow", @@ -61,7 +61,7 @@ private: ); } - static ActionNode* heroic_strike(PlayerbotAI* botAI) + static ActionNode* heroic_strike(PlayerbotAI* /*botAI*/) { return new ActionNode( "heroic strike", @@ -71,7 +71,7 @@ private: ); } - static ActionNode* enraged_regeneration(PlayerbotAI* botAI) + static ActionNode* enraged_regeneration(PlayerbotAI* /*botAI*/) { return new ActionNode( "enraged regeneration", @@ -81,7 +81,7 @@ private: ); } - static ActionNode* retaliation(PlayerbotAI* botAI) + static ActionNode* retaliation(PlayerbotAI* /*botAI*/) { return new ActionNode( "retaliation", @@ -91,7 +91,7 @@ private: ); } - static ActionNode* shattering_throw(PlayerbotAI* botAI) + static ActionNode* shattering_throw(PlayerbotAI* /*botAI*/) { return new ActionNode( "shattering throw", diff --git a/src/Ai/Class/Warrior/Strategy/FuryWarriorStrategy.cpp b/src/Ai/Class/Warrior/Strategy/FuryWarriorStrategy.cpp index 67e5cb461..24e05ffe9 100644 --- a/src/Ai/Class/Warrior/Strategy/FuryWarriorStrategy.cpp +++ b/src/Ai/Class/Warrior/Strategy/FuryWarriorStrategy.cpp @@ -18,7 +18,7 @@ public: } private: - static ActionNode* charge(PlayerbotAI* botAI) + static ActionNode* charge(PlayerbotAI* /*botAI*/) { return new ActionNode( "charge", @@ -28,7 +28,7 @@ private: ); } - static ActionNode* intercept(PlayerbotAI* botAI) + static ActionNode* intercept(PlayerbotAI* /*botAI*/) { return new ActionNode( "intercept", @@ -38,7 +38,7 @@ private: ); } - static ActionNode* piercing_howl(PlayerbotAI* botAI) + static ActionNode* piercing_howl(PlayerbotAI* /*botAI*/) { return new ActionNode( "piercing howl", @@ -48,7 +48,7 @@ private: ); } - static ActionNode* pummel(PlayerbotAI* botAI) + static ActionNode* pummel(PlayerbotAI* /*botAI*/) { return new ActionNode( "pummel", @@ -58,7 +58,7 @@ private: ); } - static ActionNode* enraged_regeneration(PlayerbotAI* botAI) + static ActionNode* enraged_regeneration(PlayerbotAI* /*botAI*/) { return new ActionNode( "enraged regeneration", diff --git a/src/Ai/Class/Warrior/Strategy/TankWarriorStrategy.cpp b/src/Ai/Class/Warrior/Strategy/TankWarriorStrategy.cpp index e6daa2646..f256f2093 100644 --- a/src/Ai/Class/Warrior/Strategy/TankWarriorStrategy.cpp +++ b/src/Ai/Class/Warrior/Strategy/TankWarriorStrategy.cpp @@ -24,7 +24,7 @@ public: } private: - static ActionNode* heroic_throw_taunt(PlayerbotAI* botAI) + static ActionNode* heroic_throw_taunt(PlayerbotAI* /*botAI*/) { return new ActionNode( "heroic throw", @@ -34,7 +34,7 @@ private: ); } - static ActionNode* heroic_throw_on_snare_target(PlayerbotAI* botAI) + static ActionNode* heroic_throw_on_snare_target(PlayerbotAI* /*botAI*/) { return new ActionNode( "heroic throw on snare target", @@ -44,7 +44,7 @@ private: ); } - static ActionNode* last_stand(PlayerbotAI* botAI) + static ActionNode* last_stand(PlayerbotAI* /*botAI*/) { return new ActionNode( "last stand", @@ -54,7 +54,7 @@ private: ); } - static ActionNode* devastate(PlayerbotAI* botAI) + static ActionNode* devastate(PlayerbotAI* /*botAI*/) { return new ActionNode( "devastate", @@ -64,7 +64,7 @@ private: ); } - static ActionNode* commanding_shout(PlayerbotAI* botAI) + static ActionNode* commanding_shout(PlayerbotAI* /*botAI*/) { return new ActionNode( "commanding shout", @@ -74,7 +74,7 @@ private: ); } - static ActionNode* sunder_armor(PlayerbotAI* botAI) + static ActionNode* sunder_armor(PlayerbotAI* /*botAI*/) { return new ActionNode( "sunder armor", @@ -84,7 +84,7 @@ private: ); } - static ActionNode* charge(PlayerbotAI* botAI) + static ActionNode* charge(PlayerbotAI* /*botAI*/) { return new ActionNode( "charge", @@ -94,7 +94,7 @@ private: ); } - static ActionNode* taunt(PlayerbotAI* botAI) + static ActionNode* taunt(PlayerbotAI* /*botAI*/) { return new ActionNode( "taunt", @@ -104,7 +104,7 @@ private: ); } - static ActionNode* vigilance(PlayerbotAI* botAI) + static ActionNode* vigilance(PlayerbotAI* /*botAI*/) { return new ActionNode( "vigilance", @@ -114,7 +114,7 @@ private: ); } - static ActionNode* enraged_regeneration(PlayerbotAI* botAI) + static ActionNode* enraged_regeneration(PlayerbotAI* /*botAI*/) { return new ActionNode( "enraged regeneration", diff --git a/src/Ai/Class/Warrior/Strategy/WarriorPullStrategy.cpp b/src/Ai/Class/Warrior/Strategy/WarriorPullStrategy.cpp new file mode 100644 index 000000000..cdae7e29c --- /dev/null +++ b/src/Ai/Class/Warrior/Strategy/WarriorPullStrategy.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include "WarriorPullStrategy.h" + +#include "AiObjectContext.h" +#include "Player.h" +#include "PlayerbotAI.h" + +std::string WarriorPullStrategy::GetPullActionName() const +{ + Player* bot = botAI->GetBot(); + Unit* target = GetTarget(); + if (!bot || !target) + return PullStrategy::GetPullActionName(); + + uint32 const heroicThrowSpellId = botAI->GetAiObjectContext()->GetValue("spell id", "heroic throw")->Get(); + if (heroicThrowSpellId && bot->HasSpell(heroicThrowSpellId) && + botAI->CanCastSpell(heroicThrowSpellId, target)) + { + return "heroic throw"; + } + + return PullStrategy::GetPullActionName(); +} diff --git a/src/Ai/Class/Warrior/Strategy/WarriorPullStrategy.h b/src/Ai/Class/Warrior/Strategy/WarriorPullStrategy.h new file mode 100644 index 000000000..c63bb21ee --- /dev/null +++ b/src/Ai/Class/Warrior/Strategy/WarriorPullStrategy.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_WARRIOR_PULL_STRATEGY_H +#define _PLAYERBOT_WARRIOR_PULL_STRATEGY_H + +#include "PullStrategy.h" + +class WarriorPullStrategy : public PullStrategy +{ +public: + WarriorPullStrategy(PlayerbotAI* botAI) : PullStrategy(botAI, "shoot") {} + + std::string GetPullActionName() const override; +}; + +#endif diff --git a/src/Ai/Class/Warrior/WarriorAiObjectContext.cpp b/src/Ai/Class/Warrior/WarriorAiObjectContext.cpp index 22754beab..781307729 100644 --- a/src/Ai/Class/Warrior/WarriorAiObjectContext.cpp +++ b/src/Ai/Class/Warrior/WarriorAiObjectContext.cpp @@ -10,8 +10,8 @@ #include "GenericWarriorNonCombatStrategy.h" #include "NamedObjectContext.h" #include "Playerbots.h" -#include "PullStrategy.h" #include "TankWarriorStrategy.h" +#include "WarriorPullStrategy.h" #include "WarriorActions.h" #include "WarriorTriggers.h" @@ -28,7 +28,7 @@ public: private: static Strategy* nc(PlayerbotAI* botAI) { return new GenericWarriorNonCombatStrategy(botAI); } static Strategy* warrior_aoe(PlayerbotAI* botAI) { return new WarrirorAoeStrategy(botAI); } - static Strategy* pull(PlayerbotAI* botAI) { return new PullStrategy(botAI, "shoot"); } + static Strategy* pull(PlayerbotAI* botAI) { return new WarriorPullStrategy(botAI); } }; class WarriorCombatStrategyFactoryInternal : public NamedObjectContext diff --git a/src/Ai/Dungeon/AuchenaiCrypts/Action/AuchenaiCryptsActions.cpp b/src/Ai/Dungeon/AuchenaiCrypts/Action/AuchenaiCryptsActions.cpp new file mode 100644 index 000000000..2ec7907c7 --- /dev/null +++ b/src/Ai/Dungeon/AuchenaiCrypts/Action/AuchenaiCryptsActions.cpp @@ -0,0 +1,117 @@ +#include "Playerbots.h" +#include "AiFactory.h" +#include "AuchenaiCryptsTriggers.h" +#include "AuchenaiCryptsActions.h" + +// Shirrak the Dead Watcher + +static const Position SHIRRAK_RANGED_POSITION = { -21.777f, -162.700f, 26.062f }; +static const Position SHIRRAK_TANK_POSITION = { -65.171f, -162.920f, 26.504f }; + +// Tank will position Shirrak at the specified coordinates, further down the corridor past the stairs + +bool ShirrakTankPositionBossAction::Execute(Event /*event*/) +{ + Unit* shirrak = AI_VALUE2(Unit*, "find target", "shirrak the dead watcher"); + if (!shirrak) + return false; + + if (bot->GetVictim() != shirrak) + return Attack(shirrak); + + if (shirrak->GetVictim() == bot && bot->IsWithinMeleeRange(shirrak) && + bot->GetHealthPct()>30.0f) + { + const Position& position = SHIRRAK_TANK_POSITION; + float distToPosition = bot->GetExactDist2d(position.GetPositionX(), + position.GetPositionY()); + if (distToPosition > 6.0f) + { + float dX = position.GetPositionX() - bot->GetPositionX(); + float dY = position.GetPositionY() - bot->GetPositionY(); + float moveDist = std::min(2.0f, distToPosition); + float moveX = bot->GetPositionX() + (dX / distToPosition) * moveDist; + float moveY = bot->GetPositionY() + (dY / distToPosition) * moveDist; + + return MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), false, false, + false, false, MovementPriority::MOVEMENT_COMBAT, true, true); + } + } + + return false; +} + +// Flee from Shirrak's Focus Fire + +bool ShirrakFleeFocusFireAction::Execute(Event /*event*/) +{ + std::list creatureList; + bot->GetCreatureListWithEntryInGrid(creatureList, static_cast(AuchenaiCryptsIDs::NPC_FOCUS_FIRE), 20.0f); + + for (Creature* flare : creatureList) + { + if (flare && flare->IsAlive()) + { + float currentDistance = bot->GetDistance2d(flare); + constexpr float safeDistance = 12.0f; + constexpr float buffer = 5.0f; + + if (currentDistance < safeDistance) + { + bot->AttackStop(); + + float distanceToMove = safeDistance - currentDistance + buffer; + + return MoveAway(flare, distanceToMove); + } + } + } + return false; +} + +// Ranged should keep distance from Shirrak, staying at the edge of the stairs + +bool ShirrakRangedKeepDistanceAction::Execute(Event /*event*/) +{ + + std::vector rangedBots; + if (Group* group = bot->GetGroup()) + { + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && botAI->IsRanged(member)) + rangedBots.push_back(member); + } + } + + auto findIt = std::find(rangedBots.begin(), rangedBots.end(), bot); + size_t botIndex = (findIt != rangedBots.end()) ? std::distance(rangedBots.begin(), findIt) : 0; + size_t count = rangedBots.size(); + + constexpr float arcSpan = M_PI / 2.0f; + float arcCenter = M_PI; + float arcStart = arcCenter - (arcSpan / 2.0f); + + float angle = (count <= 1) ? arcCenter : (arcStart + (arcSpan * (float)botIndex / (float)(count - 1))); + + constexpr float spreadRadius = 3.0f; + float targetX = SHIRRAK_RANGED_POSITION.GetPositionX() + cos(angle) * spreadRadius; + float targetY = SHIRRAK_RANGED_POSITION.GetPositionY() + sin(angle) * spreadRadius; + + float distToSpot = bot->GetExactDist2d(targetX, targetY); + + if (distToSpot > 4.0f) + { + float dX = targetX - bot->GetPositionX(); + float dY = targetY - bot->GetPositionY(); + + float moveDist = std::min(2.0f, distToSpot); + float moveX = bot->GetPositionX() + (dX / distToSpot) * moveDist; + float moveY = bot->GetPositionY() + (dY / distToSpot) * moveDist; + + return MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), false, false, + false, false, MovementPriority::MOVEMENT_COMBAT, true, false); + } + return false; +} diff --git a/src/Ai/Dungeon/AuchenaiCrypts/Action/AuchenaiCryptsActions.h b/src/Ai/Dungeon/AuchenaiCrypts/Action/AuchenaiCryptsActions.h new file mode 100644 index 000000000..4764efb65 --- /dev/null +++ b/src/Ai/Dungeon/AuchenaiCrypts/Action/AuchenaiCryptsActions.h @@ -0,0 +1,31 @@ +#ifndef _PLAYERBOT_TBCDUNGEONAUCHENAICRYPTSACTIONS_H +#define _PLAYERBOT_TBCDUNGEONAUCHENAICRYPTSACTIONS_H + +#include "AttackAction.h" +#include "MovementActions.h" +#include "AuchenaiCryptsTriggers.h" + +// Shirrak the Dead Watcher + +class ShirrakTankPositionBossAction : public AttackAction +{ +public: + ShirrakTankPositionBossAction(PlayerbotAI* botAI) : AttackAction(botAI, "shirrak tank position boss") {} + bool Execute(Event event) override; +}; + +class ShirrakFleeFocusFireAction : public MovementAction +{ +public: + ShirrakFleeFocusFireAction(PlayerbotAI* botAI) : MovementAction(botAI, "shirrak flee focus fire") {} + bool Execute(Event event) override; +}; + +class ShirrakRangedKeepDistanceAction : public MovementAction +{ +public: + ShirrakRangedKeepDistanceAction(PlayerbotAI* botAI) : MovementAction(botAI, "shirrak ranged keep distance") {} + bool Execute(Event event) override; +}; + +#endif diff --git a/src/Ai/Dungeon/AuchenaiCrypts/AuchenaiCryptsActionContext.h b/src/Ai/Dungeon/AuchenaiCrypts/AuchenaiCryptsActionContext.h new file mode 100644 index 000000000..4eea58716 --- /dev/null +++ b/src/Ai/Dungeon/AuchenaiCrypts/AuchenaiCryptsActionContext.h @@ -0,0 +1,34 @@ +#ifndef _PLAYERBOT_TBCDUNGEONAUCHENAICRYPTSACTIONCONTEXT_H +#define _PLAYERBOT_TBCDUNGEONAUCHENAICRYPTSACTIONCONTEXT_H + +#include "AiObjectContext.h" +#include "Action.h" +#include "AuchenaiCryptsActions.h" + +class TbcDungeonAuchenaiCryptsActionContext : public NamedObjectContext +{ +public: + TbcDungeonAuchenaiCryptsActionContext() : NamedObjectContext(false, true) + { + creators["shirrak tank position boss"] = + &TbcDungeonAuchenaiCryptsActionContext::shirrak_tank_position_boss; + + creators["shirrak flee focus fire"] = + &TbcDungeonAuchenaiCryptsActionContext::shirrak_flee_focus_fire; + + creators["shirrak ranged keep distance"] = + &TbcDungeonAuchenaiCryptsActionContext::shirrak_ranged_keep_distance; + } +private: + + static Action* shirrak_tank_position_boss( + PlayerbotAI* botAI) { return new ShirrakTankPositionBossAction(botAI); } + + static Action* shirrak_flee_focus_fire( + PlayerbotAI* botAI) { return new ShirrakFleeFocusFireAction(botAI); } + + static Action* shirrak_ranged_keep_distance( + PlayerbotAI* botAI) { return new ShirrakRangedKeepDistanceAction(botAI); } +}; + +#endif diff --git a/src/Ai/Dungeon/AuchenaiCrypts/AuchenaiCryptsTriggerContext.h b/src/Ai/Dungeon/AuchenaiCrypts/AuchenaiCryptsTriggerContext.h new file mode 100644 index 000000000..95f15f16a --- /dev/null +++ b/src/Ai/Dungeon/AuchenaiCrypts/AuchenaiCryptsTriggerContext.h @@ -0,0 +1,35 @@ +#ifndef _PLAYERBOT_TBCDUNGEONAUCHENAICRYPTSTRIGGERCONTEXT_H +#define _PLAYERBOT_TBCDUNGEONAUCHENAICRYPTSTRIGGERCONTEXT_H + +#include "AiObjectContext.h" +#include "TriggerContext.h" +#include "AuchenaiCryptsTriggers.h" + +class TbcDungeonAuchenaiCryptsTriggerContext : public NamedObjectContext +{ +public: + // Shirrak the Dead Watcher + TbcDungeonAuchenaiCryptsTriggerContext() + { + creators["shirrak tank position boss"] = + &TbcDungeonAuchenaiCryptsTriggerContext::shirrak_tank_position_boss; + + creators["shirrak flee focus fire"] = + &TbcDungeonAuchenaiCryptsTriggerContext::shirrak_flee_focus_fire; + + creators["shirrak ranged keep distance"] = + &TbcDungeonAuchenaiCryptsTriggerContext::shirrak_ranged_keep_distance; + } +private: + // Shirrak the Dead Watcher + static Trigger* shirrak_tank_position_boss( + PlayerbotAI* botAI) { return new ShirrakTankPositionBossTrigger(botAI); } + + static Trigger* shirrak_flee_focus_fire( + PlayerbotAI* botAI) { return new ShirrakFleeFocusFireTrigger(botAI); } + + static Trigger* shirrak_ranged_keep_distance( + PlayerbotAI* botAI) { return new ShirrakRangedKeepDistanceTrigger(botAI); } +}; + +#endif diff --git a/src/Ai/Dungeon/AuchenaiCrypts/Multiplier/AuchenaiCryptsMultipliers.cpp b/src/Ai/Dungeon/AuchenaiCrypts/Multiplier/AuchenaiCryptsMultipliers.cpp new file mode 100644 index 000000000..2f74a5a58 --- /dev/null +++ b/src/Ai/Dungeon/AuchenaiCrypts/Multiplier/AuchenaiCryptsMultipliers.cpp @@ -0,0 +1,45 @@ +#include "AuchenaiCryptsMultipliers.h" +#include "AuchenaiCryptsActions.h" +#include "AuchenaiCryptsTriggers.h" +#include "MovementActions.h" +#include "ReachTargetActions.h" +#include "FollowActions.h" +#include "AiObjectContext.h" +#include "Playerbots.h" + +// Shirrak the Dead Watcher + +// Flee from Focus Fire and dont run back in +float ShirrakFleeFocusFireMultiplier::GetValue(Action* action) +{ + if (!AI_VALUE2(Unit*, "find target", "shirrak the dead watcher")) + return 1.0f; + + std::list creatureList; + bot->GetCreatureListWithEntryInGrid(creatureList, static_cast(AuchenaiCryptsIDs::NPC_FOCUS_FIRE), 20.0f); + + for (Creature* flare : creatureList) + { + if (flare && flare->IsAlive()) + { + if (dynamic_cast(action)) + return 0.0f; + + float currentDistance = bot->GetDistance2d(flare); + constexpr float safeDistance = 12.0f; + constexpr float buffer = 5.0f; + + if (currentDistance < safeDistance + buffer && ( + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action))) + { + return 0.0f; + } + } + } + return 1.0f; +} diff --git a/src/Ai/Dungeon/AuchenaiCrypts/Multiplier/AuchenaiCryptsMultipliers.h b/src/Ai/Dungeon/AuchenaiCrypts/Multiplier/AuchenaiCryptsMultipliers.h new file mode 100644 index 000000000..df5de2318 --- /dev/null +++ b/src/Ai/Dungeon/AuchenaiCrypts/Multiplier/AuchenaiCryptsMultipliers.h @@ -0,0 +1,13 @@ +#ifndef _PLAYERBOT_TBCDUNGEONAUCHENAICRYPTSMULTIPLIERS_H +#define _PLAYERBOT_TBCDUNGEONAUCHENAICRYPTSMULTIPLIERS_H + +#include "Multiplier.h" + +class ShirrakFleeFocusFireMultiplier : public Multiplier +{ +public: + ShirrakFleeFocusFireMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "shirrak flee focus fire") {} + float GetValue(Action* action) override; +}; + +#endif diff --git a/src/Ai/Dungeon/AuchenaiCrypts/Strategy/AuchenaiCryptsStrategy.cpp b/src/Ai/Dungeon/AuchenaiCrypts/Strategy/AuchenaiCryptsStrategy.cpp new file mode 100644 index 000000000..f975d46bb --- /dev/null +++ b/src/Ai/Dungeon/AuchenaiCrypts/Strategy/AuchenaiCryptsStrategy.cpp @@ -0,0 +1,21 @@ +#include "AuchenaiCryptsTriggers.h" +#include "AuchenaiCryptsStrategy.h" +#include "AuchenaiCryptsMultipliers.h" + +void TbcDungeonAuchenaiCryptsStrategy::InitTriggers(std::vector& triggers) +{ + // Shirrak The Dead Watcher + triggers.push_back(new TriggerNode("shirrak tank position boss", { + NextAction("shirrak tank position boss", ACTION_RAID + 1) })); + + triggers.push_back(new TriggerNode("shirrak flee focus fire", { + NextAction("shirrak flee focus fire", ACTION_EMERGENCY + 10) })); + + triggers.push_back(new TriggerNode("shirrak ranged keep distance", { + NextAction("shirrak ranged keep distance", ACTION_RAID + 1) })); +} + +void TbcDungeonAuchenaiCryptsStrategy::InitMultipliers(std::vector& multipliers) +{ + multipliers.push_back(new ShirrakFleeFocusFireMultiplier(botAI)); +} diff --git a/src/Ai/Dungeon/AuchenaiCrypts/Strategy/AuchenaiCryptsStrategy.h b/src/Ai/Dungeon/AuchenaiCrypts/Strategy/AuchenaiCryptsStrategy.h new file mode 100644 index 000000000..ff82a0266 --- /dev/null +++ b/src/Ai/Dungeon/AuchenaiCrypts/Strategy/AuchenaiCryptsStrategy.h @@ -0,0 +1,19 @@ +#ifndef _PLAYERBOT_TBCDUNGEONAUCHENAICRYPTSSTRATEGY_H +#define _PLAYERBOT_TBCDUNGEONAUCHENAICRYPTSSTRATEGY_H + +#include "AiObjectContext.h" +#include "Strategy.h" +#include "Multiplier.h" + +class TbcDungeonAuchenaiCryptsStrategy : public Strategy +{ +public: + TbcDungeonAuchenaiCryptsStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} + + virtual std::string const getName() override { return "tbc-ac"; } + + virtual void InitTriggers(std::vector &triggers) override; + virtual void InitMultipliers(std::vector &multipliers) override; +}; + +#endif diff --git a/src/Ai/Dungeon/AuchenaiCrypts/Trigger/AuchenaiCryptsTriggers.cpp b/src/Ai/Dungeon/AuchenaiCrypts/Trigger/AuchenaiCryptsTriggers.cpp new file mode 100644 index 000000000..372614d2f --- /dev/null +++ b/src/Ai/Dungeon/AuchenaiCrypts/Trigger/AuchenaiCryptsTriggers.cpp @@ -0,0 +1,34 @@ +#include "Playerbots.h" +#include "AuchenaiCryptsTriggers.h" +#include "AiObject.h" +#include "AiObjectContext.h" + +// Shirrak the Dead Watcher + +bool ShirrakTankPositionBossTrigger::IsActive() +{ + return botAI->IsTank(bot) && + AI_VALUE2(Unit*, "find target", "shirrak the dead watcher"); +} + +bool ShirrakFleeFocusFireTrigger::IsActive() +{ + if (!AI_VALUE2(Unit*, "find target", "shirrak the dead watcher")) + return false; + + std::list creatureList; + bot->GetCreatureListWithEntryInGrid(creatureList, static_cast(AuchenaiCryptsIDs::NPC_FOCUS_FIRE), 20.0f); + + for (Creature* flare : creatureList) + { + if (flare && flare->IsAlive()) + return true; + } + return false; +} + +bool ShirrakRangedKeepDistanceTrigger::IsActive() +{ + return botAI->IsRanged(bot) && + AI_VALUE2(Unit*, "find target", "shirrak the dead watcher"); +} diff --git a/src/Ai/Dungeon/AuchenaiCrypts/Trigger/AuchenaiCryptsTriggers.h b/src/Ai/Dungeon/AuchenaiCrypts/Trigger/AuchenaiCryptsTriggers.h new file mode 100644 index 000000000..1d3144194 --- /dev/null +++ b/src/Ai/Dungeon/AuchenaiCrypts/Trigger/AuchenaiCryptsTriggers.h @@ -0,0 +1,38 @@ +#ifndef _PLAYERBOT_TBCDUNGEONAUCHENAICRYPTSTRIGGERS_H +#define _PLAYERBOT_TBCDUNGEONAUCHENAICRYPTSTRIGGERS_H + +#include "Trigger.h" +#include "GenericTriggers.h" +#include "DungeonStrategyUtils.h" + +enum class AuchenaiCryptsIDs : uint32 +{ + // Shirrak The Dead Watcher + NPC_FOCUS_FIRE = 18374, +}; + +class ShirrakTankPositionBossTrigger : public Trigger +{ +public: + ShirrakTankPositionBossTrigger(PlayerbotAI* botAI) : Trigger(botAI, "shirrak tank position boss") {} + + bool IsActive() override; +}; + +class ShirrakFleeFocusFireTrigger : public Trigger +{ +public: + ShirrakFleeFocusFireTrigger(PlayerbotAI* botAI) : Trigger(botAI, "shirrak flee focus fire") {} + + bool IsActive() override; +}; + +class ShirrakRangedKeepDistanceTrigger : public Trigger +{ +public: + ShirrakRangedKeepDistanceTrigger(PlayerbotAI* botAI) : Trigger(botAI, "shirrak ranged keep distance") {} + + bool IsActive() override; +}; + +#endif diff --git a/src/Ai/Dungeon/DungeonStrategyContext.h b/src/Ai/Dungeon/DungeonStrategyContext.h index 3311aeee2..07e5fe505 100644 --- a/src/Ai/Dungeon/DungeonStrategyContext.h +++ b/src/Ai/Dungeon/DungeonStrategyContext.h @@ -2,6 +2,7 @@ #define _PLAYERBOT_DUNGEONSTRATEGYCONTEXT_H #include "Strategy.h" +#include "AuchenaiCrypts/Strategy/AuchenaiCryptsStrategy.h" #include "UtgardeKeep/Strategy/UtgardeKeepStrategy.h" #include "Nexus/Strategy/NexusStrategy.h" #include "AzjolNerub/Strategy/AzjolNerubStrategy.h" @@ -44,7 +45,7 @@ class DungeonStrategyContext : public NamedObjectContext // ... // Burning Crusade - // ... + creators["tbc-ac"] = &DungeonStrategyContext::tbc_ac; // Auchindoun: Auchenai Crypts // Wrath of the Lich King creators["wotlk-uk"] = &DungeonStrategyContext::wotlk_uk; // Utgarde Keep @@ -65,6 +66,7 @@ class DungeonStrategyContext : public NamedObjectContext creators["wotlk-fos"] = &DungeonStrategyContext::wotlk_fos; // The Forge of Souls } private: + static Strategy* tbc_ac(PlayerbotAI* botAI) { return new TbcDungeonAuchenaiCryptsStrategy(botAI); } static Strategy* wotlk_uk(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } static Strategy* wotlk_nex(PlayerbotAI* botAI) { return new WotlkDungeonNexStrategy(botAI); } static Strategy* wotlk_an(PlayerbotAI* botAI) { return new WotlkDungeonANStrategy(botAI); } diff --git a/src/Ai/Dungeon/PitOfSaron/Action/PitOfSaronActions.cpp b/src/Ai/Dungeon/PitOfSaron/Action/PitOfSaronActions.cpp index 27546e796..201a44199 100644 --- a/src/Ai/Dungeon/PitOfSaron/Action/PitOfSaronActions.cpp +++ b/src/Ai/Dungeon/PitOfSaron/Action/PitOfSaronActions.cpp @@ -134,7 +134,7 @@ bool IckAndKrickAction::PoisonNova(bool poisonNova, Unit* boss) return false; } -bool IckAndKrickAction::ExplosiveBarrage(bool explosiveBarrage, Unit* boss) +bool IckAndKrickAction::ExplosiveBarrage(bool /*explosiveBarrage*/, Unit* boss) { std::vector orbs; Unit* closestOrb = nullptr; diff --git a/src/Ai/Dungeon/PitOfSaron/Multiplier/PitOfSaronMultipliers.cpp b/src/Ai/Dungeon/PitOfSaron/Multiplier/PitOfSaronMultipliers.cpp index be36e480f..00de758c1 100644 --- a/src/Ai/Dungeon/PitOfSaron/Multiplier/PitOfSaronMultipliers.cpp +++ b/src/Ai/Dungeon/PitOfSaron/Multiplier/PitOfSaronMultipliers.cpp @@ -27,7 +27,7 @@ float IckAndKrickMultiplier::GetValue(Action* action) return 1.0f; } -float GarfrostMultiplier::GetValue(Action* action) +float GarfrostMultiplier::GetValue(Action* /*action*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "garfrost"); if (!boss) diff --git a/src/Ai/Dungeon/TbcDungeonActionContext.h b/src/Ai/Dungeon/TbcDungeonActionContext.h new file mode 100644 index 000000000..8c3547224 --- /dev/null +++ b/src/Ai/Dungeon/TbcDungeonActionContext.h @@ -0,0 +1,6 @@ +#ifndef _PLAYERBOT_TBCDUNGEONACTIONCONTEXT_H +#define _PLAYERBOT_TBCDUNGEONACTIONCONTEXT_H + +#include "AuchenaiCrypts/AuchenaiCryptsActionContext.h" + +#endif diff --git a/src/Ai/Dungeon/TbcDungeonTriggerContext.h b/src/Ai/Dungeon/TbcDungeonTriggerContext.h new file mode 100644 index 000000000..9a680b7af --- /dev/null +++ b/src/Ai/Dungeon/TbcDungeonTriggerContext.h @@ -0,0 +1,6 @@ +#ifndef _PLAYERBOT_TBCDUNGEONTRIGGERCONTEXT_H +#define _PLAYERBOT_TBCDUNGEONTRIGGERCONTEXT_H + +#include "AuchenaiCrypts/AuchenaiCryptsTriggerContext.h" + +#endif diff --git a/src/Ai/Dungeon/TrialOfTheChampion/Strategy/TrialOfTheChampionStrategy.cpp b/src/Ai/Dungeon/TrialOfTheChampion/Strategy/TrialOfTheChampionStrategy.cpp index 323970d52..0912a912c 100644 --- a/src/Ai/Dungeon/TrialOfTheChampion/Strategy/TrialOfTheChampionStrategy.cpp +++ b/src/Ai/Dungeon/TrialOfTheChampion/Strategy/TrialOfTheChampionStrategy.cpp @@ -15,6 +15,6 @@ void WotlkDungeonToCStrategy::InitTriggers(std::vector &triggers) } -void WotlkDungeonToCStrategy::InitMultipliers(std::vector &multipliers) +void WotlkDungeonToCStrategy::InitMultipliers(std::vector &/*multipliers*/) { } diff --git a/src/Ai/Raid/GruulsLair/Multiplier/RaidGruulsLairMultipliers.cpp b/src/Ai/Raid/GruulsLair/Multiplier/RaidGruulsLairMultipliers.cpp index 6604fc525..a38f8a291 100644 --- a/src/Ai/Raid/GruulsLair/Multiplier/RaidGruulsLairMultipliers.cpp +++ b/src/Ai/Raid/GruulsLair/Multiplier/RaidGruulsLairMultipliers.cpp @@ -57,7 +57,7 @@ float HighKingMaulgarDisableArcaneShotOnKroshMultiplier::GetValue(Action* action float HighKingMaulgarDisableMageTankAOEMultiplier::GetValue(Action* action) { - if (IsKroshMageTank(botAI, bot) && + if (IsKroshMageTank(bot) && (dynamic_cast(action) || dynamic_cast(action) || dynamic_cast(action) || dynamic_cast(action) || dynamic_cast(action) || dynamic_cast(action))) diff --git a/src/Ai/Raid/GruulsLair/Trigger/RaidGruulsLairTriggers.cpp b/src/Ai/Raid/GruulsLair/Trigger/RaidGruulsLairTriggers.cpp index 4bc5efe99..3caadb384 100644 --- a/src/Ai/Raid/GruulsLair/Trigger/RaidGruulsLairTriggers.cpp +++ b/src/Ai/Raid/GruulsLair/Trigger/RaidGruulsLairTriggers.cpp @@ -31,14 +31,14 @@ bool HighKingMaulgarIsMageTankTrigger::IsActive() { Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand"); - return IsKroshMageTank(botAI, bot) && krosh; + return IsKroshMageTank(bot) && krosh; } bool HighKingMaulgarIsMoonkinTankTrigger::IsActive() { Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed"); - return IsKigglerMoonkinTank(botAI, bot) && kiggler; + return IsKigglerMoonkinTank(bot) && kiggler; } bool HighKingMaulgarDeterminingKillOrderTrigger::IsActive() @@ -53,8 +53,8 @@ bool HighKingMaulgarDeterminingKillOrderTrigger::IsActive() !(botAI->IsMainTank(bot) && maulgar) && !(botAI->IsAssistTankOfIndex(bot, 0, false) && olm) && !(botAI->IsAssistTankOfIndex(bot, 1, false) && blindeye) && - !(IsKroshMageTank(botAI, bot) && krosh) && - !(IsKigglerMoonkinTank(botAI, bot) && kiggler); + !(IsKroshMageTank(bot) && krosh) && + !(IsKigglerMoonkinTank(bot) && kiggler); } bool HighKingMaulgarHealerInDangerTrigger::IsActive() diff --git a/src/Ai/Raid/GruulsLair/Util/RaidGruulsLairHelpers.cpp b/src/Ai/Raid/GruulsLair/Util/RaidGruulsLairHelpers.cpp index b29549b82..7195f0ebd 100644 --- a/src/Ai/Raid/GruulsLair/Util/RaidGruulsLairHelpers.cpp +++ b/src/Ai/Raid/GruulsLair/Util/RaidGruulsLairHelpers.cpp @@ -39,7 +39,7 @@ namespace GruulsLairHelpers return false; } - bool IsKroshMageTank(PlayerbotAI* botAI, Player* bot) + bool IsKroshMageTank(Player* bot) { Group* group = bot->GetGroup(); if (!group) @@ -79,7 +79,7 @@ namespace GruulsLairHelpers return highestHpMage == bot; } - bool IsKigglerMoonkinTank(PlayerbotAI* botAI, Player* bot) + bool IsKigglerMoonkinTank(Player* bot) { Group* group = bot->GetGroup(); if (!group) diff --git a/src/Ai/Raid/GruulsLair/Util/RaidGruulsLairHelpers.h b/src/Ai/Raid/GruulsLair/Util/RaidGruulsLairHelpers.h index 4615a9b7a..f9315565b 100644 --- a/src/Ai/Raid/GruulsLair/Util/RaidGruulsLairHelpers.h +++ b/src/Ai/Raid/GruulsLair/Util/RaidGruulsLairHelpers.h @@ -29,8 +29,8 @@ namespace GruulsLairHelpers constexpr uint32 GRUULS_LAIR_MAP_ID = 565; bool IsAnyOgreBossAlive(PlayerbotAI* botAI); - bool IsKroshMageTank(PlayerbotAI* botAI, Player* bot); - bool IsKigglerMoonkinTank(PlayerbotAI* botAI, Player* bot); + bool IsKroshMageTank(Player* bot); + bool IsKigglerMoonkinTank(Player* bot); bool IsPositionSafe(PlayerbotAI* botAI, Player* bot, Position pos); bool TryGetNewSafePosition(PlayerbotAI* botAI, Player* bot, Position& outPos); diff --git a/src/Ai/Raid/Icecrown/Action/RaidIccActions.cpp b/src/Ai/Raid/Icecrown/Action/RaidIccActions.cpp index 336bd45e2..0ae27ffab 100644 --- a/src/Ai/Raid/Icecrown/Action/RaidIccActions.cpp +++ b/src/Ai/Raid/Icecrown/Action/RaidIccActions.cpp @@ -1998,10 +1998,10 @@ bool IccRotfaceGroupPositionAction::PositionRangedAndHealers(Unit* boss,Unit *sm if (!isHeroic) return false; - return FindAndMoveFromClosestMember(boss, smallOoze); + return FindAndMoveFromClosestMember(smallOoze); } -bool IccRotfaceGroupPositionAction::FindAndMoveFromClosestMember(Unit* boss, Unit* smallOoze) +bool IccRotfaceGroupPositionAction::FindAndMoveFromClosestMember(Unit* smallOoze) { const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); @@ -6925,7 +6925,7 @@ bool IccLichKingWinterAction::Execute(Event /*event*/) isVictim = true; // First priority: Get out of Defile if we're in one - if (!IsPositionSafeFromDefile(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), 3.0f)) + if (!IsPositionSafeFromDefile(bot->GetPositionX(), bot->GetPositionY(), 3.0f)) { // Find nearest safe position (use tank position as fallback) const Position* safePos = botAI->IsTank(bot) ? GetMainTankPosition() : GetMainTankRangedPosition(); @@ -7124,7 +7124,7 @@ const Position* IccLichKingWinterAction::GetMainTankRangedPosition() return &ICC_LK_FROSTR3_POSITION; } -bool IccLichKingWinterAction::IsPositionSafeFromDefile(float x, float y, float z, float minSafeDistance) +bool IccLichKingWinterAction::IsPositionSafeFromDefile(float x, float y, float minSafeDistance) { Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king"); if (!boss) @@ -7190,7 +7190,7 @@ bool IccLichKingWinterAction::TryMoveToPosition(float targetX, float targetY, fl dy /= distance; // First check if direct path is safe - if (bot->IsWithinLOS(targetX, targetY, targetZ) && IsPositionSafeFromDefile(targetX, targetY, targetZ, 3.0f)) + if (bot->IsWithinLOS(targetX, targetY, targetZ) && IsPositionSafeFromDefile(targetX, targetY, 3.0f)) { if (isForced) botAI->Reset(); @@ -7221,7 +7221,7 @@ bool IccLichKingWinterAction::TryMoveToPosition(float targetX, float targetY, fl float testY = currentY + dy * attemptDistance + offsetY * direction; float testZ = targetZ; - if (bot->IsWithinLOS(testX, testY, testZ) && IsPositionSafeFromDefile(testX, testY, testZ, 3.0f)) + if (bot->IsWithinLOS(testX, testY, testZ) && IsPositionSafeFromDefile(testX, testY, 3.0f)) { if (isForced) botAI->Reset(); @@ -7271,7 +7271,7 @@ void IccLichKingWinterAction::HandleTankPositioning() const Position* targetPos = GetMainTankPosition(); // First check if current position is safe - if (!IsPositionSafeFromDefile(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), 3.0f)) + if (!IsPositionSafeFromDefile(bot->GetPositionX(), bot->GetPositionY(), 3.0f)) { // If in defile, prioritize getting out TryMoveToPosition(targetPos->GetPositionX(), targetPos->GetPositionY(), 840.857f, true); @@ -7295,7 +7295,7 @@ void IccLichKingWinterAction::HandleTankPositioning() } // Once in position, handle add management from tank position - HandleMainTankAddManagement(boss, targetPos); + HandleMainTankAddManagement(targetPos); } // ASSIST TANK: More flexible positioning based on add collection else if (botAI->IsAssistTank(bot)) @@ -7312,7 +7312,7 @@ void IccLichKingWinterAction::HandleTankPositioning() } // Handle assist tank add collection and positioning - HandleAssistTankAddManagement(boss, targetPos); + HandleAssistTankAddManagement(targetPos); } } @@ -7405,7 +7405,7 @@ void IccLichKingWinterAction::HandleRangedPositioning() const Position* targetPos = GetMainTankRangedPosition(); // First check if current position is safe - if (!IsPositionSafeFromDefile(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), 3.0f)) + if (!IsPositionSafeFromDefile(bot->GetPositionX(), bot->GetPositionY(), 3.0f)) { // If in defile, prioritize getting out TryMoveToPosition(targetPos->GetPositionX(), targetPos->GetPositionY(), 840.857f, true); @@ -7484,7 +7484,7 @@ void IccLichKingWinterAction::HandleRangedPositioning() } } -void IccLichKingWinterAction::HandleMainTankAddManagement(Unit* boss, const Position* tankPos) +void IccLichKingWinterAction::HandleMainTankAddManagement(const Position* tankPos) { if (!botAI->IsMainTank(bot)) return; @@ -7607,7 +7607,7 @@ void IccLichKingWinterAction::HandleMainTankAddManagement(Unit* boss, const Posi } } -void IccLichKingWinterAction::HandleAssistTankAddManagement(Unit* boss, const Position* tankPos) +void IccLichKingWinterAction::HandleAssistTankAddManagement(const Position* tankPos) { if (!botAI->IsAssistTank(bot)) return; @@ -7812,7 +7812,7 @@ bool IccLichKingAddsAction::Execute(Event /*event*/) return true; // Handle shambling horror interactions - HandleShamblingHorrors(boss, hasPlague); + HandleShamblingHorrors(); // Handle assist tank add management if (HandleAssistTankAddManagement(boss, diff)) @@ -8251,7 +8251,7 @@ bool IccLichKingAddsAction::HandleQuakeMechanics(Unit* boss) return false; } -void IccLichKingAddsAction::HandleShamblingHorrors(Unit* boss, bool hasPlague) +void IccLichKingAddsAction::HandleShamblingHorrors() { // Find closest shambling horror GuidVector npcs2 = AI_VALUE(GuidVector, "nearest hostile npcs"); diff --git a/src/Ai/Raid/Icecrown/Action/RaidIccActions.h b/src/Ai/Raid/Icecrown/Action/RaidIccActions.h index 9ac2a48a0..1d63a4e36 100644 --- a/src/Ai/Raid/Icecrown/Action/RaidIccActions.h +++ b/src/Ai/Raid/Icecrown/Action/RaidIccActions.h @@ -286,7 +286,7 @@ public: bool HandleOozeTargeting(); bool HandleOozeMemberPositioning(); bool PositionRangedAndHealers(Unit* boss,Unit* smallOoze); - bool FindAndMoveFromClosestMember(Unit* boss, Unit* smallOoze); + bool FindAndMoveFromClosestMember(Unit* smallOoze); }; class IccRotfaceMoveAwayFromExplosionAction : public MovementAction @@ -623,12 +623,12 @@ class IccLichKingWinterAction : public AttackAction void HandlePositionCorrection(); bool IsValidCollectibleAdd(Unit* unit); - bool IsPositionSafeFromDefile(float x, float y, float z, float minSafeDistance); + bool IsPositionSafeFromDefile(float x, float y, float minSafeDistance); void HandleTankPositioning(); void HandleMeleePositioning(); void HandleRangedPositioning(); - void HandleMainTankAddManagement(Unit* boss, const Position* tankPos); - void HandleAssistTankAddManagement(Unit* boss, const Position* tankPos); + void HandleMainTankAddManagement(const Position* tankPos); + void HandleAssistTankAddManagement(const Position* tankPos); private: const Position* GetMainTankPosition(); @@ -648,7 +648,7 @@ class IccLichKingAddsAction : public AttackAction void HandleHeroicNonTankPositioning(Difficulty diff, Unit* terenasMenethilHC); void HandleSpiritMarkingAndTargeting(Difficulty diff, Unit* terenasMenethilHC); bool HandleQuakeMechanics(Unit* boss); - void HandleShamblingHorrors(Unit* boss, bool hasPlague); + void HandleShamblingHorrors(); bool HandleAssistTankAddManagement(Unit* boss, Difficulty diff); void HandleMeleePositioning(Unit* boss, bool hasPlague, Difficulty diff); void HandleMainTankTargeting(Unit* boss, Difficulty diff); diff --git a/src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.cpp b/src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.cpp index 40ee354e2..bc304ec0c 100644 --- a/src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.cpp +++ b/src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.cpp @@ -559,7 +559,7 @@ bool IccBqlVampiricBiteTrigger::IsActive() bool IccValkyreSpearTrigger::IsActive() { // Check if there's a spear nearby - if (Creature* spear = bot->FindNearestCreature(NPC_SPEAR, 100.0f)) + if (bot->FindNearestCreature(NPC_SPEAR, 100.0f)) return true; return false; diff --git a/src/Ai/Raid/Karazhan/Action/RaidKarazhanActions.cpp b/src/Ai/Raid/Karazhan/Action/RaidKarazhanActions.cpp index 4e175f0f6..69ef73987 100644 --- a/src/Ai/Raid/Karazhan/Action/RaidKarazhanActions.cpp +++ b/src/Ai/Raid/Karazhan/Action/RaidKarazhanActions.cpp @@ -744,7 +744,7 @@ bool NetherspiteBlockBlueBeamAction::Execute(Event /*event*/) float candidateX = bx + dx * dist; float candidateY = by + dy * dist; float candidateZ = bz; - if (!IsSafePosition(candidateX, candidateY, candidateZ, voidZones, 4.0f)) + if (!IsSafePosition(candidateX, candidateY, voidZones, 4.0f)) continue; float distToIdeal = fabs(dist - idealDistance); @@ -836,7 +836,7 @@ bool NetherspiteBlockGreenBeamAction::Execute(Event /*event*/) float candidateX = bx + dx * dist; float candidateY = by + dy * dist; float candidateZ = bz; - if (!IsSafePosition(candidateX, candidateY, candidateZ, voidZones, 4.0f)) + if (!IsSafePosition(candidateX, candidateY, voidZones, 4.0f)) continue; float distToIdeal = fabs(dist - 18.0f); @@ -873,7 +873,7 @@ bool NetherspiteAvoidBeamAndVoidZoneAction::Execute(Event /*event*/) std::vector voidZones = GetAllVoidZones(botAI, bot); bool nearVoidZone = !IsSafePosition(bot->GetPositionX(), bot->GetPositionY(), - bot->GetPositionZ(), voidZones, 4.0f); + voidZones, 4.0f); std::vector beams; Unit* redPortal = bot->FindNearestCreature(NPC_RED_PORTAL, 150.0f); @@ -922,7 +922,7 @@ bool NetherspiteAvoidBeamAndVoidZoneAction::Execute(Event /*event*/) float cy = botY + std::sin(angle) * dist; float cz = netherspiteZ; - if (!IsSafePosition(cx, cy, cz, voidZones, 4.0f) || + if (!IsSafePosition(cx, cy, voidZones, 4.0f) || !IsAwayFromBeams(cx, cy, beams, netherspite)) continue; diff --git a/src/Ai/Raid/Karazhan/Util/RaidKarazhanHelpers.cpp b/src/Ai/Raid/Karazhan/Util/RaidKarazhanHelpers.cpp index 513920e11..82ecbdb7b 100644 --- a/src/Ai/Raid/Karazhan/Util/RaidKarazhanHelpers.cpp +++ b/src/Ai/Raid/Karazhan/Util/RaidKarazhanHelpers.cpp @@ -272,7 +272,7 @@ namespace KarazhanHelpers return voidZones; } - bool IsSafePosition(float x, float y, float z, const std::vector& hazards, float hazardRadius) + bool IsSafePosition(float x, float y, const std::vector& hazards, float hazardRadius) { for (Unit* hazard : hazards) { @@ -351,7 +351,7 @@ namespace KarazhanHelpers destX, destY, destZ, true)) continue; - if (!IsSafePosition(destX, destY, destZ, hazards, safeDistance)) + if (!IsSafePosition(destX, destY, hazards, safeDistance)) continue; if (requireSafePath) diff --git a/src/Ai/Raid/Karazhan/Util/RaidKarazhanHelpers.h b/src/Ai/Raid/Karazhan/Util/RaidKarazhanHelpers.h index 885af774d..055a93a58 100644 --- a/src/Ai/Raid/Karazhan/Util/RaidKarazhanHelpers.h +++ b/src/Ai/Raid/Karazhan/Util/RaidKarazhanHelpers.h @@ -116,7 +116,7 @@ namespace KarazhanHelpers std::vector GetGreenBlockers(PlayerbotAI* botAI, Player* bot); std::tuple GetCurrentBeamBlockers(PlayerbotAI* botAI, Player* bot); std::vector GetAllVoidZones(PlayerbotAI *botAI, Player* bot); - bool IsSafePosition (float x, float y, float z, const std::vector& hazards, float hazardRadius); + bool IsSafePosition (float x, float y, const std::vector& hazards, float hazardRadius); std::vector GetSpawnedInfernals(PlayerbotAI* botAI); bool IsStraightPathSafe( const Position& start, const Position& target, diff --git a/src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.cpp b/src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.cpp index ffe446965..024ffe4d6 100644 --- a/src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.cpp +++ b/src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.cpp @@ -499,7 +499,7 @@ bool MagtheridonUseManticronCubeAction::Execute(Event /*event*/) return false; // Release cubes after Blast Nova is interrupted - if (HandleCubeRelease(magtheridon, cube)) + if (HandleCubeRelease(magtheridon)) return true; // Check if cube logic should be active (49+ second rule) @@ -520,7 +520,7 @@ bool MagtheridonUseManticronCubeAction::Execute(Event /*event*/) return false; } -bool MagtheridonUseManticronCubeAction::HandleCubeRelease(Unit* magtheridon, GameObject* cube) +bool MagtheridonUseManticronCubeAction::HandleCubeRelease(Unit* magtheridon) { if (bot->HasAura(SPELL_SHADOW_GRASP) && !(magtheridon->HasUnitState(UNIT_STATE_CASTING) && diff --git a/src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.h b/src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.h index d47d06459..7abc493c2 100644 --- a/src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.h +++ b/src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.h @@ -81,7 +81,7 @@ public: bool Execute(Event event) override; private: - bool HandleCubeRelease(Unit* magtheridon, GameObject* cube); + bool HandleCubeRelease(Unit* magtheridon); bool ShouldActivateCubeLogic(Unit* magtheridon); bool HandleWaitingPhase(const MagtheridonHelpers::CubeInfo& cubeInfo); bool HandleCubeInteraction(const MagtheridonHelpers::CubeInfo& cubeInfo, GameObject* cube); diff --git a/src/Ai/Raid/Magtheridon/Util/RaidMagtheridonHelpers.cpp b/src/Ai/Raid/Magtheridon/Util/RaidMagtheridonHelpers.cpp index 344dda5ba..e51ad24e5 100644 --- a/src/Ai/Raid/Magtheridon/Util/RaidMagtheridonHelpers.cpp +++ b/src/Ai/Raid/Magtheridon/Util/RaidMagtheridonHelpers.cpp @@ -118,7 +118,7 @@ namespace MagtheridonHelpers std::unordered_map spreadWaitTimer; std::unordered_map dpsWaitTimer; - bool IsSafeFromMagtheridonHazards(PlayerbotAI* botAI, Player* bot, float x, float y, float z) + bool IsSafeFromMagtheridonHazards(PlayerbotAI* botAI, Player* /*bot*/, float x, float y, float /*z*/) { // Debris std::vector debrisHazards; diff --git a/src/Ai/Raid/Onyxia/Strategy/RaidOnyxiaStrategy.cpp b/src/Ai/Raid/Onyxia/Strategy/RaidOnyxiaStrategy.cpp index b1217f59e..bb61bc004 100644 --- a/src/Ai/Raid/Onyxia/Strategy/RaidOnyxiaStrategy.cpp +++ b/src/Ai/Raid/Onyxia/Strategy/RaidOnyxiaStrategy.cpp @@ -24,7 +24,7 @@ void RaidOnyxiaStrategy::InitTriggers(std::vector& triggers) "ony whelps spawn", { NextAction("ony kill whelps", ACTION_RAID + 1) })); } -void RaidOnyxiaStrategy::InitMultipliers(std::vector& multipliers) +void RaidOnyxiaStrategy::InitMultipliers(std::vector& /*multipliers*/) { // Empty for now } diff --git a/src/Ai/Raid/SerpentshrineCavern/Action/RaidSSCActions.cpp b/src/Ai/Raid/SerpentshrineCavern/Action/RaidSSCActions.cpp index 0b31a1c13..68dbc8b62 100644 --- a/src/Ai/Raid/SerpentshrineCavern/Action/RaidSSCActions.cpp +++ b/src/Ai/Raid/SerpentshrineCavern/Action/RaidSSCActions.cpp @@ -1466,16 +1466,16 @@ bool MorogrimTidewalkerMoveBossToTankPositionAction::Execute(Event /*event*/) if (tidewalker->GetVictim() == bot && bot->IsWithinMeleeRange(tidewalker)) { if (tidewalker->GetHealthPct() > 26.0f) - return MoveToPhase1TankPosition(tidewalker); + return MoveToPhase1TankPosition(); else - return MoveToPhase2TankPosition(tidewalker); + return MoveToPhase2TankPosition(); } return false; } // Phase 1: tank position is up against the Northeast pillar -bool MorogrimTidewalkerMoveBossToTankPositionAction::MoveToPhase1TankPosition(Unit* tidewalker) +bool MorogrimTidewalkerMoveBossToTankPositionAction::MoveToPhase1TankPosition() { const Position& phase1 = TIDEWALKER_PHASE_1_TANK_POSITION; float distToPhase1 = bot->GetExactDist2d(phase1.GetPositionX(), phase1.GetPositionY()); @@ -1495,7 +1495,7 @@ bool MorogrimTidewalkerMoveBossToTankPositionAction::MoveToPhase1TankPosition(Un } // Phase 2: move in two steps to get around the pillar and back up into the Northeast corner -bool MorogrimTidewalkerMoveBossToTankPositionAction::MoveToPhase2TankPosition(Unit* tidewalker) +bool MorogrimTidewalkerMoveBossToTankPositionAction::MoveToPhase2TankPosition() { const Position& phase2 = TIDEWALKER_PHASE_2_TANK_POSITION; const Position& transition = TIDEWALKER_PHASE_TRANSITION_WAYPOINT; @@ -2151,7 +2151,7 @@ bool LadyVashjPassTheTaintedCoreAction::Execute(Event /*event*/) { // Passer order: HealAssistantOfIndex 0, 1, 2, then RangedDpsAssistantOfIndex 0 if (bot == firstCorePasser && - LineUpFirstCorePasser(designatedLooter, closestTrigger)) + LineUpFirstCorePasser(designatedLooter)) { return true; } @@ -2176,7 +2176,7 @@ bool LadyVashjPassTheTaintedCoreAction::Execute(Event /*event*/) // Designated core looter logic // Applicable only if cheat mode is on and thus looter is a bot if (bot == designatedLooter && - IsFirstCorePasserInPosition(designatedLooter, firstCorePasser, closestTrigger)) + IsFirstCorePasserInPosition(firstCorePasser)) { const time_t now = std::time(nullptr); auto it = lastImbueAttempt.find(instanceId); @@ -2192,7 +2192,7 @@ bool LadyVashjPassTheTaintedCoreAction::Execute(Event /*event*/) // First core passer: receive core from looter at the top of the stairs, // pass to second core passer else if (bot == firstCorePasser && - IsSecondCorePasserInPosition(firstCorePasser, secondCorePasser, closestTrigger)) + IsSecondCorePasserInPosition(secondCorePasser)) { const time_t now = std::time(nullptr); auto it = lastImbueAttempt.find(instanceId); @@ -2209,7 +2209,7 @@ bool LadyVashjPassTheTaintedCoreAction::Execute(Event /*event*/) // of the first passer, move to the generator; otherwise, move as close as // possible to the generator while staying in passing range else if (bot == secondCorePasser && !UseCoreOnNearestGenerator(instanceId) && - IsThirdCorePasserInPosition(secondCorePasser, thirdCorePasser, closestTrigger)) + IsThirdCorePasserInPosition(thirdCorePasser)) { const time_t now = std::time(nullptr); auto it = lastImbueAttempt.find(instanceId); @@ -2226,7 +2226,7 @@ bool LadyVashjPassTheTaintedCoreAction::Execute(Event /*event*/) // of the second passer, move to the generator; otherwise, move as close as // possible to the generator while staying in passing range else if (bot == thirdCorePasser && !UseCoreOnNearestGenerator(instanceId) && - IsFourthCorePasserInPosition(thirdCorePasser, fourthCorePasser, closestTrigger)) + IsFourthCorePasserInPosition(fourthCorePasser)) { const time_t now = std::time(nullptr); auto it = lastImbueAttempt.find(instanceId); @@ -2249,7 +2249,7 @@ bool LadyVashjPassTheTaintedCoreAction::Execute(Event /*event*/) } bool LadyVashjPassTheTaintedCoreAction::LineUpFirstCorePasser( - Player* designatedLooter, Unit* closestTrigger) + Player* designatedLooter) { const float centerX = VASHJ_PLATFORM_CENTER_POSITION.GetPositionX(); const float centerY = VASHJ_PLATFORM_CENTER_POSITION.GetPositionY(); @@ -2288,8 +2288,6 @@ bool LadyVashjPassTheTaintedCoreAction::LineUpSecondCorePasser( if (itFirst == intendedLineup.end()) return false; - const Position& firstLineup = itFirst->second; - auto itSecond = intendedLineup.find(bot->GetGUID()); if (itSecond == intendedLineup.end()) { @@ -2343,9 +2341,9 @@ bool LadyVashjPassTheTaintedCoreAction::LineUpThirdCorePasser( Player* secondCorePasser, Unit* closestTrigger) { bool needThirdPasser = - (IsFirstCorePasserInPosition(designatedLooter, firstCorePasser, closestTrigger) && + (IsFirstCorePasserInPosition(firstCorePasser) && firstCorePasser->GetExactDist2d(closestTrigger) > 42.0f) || - (IsSecondCorePasserInPosition(firstCorePasser, secondCorePasser, closestTrigger) && + (IsSecondCorePasserInPosition(secondCorePasser) && secondCorePasser->GetExactDist2d(closestTrigger) > 4.0f); if (!needThirdPasser) @@ -2408,9 +2406,9 @@ bool LadyVashjPassTheTaintedCoreAction::LineUpFourthCorePasser( Player* thirdCorePasser, Unit* closestTrigger) { bool needFourthPasser = - (IsSecondCorePasserInPosition(firstCorePasser, secondCorePasser, closestTrigger) && + (IsSecondCorePasserInPosition(secondCorePasser) && secondCorePasser->GetExactDist2d(closestTrigger) > 42.0f) || - (IsThirdCorePasserInPosition(secondCorePasser, thirdCorePasser, closestTrigger) && + (IsThirdCorePasserInPosition(thirdCorePasser) && thirdCorePasser->GetExactDist2d(closestTrigger) > 4.0f); if (!needFourthPasser) @@ -2460,8 +2458,7 @@ bool LadyVashjPassTheTaintedCoreAction::LineUpFourthCorePasser( // The next four functions check if the respective passer is <= 2 yards of their intended // position and are used to determine when the prior bot in the chain can pass the core -bool LadyVashjPassTheTaintedCoreAction::IsFirstCorePasserInPosition( - Player* designatedLooter, Player* firstCorePasser, Unit* closestTrigger) +bool LadyVashjPassTheTaintedCoreAction::IsFirstCorePasserInPosition(Player* firstCorePasser) { auto itSnap = intendedLineup.find(firstCorePasser->GetGUID()); if (itSnap != intendedLineup.end()) @@ -2474,8 +2471,7 @@ bool LadyVashjPassTheTaintedCoreAction::IsFirstCorePasserInPosition( return false; } -bool LadyVashjPassTheTaintedCoreAction::IsSecondCorePasserInPosition( - Player* firstCorePasser, Player* secondCorePasser, Unit* closestTrigger) +bool LadyVashjPassTheTaintedCoreAction::IsSecondCorePasserInPosition(Player* secondCorePasser) { auto itSnap = intendedLineup.find(secondCorePasser->GetGUID()); if (itSnap != intendedLineup.end()) @@ -2488,8 +2484,7 @@ bool LadyVashjPassTheTaintedCoreAction::IsSecondCorePasserInPosition( return false; } -bool LadyVashjPassTheTaintedCoreAction::IsThirdCorePasserInPosition( - Player* secondCorePasser, Player* thirdCorePasser, Unit* closestTrigger) +bool LadyVashjPassTheTaintedCoreAction::IsThirdCorePasserInPosition(Player* thirdCorePasser) { auto itSnap = intendedLineup.find(thirdCorePasser->GetGUID()); if (itSnap != intendedLineup.end()) @@ -2502,8 +2497,7 @@ bool LadyVashjPassTheTaintedCoreAction::IsThirdCorePasserInPosition( return false; } -bool LadyVashjPassTheTaintedCoreAction::IsFourthCorePasserInPosition( - Player* thirdCorePasser, Player* fourthCorePasser, Unit* closestTrigger) +bool LadyVashjPassTheTaintedCoreAction::IsFourthCorePasserInPosition(Player* fourthCorePasser) { auto itSnap = intendedLineup.find(fourthCorePasser->GetGUID()); if (itSnap != intendedLineup.end()) diff --git a/src/Ai/Raid/SerpentshrineCavern/Action/RaidSSCActions.h b/src/Ai/Raid/SerpentshrineCavern/Action/RaidSSCActions.h index 08ad4c48a..9443354c4 100644 --- a/src/Ai/Raid/SerpentshrineCavern/Action/RaidSSCActions.h +++ b/src/Ai/Raid/SerpentshrineCavern/Action/RaidSSCActions.h @@ -312,8 +312,8 @@ public: bool Execute(Event event) override; private: - bool MoveToPhase1TankPosition(Unit* tidewalker); - bool MoveToPhase2TankPosition(Unit* tidewalker); + bool MoveToPhase1TankPosition(); + bool MoveToPhase2TankPosition(); }; class MorogrimTidewalkerPhase2RepositionRangedAction : public MovementAction @@ -414,14 +414,14 @@ public: bool Execute(Event event) override; private: - bool LineUpFirstCorePasser(Player* designatedLooter, Unit* closestTrigger); + bool LineUpFirstCorePasser(Player* designatedLooter); bool LineUpSecondCorePasser(Player* firstCorePasser, Unit* closestTrigger); bool LineUpThirdCorePasser(Player* designatedLooter, Player* firstCorePasser, Player* secondCorePasser, Unit* closestTrigger); bool LineUpFourthCorePasser(Player* firstCorePasser, Player* secondCorePasser, Player* thirdCorePasser, Unit* closestTrigger); - bool IsFirstCorePasserInPosition(Player* designatedLooter, Player* firstCorePasser, Unit* closestTrigger); - bool IsSecondCorePasserInPosition(Player* firstCorePasser, Player* secondCorePasser, Unit* closestTrigger); - bool IsThirdCorePasserInPosition(Player* secondCorePasser, Player* thirdCorePasser, Unit* closestTrigger); - bool IsFourthCorePasserInPosition(Player* thirdCorePasser, Player* fourthCorePasser, Unit* closestTrigger); + bool IsFirstCorePasserInPosition(Player* firstCorePasser); + bool IsSecondCorePasserInPosition(Player* secondCorePasser); + bool IsThirdCorePasserInPosition(Player* thirdCorePasser); + bool IsFourthCorePasserInPosition(Player* fourthCorePasser); void ScheduleTransferCoreAfterImbue(PlayerbotAI* botAI, Player* giver, Player* receiver); bool UseCoreOnNearestGenerator(const uint32 instanceId); }; diff --git a/src/Ai/Raid/SerpentshrineCavern/Multiplier/RaidSSCMultipliers.cpp b/src/Ai/Raid/SerpentshrineCavern/Multiplier/RaidSSCMultipliers.cpp index c841c7dcb..f43ef4667 100644 --- a/src/Ai/Raid/SerpentshrineCavern/Multiplier/RaidSSCMultipliers.cpp +++ b/src/Ai/Raid/SerpentshrineCavern/Multiplier/RaidSSCMultipliers.cpp @@ -690,13 +690,11 @@ float LadyVashjCorePassersPrioritizePositioningMultiplier::GetValue(Action* acti auto coreHandlers = GetCoreHandlers(botAI, bot); bool isCoreHandler = false; - int myIndex = -1; for (int i = 0; i < static_cast(coreHandlers.size()); ++i) { if (coreHandlers[i] && coreHandlers[i] == bot) { isCoreHandler = true; - myIndex = i; } } if (!isCoreHandler) diff --git a/src/Ai/Raid/TempestKeep/Action/RaidTempestKeepActions.cpp b/src/Ai/Raid/TempestKeep/Action/RaidTempestKeepActions.cpp index edcc7ad58..c31a5e7ad 100644 --- a/src/Ai/Raid/TempestKeep/Action/RaidTempestKeepActions.cpp +++ b/src/Ai/Raid/TempestKeep/Action/RaidTempestKeepActions.cpp @@ -213,7 +213,7 @@ bool AlarAssistTanksPickUpEmbersAction::Execute(Event /*event*/) if (!isAlarInPhase2[alar->GetMap()->GetInstanceId()]) return HandlePhase1Embers(alar); else - return HandlePhase2Embers(alar); + return HandlePhase2Embers(); } // Embers will be tanked by only the second assist tank in Phase 1 @@ -275,7 +275,7 @@ bool AlarAssistTanksPickUpEmbersAction::HandlePhase1Embers(Unit* alar) // One Ember will be tanked by the second assist tank in Phase 2, and the other by // the main tank or first assist tank (whichever is not tanking Al'ar) -bool AlarAssistTanksPickUpEmbersAction::HandlePhase2Embers(Unit* alar) +bool AlarAssistTanksPickUpEmbersAction::HandlePhase2Embers() { auto [firstEmber, secondEmber] = GetFirstTwoEmbersOfAlar(botAI); diff --git a/src/Ai/Raid/TempestKeep/Action/RaidTempestKeepActions.h b/src/Ai/Raid/TempestKeep/Action/RaidTempestKeepActions.h index 8b021304e..41b087e3c 100644 --- a/src/Ai/Raid/TempestKeep/Action/RaidTempestKeepActions.h +++ b/src/Ai/Raid/TempestKeep/Action/RaidTempestKeepActions.h @@ -66,7 +66,7 @@ public: private: bool HandlePhase1Embers(Unit* alar); - bool HandlePhase2Embers(Unit* alar); + bool HandlePhase2Embers(); }; class AlarRangedDpsPrioritizeEmbersAction : public AttackAction diff --git a/src/Ai/Raid/Ulduar/Action/RaidUlduarActions.cpp b/src/Ai/Raid/Ulduar/Action/RaidUlduarActions.cpp index 798b77904..217ab2c06 100644 --- a/src/Ai/Raid/Ulduar/Action/RaidUlduarActions.cpp +++ b/src/Ai/Raid/Ulduar/Action/RaidUlduarActions.cpp @@ -2332,7 +2332,6 @@ bool MimironRocketStrikeAction::isUseful() bool MimironRocketStrikeAction::Execute(Event /*event*/) { - Unit* leviathanMkII = nullptr; Unit* vx001 = nullptr; Unit* aerialCommandUnit = nullptr; @@ -2344,9 +2343,7 @@ bool MimironRocketStrikeAction::Execute(Event /*event*/) if (!target || !target->IsAlive()) continue; - if (target->GetEntry() == NPC_LEVIATHAN_MKII) - leviathanMkII = target; - else if (target->GetEntry() == NPC_VX001) + if (target->GetEntry() == NPC_VX001) vx001 = target; else if (target->GetEntry() == NPC_AERIAL_COMMAND_UNIT) aerialCommandUnit = target; diff --git a/src/Ai/Raid/ZulAman/Multiplier/RaidZulAmanMultipliers.cpp b/src/Ai/Raid/ZulAman/Multiplier/RaidZulAmanMultipliers.cpp index e7d16920f..b727681a4 100644 --- a/src/Ai/Raid/ZulAman/Multiplier/RaidZulAmanMultipliers.cpp +++ b/src/Ai/Raid/ZulAman/Multiplier/RaidZulAmanMultipliers.cpp @@ -157,7 +157,7 @@ float JanalaiStayAwayFromFireBombsMultiplier::GetValue(Action* action) if (!AI_VALUE2(Unit*, "find target", "jan'alai")) return 1.0f; - if (!HasFireBombNearby(botAI, bot)) + if (!HasFireBombNearby(bot)) return 1.0f; if (dynamic_cast(action) || diff --git a/src/Ai/Raid/ZulAman/Trigger/RaidZulAmanTriggers.cpp b/src/Ai/Raid/ZulAman/Trigger/RaidZulAmanTriggers.cpp index ad338a5a7..22b072702 100644 --- a/src/Ai/Raid/ZulAman/Trigger/RaidZulAmanTriggers.cpp +++ b/src/Ai/Raid/ZulAman/Trigger/RaidZulAmanTriggers.cpp @@ -108,7 +108,7 @@ bool JanalaiBossEngagedByTanksTrigger::IsActive() !AI_VALUE2(Unit*, "find target", "jan'alai")) return false; - return !HasFireBombNearby(botAI, bot); + return !HasFireBombNearby(bot); } bool JanalaiBossCastsFlameBreathTrigger::IsActive() @@ -118,13 +118,13 @@ bool JanalaiBossCastsFlameBreathTrigger::IsActive() AI_VALUE2(Unit*, "find target", "amani dragonhawk hatchling")) return false; - return !HasFireBombNearby(botAI, bot); + return !HasFireBombNearby(bot); } bool JanalaiBossSummoningFireBombsTrigger::IsActive() { return AI_VALUE2(Unit*, "find target", "jan'alai") && - HasFireBombNearby(botAI, bot); + HasFireBombNearby(bot); } bool JanalaiAmanishiHatchersSpawnedTrigger::IsActive() diff --git a/src/Ai/Raid/ZulAman/Util/RaidZulAmanHelpers.cpp b/src/Ai/Raid/ZulAman/Util/RaidZulAmanHelpers.cpp index 77c268817..eeff879a4 100644 --- a/src/Ai/Raid/ZulAman/Util/RaidZulAmanHelpers.cpp +++ b/src/Ai/Raid/ZulAman/Util/RaidZulAmanHelpers.cpp @@ -143,7 +143,7 @@ namespace ZulAmanHelpers // Jan'alai const Position JANALAI_TANK_POSITION = { -33.873f, 1149.571f, 19.146f }; - bool HasFireBombNearby(PlayerbotAI* botAI, Player* bot) + bool HasFireBombNearby(Player* bot) { constexpr float searchRadius = 30.0f; std::list creatureList; diff --git a/src/Ai/Raid/ZulAman/Util/RaidZulAmanHelpers.h b/src/Ai/Raid/ZulAman/Util/RaidZulAmanHelpers.h index 4c27f0238..5da447a7e 100644 --- a/src/Ai/Raid/ZulAman/Util/RaidZulAmanHelpers.h +++ b/src/Ai/Raid/ZulAman/Util/RaidZulAmanHelpers.h @@ -97,7 +97,7 @@ namespace ZulAmanHelpers // Jan'alai extern const Position JANALAI_TANK_POSITION; - bool HasFireBombNearby(PlayerbotAI* botAI, Player* bot); + bool HasFireBombNearby(Player* bot); std::pair GetAmanishiHatcherPair(PlayerbotAI* botAI); // Halazzi diff --git a/src/Bot/Engine/BuildSharedActionContexts.cpp b/src/Bot/Engine/BuildSharedActionContexts.cpp index 8fbb6c135..7e243eadb 100644 --- a/src/Bot/Engine/BuildSharedActionContexts.cpp +++ b/src/Bot/Engine/BuildSharedActionContexts.cpp @@ -18,6 +18,7 @@ #include "Ai/Raid/Ulduar/RaidUlduarActionContext.h" #include "Ai/Raid/Onyxia/RaidOnyxiaActionContext.h" #include "Ai/Raid/Icecrown/RaidIccActionContext.h" +#include "Ai/Dungeon/TbcDungeonActionContext.h" #include "Ai/Dungeon/WotlkDungeonActionContext.h" void AiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList& actionContexts) @@ -41,6 +42,7 @@ void AiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList& triggerContexts) @@ -41,6 +42,7 @@ void AiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextList::iterator i = strategies.find(name); + return i != strategies.end() ? i->second : nullptr; +} + void Engine::ProcessTriggers(bool minimal) { std::unordered_map fires; diff --git a/src/Bot/Engine/Engine.h b/src/Bot/Engine/Engine.h index 8a7c34189..976252cc5 100644 --- a/src/Bot/Engine/Engine.h +++ b/src/Bot/Engine/Engine.h @@ -70,6 +70,7 @@ public: void addStrategiesNoInit(std::string first, ...); bool removeStrategy(std::string const name, bool init = true); bool HasStrategy(std::string const name); + Strategy* GetStrategy(std::string const name); void removeAllStrategies(); void toggleStrategy(std::string const name); std::string const ListStrategies(); diff --git a/src/Bot/Factory/AiFactory.cpp b/src/Bot/Factory/AiFactory.cpp index a821886f9..6c638e807 100644 --- a/src/Bot/Factory/AiFactory.cpp +++ b/src/Bot/Factory/AiFactory.cpp @@ -315,7 +315,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa break; case CLASS_WARRIOR: if (tab == WARRIOR_TAB_PROTECTION) - engine->addStrategiesNoInit("tank", "tank assist", "aoe", nullptr); + engine->addStrategiesNoInit("tank", "tank assist", "pull", "pull back", "aoe", nullptr); else if (tab == WARRIOR_TAB_ARMS || !player->HasSpell(1680)) // Whirlwind engine->addStrategiesNoInit("arms", "aoe", "dps assist", nullptr); else @@ -333,7 +333,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa break; case CLASS_PALADIN: if (tab == PALADIN_TAB_PROTECTION) - engine->addStrategiesNoInit("tank", "tank assist", "bthreat", "barmor", "cure", nullptr); + engine->addStrategiesNoInit("tank", "tank assist", "pull", "pull back", "bthreat", "barmor", "cure", nullptr); else if (tab == PALADIN_TAB_HOLY) engine->addStrategiesNoInit("heal", "dps assist", "cure", "bcast", nullptr); else @@ -352,7 +352,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa if (player->HasSpell(768) /*cat form*/ && !player->HasAura(16931) /*thick hide*/) engine->addStrategiesNoInit("cat", "dps assist", nullptr); else - engine->addStrategiesNoInit("bear", "tank assist", nullptr); + engine->addStrategiesNoInit("bear", "tank assist", "pull", "pull back", nullptr); } break; case CLASS_HUNTER: @@ -383,7 +383,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa break; case CLASS_DEATH_KNIGHT: if (tab == DEATH_KNIGHT_TAB_BLOOD) - engine->addStrategiesNoInit("blood", "tank assist", nullptr); + engine->addStrategiesNoInit("blood", "tank assist", "pull", "pull back", nullptr); else if (tab == DEATH_KNIGHT_TAB_FROST) engine->addStrategiesNoInit("frost", "frost aoe", "dps assist", nullptr); else @@ -510,7 +510,7 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const case CLASS_PALADIN: if (tab == PALADIN_TAB_PROTECTION) { - nonCombatEngine->addStrategiesNoInit("bthreat", "tank assist", "barmor", nullptr); + nonCombatEngine->addStrategiesNoInit("bthreat", "tank assist", "pull", "barmor", nullptr); if (player->GetLevel() >= 20) nonCombatEngine->addStrategy("bhealth", false); else @@ -548,14 +548,14 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const if (player->GetLevel() >= 20 && !player->HasAura(16931) /*thick hide*/) nonCombatEngine->addStrategy("dps assist", false); else - nonCombatEngine->addStrategy("tank assist", false); + nonCombatEngine->addStrategiesNoInit("tank assist", "pull", nullptr); } else nonCombatEngine->addStrategiesNoInit("dps assist", "cure", nullptr); break; case CLASS_WARRIOR: if (tab == WARRIOR_TAB_PROTECTION) - nonCombatEngine->addStrategy("tank assist", false); + nonCombatEngine->addStrategiesNoInit("tank assist", "pull", nullptr); else nonCombatEngine->addStrategy("dps assist", false); break; @@ -571,7 +571,7 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const break; case CLASS_DEATH_KNIGHT: if (tab == DEATH_KNIGHT_TAB_BLOOD) - nonCombatEngine->addStrategy("tank assist", false); + nonCombatEngine->addStrategiesNoInit("tank assist", "pull", nullptr); else nonCombatEngine->addStrategy("dps assist", false); break; diff --git a/src/Bot/Factory/PlayerbotFactory.cpp b/src/Bot/Factory/PlayerbotFactory.cpp index 11f301feb..f8e0e5dfe 100644 --- a/src/Bot/Factory/PlayerbotFactory.cpp +++ b/src/Bot/Factory/PlayerbotFactory.cpp @@ -5,6 +5,7 @@ #include "PlayerbotFactory.h" +#include #include #include "AccountMgr.h" @@ -47,16 +48,276 @@ static std::vector initSlotsOrder = {EQUIPMENT_SLOT_TRINKET1, EQUIPMENT_ EQUIPMENT_SLOT_LEGS, EQUIPMENT_SLOT_HANDS, EQUIPMENT_SLOT_NECK, EQUIPMENT_SLOT_BODY, EQUIPMENT_SLOT_WAIST, EQUIPMENT_SLOT_FEET, EQUIPMENT_SLOT_WRISTS, EQUIPMENT_SLOT_FINGER1, EQUIPMENT_SLOT_FINGER2, EQUIPMENT_SLOT_BACK}; -uint32 PlayerbotFactory::tradeSkills[] = {SKILL_ALCHEMY, SKILL_ENCHANTING, SKILL_SKINNING, SKILL_TAILORING, - SKILL_LEATHERWORKING, SKILL_ENGINEERING, SKILL_HERBALISM, SKILL_MINING, - SKILL_BLACKSMITHING, SKILL_COOKING, SKILL_FIRST_AID, SKILL_FISHING, - SKILL_JEWELCRAFTING}; +uint32 PlayerbotFactory::tradeSkills[] = {SKILL_ALCHEMY, SKILL_ENCHANTING, SKILL_SKINNING, + SKILL_TAILORING, SKILL_LEATHERWORKING, SKILL_ENGINEERING, + SKILL_HERBALISM, SKILL_INSCRIPTION, SKILL_MINING, + SKILL_BLACKSMITHING, SKILL_COOKING, SKILL_FIRST_AID, + SKILL_FISHING, SKILL_JEWELCRAFTING}; std::list PlayerbotFactory::classQuestIds; std::list PlayerbotFactory::specialQuestIds; std::vector PlayerbotFactory::enchantSpellIdCache; std::vector PlayerbotFactory::enchantGemIdCache; std::unordered_map> PlayerbotFactory::trainerIdCache; +std::vector PlayerbotFactory::ccBreakTrinketCache; + +bool PlayerbotFactory::IsPrimaryTradeSkill(uint16 skillId) +{ + SkillLineEntry const* skillLine = sSkillLineStore.LookupEntry(skillId); + return skillLine && skillLine->categoryId == SKILL_CATEGORY_PROFESSION; +} + +bool PlayerbotFactory::IsGatheringTradeSkill(uint16 skillId) +{ + switch (skillId) + { + case SKILL_HERBALISM: + case SKILL_MINING: + case SKILL_SKINNING: + return true; + default: + return false; + } +} + +bool PlayerbotFactory::IsCraftingTradeSkill(uint16 skillId) +{ + return IsPrimaryTradeSkill(skillId) && !IsGatheringTradeSkill(skillId); +} + +uint32 PlayerbotFactory::GetProfessionStarterSpell(uint16 skillId) +{ + static constexpr std::array, 14> ProfessionStarterSpells = {{ + {SKILL_ALCHEMY, 2259}, + {SKILL_BLACKSMITHING, 2018}, + {SKILL_COOKING, 2550}, + {SKILL_ENCHANTING, 7411}, + {SKILL_ENGINEERING, 4036}, + {SKILL_FIRST_AID, 3273}, + {SKILL_FISHING, 7620}, + {SKILL_HERBALISM, 2366}, + {SKILL_INSCRIPTION, 45357}, + {SKILL_JEWELCRAFTING, 25229}, + {SKILL_LEATHERWORKING, 2108}, + {SKILL_MINING, 2575}, + {SKILL_SKINNING, 8613}, + {SKILL_TAILORING, 3908} + }}; + + for (auto const& [professionSkill, starterSpell] : ProfessionStarterSpells) + { + if (professionSkill == skillId) + return starterSpell; + } + + return 0; +} + +std::vector PlayerbotFactory::GetClassProfessionPairs(Player* bot) +{ + switch (bot->getClass()) + { + case CLASS_WARRIOR: + return {{SKILL_MINING, SKILL_BLACKSMITHING, 45}, + {SKILL_MINING, SKILL_ENGINEERING, 30}, + {SKILL_MINING, SKILL_JEWELCRAFTING, 15}, + {SKILL_HERBALISM, SKILL_ALCHEMY, 10}}; + case CLASS_PALADIN: + return {{SKILL_MINING, SKILL_BLACKSMITHING, 45}, + {SKILL_MINING, SKILL_JEWELCRAFTING, 30}, + {SKILL_MINING, SKILL_ENGINEERING, 15}, + {SKILL_HERBALISM, SKILL_ALCHEMY, 10}}; + case CLASS_DEATH_KNIGHT: + return {{SKILL_MINING, SKILL_BLACKSMITHING, 45}, + {SKILL_MINING, SKILL_ENGINEERING, 35}, + {SKILL_MINING, SKILL_JEWELCRAFTING, 20}}; + case CLASS_HUNTER: + return {{SKILL_SKINNING, SKILL_LEATHERWORKING, 45}, + {SKILL_MINING, SKILL_ENGINEERING, 35}, + {SKILL_HERBALISM, SKILL_ALCHEMY, 10}, + {SKILL_MINING, SKILL_JEWELCRAFTING, 10}}; + case CLASS_ROGUE: + return {{SKILL_SKINNING, SKILL_LEATHERWORKING, 35}, + {SKILL_HERBALISM, SKILL_ALCHEMY, 25}, + {SKILL_MINING, SKILL_ENGINEERING, 25}, + {SKILL_MINING, SKILL_JEWELCRAFTING, 10}, + {SKILL_HERBALISM, SKILL_INSCRIPTION, 5}}; + case CLASS_DRUID: + return {{SKILL_SKINNING, SKILL_LEATHERWORKING, 35}, + {SKILL_HERBALISM, SKILL_ALCHEMY, 35}, + {SKILL_HERBALISM, SKILL_INSCRIPTION, 20}, + {SKILL_MINING, SKILL_JEWELCRAFTING, 10}}; + case CLASS_SHAMAN: + return {{SKILL_HERBALISM, SKILL_ALCHEMY, 35}, + {SKILL_SKINNING, SKILL_LEATHERWORKING, 25}, + {SKILL_HERBALISM, SKILL_INSCRIPTION, 25}, + {SKILL_MINING, SKILL_JEWELCRAFTING, 15}}; + case CLASS_PRIEST: + return {{SKILL_TAILORING, SKILL_ENCHANTING, 45}, + {SKILL_HERBALISM, SKILL_INSCRIPTION, 30}, + {SKILL_HERBALISM, SKILL_ALCHEMY, 25}}; + case CLASS_MAGE: + return {{SKILL_TAILORING, SKILL_ENCHANTING, 50}, + {SKILL_HERBALISM, SKILL_ALCHEMY, 25}, + {SKILL_HERBALISM, SKILL_INSCRIPTION, 25}}; + case CLASS_WARLOCK: + default: + return {{SKILL_TAILORING, SKILL_ENCHANTING, 50}, + {SKILL_HERBALISM, SKILL_ALCHEMY, 25}, + {SKILL_HERBALISM, SKILL_INSCRIPTION, 25}}; + } +} + +std::vector PlayerbotFactory::GetRandomProfessionPairs() +{ + return {{SKILL_MINING, SKILL_BLACKSMITHING, 20}, + {SKILL_MINING, SKILL_ENGINEERING, 18}, + {SKILL_MINING, SKILL_JEWELCRAFTING, 16}, + {SKILL_SKINNING, SKILL_LEATHERWORKING, 18}, + {SKILL_HERBALISM, SKILL_ALCHEMY, 18}, + {SKILL_HERBALISM, SKILL_INSCRIPTION, 14}, + {SKILL_TAILORING, SKILL_ENCHANTING, 10}, + {SKILL_HERBALISM, SKILL_MINING, 6}, + {SKILL_HERBALISM, SKILL_SKINNING, 5}, + {SKILL_MINING, SKILL_SKINNING, 5}}; +} + +std::pair PlayerbotFactory::ChooseProfessionPair( + std::vector const& professionPairs) +{ + uint32 totalWeight = 0; + for (WeightedProfessionPair const& pair : professionPairs) + totalWeight += pair.weight; + + if (!totalWeight) + return {SKILL_HERBALISM, SKILL_ALCHEMY}; + + uint32 roll = urand(1, totalWeight); + for (WeightedProfessionPair const& pair : professionPairs) + { + if (roll <= pair.weight) + return {pair.firstSkill, pair.secondSkill}; + + roll -= pair.weight; + } + + WeightedProfessionPair const& fallback = professionPairs.back(); + return {fallback.firstSkill, fallback.secondSkill}; +} + +bool PlayerbotFactory::HasProfessionPair(std::vector const& professionPairs, + uint16 firstSkill, uint16 secondSkill) +{ + for (WeightedProfessionPair const& pair : professionPairs) + { + if (pair.firstSkill == firstSkill && pair.secondSkill == secondSkill) + return true; + } + + return false; +} + +uint16 PlayerbotFactory::ChooseSingleProfession(std::vector const& professionPairs) +{ + std::vector> gatheringSkills; + std::vector> craftingSkills; + + auto addWeightedSkill = [](std::vector>& skills, uint16 skillId, uint32 weight) + { + for (std::pair& skill : skills) + { + if (skill.first == skillId) + { + skill.second += weight; + return; + } + } + + skills.push_back({skillId, weight}); + }; + + for (WeightedProfessionPair const& pair : professionPairs) + { + if (IsGatheringTradeSkill(pair.firstSkill)) + addWeightedSkill(gatheringSkills, pair.firstSkill, pair.weight); + if (IsCraftingTradeSkill(pair.firstSkill)) + addWeightedSkill(craftingSkills, pair.firstSkill, pair.weight); + + if (IsGatheringTradeSkill(pair.secondSkill)) + addWeightedSkill(gatheringSkills, pair.secondSkill, pair.weight); + if (IsCraftingTradeSkill(pair.secondSkill)) + addWeightedSkill(craftingSkills, pair.secondSkill, pair.weight); + } + + std::vector>* selectedPool = nullptr; + if (!gatheringSkills.empty() && !craftingSkills.empty()) + selectedPool = urand(0, 1) == 0 ? &gatheringSkills : &craftingSkills; + else if (!gatheringSkills.empty()) + selectedPool = &gatheringSkills; + else if (!craftingSkills.empty()) + selectedPool = &craftingSkills; + + if (!selectedPool || selectedPool->empty()) + return SKILL_HERBALISM; + + uint32 totalWeight = 0; + for (std::pair const& skill : *selectedPool) + totalWeight += skill.second; + + if (!totalWeight) + return selectedPool->front().first; + + uint32 roll = urand(1, totalWeight); + for (std::pair const& skill : *selectedPool) + { + if (roll <= skill.second) + return skill.first; + + roll -= skill.second; + } + + return selectedPool->back().first; +} + +uint32 PlayerbotFactory::GetStoredOrRandomValue(Player* bot, + std::string const& key, + uint32 minValue, + uint32 maxValue) +{ + uint32 value = sRandomPlayerbotMgr.GetValue(bot, key); + if (value < minValue || value > maxValue) + { + value = urand(minValue, maxValue); + sRandomPlayerbotMgr.SetValue(bot, key, value); + } + + return value; +} + +bool PlayerbotFactory::HasAnySpell(Player* bot, std::vector const& spells) +{ + for (uint32 spellId : spells) + { + if (bot->HasSpell(spellId)) + return true; + } + + return false; +} + +bool PlayerbotFactory::LearnProfessionSpecialization(Player* bot, + ProfessionSpecializationSpell knownSpell, + ProfessionSpecializationSpell learnSpell) +{ + uint32 const knownSpellId = static_cast(knownSpell); + uint32 const learnSpellId = static_cast(learnSpell); + + if (bot->HasSpell(knownSpellId) || !sSpellMgr->GetSpellInfo(learnSpellId)) + return false; + + bot->CastSpell(bot, learnSpellId, true); + return bot->HasSpell(knownSpellId); +} PlayerbotFactory::PlayerbotFactory(Player* bot, uint32 level, uint32 itemQuality, uint32 gearScoreLimit) : level(level), itemQuality(itemQuality), gearScoreLimit(gearScoreLimit), bot(bot) @@ -200,6 +461,69 @@ void PlayerbotFactory::Init() enchantGemIdCache.push_back(gemId); } LOG_INFO("playerbots", "Loading {} enchantment gems", enchantGemIdCache.size()); + + BuildCcBreakTrinketCache(); +} + +void PlayerbotFactory::BuildCcBreakTrinketCache() +{ + ccBreakTrinketCache.clear(); + // Spell 42292: removes all movement-impairing and loss-of-control effects — the PvP trinket spell. + QueryResult result = WorldDatabase.Query( + "SELECT entry, ItemLevel FROM item_template " + "WHERE Quality >= 2 AND InventoryType = 12 " + "AND (FlagsExtra & 8192) = 0 " + "AND (spellid_1 = 42292 OR spellid_2 = 42292 OR spellid_3 = 42292 " + " OR spellid_4 = 42292 OR spellid_5 = 42292)"); + + if (!result) + { + LOG_INFO("playerbots", "CC-break trinket cache: no items found."); + return; + } + + struct CcItem { uint32 itemId; uint16 itemLevel; }; + std::vector tmp; + do + { + Field* f = result->Fetch(); + tmp.push_back({f[0].Get(), f[1].Get()}); + } while (result->NextRow()); + + std::sort(tmp.begin(), tmp.end(), [](const CcItem& a, const CcItem& b) { + return a.itemLevel > b.itemLevel; + }); + for (auto& c : tmp) + ccBreakTrinketCache.push_back(c.itemId); + + LOG_INFO("playerbots", "CC-break trinket cache: {} items.", ccBreakTrinketCache.size()); +} + +uint8 PlayerbotFactory::GetPreferredArmorType(uint8 cls) +{ + switch (cls) + { + case CLASS_WARRIOR: + case CLASS_PALADIN: + case CLASS_DEATH_KNIGHT: + return ITEM_SUBCLASS_ARMOR_PLATE; + + case CLASS_HUNTER: + case CLASS_SHAMAN: + return ITEM_SUBCLASS_ARMOR_MAIL; + + case CLASS_ROGUE: + case CLASS_DRUID: + return ITEM_SUBCLASS_ARMOR_LEATHER; + + case CLASS_PRIEST: + case CLASS_MAGE: + case CLASS_WARLOCK: + return ITEM_SUBCLASS_ARMOR_CLOTH; + + default: + return 0; + } } void PlayerbotFactory::Prepare() @@ -301,9 +625,9 @@ void PlayerbotFactory::Randomize(bool incremental) if (!incremental || !sPlayerbotAIConfig.equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig.equipmentPersistenceLevel) { - InitTalentsTree(); + uint32 specIndex = InitTalentsTree(); + sRandomPlayerbotMgr.SetValue(bot->GetGUID().GetCounter(), "specNo", specIndex + 1); } - sRandomPlayerbotMgr.SetValue(bot->GetGUID().GetCounter(), "specNo", 0); if (botAI) { PlayerbotRepository::instance().Reset(botAI); @@ -1099,7 +1423,7 @@ void PlayerbotFactory::ResetQuests() } } -void PlayerbotFactory::InitTalentsTree(bool increment /*false*/, bool use_template /*true*/, bool reset /*false*/) +uint32 PlayerbotFactory::InitTalentsTree(bool increment /*false*/, bool use_template /*true*/, bool reset /*false*/) { uint32 specTab; uint8 cls = bot->getClass(); @@ -1167,7 +1491,14 @@ void PlayerbotFactory::InitTalentsTree(bool increment /*false*/, bool use_templa if (bot->GetFreeTalentPoints()) InitTalents((specTab + 2) % 3); + if (bot->getClass() == CLASS_SHAMAN && bot->HasSpell(30798)) + { + bot->SetSkill(SKILL_DUAL_WIELD, 0, 1, 1); + bot->SetCanDualWield(true); + } + bot->SendTalentsInfoData(false); + return sPlayerbotAIConfig.randomClassSpecIndex[cls][specTab]; } void PlayerbotFactory::InitTalentsBySpecNo(Player* bot, int specNo, bool reset) @@ -1245,7 +1576,15 @@ void PlayerbotFactory::InitTalentsBySpecNo(Player* bot, int specNo, bool reset) break; } } + + if (bot->getClass() == CLASS_SHAMAN && bot->HasSpell(30798)) + { + bot->SetSkill(SKILL_DUAL_WIELD, 0, 1, 1); + bot->SetCanDualWield(true); + } + bot->SendTalentsInfoData(false); + sRandomPlayerbotMgr.SetValue(bot->GetGUID().GetCounter(), "specNo", (uint32)specNo + 1); } void PlayerbotFactory::InitTalentsByParsedSpecLink(Player* bot, std::vector> parsedSpecLink, @@ -1748,7 +2087,35 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) uint32 blevel = bot->GetLevel(); int32 delta = std::min(blevel, 10u); + bool isPvp = sRandomPlayerbotMgr.IsSpecPvp(bot->GetGUID().GetCounter(), bot->getClass()); + StatsWeightCalculator calculator(bot); + if (isPvp) + calculator.SetPvpSpec(true); + + // Pre-select CC-break trinket for PvP specs: best available by item level + // that the bot meets the level requirement for. + // Humans (Every Man for Himself) and Undead (Will of the Forsaken) have a + // racial that shares the PvP trinket cooldown, so they don't need one. + bool racialHasCcBreak = (bot->getRace() == RACE_HUMAN || bot->getRace() == RACE_UNDEAD_PLAYER); + uint32 pvpTrinket1 = 0; + if (isPvp && level >= 50 && !racialHasCcBreak) + { + for (uint32 itemId : ccBreakTrinketCache) + { + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); + if (!proto) continue; + // Respect gear quality limit: trinket must not exceed itemQuality setting + if (static_cast(proto->Quality) > itemQuality) continue; + if (proto->RequiredLevel > level) continue; + if (!CanEquipItem(proto)) continue; + uint16 dest; + if (!CanEquipUnseenItem(EQUIPMENT_SLOT_TRINKET1, dest, itemId)) continue; + pvpTrinket1 = itemId; + break; + } + } + for (int32 slot : initSlotsOrder) { if (slot == EQUIPMENT_SLOT_TABARD || slot == EQUIPMENT_SLOT_BODY) @@ -1768,6 +2135,10 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) (slot != EQUIPMENT_SLOT_RANGED)) continue; + // Exclude resilience weighting for trinkets + bool isTrinketSlot = (slot == EQUIPMENT_SLOT_TRINKET1 || slot == EQUIPMENT_SLOT_TRINKET2); + calculator.SetExcludeResilience(isTrinketSlot); + Item* oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); if (second_chance && oldItem) @@ -1777,6 +2148,28 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); + // PvP specs: force TRINKET1 to the best available CC-break trinket. + if (slot == EQUIPMENT_SLOT_TRINKET1 && pvpTrinket1 != 0) + { + if (oldItem) + { + uint8 bagIndex = oldItem->GetBagSlot(); + uint8 oldSlot = oldItem->GetSlot(); + uint8 dstBag = NULL_BAG; + WorldPacket packet(CMSG_AUTOSTORE_BAG_ITEM, 3); + packet << bagIndex << oldSlot << dstBag; + WorldPackets::Item::AutoStoreBagItem nicePacket(std::move(packet)); + nicePacket.Read(); + bot->GetSession()->HandleAutoStoreBagItemOpcode(nicePacket); + oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); + if (oldItem) continue; + } + uint16 dest; + if (CanEquipUnseenItem(slot, dest, pvpTrinket1)) + bot->EquipNewItem(dest, pvpTrinket1, true); + continue; + } + int32 desiredQuality = itemQuality; if (urand(0, 100) < 100 * sPlayerbotAIConfig.randomGearLoweringChance && desiredQuality > ITEM_QUALITY_NORMAL) desiredQuality--; @@ -1794,6 +2187,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) if (urand(1, 100) <= skipProb) continue; + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); // disable next expansion gear if (sPlayerbotAIConfig.limitGearExpansion && bot->GetLevel() <= 60 && itemId >= 23728) continue; @@ -1804,7 +2198,6 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) // wearable TBC items above 35570 but nothing of significance continue; - ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); if (!proto) continue; @@ -1855,7 +2248,15 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) ItemTemplate const* proto = sObjectMgr->GetItemTemplate(newItemId); - float cur_score = calculator.CalculateItem(newItemId); + float cur_score = calculator.CalculateItem(newItemId, 0, slot); + + if (cur_score > 0.0f && proto && proto->Class == ITEM_CLASS_ARMOR && sPlayerbotAIConfig.preferClassArmorType) + { + uint8 preferredArmorType = GetPreferredArmorType(bot->getClass()); + if (preferredArmorType != 0 && proto->SubClass == preferredArmorType) + cur_score *= 3.0f; // 3x multiplier for preferred armor type + } + if (cur_score > bestScoreForSlot) { // delay heavy check to here @@ -1881,7 +2282,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) if (incremental && oldItem) { - float old_score = calculator.CalculateItem(oldItem->GetEntry(), oldItem->GetItemRandomPropertyId()); + float old_score = calculator.CalculateItem(oldItem->GetEntry(), oldItem->GetItemRandomPropertyId(), slot); if (bestScoreForSlot < 1.2f * old_score) continue; } @@ -1934,7 +2335,14 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) (slot != EQUIPMENT_SLOT_RANGED)) continue; - if (bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot) != nullptr) + // CC-break trinket was force-equipped in the main pass; leave it alone. + if (slot == EQUIPMENT_SLOT_TRINKET1 && pvpTrinket1 != 0) + continue; + + bool isTrinketSlot = (slot == EQUIPMENT_SLOT_TRINKET1 || slot == EQUIPMENT_SLOT_TRINKET2); + calculator.SetExcludeResilience(isTrinketSlot); + + if (Item* oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot)) bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true); std::vector& ids = items[slot]; @@ -1949,7 +2357,15 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) ItemTemplate const* proto = sObjectMgr->GetItemTemplate(newItemId); - float cur_score = calculator.CalculateItem(newItemId); + float cur_score = calculator.CalculateItem(newItemId, 0, slot); + + if (cur_score > 0.0f && proto && proto->Class == ITEM_CLASS_ARMOR && sPlayerbotAIConfig.preferClassArmorType) + { + uint8 preferredArmorType = GetPreferredArmorType(bot->getClass()); + if (preferredArmorType != 0 && proto->SubClass == preferredArmorType) + cur_score *= 3.0f; // 3x multiplier for preferred armor type + } + if (cur_score > bestScoreForSlot) { // delay heavy check to here @@ -1970,8 +2386,6 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) if (!CanEquipUnseenItem(slot, dest, bestItemForSlot)) continue; - Item* newItem = bot->EquipNewItem(dest, bestItemForSlot, true); - bot->EquipNewItem(dest, bestItemForSlot, true); bot->AutoUnequipOffhandIfNeed(); } @@ -2133,7 +2547,7 @@ void PlayerbotFactory::InitBags(bool destroyOld) if (old_bag) continue; - Item* newItem = bot->EquipNewItem(dest, newItemId, true); + bot->EquipNewItem(dest, newItemId, true); // if (newItem) // { // newItem->AddToWorld(); @@ -2250,69 +2664,278 @@ bool PlayerbotFactory::CanEquipUnseenItem(uint8 slot, uint16& dest, uint32 item) void PlayerbotFactory::InitTradeSkills() { + if (!sRandomPlayerbotMgr.IsRandomBot(bot)) + return; + + uint32 const maxPrimaryTradeSkills = + std::min(2, sWorld->getIntConfig(CONFIG_MAX_PRIMARY_TRADE_SKILL)); + uint16 firstSkill = sRandomPlayerbotMgr.GetValue(bot, "firstSkill"); uint16 secondSkill = sRandomPlayerbotMgr.GetValue(bot, "secondSkill"); - if (!firstSkill || !secondSkill) + ProfessionRollType professionRollType = + static_cast(sRandomPlayerbotMgr.GetValue(bot, "professionRollType")); + + if (professionRollType != ProfessionRollType::Class && professionRollType != ProfessionRollType::Random) { - std::vector firstSkills; - std::vector secondSkills; + professionRollType = urand(1, 100) <= sPlayerbotAIConfig.classMatchingProfessionChance + ? ProfessionRollType::Class + : ProfessionRollType::Random; + sRandomPlayerbotMgr.SetValue(bot, "professionRollType", static_cast(professionRollType)); + } - switch (bot->getClass()) - { - case CLASS_WARRIOR: - case CLASS_PALADIN: - case CLASS_DEATH_KNIGHT: - firstSkills.push_back(SKILL_MINING); - secondSkills.push_back(SKILL_BLACKSMITHING); - secondSkills.push_back(SKILL_ENGINEERING); - secondSkills.push_back(SKILL_JEWELCRAFTING); - break; - case CLASS_SHAMAN: - case CLASS_DRUID: - case CLASS_HUNTER: - case CLASS_ROGUE: - firstSkills.push_back(SKILL_SKINNING); - secondSkills.push_back(SKILL_LEATHERWORKING); - break; - default: - firstSkills.push_back(SKILL_TAILORING); - secondSkills.push_back(SKILL_ENCHANTING); - } + std::vector professionPairs = professionRollType == ProfessionRollType::Class + ? GetClassProfessionPairs(bot) + : GetRandomProfessionPairs(); - switch (urand(0, 6)) + bool const hasStoredProfessionPair = firstSkill && secondSkill && firstSkill != secondSkill && + IsPrimaryTradeSkill(firstSkill) && IsPrimaryTradeSkill(secondSkill) && + HasProfessionPair(professionPairs, firstSkill, secondSkill); + bool const keepExistingProfessionPair = maxPrimaryTradeSkills < 2 && hasStoredProfessionPair; + + if (maxPrimaryTradeSkills == 1 && !keepExistingProfessionPair) + { + if (!IsPrimaryTradeSkill(firstSkill) || secondSkill != 0) { - case 0: - firstSkill = SKILL_HERBALISM; - secondSkill = SKILL_ALCHEMY; - break; - case 1: - firstSkill = SKILL_HERBALISM; - secondSkill = SKILL_MINING; - break; - case 2: - firstSkill = SKILL_MINING; - secondSkill = SKILL_SKINNING; - break; - case 3: - firstSkill = SKILL_HERBALISM; - secondSkill = SKILL_SKINNING; - break; - default: - firstSkill = firstSkills[urand(0, firstSkills.size() - 1)]; - secondSkill = secondSkills[urand(0, secondSkills.size() - 1)]; - break; + firstSkill = ChooseSingleProfession(professionPairs); + secondSkill = 0; + + sRandomPlayerbotMgr.SetValue(bot, "firstSkill", firstSkill); + sRandomPlayerbotMgr.SetValue(bot, "secondSkill", secondSkill); } + } + else if (maxPrimaryTradeSkills == 0 && !keepExistingProfessionPair) + { + firstSkill = 0; + secondSkill = 0; sRandomPlayerbotMgr.SetValue(bot, "firstSkill", firstSkill); sRandomPlayerbotMgr.SetValue(bot, "secondSkill", secondSkill); } + if (maxPrimaryTradeSkills >= 2 && + (!firstSkill || !secondSkill || firstSkill == secondSkill || !IsPrimaryTradeSkill(firstSkill) || + !IsPrimaryTradeSkill(secondSkill) || !HasProfessionPair(professionPairs, firstSkill, secondSkill))) + { + auto const& professionPair = ChooseProfessionPair(professionPairs); + firstSkill = professionPair.first; + secondSkill = professionPair.second; + + sRandomPlayerbotMgr.SetValue(bot, "firstSkill", firstSkill); + sRandomPlayerbotMgr.SetValue(bot, "secondSkill", secondSkill); + } + + std::vector primarySkills; + if (keepExistingProfessionPair) + { + primarySkills.push_back(firstSkill); + primarySkills.push_back(secondSkill); + } + else if (maxPrimaryTradeSkills > 0) + primarySkills.push_back(firstSkill); + if (!keepExistingProfessionPair && maxPrimaryTradeSkills > 1) + primarySkills.push_back(secondSkill); + SetRandomSkill(SKILL_FIRST_AID); SetRandomSkill(SKILL_FISHING); SetRandomSkill(SKILL_COOKING); - SetRandomSkill(firstSkill); - SetRandomSkill(secondSkill); + for (uint16 skillId : primarySkills) + SetRandomSkill(skillId); + + std::vector skillsToLearn = {SKILL_FIRST_AID, SKILL_FISHING, SKILL_COOKING}; + skillsToLearn.insert(skillsToLearn.end(), primarySkills.begin(), primarySkills.end()); + + for (uint16 skillId : skillsToLearn) + { + uint32 spellId = GetProfessionStarterSpell(skillId); + if (!spellId || bot->HasSpell(spellId)) + continue; + + if (IsPrimaryTradeSkill(skillId) && !bot->GetFreePrimaryProfessionPoints() && + !(keepExistingProfessionPair && bot->HasSkill(skillId))) + continue; + + bot->learnSpell(spellId, false); + } + + InitTradeSpecializations(); +} + +void PlayerbotFactory::InitTradeSpecializations() +{ + InitAlchemySpecialization(); + InitEngineeringSpecialization(); + InitLeatherworkingSpecialization(); + InitTailoringSpecialization(); + InitBlacksmithingSpecialization(); +} + +bool PlayerbotFactory::InitAlchemySpecialization() +{ + if (!bot->HasSkill(SKILL_ALCHEMY) || + bot->GetBaseSkillValue(SKILL_ALCHEMY) < 325 || + bot->GetLevel() <= 67) + return false; + + if (HasAnySpell(bot, {static_cast(ProfessionSpecializationSpell::Transmute), + static_cast(ProfessionSpecializationSpell::Elixir), + static_cast(ProfessionSpecializationSpell::Potion)})) + return false; + + switch (GetStoredOrRandomValue(bot, "alchemySpecialization", 1, 3)) + { + case 1: + return LearnProfessionSpecialization(bot, + ProfessionSpecializationSpell::Transmute, + ProfessionSpecializationSpell::LearnTransmute); + case 2: + return LearnProfessionSpecialization(bot, + ProfessionSpecializationSpell::Elixir, + ProfessionSpecializationSpell::LearnElixir); + case 3: + default: + return LearnProfessionSpecialization(bot, + ProfessionSpecializationSpell::Potion, + ProfessionSpecializationSpell::LearnPotion); + } +} + +bool PlayerbotFactory::InitEngineeringSpecialization() +{ + if (!bot->HasSkill(SKILL_ENGINEERING) || + bot->GetBaseSkillValue(SKILL_ENGINEERING) < 200 || + bot->GetLevel() < 30) + return false; + + if (HasAnySpell(bot, {static_cast(ProfessionSpecializationSpell::Goblin), + static_cast(ProfessionSpecializationSpell::Gnomish)})) + return false; + + switch (GetStoredOrRandomValue(bot, "engineeringSpecialization", 1, 2)) + { + case 1: + return LearnProfessionSpecialization(bot, + ProfessionSpecializationSpell::Goblin, + ProfessionSpecializationSpell::LearnGoblin); + case 2: + default: + return LearnProfessionSpecialization(bot, + ProfessionSpecializationSpell::Gnomish, + ProfessionSpecializationSpell::LearnGnomish); + } +} + +bool PlayerbotFactory::InitLeatherworkingSpecialization() +{ + if (!bot->HasSkill(SKILL_LEATHERWORKING) || + bot->GetBaseSkillValue(SKILL_LEATHERWORKING) < 225 || + bot->GetLevel() <= 40) + return false; + + if (HasAnySpell(bot, {static_cast(ProfessionSpecializationSpell::Dragon), + static_cast(ProfessionSpecializationSpell::Elemental), + static_cast(ProfessionSpecializationSpell::Tribal)})) + return false; + + switch (GetStoredOrRandomValue(bot, "leatherSpecialization", 1, 3)) + { + case 1: + return LearnProfessionSpecialization(bot, + ProfessionSpecializationSpell::Dragon, + ProfessionSpecializationSpell::LearnDragon); + case 2: + return LearnProfessionSpecialization(bot, + ProfessionSpecializationSpell::Elemental, + ProfessionSpecializationSpell::LearnElemental); + case 3: + default: + return LearnProfessionSpecialization(bot, + ProfessionSpecializationSpell::Tribal, + ProfessionSpecializationSpell::LearnTribal); + } +} + +bool PlayerbotFactory::InitTailoringSpecialization() +{ + if (!bot->HasSkill(SKILL_TAILORING) || + bot->GetBaseSkillValue(SKILL_TAILORING) < 350 || + bot->GetLevel() <= 59) + return false; + + if (HasAnySpell(bot, {static_cast(ProfessionSpecializationSpell::Spellfire), + static_cast(ProfessionSpecializationSpell::Mooncloth), + static_cast(ProfessionSpecializationSpell::Shadoweave)})) + return false; + + switch (GetStoredOrRandomValue(bot, "tailorSpecialization", 1, 3)) + { + case 1: + return LearnProfessionSpecialization(bot, + ProfessionSpecializationSpell::Spellfire, + ProfessionSpecializationSpell::LearnSpellfire); + case 2: + return LearnProfessionSpecialization(bot, + ProfessionSpecializationSpell::Mooncloth, + ProfessionSpecializationSpell::LearnMooncloth); + case 3: + default: + return LearnProfessionSpecialization(bot, + ProfessionSpecializationSpell::Shadoweave, + ProfessionSpecializationSpell::LearnShadoweave); + } +} + +bool PlayerbotFactory::InitBlacksmithingSpecialization() +{ + bool learnedSpecialization = false; + + if (!bot->HasSkill(SKILL_BLACKSMITHING) || + bot->GetBaseSkillValue(SKILL_BLACKSMITHING) < 225) + return false; + + if (!bot->HasSpell(static_cast(ProfessionSpecializationSpell::Armor)) && + !bot->HasSpell(static_cast(ProfessionSpecializationSpell::Weapon))) + { + switch (GetStoredOrRandomValue(bot, "blacksmithSpecialization", 1, 2)) + { + case 1: + learnedSpecialization = LearnProfessionSpecialization(bot, + ProfessionSpecializationSpell::Armor, + ProfessionSpecializationSpell::LearnArmor); + break; + case 2: + default: + learnedSpecialization = LearnProfessionSpecialization(bot, + ProfessionSpecializationSpell::Weapon, + ProfessionSpecializationSpell::LearnWeapon); + break; + } + } + + if (!bot->HasSpell(static_cast(ProfessionSpecializationSpell::Weapon)) || + bot->GetBaseSkillValue(SKILL_BLACKSMITHING) < 250 || + bot->GetLevel() <= 49 || + HasAnySpell(bot, {static_cast(ProfessionSpecializationSpell::Hammer), + static_cast(ProfessionSpecializationSpell::Axe), + static_cast(ProfessionSpecializationSpell::Sword)})) + return learnedSpecialization; + + switch (GetStoredOrRandomValue(bot, "blacksmithWeaponSpecialization", 1, 3)) + { + case 1: + return LearnProfessionSpecialization(bot, + ProfessionSpecializationSpell::Hammer, + ProfessionSpecializationSpell::LearnHammer); + case 2: + return LearnProfessionSpecialization(bot, + ProfessionSpecializationSpell::Axe, + ProfessionSpecializationSpell::LearnAxe); + case 3: + default: + return LearnProfessionSpecialization(bot, + ProfessionSpecializationSpell::Sword, + ProfessionSpecializationSpell::LearnSword); + } } void PlayerbotFactory::UpdateTradeSkills() @@ -2456,6 +3079,9 @@ void PlayerbotFactory::InitSkills() break; } + InitTradeSkills(); + InitInventorySkill(); + // switch (bot->getClass()) // { // case CLASS_WARRIOR: @@ -3804,30 +4430,21 @@ void PlayerbotFactory::InitInventory() void PlayerbotFactory::InitInventorySkill() { - if (bot->HasSkill(SKILL_MINING)) - { + if (bot->HasSkill(SKILL_MINING) && !bot->HasItemCount(2901, 1, true)) StoreItem(2901, 1); // Mining Pick - } - if (bot->HasSkill(SKILL_BLACKSMITHING) || bot->HasSkill(SKILL_ENGINEERING)) - { + if ((bot->HasSkill(SKILL_BLACKSMITHING) || bot->HasSkill(SKILL_ENGINEERING)) && + !bot->HasItemCount(5956, 1, true)) StoreItem(5956, 1); // Blacksmith Hammer - } - if (bot->HasSkill(SKILL_ENGINEERING)) - { + if (bot->HasSkill(SKILL_ENGINEERING) && !bot->HasItemCount(6219, 1, true)) StoreItem(6219, 1); // Arclight Spanner - } - if (bot->HasSkill(SKILL_ENCHANTING)) - { + if (bot->HasSkill(SKILL_ENCHANTING) && !bot->HasItemCount(16207, 1, true)) StoreItem(16207, 1); // Runed Arcanite Rod - } - if (bot->HasSkill(SKILL_SKINNING)) - { + if (bot->HasSkill(SKILL_SKINNING) && !bot->HasItemCount(7005, 1, true)) StoreItem(7005, 1); // Skinning Knife - } } Item* PlayerbotFactory::StoreItem(uint32 itemId, uint32 count) @@ -4266,7 +4883,7 @@ void PlayerbotFactory::ApplyEnchantTemplate(uint8 spec) // const SpellItemEnchantmentEntry* a = sSpellItemEnchantmentStore.LookupEntry(1); } -void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destroyOld) +void PlayerbotFactory::ApplyEnchantAndGemsNew(bool /*destroyOld*/) { //int32 bestGemEnchantId[4] = {-1, -1, -1, -1}; // 1, 2, 4, 8 color //not used, line marked for removal. //float bestGemScore[4] = {0, 0, 0, 0}; //not used, line marked for removal. diff --git a/src/Bot/Factory/PlayerbotFactory.h b/src/Bot/Factory/PlayerbotFactory.h index d943463ea..ba32e6a11 100644 --- a/src/Bot/Factory/PlayerbotFactory.h +++ b/src/Bot/Factory/PlayerbotFactory.h @@ -6,6 +6,9 @@ #ifndef _PLAYERBOT_PLAYERBOTFACTORY_H #define _PLAYERBOT_PLAYERBOTFACTORY_H +#include +#include + #include "InventoryAction.h" #include "Player.h" #include "PlayerbotAI.h" @@ -60,7 +63,7 @@ public: static uint32 tradeSkills[]; static float CalculateEnchantScore(uint32 enchant_id, Player* bot); - void InitTalentsTree(bool incremental = false, bool use_template = true, bool reset = false); + uint32 InitTalentsTree(bool incremental = false, bool use_template = true, bool reset = false); static void InitTalentsBySpecNo(Player* bot, int specNo, bool reset); static void InitTalentsByParsedSpecLink(Player* bot, std::vector> parsedSpecLink, bool reset); void InitAvailableSpells(); @@ -85,14 +88,94 @@ public: void InitKeyring(); void InitReputation(); void InitAttunementQuests(); + void InitGuild(); private: + enum class ProfessionSpecializationSpell : uint32 + { + Weapon = 9787, + Armor = 9788, + Hammer = 17040, + Axe = 17041, + Sword = 17039, + + LearnWeapon = 9789, + LearnArmor = 9790, + LearnHammer = 39099, + LearnAxe = 39098, + LearnSword = 39097, + + Dragon = 10656, + Elemental = 10658, + Tribal = 10660, + + LearnDragon = 10657, + LearnElemental = 10659, + LearnTribal = 10661, + + Spellfire = 26797, + Mooncloth = 26798, + Shadoweave = 26801, + + Goblin = 20222, + Gnomish = 20219, + + LearnGoblin = 20221, + LearnGnomish = 20220, + + LearnSpellfire = 26796, + LearnMooncloth = 26799, + LearnShadoweave = 26800, + + Transmute = 28672, + Elixir = 28677, + Potion = 28675, + + LearnTransmute = 28674, + LearnElixir = 28678, + LearnPotion = 28676 + }; + + enum class ProfessionRollType : uint32 + { + Random = 1, + Class = 2 + }; + + struct WeightedProfessionPair + { + uint16 firstSkill; + uint16 secondSkill; + uint32 weight; + }; + void Prepare(); // void InitSecondEquipmentSet(); // void InitEquipmentNew(bool incremental); bool CanEquipItem(ItemTemplate const* proto); bool CanEquipUnseenItem(uint8 slot, uint16& dest, uint32 item); + static bool IsPrimaryTradeSkill(uint16 skillId); + static bool IsGatheringTradeSkill(uint16 skillId); + static bool IsCraftingTradeSkill(uint16 skillId); + static uint32 GetProfessionStarterSpell(uint16 skillId); + static std::vector GetClassProfessionPairs(Player* bot); + static std::vector GetRandomProfessionPairs(); + static std::pair ChooseProfessionPair(std::vector const& professionPairs); + static bool HasProfessionPair(std::vector const& professionPairs, + uint16 firstSkill, uint16 secondSkill); + static uint16 ChooseSingleProfession(std::vector const& professionPairs); + static uint32 GetStoredOrRandomValue(Player* bot, std::string const& key, uint32 minValue, uint32 maxValue); + static bool HasAnySpell(Player* bot, std::vector const& spells); + static bool LearnProfessionSpecialization(Player* bot, + ProfessionSpecializationSpell knownSpell, + ProfessionSpecializationSpell learnSpell); void InitTradeSkills(); + void InitTradeSpecializations(); + bool InitAlchemySpecialization(); + bool InitEngineeringSpecialization(); + bool InitLeatherworkingSpecialization(); + bool InitTailoringSpecialization(); + bool InitBlacksmithingSpecialization(); void UpdateTradeSkills(); void SetRandomSkill(uint16 id); void ClearSpells(); @@ -107,6 +190,8 @@ private: std::vector GetCurrentGemsCount(); bool CanEquipArmor(ItemTemplate const* proto); bool CanEquipWeapon(ItemTemplate const* proto); + static void BuildCcBreakTrinketCache(); + uint8 GetPreferredArmorType(uint8 cls); void EnchantItem(Item* item); void AddItemStats(uint32 mod, uint8& sp, uint8& ap, uint8& tank); bool CheckItemStats(uint8 sp, uint8 ap, uint8 tank); @@ -117,7 +202,6 @@ private: void InitInventoryEquip(); void InitInventorySkill(); Item* StoreItem(uint32 itemId, uint32 count); - void InitGuild(); void InitArenaTeam(); void InitImmersive(); static void AddPrevQuests(uint32 questId, std::list& questIds); @@ -138,6 +222,7 @@ private: static std::unordered_map> trainerIdCache; static std::vector enchantSpellIdCache; static std::vector enchantGemIdCache; + static std::vector ccBreakTrinketCache; protected: EnchantContainer m_EnchantContainer; diff --git a/src/Bot/PlayerbotAI.cpp b/src/Bot/PlayerbotAI.cpp index 288ee0180..f5356b773 100644 --- a/src/Bot/PlayerbotAI.cpp +++ b/src/Bot/PlayerbotAI.cpp @@ -266,77 +266,104 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal) if (!CanUpdateAI()) return; - // Handle the current spell + // Handle a spell that is still in its preparing phase (including channeled spells). Spell* currentSpell = bot->GetCurrentSpell(CURRENT_GENERIC_SPELL); if (!currentSpell) currentSpell = bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL); if (currentSpell) { - const SpellInfo* spellInfo = currentSpell->GetSpellInfo(); - if (spellInfo && currentSpell->getState() == SPELL_STATE_PREPARING) + if (currentSpell->getState() == SPELL_STATE_PREPARING) { - Unit* spellTarget = currentSpell->m_targets.GetUnitTarget(); - // Interrupt if target is dead or spell can't target dead units - if (spellTarget && !spellTarget->IsAlive() && !spellInfo->IsAllowingDeadTarget()) + // Allow external scripts to interrupt a cast in progress + if (spellInterruptRequested) { + spellInterruptRequested = false; InterruptSpell(); YieldThread(bot, GetReactDelay()); return; } - GameObject* goSpellTarget = currentSpell->m_targets.GetGOTarget(); - - if (goSpellTarget && !goSpellTarget->isSpawned()) + const SpellInfo* spellInfo = currentSpell->GetSpellInfo(); + if (spellInfo) { - InterruptSpell(); - YieldThread(bot, GetReactDelay()); - return; - } - - bool isHeal = false; - bool isSingleTarget = true; - - for (uint8 i = 0; i < 3; ++i) - { - if (!spellInfo->Effects[i].Effect) - continue; - - // Check if spell is a heal - if (spellInfo->Effects[i].Effect == SPELL_EFFECT_HEAL || - spellInfo->Effects[i].Effect == SPELL_EFFECT_HEAL_MAX_HEALTH || - spellInfo->Effects[i].Effect == SPELL_EFFECT_HEAL_MECHANICAL) - isHeal = true; - - // Check if spell is single-target - if ((spellInfo->Effects[i].TargetA.GetTarget() && - spellInfo->Effects[i].TargetA.GetTarget() != TARGET_UNIT_TARGET_ALLY) || - (spellInfo->Effects[i].TargetB.GetTarget() && - spellInfo->Effects[i].TargetB.GetTarget() != TARGET_UNIT_TARGET_ALLY)) + Unit* spellTarget = currentSpell->m_targets.GetUnitTarget(); + // Interrupt if target is dead or spell can't target dead units + if (spellTarget && !spellTarget->IsAlive() && !spellInfo->IsAllowingDeadTarget()) { - isSingleTarget = false; + InterruptSpell(); + YieldThread(bot, GetReactDelay()); + return; } - } - // Interrupt if target ally has full health (heal by other member) - if (isHeal && isSingleTarget && spellTarget && spellTarget->IsFullHealth()) - { - InterruptSpell(); + GameObject* goSpellTarget = currentSpell->m_targets.GetGOTarget(); + + if (goSpellTarget && !goSpellTarget->isSpawned()) + { + InterruptSpell(); + YieldThread(bot, GetReactDelay()); + return; + } + + bool isHeal = false; + bool isSingleTarget = true; + + for (uint8 i = 0; i < 3; ++i) + { + if (!spellInfo->Effects[i].Effect) + continue; + + // Check if spell is a heal + if (spellInfo->Effects[i].Effect == SPELL_EFFECT_HEAL || + spellInfo->Effects[i].Effect == SPELL_EFFECT_HEAL_MAX_HEALTH || + spellInfo->Effects[i].Effect == SPELL_EFFECT_HEAL_MECHANICAL) + isHeal = true; + + // Check if spell is single-target + if ((spellInfo->Effects[i].TargetA.GetTarget() && + spellInfo->Effects[i].TargetA.GetTarget() != TARGET_UNIT_TARGET_ALLY) || + (spellInfo->Effects[i].TargetB.GetTarget() && + spellInfo->Effects[i].TargetB.GetTarget() != TARGET_UNIT_TARGET_ALLY)) + { + isSingleTarget = false; + } + } + + // Interrupt if target ally has full health (heal by other member) + if (isHeal && isSingleTarget && spellTarget && spellTarget->IsFullHealth()) + { + InterruptSpell(); + YieldThread(bot, GetReactDelay()); + return; + } + + // Ensure bot is facing target if necessary + if (spellTarget && !bot->HasInArc(CAST_ANGLE_IN_FRONT, spellTarget) && + (spellInfo->FacingCasterFlags & SPELL_FACING_FLAG_INFRONT)) + { + ServerFacade::instance().SetFacingTo(bot, spellTarget); + } + + // Wait for spell cast YieldThread(bot, GetReactDelay()); return; } + } + } - // Ensure bot is facing target if necessary - if (spellTarget && !bot->HasInArc(CAST_ANGLE_IN_FRONT, spellTarget) && - (spellInfo->FacingCasterFlags & SPELL_FACING_FLAG_INFRONT)) - { - ServerFacade::instance().SetFacingTo(bot, spellTarget); - } - - // Wait for spell cast + if (spellInterruptRequested) + { + // At this point the preparing-cast branch above did not consume the request. + // Interrupt a current channel if one still exists; otherwise, clear the stale request. + if (bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL)) + { + spellInterruptRequested = false; + InterruptSpell(); YieldThread(bot, GetReactDelay()); return; } + + spellInterruptRequested = false; } // Handle transport check delay @@ -464,7 +491,7 @@ void PlayerbotAI::UpdateAIInternal([[maybe_unused]] uint32 elapsed, bool minimal continue; } - ChatReplyAction::ChatReplyDo(bot, it->m_type, it->m_guid1, it->m_guid2, it->m_msg, it->m_chanName, it->m_name); + ChatReplyAction::ChatReplyDo(bot, it->m_type, it->m_guid1, it->m_msg, it->m_chanName, it->m_name); it = chatReplies.erase(it); } @@ -855,6 +882,7 @@ void PlayerbotAI::Reset(bool full) aiObjectContext->GetValue("current target")->Set(nullptr); aiObjectContext->GetValue("prioritized targets")->Reset(); aiObjectContext->GetValue("pull target")->Set(ObjectGuid::Empty); + aiObjectContext->GetValue("pull strategy target")->Set(ObjectGuid::Empty); aiObjectContext->GetValue("rpg target")->Set(GuidPosition()); aiObjectContext->GetValue("loot target")->Set(LootObject()); aiObjectContext->GetValue("lfg proposal")->Set(0); @@ -1464,6 +1492,7 @@ void PlayerbotAI::DoNextAction(bool min) aiObjectContext->GetValue("current target")->Set(nullptr); aiObjectContext->GetValue("enemy player target")->Set(nullptr); aiObjectContext->GetValue("pull target")->Set(ObjectGuid::Empty); + aiObjectContext->GetValue("pull strategy target")->Set(ObjectGuid::Empty); aiObjectContext->GetValue("loot target")->Set(LootObject()); ChangeEngine(BOT_STATE_DEAD); @@ -1573,7 +1602,7 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster) static const std::vector allInstanceStrategies = { "aq20", "bwl", "karazhan", "gruulslair", "icc", "magtheridon", "moltencore", - "naxx", "onyxia", "ssc", "tempestkeep", "ulduar", "voa", "wotlk-an", "wotlk-cos", + "naxx", "onyxia", "ssc", "tbc-ac", "tempestkeep", "ulduar", "voa", "wotlk-an", "wotlk-cos", "wotlk-dtk", "wotlk-eoe", "wotlk-fos", "wotlk-gd", "wotlk-hol", "wotlk-hor", "wotlk-hos", "wotlk-nex", "wotlk-occ", "wotlk-ok", "wotlk-os", "wotlk-pos", "wotlk-toc", "wotlk-uk", "wotlk-up", "wotlk-vh", "zulaman" @@ -1613,7 +1642,10 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster) strategyName = "ssc"; // Serpentshrine Cavern break; case 550: - strategyName = "tempestkeep"; // Tempest Keep + strategyName = "tempestkeep"; // Tempest Keep: The Eye + break; + case 558: + strategyName = "tbc-ac"; // Auchindoun: Auchenai Crypts break; case 565: strategyName = "gruulslair"; // Gruul's Lair @@ -1791,7 +1823,12 @@ bool PlayerbotAI::ContainsStrategy(StrategyType type) bool PlayerbotAI::HasStrategy(std::string const name, BotState type) { return engines[type]->HasStrategy(name); } -void PlayerbotAI::ResetStrategies(bool load) +Strategy* PlayerbotAI::GetStrategy(std::string const name, BotState type) +{ + return engines[type] ? engines[type]->GetStrategy(name) : nullptr; +} + +void PlayerbotAI::ResetStrategies(bool /*load*/) { for (uint8 i = 0; i < BOT_STATE_MAX; i++) engines[i]->removeAllStrategies(); @@ -4204,6 +4241,19 @@ void PlayerbotAI::RemoveAura(std::string const name) bot->RemoveAurasDueToSpell(spellid); } +void PlayerbotAI::RequestSpellInterrupt() +{ + Spell* currentSpell = bot->GetCurrentSpell(CURRENT_GENERIC_SPELL); + if (currentSpell && currentSpell->getState() == SPELL_STATE_PREPARING) + { + spellInterruptRequested = true; + return; + } + + if (bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL)) + spellInterruptRequested = true; +} + bool PlayerbotAI::IsInterruptableSpellCasting(Unit* target, std::string const spell) { if (!IsValidUnit(target)) diff --git a/src/Bot/PlayerbotAI.h b/src/Bot/PlayerbotAI.h index 1aafcdefc..796dd4181 100644 --- a/src/Bot/PlayerbotAI.h +++ b/src/Bot/PlayerbotAI.h @@ -3,8 +3,8 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ -#ifndef _PLAYERBOT_PLAYERbotAI_H -#define _PLAYERBOT_PLAYERbotAI_H +#ifndef _PLAYERBOT_PLAYERBOTAI_H +#define _PLAYERBOT_PLAYERBOTAI_H #include @@ -406,6 +406,7 @@ public: void ChangeStrategy(std::string const name, BotState type); void ClearStrategies(BotState type); std::vector GetStrategies(BotState type); + Strategy* GetStrategy(std::string const name, BotState type); void ApplyInstanceStrategies(uint32 mapId, bool tellMaster = false); void EvaluateHealerDpsStrategy(); bool ContainsStrategy(StrategyType type); @@ -472,6 +473,7 @@ public: void SpellInterrupted(uint32 spellid); int32 CalculateGlobalCooldown(uint32 spellid); void InterruptSpell(); + void RequestSpellInterrupt(); void RemoveAura(std::string const name); void RemoveShapeshift(); void WaitForSpellCast(Spell* spell); @@ -648,6 +650,7 @@ protected: BotCheatMask cheatMask = BotCheatMask::none; Position jumpDestination = Position(); uint32 nextTransportCheck = 0; + bool spellInterruptRequested = false; }; #endif diff --git a/src/Bot/PlayerbotMgr.cpp b/src/Bot/PlayerbotMgr.cpp index c3b614a98..68eb2fd1e 100644 --- a/src/Bot/PlayerbotMgr.cpp +++ b/src/Bot/PlayerbotMgr.cpp @@ -64,7 +64,7 @@ private: }; std::unordered_set BotInitGuard::botsBeingInitialized; -std::unordered_set PlayerbotHolder::botLoading; +std::unordered_map PlayerbotHolder::botLoading; PlayerbotHolder::PlayerbotHolder() : PlayerbotAIBase(false) {} class PlayerbotLoginQueryHolder : public LoginQueryHolder @@ -121,7 +121,13 @@ void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId LOG_DEBUG("playerbots", "PlayerbotMgr not found for master player with GUID: {}", masterPlayer->GetGUID().GetRawValue()); return; } - uint32 count = mgr->GetPlayerbotsCount() + botLoading.size(); + uint32 loadingForMaster = 0; + for (auto const& [guid, acctId] : botLoading) + { + if (acctId == masterAccountId) + ++loadingForMaster; + } + uint32 count = mgr->GetPlayerbotsCount() + loadingForMaster; if (count >= PlayerbotAIConfig::instance().maxAddedBots) { allowed = false; @@ -144,7 +150,7 @@ void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId return; } - botLoading.insert(playerGuid); + botLoading.emplace(playerGuid, masterAccountId); // Always login in with world session to avoid race condition sWorld->AddQueryHolderCallback(CharacterDatabase.DelayQueryHolder(holder)) @@ -1661,7 +1667,7 @@ void PlayerbotMgr::TellError(std::string const botName, std::string const text) errors[text] = names; } -void PlayerbotMgr::CheckTellErrors(uint32 elapsed) +void PlayerbotMgr::CheckTellErrors(uint32 /*elapsed*/) { time_t now = time(nullptr); if ((now - lastErrorTell) < sPlayerbotAIConfig.errorDelay / 1000) diff --git a/src/Bot/PlayerbotMgr.h b/src/Bot/PlayerbotMgr.h index b80f6f236..316e34d47 100644 --- a/src/Bot/PlayerbotMgr.h +++ b/src/Bot/PlayerbotMgr.h @@ -57,7 +57,7 @@ protected: virtual void OnBotLoginInternal(Player* const bot) = 0; PlayerBotMap playerBots; - static std::unordered_set botLoading; + static std::unordered_map botLoading; }; class PlayerbotMgr : public PlayerbotHolder diff --git a/src/Bot/RandomPlayerbotMgr.cpp b/src/Bot/RandomPlayerbotMgr.cpp index 04297822c..4ce498f39 100644 --- a/src/Bot/RandomPlayerbotMgr.cpp +++ b/src/Bot/RandomPlayerbotMgr.cpp @@ -278,7 +278,7 @@ void RandomPlayerbotMgr::LogPlayerLocation() } } -void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/) +void RandomPlayerbotMgr::UpdateAIInternal(uint32 /*elapsed*/, bool /*minimal*/) { if (totalPmo) totalPmo->finish(); @@ -2259,6 +2259,16 @@ CachedEvent* RandomPlayerbotMgr::FindEvent(uint32 bot, std::string const& event) return &e; } +bool RandomPlayerbotMgr::IsSpecPvp(uint32 bot, uint8 cls) +{ + uint32 stored = GetValue(bot, "specNo"); + if (!stored) + return false; + uint32 specIndex = stored - 1; + std::string const& name = sPlayerbotAIConfig.premadeSpecName[cls][specIndex]; + return !name.empty() && name.find("pvp") != std::string::npos; +} + uint32 RandomPlayerbotMgr::GetEventValue(uint32 bot, std::string const& event) { if (CachedEvent* e = FindEvent(bot, event)) @@ -2525,6 +2535,13 @@ void RandomPlayerbotMgr::OnBotLoginInternal(Player* const bot) } } + // Run guild recovery/assignment at login to handle empty guild tables after restart. + if (sPlayerbotAIConfig.randomBotGuildCount > 0) + { + PlayerbotFactory factory(bot, bot->GetLevel()); + factory.InitGuild(); + } + if (sPlayerbotAIConfig.randomBotFixedLevel) { bot->SetPlayerFlag(PLAYER_FLAGS_NO_XP_GAIN); diff --git a/src/Bot/RandomPlayerbotMgr.h b/src/Bot/RandomPlayerbotMgr.h index db74f2cbe..b68c77e41 100644 --- a/src/Bot/RandomPlayerbotMgr.h +++ b/src/Bot/RandomPlayerbotMgr.h @@ -140,6 +140,7 @@ public: std::string GetData(uint32 bot, std::string const& type); void SetValue(uint32 bot, std::string const& type, uint32 value, std::string const& data = ""); void SetValue(Player* bot, std::string const& type, uint32 value, std::string const& data = ""); + bool IsSpecPvp(uint32 bot, uint8 cls); void Remove(Player* bot); ObjectGuid GetBattleMasterGUID(Player* bot, BattlegroundTypeId bgTypeId); CreatureData const* GetCreatureDataByEntry(uint32 entry); diff --git a/src/Db/PlayerbotRepository.cpp b/src/Db/PlayerbotRepository.cpp index 731534edd..115a4e95f 100644 --- a/src/Db/PlayerbotRepository.cpp +++ b/src/Db/PlayerbotRepository.cpp @@ -65,7 +65,7 @@ void PlayerbotRepository::Save(PlayerbotAI* botAI) SaveValue(guid, "dead", FormatStrategies("dead", botAI->GetStrategies(BOT_STATE_DEAD))); } -std::string const PlayerbotRepository::FormatStrategies(std::string const type, std::vector strategies) +std::string const PlayerbotRepository::FormatStrategies(std::string const /*type*/, std::vector strategies) { std::ostringstream out; for (std::vector::iterator i = strategies.begin(); i != strategies.end(); ++i) diff --git a/src/Mgr/Guild/PlayerbotGuildMgr.cpp b/src/Mgr/Guild/PlayerbotGuildMgr.cpp index 001a438cb..5dc1095de 100644 --- a/src/Mgr/Guild/PlayerbotGuildMgr.cpp +++ b/src/Mgr/Guild/PlayerbotGuildMgr.cpp @@ -150,13 +150,10 @@ void PlayerbotGuildMgr::OnGuildUpdate(Guild* guild) void PlayerbotGuildMgr::ResetGuildCache() { - for (auto it = _guildCache.begin(); it != _guildCache.end();) - { - GuildCache& cached = it->second; - cached.memberCount = 0; - cached.faction = 2; - cached.status = 0; - } + _guildCache.clear(); + + for (auto& nameEntry : _guildNames) + nameEntry.second = true; } void PlayerbotGuildMgr::LoadGuildNames() diff --git a/src/Mgr/Item/ItemVisitors.h b/src/Mgr/Item/ItemVisitors.h index 930aa1f4a..dd581ddba 100644 --- a/src/Mgr/Item/ItemVisitors.h +++ b/src/Mgr/Item/ItemVisitors.h @@ -325,9 +325,6 @@ public: FindMountVisitor(Player* bot) : FindUsableItemVisitor(bot) {} bool Accept(ItemTemplate const* proto) override; - -private: - uint32 effectId; }; class FindPetVisitor : public FindUsableItemVisitor diff --git a/src/Mgr/Item/RandomItemMgr.cpp b/src/Mgr/Item/RandomItemMgr.cpp index d2a279727..e08f2c385 100644 --- a/src/Mgr/Item/RandomItemMgr.cpp +++ b/src/Mgr/Item/RandomItemMgr.cpp @@ -176,7 +176,7 @@ RandomItemMgr::~RandomItemMgr() predicates.clear(); } -bool RandomItemMgr::HandleConsoleCommand(ChatHandler* handler, char const* args) +bool RandomItemMgr::HandleConsoleCommand(ChatHandler* /*handler*/, char const* args) { if (!args || !*args) { @@ -1823,7 +1823,7 @@ uint32 RandomItemMgr::GetUpgrade(Player* player, std::string spec, uint8 slot, u } std::vector RandomItemMgr::GetUpgradeList(Player* player, std::string spec, uint8 slot, uint32 quality, - uint32 itemId, uint32 amount) + uint32 itemId, uint32 /*amount*/) { std::vector listItems; if (!player) diff --git a/src/Mgr/Item/StatsWeightCalculator.cpp b/src/Mgr/Item/StatsWeightCalculator.cpp index 361e4a5f7..b232b1e6b 100644 --- a/src/Mgr/Item/StatsWeightCalculator.cpp +++ b/src/Mgr/Item/StatsWeightCalculator.cpp @@ -25,6 +25,10 @@ namespace constexpr uint32 SPELL_MOLTEN_ARMOR_RANK_1 = 30482; constexpr uint32 SPELL_MOLTEN_ARMOR_RANK_2 = 43045; constexpr uint32 SPELL_MOLTEN_ARMOR_RANK_3 = 43046; +constexpr uint32 SPELL_FEL_ARMOR_RANK_1 = 28176; +constexpr uint32 SPELL_FEL_ARMOR_RANK_2 = 28189; +constexpr uint32 SPELL_FEL_ARMOR_RANK_3 = 47892; +constexpr uint32 SPELL_FEL_ARMOR_RANK_4 = 47893; } StatsWeightCalculator::StatsWeightCalculator(Player* player) : player_(player) @@ -68,7 +72,7 @@ void StatsWeightCalculator::Reset() } } -float StatsWeightCalculator::CalculateItem(uint32 itemId, int32 randomPropertyIds) +float StatsWeightCalculator::CalculateItem(uint32 itemId, int32 randomPropertyIds, int32 slot) { ItemTemplate const* proto = &sObjectMgr->GetItemTemplateStore()->at(itemId); @@ -107,10 +111,12 @@ float StatsWeightCalculator::CalculateItem(uint32 itemId, int32 randomPropertyId weight_ *= PlayerbotFactory::CalcMixedGearScore(lvl, ITEM_QUALITY_EPIC); else weight_ *= PlayerbotFactory::CalcMixedGearScore(proto->ItemLevel, proto->Quality); - - return weight_; } - // If quality/level blending is disabled, also return the calculated weight. + + // Apply weapon speed governance if slot is provided and this is a weapon + if (sPlayerbotAIConfig.preferredSpecWeapons && slot >= 0 && proto->Class == ITEM_CLASS_WEAPON) + weight_ *= ApplyPreferredSpecWeapons(proto, slot); + return weight_; } @@ -208,6 +214,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_HIT] += 1.7f; stats_weights_[STATS_TYPE_CRIT] += 1.4f; stats_weights_[STATS_TYPE_HASTE] += 1.6f; + stats_weights_[STATS_TYPE_SPELL_POWER] -= 1.0f; stats_weights_[STATS_TYPE_RANGED_DPS] += 7.5f; } else if (cls == CLASS_HUNTER && tab == HUNTER_TAB_MARKSMANSHIP) @@ -218,6 +225,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_HIT] += 2.1f; stats_weights_[STATS_TYPE_CRIT] += 2.0f; stats_weights_[STATS_TYPE_HASTE] += 1.8f; + stats_weights_[STATS_TYPE_SPELL_POWER] -= 1.0f; stats_weights_[STATS_TYPE_RANGED_DPS] += 10.0f; } else if (cls == CLASS_ROGUE && tab == ROGUE_TAB_COMBAT) @@ -229,6 +237,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_HIT] += 2.1f; stats_weights_[STATS_TYPE_CRIT] += 1.4f; stats_weights_[STATS_TYPE_HASTE] += 1.7f; + stats_weights_[STATS_TYPE_SPELL_POWER] -= 1.0f; stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f; stats_weights_[STATS_TYPE_MELEE_DPS] += 7.0f; } @@ -253,64 +262,69 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_HIT] += 2.1f; stats_weights_[STATS_TYPE_CRIT] += 1.1f; stats_weights_[STATS_TYPE_HASTE] += 1.8f; + stats_weights_[STATS_TYPE_SPELL_POWER] -= 1.0f; stats_weights_[STATS_TYPE_EXPERTISE] += 2.1f; stats_weights_[STATS_TYPE_MELEE_DPS] += 5.0f; } else if (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY) { - stats_weights_[STATS_TYPE_AGILITY] += 1.8f; - stats_weights_[STATS_TYPE_STRENGTH] += 2.6f; - stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f; + stats_weights_[STATS_TYPE_AGILITY] += 0.8f; + stats_weights_[STATS_TYPE_STRENGTH] += 2.5f; + stats_weights_[STATS_TYPE_ATTACK_POWER] += 0.8f; stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 2.1f; stats_weights_[STATS_TYPE_HIT] += 2.3f; stats_weights_[STATS_TYPE_CRIT] += 2.2f; - stats_weights_[STATS_TYPE_HASTE] += 1.8f; + stats_weights_[STATS_TYPE_HASTE] += 0.8f; + stats_weights_[STATS_TYPE_SPELL_POWER] -= 2.0f; + stats_weights_[STATS_TYPE_DEFENSE] -= 1.0f; stats_weights_[STATS_TYPE_EXPERTISE] += 2.5f; stats_weights_[STATS_TYPE_MELEE_DPS] += 7.0f; } else if (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_ARMS) { - stats_weights_[STATS_TYPE_AGILITY] += 1.6f; - stats_weights_[STATS_TYPE_STRENGTH] += 2.3f; - stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f; + stats_weights_[STATS_TYPE_AGILITY] += 0.8f; + stats_weights_[STATS_TYPE_STRENGTH] += 2.5f; + stats_weights_[STATS_TYPE_ATTACK_POWER] += 0.8f; stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.7f; stats_weights_[STATS_TYPE_HIT] += 2.0f; stats_weights_[STATS_TYPE_CRIT] += 1.9f; stats_weights_[STATS_TYPE_HASTE] += 0.8f; + stats_weights_[STATS_TYPE_SPELL_POWER] -= 2.0f; + stats_weights_[STATS_TYPE_DEFENSE] -= 1.0f; stats_weights_[STATS_TYPE_EXPERTISE] += 1.4f; stats_weights_[STATS_TYPE_MELEE_DPS] += 7.0f; } else if (cls == CLASS_DEATH_KNIGHT && tab == DEATH_KNIGHT_TAB_FROST) { - stats_weights_[STATS_TYPE_AGILITY] += 1.7f; - stats_weights_[STATS_TYPE_STRENGTH] += 2.8f; - stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f; + stats_weights_[STATS_TYPE_AGILITY] += 0.5f; + stats_weights_[STATS_TYPE_STRENGTH] += 2.5f; + stats_weights_[STATS_TYPE_ATTACK_POWER] += 0.5f; stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 2.7f; stats_weights_[STATS_TYPE_HIT] += 2.3f; stats_weights_[STATS_TYPE_CRIT] += 2.2f; stats_weights_[STATS_TYPE_HASTE] += 2.1f; + stats_weights_[STATS_TYPE_SPELL_POWER] -= 1.0f; stats_weights_[STATS_TYPE_EXPERTISE] += 2.5f; stats_weights_[STATS_TYPE_MELEE_DPS] += 7.0f; } else if (cls == CLASS_DEATH_KNIGHT && tab == DEATH_KNIGHT_TAB_UNHOLY) { - stats_weights_[STATS_TYPE_AGILITY] += 0.9f; + stats_weights_[STATS_TYPE_AGILITY] += 0.5f; stats_weights_[STATS_TYPE_STRENGTH] += 2.5f; - stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f; + stats_weights_[STATS_TYPE_ATTACK_POWER] += 0.5f; stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.3f; stats_weights_[STATS_TYPE_HIT] += 2.2f; stats_weights_[STATS_TYPE_CRIT] += 1.7f; stats_weights_[STATS_TYPE_HASTE] += 1.8f; + stats_weights_[STATS_TYPE_SPELL_POWER] -= 1.0f; stats_weights_[STATS_TYPE_EXPERTISE] += 1.5f; stats_weights_[STATS_TYPE_MELEE_DPS] += 5.0f; } else if (cls == CLASS_PALADIN && tab == PALADIN_TAB_RETRIBUTION) { - stats_weights_[STATS_TYPE_AGILITY] += 1.6f; + stats_weights_[STATS_TYPE_AGILITY] += 0.5f; stats_weights_[STATS_TYPE_STRENGTH] += 2.5f; - stats_weights_[STATS_TYPE_INTELLECT] += 0.1f; - stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f; - stats_weights_[STATS_TYPE_SPELL_POWER] += 0.3f; + stats_weights_[STATS_TYPE_ATTACK_POWER] += 0.5f; stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.5f; stats_weights_[STATS_TYPE_HIT] += 1.9f; stats_weights_[STATS_TYPE_CRIT] += 1.7f; @@ -324,7 +338,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_STRENGTH] += 1.1f; stats_weights_[STATS_TYPE_INTELLECT] += 0.3f; stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f; - stats_weights_[STATS_TYPE_SPELL_POWER] += 0.95f; + stats_weights_[STATS_TYPE_SPELL_POWER] += 0.5f; stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 0.9f; stats_weights_[STATS_TYPE_HIT] += 2.1f; stats_weights_[STATS_TYPE_CRIT] += 1.5f; @@ -343,6 +357,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_HIT] += 1.1f; stats_weights_[STATS_TYPE_CRIT] += 0.8f; stats_weights_[STATS_TYPE_HASTE] += 1.0f; + stats_weights_[STATS_TYPE_ATTACK_POWER] -= 1.0f; stats_weights_[STATS_TYPE_RANGED_DPS] += 1.0f; } else if (cls == CLASS_MAGE && tab == MAGE_TAB_FIRE) @@ -353,15 +368,17 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_HIT] += 1.2f; stats_weights_[STATS_TYPE_CRIT] += 1.1f; stats_weights_[STATS_TYPE_HASTE] += 0.8f; + stats_weights_[STATS_TYPE_ATTACK_POWER] -= 1.0f; stats_weights_[STATS_TYPE_RANGED_DPS] += 1.0f; } else if (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ELEMENTAL) { - stats_weights_[STATS_TYPE_INTELLECT] += 0.25f; - stats_weights_[STATS_TYPE_SPELL_POWER] += 1.0f; + stats_weights_[STATS_TYPE_INTELLECT] += 0.5f; + stats_weights_[STATS_TYPE_SPELL_POWER] += 1.2f; stats_weights_[STATS_TYPE_HIT] += 1.1f; stats_weights_[STATS_TYPE_CRIT] += 0.8f; stats_weights_[STATS_TYPE_HASTE] += 1.0f; + stats_weights_[STATS_TYPE_MANA_REGENERATION] += 0.5f; } else if ((cls == CLASS_PALADIN && tab == PALADIN_TAB_HOLY) || (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_RESTORATION)) @@ -382,14 +399,15 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_MANA_REGENERATION] += 0.9f; stats_weights_[STATS_TYPE_CRIT] += 0.6f; stats_weights_[STATS_TYPE_HASTE] += 0.8f; + stats_weights_[STATS_TYPE_ATTACK_POWER] -= 1.0f; stats_weights_[STATS_TYPE_RANGED_DPS] += 1.0f; } else if ((cls == CLASS_WARRIOR && tab == WARRIOR_TAB_PROTECTION) || (cls == CLASS_PALADIN && tab == PALADIN_TAB_PROTECTION)) { - stats_weights_[STATS_TYPE_AGILITY] += 2.0f; - stats_weights_[STATS_TYPE_STRENGTH] += 1.0f; - stats_weights_[STATS_TYPE_STAMINA] += 3.5f; + stats_weights_[STATS_TYPE_AGILITY] += 0.2f; + stats_weights_[STATS_TYPE_STRENGTH] += 1.3f; + stats_weights_[STATS_TYPE_STAMINA] += 3.0f; stats_weights_[STATS_TYPE_ATTACK_POWER] += 0.2f; stats_weights_[STATS_TYPE_DEFENSE] += 2.5f; stats_weights_[STATS_TYPE_PARRY] += 2.0f; @@ -399,26 +417,26 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_BLOCK_VALUE] += 0.5f; stats_weights_[STATS_TYPE_ARMOR] += 0.15f; stats_weights_[STATS_TYPE_HIT] += 2.0f; - stats_weights_[STATS_TYPE_CRIT] += 0.2f; - stats_weights_[STATS_TYPE_HASTE] += 0.5f; + stats_weights_[STATS_TYPE_SPELL_POWER] -= 2.0f; stats_weights_[STATS_TYPE_EXPERTISE] += 3.0f; stats_weights_[STATS_TYPE_MELEE_DPS] += 2.0f; } else if (cls == CLASS_DEATH_KNIGHT && tab == DEATH_KNIGHT_TAB_BLOOD) { - stats_weights_[STATS_TYPE_AGILITY] += 2.0f; - stats_weights_[STATS_TYPE_STRENGTH] += 1.0f; - stats_weights_[STATS_TYPE_STAMINA] += 3.5f; + stats_weights_[STATS_TYPE_AGILITY] += 0.2f; + stats_weights_[STATS_TYPE_STRENGTH] += 1.3f; + stats_weights_[STATS_TYPE_STAMINA] += 3.0f; stats_weights_[STATS_TYPE_ATTACK_POWER] += 0.2f; - stats_weights_[STATS_TYPE_DEFENSE] += 3.5f; + stats_weights_[STATS_TYPE_DEFENSE] += 2.5f; stats_weights_[STATS_TYPE_PARRY] += 2.0f; stats_weights_[STATS_TYPE_DODGE] += 2.0f; + stats_weights_[STATS_TYPE_BLOCK_RATING] -= 2.0f; + stats_weights_[STATS_TYPE_BLOCK_VALUE] -= 2.0f; // stats_weights_[STATS_TYPE_RESILIENCE] += 2.0f; stats_weights_[STATS_TYPE_ARMOR] += 0.15f; stats_weights_[STATS_TYPE_HIT] += 2.0f; - stats_weights_[STATS_TYPE_CRIT] += 0.5f; - stats_weights_[STATS_TYPE_HASTE] += 0.5f; - stats_weights_[STATS_TYPE_EXPERTISE] += 3.5f; + stats_weights_[STATS_TYPE_SPELL_POWER] -= 1.0f; + stats_weights_[STATS_TYPE_EXPERTISE] += 3.0f; stats_weights_[STATS_TYPE_MELEE_DPS] += 2.0f; } else @@ -467,10 +485,23 @@ void StatsWeightCalculator::GenerateAdditionalWeights(Player* player) && !player->HasSpell(SPELL_MOLTEN_ARMOR_RANK_2) && !player->HasSpell(SPELL_MOLTEN_ARMOR_RANK_3)) { - stats_weights_[STATS_TYPE_INTELLECT] += 0.2f; - stats_weights_[STATS_TYPE_SPIRIT] -= 0.0f; + if (tab != MAGE_TAB_FIRE) + stats_weights_[STATS_TYPE_SPIRIT] -= 0.6f; + else + stats_weights_[STATS_TYPE_SPIRIT] -= 0.7f; } } + else if (cls == CLASS_WARLOCK) + { + if (!player->HasSpell(SPELL_FEL_ARMOR_RANK_1) && !player->HasSpell(SPELL_FEL_ARMOR_RANK_2) && + !player->HasSpell(SPELL_FEL_ARMOR_RANK_3) && !player->HasSpell(SPELL_FEL_ARMOR_RANK_4)) + stats_weights_[STATS_TYPE_SPIRIT] -= 0.4f; + } + + if (pvpSpec_ && !exclude_resilience_) + stats_weights_[STATS_TYPE_RESILIENCE] += 7.0f; + else if (!pvpSpec_) + stats_weights_[STATS_TYPE_RESILIENCE] -= 3.0f; } void StatsWeightCalculator::CalculateItemSetMod(Player* player, ItemTemplate const* proto) @@ -517,7 +548,7 @@ void StatsWeightCalculator::CalculateItemSetMod(Player* player, ItemTemplate con weight_ *= multiplier; } -void StatsWeightCalculator::CalculateSocketBonus(Player* player, ItemTemplate const* proto) +void StatsWeightCalculator::CalculateSocketBonus(Player* /*player*/, ItemTemplate const* proto) { uint32 socketNum = 0; for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT + MAX_GEM_SOCKETS; @@ -561,7 +592,8 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto) (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !player_->CanTitanGrip() && player_->CanDualWield()) || (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_PROTECTION) || - (cls == CLASS_PALADIN && tab == PALADIN_TAB_PROTECTION))) + (cls == CLASS_PALADIN && tab == PALADIN_TAB_PROTECTION) || + (cls == CLASS_PALADIN && tab == PALADIN_TAB_HOLY))) { weight_ *= 0.1; } @@ -580,11 +612,16 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto) weight_ *= 0.1; } // caster's main hand (cannot duel weapon but can equip two-hands stuff) - if (cls == CLASS_MAGE || cls == CLASS_PRIEST || cls == CLASS_WARLOCK || cls == CLASS_DRUID || - (cls == CLASS_SHAMAN && !player_->CanDualWield())) + if ((cls == CLASS_MAGE || cls == CLASS_PRIEST || cls == CLASS_WARLOCK || cls == CLASS_DRUID || + (cls == CLASS_SHAMAN && !player_->CanDualWield())) && + !(cls == CLASS_PALADIN && tab == PALADIN_TAB_HOLY)) { weight_ *= 0.65; } + if (cls == CLASS_PALADIN && tab == PALADIN_TAB_HOLY) + { + weight_ *= 0.8; + } } // fury with titan's grip if ((!isDoubleHand || proto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM || @@ -755,3 +792,163 @@ void StatsWeightCalculator::ApplyWeightFinetune(Player* player) } } } + +float StatsWeightCalculator::ApplyPreferredSpecWeapons(ItemTemplate const* proto, int32 slot) +{ + // Multiply score by 3x when this weapon's delay matches the spec-ideal speed. + float weight = 2.0f; + + // Applies to mainhand, offhand, and ranged slots only. + if (slot != EQUIPMENT_SLOT_MAINHAND && + slot != EQUIPMENT_SLOT_OFFHAND && + slot != EQUIPMENT_SLOT_RANGED) + return 1.0f; + + uint32 delay = proto->Delay; // milliseconds + float boost = 1.0f + weight; // applied on a match + + // Hunter: melee weapons are stat sticks — speed irrelevant. + // Ranged weapons scale Aimed/Chimera/Explosive Shot from top-end damage, + // so a slow ranged weapon (>=2600 ms) is strongly preferred. + if (cls == CLASS_HUNTER) + { + if (slot == EQUIPMENT_SLOT_RANGED && delay >= 2600) + return boost; + return 1.0f; + } + + // Feral Druid: forms normalise attack speed; raw weapon Delay is irrelevant. + if (cls == CLASS_DRUID && tab == DRUID_TAB_FERAL) + return 1.0f; + + switch (cls) + { + case CLASS_WARRIOR: + if (tab == WARRIOR_TAB_ARMS) + { + // Arms: slow 2H axes or polearms in mainhand only (Axe Specialization: +5% crit). + bool isAxeOrPolearm = (proto->SubClass == ITEM_SUBCLASS_WEAPON_AXE2 || + proto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM); + if (slot == EQUIPMENT_SLOT_MAINHAND && delay >= 3400 && isAxeOrPolearm) + return boost; + } + else if (tab == WARRIOR_TAB_FURY) + { + if (!player_->CanDualWield()) + { + // Pre-DW: treat like Arms — slow 2H in mainhand only. + if (slot == EQUIPMENT_SLOT_MAINHAND && delay >= 3400) + return boost; + } + else if (player_->CanTitanGrip()) + { + // Titan's Grip: slow 2H (>=3400) in both hands. + if (delay >= 3400) + return boost; + } + else + { + // 1H DW: slow 1H (>=2600) in both hands. + // 2H must be excluded — delay >= 2600 would otherwise pass + // for a 2H heirloom (~3600ms) just as it did for Enhancement. + if (proto->InventoryType == INVTYPE_2HWEAPON) + break; + if (delay >= 2600) + return boost; + } + } + else if (tab == WARRIOR_TAB_PROTECTION) + { + // Prot: slow 1H (>=2600) in mainhand. Shield in offhand, no speed bonus. + if (slot == EQUIPMENT_SLOT_MAINHAND && delay >= 2600) + return boost; + } + break; + + case CLASS_PALADIN: + if (tab == PALADIN_TAB_RETRIBUTION) + { + // Ret: slow 2H in mainhand only. + if (slot == EQUIPMENT_SLOT_MAINHAND && delay >= 3400) + return boost; + } + else if (tab == PALADIN_TAB_PROTECTION) + { + // Prot: slow 1H (>=2600) in mainhand. Shield in offhand. + if (slot == EQUIPMENT_SLOT_MAINHAND && delay >= 2600) + return boost; + } + break; + + case CLASS_DEATH_KNIGHT: + if (tab == DEATH_KNIGHT_TAB_BLOOD || tab == DEATH_KNIGHT_TAB_UNHOLY) + { + // Blood / Unholy: slow 2H in mainhand only. + if (slot == EQUIPMENT_SLOT_MAINHAND && delay >= 3400) + return boost; + } + else if (tab == DEATH_KNIGHT_TAB_FROST) + { + // Frost DK has Dual Wield innately — always dual-wields 1H. + if (proto->InventoryType == INVTYPE_2HWEAPON) + break; + if (delay >= 2600) + return boost; + } + break; + + case CLASS_SHAMAN: + if (tab == SHAMAN_TAB_ENHANCEMENT) + { + if (!player_->CanDualWield()) + { + // Pre-Dual Wield: Enhancement plays like a 2H spec. + if (slot == EQUIPMENT_SLOT_MAINHAND && delay >= 3400) + return boost; + } + else + { + // Post-Dual Wield: slow 1H (>=2600) in both hands. + if (proto->InventoryType == INVTYPE_2HWEAPON) + break; + + if (delay >= 2600) + { + float mult = boost; + if (slot == EQUIPMENT_SLOT_OFFHAND) + { + Item* mh = player_->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND); + if (mh && mh->GetTemplate() && mh->GetTemplate()->Delay == delay) + mult *= boost; // synchronized: ×(1+weight)² total = ×9 for 2.0f weight + } + return mult; + } + } + } + break; + + case CLASS_ROGUE: + if (tab == ROGUE_TAB_COMBAT) + { + // Combat: slow MH (>=2600), fast OH (<=1500). + if (slot == EQUIPMENT_SLOT_MAINHAND && delay >= 2600) + return boost; + if (slot == EQUIPMENT_SLOT_OFFHAND && delay <= 1500) + return boost; + } + else // Assassination or Subtlety: slow dagger MH, fast dagger OH. + { + bool isDagger = (proto->SubClass == ITEM_SUBCLASS_WEAPON_DAGGER); + if (slot == EQUIPMENT_SLOT_MAINHAND && isDagger && delay >= 1700) + return boost; + if (slot == EQUIPMENT_SLOT_OFFHAND && isDagger && delay <= 1500) + return boost; + } + break; + + default: + break; + } + + return 1.0f; +} diff --git a/src/Mgr/Item/StatsWeightCalculator.h b/src/Mgr/Item/StatsWeightCalculator.h index 4390e6af1..d97dafbb3 100644 --- a/src/Mgr/Item/StatsWeightCalculator.h +++ b/src/Mgr/Item/StatsWeightCalculator.h @@ -28,12 +28,14 @@ class StatsWeightCalculator public: StatsWeightCalculator(Player* player); void Reset(); - float CalculateItem(uint32 itemId, int32 randomPropertyId = 0); + float CalculateItem(uint32 itemId, int32 randomPropertyId = 0, int32 slot = -1); float CalculateEnchant(uint32 enchantId); void SetOverflowPenalty(bool apply) { enable_overflow_penalty_ = apply; } void SetItemSetBonus(bool apply) { enable_item_set_bonus_ = apply; } void SetQualityBlend(bool apply) { enable_quality_blend_ = apply; } + void SetPvpSpec(bool isPvp) { pvpSpec_ = isPvp; } + void SetExcludeResilience(bool exclude) { exclude_resilience_ = exclude; } private: void GenerateWeights(Player* player); @@ -45,6 +47,7 @@ public: void CalculateSocketBonus(Player* player, ItemTemplate const* proto); void CalculateItemTypePenalty(ItemTemplate const* proto); + float ApplyPreferredSpecWeapons(ItemTemplate const* proto, int32 slot); bool NotBestArmorType(uint32 item_subclass_armor); @@ -65,6 +68,8 @@ private: float weight_; float stats_weights_[STATS_TYPE_MAX]; + bool pvpSpec_ = false; + bool exclude_resilience_ = false; }; #endif diff --git a/src/Mgr/Talent/Talentspec.cpp b/src/Mgr/Talent/Talentspec.cpp index 7ee760ed4..2cb6ce5c4 100644 --- a/src/Mgr/Talent/Talentspec.cpp +++ b/src/Mgr/Talent/Talentspec.cpp @@ -142,7 +142,7 @@ bool TalentSpec::CheckTalents(uint32 level, std::ostringstream* out) } // Set the talents for the bots to the current spec. -void TalentSpec::ApplyTalents(Player* bot, std::ostringstream* out) +void TalentSpec::ApplyTalents(Player* bot, std::ostringstream* /*out*/) { for (auto& entry : talents) { @@ -397,7 +397,7 @@ uint32 TalentSpec::highestTree() return 0; } -std::string const TalentSpec::FormatSpec(Player* bot) +std::string const TalentSpec::FormatSpec(Player* /*bot*/) { // uint8 cls = bot->getClass(); //not used, (used in lined 403), line marked for removal. diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index 4c8979ace..1b5e6c98a 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -125,6 +125,8 @@ bool PlayerbotAIConfig::Initialize() incrementalGearInit = sConfigMgr->GetOption("AiPlayerbot.IncrementalGearInit", true); randomGearQualityLimit = sConfigMgr->GetOption("AiPlayerbot.RandomGearQualityLimit", 3); randomGearScoreLimit = sConfigMgr->GetOption("AiPlayerbot.RandomGearScoreLimit", 0); + preferClassArmorType = sConfigMgr->GetOption("AiPlayerbot.PreferClassArmorType", false); + preferredSpecWeapons = sConfigMgr->GetOption("AiPlayerbot.PreferredSpecWeapons", false); randomBotMinLevelChance = sConfigMgr->GetOption("AiPlayerbot.RandomBotMinLevelChance", 0.1f); randomBotMaxLevelChance = sConfigMgr->GetOption("AiPlayerbot.RandomBotMaxLevelChance", 0.1f); @@ -167,11 +169,13 @@ bool PlayerbotAIConfig::Initialize() pvpProhibitedZoneIds); LoadList>( sConfigMgr->GetOption("AiPlayerbot.PvpProhibitedAreaIds", - "976,35,392,2268,4161,4010,4317,4312,3649,3887,3958,3724,4080,3938,3754,3786,3973"), + "976,35,392,2268,4161,4010,4317,4312,3649,3887,3958,3724,4080,3938,3754,3786," + "3973,4085,4086,4087,4088"), pvpProhibitedAreaIds); fastReactInBG = sConfigMgr->GetOption("AiPlayerbot.FastReactInBG", true); LoadList>( - sConfigMgr->GetOption("AiPlayerbot.RandomBotQuestIds", "3802,5505,6502,7761,7848,10277,10285,11492,13188,13189,24499,24511,24710,24712"), + sConfigMgr->GetOption("AiPlayerbot.RandomBotQuestIds", "3802,5505,6502,7761,7848,10277,10285,11492," + "13188,13189,24499,24511,24710,24712"), randomBotQuestIds); LoadSet>( @@ -181,7 +185,8 @@ bool PlayerbotAIConfig::Initialize() "165739,165738,175245,175970,176325,176327,123329,2560"), disallowedGameObjects); LoadSet>( - sConfigMgr->GetOption("AiPlayerbot.AttunementQuests", "10279,10277,10282,10283,10284,10285,10296,10297,10298,11481,11482,11488,11490,11492,10901,10888,10445,10985"), + sConfigMgr->GetOption("AiPlayerbot.AttunementQuests", "10279,10277,10282,10283,10284,10285,10296," + "10297,10298,11481,11482,11488,11490,11492,10901,10888,10445,10985"), attunementQuests); LoadSet>( @@ -233,6 +238,8 @@ bool PlayerbotAIConfig::Initialize() EnableICCBuffs = sConfigMgr->GetOption("AiPlayerbot.EnableICCBuffs", true); //////////////////////////// Professions + classMatchingProfessionChance = + std::min(100, sConfigMgr->GetOption("AiPlayerbot.ClassMatchingProfessionChance", 30)); fishingDistanceFromMaster = sConfigMgr->GetOption("AiPlayerbot.FishingDistanceFromMaster", 10.0f); endFishingWithMaster = sConfigMgr->GetOption("AiPlayerbot.EndFishingWithMaster", 30.0f); fishingDistance = sConfigMgr->GetOption("AiPlayerbot.FishingDistance", 40.0f); diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index 8b5dc0922..2a133f938 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -128,6 +128,8 @@ public: bool incrementalGearInit; int32 randomGearQualityLimit; int32 randomGearScoreLimit; + bool preferClassArmorType; + bool preferredSpecWeapons; float randomBotMinLevelChance, randomBotMaxLevelChance; float randomBotRpgChance; uint32 minRandomBots, maxRandomBots; @@ -152,6 +154,7 @@ public: // Professions bool enableFishingWithMaster; + uint32 classMatchingProfessionChance; float fishingDistanceFromMaster, fishingDistance, endFishingWithMaster; // chat diff --git a/src/Script/Playerbots.cpp b/src/Script/Playerbots.cpp index 7db91eb40..5be7e8855 100644 --- a/src/Script/Playerbots.cpp +++ b/src/Script/Playerbots.cpp @@ -226,7 +226,7 @@ public: return true; } - bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Guild* guild) override + bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Guild* /*guild*/) override { if (type != CHAT_MSG_GUILD) return true; @@ -445,7 +445,7 @@ public: playerbotMgr->HandleMasterOutgoingPacket(*packet); } - void OnPlayerbotUpdate(uint32 diff) override + void OnPlayerbotUpdate(uint32 /*diff*/) override { sRandomPlayerbotMgr.UpdateSessions(); // Per-bot updates only } diff --git a/src/Util/ServerFacade.cpp b/src/Util/ServerFacade.cpp index 1ba1eb5ea..bc17e5fe3 100644 --- a/src/Util/ServerFacade.cpp +++ b/src/Util/ServerFacade.cpp @@ -39,7 +39,7 @@ bool ServerFacade::IsDistanceGreaterOrEqualThan(float dist1, float dist2) { retu bool ServerFacade::IsDistanceLessOrEqualThan(float dist1, float dist2) { return !IsDistanceGreaterThan(dist1, dist2); } -void ServerFacade::SetFacingTo(Player* bot, WorldObject* wo, bool force) +void ServerFacade::SetFacingTo(Player* bot, WorldObject* wo, bool /*force*/) { if (!bot) return;