Compare commits

..

1 Commits

Author SHA1 Message Date
bash
3a756f5840 Revert "Fixed opening trade window while using DBM or Questie addon (#1363)"
This reverts commit dfa87faf5e.
2025-06-09 00:27:17 +02:00
198 changed files with 8968 additions and 20628 deletions

View File

@@ -32,7 +32,6 @@
# ACTIVITIES
# SPELLS
# STRATEGIES
# RPG STRATEGY
# TELEPORTS
# BATTLEGROUND & ARENA & PVP
# INTERVALS
@@ -484,8 +483,8 @@ AiPlayerbot.AutoGearQualityLimit = 3
# Equipment item level (not gearscore) limitation for autogear command (0 = no limit)
# Classic
# Max iLVL Tier 1 = 66 | Tier 2 = 76 | Tier 2.5 = 88 | Tier 3 = 92
# Max iLVL Phase 1(MC, Ony, ZG) = 78 | Phase 2(BWL) = 83 | Phase 2.5(AQ40) = 88 | Phase 3(Naxx40) = 92
# Max iLVL Tier 1 = 66 | Tier 2 = 76 | Tier 2.5 = 81 | Tier 3 = 99
# Max iLVL Phase 1 = 71(MC, ONY, ZG) | Phase 2(BWL) = 77 | Phase 2.5(AQ) = 88 | Phase 3(NAXX) = 100 (NOT RECOMMENDED SINCE ILVL OVERLAPS BETWEEN TIERS)
# TBC
# Max iLVL Tier 4 = 120 | Tier 5 = 133 | Tier 6 = 164
# Max iLVL Phase 1(Kara, Gruul, Mag) = 125 | Phase 1.5(ZA) = 138 | Phase 2(SC, TK) = 141 | Phase 3(Hyjal, BT) = 156 | Phase 4(Sunwell) = 164
@@ -501,10 +500,9 @@ AiPlayerbot.AutoGearScoreLimit = 0
# "mana" (bots have infinite mana)
# "power" (bots have infinite energy, rage, and runic power)
# "taxi" (bots may use all flight paths, though they will not actually learn them)
# "raid" (bots use cheats implemented into raid strategies)
# To use multiple cheats, separate them by commas below (e.g., to enable all, use "gold,health,mana,power,raid,taxi")
# Default: taxi and raid are enabled
AiPlayerbot.BotCheats = "taxi,raid"
# To use multiple cheats, separate them by commas below (e.g., to enable all, use "gold,health,mana,power,taxi")
# Default: taxi is enabled
AiPlayerbot.BotCheats = "taxi"
#
#
@@ -590,9 +588,6 @@ AiPlayerbot.LimitTalentsExpansion = 0
# Default: 1 (enabled)
AiPlayerbot.EnableRandomBotTrading = 1
# Configure message prefixes which will be excluded in analysis in trade action to open trade window
AiPlayerbot.TradeActionExcludedPrefixes = "RPLL_H_,DBMv4,{звезда} Questie,{rt1} Questie"
#
#
#
@@ -647,8 +642,8 @@ AiPlayerbot.RandomGearQualityLimit = 3
# Equipment item level (not gearscore) limitation for randombots (0 = no limit)
# Classic
# Max iLVL Tier 1 = 66 | Tier 2 = 76 | Tier 2.5 = 88 | Tier 3 = 92
# Max iLVL Phase 1(MC, Ony, ZG) = 78 | Phase 2(BWL) = 83 | Phase 2.5(AQ40) = 88 | Phase 3(Naxx40) = 92
# Max iLVL Tier 1 = 66 | Tier 2 = 76 | Tier 2.5 = 81 | Tier 3 = 99
# Max iLVL Phase 1 = 71(MC, ONY, ZG) | Phase 2(BWL) = 77 | Phase 2.5(AQ) = 88 | Phase 3(NAXX) = 100 (NOT RECOMMENDED SINCE ILVL OVERLAPS BETWEEN TIERS)
# TBC
# Max iLVL Tier 4 = 120 | Tier 5 = 133 | Tier 6 = 164
# Max iLVL Phase 1(Kara, Gruul, Mag) = 125 | Phase 1.5(ZA) = 138 | Phase 2(SC, TK) = 141 | Phase 3(Hyjal, BT) = 156 | Phase 4(Sunwell) = 164
@@ -658,16 +653,12 @@ AiPlayerbot.RandomGearQualityLimit = 3
# Default: 0 (no limit)
AiPlayerbot.RandomGearScoreLimit = 0
# If disabled, random bots can only upgrade equipment through looting and quests
# Default: 1 (enabled)
AiPlayerbot.IncrementalGearInit = 1
# Set minimum level of bots that will enchant their equipment (Maxlevel + 1 to disable)
# Default: 60
AiPlayerbot.MinEnchantingBotLevel = 60
# Enable expansion limitation for bot enchants
# If enabled, bots will not use TBC enchants until level 61 or WotLK enchants until level 71
# If enabled, bots will not use TBC enchants until level 61 or WotLK enchanges until level 71
# Default: 1 (enabled)
AiPlayerbot.LimitEnchantExpansion = 1
@@ -699,10 +690,6 @@ AiPlayerbot.AutoUpgradeEquip = 1
# Default: 0 (disabled)
AiPlayerbot.HunterWolfPet = 0
# Prohibit hunter bots from creating pets with any family ID listed below in ExcludedHunterPetFamilies
# See the creature_family database table for all pet families by ID (note: ID for spiders is 3)
AiPlayerbot.ExcludedHunterPetFamilies = ""
#
#
#
@@ -730,8 +717,8 @@ AiPlayerbot.BotActiveAloneForceWhenInGuild = 1
# The default is 1. When enabled (smart) scales the 'BotActiveAlone' value.
# (The scaling will be overruled by the BotActiveAloneForceWhen...rules)
#
# Limitfloor - when DIFF (latency) is above floor, activity scaling begins
# LimitCeiling - when DIFF (latency) is above ceiling, activity is 0%
# Limitfloor - when DIFF (latency) above floor, activity scaling is applied starting with 90%
# LimitCeiling - when DIFF (latency) above ceiling, activity is 0%;
#
# MinLevel - only apply scaling when level is above or equal to min(bot)Level
# MaxLevel - only apply scaling when level is lower or equal of max(bot)Level
@@ -762,6 +749,11 @@ AiPlayerbot.RandomBotGroupNearby = 0
# Default: 1 (enabled)
AiPlayerbot.AutoDoQuests = 1
# Randombots will behave more like real players (experimental)
# This option will override AiPlayerbot.AutoDoQuests, RandomBotTeleLowerLevel, and RandomBotTeleHigherLevel
# Default: 1 (enabled)
AiPlayerbot.EnableNewRpgStrategy = 1
# Quest items to keep in bots' inventories (do not destroy)
AiPlayerbot.RandomBotQuestItems = "5175,5176,5177,5178,6948,11000,12382,13704,16309"
@@ -813,48 +805,42 @@ AiPlayerbot.RandomBotNonCombatStrategies = ""
AiPlayerbot.CombatStrategies = ""
AiPlayerbot.NonCombatStrategies = ""
# Remove "healer dps" strategy on specified maps.
# Default: 0 (disabled)
AiPlayerbot.HealerDPSMapRestriction = 0
# List of Map IDs where "healer dps" strategy will be removed if AiPlayerbot.HealerDPSMapRestriction is enabled
# Default: (Dungeon and Raid maps) "33,34,36,43,47,48,70,90,109,129,209,229,230,329,349,389,429,1001,1004,1007,269,540,542,543,545,546,547,552,553,554,555,556,557,558,560,585,574,575,576,578,595,599,600,601,602,604,608,619,632,650,658,668,409,469,509,531,532,534,544,548,550,564,565,580,249,533,603,615,616,624,631,649,724"
AiPlayerbot.RestrictedHealerDPSMaps = "33,34,36,43,47,48,70,90,109,129,209,229,230,329,349,389,429,1001,1004,1007,269,540,542,543,545,546,547,552,553,554,555,556,557,558,560,585,574,575,576,578,595,599,600,601,602,604,608,619,632,650,658,668,409,469,509,531,532,534,544,548,550,564,565,580,249,533,603,615,616,624,631,649,724"
#
#
#
####################################################################################################
####################################################################################################
# RPG STRATEGY
# TELEPORTS
#
#
# Randombots will behave more like real players (experimental)
# This option will override AiPlayerbot.AutoDoQuests, RandomBotTeleLowerLevel, and RandomBotTeleHigherLevel
# Maps where bots can be teleported to
AiPlayerbot.RandomBotMaps = 0,1,530,571
# Probabilty bots teleport to banker (city)
# Default: 0.25
AiPlayerbot.ProbTeleToBankers = 0.25
# How far randombots are teleported after death
AiPlayerbot.RandomBotTeleportDistance = 100
# How many levels below the lowest-level creature in a zone, can a bot be
# This will have no effect if AiPlayerbot.EnableNewRpgStrategy is enabled
# Default: 1 (randombot will leave if they are more than 1 level lower)
AiPlayerbot.RandomBotTeleLowerLevel = 1
# How many levels above the highest-level creature in a zone, can a bot be
# This will have no effect if AiPlayerbot.EnableNewRpgStrategy is enabled
# Default: 3 (randombot will leave if they are more than 3 levels higher)
AiPlayerbot.RandomBotTeleHigherLevel = 3
# Bots automatically teleport to another place for leveling on levelup
# Default: 1 (enabled)
AiPlayerbot.EnableNewRpgStrategy = 1
# Control probability weights for RPG status of bots. Takes effect only when the status meets its premise.
# Sum of weights need not be 100. Set to 0 to disable the status.
#
# WanderRandom (Default: 15 Move randomly nearby to find and kill mobs)
# WanderNpc (Default: 20 Randomly interact with nearby NPCs)
# GoGrind (Default: 15 Go to nearby level-appropriate locations to grind for killing mobs)
# GoCamp (Default: 10 Return to a nearby camp depending on innkeeper/flightmaster)
# DoQuest (Default: 60 Select quest from the quest log and head to the location to attempt completion)
# TravelFlight (Default: 15 Go to the nearest flightmaster and fly to a level-appropriate area)
# Rest (Default: 5 Take a break for a while and do nothing)
AiPlayerbot.RpgStatusProbWeight.WanderRandom = 15
AiPlayerbot.RpgStatusProbWeight.WanderNpc = 20
AiPlayerbot.RpgStatusProbWeight.GoGrind = 15
AiPlayerbot.RpgStatusProbWeight.GoCamp = 10
AiPlayerbot.RpgStatusProbWeight.DoQuest = 60
AiPlayerbot.RpgStatusProbWeight.TravelFlight = 15
AiPlayerbot.RpgStatusProbWeight.Rest = 5
AiPlayerbot.AutoTeleportForLevel = 1
# Bots' minimum and maximum level when teleporting in and out of a zone, according to the new RPG strategy
# Requires EnableNewRpgStrategy enabled
# Format: AiPlayerbot.ZoneBracket.zoneID = minLevel,maxLevel
#
# Classic WoW - Low-level zones:
@@ -994,40 +980,6 @@ AiPlayerbot.ZoneBracket.4197 = 79,80
#
####################################################################################################
####################################################################################################
# TELEPORTS
#
#
# Maps where bots can be teleported to
AiPlayerbot.RandomBotMaps = 0,1,530,571
# Probabilty bots teleport to banker (city)
# Default: 0.25
AiPlayerbot.ProbTeleToBankers = 0.25
# How far randombots are teleported after death
AiPlayerbot.RandomBotTeleportDistance = 100
# How many levels below the lowest-level creature in a zone, can a bot be
# This will have no effect if AiPlayerbot.EnableNewRpgStrategy is enabled
# Default: 1 (randombot will leave if they are more than 1 level lower)
AiPlayerbot.RandomBotTeleLowerLevel = 1
# How many levels above the highest-level creature in a zone, can a bot be
# This will have no effect if AiPlayerbot.EnableNewRpgStrategy is enabled
# Default: 3 (randombot will leave if they are more than 3 levels higher)
AiPlayerbot.RandomBotTeleHigherLevel = 3
# Bots automatically teleport to another place for leveling on levelup
# Default: 1 (enabled)
AiPlayerbot.AutoTeleportForLevel = 1
#
#
#
####################################################################################################
####################################################################################################
# BATTLEGROUNDS & ARENAS & PVP
#
@@ -1107,10 +1059,10 @@ AiPlayerbot.RandomBotArenaTeamMinRating = 1000
AiPlayerbot.DeleteRandomBotArenaTeams = 0
# PvP Restricted Zones (bots don't pvp)
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"
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,139"
# PvP Restricted Areas (bots don't pvp)
AiPlayerbot.PvpProhibitedAreaIds = "976,35,392,2268,4161,4010,4317,4312,3649,3887,3958,3724,4080"
AiPlayerbot.PvpProhibitedAreaIds = "976,35,392,2268,4161,4010,4317,4312"
# Improve reaction speeds in battlegrounds and arenas (may cause lag)
AiPlayerbot.FastReactInBG = 1
@@ -1184,18 +1136,6 @@ AiPlayerbot.PremadeSpecName.1.2 = prot pve
AiPlayerbot.PremadeSpecGlyph.1.2 = 43424,43395,43425,43399,49084,45793
AiPlayerbot.PremadeSpecLink.1.2.60 = --053351225000210521030113321
AiPlayerbot.PremadeSpecLink.1.2.80 = 3500030023-301-053351225000210521030113321
AiPlayerbot.PremadeSpecName.1.3 = arms pvp
AiPlayerbot.PremadeSpecGlyph.1.3 = 43417,43397,43423,43396,49084,43421
AiPlayerbot.PremadeSpecLink.1.3.60 = 0320232023331100032212012221251
AiPlayerbot.PremadeSpecLink.1.3.80 = 0320332023335100232212013231251-3250001
AiPlayerbot.PremadeSpecName.1.4 = fury pvp
AiPlayerbot.PremadeSpecGlyph.1.4 = 43432,43397,43417,43395,43396,43418
AiPlayerbot.PremadeSpecLink.1.4.60 = -325000131500212250120511351
AiPlayerbot.PremadeSpecLink.1.4.80 = 03220300233-325000131500212250122511351
AiPlayerbot.PremadeSpecName.1.5 = prot pvp
AiPlayerbot.PremadeSpecGlyph.1.5 = 43425,43397,43415,43396,49084,45792
AiPlayerbot.PremadeSpecLink.1.5.60 = --250031220223012520332113321
AiPlayerbot.PremadeSpecLink.1.5.80 = 0502300123-3-250031220223012521332113321
#
#
@@ -1220,18 +1160,6 @@ AiPlayerbot.PremadeSpecGlyph.2.2 = 41092,43367,41099,43369,43365,43869
AiPlayerbot.PremadeSpecLink.2.2.60 = --05230051203331302133231131
AiPlayerbot.PremadeSpecLink.2.2.65 = -05-05230051203331302133231131
AiPlayerbot.PremadeSpecLink.2.2.80 = 050501-05-05232051203331302133231331
AiPlayerbot.PremadeSpecName.2.3 = holy pvp
AiPlayerbot.PremadeSpecGlyph.2.3 = 41110,43367,45746,43366,43365,45747
AiPlayerbot.PremadeSpecLink.2.3.60 = 50332150300013050133215221
AiPlayerbot.PremadeSpecLink.2.3.80 = 50332150300013050133315221-5032013122
AiPlayerbot.PremadeSpecName.2.4 = prot pvp
AiPlayerbot.PremadeSpecGlyph.2.4 = 41092,43369,41101,43368,43365,45745
AiPlayerbot.PremadeSpecLink.2.4.60 = -15320130223122311323311321
AiPlayerbot.PremadeSpecLink.2.4.80 = -15320130223122321333312321-052300502
AiPlayerbot.PremadeSpecName.2.5 = ret pvp
AiPlayerbot.PremadeSpecGlyph.2.5 = 41095,43369,41102,43368,43365,45747
AiPlayerbot.PremadeSpecLink.2.5.60 = --05230250203331222133201321
AiPlayerbot.PremadeSpecLink.2.5.80 = -1532013022-05230250203331322133201321
#
#
@@ -1244,31 +1172,17 @@ AiPlayerbot.PremadeSpecLink.2.5.80 = -1532013022-05230250203331322133201321
#
AiPlayerbot.PremadeSpecName.3.0 = bm pve
AiPlayerbot.PremadeSpecGlyph.3.0 = 42912,43350,42902,43351,43338,42914
AiPlayerbot.PremadeSpecLink.3.0.40 = 512002015051122301
AiPlayerbot.PremadeSpecLink.3.0.60 = 51200201505112233110531151
AiPlayerbot.PremadeSpecLink.3.0.80 = 51200201505112243130531351-005305101
AiPlayerbot.PremadeSpecGlyph.3.0 = 42912,43350,42902,43351,43338,45732
AiPlayerbot.PremadeSpecLink.3.0.60 = 51200201505112243110531051
AiPlayerbot.PremadeSpecLink.3.0.80 = 51200201505112243120531251-025305101
AiPlayerbot.PremadeSpecName.3.1 = mm pve
AiPlayerbot.PremadeSpecGlyph.3.1 = 42912,43350,45625,43351,43338,42914
AiPlayerbot.PremadeSpecLink.3.1.60 = -035305101030013233115031151
AiPlayerbot.PremadeSpecLink.3.1.80 = 502-035305101230013233135031351-5000002
AiPlayerbot.PremadeSpecGlyph.3.1 = 42912,43350,42914,43351,43338,45732
AiPlayerbot.PremadeSpecLink.3.1.60 = -025315101030013233125031051
AiPlayerbot.PremadeSpecLink.3.1.80 = 502-025335101030013233135031351-5000002
AiPlayerbot.PremadeSpecName.3.2 = surv pve
AiPlayerbot.PremadeSpecGlyph.3.2 = 45733,43350,45731,43351,43338,45732
AiPlayerbot.PremadeSpecGlyph.3.2 = 42912,43350,45731,43351,43338,45732
AiPlayerbot.PremadeSpecLink.3.2.60 = --5000032500033330502135201311
AiPlayerbot.PremadeSpecLink.3.2.80 = -005305101-5000032500033330532135301321
AiPlayerbot.PremadeSpecName.3.3 = bm pvp
AiPlayerbot.PremadeSpecGlyph.3.3 = 42897,42900,42902,43356,43338,42900
AiPlayerbot.PremadeSpecLink.3.3.60 = 05203201505012233100531151
AiPlayerbot.PremadeSpecLink.3.3.80 = 05203201505012233100531351-005305101-03
AiPlayerbot.PremadeSpecName.3.4 = mm pvp
AiPlayerbot.PremadeSpecGlyph.3.4 = 42912,43351,42897,43338,43356,42904
AiPlayerbot.PremadeSpecLink.3.4.60 = -034305101030213231135031051
AiPlayerbot.PremadeSpecLink.3.4.80 = -035305101030213233135031051-53013020102
AiPlayerbot.PremadeSpecName.3.5 = surv pvp
AiPlayerbot.PremadeSpecGlyph.3.5 = 42912,43350,42904,43356,43338,45731
AiPlayerbot.PremadeSpecLink.3.5.60 = --2300302410233030533135001031
AiPlayerbot.PremadeSpecLink.3.5.80 = -005305201-2300302510233330533135001031
# HUNTER PET
#
@@ -1304,18 +1218,6 @@ AiPlayerbot.PremadeSpecName.4.2 = subtlety pve
AiPlayerbot.PremadeSpecGlyph.4.2 = 42967,43379,45764,43380,43378,45767
AiPlayerbot.PremadeSpecLink.4.2.60 = --5022012030321121350115031151
AiPlayerbot.PremadeSpecLink.4.2.80 = 30532010114--5022012030321121350115031151
AiPlayerbot.PremadeSpecName.4.3 = as pvp
AiPlayerbot.PremadeSpecGlyph.4.3 = 42974,43380,45768,43379,43376,42971
AiPlayerbot.PremadeSpecLink.4.3.60 = 005303103342102522103031--50002
AiPlayerbot.PremadeSpecLink.4.3.80 = 005303103342102522103031-004-532023203000012
AiPlayerbot.PremadeSpecName.4.4 = combat pvp
AiPlayerbot.PremadeSpecGlyph.4.4 = 42972,43380,45762,43376,43378,42971
AiPlayerbot.PremadeSpecLink.4.4.60 = -3250002050225010223102321251
AiPlayerbot.PremadeSpecLink.4.4.80 = 305120105-3250002050235010223102521251
AiPlayerbot.PremadeSpecName.4.5 = subtlety pvp
AiPlayerbot.PremadeSpecGlyph.4.5 = 42968,43376,45764,43380,43379,42971
AiPlayerbot.PremadeSpecLink.4.5.60 = --5120212030320121330133221251
AiPlayerbot.PremadeSpecLink.4.5.80 = 3023031-3-5120212030320121350135231251
#
#
@@ -1339,18 +1241,6 @@ AiPlayerbot.PremadeSpecName.5.2 = shadow pve
AiPlayerbot.PremadeSpecGlyph.5.2 = 42406,43371,42407,43374,43342,42415
AiPlayerbot.PremadeSpecLink.5.2.60 = --325003041203010323150301351
AiPlayerbot.PremadeSpecLink.5.2.80 = 0503203--325023051223010323152301351
AiPlayerbot.PremadeSpecName.5.3 = disc pvp
AiPlayerbot.PremadeSpecGlyph.5.3 = 42408,43371,45760,43370,43374,45756
AiPlayerbot.PremadeSpecLink.5.3.60 = 5003203130320512201323031051
AiPlayerbot.PremadeSpecLink.5.3.80 = 5003203130322512331013231151-23050113
AiPlayerbot.PremadeSpecName.5.4 = holy pvp
AiPlayerbot.PremadeSpecGlyph.5.4 = 42411,43371,42408,43370,43374,45755
AiPlayerbot.PremadeSpecLink.5.4.60 = -235501031000152430320031151
AiPlayerbot.PremadeSpecLink.5.4.80 = 500320313-235501031000152530320031351
AiPlayerbot.PremadeSpecName.5.5 = shadow pvp
AiPlayerbot.PremadeSpecGlyph.5.5 = 42407,43371,45753,43370,43374,42408
AiPlayerbot.PremadeSpecLink.5.5.60 = --005323241223112003102311351
AiPlayerbot.PremadeSpecLink.5.5.80 = 50332031003--005323241223112003102311351
#
#
@@ -1378,19 +1268,6 @@ AiPlayerbot.PremadeSpecName.6.3 = double aura blood pve
AiPlayerbot.PremadeSpecGlyph.6.3 = 45805,43673,43827,43544,43672,43554
AiPlayerbot.PremadeSpecLink.6.3.60 = 005512153330030320102013-305
AiPlayerbot.PremadeSpecLink.6.3.80 = 005512153330030320102013-3050505002023001-002
AiPlayerbot.PremadeSpecName.6.4 = blood pvp
AiPlayerbot.PremadeSpecGlyph.6.4 = 43534,43535,45799,43673,43672,45805
AiPlayerbot.PremadeSpecLink.6.4.60 = 2305021503003313201222101351
AiPlayerbot.PremadeSpecLink.6.4.80 = 2305021503003313201222101351--032232300023
AiPlayerbot.PremadeSpecName.6.5 = frost pvp
AiPlayerbot.PremadeSpecGlyph.6.5 = 43543,43539,45800,43673,43672,45806
AiPlayerbot.PremadeSpecLink.6.5.60 = -32015351022203012001233101251
AiPlayerbot.PremadeSpecLink.6.5.80 = 0055-32015351052203012001233131351-03
AiPlayerbot.PremadeSpecName.6.6 = unholy pvp
AiPlayerbot.PremadeSpecGlyph.6.6 = 45804,43539,43549,43673,43672,45805
AiPlayerbot.PremadeSpecLink.6.6.60 = --2301323301002152230101203103151
AiPlayerbot.PremadeSpecLink.6.6.80 = -320050410002-2301323301002152230101203133151
#
#
@@ -1414,19 +1291,6 @@ AiPlayerbot.PremadeSpecName.7.2 = resto pve
AiPlayerbot.PremadeSpecGlyph.7.2 = 41517,43385,41527,43386,44923,45775
AiPlayerbot.PremadeSpecLink.7.2.60 = --50005301235310501102321251
AiPlayerbot.PremadeSpecLink.7.2.80 = -00502033-50005331335310501122331251
AiPlayerbot.PremadeSpecName.7.3 = ele pvp
AiPlayerbot.PremadeSpecGlyph.7.3 = 45778,43388,45770,43725,43386,41524
AiPlayerbot.PremadeSpecLink.7.3.60 = 0533001503213051322301341
AiPlayerbot.PremadeSpecLink.7.3.80 = 0533051503213051322331351-023212001
AiPlayerbot.PremadeSpecName.7.4 = enh pvp
AiPlayerbot.PremadeSpecGlyph.7.4 = 45778,43388,41526,43725,43344,45771
AiPlayerbot.PremadeSpecLink.7.4.60 = -02305203105001333201131131151
AiPlayerbot.PremadeSpecLink.7.4.80 = 0503351-02305203105001333211131231251
AiPlayerbot.PremadeSpecName.7.5 = resto pvp
AiPlayerbot.PremadeSpecGlyph.7.5 = 45778,43388,45775,43725,43344,41535
AiPlayerbot.PremadeSpecLink.7.5.60 = --05032331331013501120321251
AiPlayerbot.PremadeSpecLink.7.5.80 = -023222301004-05032331331013501120331251
#
#
@@ -1440,8 +1304,8 @@ AiPlayerbot.PremadeSpecLink.7.5.80 = -023222301004-05032331331013501120331251
AiPlayerbot.PremadeSpecName.8.0 = arcane pve
AiPlayerbot.PremadeSpecGlyph.8.0 = 42735,43339,44955,43364,43361,42751
AiPlayerbot.PremadeSpecLink.8.0.60 = 230005231100330150323102500321
AiPlayerbot.PremadeSpecLink.8.0.80 = 230005231100330150323102505321-03-203303001
AiPlayerbot.PremadeSpecLink.8.0.60 = 23000503110033014032310150532
AiPlayerbot.PremadeSpecLink.8.0.80 = 23000523310033015032310250532-03-203203001
AiPlayerbot.PremadeSpecName.8.1 = fire pve
AiPlayerbot.PremadeSpecGlyph.8.1 = 42739,43339,45737,43364,44920,42751
AiPlayerbot.PremadeSpecLink.8.1.60 = -0055030011302231053120321341
@@ -1453,20 +1317,7 @@ AiPlayerbot.PremadeSpecLink.8.2.80 = 23002303110003--053303031320310003015223135
AiPlayerbot.PremadeSpecName.8.3 = frostfire pve
AiPlayerbot.PremadeSpecGlyph.8.3 = 44684,44920,42751,43339,43364,45737
AiPlayerbot.PremadeSpecLink.8.3.60 = -2305032012303331053120300051
AiPlayerbot.PremadeSpecLink.8.3.80 = -2305032012303331053120321351-023302031
AiPlayerbot.PremadeSpecName.8.4 = arcane pvp
AiPlayerbot.PremadeSpecGlyph.8.4 = 42735,43364,42738,43360,43357,42752
AiPlayerbot.PremadeSpecLink.8.4.60 = 205323200122032103303102015221
AiPlayerbot.PremadeSpecLink.8.4.80 = 205323200122032103303102015321-23002-303020301
AiPlayerbot.PremadeSpecName.8.5 = fire pvp
AiPlayerbot.PremadeSpecGlyph.8.5 = 42738,43364,42752,43360,43357,45737
AiPlayerbot.PremadeSpecLink.8.5.60 = -2305202312020031223122301351
AiPlayerbot.PremadeSpecLink.8.5.80 = 230321030122-2305212312020031223122301351
AiPlayerbot.PremadeSpecName.8.6 = frost pvp
AiPlayerbot.PremadeSpecGlyph.8.6 = 42738,43364,45740,43357,43360,42752
AiPlayerbot.PremadeSpecLink.8.6.60 = --3533203210203100232102231151
AiPlayerbot.PremadeSpecLink.8.6.80 = 23032103010203--3533203210203100232102231151
AiPlayerbot.PremadeSpecLink.8.3.80 = -2305032012303331053120311351-023303031
#
#
@@ -1482,7 +1333,7 @@ AiPlayerbot.PremadeSpecName.9.0 = affli pve
AiPlayerbot.PremadeSpecGlyph.9.0 = 45785,43390,50077,43394,43393,45779
AiPlayerbot.PremadeSpecLink.9.0.60 = 2350022001113510053500131151
AiPlayerbot.PremadeSpecLink.9.0.70 = 2350022001113510053500131151--55
AiPlayerbot.PremadeSpecLink.9.0.80 = 2350022001123510253500331151--55000005
AiPlayerbot.PremadeSpecLink.9.0.80 = 2350022001113510253500331151--5500000501
AiPlayerbot.PremadeSpecName.9.1 = demo pve
AiPlayerbot.PremadeSpecGlyph.9.1 = 45785,43390,50077,43394,43393,42459
AiPlayerbot.PremadeSpecLink.9.1.60 = -003203301135112530135201051
@@ -1490,21 +1341,8 @@ AiPlayerbot.PremadeSpecLink.9.1.70 = -003203301135112530135201051-55
AiPlayerbot.PremadeSpecLink.9.1.80 = -003203301135112530135221351-55000005
AiPlayerbot.PremadeSpecName.9.2 = destro pve
AiPlayerbot.PremadeSpecGlyph.9.2 = 45785,43390,50077,43394,43393,42454
AiPlayerbot.PremadeSpecLink.9.2.60 = --05203215200231051305031151
AiPlayerbot.PremadeSpecLink.9.2.80 = 23-0302-05203215220331051335231351
AiPlayerbot.PremadeSpecName.9.3 = affli pvp
AiPlayerbot.PremadeSpecGlyph.9.3 = 50077,43392,42455,43390,43389,45783
AiPlayerbot.PremadeSpecLink.9.3.60 = 0350002231223011053502301151
AiPlayerbot.PremadeSpecLink.9.3.80 = 2350002231223111053502301151-2032003011302
AiPlayerbot.PremadeSpecName.9.4 = demo pvp
AiPlayerbot.PremadeSpecGlyph.9.4 = 42459,43392,45780,43390,43389,45783
AiPlayerbot.PremadeSpecLink.9.4.60 = -003203301135202530135001251
AiPlayerbot.PremadeSpecLink.9.4.80 = -003203301135202530135011351-052300152
AiPlayerbot.PremadeSpecName.9.5 = destro pvp
AiPlayerbot.PremadeSpecGlyph.9.5 = 42471,43392,42454,43390,43389,45783
AiPlayerbot.PremadeSpecLink.9.5.60 = --05230015220331351005031051
AiPlayerbot.PremadeSpecLink.9.5.80 = -2032003311302-05230015220331351005031051
AiPlayerbot.PremadeSpecLink.9.2.60 = --05203205210131051313230341
AiPlayerbot.PremadeSpecLink.9.2.80 = -03310030003-05203205210331051335230351
#
#
@@ -1532,19 +1370,6 @@ AiPlayerbot.PremadeSpecName.11.3 = cat pve
AiPlayerbot.PremadeSpecGlyph.11.3 = 40902,43331,40901,43335,44922,45604
AiPlayerbot.PremadeSpecLink.11.3.60 = -552202032322010053100030310501
AiPlayerbot.PremadeSpecLink.11.3.80 = -553202032322010053100030310511-205503012
AiPlayerbot.PremadeSpecName.11.4 = balance pvp
AiPlayerbot.PremadeSpecGlyph.11.4 = 40921,43331,45622,43674,43335,45623
AiPlayerbot.PremadeSpecLink.11.4.60 = 5012203115331002213032311231
AiPlayerbot.PremadeSpecLink.11.4.80 = 5022203125331003213035311231--230033012
AiPlayerbot.PremadeSpecName.11.5 = cat pvp
AiPlayerbot.PremadeSpecGlyph.11.5 = 40902,43331,45601,43674,43335,40901
AiPlayerbot.PremadeSpecLink.11.5.60 = -513202032322010053103030310501
AiPlayerbot.PremadeSpecLink.11.5.80 = -523202032322010053103030310511-205503012
AiPlayerbot.PremadeSpecName.11.6 = resto pvp
AiPlayerbot.PremadeSpecGlyph.11.6 = 40913,43331,40906,43335,43674,45623
AiPlayerbot.PremadeSpecLink.11.6.60 = --230033312031500511350013051
AiPlayerbot.PremadeSpecLink.11.6.80 = 05320021--230033312031500531353013251
#
#
@@ -1564,12 +1389,41 @@ AiPlayerbot.PremadeSpecLink.11.6.80 = 05320021--230033312031500531353013251
# Applies a permanent buff to all bots simulating effects of spells, flasks, food, runes, etc.
# 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.;
# Use 0 for any field to make it agnostic (e.g., 0 for FactionID means the entry will apply buffs to 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 may be deleted or modified, and new custom entries may be added
# Numbers after the equal signs are spell IDs and may be customized
# See Randombots Default Talent Specs for more info on each spec; they are listed in that section by the names in the parentheticals (e.g., arms pve, fury pve)
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.WorldBuff.0.1.0.80.80 = 53760,57358 #WARRIOR ARMS (arms pve)
AiPlayerbot.WorldBuff.0.1.1.80.80 = 53760,57358 #WARRIOR FURY (fury pve)
AiPlayerbot.WorldBuff.0.1.2.80.80 = 53758,57356 #WARRIOR PROTECTION (prot pve)
AiPlayerbot.WorldBuff.0.2.0.80.80 = 60347,53749,57332 #PALADIN HOLY (holy pve)
AiPlayerbot.WorldBuff.0.2.1.80.80 = 53758,57356 #PALADIN PROTECTION (prot pve)
AiPlayerbot.WorldBuff.0.2.2.80.80 = 53760,57371 #PALADIN RETRIBUTION (ret pve)
AiPlayerbot.WorldBuff.0.3.0.80.80 = 53760,57325 #HUNTER BEAST (bm pve)
AiPlayerbot.WorldBuff.0.3.1.80.80 = 53760,57358 #HUNTER MARKSMANSHIP (mm pve)
AiPlayerbot.WorldBuff.0.3.2.80.80 = 53760,57367 #HUNTER SURVIVAL (surv pve)
AiPlayerbot.WorldBuff.0.4.0.80.80 = 53760,57325 #ROGUE ASSASINATION (as pve)
AiPlayerbot.WorldBuff.0.4.1.80.80 = 53760,57358 #ROGUE COMBAT (combat pve)
AiPlayerbot.WorldBuff.0.4.2.80.80 = 53760,57367 #ROGUE SUBTLETY (subtlety pve)
AiPlayerbot.WorldBuff.0.5.0.80.80 = 53755,57327 #PRIEST DISCIPLINE (disc pve)
AiPlayerbot.WorldBuff.0.5.1.80.80 = 53755,57327 #PRIEST HOLY (holy pve)
AiPlayerbot.WorldBuff.0.5.2.80.80 = 53755,57327 #PRIEST SHADOW (shadow pve)
AiPlayerbot.WorldBuff.0.6.0.80.80 = 53758,57356 #DEATH KNIGHT BLOOD (blood pve)
AiPlayerbot.WorldBuff.0.6.1.80.80 = 53760,57358 #DEATH KNIGHT FROST (frost pve)
AiPlayerbot.WorldBuff.0.6.2.80.80 = 53760,57358 #DEATH KNIGHT UNHOLY (unholy pve)
AiPlayerbot.WorldBuff.0.6.3.80.80 = 53760,57371 #DEATH KNIGHT BLOOD DPS (double aura blood pve)
AiPlayerbot.WorldBuff.0.7.0.80.80 = 53755,57327 #SHAMAN ELEMENTAL (ele pve)
AiPlayerbot.WorldBuff.0.7.1.80.80 = 53760,57325 #SHAMAN ENHANCEMENT (enh pve)
AiPlayerbot.WorldBuff.0.7.2.80.80 = 53755,57327 #SHAMAN RESTORATION (resto pve)
AiPlayerbot.WorldBuff.0.8.0.80.80 = 53755,57327 #MAGE ARCANE (arcane pve)
AiPlayerbot.WorldBuff.0.8.1.80.80 = 53755,57327 #MAGE FIRE (fire pve)
AiPlayerbot.WorldBuff.0.8.2.80.80 = 53755,57327 #MAGE FROST (frost pve)
AiPlayerbot.WorldBuff.0.9.0.80.80 = 53755,57327 #WARLOCK AFFLICTION (affli pve)
AiPlayerbot.WorldBuff.0.9.1.80.80 = 53755,57327 #WARLOCK DEMONOLOGY (demo pve)
AiPlayerbot.WorldBuff.0.9.2.80.80 = 53755,57327 #WARLOCK DESTRUCTION (destro pve)
AiPlayerbot.WorldBuff.0.11.0.80.80 = 53755,57327 #DRUID BALANCE (balance pve)
AiPlayerbot.WorldBuff.0.11.1.80.80 = 53749,53763,57367 #DRUID FERAL BEAR (bear pve)
AiPlayerbot.WorldBuff.0.11.2.80.80 = 54212,57334 #DRUID RESTORATION (resto pve)
AiPlayerbot.WorldBuff.0.11.3.80.80 = 53760,57358 #DRUID FERAL CAT (cat pve)
#
#
@@ -1736,11 +1590,11 @@ AiPlayerbot.RandomClassSpecIndex.8.2 = 2
#
#
AiPlayerbot.RandomClassSpecProb.9.0 = 33
AiPlayerbot.RandomClassSpecProb.9.0 = 45
AiPlayerbot.RandomClassSpecIndex.9.0 = 0
AiPlayerbot.RandomClassSpecProb.9.1 = 34
AiPlayerbot.RandomClassSpecProb.9.1 = 45
AiPlayerbot.RandomClassSpecIndex.9.1 = 1
AiPlayerbot.RandomClassSpecProb.9.2 = 33
AiPlayerbot.RandomClassSpecProb.9.2 = 10
AiPlayerbot.RandomClassSpecIndex.9.2 = 2
#
@@ -1974,15 +1828,9 @@ AiPlayerbot.AllowedLogFiles = ""
####################################################################################################
# A list of gameObject GUID's that are not allowed for bots to interact with.
#
AiPlayerbot.DisallowedGameObjects = 176213,17155,2656,74448,19020,3719,3658,3705,3706,105579,75293,17155,2857,179490
#
# List of GUID's:
# QuestItems:
# 176213 = Blood of Heroes, 17155 = Defias Gunpowder, 2656 = Waterlogged Envelope
# Chests:
# Large Solid Chest = 74448, Box of Assorted Parts = 19020, Food Crate = 3719, Water Barrel = 3658, Barrel of Milk = 3705, Barrel of sweet Nectar = 3706, Tattered Chest = 105579, Large bettered Chest = 75293, Solid Chest = 2857, Battered Foodlocker = 179490
# Feel free to edit and help to complete.
# Example: 176213 = Blood of Heroes
# Example: 17155 = Defias Gunpowder
AiPlayerbot.DisallowedGameObjects = 176213,17155
#
####################################################################################################
@@ -2040,9 +1888,3 @@ AiPlayerbot.TargetPosRecalcDistance = 0.1
# Allow bots to be summoned near innkeepers
AiPlayerbot.SummonAtInnkeepersEnabled = 1
# Enable buffs in ICC to make Heroic easier and more casual.
# 30% more damage, 40% damage reduction (tank bots), increased all resistances, reduced threat for non tank bots, increased threat for tank bots.
# Buffs will be applied on PP, Sindragosa and Lich King
AiPlayerbot.EnableICCBuffs = 1

View File

@@ -1,8 +0,0 @@
DROP TABLE IF EXISTS `playerbots_account_type`;
CREATE TABLE `playerbots_account_type` (
`account_id` int unsigned NOT NULL,
`account_type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '0 = unassigned, 1 = RNDbot, 2 = AddClass',
`assignment_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`account_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Playerbot account type assignments';

View File

@@ -1,9 +0,0 @@
-- Create playerbots_account_type table for tracking accounts assignments
DROP TABLE IF EXISTS `playerbots_account_type`;
CREATE TABLE `playerbots_account_type` (
`account_id` int unsigned NOT NULL,
`account_type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '0 = unassigned, 1 = RNDbot, 2 = AddClass',
`assignment_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`account_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Playerbot account type assignments';

View File

@@ -80,16 +80,13 @@ uint8 AiFactory::GetPlayerSpecTab(Player* bot)
switch (bot->getClass())
{
case CLASS_MAGE:
tab = MAGE_TAB_FROST;
tab = 1;
break;
case CLASS_PALADIN:
tab = PALADIN_TAB_RETRIBUTION;
tab = 2;
break;
case CLASS_PRIEST:
tab = PRIEST_TAB_HOLY;
break;
case CLASS_WARLOCK:
tab = WARLOCK_TAB_DEMONOLOGY;
tab = 1;
break;
}
@@ -305,22 +302,22 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
break;
case CLASS_MAGE:
if (tab == 0)
engine->addStrategiesNoInit("arcane", nullptr);
engine->addStrategiesNoInit("arcane", "arcane aoe", nullptr);
else if (tab == 1)
{
if (player->HasSpell(44614) /*Frostfire Bolt*/ && player->HasAura(15047) /*Ice Shards*/)
{
engine->addStrategiesNoInit("frostfire", nullptr);
engine->addStrategiesNoInit("frostfire", "frostfire aoe", nullptr);
}
else
{
engine->addStrategiesNoInit("fire", nullptr);
engine->addStrategiesNoInit("fire", "fire aoe", nullptr);
}
}
else
engine->addStrategiesNoInit("frost", nullptr);
engine->addStrategiesNoInit("frost", "frost aoe", nullptr);
engine->addStrategiesNoInit("dps", "dps assist", "cure", "aoe", nullptr);
engine->addStrategiesNoInit("dps", "dps assist", "cure", nullptr);
break;
case CLASS_WARRIOR:
if (tab == 2)
@@ -370,14 +367,12 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
}
break;
case CLASS_HUNTER:
if (tab == 0) // Beast Mastery
engine->addStrategiesNoInit("bm", nullptr);
else if (tab == 1) // Marksmanship
engine->addStrategiesNoInit("mm", nullptr);
else if (tab == 2) // Survival
engine->addStrategiesNoInit("surv", nullptr);
engine->addStrategiesNoInit("cc", "dps assist", "aoe", nullptr);
engine->addStrategiesNoInit("dps", "aoe", "bdps", "dps assist", nullptr);
engine->addStrategy("dps debuff", false);
// if (tab == HUNTER_TAB_SURVIVAL)
// {
// engine->addStrategy("trap weave", false);
// }
break;
case CLASS_ROGUE:
if (tab == ROGUE_TAB_ASSASSINATION || tab == ROGUE_TAB_SUBTLETY)
@@ -390,16 +385,8 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
}
break;
case CLASS_WARLOCK:
if (tab == 0) // Affliction
engine->addStrategiesNoInit("affli", "curse of agony", nullptr);
else if (tab == 1) // Demonology
engine->addStrategiesNoInit("demo", "curse of agony", "meta melee", nullptr);
else if (tab == 2) // Destruction
engine->addStrategiesNoInit("destro", "curse of elements", nullptr);
engine->addStrategiesNoInit("cc", "dps assist", "aoe", nullptr);
engine->addStrategiesNoInit("dps assist", "dps", "dps debuff", "aoe", nullptr);
break;
case CLASS_DEATH_KNIGHT:
if (tab == 0)
engine->addStrategiesNoInit("blood", "tank assist", nullptr);
@@ -420,8 +407,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
{
if (sPlayerbotAIConfig->autoSaveMana)
engine->addStrategy("save mana", false);
if (!sPlayerbotAIConfig->IsRestrictedHealerDPSMap(player->GetMapId()))
engine->addStrategy("healer dps", false);
engine->addStrategy("healer dps", false);
}
if (facade->IsRealPlayer() || sRandomPlayerbotMgr->IsRandomBot(player))
{
@@ -602,17 +588,17 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
case CLASS_WARLOCK:
if (tab == WARLOCK_TAB_AFFLICATION)
{
nonCombatEngine->addStrategiesNoInit("felhunter", "spellstone", nullptr);
nonCombatEngine->addStrategiesNoInit("bmana", nullptr);
}
else if (tab == WARLOCK_TAB_DEMONOLOGY)
{
nonCombatEngine->addStrategiesNoInit("felguard", "spellstone", nullptr);
nonCombatEngine->addStrategiesNoInit("bdps", nullptr);
}
else if (tab == WARLOCK_TAB_DESTRUCTION)
{
nonCombatEngine->addStrategiesNoInit("imp", "firestone", nullptr);
nonCombatEngine->addStrategiesNoInit("bhealth", nullptr);
}
nonCombatEngine->addStrategiesNoInit("dps assist", "ss self", nullptr);
nonCombatEngine->addStrategiesNoInit("dps assist", nullptr);
break;
case CLASS_DEATH_KNIGHT:
if (tab == 0)

View File

@@ -525,11 +525,6 @@ uint32 GuildTaskMgr::GetMaxItemTaskCount(uint32 itemId)
bool GuildTaskMgr::IsGuildTaskItem(uint32 itemId, uint32 guildId)
{
if (!sPlayerbotAIConfig->guildTaskEnabled)
{
return 0;
}
uint32 value = 0;
PlayerbotsDatabasePreparedStatement* stmt =
@@ -553,11 +548,6 @@ bool GuildTaskMgr::IsGuildTaskItem(uint32 itemId, uint32 guildId)
std::map<uint32, uint32> GuildTaskMgr::GetTaskValues(uint32 owner, std::string const type,
[[maybe_unused]] uint32* validIn /* = nullptr */)
{
if (!sPlayerbotAIConfig->guildTaskEnabled)
{
return std::map<uint32, uint32>();
}
std::map<uint32, uint32> results;
PlayerbotsDatabasePreparedStatement* stmt =
@@ -586,11 +576,6 @@ std::map<uint32, uint32> GuildTaskMgr::GetTaskValues(uint32 owner, std::string c
uint32 GuildTaskMgr::GetTaskValue(uint32 owner, uint32 guildId, std::string const type, [[maybe_unused]] uint32* validIn /* = nullptr */)
{
if (!sPlayerbotAIConfig->guildTaskEnabled)
{
return 0;
}
uint32 value = 0;
PlayerbotsDatabasePreparedStatement* stmt =

View File

@@ -6,8 +6,6 @@
#include "LootObjectStack.h"
#include "LootMgr.h"
#include "Object.h"
#include "ObjectAccessor.h"
#include "Playerbots.h"
#include "Unit.h"
@@ -289,7 +287,7 @@ bool LootObject::IsLootPossible(Player* bot)
if (reqItem && !bot->HasItemCount(reqItem, 1))
return false;
if (abs(worldObj->GetPositionZ() - bot->GetPositionZ()) > INTERACTION_DISTANCE - 2.0f)
if (abs(worldObj->GetPositionZ() - bot->GetPositionZ()) > INTERACTION_DISTANCE -2.0f)
return false;
Creature* creature = botAI->GetCreature(guid);
@@ -301,7 +299,7 @@ bool LootObject::IsLootPossible(Player* bot)
// Prevent bot from running to chests that are unlootable (e.g. Gunship Armory before completing the event)
GameObject* go = botAI->GetGameObject(guid);
if (go && go->HasFlag(GAMEOBJECT_FLAGS, GO_FLAG_INTERACT_COND | GO_FLAG_NOT_SELECTABLE))
if (go && go->HasFlag(GAMEOBJECT_FLAGS, GO_FLAG_INTERACT_COND | GO_FLAG_NOT_SELECTABLE))
return false;
if (skillId == SKILL_NONE)
@@ -319,17 +317,29 @@ bool LootObject::IsLootPossible(Player* bot)
uint32 skillValue = uint32(bot->GetSkillValue(skillId));
if (reqSkillValue > skillValue)
return false;
if (skillId == SKILL_MINING && !bot->HasItemCount(756, 1) && !bot->HasItemCount(778, 1) &&
!bot->HasItemCount(1819, 1) && !bot->HasItemCount(1893, 1) && !bot->HasItemCount(1959, 1) &&
!bot->HasItemCount(2901, 1) && !bot->HasItemCount(9465, 1) && !bot->HasItemCount(20723, 1) &&
!bot->HasItemCount(40772, 1) && !bot->HasItemCount(40892, 1) && !bot->HasItemCount(40893, 1))
if (skillId == SKILL_MINING &&
!bot->HasItemCount(756, 1) &&
!bot->HasItemCount(778, 1) &&
!bot->HasItemCount(1819, 1) &&
!bot->HasItemCount(1893, 1) &&
!bot->HasItemCount(1959, 1) &&
!bot->HasItemCount(2901, 1) &&
!bot->HasItemCount(9465, 1) &&
!bot->HasItemCount(20723, 1) &&
!bot->HasItemCount(40772, 1) &&
!bot->HasItemCount(40892, 1) &&
!bot->HasItemCount(40893, 1))
{
return false; // Bot is missing a mining pick
}
if (skillId == SKILL_SKINNING && !bot->HasItemCount(7005, 1) && !bot->HasItemCount(40772, 1) &&
!bot->HasItemCount(40893, 1) && !bot->HasItemCount(12709, 1) && !bot->HasItemCount(19901, 1))
if (skillId == SKILL_SKINNING &&
!bot->HasItemCount(7005, 1) &&
!bot->HasItemCount(40772, 1) &&
!bot->HasItemCount(40893, 1) &&
!bot->HasItemCount(12709, 1) &&
!bot->HasItemCount(19901, 1))
{
return false; // Bot is missing a skinning knife
}
@@ -366,45 +376,42 @@ void LootObjectStack::Clear() { availableLoot.clear(); }
bool LootObjectStack::CanLoot(float maxDistance)
{
LootObject nearest = GetNearest(maxDistance);
return !nearest.IsEmpty();
std::vector<LootObject> ordered = OrderByDistance(maxDistance);
return !ordered.empty();
}
LootObject LootObjectStack::GetLoot(float maxDistance)
{
LootObject nearest = GetNearest(maxDistance);
return nearest.IsEmpty() ? LootObject() : nearest;
std::vector<LootObject> ordered = OrderByDistance(maxDistance);
return ordered.empty() ? LootObject() : *ordered.begin();
}
LootObject LootObjectStack::GetNearest(float maxDistance)
std::vector<LootObject> LootObjectStack::OrderByDistance(float maxDistance)
{
availableLoot.shrink(time(nullptr) - 30);
LootObject nearest;
float nearestDistance = std::numeric_limits<float>::max();
std::map<float, LootObject> sortedMap;
LootTargetList safeCopy(availableLoot);
for (LootTargetList::iterator i = safeCopy.begin(); i != safeCopy.end(); i++)
{
ObjectGuid guid = i->guid;
WorldObject* worldObj = ObjectAccessor::GetWorldObject(*bot, guid);
if (!worldObj)
LootObject lootObject(bot, guid);
if (!lootObject.IsLootPossible(bot)) // Ensure loot object is valid
continue;
WorldObject* worldObj = lootObject.GetWorldObject(bot);
if (!worldObj) // Prevent null pointer dereference
{
continue;
}
float distance = bot->GetDistance(worldObj);
if (distance >= nearestDistance || (maxDistance && distance > maxDistance))
continue;
LootObject lootObject(bot, guid);
if (!lootObject.IsLootPossible(bot))
continue;
nearestDistance = distance;
nearest = lootObject;
if (!maxDistance || distance <= maxDistance)
sortedMap[distance] = lootObject;
}
return nearest;
}
std::vector<LootObject> result;
for (auto& [_, lootObject] : sortedMap)
result.push_back(lootObject);
return result;
}

View File

@@ -78,7 +78,7 @@ public:
LootObject GetLoot(float maxDistance = 0);
private:
LootObject GetNearest(float maxDistance = 0);
std::vector<LootObject> OrderByDistance(float maxDistance = 0);
Player* bot;
LootTargetList availableLoot;

View File

@@ -212,8 +212,7 @@ PlayerbotAI::PlayerbotAI(Player* bot)
masterIncomingPacketHandlers.AddHandler(CMSG_PUSHQUESTTOPARTY, "quest share");
botOutgoingPacketHandlers.AddHandler(SMSG_QUESTUPDATE_COMPLETE, "quest update complete");
botOutgoingPacketHandlers.AddHandler(SMSG_QUESTUPDATE_ADD_KILL, "quest update add kill");
// SMSG_QUESTUPDATE_ADD_ITEM no longer used
// botOutgoingPacketHandlers.AddHandler(SMSG_QUESTUPDATE_ADD_ITEM, "quest update add item");
// botOutgoingPacketHandlers.AddHandler(SMSG_QUESTUPDATE_ADD_ITEM, "quest update add item"); // SMSG_QUESTUPDATE_ADD_ITEM no longer used
botOutgoingPacketHandlers.AddHandler(SMSG_QUEST_CONFIRM_ACCEPT, "confirm quest");
}
@@ -722,7 +721,6 @@ void PlayerbotAI::HandleTeleportAck()
// SetNextCheckDelay(urand(2000, 5000));
if (sPlayerbotAIConfig->applyInstanceStrategies)
ApplyInstanceStrategies(bot->GetMapId(), true);
EvaluateHealerDpsStrategy();
Reset(true);
}
@@ -1050,9 +1048,6 @@ void PlayerbotAI::HandleBotOutgoingPacket(WorldPacket const& packet)
default:
return;
}
if (chanName == "World")
return;
// do not reply to self but always try to reply to real player
if (guid1 != bot->GetGUID())
@@ -1296,6 +1291,11 @@ void PlayerbotAI::DoNextAction(bool min)
return;
}
if (bot->HasUnitState(UNIT_STATE_IN_FLIGHT))
{
SetNextCheckDelay(sPlayerbotAIConfig->passiveDelay);
return;
}
// Change engine if just died
bool isBotAlive = bot->IsAlive();
@@ -1425,8 +1425,8 @@ void PlayerbotAI::DoNextAction(bool min)
master = newMaster;
botAI->SetMaster(newMaster);
botAI->ResetStrategies();
if (!bot->InBattleground())
if (!bot->InBattleground())
{
botAI->ChangeStrategy("+follow", BOT_STATE_NON_COMBAT);
@@ -1437,7 +1437,7 @@ void PlayerbotAI::DoNextAction(bool min)
}
else
{
// we're in a battleground, stay with the pack and focus on objective
// we're in a battleground, stay with the pack and focus on objective
botAI->ChangeStrategy("-follow", BOT_STATE_NON_COMBAT);
}
}
@@ -2358,6 +2358,7 @@ std::string PlayerbotAI::GetLocalizedCreatureName(uint32 entry)
return name;
}
std::string PlayerbotAI::GetLocalizedGameObjectName(uint32 entry)
{
std::string name;
@@ -2936,18 +2937,6 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell,
return false;
}
if ((bot->GetShapeshiftForm() == FORM_FLIGHT || bot->GetShapeshiftForm() == FORM_FLIGHT_EPIC) && !bot->IsInCombat())
{
if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster()))
{
LOG_DEBUG(
"playerbots",
"Can cast spell failed. In flight form (not in combat). - target name: {}, spellid: {}, bot name: {}",
target->GetName(), spellid, bot->GetName());
}
return false;
}
uint32 CastingTime = !spellInfo->IsChanneled() ? spellInfo->CalcCastTime(bot) : spellInfo->GetDuration();
// bool interruptOnMove = spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT;
if ((CastingTime || spellInfo->IsAutoRepeatRangedSpell()) && bot->isMoving())
@@ -2962,19 +2951,14 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell,
if (!itemTarget)
{
// Exception for Deep Freeze (44572) - allow cast for damage on immune targets (e.g., bosses)
if (target->IsImmunedToSpell(spellInfo))
{
if (spellid != 44572) // Deep Freeze
if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster()))
{
if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster()))
{
LOG_DEBUG("playerbots", "target is immuned to spell - target name: {}, spellid: {}, bot name: {}",
target->GetName(), spellid, bot->GetName());
}
return false;
LOG_DEBUG("playerbots", "target is immuned to spell - target name: {}, spellid: {}, bot name: {}",
target->GetName(), spellid, bot->GetName());
}
// Otherwise, allow Deep Freeze even if immune
return false;
}
if (bot != target && sServerFacade->GetDistance2d(bot, target) > sPlayerbotAIConfig->sightDistance)
@@ -3170,41 +3154,22 @@ bool PlayerbotAI::CastSpell(uint32 spellId, Unit* target, Item* itemTarget)
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
if (pet && pet->HasSpell(spellId))
{
// List of spell IDs for which we do NOT want to toggle auto-cast or send message
// We are excluding Spell Lock and Devour Magic because they are casted in the GenericWarlockStrategy
// Without this exclusion, the skill would be togged for auto-cast and the player would
// be spammed with messages about enabling/disabling auto-cast
switch (spellId)
bool autocast = false;
for (unsigned int& m_autospell : pet->m_autospells)
{
case 19244: // Spell Lock rank 1
case 19647: // Spell Lock rank 2
case 19505: // Devour Magic rank 1
case 19731: // Devour Magic rank 2
case 19734: // Devour Magic rank 3
case 19736: // Devour Magic rank 4
case 27276: // Devour Magic rank 5
case 27277: // Devour Magic rank 6
case 48011: // Devour Magic rank 7
// No message - just break out of the switch and let normal cast logic continue
if (m_autospell == spellId)
{
autocast = true;
break;
default:
bool autocast = false;
for (unsigned int& m_autospell : pet->m_autospells)
{
if (m_autospell == spellId)
{
autocast = true;
break;
}
}
pet->ToggleAutocast(spellInfo, !autocast);
std::ostringstream out;
out << (autocast ? "|cffff0000|Disabling" : "|cFF00ff00|Enabling") << " pet auto-cast for ";
out << chatHelper.FormatSpell(spellInfo);
TellMaster(out);
return true;
}
}
pet->ToggleAutocast(spellInfo, !autocast);
std::ostringstream out;
out << (autocast ? "|cffff0000|Disabling" : "|cFF00ff00|Enabling") << " pet auto-cast for ";
out << chatHelper.FormatSpell(spellInfo);
TellMaster(out);
return true;
}
// aiObjectContext->GetValue<LastMovement&>("last movement")->Get().Set(nullptr);
@@ -3346,14 +3311,13 @@ bool PlayerbotAI::CastSpell(uint32 spellId, Unit* target, Item* itemTarget)
std::ostringstream out;
out << "Spell cast failed - ";
out << "Spell ID: " << spellId << " (" << ChatHelper::FormatSpell(spellInfo) << "), ";
out << "Error Code: " << static_cast<int>(result) << " (0x" << std::hex << static_cast<int>(result)
<< std::dec << "), ";
out << "Error Code: " << static_cast<int>(result) << " (0x" << std::hex << static_cast<int>(result) << std::dec << "), ";
out << "Bot: " << bot->GetName() << ", ";
// Check spell target type
if (targets.GetUnitTarget())
{
out << "Target: Unit (" << targets.GetUnitTarget()->GetName()
out << "Target: Unit (" << targets.GetUnitTarget()->GetName()
<< ", Low GUID: " << targets.GetUnitTarget()->GetGUID().GetCounter()
<< ", High GUID: " << static_cast<uint32>(targets.GetUnitTarget()->GetGUID().GetHigh()) << "), ";
}
@@ -3369,7 +3333,7 @@ bool PlayerbotAI::CastSpell(uint32 spellId, Unit* target, Item* itemTarget)
out << "Target: Item (Low GUID: " << targets.GetItemTarget()->GetGUID().GetCounter()
<< ", High GUID: " << static_cast<uint32>(targets.GetItemTarget()->GetGUID().GetHigh()) << "), ";
}
// Check if bot is in trade mode
if (bot->GetTradeData())
{
@@ -3377,7 +3341,7 @@ bool PlayerbotAI::CastSpell(uint32 spellId, Unit* target, Item* itemTarget)
Item* tradeItem = bot->GetTradeData()->GetTraderData()->GetItem(TRADE_SLOT_NONTRADED);
if (tradeItem)
{
out << "Trade Item: " << tradeItem->GetEntry()
out << "Trade Item: " << tradeItem->GetEntry()
<< " (Low GUID: " << tradeItem->GetGUID().GetCounter()
<< ", High GUID: " << static_cast<uint32>(tradeItem->GetGUID().GetHigh()) << "), ";
}
@@ -3390,7 +3354,7 @@ bool PlayerbotAI::CastSpell(uint32 spellId, Unit* target, Item* itemTarget)
{
out << "Trade Mode: Inactive, ";
}
TellMasterNoFacing(out);
}
@@ -3970,37 +3934,15 @@ bool IsAlliance(uint8 race)
bool PlayerbotAI::HasRealPlayerMaster()
{
// if (master)
// {
// PlayerbotAI* masterBotAI = GET_PLAYERBOT_AI(master);
// return !masterBotAI || masterBotAI->IsRealPlayer();
// }
//
// return false;
// Removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
/* 1) The "master" pointer can be null if the bot was created
without a master player or if the master was just removed. */
if (!master)
return false;
/* 2) Is the master player still present in the world?
If FindPlayer fails, we invalidate "master" and stop here. */
if (!ObjectAccessor::FindPlayer(master->GetGUID()))
if (master)
{
master = nullptr; // avoids repeating the check on the next tick
return false;
PlayerbotAI* masterBotAI = GET_PLAYERBOT_AI(master);
return !masterBotAI || masterBotAI->IsRealPlayer();
}
/* 3) If the master is a bot, we check that it is itself controlled
by a real player. Otherwise, it's already a real player → true. */
if (PlayerbotAI* masterBotAI = GET_PLAYERBOT_AI(master))
return masterBotAI->IsRealPlayer(); // bot controlled by a player?
return true; // master = real player
return false;
}
bool PlayerbotAI::HasActivePlayerMaster() { return master && !GET_PLAYERBOT_AI(master); }
bool PlayerbotAI::IsAlt() { return HasRealPlayerMaster() && !sRandomPlayerbotMgr->IsRandomBot(bot); }
@@ -4351,7 +4293,7 @@ bool PlayerbotAI::AllowActive(ActivityType activityType)
if (!player || !player->IsInWorld())
continue;
Player* connectedPlayer = ObjectAccessor::FindPlayer(player->GetGUID());
Player* connectedPlayer = ObjectAccessor::FindPlayer(player->GetGUID());
if (!connectedPlayer)
continue;
@@ -4423,28 +4365,26 @@ bool PlayerbotAI::AllowActivity(ActivityType activityType, bool checkNow)
uint32 PlayerbotAI::AutoScaleActivity(uint32 mod)
{
// Current max server update time (ms), and the configured floor/ceiling values for bot scaling
uint32 maxDiff = sWorldUpdateTime.GetMaxUpdateTimeOfCurrentTable();
uint32 diffLimitFloor = sPlayerbotAIConfig->botActiveAloneSmartScaleDiffLimitfloor;
uint32 diffLimitCeiling = sPlayerbotAIConfig->botActiveAloneSmartScaleDiffLimitCeiling;
double spreadSize = (double)(diffLimitCeiling - diffLimitFloor) / 6;
if (diffLimitCeiling <= diffLimitFloor)
{
// Perfrom binary decision if ceiling <= floor: Either all bots are active or none are
return (maxDiff > diffLimitCeiling) ? 0 : mod;
}
// apply scaling
if (maxDiff > diffLimitCeiling)
return 0;
if (maxDiff > diffLimitFloor + (4 * spreadSize))
return (mod * 1) / 10;
if (maxDiff > diffLimitFloor + (3 * spreadSize))
return (mod * 3) / 10;
if (maxDiff > diffLimitFloor + (2 * spreadSize))
return (mod * 5) / 10;
if (maxDiff > diffLimitFloor + (1 * spreadSize))
return (mod * 7) / 10;
if (maxDiff > diffLimitFloor)
return (mod * 9) / 10;
if (maxDiff <= diffLimitFloor)
return mod;
// Calculate lag progress from floor to ceiling (0 to 1)
double lagProgress = (maxDiff - diffLimitFloor) / (double)(diffLimitCeiling - diffLimitFloor);
// Apply the percentage of active bots (the complement of lag progress) to the mod value
return static_cast<uint32>(mod * (1 - lagProgress));
return mod;
}
bool PlayerbotAI::IsOpposing(Player* player) { return IsOpposing(player->getRace(), bot->getRace()); }
@@ -4469,51 +4409,12 @@ void PlayerbotAI::RemoveShapeshift()
// RemoveAura("tree of life");
}
// Mirrors Blizzards GetAverageItemLevel rules :
// https://wowpedia.fandom.com/wiki/API_GetAverageItemLevel
// NOTE : function rewritten as flags "withBags" and "withBank" not used, and _fillGearScoreData sometimes attribute
// one-hand/2H Weapon in wrong slots
uint32 PlayerbotAI::GetEquipGearScore(Player* player)
{
constexpr uint8 TOTAL_SLOTS = 17; // every slot except Body & Tabard
uint32 sumLevel = 0;
/* ---------- 0. Detect “ignore off-hand” situations --------- */
Item* main = player->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
Item* off = player->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
bool ignoreOffhand = false; // true → divisor = 16
if (main)
{
bool twoHand = (main->GetTemplate()->InventoryType == INVTYPE_2HWEAPON);
if (twoHand && !player->HasAura(SPELL_TITAN_GRIP))
ignoreOffhand = true; // classic 2-hander
}
else if (!off) // both hands empty
ignoreOffhand = true;
/* ---------- 1. Sum up item-levels -------------------------- */
for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
{
if (slot == EQUIPMENT_SLOT_BODY || slot == EQUIPMENT_SLOT_TABARD)
continue; // Blizzard never counts these
if (ignoreOffhand && slot == EQUIPMENT_SLOT_OFFHAND)
continue; // skip off-hand in 2-H case
if (Item* it = player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
sumLevel += it->GetTemplate()->ItemLevel; // missing items add 0
}
/* ---------- 2. Divide by 17 or 16 -------------------------- */
const uint8 divisor = ignoreOffhand ? TOTAL_SLOTS - 1 : TOTAL_SLOTS; // 16 or 17
return sumLevel / divisor;
}
// NOTE : function rewritten as flags "withBags" and "withBank" not used, and _fillGearScoreData sometimes attribute
// one-hand/2H Weapon in wrong slots
/*uint32 PlayerbotAI::GetEquipGearScore(Player* player)
{
// This function aims to calculate the equipped gear score
uint32 sum = 0;
uint8 count = EQUIPMENT_SLOT_END - 2; // ignore body and tabard slots
uint8 mh_type = 0;
@@ -4522,21 +4423,21 @@ uint32 PlayerbotAI::GetEquipGearScore(Player* player)
{
Item* item =player->GetItemByPos(INVENTORY_SLOT_BAG_0, i);
if (item && i != EQUIPMENT_SLOT_BODY && i != EQUIPMENT_SLOT_TABARD)
{
{
ItemTemplate const* proto = item->GetTemplate();
sum += proto->ItemLevel;
// If character is not warfury and have 2 hand weapon equipped, main hand will be counted twice
if (i == SLOT_MAIN_HAND)
mh_type = item->GetTemplate()->InventoryType;
if (!player->HasAura(SPELL_TITAN_GRIP) && mh_type == INVTYPE_2HWEAPON && i == SLOT_MAIN_HAND)
sum += item->GetTemplate()->ItemLevel;
}
}
}
uint32 gs = uint32(sum / count);
return gs;
}*/
}
/*uint32 PlayerbotAI::GetEquipGearScore(Player* player, bool withBags, bool withBank)
{
@@ -4769,7 +4670,7 @@ void PlayerbotAI::_fillGearScoreData(Player* player, Item* item, std::vector<uin
case INVTYPE_SHOULDERS:
(*gearScore)[EQUIPMENT_SLOT_SHOULDERS] = std::max((*gearScore)[EQUIPMENT_SLOT_SHOULDERS], level);
break;
case INVTYPE_BODY: // Shouldn't be considered when calculating average ilevel
case INVTYPE_BODY: //Shouldn't be considered when calculating average ilevel
(*gearScore)[EQUIPMENT_SLOT_BODY] = std::max((*gearScore)[EQUIPMENT_SLOT_BODY], level);
break;
case INVTYPE_CHEST:
@@ -5127,13 +5028,13 @@ Item* PlayerbotAI::FindAmmo() const
}
// Find Consumable
Item* PlayerbotAI::FindConsumable(uint32 itemId) const
Item* PlayerbotAI::FindConsumable(uint32 displayId) const
{
return FindItemInInventory(
[itemId](ItemTemplate const* pItemProto) -> bool
[displayId](ItemTemplate const* pItemProto) -> bool
{
return (pItemProto->Class == ITEM_CLASS_CONSUMABLE || pItemProto->Class == ITEM_CLASS_TRADE_GOODS) &&
pItemProto->ItemId == itemId;
pItemProto->DisplayInfoID == displayId;
});
}
@@ -5147,80 +5048,80 @@ Item* PlayerbotAI::FindBandage() const
Item* PlayerbotAI::FindOpenableItem() const
{
return FindItemInInventory(
[this](ItemTemplate const* itemTemplate) -> bool
{
return (itemTemplate->Flags & ITEM_FLAG_HAS_LOOT) &&
(itemTemplate->LockID == 0 || !this->bot->GetItemByEntry(itemTemplate->ItemId)->IsLocked());
});
return FindItemInInventory([this](ItemTemplate const* itemTemplate) -> bool
{
return (itemTemplate->Flags & ITEM_FLAG_HAS_LOOT) &&
(itemTemplate->LockID == 0 || !this->bot->GetItemByEntry(itemTemplate->ItemId)->IsLocked());
});
}
Item* PlayerbotAI::FindLockedItem() const
{
return FindItemInInventory(
[this](ItemTemplate const* itemTemplate) -> bool
return FindItemInInventory([this](ItemTemplate const* itemTemplate) -> bool
{
if (!this->bot->HasSkill(SKILL_LOCKPICKING)) // Ensure bot has Lockpicking skill
return false;
if (itemTemplate->LockID == 0) // Ensure the item is actually locked
return false;
Item* item = this->bot->GetItemByEntry(itemTemplate->ItemId);
if (!item || !item->IsLocked()) // Ensure item instance is locked
return false;
// Check if bot has enough Lockpicking skill
LockEntry const* lockInfo = sLockStore.LookupEntry(itemTemplate->LockID);
if (!lockInfo)
return false;
for (uint8 j = 0; j < 8; ++j)
{
if (!this->bot->HasSkill(SKILL_LOCKPICKING)) // Ensure bot has Lockpicking skill
return false;
if (itemTemplate->LockID == 0) // Ensure the item is actually locked
return false;
Item* item = this->bot->GetItemByEntry(itemTemplate->ItemId);
if (!item || !item->IsLocked()) // Ensure item instance is locked
return false;
// Check if bot has enough Lockpicking skill
LockEntry const* lockInfo = sLockStore.LookupEntry(itemTemplate->LockID);
if (!lockInfo)
return false;
for (uint8 j = 0; j < 8; ++j)
if (lockInfo->Type[j] == LOCK_KEY_SKILL)
{
if (lockInfo->Type[j] == LOCK_KEY_SKILL)
uint32 skillId = SkillByLockType(LockType(lockInfo->Index[j]));
if (skillId == SKILL_LOCKPICKING)
{
uint32 skillId = SkillByLockType(LockType(lockInfo->Index[j]));
if (skillId == SKILL_LOCKPICKING)
{
uint32 requiredSkill = lockInfo->Skill[j];
uint32 botSkill = this->bot->GetSkillValue(SKILL_LOCKPICKING);
return botSkill >= requiredSkill;
}
uint32 requiredSkill = lockInfo->Skill[j];
uint32 botSkill = this->bot->GetSkillValue(SKILL_LOCKPICKING);
return botSkill >= requiredSkill;
}
}
}
return false;
});
return false;
});
}
static const uint32 uPriorizedSharpStoneIds[8] = {ADAMANTITE_SHARPENING_DISPLAYID, FEL_SHARPENING_DISPLAYID,
ELEMENTAL_SHARPENING_DISPLAYID, DENSE_SHARPENING_DISPLAYID,
SOLID_SHARPENING_DISPLAYID, HEAVY_SHARPENING_DISPLAYID,
COARSE_SHARPENING_DISPLAYID, ROUGH_SHARPENING_DISPLAYID};
static const uint32 uPriorizedWeightStoneIds[7] = {ADAMANTITE_WEIGHTSTONE_DISPLAYID, FEL_WEIGHTSTONE_DISPLAYID,
DENSE_WEIGHTSTONE_DISPLAYID, SOLID_WEIGHTSTONE_DISPLAYID,
HEAVY_WEIGHTSTONE_DISPLAYID, COARSE_WEIGHTSTONE_DISPLAYID,
ROUGH_WEIGHTSTONE_DISPLAYID};
/**
* FindStoneFor()
* return Item* Returns sharpening/weight stone item eligible to enchant a bot weapon
*
* params:weapon Item* the weap<61>n the function should search and return a enchanting item for
* return nullptr if no relevant item is found in bot inventory, else return a sharpening or weight
* stone based on the weapon subclass
*
*/
Item* PlayerbotAI::FindStoneFor(Item* weapon) const
{
if (!weapon)
return nullptr;
const ItemTemplate* item_template = weapon->GetTemplate();
if (!item_template)
return nullptr;
static const std::vector<uint32_t> uPrioritizedSharpStoneIds = {
ADAMANTITE_SHARPENING_STONE, FEL_SHARPENING_STONE, ELEMENTAL_SHARPENING_STONE, DENSE_SHARPENING_STONE,
SOLID_SHARPENING_STONE, HEAVY_SHARPENING_STONE, COARSE_SHARPENING_STONE, ROUGH_SHARPENING_STONE
};
static const std::vector<uint32_t> uPrioritizedWeightStoneIds = {
ADAMANTITE_WEIGHTSTONE, FEL_WEIGHTSTONE, DENSE_WEIGHTSTONE, SOLID_WEIGHTSTONE,
HEAVY_WEIGHTSTONE, COARSE_WEIGHTSTONE, ROUGH_WEIGHTSTONE
};
Item* stone = nullptr;
ItemTemplate const* pProto = weapon->GetTemplate();
if (pProto && (pProto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD || pProto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD2 ||
pProto->SubClass == ITEM_SUBCLASS_WEAPON_AXE || pProto->SubClass == ITEM_SUBCLASS_WEAPON_AXE2 ||
pProto->SubClass == ITEM_SUBCLASS_WEAPON_DAGGER || pProto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM))
pProto->SubClass == ITEM_SUBCLASS_WEAPON_DAGGER))
{
for (uint8 i = 0; i < std::size(uPrioritizedSharpStoneIds); ++i)
for (uint8 i = 0; i < std::size(uPriorizedSharpStoneIds); ++i)
{
stone = FindConsumable(uPrioritizedSharpStoneIds[i]);
stone = FindConsumable(uPriorizedSharpStoneIds[i]);
if (stone)
{
return stone;
@@ -5228,12 +5129,11 @@ static const std::vector<uint32_t> uPrioritizedWeightStoneIds = {
}
}
else if (pProto &&
(pProto->SubClass == ITEM_SUBCLASS_WEAPON_MACE || pProto->SubClass == ITEM_SUBCLASS_WEAPON_MACE2 ||
pProto->SubClass == ITEM_SUBCLASS_WEAPON_STAFF || pProto->SubClass == ITEM_SUBCLASS_WEAPON_FIST))
(pProto->SubClass == ITEM_SUBCLASS_WEAPON_MACE || pProto->SubClass == ITEM_SUBCLASS_WEAPON_MACE2))
{
for (uint8 i = 0; i < std::size(uPrioritizedWeightStoneIds); ++i)
for (uint8 i = 0; i < std::size(uPriorizedWeightStoneIds); ++i)
{
stone = FindConsumable(uPrioritizedWeightStoneIds[i]);
stone = FindConsumable(uPriorizedWeightStoneIds[i]);
if (stone)
{
return stone;
@@ -5246,7 +5146,6 @@ static const std::vector<uint32_t> uPrioritizedWeightStoneIds = {
Item* PlayerbotAI::FindOilFor(Item* weapon) const
{
if (!weapon)
return nullptr;
@@ -5254,60 +5153,35 @@ Item* PlayerbotAI::FindOilFor(Item* weapon) const
if (!item_template)
return nullptr;
static const std::vector<uint32_t> uPrioritizedWizardOilIds = {
BRILLIANT_WIZARD_OIL, SUPERIOR_WIZARD_OIL, WIZARD_OIL, LESSER_WIZARD_OIL, MINOR_WIZARD_OIL,
BRILLIANT_MANA_OIL, SUPERIOR_MANA_OIL, LESSER_MANA_OIL, MINOR_MANA_OIL};
// static const will only get created once whatever the call amout
static const std::vector<uint32_t> uPriorizedWizardOilIds = {
MINOR_WIZARD_OIL, MINOR_MANA_OIL, LESSER_WIZARD_OIL, LESSER_MANA_OIL, BRILLIANT_WIZARD_OIL,
BRILLIANT_MANA_OIL, WIZARD_OIL, SUPERIOR_MANA_OIL, SUPERIOR_WIZARD_OIL};
static const std::vector<uint32_t> uPrioritizedManaOilIds = {
BRILLIANT_MANA_OIL, SUPERIOR_MANA_OIL, LESSER_MANA_OIL, MINOR_MANA_OIL,
BRILLIANT_WIZARD_OIL, SUPERIOR_WIZARD_OIL, WIZARD_OIL, LESSER_WIZARD_OIL, MINOR_WIZARD_OIL};
// static const will only get created once whatever the call amout
static const std::vector<uint32_t> uPriorizedManaOilIds = {
MINOR_MANA_OIL, MINOR_WIZARD_OIL, LESSER_MANA_OIL, LESSER_WIZARD_OIL, BRILLIANT_MANA_OIL,
BRILLIANT_WIZARD_OIL, SUPERIOR_MANA_OIL, WIZARD_OIL, SUPERIOR_WIZARD_OIL};
Item* oil = nullptr;
int botClass = bot->getClass();
int specTab = AiFactory::GetPlayerSpecTab(bot);
const std::vector<uint32_t>* prioritizedOils = nullptr;
switch (botClass)
if (item_template->SubClass == ITEM_SUBCLASS_WEAPON_SWORD ||
item_template->SubClass == ITEM_SUBCLASS_WEAPON_STAFF || item_template->SubClass == ITEM_SUBCLASS_WEAPON_DAGGER)
{
case CLASS_PRIEST:
prioritizedOils = (specTab == 2) ? &uPrioritizedWizardOilIds : &uPrioritizedManaOilIds;
break;
case CLASS_MAGE:
prioritizedOils = &uPrioritizedWizardOilIds;
break;
case CLASS_DRUID:
if (specTab == 0) // Balance
prioritizedOils = &uPrioritizedWizardOilIds;
else if (specTab == 1) // Feral
prioritizedOils = nullptr;
else // Restoration (specTab == 2) or any other/unspecified spec
prioritizedOils = &uPrioritizedManaOilIds;
break;
case CLASS_HUNTER:
prioritizedOils = &uPrioritizedManaOilIds;
break;
case CLASS_PALADIN:
if (specTab == 1) // Protection
prioritizedOils = &uPrioritizedWizardOilIds;
else if (specTab == 2) // Retribution
prioritizedOils = nullptr;
else // Holy (specTab == 0) or any other/unspecified spec
prioritizedOils = &uPrioritizedManaOilIds;
break;
default:
prioritizedOils = &uPrioritizedManaOilIds;
break;
}
if (prioritizedOils)
{
for (const auto& id : *prioritizedOils)
for (const auto& id : uPriorizedWizardOilIds)
{
oil = FindConsumable(id);
if (oil)
return oil;
}
}
else if (item_template->SubClass == ITEM_SUBCLASS_WEAPON_MACE ||
item_template->SubClass == ITEM_SUBCLASS_WEAPON_MACE2)
{
for (const auto& id : uPriorizedManaOilIds)
{
oil = FindConsumable(id);
if (oil)
{
return oil;
}
}
}
@@ -6138,35 +6012,6 @@ ChatChannelSource PlayerbotAI::GetChatChannelSource(Player* bot, uint32 type, st
return ChatChannelSource::SRC_UNDEFINED;
}
bool PlayerbotAI::CheckLocationDistanceByLevel(Player* player, const WorldLocation& loc, bool fromStartUp)
{
if (player->GetLevel() > 16)
return true;
float dis = 0.0f;
if (fromStartUp)
{
PlayerInfo const* pInfo = sObjectMgr->GetPlayerInfo(player->getRace(true), player->getClass());
if (loc.GetMapId() != pInfo->mapId)
return false;
dis = loc.GetExactDist(pInfo->positionX, pInfo->positionY, pInfo->positionZ);
}
else
{
if (loc.GetMapId() != player->GetMapId())
return false;
dis = loc.GetExactDist(player);
}
float bound = 10000.0f;
if (player->GetLevel() <= 4)
bound = 500.0f;
else if (player->GetLevel() <= 10)
bound = 2500.0f;
return dis <= bound;
}
std::vector<const Quest*> PlayerbotAI::GetAllCurrentQuests()
{
std::vector<const Quest*> result;
@@ -6442,27 +6287,17 @@ void PlayerbotAI::AddTimedEvent(std::function<void()> callback, uint32 delayMs)
class LambdaEvent final : public BasicEvent
{
std::function<void()> _cb;
public:
explicit LambdaEvent(std::function<void()> cb) : _cb(std::move(cb)) {}
bool Execute(uint64 /*execTime*/, uint32 /*diff*/) override
{
_cb();
return true; // remove after execution
return true; // remove after execution
}
};
// Every Player already owns an EventMap called m_Events
bot->m_Events.AddEvent(new LambdaEvent(std::move(callback)), bot->m_Events.CalculateTime(delayMs));
}
void PlayerbotAI::EvaluateHealerDpsStrategy()
{
if (!IsHeal(bot, true))
return;
if (sPlayerbotAIConfig->IsRestrictedHealerDPSMap(bot->GetMapId()))
ChangeStrategy("-healer dps", BOT_STATE_COMBAT);
else
ChangeStrategy("+healer dps", BOT_STATE_COMBAT);
}
bot->m_Events.AddEvent(
new LambdaEvent(std::move(callback)),
bot->m_Events.CalculateTime(delayMs));
}

View File

@@ -46,27 +46,27 @@ struct GameObjectData;
enum StrategyType : uint32;
enum HealingItemId
enum HealingItemDisplayId
{
HEALTHSTONE = 5512,
MAJOR_HEALING_POTION = 13446,
WHIPPER_ROOT_TUBER = 11951,
NIGHT_DRAGON_BREATH = 11952,
LIMITED_INVULNERABILITY_POTION = 3387,
GREATER_DREAMLESS_SLEEP_POTION = 22886,
SUPERIOR_HEALING_POTION = 3928,
CRYSTAL_RESTORE = 11564,
DREAMLESS_SLEEP_POTION = 12190,
GREATER_HEALING_POTION = 1710,
HEALING_POTION = 929,
LESSER_HEALING_POTION = 858,
DISCOLORED_HEALING_POTION = 3391,
MINOR_HEALING_POTION = 118,
VOLATILE_HEALING_POTION = 28100,
SUPER_HEALING_POTION = 22829,
CRYSTAL_HEALING_POTION = 13462,
FEL_REGENERATION_POTION = 28101,
MAJOR_DREAMLESS_SLEEP_POTION = 20002
HEALTHSTONE_DISPLAYID = 8026,
MAJOR_HEALING_POTION = 24152,
WHIPPER_ROOT_TUBER = 21974,
NIGHT_DRAGON_BREATH = 21975,
LIMITED_INVULNERABILITY_POTION = 24213,
GREATER_DREAMLESS_SLEEP_POTION = 17403,
SUPERIOR_HEALING_POTION = 15714,
CRYSTAL_RESTORE = 2516,
DREAMLESS_SLEEP_POTION = 17403,
GREATER_HEALING_POTION = 15713,
HEALING_POTION = 15712,
LESSER_HEALING_POTION = 15711,
DISCOLORED_HEALING_POTION = 15736,
MINOR_HEALING_POTION = 15710,
VOLATILE_HEALING_POTION = 24212,
SUPER_HEALING_POTION = 37807,
CRYSTAL_HEALING_POTION = 47132,
FEL_REGENERATION_POTION = 37864,
MAJOR_DREAMLESS_SLEEP_POTION = 37845
};
enum BotState
@@ -152,7 +152,6 @@ static std::map<ChatChannelSource, std::string> ChatChannelSourceStr = {
{SRC_RAID, "SRC_RAID"},
{SRC_UNDEFINED, "SRC_UNDEFINED"}};
enum ChatChannelId
{
GENERAL = 1,
@@ -163,66 +162,60 @@ enum ChatChannelId
GUILD_RECRUITMENT = 25,
};
enum RoguePoisonId
enum RoguePoisonDisplayId
{
INSTANT_POISON = 6947,
INSTANT_POISON_II = 6949,
INSTANT_POISON_III = 6950,
INSTANT_POISON_IV = 8926,
INSTANT_POISON_V = 8927,
INSTANT_POISON_VI = 8928,
INSTANT_POISON_VII = 21927,
INSTANT_POISON_VIII = 43230,
INSTANT_POISON_IX = 43231,
DEADLY_POISON = 2892,
DEADLY_POISON_II = 2893,
DEADLY_POISON_III = 8984,
DEADLY_POISON_IV = 8985,
DEADLY_POISON_V = 20844,
DEADLY_POISON_VI = 22053,
DEADLY_POISON_VII = 22054,
DEADLY_POISON_VIII = 43232,
DEADLY_POISON_IX = 43233
DEADLY_POISON_DISPLAYID = 13707,
INSTANT_POISON_DISPLAYID = 13710,
WOUND_POISON_DISPLAYID = 37278
};
enum SharpeningStoneId
enum SharpeningStoneDisplayId
{
ROUGH_SHARPENING_STONE = 2862,
COARSE_SHARPENING_STONE = 2863,
HEAVY_SHARPENING_STONE = 2871,
SOLID_SHARPENING_STONE = 7964,
DENSE_SHARPENING_STONE = 12404,
ELEMENTAL_SHARPENING_STONE = 18262,
FEL_SHARPENING_STONE = 23528,
ADAMANTITE_SHARPENING_STONE = 23529
ROUGH_SHARPENING_DISPLAYID = 24673,
COARSE_SHARPENING_DISPLAYID = 24674,
HEAVY_SHARPENING_DISPLAYID = 24675,
SOLID_SHARPENING_DISPLAYID = 24676,
DENSE_SHARPENING_DISPLAYID = 24677,
CONSECRATED_SHARPENING_DISPLAYID =
24674, // will not be used because bot can not know if it will face undead targets
ELEMENTAL_SHARPENING_DISPLAYID = 21072,
FEL_SHARPENING_DISPLAYID = 39192,
ADAMANTITE_SHARPENING_DISPLAYID = 39193
};
enum WeightstoneId
enum WeightStoneDisplayId
{
ROUGH_WEIGHTSTONE = 3239,
COARSE_WEIGHTSTONE = 3240,
HEAVY_WEIGHTSTONE = 3241,
SOLID_WEIGHTSTONE = 7965,
DENSE_WEIGHTSTONE = 12643,
FEL_WEIGHTSTONE = 28420,
ADAMANTITE_WEIGHTSTONE = 28421
ROUGH_WEIGHTSTONE_DISPLAYID = 24683,
COARSE_WEIGHTSTONE_DISPLAYID = 24684,
HEAVY_WEIGHTSTONE_DISPLAYID = 24685,
SOLID_WEIGHTSTONE_DISPLAYID = 24686,
DENSE_WEIGHTSTONE_DISPLAYID = 24687,
FEL_WEIGHTSTONE_DISPLAYID = 39548,
ADAMANTITE_WEIGHTSTONE_DISPLAYID = 39549
};
enum WizardOilId
enum WizardOilDisplayId
{
MINOR_WIZARD_OIL = 20744,
LESSER_WIZARD_OIL = 20746,
WIZARD_OIL = 20750,
BRILLIANT_WIZARD_OIL = 20749,
SUPERIOR_WIZARD_OIL = 22522
MINOR_WIZARD_OIL = 9731,
LESSER_WIZARD_OIL = 47903,
BRILLIANT_WIZARD_OIL = 47901,
WIZARD_OIL = 47905,
SUPERIOR_WIZARD_OIL = 47904,
/// Blessed Wizard Oil = 26865 //scourge inv
};
enum ManaOilId
enum ManaOilDisplayId
{
MINOR_MANA_OIL = 20745,
LESSER_MANA_OIL = 20747,
BRILLIANT_MANA_OIL = 20748,
SUPERIOR_MANA_OIL = 22521
MINOR_MANA_OIL = 34492,
LESSER_MANA_OIL = 47902,
BRILLIANT_MANA_OIL = 41488,
SUPERIOR_MANA_OIL = 36862
};
enum ShieldWardDisplayId
{
LESSER_WARD_OFSHIELDING = 38759,
GREATER_WARD_OFSHIELDING = 38760
};
enum class BotTypeNumber : uint8
@@ -408,7 +401,6 @@ public:
void ClearStrategies(BotState type);
std::vector<std::string> GetStrategies(BotState type);
void ApplyInstanceStrategies(uint32 mapId, bool tellMaster = false);
void EvaluateHealerDpsStrategy();
bool ContainsStrategy(StrategyType type);
bool HasStrategy(std::string const name, BotState type);
BotState GetState() { return currentState; };
@@ -479,7 +471,7 @@ public:
Item* FindBandage() const;
Item* FindOpenableItem() const;
Item* FindLockedItem() const;
Item* FindConsumable(uint32 itemId) const;
Item* FindConsumable(uint32 displayId) const;
Item* FindStoneFor(Item* weapon) const;
Item* FindOilFor(Item* weapon) const;
void ImbueItem(Item* item, uint32 targetFlag, ObjectGuid targetGUID);
@@ -552,8 +544,6 @@ public:
bool IsSafe(Player* player);
bool IsSafe(WorldObject* obj);
ChatChannelSource GetChatChannelSource(Player* bot, uint32 type, std::string channelName);
bool CheckLocationDistanceByLevel(Player* player, const WorldLocation &loc, bool fromStartUp = false);
bool HasCheat(BotCheatMask mask)
{

View File

@@ -4,9 +4,10 @@
*/
#include "PlayerbotAIConfig.h"
#include <iostream>
#include "Config.h"
#include "NewRpgInfo.h"
#include "PlayerbotDungeonSuggestionMgr.h"
#include "PlayerbotFactory.h"
#include "Playerbots.h"
@@ -118,7 +119,6 @@ bool PlayerbotAIConfig::Initialize()
tellWhenAvoidAoe = sConfigMgr->GetOption<bool>("AiPlayerbot.TellWhenAvoidAoe", false);
randomGearLoweringChance = sConfigMgr->GetOption<float>("AiPlayerbot.RandomGearLoweringChance", 0.0f);
incrementalGearInit = sConfigMgr->GetOption<bool>("AiPlayerbot.IncrementalGearInit", true);
randomGearQualityLimit = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomGearQualityLimit", 3);
randomGearScoreLimit = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomGearScoreLimit", 0);
@@ -148,7 +148,7 @@ bool PlayerbotAIConfig::Initialize()
LoadList<std::vector<uint32>>(
sConfigMgr->GetOption<std::string>("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"),
"3539,3623,4152,3988,4658,4284,4418,4436,4275,4323,4395"),
pvpProhibitedZoneIds);
LoadList<std::vector<uint32>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.PvpProhibitedAreaIds", "976,35"),
pvpProhibitedAreaIds);
@@ -156,7 +156,7 @@ bool PlayerbotAIConfig::Initialize()
LoadList<std::vector<uint32>>(
sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotQuestIds", "7848,3802,5505,6502,7761"),
randomBotQuestIds);
LoadSet<std::set<uint32>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.DisallowedGameObjects", "176213,17155"),
disallowedGameObjects);
botAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.BotAutologin", false);
@@ -190,19 +190,6 @@ bool PlayerbotAIConfig::Initialize()
sConfigMgr->GetOption<int32>("AiPlayerbot.MaxRandomBotsPriceChangeInterval", 48 * HOUR);
randomBotJoinLfg = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotJoinLfg", true);
restrictHealerDPS = sConfigMgr->GetOption<bool>("AiPlayerbot.HealerDPSMapRestriction", false);
LoadList<std::vector<uint32>>(
sConfigMgr->GetOption<std::string>("AiPlayerbot.RestrictedHealerDPSMaps",
"33,34,36,43,47,48,70,90,109,129,209,229,230,329,349,389,429,1001,1004,"
"1007,269,540,542,543,545,546,547,552,553,554,555,556,557,558,560,585,574,"
"575,576,578,595,599,600,601,602,604,608,619,632,650,658,668,409,469,509,"
"531,532,534,544,548,550,564,565,580,249,533,603,615,616,624,631,649,724"),
restrictedHealerDPSMaps);
//////////////////////////// ICC
EnableICCBuffs = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableICCBuffs", true);
//////////////////////////// CHAT
enableBroadcasts = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableBroadcasts", true);
randomBotTalk = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotTalk", false);
@@ -354,7 +341,7 @@ bool PlayerbotAIConfig::Initialize()
{
std::string setting = "AiPlayerbot.ZoneBracket." + std::to_string(zoneId);
std::string value = sConfigMgr->GetOption<std::string>(setting, "");
if (!value.empty())
{
size_t commaPos = value.find(',');
@@ -456,7 +443,7 @@ bool PlayerbotAIConfig::Initialize()
}
botCheats.clear();
LoadListString<std::vector<std::string>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.BotCheats", "taxi,raid"),
LoadListString<std::vector<std::string>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.BotCheats", "taxi"),
botCheats);
botCheatMask = 0;
@@ -471,17 +458,30 @@ bool PlayerbotAIConfig::Initialize()
botCheatMask |= (uint32)BotCheatMask::mana;
if (std::find(botCheats.begin(), botCheats.end(), "power") != botCheats.end())
botCheatMask |= (uint32)BotCheatMask::power;
if (std::find(botCheats.begin(), botCheats.end(), "raid") != botCheats.end())
botCheatMask |= (uint32)BotCheatMask::raid;
LoadListString<std::vector<std::string>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.AllowedLogFiles", ""),
allowedLogFiles);
LoadListString<std::vector<std::string>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.TradeActionExcludedPrefixes", ""),
tradeActionExcludedPrefixes);
worldBuffs.clear();
loadWorldBuff();
LOG_INFO("playerbots", "Loading World Buff Feature...");
LOG_INFO("playerbots", "Loading Worldbuff...");
for (uint32 factionId = 0; factionId < 3; factionId++)
{
for (uint32 classId = 0; classId < MAX_CLASSES; classId++)
{
for (uint32 specId = 0; specId <= MAX_WORLDBUFF_SPECNO; specId++)
{
for (uint32 minLevel = 0; minLevel <= randomBotMaxLevel; minLevel++)
{
for (uint32 maxLevel = minLevel; maxLevel <= randomBotMaxLevel; maxLevel++)
{
loadWorldBuff(factionId, classId, specId, minLevel, maxLevel);
}
loadWorldBuff(factionId, classId, specId, minLevel, 0);
}
}
}
}
randomBotAccountPrefix = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotAccountPrefix", "rndbot");
randomBotAccountCount = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAccountCount", 0);
@@ -579,16 +579,7 @@ bool PlayerbotAIConfig::Initialize()
autoLearnQuestSpells = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoLearnQuestSpells", false);
autoTeleportForLevel = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoTeleportForLevel", false);
autoDoQuests = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoDoQuests", true);
enableNewRpgStrategy = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableNewRpgStrategy", true);
RpgStatusProbWeight[RPG_WANDER_RANDOM] = sConfigMgr->GetOption<int32>("AiPlayerbot.RpgStatusProbWeight.WanderRandom", 15);
RpgStatusProbWeight[RPG_WANDER_NPC] = sConfigMgr->GetOption<int32>("AiPlayerbot.RpgStatusProbWeight.WanderNpc", 20);
RpgStatusProbWeight[RPG_GO_GRIND] = sConfigMgr->GetOption<int32>("AiPlayerbot.RpgStatusProbWeight.GoGrind", 15);
RpgStatusProbWeight[RPG_GO_CAMP] = sConfigMgr->GetOption<int32>("AiPlayerbot.RpgStatusProbWeight.GoCamp", 10);
RpgStatusProbWeight[RPG_DO_QUEST] = sConfigMgr->GetOption<int32>("AiPlayerbot.RpgStatusProbWeight.DoQuest", 60);
RpgStatusProbWeight[RPG_TRAVEL_FLIGHT] = sConfigMgr->GetOption<int32>("AiPlayerbot.RpgStatusProbWeight.TravelFlight", 15);
RpgStatusProbWeight[RPG_REST] = sConfigMgr->GetOption<int32>("AiPlayerbot.RpgStatusProbWeight.Rest", 5);
enableNewRpgStrategy = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableNewRpgStrategy", false);
syncLevelWithPlayers = sConfigMgr->GetOption<bool>("AiPlayerbot.SyncLevelWithPlayers", false);
freeFood = sConfigMgr->GetOption<bool>("AiPlayerbot.FreeFood", true);
randomBotGroupNearby = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotGroupNearby", true);
@@ -609,9 +600,6 @@ bool PlayerbotAIConfig::Initialize()
return true;
}
// Assign account types after accounts are created
sRandomPlayerbotMgr->AssignAccountTypes();
if (sPlayerbotAIConfig->enabled)
{
sRandomPlayerbotMgr->Init();
@@ -623,16 +611,18 @@ bool PlayerbotAIConfig::Initialize()
sPlayerbotTextMgr->LoadBotTextChance();
PlayerbotFactory::Init();
AiObjectContext::BuildAllSharedContexts();
if (!sPlayerbotAIConfig->autoDoQuests)
{
LOG_INFO("server.loading", "Loading Quest Detail Data...");
sTravelMgr->LoadQuestTravelTable();
}
if (sPlayerbotAIConfig->randomBotSuggestDungeons)
{
sPlayerbotDungeonSuggestionMgr->LoadDungeonSuggestions();
}
excludedHunterPetFamilies.clear();
LoadList<std::vector<uint32>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.ExcludedHunterPetFamilies", ""), excludedHunterPetFamilies);
LOG_INFO("server.loading", "---------------------------------------");
LOG_INFO("server.loading", " AI Playerbots initialized ");
LOG_INFO("server.loading", "---------------------------------------");
@@ -665,12 +655,6 @@ bool PlayerbotAIConfig::IsInPvpProhibitedArea(uint32 id)
return find(pvpProhibitedAreaIds.begin(), pvpProhibitedAreaIds.end(), id) != pvpProhibitedAreaIds.end();
}
bool PlayerbotAIConfig::IsRestrictedHealerDPSMap(uint32 mapId) const
{
return restrictHealerDPS &&
std::find(restrictedHealerDPSMaps.begin(), restrictedHealerDPSMaps.end(), mapId) != restrictedHealerDPSMaps.end();
}
std::string const PlayerbotAIConfig::GetTimestampStr()
{
time_t t = time(nullptr);
@@ -743,62 +727,88 @@ void PlayerbotAIConfig::log(std::string const fileName, char const* str, ...)
fflush(stdout);
}
void PlayerbotAIConfig::loadWorldBuff()
void PlayerbotAIConfig::loadWorldBuff(uint32 factionId1, uint32 classId1, uint32 specId1, uint32 minLevel1, uint32 maxLevel1)
{
std::string matrix = sConfigMgr->GetOption<std::string>("AiPlayerbot.WorldBuffMatrix", "", true);
if (matrix.empty())
return;
std::vector<uint32> buffs;
std::istringstream entryStream(matrix);
std::string entry;
std::ostringstream os;
os << "AiPlayerbot.WorldBuff." << factionId1 << "." << classId1 << "." << specId1 << "." << minLevel1 << "." << maxLevel1;
while (std::getline(entryStream, entry, ';'))
LoadList<std::vector<uint32>>(sConfigMgr->GetOption<std::string>(os.str().c_str(), "", false), buffs);
for (auto buff : buffs)
{
worldBuff wb = {buff, factionId1, classId1, specId1, minLevel1, maxLevel1};
worldBuffs.push_back(wb);
}
entry.erase(0, entry.find_first_not_of(" \t\r\n"));
entry.erase(entry.find_last_not_of(" \t\r\n") + 1);
if (maxLevel1 == 0)
{
std::ostringstream os;
os << "AiPlayerbot.WorldBuff." << factionId1 << "." << classId1 << "." << specId1 << "." << minLevel1;
size_t firstColon = entry.find(':');
size_t secondColon = entry.find(':', firstColon + 1);
LoadList<std::vector<uint32>>(sConfigMgr->GetOption<std::string>(os.str().c_str(), "", false), buffs);
if (firstColon == std::string::npos || secondColon == std::string::npos)
for (auto buff : buffs)
{
LOG_ERROR("playerbots", "Malformed entry: [{}]", entry);
continue;
worldBuff wb = {buff, factionId1, classId1, specId1, minLevel1, maxLevel1};
worldBuffs.push_back(wb);
}
}
std::string metaPart = entry.substr(firstColon + 1, secondColon - firstColon - 1);
std::string spellPart = entry.substr(secondColon + 1);
if (maxLevel1 == 0 && minLevel1 == 0)
{
std::ostringstream os;
os << "AiPlayerbot.WorldBuff." << factionId1 << "." << factionId1 << "." << classId1 << "." << specId1;
std::vector<uint32> ids;
std::istringstream metaStream(metaPart);
std::string token;
while (std::getline(metaStream, token, ','))
LoadList<std::vector<uint32>>(sConfigMgr->GetOption<std::string>(os.str().c_str(), "", false), buffs);
for (auto buff : buffs)
{
try {
ids.push_back(static_cast<uint32>(std::stoi(token)));
} catch (...) {
LOG_ERROR("playerbots", "Invalid meta token in [{}]", entry);
break;
}
worldBuff wb = {buff, factionId1, classId1, specId1, minLevel1, maxLevel1};
worldBuffs.push_back(wb);
}
}
if (ids.size() != 5)
if (maxLevel1 == 0 && minLevel1 == 0 && specId1 == 0)
{
std::ostringstream os;
os << "AiPlayerbot.WorldBuff." << factionId1 << "." << factionId1 << "." << classId1;
LoadList<std::vector<uint32>>(sConfigMgr->GetOption<std::string>(os.str().c_str(), "", false), buffs);
for (auto buff : buffs)
{
LOG_ERROR("playerbots", "Entry [{}] has incomplete meta block", entry);
continue;
worldBuff wb = {buff, factionId1, classId1, specId1, minLevel1, maxLevel1};
worldBuffs.push_back(wb);
}
}
std::istringstream spellStream(spellPart);
while (std::getline(spellStream, token, ','))
if (classId1 == 0 && maxLevel1 == 0 && minLevel1 == 0 && specId1 == 0)
{
std::ostringstream os;
os << "AiPlayerbot.WorldBuff." << factionId1;
LoadList<std::vector<uint32>>(sConfigMgr->GetOption<std::string>(os.str().c_str(), "", false), buffs);
for (auto buff : buffs)
{
try {
uint32 spellId = static_cast<uint32>(std::stoi(token));
worldBuff wb = { spellId, ids[0], ids[1], ids[2], ids[3], ids[4] };
worldBuffs.push_back(wb);
} catch (...) {
LOG_ERROR("playerbots", "Invalid spell ID in [{}]", entry);
}
worldBuff wb = {buff, factionId1, classId1, specId1, minLevel1, maxLevel1};
worldBuffs.push_back(wb);
}
}
if (factionId1 == 0 && classId1 == 0 && maxLevel1 == 0 && minLevel1 == 0 && specId1 == 0)
{
std::ostringstream os;
os << "AiPlayerbot.WorldBuff";
LoadList<std::vector<uint32>>(sConfigMgr->GetOption<std::string>(os.str().c_str(), "", false), buffs);
for (auto buff : buffs)
{
worldBuff wb = {buff, factionId1, classId1, specId1, minLevel1, maxLevel1};
worldBuffs.push_back(wb);
}
}
}

View File

@@ -7,7 +7,6 @@
#define _PLAYERBOT_PLAYERbotAICONFIG_H
#include <mutex>
#include <unordered_map>
#include "Common.h"
#include "DBCEnums.h"
@@ -22,8 +21,7 @@ enum class BotCheatMask : uint32
health = 4,
mana = 8,
power = 16,
raid = 32,
maxMask = 64
maxMask = 32
};
enum class HealingManaEfficiency : uint8
@@ -36,27 +34,8 @@ enum class HealingManaEfficiency : uint8
SUPERIOR = 32
};
enum NewRpgStatus : int
{
RPG_STATUS_START = 0,
// Going to far away place
RPG_GO_GRIND = 0,
RPG_GO_CAMP = 1,
// Exploring nearby
RPG_WANDER_RANDOM = 2,
RPG_WANDER_NPC = 3,
// Do Quest (based on quest status)
RPG_DO_QUEST = 4,
// Travel
RPG_TRAVEL_FLIGHT = 5,
// Taking a break
RPG_REST = 6,
// Initial status
RPG_IDLE = 7,
RPG_STATUS_END = 8
};
#define MAX_SPECNO 20
#define MAX_WORLDBUFF_SPECNO 3
class PlayerbotAIConfig
{
@@ -77,7 +56,6 @@ public:
bool enabled;
bool disabledWithoutRealPlayer;
bool EnableICCBuffs;
bool allowAccountBots, allowGuildBots, allowTrustedAccountBots;
bool randomBotGuildNearby, randomBotInvitePlayer, inviteChat;
uint32 globalCoolDown, reactDelay, maxWaitForMove, disableMoveSplinePath, maxMovementSearchTime, expireActionTime,
@@ -108,7 +86,6 @@ public:
std::vector<uint32> randomBotQuestIds;
uint32 randomBotTeleportDistance;
float randomGearLoweringChance;
bool incrementalGearInit;
int32 randomGearQualityLimit;
int32 randomGearScoreLimit;
float randomBotMinLevelChance, randomBotMaxLevelChance;
@@ -277,7 +254,6 @@ public:
uint32 iterationsPerTick;
std::mutex m_logMtx;
std::vector<std::string> tradeActionExcludedPrefixes;
std::vector<std::string> allowedLogFiles;
std::unordered_map<std::string, std::pair<FILE*, bool>> logFiles;
@@ -287,11 +263,11 @@ public:
struct worldBuff
{
uint32 spellId;
uint32 factionId;
uint32 classId;
uint32 specId;
uint32 minLevel;
uint32 maxLevel;
uint32 factionId = 0;
uint32 classId = 0;
uint32 specId = 0;
uint32 minLevel = 0;
uint32 maxLevel = 0;
};
std::vector<worldBuff> worldBuffs;
@@ -336,7 +312,6 @@ public:
bool autoLearnTrainerSpells;
bool autoDoQuests;
bool enableNewRpgStrategy;
std::unordered_map<NewRpgStatus, uint32> RpgStatusProbWeight;
bool syncLevelWithPlayers;
bool freeFood;
bool autoLearnQuestSpells;
@@ -398,16 +373,9 @@ public:
}
void log(std::string const fileName, const char* str, ...);
void loadWorldBuff();
void loadWorldBuff(uint32 factionId, uint32 classId, uint32 specId, uint32 minLevel, uint32 maxLevel);
static std::vector<std::vector<uint32>> ParseTempTalentsOrder(uint32 cls, std::string temp_talents_order);
static std::vector<std::vector<uint32>> ParseTempPetTalentsOrder(uint32 spec, std::string temp_talents_order);
bool restrictHealerDPS = false;
std::vector<uint32> restrictedHealerDPSMaps;
bool IsRestrictedHealerDPSMap(uint32 mapId) const;
std::vector<uint32> excludedHunterPetFamilies;
};
#define sPlayerbotAIConfig PlayerbotAIConfig::instance()

View File

@@ -36,10 +36,6 @@
#include "BroadcastHelper.h"
#include "PlayerbotDbStore.h"
#include "WorldSessionMgr.h"
#include "DatabaseEnv.h" // Added for gender choice
#include <algorithm> // Added for gender choice
#include "Log.h" // removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
#include <shared_mutex> // removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
class BotInitGuard
{
@@ -170,7 +166,7 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con
uint32 botAccountId = holder.GetAccountId();
// At login DBC locale should be what the server is set to use by default (as spells etc are hardcoded to ENUS this
// allows channels to work as intended)
WorldSession* botSession = new WorldSession(botAccountId, "", 0x0, nullptr, SEC_PLAYER, EXPANSION_WRATH_OF_THE_LICH_KING,
WorldSession* botSession = new WorldSession(botAccountId, "", nullptr, SEC_PLAYER, EXPANSION_WRATH_OF_THE_LICH_KING,
time_t(0), sWorld->GetDefaultDbcLocale(), 0, false, false, 0, true);
botSession->HandlePlayerLoginFromDB(holder); // will delete lqh
@@ -588,8 +584,8 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
}
bot->SaveToDB(false, false);
bool addClassBot = sRandomPlayerbotMgr->IsAccountType(accountId, 2);
if (addClassBot && master && abs((int)master->GetLevel() - (int)bot->GetLevel()) > 3)
bool addClassBot = sRandomPlayerbotMgr->IsAddclassBot(bot->GetGUID().GetCounter());
if (addClassBot && master && isRandomAccount && abs((int)master->GetLevel() - (int)bot->GetLevel()) > 3)
{
// PlayerbotFactory factory(bot, master->GetLevel());
// factory.Randomize(false);
@@ -841,18 +837,6 @@ std::string const PlayerbotHolder::ProcessBotCommand(std::string const cmd, Obje
return "unknown command";
}
// Added for gender choice : Returns the gender of an offline character: 0 = male, 1 = female.
static uint8 GetOfflinePlayerGender(ObjectGuid guid)
{
QueryResult result = CharacterDatabase.Query(
"SELECT gender FROM characters WHERE guid = {}", guid.GetCounter());
if (result)
return (*result)[0].Get<uint8>(); // 0 = male, 1 = female
return GENDER_MALE; // fallback value
}
bool PlayerbotMgr::HandlePlayerbotMgrCommand(ChatHandler* handler, char const* args)
{
if (!sPlayerbotAIConfig->enabled)
@@ -895,17 +879,15 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
if (!*args)
{
messages.push_back("usage: list/reload/tweak/self or add/addaccount/init/remove PLAYERNAME\n");
messages.push_back("usage: addclass CLASSNAME [male|female|0|1]");
messages.push_back("usage: addclass CLASSNAME");
return messages;
}
char* cmd = strtok((char*)args, " ");
char* charname = strtok(nullptr, " ");
char* genderArg = strtok(nullptr, " "); // Added for gender choice [male|female|0|1] optionnel
if (!cmd)
{
messages.push_back("usage: list/reload/tweak/self or add/init/remove PLAYERNAME or addclass CLASSNAME [male|female]");
messages.push_back("usage: list/reload/tweak/self or add/init/remove PLAYERNAME or addclass CLASSNAME");
return messages;
}
@@ -1128,24 +1110,6 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
messages.push_back("Error: Invalid Class. Try again.");
return messages;
}
// Added for gender choice : Parsing gender
int8 gender = -1; // -1 = gender will be random
if (genderArg)
{
std::string g = genderArg;
std::transform(g.begin(), g.end(), g.begin(), ::tolower);
if (g == "male" || g == "0")
gender = GENDER_MALE; // 0
else if (g == "female" || g == "1")
gender = GENDER_FEMALE; // 1
else
{
messages.push_back("Unknown gender : " + g + " (male/female/0/1)");
return messages;
}
} //end
if (claz == 6 && master->GetLevel() < sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL))
{
messages.push_back("Your level is too low to summon Deathknight");
@@ -1155,9 +1119,6 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
const std::unordered_set<ObjectGuid> &guidCache = sRandomPlayerbotMgr->addclassCache[RandomPlayerbotMgr::GetTeamClassIdx(teamId == TEAM_ALLIANCE, claz)];
for (const ObjectGuid &guid: guidCache)
{
// If the user requested a specific gender, skip any character that doesn't match.
if (gender != -1 && GetOfflinePlayerGender(guid) != gender)
continue;
if (botLoading.find(guid) != botLoading.end())
continue;
if (ObjectAccessor::FindConnectedPlayer(guid))
@@ -1728,70 +1689,21 @@ void PlayerbotsMgr::RemovePlayerBotData(ObjectGuid const& guid, bool is_AI)
PlayerbotAI* PlayerbotsMgr::GetPlayerbotAI(Player* player)
{
// if (!(sPlayerbotAIConfig->enabled) || !player)
// {
if (!(sPlayerbotAIConfig->enabled) || !player)
{
return nullptr;
}
// if (player->GetSession()->isLogingOut() || player->IsDuringRemoveFromWorld()) {
// return nullptr;
// }
// // if (player->GetSession()->isLogingOut() || player->IsDuringRemoveFromWorld()) {
// // return nullptr;
// // }
// auto itr = _playerbotsAIMap.find(player->GetGUID());
// if (itr != _playerbotsAIMap.end())
// {
// if (itr->second->IsBotAI())
// return reinterpret_cast<PlayerbotAI*>(itr->second);
// }
//
// return nullptr;
// removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
if (!player || !sPlayerbotAIConfig->enabled)
return nullptr;
// First read the GUID into a local variable, but ONLY after the check!
ObjectGuid guid = player->GetGUID(); // <-- OK here, we know that player != nullptr
{
std::shared_lock rlock(_aiMutex);
auto it = _playerbotsAIMap.find(guid);
if (it != _playerbotsAIMap.end() && it->second->IsBotAI())
return static_cast<PlayerbotAI*>(it->second);
}
// Transient state: NEVER break the master ⇄ bots relationship here.
if (!ObjectAccessor::FindPlayer(guid))
auto itr = _playerbotsAIMap.find(player->GetGUID());
if (itr != _playerbotsAIMap.end())
{
RemovePlayerbotAI(guid, /*removeMgrEntry=*/false);
}
return nullptr;
}
// removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
PlayerbotAI* PlayerbotsMgr::GetPlayerbotAIByGuid(ObjectGuid guid)
{
if (!sPlayerbotAIConfig->enabled)
return nullptr;
std::shared_lock rlock(_aiMutex);
auto it = _playerbotsAIMap.find(guid);
if (it != _playerbotsAIMap.end() && it->second->IsBotAI())
return static_cast<PlayerbotAI*>(it->second);
return nullptr;
}
void PlayerbotsMgr::RemovePlayerbotAI(ObjectGuid const& guid, bool removeMgrEntry /*= true*/)
{
std::unique_lock wlock(_aiMutex);
if (auto it = _playerbotsAIMap.find(guid); it != _playerbotsAIMap.end())
{
delete it->second;
_playerbotsAIMap.erase(it);
LOG_DEBUG("playerbots", "Removed stale AI for GUID {}",
static_cast<uint64>(guid.GetRawValue()));
if (itr->second->IsBotAI())
return reinterpret_cast<PlayerbotAI*>(itr->second);
}
if (removeMgrEntry)
_playerbotsMgrMap.erase(guid); // we NO longer touch the relation in a "soft" purge
return nullptr;
}
PlayerbotMgr* PlayerbotsMgr::GetPlayerbotMgr(Player* player)

View File

@@ -12,7 +12,6 @@
#include "PlayerbotAIBase.h"
#include "QueryHolder.h"
#include "QueryResult.h"
#include <shared_mutex> // removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
class ChatHandler;
class PlayerbotAI;
@@ -115,38 +114,13 @@ public:
void RemovePlayerBotData(ObjectGuid const& guid, bool is_AI);
PlayerbotAI* GetPlayerbotAI(Player* player);
PlayerbotAI* GetPlayerbotAIByGuid(ObjectGuid guid); // removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
// void RemovePlayerbotAI(ObjectGuid const& guid); // removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
// removeMgrEntry = true => "hard" purge (AI + manager relation), for real logouts
// removeMgrEntry = false => "soft" purge (AI only), for detected "stale" cases
void RemovePlayerbotAI(ObjectGuid const& guid, bool removeMgrEntry = true);
PlayerbotMgr* GetPlayerbotMgr(Player* player);
private:
std::unordered_map<ObjectGuid, PlayerbotAIBase*> _playerbotsAIMap;
std::unordered_map<ObjectGuid, PlayerbotAIBase*> _playerbotsMgrMap;
mutable std::shared_mutex _aiMutex; // removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
};
#define sPlayerbotsMgr PlayerbotsMgr::instance()
// Temporary addition If it keeps crashing, we will use them.
// Like
// BEFORE : PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
// AFTER (safe) : PlayerbotAI* botAI = GET_PLAYERBOT_AI_SAFE(bot);
// BEFORE : if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(player)) { ... }
// AFTER (safe) : if (PlayerbotAI* botAI = GET_PLAYERBOT_AI_SAFE(player)) { ... }
// --- SAFE helpers (append to PlayerbotMgr.h) ---
inline PlayerbotAI* GET_PLAYERBOT_AI_SAFE(Player* p)
{
// Avoid any dereference during transient states (nullptr, teleport, flight, etc.)
return p ? sPlayerbotsMgr->GetPlayerbotAI(p) : nullptr;
}
inline PlayerbotMgr* GET_PLAYERBOT_MGR_SAFE(Player* p)
{
return p ? sPlayerbotsMgr->GetPlayerbotMgr(p) : nullptr;
}
// --- end SAFE helpers ---
#endif

View File

@@ -29,7 +29,6 @@
#include "ScriptMgr.h"
#include "cs_playerbots.h"
#include "cmath"
#include "BattleGroundTactics.h"
class PlayerbotsDatabaseScript : public DatabaseScript
{
@@ -197,13 +196,21 @@ public:
sRandomPlayerbotMgr->HandleCommand(type, msg, player);
}
bool OnPlayerBeforeAchievementComplete(Player* player, AchievementEntry const* achievement) override
bool OnPlayerBeforeCriteriaProgress(Player* player, AchievementCriteriaEntry const* /*criteria*/) override
{
if (sRandomPlayerbotMgr->IsRandomBot(player) && (achievement->flags == 256 || achievement->flags == 768))
if (sRandomPlayerbotMgr->IsRandomBot(player))
{
return false;
}
return true;
}
bool OnPlayerBeforeAchievementComplete(Player* player, AchievementEntry const* /*achievement*/) override
{
if (sRandomPlayerbotMgr->IsRandomBot(player))
{
return false;
}
return true;
}
@@ -363,10 +370,6 @@ public:
void OnPlayerbotLogout(Player* player) override
{
// immediate purge of the bot's AI upon disconnection
if (player && player->GetSession()->IsBot())
sPlayerbotsMgr->RemovePlayerbotAI(player->GetGUID()); // removes a long-standing crash (0xC0000005 ACCESS_VIOLATION)
if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player))
{
PlayerbotAI* botAI = GET_PLAYERBOT_AI(player);
@@ -386,43 +389,6 @@ public:
}
};
class PlayerBotsBGScript : public BGScript
{
public:
PlayerBotsBGScript() : BGScript("PlayerBotsBGScript") {}
void OnBattlegroundStart(Battleground* bg) override
{
BGStrategyData data;
switch (bg->GetBgTypeID())
{
case BATTLEGROUND_WS:
data.allianceStrategy = urand(0, WS_STRATEGY_MAX - 1);
data.hordeStrategy = urand(0, WS_STRATEGY_MAX - 1);
break;
case BATTLEGROUND_AB:
data.allianceStrategy = urand(0, AB_STRATEGY_MAX - 1);
data.hordeStrategy = urand(0, AB_STRATEGY_MAX - 1);
break;
case BATTLEGROUND_AV:
data.allianceStrategy = urand(0, AV_STRATEGY_MAX - 1);
data.hordeStrategy = urand(0, AV_STRATEGY_MAX - 1);
break;
case BATTLEGROUND_EY:
data.allianceStrategy = urand(0, EY_STRATEGY_MAX - 1);
data.hordeStrategy = urand(0, EY_STRATEGY_MAX - 1);
break;
default:
break;
}
bgStrategies[bg->GetInstanceID()] = data;
}
void OnBattlegroundEnd(Battleground* bg, TeamId /*winnerTeam*/) override { bgStrategies.erase(bg->GetInstanceID()); }
};
void AddPlayerbotsScripts()
{
new PlayerbotsDatabaseScript();
@@ -431,7 +397,6 @@ void AddPlayerbotsScripts()
new PlayerbotsServerScript();
new PlayerbotsWorldScript();
new PlayerbotsScript();
new PlayerBotsBGScript();
AddSC_playerbots_commandscript();
}

View File

@@ -393,118 +393,37 @@ std::string const RandomPlayerbotFactory::CreateRandomBotName(NameRaceAndGender
return std::move(botName);
}
// Calculates the total number of required accounts, either using the specified randomBotAccountCount
// or determining it dynamically based on MaxRandomBots, EnablePeriodicOnlineOffline and its ratio,
// and AddClassAccountPoolSize. The system also factors in the types of existing account, as assigned by
// AssignAccountTypes()
uint32 RandomPlayerbotFactory::CalculateTotalAccountCount()
{
// Reset account types if features are disabled
// Reset is done here to precede needed accounts calculations
if (sPlayerbotAIConfig->maxRandomBots == 0 || sPlayerbotAIConfig->addClassAccountPoolSize == 0)
{
if (sPlayerbotAIConfig->maxRandomBots == 0)
{
PlayerbotsDatabase.Execute("UPDATE playerbots_account_type SET account_type = 0 WHERE account_type = 1");
LOG_INFO("playerbots", "MaxRandomBots set to 0, any RNDbot accounts (type 1) will be unassigned (type 0)");
}
if (sPlayerbotAIConfig->addClassAccountPoolSize == 0)
{
PlayerbotsDatabase.Execute("UPDATE playerbots_account_type SET account_type = 0 WHERE account_type = 2");
LOG_INFO("playerbots", "AddClassAccountPoolSize set to 0, any AddClass accounts (type 2) will be unassigned (type 0)");
}
// Wait for DB to reflect the change, up to 1 second max. This is needed to make sure other logs don't show wrong info
for (int waited = 0; waited < 1000; waited += 50)
{
QueryResult res = PlayerbotsDatabase.Query("SELECT COUNT(*) FROM playerbots_account_type WHERE account_type IN ({}, {})",
sPlayerbotAIConfig->maxRandomBots == 0 ? 1 : -1,
sPlayerbotAIConfig->addClassAccountPoolSize == 0 ? 2 : -1);
if (!res || res->Fetch()[0].Get<uint64>() == 0)
{
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(50)); // Extra 50ms fixed delay for safety.
}
}
// Calculates the total number of required accounts, either using the specified randomBotAccountCount
// or determining it dynamically based on the WOTLK condition, max random bots, rotation pool size,
// and additional class account pool size.
// Checks if randomBotAccountCount is set, otherwise calculate it dynamically.
if (sPlayerbotAIConfig->randomBotAccountCount > 0)
return sPlayerbotAIConfig->randomBotAccountCount;
// Check existing account types
uint32 existingRndBotAccounts = 0;
uint32 existingAddClassAccounts = 0;
uint32 existingUnassignedAccounts = 0;
// Avoid creating accounts if both maxRandom & ClassBots are set to zero.
if (sPlayerbotAIConfig->maxRandomBots == 0 &&
sPlayerbotAIConfig->addClassAccountPoolSize == 0)
return 0;
QueryResult typeCheck = PlayerbotsDatabase.Query("SELECT account_type, COUNT(*) FROM playerbots_account_type GROUP BY account_type");
if (typeCheck)
{
do
{
Field* fields = typeCheck->Fetch();
uint8 accountType = fields[0].Get<uint8>();
uint32 count = fields[1].Get<uint32>();
//bool isWOTLK = sWorld->getIntConfig(CONFIG_EXPANSION) == EXPANSION_WRATH_OF_THE_LICH_KING; //not used, line marked for removal.
if (accountType == 0) existingUnassignedAccounts = count;
else if (accountType == 1) existingRndBotAccounts = count;
else if (accountType == 2) existingAddClassAccounts = count;
} while (typeCheck->NextRow());
}
// Determine divisor based on Death Knight login eligibility and requested A&H faction ratio
// Determine divisor based on WOTLK condition
int divisor = CalculateAvailableCharsPerAccount();
// Calculate max bots
int maxBots = sPlayerbotAIConfig->maxRandomBots;
// Take periodic online - offline into account
// Take perodic online - offline into account
if (sPlayerbotAIConfig->enablePeriodicOnlineOffline)
{
maxBots *= sPlayerbotAIConfig->periodicOnlineOfflineRatio;
}
// Calculate number of accounts needed for RNDbots
// Result is rounded up for maxBots not cleanly divisible by the divisor
uint32 neededRndBotAccounts = (maxBots + divisor - 1) / divisor;
uint32 neededAddClassAccounts = sPlayerbotAIConfig->addClassAccountPoolSize;
// Start with existing total
uint32 existingTotal = existingRndBotAccounts + existingAddClassAccounts + existingUnassignedAccounts;
// Calculate shortfalls after using unassigned accounts
uint32 availableUnassigned = existingUnassignedAccounts;
uint32 additionalAccountsNeeded = 0;
// Check RNDbot needs
if (neededRndBotAccounts > existingRndBotAccounts)
{
uint32 rndBotShortfall = neededRndBotAccounts - existingRndBotAccounts;
if (rndBotShortfall <= availableUnassigned)
availableUnassigned -= rndBotShortfall;
else
{
additionalAccountsNeeded += (rndBotShortfall - availableUnassigned);
availableUnassigned = 0;
}
}
// Check AddClass needs
if (neededAddClassAccounts > existingAddClassAccounts)
{
uint32 addClassShortfall = neededAddClassAccounts - existingAddClassAccounts;
if (addClassShortfall <= availableUnassigned)
availableUnassigned -= addClassShortfall;
else
{
additionalAccountsNeeded += (addClassShortfall - availableUnassigned);
availableUnassigned = 0;
}
}
// Return existing total plus any additional accounts needed
return existingTotal + additionalAccountsNeeded;
// Calculate base accounts, add class account pool size, and add 1 as a fixed offset
uint32 baseAccounts = maxBots / divisor;
return baseAccounts + sPlayerbotAIConfig->addClassAccountPoolSize + 1;
}
uint32 RandomPlayerbotFactory::CalculateAvailableCharsPerAccount()
@@ -556,9 +475,8 @@ void RandomPlayerbotFactory::CreateRandomBots()
LOG_INFO("playerbots", "Deleting all random bot characters and accounts...");
// First execute all the cleanup SQL commands
// Clear playerbots_random_bots and playerbots_account_type
// Clear playerbots_random_bots table
PlayerbotsDatabase.Execute("DELETE FROM playerbots_random_bots");
PlayerbotsDatabase.Execute("DELETE FROM playerbots_account_type");
// Get the database names dynamically
std::string loginDBName = LoginDatabase.GetConnectionInfo()->database;
@@ -568,13 +486,6 @@ void RandomPlayerbotFactory::CreateRandomBots()
CharacterDatabase.Execute("DELETE FROM characters WHERE account IN (SELECT id FROM " + loginDBName + ".account WHERE username LIKE '{}%%')",
sPlayerbotAIConfig->randomBotAccountPrefix.c_str());
// Wait for the characters to be deleted before proceeding to dependent deletes
while (CharacterDatabase.QueueSize())
{
std::this_thread::sleep_for(1s);
}
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Extra 100ms fixed delay for safety.
// Clean up orphaned entries in playerbots_guild_tasks
PlayerbotsDatabase.Execute("DELETE FROM playerbots_guild_tasks WHERE owner NOT IN (SELECT guid FROM " + characterDBName + ".characters)");
@@ -589,13 +500,11 @@ void RandomPlayerbotFactory::CreateRandomBots()
CharacterDatabase.Execute("DELETE FROM character_achievement WHERE guid NOT IN (SELECT guid FROM characters)");
CharacterDatabase.Execute("DELETE FROM character_achievement_progress WHERE guid NOT IN (SELECT guid FROM characters)");
CharacterDatabase.Execute("DELETE FROM character_action WHERE guid NOT IN (SELECT guid FROM characters)");
CharacterDatabase.Execute("DELETE FROM character_arena_stats WHERE guid NOT IN (SELECT guid FROM characters)");
CharacterDatabase.Execute("DELETE FROM character_aura WHERE guid NOT IN (SELECT guid FROM characters)");
CharacterDatabase.Execute("DELETE FROM character_entry_point WHERE guid NOT IN (SELECT guid FROM characters)");
CharacterDatabase.Execute("DELETE FROM character_glyphs WHERE guid NOT IN (SELECT guid FROM characters)");
CharacterDatabase.Execute("DELETE FROM character_homebind WHERE guid NOT IN (SELECT guid FROM characters)");
CharacterDatabase.Execute("DELETE FROM character_inventory WHERE guid NOT IN (SELECT guid FROM characters)");
CharacterDatabase.Execute("DELETE FROM item_instance WHERE owner_guid NOT IN (SELECT guid FROM characters) AND owner_guid > 0");
CharacterDatabase.Execute("DELETE FROM character_inventory WHERE guid NOT IN (SELECT guid FROM characters)");
// Clean up pet data
CharacterDatabase.Execute("DELETE FROM character_pet WHERE owner NOT IN (SELECT guid FROM characters)");
@@ -650,23 +559,11 @@ void RandomPlayerbotFactory::CreateRandomBots()
uint32 timer = getMSTime();
// After ALL deletions, make sure data is commited to DB
LoginDatabase.Execute("COMMIT");
CharacterDatabase.Execute("COMMIT");
PlayerbotsDatabase.Execute("COMMIT");
// Wait for all pending database operations to complete
while (LoginDatabase.QueueSize() || CharacterDatabase.QueueSize() || PlayerbotsDatabase.QueueSize())
{
std::this_thread::sleep_for(1s);
}
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Extra 100ms fixed delay for safety.
// Flush tables to ensure all data in memory are written to disk
LoginDatabase.Execute("FLUSH TABLES");
CharacterDatabase.Execute("FLUSH TABLES");
PlayerbotsDatabase.Execute("FLUSH TABLES");
LOG_INFO("playerbots", ">> Random bot accounts and data deleted in {} ms", GetMSTimeDiffToNow(timer));
LOG_INFO("playerbots", "Please reset the AiPlayerbot.DeleteRandomBotAccounts to 0 and restart the server...");
World::StopNow(SHUTDOWN_EXIT_CODE);
@@ -785,7 +682,7 @@ void RandomPlayerbotFactory::CreateRandomBots()
LOG_DEBUG("playerbots", "Creating random bot characters for account: [{}/{}]", accountNumber + 1, totalAccountCount);
RandomPlayerbotFactory factory(accountId);
WorldSession* session = new WorldSession(accountId, "", 0x0, nullptr, SEC_PLAYER, EXPANSION_WRATH_OF_THE_LICH_KING,
WorldSession* session = new WorldSession(accountId, "", nullptr, SEC_PLAYER, EXPANSION_WRATH_OF_THE_LICH_KING,
time_t(0), LOCALE_enUS, 0, false, false, 0, true);
sessionBots.push_back(session);

File diff suppressed because it is too large Load Diff

View File

@@ -60,6 +60,7 @@ public:
bool IsEmpty() { return !lastChangeTime; }
public:
uint32 value;
uint32 lastChangeTime;
uint32 validIn;
@@ -103,6 +104,10 @@ public:
void LogPlayerLocation();
void UpdateAIInternal(uint32 elapsed, bool minimal = false) override;
private:
//void ScaleBotActivity();
public:
uint32 activeBots = 0;
static bool HandlePlayerbotConsoleCommand(ChatHandler* handler, char const* args);
bool IsRandomBot(Player* bot);
@@ -175,8 +180,6 @@ public:
std::map<uint8, std::vector<WorldLocation>> locsPerLevelCache;
std::map<uint8, std::vector<WorldLocation>> allianceStarterPerLevelCache;
std::map<uint8, std::vector<WorldLocation>> hordeStarterPerLevelCache;
std::vector<uint32> allianceFlightMasterCache;
std::vector<uint32> hordeFlightMasterCache;
struct LevelBracket {
uint32 low;
uint32 high;
@@ -184,11 +187,6 @@ public:
};
std::map<uint32, LevelBracket> zone2LevelBracket;
std::map<uint8, std::vector<WorldLocation>> bankerLocsPerLevelCache;
// Account type management
void AssignAccountTypes();
bool IsAccountType(uint32 accountId, uint8 accountType);
protected:
void OnBotLoginInternal(Player* const bot) override;
@@ -218,8 +216,10 @@ private:
void RandomTeleport(Player* bot, std::vector<WorldLocation>& locs, bool hearth = false);
uint32 GetZoneLevel(uint16 mapId, float teleX, float teleY, float teleZ);
typedef void (RandomPlayerbotMgr::*ConsoleCommandHandler)(Player*);
std::vector<Player*> players;
uint32 processTicks;
// std::map<uint32, std::vector<WorldLocation>> rpgLocsCache;
std::map<uint32, std::map<uint32, std::vector<WorldLocation>>> rpgLocsCacheLevel;
@@ -228,12 +228,6 @@ private:
std::list<uint32> currentBots;
uint32 bgBotsCount;
uint32 playersLevel;
// Account lists
std::vector<uint32> rndBotTypeAccounts; // Accounts marked as RNDbot (type 1)
std::vector<uint32> addClassTypeAccounts; // Accounts marked as AddClass (type 2)
//void ScaleBotActivity(); // Deprecated function
};
#define sRandomPlayerbotMgr RandomPlayerbotMgr::instance()

View File

@@ -3336,7 +3336,7 @@ void TravelMgr::LoadQuestTravelTable()
uint32 accountId = fields[0].Get<uint32>();
WorldSession* session =
new WorldSession(accountId, "", 0x0, nullptr, SEC_PLAYER, EXPANSION_WRATH_OF_THE_LICH_KING, time_t(0),
new WorldSession(accountId, "", nullptr, SEC_PLAYER, EXPANSION_WRATH_OF_THE_LICH_KING, time_t(0),
LOCALE_enUS, 0, false, false, 0, true);
std::vector<std::pair<std::pair<uint32, uint32>, uint32>> classSpecLevel;

View File

@@ -39,7 +39,6 @@
#include "SpellAuraDefines.h"
#include "StatsWeightCalculator.h"
#include "World.h"
#include "AiObjectContext.h"
const uint64 diveMask = (1LL << 7) | (1LL << 44) | (1LL << 37) | (1LL << 38) | (1LL << 26) | (1LL << 30) | (1LL << 27) |
(1LL << 33) | (1LL << 24) | (1LL << 34);
@@ -164,16 +163,15 @@ void PlayerbotFactory::Init()
{
continue;
}
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(gemId);
if (proto) {
if (proto->ItemLevel < 60)
continue;
if (proto->Flags & ITEM_FLAG_UNIQUE_EQUIPPABLE)
continue;
if (proto->ItemLevel < 60)
continue;
if (proto->Flags & ITEM_FLAG_UNIQUE_EQUIPPABLE)
{
continue;
}
if (sRandomItemMgr->IsTestItem(gemId))
continue;
@@ -181,11 +179,9 @@ void PlayerbotFactory::Init()
{
continue;
}
// LOG_INFO("playerbots", "Add {} to enchantment gems", gemId);
enchantGemIdCache.push_back(gemId);
}
LOG_INFO("playerbots", "Loading {} enchantment gems", enchantGemIdCache.size());
}
@@ -228,22 +224,24 @@ void PlayerbotFactory::Randomize(bool incremental)
{
bot->resetTalents(true);
}
// bot->SaveToDB(false, false);
ClearSkills();
// bot->SaveToDB(false, false);
ClearSpells();
// bot->SaveToDB(false, false);
if (!incremental)
{
ClearSkills();
ClearSpells();
ResetQuests();
if (!sPlayerbotAIConfig->equipmentPersistence || level < sPlayerbotAIConfig->equipmentPersistenceLevel)
{
ClearAllItems();
}
}
ClearInventory();
if (!sPlayerbotAIConfig->equipmentPersistence || level < sPlayerbotAIConfig->equipmentPersistenceLevel)
{
ClearAllItems();
}
bot->RemoveAllSpellCooldown();
UnbindInstance();
bot->GiveLevel(level);
bot->InitStatsForLevel(true);
bot->InitStatsForLevel();
CancelAuras();
// bot->SaveToDB(false, false);
if (pmo)
@@ -282,6 +280,7 @@ void PlayerbotFactory::Randomize(bool incremental)
LOG_DEBUG("playerbots", "Initializing skills (step 1)...");
pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Skills1");
InitSkills();
// InitTradeSkills();
if (pmo)
pmo->finish();
@@ -338,8 +337,7 @@ void PlayerbotFactory::Randomize(bool incremental)
if (!incremental || !sPlayerbotAIConfig->equipmentPersistence ||
bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel)
{
if (sPlayerbotAIConfig->incrementalGearInit || !incremental)
InitEquipment(incremental, incremental ? false : sPlayerbotAIConfig->twoRoundsGearInit);
InitEquipment(incremental, incremental ? false : sPlayerbotAIConfig->twoRoundsGearInit);
}
// bot->SaveToDB(false, false);
if (pmo)
@@ -417,7 +415,7 @@ void PlayerbotFactory::Randomize(bool incremental)
pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Consumable");
LOG_DEBUG("playerbots", "Initializing consumables...");
InitConsumables();
AddConsumables();
if (pmo)
pmo->finish();
@@ -485,8 +483,11 @@ void PlayerbotFactory::Refresh()
InitAmmo();
InitFood();
InitReagents();
InitConsumables();
InitPotions();
// InitPotions();
if (!sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel)
{
InitTalentsTree(true, true, true);
}
InitPet();
InitPetTalents();
InitClassSpells();
@@ -496,10 +497,7 @@ void PlayerbotFactory::Refresh()
InitSpecialSpells();
InitMounts();
InitKeyring();
if (!sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel)
{
InitTalentsTree(true, true, true);
}
InitPotions();
if (bot->GetLevel() >= sPlayerbotAIConfig->minEnchantingBotLevel)
{
ApplyEnchantAndGemsNew();
@@ -513,218 +511,151 @@ void PlayerbotFactory::Refresh()
// bot->SaveToDB(false, false);
}
void PlayerbotFactory::InitConsumables()
void PlayerbotFactory::AddConsumables()
{
int specTab = AiFactory::GetPlayerSpecTab(bot);
std::vector<std::pair<uint32, uint32>> items;
switch (bot->getClass())
{
case CLASS_PRIEST:
{
// Discipline or Holy: Mana Oil
if (specTab == 0 || specTab == 1)
{
std::vector<uint32> mana_oils = {BRILLIANT_MANA_OIL, SUPERIOR_MANA_OIL, LESSER_MANA_OIL, MINOR_MANA_OIL};
for (uint32 itemId : mana_oils)
{
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
if (proto->RequiredLevel > level || level > 75)
continue;
items.push_back({itemId, 4});
break;
}
}
// Shadow: Wizard Oil
if (specTab == 2)
{
std::vector<uint32> wizard_oils = {BRILLIANT_WIZARD_OIL, SUPERIOR_WIZARD_OIL, WIZARD_OIL, LESSER_WIZARD_OIL, MINOR_WIZARD_OIL};
for (uint32 itemId : wizard_oils)
{
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
if (proto->RequiredLevel > level || level > 75)
continue;
items.push_back({itemId, 4});
break;
}
}
break;
}
case CLASS_MAGE:
case CLASS_WARLOCK:
{
// Always Wizard Oil
std::vector<uint32> wizard_oils = {BRILLIANT_WIZARD_OIL, SUPERIOR_WIZARD_OIL, WIZARD_OIL, LESSER_WIZARD_OIL, MINOR_WIZARD_OIL};
for (uint32 itemId : wizard_oils)
if (level >= 5 && level < 20)
{
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
if (proto->RequiredLevel > level || level > 75)
continue;
items.push_back({itemId, 4});
break;
StoreItem(CONSUM_ID_MINOR_WIZARD_OIL, 5);
}
break;
}
case CLASS_DRUID:
{
// Balance: Wizard Oil
if (specTab == 0)
if (level >= 20 && level < 40)
{
std::vector<uint32> wizard_oils = {BRILLIANT_WIZARD_OIL, SUPERIOR_WIZARD_OIL, WIZARD_OIL, LESSER_WIZARD_OIL, MINOR_WIZARD_OIL};
for (uint32 itemId : wizard_oils)
{
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
if (proto->RequiredLevel > level || level > 75)
continue;
items.push_back({itemId, 4});
break;
}
StoreItem(CONSUM_ID_MINOR_MANA_OIL, 5);
StoreItem(CONSUM_ID_MINOR_WIZARD_OIL, 5);
}
// Feral: Sharpening Stones & Weightstones
else if (specTab == 1)
if (level >= 40 && level < 45)
{
std::vector<uint32> sharpening_stones = {ADAMANTITE_SHARPENING_STONE, FEL_SHARPENING_STONE, DENSE_SHARPENING_STONE, SOLID_SHARPENING_STONE, HEAVY_SHARPENING_STONE, COARSE_SHARPENING_STONE, ROUGH_SHARPENING_STONE};
std::vector<uint32> weightstones = {ADAMANTITE_WEIGHTSTONE, FEL_WEIGHTSTONE, DENSE_WEIGHTSTONE, SOLID_WEIGHTSTONE, HEAVY_WEIGHTSTONE, COARSE_WEIGHTSTONE, ROUGH_WEIGHTSTONE};
for (uint32 itemId : sharpening_stones)
{
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
if (proto->RequiredLevel > level || level > 75)
continue;
items.push_back({itemId, 20});
break;
}
for (uint32 itemId : weightstones)
{
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
if (proto->RequiredLevel > level || level > 75)
continue;
items.push_back({itemId, 20});
break;
}
StoreItem(CONSUM_ID_MINOR_MANA_OIL, 5);
StoreItem(CONSUM_ID_WIZARD_OIL, 5);
}
// Restoration: Mana Oil
else if (specTab == 2)
if (level >= 45 && level < 52)
{
std::vector<uint32> mana_oils = {BRILLIANT_MANA_OIL, SUPERIOR_MANA_OIL, LESSER_MANA_OIL, MINOR_MANA_OIL};
for (uint32 itemId : mana_oils)
{
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
if (proto->RequiredLevel > level || level > 75)
continue;
items.push_back({itemId, 4});
break;
}
StoreItem(CONSUM_ID_BRILLIANT_MANA_OIL, 5);
StoreItem(CONSUM_ID_BRILLIANT_WIZARD_OIL, 5);
}
if (level >= 52 && level < 58)
{
StoreItem(CONSUM_ID_SUPERIOR_MANA_OIL, 5);
StoreItem(CONSUM_ID_BRILLIANT_WIZARD_OIL, 5);
}
if (level >= 58)
{
StoreItem(CONSUM_ID_SUPERIOR_MANA_OIL, 5);
StoreItem(CONSUM_ID_SUPERIOR_WIZARD_OIL, 5);
}
break;
}
case CLASS_PALADIN:
{
// Holy: Mana Oil
if (specTab == 0)
{
std::vector<uint32> mana_oils = {BRILLIANT_MANA_OIL, SUPERIOR_MANA_OIL, LESSER_MANA_OIL, MINOR_MANA_OIL};
for (uint32 itemId : mana_oils)
{
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
if (proto->RequiredLevel > level || level > 75)
continue;
items.push_back({itemId, 4});
break;
}
}
// Protection: Wizard Oil (Protection prioritizes Superior over Brilliant)
else if (specTab == 1)
{
std::vector<uint32> wizard_oils = {BRILLIANT_WIZARD_OIL, SUPERIOR_WIZARD_OIL, WIZARD_OIL, LESSER_WIZARD_OIL, MINOR_WIZARD_OIL};
for (uint32 itemId : wizard_oils)
{
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
if (proto->RequiredLevel > level || level > 75)
continue;
items.push_back({itemId, 4});
break;
}
}
// Retribution: Sharpening Stones & Weightstones
else if (specTab == 2)
{
std::vector<uint32> sharpening_stones = {ADAMANTITE_SHARPENING_STONE, FEL_SHARPENING_STONE, DENSE_SHARPENING_STONE, SOLID_SHARPENING_STONE, HEAVY_SHARPENING_STONE, COARSE_SHARPENING_STONE, ROUGH_SHARPENING_STONE};
std::vector<uint32> weightstones = {ADAMANTITE_WEIGHTSTONE, FEL_WEIGHTSTONE, DENSE_WEIGHTSTONE, SOLID_WEIGHTSTONE, HEAVY_WEIGHTSTONE, COARSE_WEIGHTSTONE, ROUGH_WEIGHTSTONE};
for (uint32 itemId : sharpening_stones)
{
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
if (proto->RequiredLevel > level || level > 75)
continue;
items.push_back({itemId, 20});
break;
}
for (uint32 itemId : weightstones)
{
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
if (proto->RequiredLevel > level || level > 75)
continue;
items.push_back({itemId, 20});
break;
}
}
break;
}
case CLASS_WARRIOR:
case CLASS_HUNTER:
{
// Sharpening Stones & Weightstones
std::vector<uint32> sharpening_stones = {ADAMANTITE_SHARPENING_STONE, FEL_SHARPENING_STONE, DENSE_SHARPENING_STONE, SOLID_SHARPENING_STONE, HEAVY_SHARPENING_STONE, COARSE_SHARPENING_STONE, ROUGH_SHARPENING_STONE};
std::vector<uint32> weightstones = {ADAMANTITE_WEIGHTSTONE, FEL_WEIGHTSTONE, DENSE_WEIGHTSTONE, SOLID_WEIGHTSTONE, HEAVY_WEIGHTSTONE, COARSE_WEIGHTSTONE, ROUGH_WEIGHTSTONE};
for (uint32 itemId : sharpening_stones)
if (level >= 1 && level < 5)
{
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
if (proto->RequiredLevel > level || level > 75)
continue;
items.push_back({itemId, 20});
break;
StoreItem(CONSUM_ID_ROUGH_SHARPENING_STONE, 5);
StoreItem(CONSUM_ID_ROUGH_WEIGHTSTONE, 5);
}
for (uint32 itemId : weightstones)
if (level >= 5 && level < 15)
{
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
if (proto->RequiredLevel > level || level > 75)
continue;
items.push_back({itemId, 20});
break;
StoreItem(CONSUM_ID_COARSE_WEIGHTSTONE, 5);
StoreItem(CONSUM_ID_COARSE_SHARPENING_STONE, 5);
}
if (level >= 15 && level < 25)
{
StoreItem(CONSUM_ID_HEAVY_WEIGHTSTONE, 5);
StoreItem(CONSUM_ID_HEAVY_SHARPENING_STONE, 5);
}
if (level >= 25 && level < 35)
{
StoreItem(CONSUM_ID_SOL_SHARPENING_STONE, 5);
StoreItem(CONSUM_ID_SOLID_WEIGHTSTONE, 5);
}
if (level >= 35 && level < 50)
{
StoreItem(CONSUM_ID_DENSE_WEIGHTSTONE, 5);
StoreItem(CONSUM_ID_DENSE_SHARPENING_STONE, 5);
}
if (level >= 50 && level < 60)
{
StoreItem(CONSUM_ID_FEL_SHARPENING_STONE, 5);
StoreItem(CONSUM_ID_FEL_WEIGHTSTONE, 5);
}
if (level >= 60)
{
StoreItem(CONSUM_ID_ADAMANTITE_WEIGHTSTONE, 5);
StoreItem(CONSUM_ID_ADAMANTITE_SHARPENING_STONE, 5);
}
break;
}
case CLASS_ROGUE:
{
// Poisons
std::vector<uint32> instant_poisons = {INSTANT_POISON_IX, INSTANT_POISON_VIII, INSTANT_POISON_VII, INSTANT_POISON_VI, INSTANT_POISON_V, INSTANT_POISON_IV, INSTANT_POISON_III, INSTANT_POISON_II, INSTANT_POISON};
std::vector<uint32> deadly_poisons = {DEADLY_POISON_IX, DEADLY_POISON_VIII, DEADLY_POISON_VII, DEADLY_POISON_VI, DEADLY_POISON_V, DEADLY_POISON_IV, DEADLY_POISON_III, DEADLY_POISON_II, DEADLY_POISON};
for (uint32 itemId : deadly_poisons)
if (level >= 20 && level < 28)
{
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
if (proto->RequiredLevel > level)
continue;
items.push_back({itemId, 20});
break;
StoreItem(CONSUM_ID_INSTANT_POISON, 5);
}
for (uint32 itemId : instant_poisons)
if (level >= 28 && level < 30)
{
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
if (proto->RequiredLevel > level)
continue;
items.push_back({itemId, 20});
break;
StoreItem(CONSUM_ID_INSTANT_POISON_II, 5);
}
if (level >= 30 && level < 36)
{
StoreItem(CONSUM_ID_DEADLY_POISON, 5);
StoreItem(CONSUM_ID_INSTANT_POISON_II, 5);
}
if (level >= 36 && level < 44)
{
StoreItem(CONSUM_ID_DEADLY_POISON_II, 5);
StoreItem(CONSUM_ID_INSTANT_POISON_III, 5);
}
if (level >= 44 && level < 52)
{
StoreItem(CONSUM_ID_DEADLY_POISON_III, 5);
StoreItem(CONSUM_ID_INSTANT_POISON_IV, 5);
}
if (level >= 52 && level < 60)
{
StoreItem(CONSUM_ID_DEADLY_POISON_IV, 5);
StoreItem(CONSUM_ID_INSTANT_POISON_V, 5);
}
if (level >= 60 && level < 62)
{
StoreItem(CONSUM_ID_DEADLY_POISON_V, 5);
StoreItem(CONSUM_ID_INSTANT_POISON_VI, 5);
}
if (level >= 62 && level < 68)
{
StoreItem(CONSUM_ID_DEADLY_POISON_VI, 5);
StoreItem(CONSUM_ID_INSTANT_POISON_VI, 5);
}
if (level >= 68)
{
StoreItem(CONSUM_ID_DEADLY_POISON_VII, 5);
StoreItem(CONSUM_ID_INSTANT_POISON_VII, 5);
}
break;
}
default:
break;
}
for (std::pair<uint32, uint32> item : items)
{
int count = (int)item.second - (int)bot->GetItemCount(item.first);
if (count > 0)
StoreItem(item.first, count);
}
}
void PlayerbotFactory::InitPetTalents()
@@ -898,7 +829,6 @@ void PlayerbotFactory::InitPet()
std::vector<uint32> ids;
CreatureTemplateContainer const* creatures = sObjectMgr->GetCreatureTemplates();
for (CreatureTemplateContainer::const_iterator itr = creatures->begin(); itr != creatures->end(); ++itr)
{
if (!itr->second.IsTameable(bot->CanTameExoticPets()))
@@ -914,12 +844,6 @@ void PlayerbotFactory::InitPet()
if (onlyWolf && itr->second.family != CREATURE_FAMILY_WOLF)
continue;
// Exclude configured pet families
if (std::find(sPlayerbotAIConfig->excludedHunterPetFamilies.begin(),
sPlayerbotAIConfig->excludedHunterPetFamilies.end(),
itr->second.family) != sPlayerbotAIConfig->excludedHunterPetFamilies.end())
continue;
ids.push_back(itr->first);
}
@@ -1100,21 +1024,9 @@ void PlayerbotFactory::InitTalentsTree(bool increment /*false*/, bool use_templa
/// @todo: match current talent with template
specTab = AiFactory::GetPlayerSpecTab(bot);
/// @todo: fix cat druid hardcode
if (bot->getClass() == CLASS_DRUID && specTab == DRUID_TAB_FERAL && bot->GetLevel() >= 20)
{
bool isCat = !bot->HasAura(16931);
if (!isCat && bot->GetLevel() == 20)
{
uint32 bearP = sPlayerbotAIConfig->randomClassSpecProb[cls][1];
uint32 catP = sPlayerbotAIConfig->randomClassSpecProb[cls][3];
if (urand(1, bearP + catP) <= catP)
isCat = true;
}
if (isCat)
{
specTab = 3;
}
}
if (bot->getClass() == CLASS_DRUID && specTab == DRUID_TAB_FERAL && bot->GetLevel() >= 20 &&
!bot->HasAura(16931))
specTab = 3;
}
else
{
@@ -1687,51 +1599,9 @@ void Shuffle(std::vector<uint32>& items)
void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance)
{
if (incremental && !sPlayerbotAIConfig->incrementalGearInit)
return;
if (level < 5) {
// original items
if (CharStartOutfitEntry const* oEntry = GetCharStartOutfitEntry(bot->getRace(), bot->getClass(), bot->getGender()))
{
for (int j = 0; j < MAX_OUTFIT_ITEMS; ++j)
{
if (oEntry->ItemId[j] <= 0)
continue;
uint32 itemId = oEntry->ItemId[j];
// skip hearthstone
if (itemId == 6948)
continue;
// just skip, reported in ObjectMgr::LoadItemTemplates
ItemTemplate const* iProto = sObjectMgr->GetItemTemplate(itemId);
if (!iProto)
continue;
// BuyCount by default
uint32 count = iProto->BuyCount;
// special amount for food/drink
if (iProto->Class == ITEM_CLASS_CONSUMABLE && iProto->SubClass == ITEM_SUBCLASS_FOOD)
{
continue;
}
if (bot->HasItemCount(itemId, count)) {
continue;
}
bot->StoreNewItemInBestSlots(itemId, count);
}
}
return;
}
std::unordered_map<uint8, std::vector<uint32>> items;
// int tab = AiFactory::GetPlayerSpecTab(bot);
uint32 blevel = bot->GetLevel();
int32 delta = std::min(blevel, 10u);
@@ -3297,97 +3167,102 @@ void PlayerbotFactory::InitFood()
void PlayerbotFactory::InitReagents()
{
int specTab = AiFactory::GetPlayerSpecTab(bot);
int level = bot->GetLevel();
std::vector<std::pair<uint32, uint32>> items;
switch (bot->getClass())
{
case CLASS_DEATH_KNIGHT:
if (level >= 56)
items.push_back({37201, 40}); // Corpse Dust
case CLASS_ROGUE:
{
std::vector<int> instant_poison_ids = {43231, 43230, 21927, 8928, 8927, 8926, 6950, 6949, 6947};
std::vector<int> deadly_poison_ids = {43233, 43232, 22054, 22053, 20844, 8985, 8984, 2893, 2892};
for (int& itemId : deadly_poison_ids)
{
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
if (proto->RequiredLevel > bot->GetLevel())
continue;
items.push_back({itemId, 20}); // deadly poison
break;
}
for (int& itemId : instant_poison_ids)
{
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
if (proto->RequiredLevel > bot->GetLevel())
continue;
items.push_back({itemId, 20}); // instant poison
break;
}
}
break;
case CLASS_SHAMAN:
// items.push_back({46978, 1}); // Totem
items.push_back({5175, 1}); // Earth Totem
items.push_back({5176, 1}); // Flame Totem
items.push_back({5177, 1}); // Water Totem
items.push_back({5178, 1}); // Air Totem
if (bot->GetLevel() >= 30)
items.push_back({17030, 40}); // Ankh
break;
case CLASS_DRUID:
if (level >= 20 && level < 30)
items.push_back({17034, 20}); // Maple Seed
else if (level >= 30 && level < 40)
items.push_back({17035, 20}); // Stranglethorn Seed
else if (level >= 40 && level < 50)
items.push_back({17036, 20}); // Ashwood Seed
else if (level >= 50 && level < 60)
case CLASS_WARLOCK:
items.push_back({6265, 20}); // shard
break;
case CLASS_PRIEST:
if (level >= 48 && level < 60)
{
items.push_back({17037, 20}); // Hornbeam Seed
items.push_back({17021, 20}); // Wild Berries
items.push_back({17028, 40}); // Wild Berries
}
else if (level >= 60 && level < 69)
else if (level >= 60 && level < 80)
{
items.push_back({17038, 20}); // Ironwood Seed
items.push_back({17026, 20}); // Wild Thornroot
}
else if (level == 69)
{
items.push_back({22147, 20}); // Flintweed Seed
items.push_back({17026, 20}); // Wild Thornroot
}
else if (level >= 70 && level < 79)
{
items.push_back({22147, 20}); // Flintweed Seed
items.push_back({22148, 20}); // Wild Quillvine
}
else if (level == 79)
{
items.push_back({44614, 20}); // Starleaf Seed
items.push_back({22148, 20}); // Wild Quillvine
items.push_back({17029, 40}); // Wild Berries
}
else if (level >= 80)
{
items.push_back({44614, 20}); // Starleaf Seed
items.push_back({44605, 20}); // Wild Spineleaf
items.push_back({44615, 40}); // Wild Berries
}
break;
case CLASS_MAGE:
if (level >= 20)
items.push_back({17031, 20}); // Rune of Teleportation
if (level >= 40)
items.push_back({17032, 20}); // Rune of Portals
if (level >= 56)
items.push_back({17020, 20}); // Arcane Powder
items.push_back({17020, 40}); // Arcane Powder
items.push_back({17031, 40}); // portal
items.push_back({17032, 40}); // portal
break;
case CLASS_DRUID:
if (level >= 20 && level < 30)
{
items.push_back({17034, 40});
}
if (level >= 30 && level < 40)
{
items.push_back({17035, 40});
}
if (level >= 40 && level < 50)
{
items.push_back({17036, 40});
}
if (level >= 50 && level < 60)
{
items.push_back({17037, 40});
items.push_back({17021, 40});
}
if (level >= 60 && level < 70)
{
items.push_back({17038, 40});
items.push_back({17026, 40});
}
if (level >= 70 && level < 80)
{
items.push_back({22147, 40});
items.push_back({22148, 40});
}
if (level >= 80)
{
items.push_back({44614, 40});
items.push_back({44605, 40});
}
break;
case CLASS_PALADIN:
if (level >= 52)
items.push_back({21177, 80}); // Symbol of Kings
items.push_back({21177, 100});
break;
case CLASS_PRIEST:
if (level >= 48 && level < 56)
items.push_back({17028, 40}); // Holy Candle
else if (level >= 56 && level < 60)
{
items.push_back({17028, 20}); // Holy Candle
items.push_back({17029, 20}); // Sacred Candle
}
else if (level >= 60 && level < 77)
items.push_back({17029, 40}); // Sacred Candle
else if (level >= 77 && level < 80)
{
items.push_back({17029, 20}); // Sacred Candle
items.push_back({44615, 20}); // Devout Candle
}
else if (level >= 80)
items.push_back({44615, 40}); // Devout Candle
break;
case CLASS_SHAMAN:
if (level >= 4)
items.push_back({5175, 1}); // Earth Totem
if (level >= 10)
items.push_back({5176, 1}); // Flame Totem
if (level >= 20)
items.push_back({5177, 1}); // Water Totem
if (level >= 30)
{
items.push_back({5178, 1}); // Air Totem
items.push_back({17030, 20}); // Ankh
}
break;
case CLASS_WARLOCK:
items.push_back({6265, 5}); // Soul Shard
case CLASS_DEATH_KNIGHT:
items.push_back({37201, 40});
break;
default:
break;
@@ -3400,75 +3275,9 @@ void PlayerbotFactory::InitReagents()
}
}
void PlayerbotFactory::CleanupConsumables() // remove old consumables as part of randombot level-up maintenance
{
std::vector<Item*> itemsToDelete;
std::vector<Item*> items;
for (uint32 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i)
if (Item* item = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
items.push_back(item);
for (uint32 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
if (Bag* bag = (Bag*)bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
for (uint32 j = 0; j < bag->GetBagSize(); ++j)
if (Item* item = bag->GetItemByPos(j))
items.push_back(item);
for (Item* item : items)
{
ItemTemplate const* proto = item->GetTemplate();
if (!proto) continue;
// Remove ammo
if (proto->Class == ITEM_CLASS_PROJECTILE)
itemsToDelete.push_back(item);
// Remove food/drink
if (proto->Class == ITEM_CLASS_CONSUMABLE && proto->SubClass == ITEM_SUBCLASS_FOOD)
itemsToDelete.push_back(item);
// Remove potions
if (proto->Class == ITEM_CLASS_CONSUMABLE && proto->SubClass == ITEM_SUBCLASS_POTION)
itemsToDelete.push_back(item);
// Remove reagents
if (proto->Class == ITEM_CLASS_REAGENT || (proto->Class == ITEM_CLASS_MISC && proto->SubClass == ITEM_SUBCLASS_REAGENT))
itemsToDelete.push_back(item);
}
std::set<uint32> idsToDelete = {
BRILLIANT_MANA_OIL, SUPERIOR_MANA_OIL, LESSER_MANA_OIL, MINOR_MANA_OIL,
BRILLIANT_WIZARD_OIL, SUPERIOR_WIZARD_OIL, WIZARD_OIL, LESSER_WIZARD_OIL, MINOR_WIZARD_OIL,
ADAMANTITE_SHARPENING_STONE, FEL_SHARPENING_STONE, DENSE_SHARPENING_STONE, SOLID_SHARPENING_STONE,
HEAVY_SHARPENING_STONE, COARSE_SHARPENING_STONE, ROUGH_SHARPENING_STONE,
ADAMANTITE_WEIGHTSTONE, FEL_WEIGHTSTONE, DENSE_WEIGHTSTONE, SOLID_WEIGHTSTONE,
HEAVY_WEIGHTSTONE, COARSE_WEIGHTSTONE, ROUGH_WEIGHTSTONE,
INSTANT_POISON_IX, INSTANT_POISON_VIII, INSTANT_POISON_VII, INSTANT_POISON_VI, INSTANT_POISON_V,
INSTANT_POISON_IV, INSTANT_POISON_III, INSTANT_POISON_II, INSTANT_POISON,
DEADLY_POISON_IX, DEADLY_POISON_VIII, DEADLY_POISON_VII, DEADLY_POISON_VI, DEADLY_POISON_V,
DEADLY_POISON_IV, DEADLY_POISON_III, DEADLY_POISON_II, DEADLY_POISON
};
for (Item* item : items)
{
ItemTemplate const* proto = item->GetTemplate();
if (!proto) continue;
if (idsToDelete.find(proto->ItemId) != idsToDelete.end())
itemsToDelete.push_back(item);
}
for (Item* item : itemsToDelete)
bot->DestroyItem(item->GetBagSlot(), item->GetSlot(), true);
}
void PlayerbotFactory::InitGlyphs(bool increment)
{
bot->InitGlyphsForLevel();
if (!increment && botAI &&
botAI->GetAiObjectContext()->GetValue<bool>("custom_glyphs")->Get())
return; // // Added for custom Glyphs - custom glyphs flag test
if (!increment)
{
@@ -3527,157 +3336,8 @@ void PlayerbotFactory::InitGlyphs(bool increment)
uint8 cls = bot->getClass();
uint8 tab = AiFactory::GetPlayerSpecTab(bot);
/// @todo: fix cat druid hardcode
// Warrior PVP exceptions
if (bot->getClass() == CLASS_WARRIOR)
{
// Arms PvP (spec index 3): If the bot has the Second Wind talent
if (bot->HasAura(29838))
tab = 3;
// Fury PvP (spec index 4): If the bot has the Blood Craze talent
else if (bot->HasAura(16492))
tab = 4;
// Protection PvP (spec index 5): If the bot has the Gag Order talent
else if (bot->HasAura(12958))
tab = 5;
}
// Paladin PvP exceptions
if (bot->getClass() == CLASS_PALADIN)
{
// Holy PvP (spec index 3): If the bot has the Sacred Cleansing talent
if (bot->HasAura(53553))
tab = 3;
// Protection PvP (spec index 4): If the bot has the Reckoning talent
else if (bot->HasAura(20179))
tab = 4;
// Retribution PvP (spec index 5): If the bot has the Divine Purpose talent
else if (bot->HasAura(31872))
tab = 5;
}
// Hunter PvP exceptions
if (bot->getClass() == CLASS_HUNTER)
{
// Beast Mastery PvP (spec index 3): If the bot has the Thick Hide talent
if (bot->HasAura(19612))
tab = 3;
// Marksmanship PvP (spec index 4): If the bot has the Concussive Barrage talent
else if (bot->HasAura(35102))
tab = 4;
// Survival PvP (spec index 5): If the bot has the Entrapment talent and does NOT have the Concussive Barrage talent
else if (bot->HasAura(19388) && !bot->HasAura(35102))
tab = 5;
}
// Rogue PvP exceptions
if (bot->getClass() == CLASS_ROGUE)
{
// Assassination PvP (spec index 3): If the bot has the Deadly Brew talent
if (bot->HasAura(51626))
tab = 3;
// Combat PvP (spec index 4): If the bot has the Throwing Specialization talent
else if (bot->HasAura(51679))
tab = 4;
// Subtlety PvP (spec index 5): If the bot has the Waylay talent
else if (bot->HasAura(51696))
tab = 5;
}
// Priest PvP exceptions
if (bot->getClass() == CLASS_PRIEST)
{
// Discipline PvP (spec index 3): If the bot has the Improved Mana Burn talent
if (bot->HasAura(14772))
tab = 3;
// Holy PvP (spec index 4): If the bot has the Body and Soul talent
else if (bot->HasAura(64129))
tab = 4;
// Shadow PvP (spec index 5): If the bot has the Improved Vampiric Embrace talent
else if (bot->HasAura(27840))
tab = 5;
}
// Death Knight PvE/PvP exceptions
if (bot->getClass() == CLASS_DEATH_KNIGHT)
{
// Double Aura Blood PvE (spec index 3): If the bot has both the Abomination's Might and Improved Icy Talons
// talents
if (bot->HasAura(53138) && bot->HasAura(55610))
tab = 3;
// Blood PvP (spec index 4): If the bot has the Sudden Doom talent
else if (bot->HasAura(49529))
tab = 4;
// Frost PvP (spec index 5): If the bot has the Acclimation talent
else if (bot->HasAura(50152))
tab = 5;
// Unholy PvP (spec index 6): If the bot has the Magic Suppression talent
else if (bot->HasAura(49611))
tab = 6;
}
// Shaman PvP exceptions
if (bot->getClass() == CLASS_SHAMAN)
{
// Elemental PvP (spec index 3): If the bot has the Astral Shift talent
if (bot->HasAura(51479))
tab = 3;
// Enhancement PvP (spec index 4): If the bot has the Earthen Power talent
else if (bot->HasAura(51524))
tab = 4;
// Restoration PvP (spec index 5): If the bot has the Focused Mind talent
else if (bot->HasAura(30866))
tab = 5;
}
// Mage PvE/PvP exceptions
if (bot->getClass() == CLASS_MAGE)
{
// Frostfire PvE (spec index 3): If the bot has both the Burnout talent and the Ice Shards talent
if (bot->HasAura(44472) && bot->HasAura(15047))
tab = 3;
// Arcane PvP (spec index 4): If the bot has the Improved Blink talent
else if (bot->HasAura(31570))
tab = 4;
// Fire PvP (spec index 5): If the bot has the Fiery Payback talent
else if (bot->HasAura(64357))
tab = 5;
// Frost PvP (spec index 6): If the bot has the Shattered Barrier talent
else if (bot->HasAura(54787))
tab = 6;
}
// Warlock PvP exceptions
if (bot->getClass() == CLASS_WARLOCK)
{
// Affliction PvP (spec index 3): If the bot has the Improved Howl of Terror talent
if (bot->HasAura(30057))
tab = 3;
// Demonology PvP (spec index 4): If the bot has both the Nemesis talent and the Intensity talent
else if (bot->HasAura(63123) && bot->HasAura(18136))
tab = 4;
// Destruction PvP (spec index 5): If the bot has the Nether Protection talent
else if (bot->HasAura(30302))
tab = 5;
}
// Druid PvE/PvP exceptions
if (bot->getClass() == CLASS_DRUID)
{
// Cat PvE (spec index 3): If the bot is Feral spec, level 20 or higher, and does NOT have the Thick Hide talent
if (tab == DRUID_TAB_FERAL && bot->GetLevel() >= 20 && !bot->HasAura(16931))
tab = 3;
// Balance PvP (spec index 4): If the bot has the Owlkin Frenzy talent
else if (bot->HasAura(48393))
tab = 4;
// Feral PvP (spec index 5): If the bot has the Primal Tenacity talent
else if (bot->HasAura(33957))
tab = 5;
// Resto PvP (spec index 6): If the bot has the Improved Barkskin talent
else if (bot->HasAura(63411))
tab = 6;
}
if (bot->getClass() == CLASS_DRUID && tab == DRUID_TAB_FERAL && bot->GetLevel() >= 20 && !bot->HasAura(16931))
tab = 3;
std::list<uint32> glyphs;
ItemTemplateContainer const* itemTemplates = sObjectMgr->GetItemTemplateStore();
for (ItemTemplateContainer::const_iterator i = itemTemplates->begin(); i != itemTemplates->end(); ++i)

View File

@@ -45,6 +45,63 @@ enum spec : uint8
ROLE_CDPS = 3
};*/
enum PriorizedConsumables
{
CONSUM_ID_ROUGH_WEIGHTSTONE = 3239,
CONSUM_ID_COARSE_WEIGHTSTONE = 3239,
CONSUM_ID_HEAVY_WEIGHTSTONE = 3241,
CONSUM_ID_SOLID_WEIGHTSTONE = 7965,
CONSUM_ID_DENSE_WEIGHTSTONE = 12643,
CONSUM_ID_FEL_WEIGHTSTONE = 28420,
CONSUM_ID_ADAMANTITE_WEIGHTSTONE = 28421,
CONSUM_ID_ROUGH_SHARPENING_STONE = 2862,
CONSUM_ID_COARSE_SHARPENING_STONE = 2863,
CONSUM_ID_HEAVY_SHARPENING_STONE = 2871,
CONSUM_ID_SOL_SHARPENING_STONE = 7964,
CONSUM_ID_DENSE_SHARPENING_STONE = 12404,
CONSUM_ID_ELEMENTAL_SHARPENING_STONE = 18262,
CONSUM_ID_CONSECRATED_SHARPENING_STONE = 23122,
CONSUM_ID_FEL_SHARPENING_STONE = 23528,
CONSUM_ID_ADAMANTITE_SHARPENING_STONE = 23529,
CONSUM_ID_LINEN_BANDAGE = 1251,
CONSUM_ID_HEAVY_LINEN_BANDAGE = 2581,
CONSUM_ID_WOOL_BANDAGE = 3530,
CONSUM_ID_HEAVY_WOOL_BANDAGE = 3531,
CONSUM_ID_SILK_BANDAGE = 6450,
CONSUM_ID_HEAVY_SILK_BANDAGE = 6451,
CONSUM_ID_MAGEWEAVE_BANDAGE = 8544,
CONSUM_ID_HEAVY_MAGEWEAVE_BANDAGE = 8545,
CONSUM_ID_RUNECLOTH_BANDAGE = 14529,
CONSUM_ID_HEAVY_RUNECLOTH_BANDAGE = 14530,
CONSUM_ID_NETHERWEAVE_BANDAGE = 21990,
CONSUM_ID_HEAVY_NETHERWEAVE_BANDAGE = 21991,
CONSUM_ID_BRILLIANT_MANA_OIL = 20748,
CONSUM_ID_MINOR_MANA_OIL = 20745,
CONSUM_ID_SUPERIOR_MANA_OIL = 22521,
CONSUM_ID_LESSER_MANA_OIL = 20747,
CONSUM_ID_BRILLIANT_WIZARD_OIL = 20749,
CONSUM_ID_MINOR_WIZARD_OIL = 20744,
CONSUM_ID_SUPERIOR_WIZARD_OIL = 22522,
CONSUM_ID_WIZARD_OIL = 20750,
CONSUM_ID_LESSER_WIZARD_OIL = 20746,
CONSUM_ID_INSTANT_POISON = 6947,
CONSUM_ID_INSTANT_POISON_II = 6949,
CONSUM_ID_INSTANT_POISON_III = 6950,
CONSUM_ID_INSTANT_POISON_IV = 8926,
CONSUM_ID_INSTANT_POISON_V = 8927,
CONSUM_ID_INSTANT_POISON_VI = 8928,
CONSUM_ID_INSTANT_POISON_VII = 21927,
CONSUM_ID_DEADLY_POISON = 2892,
CONSUM_ID_DEADLY_POISON_II = 2893,
CONSUM_ID_DEADLY_POISON_III = 8984,
CONSUM_ID_DEADLY_POISON_IV = 8985,
CONSUM_ID_DEADLY_POISON_V = 20844,
CONSUM_ID_DEADLY_POISON_VI = 22053,
CONSUM_ID_DEADLY_POISON_VII = 22054
};
#define MAX_CONSUM_ID 28
class PlayerbotFactory
{
public:
@@ -71,10 +128,8 @@ public:
void InitAmmo();
static uint32 CalcMixedGearScore(uint32 gs, uint32 quality);
void InitPetTalents();
void CleanupConsumables();
void InitReagents();
void InitConsumables();
void InitPotions();
void InitGlyphs(bool increment = false);
void InitFood();
void InitMounts();
@@ -85,6 +140,7 @@ public:
void InitKeyring();
void InitReputation();
void InitAttunementQuests();
void InitPotions();
private:
void Prepare();
@@ -121,6 +177,7 @@ private:
void InitGuild();
void InitArenaTeam();
void InitImmersive();
void AddConsumables();
static void AddPrevQuests(uint32 questId, std::list<uint32>& questIds);
void LoadEnchantContainer();
void ApplyEnchantTemplate();

View File

@@ -29,12 +29,12 @@ void StatsCollector::CollectItemStats(ItemTemplate const* proto)
{
if (proto->IsRangedWeapon())
{
float val = (proto->Damage[0].DamageMin + proto->Damage[0].DamageMax) * 1000 / 2 / proto->Delay;
uint32 val = (proto->Damage[0].DamageMin + proto->Damage[0].DamageMax) * 1000 / 2 / proto->Delay;
stats[STATS_TYPE_RANGED_DPS] += val;
}
else if (proto->IsWeapon())
{
float val = (proto->Damage[0].DamageMin + proto->Damage[0].DamageMax) * 1000 / 2 / proto->Delay;
uint32 val = (proto->Damage[0].DamageMin + proto->Damage[0].DamageMax) * 1000 / 2 / proto->Delay;
stats[STATS_TYPE_MELEE_DPS] += val;
}
stats[STATS_TYPE_ARMOR] += proto->Armor;
@@ -436,10 +436,10 @@ void StatsCollector::CollectByItemStatType(uint32 itemStatType, int32 val)
switch (itemStatType)
{
case ITEM_MOD_MANA:
stats[STATS_TYPE_MANA_REGENERATION] += (float)val / 10;
stats[STATS_TYPE_MANA_REGENERATION] += val / 10;
break;
case ITEM_MOD_HEALTH:
stats[STATS_TYPE_STAMINA] += (float)val / 15;
stats[STATS_TYPE_STAMINA] += val / 15;
break;
case ITEM_MOD_AGILITY:
stats[STATS_TYPE_AGILITY] += val;
@@ -747,11 +747,11 @@ void StatsCollector::HandleApplyAura(const SpellEffectInfo& effectInfo, float mu
}
}
float StatsCollector::AverageValue(const SpellEffectInfo& effectInfo)
int32 StatsCollector::AverageValue(const SpellEffectInfo& effectInfo)
{
// float basePointsPerLevel = effectInfo.RealPointsPerLevel; //not used, line marked for removal.
float basePoints = effectInfo.BasePoints;
int32 randomPoints = effectInfo.DieSides;
int32 basePoints = effectInfo.BasePoints;
int32 randomPoints = int32(effectInfo.DieSides);
switch (randomPoints)
{
@@ -761,7 +761,7 @@ float StatsCollector::AverageValue(const SpellEffectInfo& effectInfo)
basePoints += 1;
break;
default:
float randvalue = (1 + randomPoints) / 2.0f;
int32 randvalue = (1 + randomPoints) / 2;
basePoints += randvalue;
break;
}

View File

@@ -71,7 +71,7 @@ public:
bool CheckSpellValidation(uint32 spellFamilyName, flag96 spelFalimyFlags, bool strict = true);
public:
float stats[STATS_TYPE_MAX];
int32 stats[STATS_TYPE_MAX];
private:
void CollectByItemStatType(uint32 itemStatType, int32 val);
@@ -80,7 +80,7 @@ private:
void HandleApplyAura(const SpellEffectInfo& effectInfo, float multiplier, bool canNextTrigger,
uint32 triggerCooldown);
float AverageValue(const SpellEffectInfo& effectInfo);
int32 AverageValue(const SpellEffectInfo& effectInfo);
private:
CollectorType type_;

View File

@@ -33,7 +33,6 @@ StatsWeightCalculator::StatsWeightCalculator(Player* player) : player_(player)
else
type_ = CollectorType::RANGED;
cls = player->getClass();
lvl = player->GetLevel();
tab = AiFactory::GetPlayerSpecTab(player);
collector_ = std::make_unique<StatsCollector>(type_, cls);
@@ -71,7 +70,7 @@ float StatsWeightCalculator::CalculateItem(uint32 itemId, int32 randomPropertyId
Reset();
collector_->CollectItemStats(proto);
if (randomPropertyIds != 0)
CalculateRandomProperty(randomPropertyIds, itemId);
@@ -182,7 +181,6 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
stats_weights_[STATS_TYPE_ARMOR] += 0.001f;
stats_weights_[STATS_TYPE_BONUS] += 1.0f;
stats_weights_[STATS_TYPE_MELEE_DPS] += 0.01f;
stats_weights_[STATS_TYPE_RANGED_DPS] += 0.01f;
if (cls == CLASS_HUNTER && (tab == HUNTER_TAB_BEASTMASTER || tab == HUNTER_TAB_SURVIVAL))
{
@@ -531,13 +529,13 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
// enhancement, rogue, ice dk, unholy dk, shield tank, fury warrior without titan's grip but with duel wield
if (((cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && player_->CanDualWield()) ||
(cls == CLASS_ROGUE) || (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_FROST) ||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !player_->CanTitanGrip() &&
player_->CanDualWield()) ||
(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)))
{
weight_ *= 0.1;
}
}
// spec with double hand
// fury without duel wield, arms, bear, retribution, blood dk
@@ -553,11 +551,15 @@ 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 ||
if (cls == CLASS_MAGE ||
cls == CLASS_PRIEST ||
cls == CLASS_WARLOCK ||
cls == CLASS_DRUID ||
(cls == CLASS_SHAMAN && !player_->CanDualWield()))
{
weight_ *= 0.65;
}
}
// fury with titan's grip
if ((!isDoubleHand || proto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM ||
@@ -566,16 +568,16 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
{
weight_ *= 0.1;
}
if (cls == CLASS_HUNTER && proto->SubClass == ITEM_SUBCLASS_WEAPON_THROWN)
{
weight_ *= 0.1;
}
if (lvl >= 10 && cls == CLASS_ROGUE && (tab == ROGUE_TAB_ASSASSINATION || tab == ROGUE_TAB_SUBTLETY) &&
proto->SubClass == ITEM_SUBCLASS_WEAPON_DAGGER)
if (cls == CLASS_ROGUE && (tab == ROGUE_TAB_ASSASSINATION || tab == ROGUE_TAB_SUBTLETY) &&
proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER)
{
weight_ *= 1.5;
weight_ *= 0.5;
}
if (cls == CLASS_ROGUE && player_->HasAura(13964) &&
@@ -658,7 +660,7 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player)
else
validPoints = 0;
}
collector_->stats[STATS_TYPE_HIT] = std::min(collector_->stats[STATS_TYPE_HIT], validPoints);
collector_->stats[STATS_TYPE_HIT] = std::min(collector_->stats[STATS_TYPE_HIT], (int)validPoints);
}
{
@@ -675,7 +677,8 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player)
else
validPoints = 0;
collector_->stats[STATS_TYPE_EXPERTISE] = std::min(collector_->stats[STATS_TYPE_EXPERTISE], validPoints);
collector_->stats[STATS_TYPE_EXPERTISE] =
std::min(collector_->stats[STATS_TYPE_EXPERTISE], (int)validPoints);
}
}
@@ -692,7 +695,7 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player)
else
validPoints = 0;
collector_->stats[STATS_TYPE_DEFENSE] = std::min(collector_->stats[STATS_TYPE_DEFENSE], validPoints);
collector_->stats[STATS_TYPE_DEFENSE] = std::min(collector_->stats[STATS_TYPE_DEFENSE], (int)validPoints);
}
}
@@ -711,7 +714,7 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player)
validPoints = 0;
collector_->stats[STATS_TYPE_ARMOR_PENETRATION] =
std::min(collector_->stats[STATS_TYPE_ARMOR_PENETRATION], validPoints);
std::min(collector_->stats[STATS_TYPE_ARMOR_PENETRATION], (int)validPoints);
}
}
}

View File

@@ -57,7 +57,6 @@ private:
CollectorType hitOverflowType_;
std::unique_ptr<StatsCollector> collector_;
uint8 cls;
uint8 lvl;
int tab;
bool enable_overflow_penalty_;
bool enable_item_set_bonus_;

View File

@@ -8,89 +8,39 @@
#include "ActionContext.h"
#include "ChatActionContext.h"
#include "ChatTriggerContext.h"
#include "DKAiObjectContext.h"
#include "DruidAiObjectContext.h"
#include "HunterAiObjectContext.h"
#include "MageAiObjectContext.h"
#include "PaladinAiObjectContext.h"
#include "Playerbots.h"
#include "PriestAiObjectContext.h"
#include "RaidUlduarActionContext.h"
#include "RaidUlduarTriggerContext.h"
#include "RogueAiObjectContext.h"
#include "ShamanAiObjectContext.h"
#include "RaidUlduarActionContext.h"
#include "SharedValueContext.h"
#include "StrategyContext.h"
#include "TriggerContext.h"
#include "ValueContext.h"
#include "WarlockAiObjectContext.h"
#include "WarriorAiObjectContext.h"
#include "WorldPacketActionContext.h"
#include "WorldPacketTriggerContext.h"
#include "dungeons/DungeonStrategyContext.h"
#include "dungeons/wotlk/WotlkDungeonActionContext.h"
#include "dungeons/wotlk/WotlkDungeonTriggerContext.h"
#include "raids/RaidStrategyContext.h"
#include "raids/aq20/RaidAq20ActionContext.h"
#include "raids/aq20/RaidAq20TriggerContext.h"
#include "raids/blackwinglair/RaidBwlActionContext.h"
#include "raids/blackwinglair/RaidBwlTriggerContext.h"
#include "raids/eyeofeternity/RaidEoEActionContext.h"
#include "raids/eyeofeternity/RaidEoETriggerContext.h"
#include "raids/icecrown/RaidIccActionContext.h"
#include "raids/icecrown/RaidIccTriggerContext.h"
#include "raids/moltencore/RaidMcActionContext.h"
#include "raids/moltencore/RaidMcTriggerContext.h"
#include "raids/naxxramas/RaidNaxxActionContext.h"
#include "raids/naxxramas/RaidNaxxTriggerContext.h"
#include "raids/icecrown/RaidIccActionContext.h"
#include "raids/icecrown/RaidIccTriggerContext.h"
#include "raids/obsidiansanctum/RaidOsActionContext.h"
#include "raids/obsidiansanctum/RaidOsTriggerContext.h"
#include "raids/eyeofeternity/RaidEoEActionContext.h"
#include "raids/vaultofarchavon/RaidVoATriggerContext.h"
#include "raids/onyxia/RaidOnyxiaActionContext.h"
#include "raids/onyxia/RaidOnyxiaTriggerContext.h"
#include "raids/vaultofarchavon/RaidVoAActionContext.h"
#include "raids/vaultofarchavon/RaidVoATriggerContext.h"
#include "raids/eyeofeternity/RaidEoETriggerContext.h"
#include "raids/moltencore/RaidMcActionContext.h"
#include "raids/moltencore/RaidMcTriggerContext.h"
#include "raids/aq20/RaidAq20ActionContext.h"
#include "raids/aq20/RaidAq20TriggerContext.h"
#include "dungeons/DungeonStrategyContext.h"
#include "dungeons/wotlk/WotlkDungeonActionContext.h"
#include "dungeons/wotlk/WotlkDungeonTriggerContext.h"
SharedNamedObjectContextList<Strategy> AiObjectContext::sharedStrategyContexts;
SharedNamedObjectContextList<Action> AiObjectContext::sharedActionContexts;
SharedNamedObjectContextList<Trigger> AiObjectContext::sharedTriggerContexts;
SharedNamedObjectContextList<UntypedValue> AiObjectContext::sharedValueContexts;
AiObjectContext::AiObjectContext(PlayerbotAI* botAI, SharedNamedObjectContextList<Strategy>& sharedStrategyContext,
SharedNamedObjectContextList<Action>& sharedActionContext,
SharedNamedObjectContextList<Trigger>& sharedTriggerContext,
SharedNamedObjectContextList<UntypedValue>& sharedValueContext)
: PlayerbotAIAware(botAI),
strategyContexts(sharedStrategyContext),
actionContexts(sharedActionContext),
triggerContexts(sharedTriggerContext),
valueContexts(sharedValueContext)
{
}
void AiObjectContext::BuildAllSharedContexts()
{
AiObjectContext::BuildSharedContexts();
PriestAiObjectContext::BuildSharedContexts();
MageAiObjectContext::BuildSharedContexts();
WarlockAiObjectContext::BuildSharedContexts();
WarriorAiObjectContext::BuildSharedContexts();
ShamanAiObjectContext::BuildSharedContexts();
PaladinAiObjectContext::BuildSharedContexts();
DruidAiObjectContext::BuildSharedContexts();
HunterAiObjectContext::BuildSharedContexts();
RogueAiObjectContext::BuildSharedContexts();
DKAiObjectContext::BuildSharedContexts();
}
void AiObjectContext::BuildSharedContexts()
{
BuildSharedStrategyContexts(sharedStrategyContexts);
BuildSharedActionContexts(sharedActionContexts);
BuildSharedTriggerContexts(sharedTriggerContexts);
BuildSharedValueContexts(sharedValueContexts);
}
void AiObjectContext::BuildSharedStrategyContexts(SharedNamedObjectContextList<Strategy>& strategyContexts)
AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
{
strategyContexts.Add(new StrategyContext());
strategyContexts.Add(new MovementStrategyContext());
@@ -98,10 +48,7 @@ void AiObjectContext::BuildSharedStrategyContexts(SharedNamedObjectContextList<S
strategyContexts.Add(new QuestStrategyContext());
strategyContexts.Add(new RaidStrategyContext());
strategyContexts.Add(new DungeonStrategyContext());
}
void AiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList<Action>& actionContexts)
{
actionContexts.Add(new ActionContext());
actionContexts.Add(new ChatActionContext());
actionContexts.Add(new WorldPacketActionContext());
@@ -130,10 +77,7 @@ void AiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList<Act
actionContexts.Add(new WotlkDungeonFoSActionContext());
actionContexts.Add(new WotlkDungeonPoSActionContext());
actionContexts.Add(new WotlkDungeonToCActionContext());
}
void AiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextList<Trigger>& triggerContexts)
{
triggerContexts.Add(new TriggerContext());
triggerContexts.Add(new ChatTriggerContext());
triggerContexts.Add(new WorldPacketTriggerContext());
@@ -162,11 +106,26 @@ void AiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextList<Tr
triggerContexts.Add(new WotlkDungeonFoSTriggerContext());
triggerContexts.Add(new WotlkDungeonPoSTriggerContext());
triggerContexts.Add(new WotlkDungeonToCTriggerContext());
valueContexts.Add(new ValueContext());
valueContexts.Add(sSharedValueContext);
}
void AiObjectContext::BuildSharedValueContexts(SharedNamedObjectContextList<UntypedValue>& valueContexts)
void AiObjectContext::Update()
{
valueContexts.Add(new ValueContext());
strategyContexts.Update();
triggerContexts.Update();
actionContexts.Update();
valueContexts.Update();
}
void AiObjectContext::Reset()
{
strategyContexts.Reset();
triggerContexts.Reset();
actionContexts.Reset();
valueContexts.Reset();
}
std::vector<std::string> AiObjectContext::Save()
@@ -259,3 +218,5 @@ std::string const AiObjectContext::FormatValues()
return out.str();
}
void AiObjectContext::AddShared(NamedObjectContext<UntypedValue>* sharedValues) { valueContexts.Add(sharedValues); }

View File

@@ -19,20 +19,10 @@
class PlayerbotAI;
typedef Strategy* (*StrategyCreator)(PlayerbotAI* botAI);
typedef Action* (*ActionCreator)(PlayerbotAI* botAI);
typedef Trigger* (*TriggerCreator)(PlayerbotAI* botAI);
typedef UntypedValue* (*ValueCreator)(PlayerbotAI* botAI);
class AiObjectContext : public PlayerbotAIAware
{
public:
static BoolCalculatedValue* custom_glyphs(PlayerbotAI* ai); // Added for cutom glyphs
AiObjectContext(PlayerbotAI* botAI,
SharedNamedObjectContextList<Strategy>& sharedStrategyContext = sharedStrategyContexts,
SharedNamedObjectContextList<Action>& sharedActionContext = sharedActionContexts,
SharedNamedObjectContextList<Trigger>& sharedTriggerContext = sharedTriggerContexts,
SharedNamedObjectContextList<UntypedValue>& sharedValueContext = sharedValueContexts);
AiObjectContext(PlayerbotAI* botAI);
virtual ~AiObjectContext() {}
virtual Strategy* GetStrategy(std::string const name);
@@ -66,30 +56,20 @@ public:
std::set<std::string> GetSupportedActions();
std::string const FormatValues();
virtual void Update();
virtual void Reset();
virtual void AddShared(NamedObjectContext<UntypedValue>* sharedValues);
std::vector<std::string> Save();
void Load(std::vector<std::string> data);
std::vector<std::string> performanceStack;
static void BuildAllSharedContexts();
static void BuildSharedContexts();
static void BuildSharedStrategyContexts(SharedNamedObjectContextList<Strategy>& strategyContexts);
static void BuildSharedActionContexts(SharedNamedObjectContextList<Action>& actionContexts);
static void BuildSharedTriggerContexts(SharedNamedObjectContextList<Trigger>& triggerContexts);
static void BuildSharedValueContexts(SharedNamedObjectContextList<UntypedValue>& valueContexts);
protected:
NamedObjectContextList<Strategy> strategyContexts;
NamedObjectContextList<Action> actionContexts;
NamedObjectContextList<Trigger> triggerContexts;
NamedObjectContextList<UntypedValue> valueContexts;
private:
static SharedNamedObjectContextList<Strategy> sharedStrategyContexts;
static SharedNamedObjectContextList<Action> sharedActionContexts;
static SharedNamedObjectContextList<Trigger> sharedTriggerContexts;
static SharedNamedObjectContextList<UntypedValue> sharedValueContexts;
};
#endif

View File

@@ -11,7 +11,6 @@
#include "Playerbots.h"
#include "Queue.h"
#include "Strategy.h"
#include "Timer.h"
Engine::Engine(PlayerbotAI* botAI, AiObjectContext* factory) : PlayerbotAIAware(botAI), aiObjectContext(factory)
{
@@ -109,8 +108,6 @@ void Engine::Reset()
}
multipliers.clear();
actionNodeFactories.creators.clear();
}
void Engine::Init()
@@ -123,10 +120,9 @@ void Engine::Init()
strategyTypeMask |= strategy->GetType();
strategy->InitMultipliers(multipliers);
strategy->InitTriggers(triggers);
for (auto &iter : strategy->actionNodeFactories.creators)
{
actionNodeFactories.creators[iter.first] = iter.second;
}
Event emptyEvent;
MultiplyAndPush(strategy->getDefaultActions(), 0.0f, false, emptyEvent, "default");
}
if (testMode)
@@ -252,9 +248,11 @@ bool Engine::DoNextAction(Unit* unit, uint32 depth, bool minimal)
ActionNode* Engine::CreateActionNode(std::string const name)
{
ActionNode* node = actionNodeFactories.GetContextObject(name, botAI);
if (node)
return node;
for (std::map<std::string, Strategy*>::iterator i = strategies.begin(); i != strategies.end(); i++)
{
if (ActionNode* node = i->second->GetAction(name))
return node;
}
return new ActionNode(name,
/*P*/ nullptr,
@@ -434,7 +432,6 @@ bool Engine::HasStrategy(std::string const name) { return strategies.find(name)
void Engine::ProcessTriggers(bool minimal)
{
std::unordered_map<Trigger*, Event> fires;
uint32 now = getMSTime();
for (std::vector<TriggerNode*>::iterator i = triggers.begin(); i != triggers.end(); i++)
{
TriggerNode* node = *i;
@@ -454,7 +451,7 @@ void Engine::ProcessTriggers(bool minimal)
if (fires.find(trigger) != fires.end())
continue;
if (testMode || trigger->needCheck(now))
if (testMode || trigger->needCheck())
{
if (minimal && node->getFirstRelevance() < 100)
continue;

View File

@@ -114,7 +114,6 @@ protected:
float lastRelevance;
std::string lastAction;
uint32 strategyTypeMask;
NamedObjectFactoryList<ActionNode> actionNodeFactories;
};
#endif

View File

@@ -29,8 +29,7 @@ public:
std::string const getQualifier() { return qualifier; }
static std::string const MultiQualify(std::vector<std::string> qualifiers, const std::string& separator,
const std::string_view brackets = "{}");
static std::string const MultiQualify(std::vector<std::string> qualifiers, const std::string& separator, const std::string_view brackets = "{}");
static std::vector<std::string> getMultiQualifiers(std::string const qualifier1);
static int32 getMultiQualifier(std::string const qualifier1, uint32 pos);
@@ -41,9 +40,9 @@ protected:
template <class T>
class NamedObjectFactory
{
public:
typedef T* (*ObjectCreator)(PlayerbotAI* botAI);
std::unordered_map<std::string, ObjectCreator> creators;
protected:
typedef T* (*ActionCreator)(PlayerbotAI* botAI);
std::unordered_map<std::string, ActionCreator> creators;
public:
T* create(std::string name, PlayerbotAI* botAI)
@@ -59,7 +58,7 @@ public:
if (creators.find(name) == creators.end())
return nullptr;
ObjectCreator creator = creators[name];
ActionCreator creator = creators[name];
if (!creator)
return nullptr;
@@ -74,7 +73,7 @@ public:
std::set<std::string> supports()
{
std::set<std::string> keys;
for (typename std::unordered_map<std::string, ObjectCreator>::iterator it = creators.begin();
for (typename std::unordered_map<std::string, ActionCreator>::iterator it = creators.begin();
it != creators.end(); it++)
keys.insert(it->first);
@@ -112,6 +111,24 @@ public:
created.clear();
}
void Update()
{
for (typename std::unordered_map<std::string, T*>::iterator i = created.begin(); i != created.end(); i++)
{
if (i->second)
i->second->Update();
}
}
void Reset()
{
for (typename std::unordered_map<std::string, T*>::iterator i = created.begin(); i != created.end(); i++)
{
if (i->second)
i->second->Reset();
}
}
bool IsShared() { return shared; }
bool IsSupportsSiblings() { return supportsSiblings; }
@@ -130,93 +147,53 @@ protected:
bool supportsSiblings;
};
template <class T>
class SharedNamedObjectContextList
{
public:
typedef T* (*ObjectCreator)(PlayerbotAI* botAI);
std::unordered_map<std::string, ObjectCreator> creators;
std::vector<NamedObjectContext<T>*> contexts;
~SharedNamedObjectContextList()
{
for (typename std::vector<NamedObjectContext<T>*>::iterator i = contexts.begin(); i != contexts.end(); i++)
delete *i;
}
void Add(NamedObjectContext<T>* context)
{
contexts.push_back(context);
for (const auto& iter : context->creators)
{
creators[iter.first] = iter.second;
}
}
};
template <class T>
class NamedObjectContextList
{
public:
typedef T* (*ObjectCreator)(PlayerbotAI* botAI);
const std::unordered_map<std::string, ObjectCreator>& creators;
const std::vector<NamedObjectContext<T>*>& contexts;
std::unordered_map<std::string, T*> created;
NamedObjectContextList(const SharedNamedObjectContextList<T>& shared)
: creators(shared.creators), contexts(shared.contexts)
virtual ~NamedObjectContextList()
{
}
~NamedObjectContextList()
{
for (typename std::unordered_map<std::string, T*>::iterator i = created.begin(); i != created.end(); i++)
for (typename std::vector<NamedObjectContext<T>*>::iterator i = contexts.begin(); i != contexts.end(); i++)
{
if (i->second)
delete i->second;
NamedObjectContext<T>* context = *i;
if (!context->IsShared())
delete context;
}
created.clear();
}
T* create(std::string name, PlayerbotAI* botAI)
{
size_t found = name.find("::");
std::string qualifier;
if (found != std::string::npos)
{
qualifier = name.substr(found + 2);
name = name.substr(0, found);
}
if (creators.find(name) == creators.end())
return nullptr;
ObjectCreator creator = creators.at(name);
if (!creator)
return nullptr;
T* object = (*creator)(botAI);
Qualified* q = dynamic_cast<Qualified*>(object);
if (q && found != std::string::npos)
q->Qualify(qualifier);
return object;
}
void Add(NamedObjectContext<T>* context) { contexts.push_back(context); }
T* GetContextObject(std::string const name, PlayerbotAI* botAI)
{
if (created.find(name) == created.end())
for (typename std::vector<NamedObjectContext<T>*>::iterator i = contexts.begin(); i != contexts.end(); i++)
{
if (T* object = create(name, botAI))
return created[name] = object;
if (T* object = (*i)->create(name, botAI))
return object;
}
return nullptr;
}
void Update()
{
for (typename std::vector<NamedObjectContext<T>*>::iterator i = contexts.begin(); i != contexts.end(); i++)
{
if (!(*i)->IsShared())
(*i)->Update();
}
}
void Reset()
{
for (typename std::vector<NamedObjectContext<T>*>::iterator i = contexts.begin(); i != contexts.end(); i++)
{
(*i)->Reset();
}
return created[name];
}
std::set<std::string> GetSiblings(std::string const name)
{
for (auto i = contexts.begin(); i != contexts.end(); i++)
for (typename std::vector<NamedObjectContext<T>*>::iterator i = contexts.begin(); i != contexts.end(); i++)
{
if (!(*i)->IsSupportsSiblings())
continue;
@@ -236,7 +213,7 @@ public:
std::set<std::string> supports()
{
std::set<std::string> result;
for (auto i = contexts.begin(); i != contexts.end(); i++)
for (typename std::vector<NamedObjectContext<T>*>::iterator i = contexts.begin(); i != contexts.end(); i++)
{
std::set<std::string> supported = (*i)->supports();
@@ -250,69 +227,46 @@ public:
std::set<std::string> GetCreated()
{
std::set<std::string> result;
for (typename std::unordered_map<std::string, T*>::iterator i = created.begin(); i != created.end(); i++)
for (typename std::vector<NamedObjectContext<T>*>::iterator i = contexts.begin(); i != contexts.end(); i++)
{
result.insert(i->first);
std::set<std::string> createdKeys = (*i)->GetCreated();
for (std::set<std::string>::iterator j = createdKeys.begin(); j != createdKeys.end(); j++)
result.insert(*j);
}
return result;
}
private:
std::vector<NamedObjectContext<T>*> contexts;
};
template <class T>
class NamedObjectFactoryList
{
public:
typedef T* (*ObjectCreator)(PlayerbotAI* botAI);
std::vector<NamedObjectFactory<T>*> factories;
std::unordered_map<std::string, ObjectCreator> creators;
virtual ~NamedObjectFactoryList()
{
for (typename std::vector<NamedObjectFactory<T>*>::iterator i = factories.begin(); i != factories.end(); i++)
for (typename std::list<NamedObjectFactory<T>*>::iterator i = factories.begin(); i != factories.end(); i++)
delete *i;
}
T* create(std::string name, PlayerbotAI* botAI)
void Add(NamedObjectFactory<T>* context) { factories.push_front(context); }
T* GetContextObject(std::string const& name, PlayerbotAI* botAI)
{
size_t found = name.find("::");
std::string qualifier;
if (found != std::string::npos)
for (typename std::list<NamedObjectFactory<T>*>::iterator i = factories.begin(); i != factories.end(); i++)
{
qualifier = name.substr(found + 2);
name = name.substr(0, found);
if (T* object = (*i)->create(name, botAI))
return object;
}
if (creators.find(name) == creators.end())
return nullptr;
ObjectCreator creator = creators[name];
if (!creator)
return nullptr;
T* object = (*creator)(botAI);
Qualified* q = dynamic_cast<Qualified*>(object);
if (q && found != std::string::npos)
q->Qualify(qualifier);
return object;
}
void Add(NamedObjectFactory<T>* context)
{
factories.push_back(context);
for (const auto& iter : context->creators)
{
creators[iter.first] = iter.second;
}
}
T* GetContextObject(std::string const name, PlayerbotAI* botAI)
{
if (T* object = create(name, botAI))
return object;
return nullptr;
}
private:
std::list<NamedObjectFactory<T>*> factories;
};
#endif

View File

@@ -41,7 +41,6 @@ enum StrategyType : uint32
// };
static float ACTION_IDLE = 0.0f;
static float ACTION_BG = 1.0f;
static float ACTION_DEFAULT = 5.0f;
static float ACTION_NORMAL = 10.0f;
static float ACTION_HIGH = 20.0f;
@@ -69,7 +68,7 @@ public:
void Update() {}
void Reset() {}
public:
protected:
NamedObjectFactoryList<ActionNode> actionNodeFactories;
};

View File

@@ -32,11 +32,12 @@ Value<Unit*>* Trigger::GetTargetValue() { return context->GetValue<Unit*>(GetTar
Unit* Trigger::GetTarget() { return GetTargetValue()->Get(); }
bool Trigger::needCheck(uint32 now)
bool Trigger::needCheck()
{
if (checkInterval < 2)
return true;
uint32 now = getMSTime();
if (!lastCheckTime || now - lastCheckTime >= checkInterval)
{
lastCheckTime = now;

View File

@@ -30,7 +30,7 @@ public:
virtual Value<Unit*>* GetTargetValue();
virtual std::string const GetTargetName() { return "self target"; }
bool needCheck(uint32 now);
bool needCheck();
protected:
int32 checkInterval;

View File

@@ -38,7 +38,6 @@
#include "InviteToGroupAction.h"
#include "LeaveGroupAction.h"
#include "LootAction.h"
#include "LootRollAction.h"
#include "MoveToRpgTargetAction.h"
#include "MoveToTravelTargetAction.h"
#include "MovementActions.h"
@@ -64,7 +63,6 @@
#include "WorldBuffAction.h"
#include "XpGainAction.h"
#include "NewRpgAction.h"
#include "CancelChannelAction.h"
class PlayerbotAI;
@@ -191,13 +189,10 @@ public:
creators["buy tabard"] = &ActionContext::buy_tabard;
creators["guild manage nearby"] = &ActionContext::guild_manage_nearby;
creators["clean quest log"] = &ActionContext::clean_quest_log;
creators["roll"] = &ActionContext::roll_action;
creators["cancel channel"] = &ActionContext::cancel_channel;
// BG Tactics
creators["bg tactics"] = &ActionContext::bg_tactics;
creators["bg move to start"] = &ActionContext::bg_move_to_start;
creators["bg reset objective force"] = &ActionContext::bg_reset_objective_force;
creators["bg move to objective"] = &ActionContext::bg_move_to_objective;
creators["bg select objective"] = &ActionContext::bg_select_objective;
creators["bg check objective"] = &ActionContext::bg_check_objective;
@@ -250,11 +245,10 @@ public:
creators["new rpg status update"] = &ActionContext::new_rpg_status_update;
creators["new rpg go grind"] = &ActionContext::new_rpg_go_grind;
creators["new rpg go camp"] = &ActionContext::new_rpg_go_camp;
creators["new rpg wander random"] = &ActionContext::new_rpg_wander_random;
creators["new rpg wander npc"] = &ActionContext::new_rpg_wander_npc;
creators["new rpg go innkeeper"] = &ActionContext::new_rpg_go_innkeeper;
creators["new rpg move random"] = &ActionContext::new_rpg_move_random;
creators["new rpg move npc"] = &ActionContext::new_rpg_move_npc;
creators["new rpg do quest"] = &ActionContext::new_rpg_do_quest;
creators["new rpg travel flight"] = &ActionContext::new_rpg_travel_flight;
}
private:
@@ -302,7 +296,6 @@ private:
static Action* arcane_torrent(PlayerbotAI* botAI) { return new CastArcaneTorrentAction(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); }
static Action* emote(PlayerbotAI* botAI) { return new EmoteAction(botAI); }
static Action* talk(PlayerbotAI* botAI) { return new TalkAction(botAI); }
@@ -379,12 +372,10 @@ private:
static Action* buy_tabard(PlayerbotAI* botAI) { return new BuyTabardAction(botAI); }
static Action* guild_manage_nearby(PlayerbotAI* botAI) { return new GuildManageNearbyAction(botAI); }
static Action* clean_quest_log(PlayerbotAI* botAI) { return new CleanQuestLogAction(botAI); }
static Action* roll_action(PlayerbotAI* botAI) { return new RollAction(botAI); }
// BG Tactics
static Action* bg_tactics(PlayerbotAI* botAI) { return new BGTactics(botAI); }
static Action* bg_move_to_start(PlayerbotAI* botAI) { return new BGTactics(botAI, "move to start"); }
static Action* bg_reset_objective_force(PlayerbotAI* botAI) { return new BGTactics(botAI, "reset objective force"); }
static Action* bg_move_to_objective(PlayerbotAI* botAI) { return new BGTactics(botAI, "move to objective"); }
static Action* bg_select_objective(PlayerbotAI* botAI) { return new BGTactics(botAI, "select objective"); }
static Action* bg_check_objective(PlayerbotAI* botAI) { return new BGTactics(botAI, "check objective"); }
@@ -437,11 +428,10 @@ private:
static Action* new_rpg_status_update(PlayerbotAI* ai) { return new NewRpgStatusUpdateAction(ai); }
static Action* new_rpg_go_grind(PlayerbotAI* ai) { return new NewRpgGoGrindAction(ai); }
static Action* new_rpg_go_camp(PlayerbotAI* ai) { return new NewRpgGoCampAction(ai); }
static Action* new_rpg_wander_random(PlayerbotAI* ai) { return new NewRpgWanderRandomAction(ai); }
static Action* new_rpg_wander_npc(PlayerbotAI* ai) { return new NewRpgWanderNpcAction(ai); }
static Action* new_rpg_go_innkeeper(PlayerbotAI* ai) { return new NewRpgGoInnKeeperAction(ai); }
static Action* new_rpg_move_random(PlayerbotAI* ai) { return new NewRpgMoveRandomAction(ai); }
static Action* new_rpg_move_npc(PlayerbotAI* ai) { return new NewRpgMoveNpcAction(ai); }
static Action* new_rpg_do_quest(PlayerbotAI* ai) { return new NewRpgDoQuestAction(ai); }
static Action* new_rpg_travel_flight(PlayerbotAI* ai) { return new NewRpgTravelFlightAction(ai); }
};
#endif

View File

@@ -59,7 +59,10 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/)
bool sameTarget = oldTarget == target && bot->GetVictim() == target;
bool inCombat = botAI->GetState() == BOT_STATE_COMBAT;
bool sameAttackMode = bot->HasUnitState(UNIT_STATE_MELEE_ATTACKING) == shouldMelee;
// there's no reason to do attack again
if (sameTarget && inCombat && sameAttackMode)
return false;
if (bot->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE ||
bot->HasUnitState(UNIT_STATE_IN_FLIGHT))
{
@@ -79,53 +82,52 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/)
if (!target->IsInWorld())
{
if (verbose)
botAI->TellError(std::string(target->GetName()) + " is no longer in the world.");
return false;
}
if ((sPlayerbotAIConfig->IsInPvpProhibitedZone(bot->GetZoneId()) ||
sPlayerbotAIConfig->IsInPvpProhibitedArea(bot->GetAreaId()))
&& (target->IsPlayer() || target->IsPet()))
{
if (verbose)
botAI->TellError("I cannot attack other players in PvP prohibited areas.");
return false;
}
if (bot->IsFriendlyTo(target))
{
if (verbose)
botAI->TellError(std::string(target->GetName()) + " is friendly to me.");
return false;
}
if (target->isDead())
{
if (verbose)
botAI->TellError(std::string(target->GetName()) + " is dead.");
return false;
}
if (!bot->IsWithinLOSInMap(target))
{
if (verbose)
botAI->TellError(std::string(target->GetName()) + " is not in my sight.");
return false;
}
if (sameTarget && inCombat && sameAttackMode)
{
if (verbose)
botAI->TellError("I am already attacking " + std::string(target->GetName()) + ".");
return false;
}
if (!bot->IsValidAttackTarget(target))
{
if (verbose)
botAI->TellError("I cannot attack an invalid target.");
botAI->TellError("I cannot attack an invalid target");
return false;
}
std::ostringstream msg;
msg << target->GetName();
if (bot->IsFriendlyTo(target))
{
msg << " is friendly to me";
if (verbose)
botAI->TellError(msg.str());
return false;
}
if (!bot->IsWithinLOSInMap(target))
{
msg << " is not in my sight";
if (verbose)
botAI->TellError(msg.str());
return false;
}
if (target->isDead())
{
msg << " is dead";
if (verbose)
botAI->TellError(msg.str());
return false;
}
if (sPlayerbotAIConfig->IsInPvpProhibitedZone(bot->GetZoneId())
&& (target->IsPlayer() || target->IsPet()))
{
if (verbose)
botAI->TellError("I cannot attack others in PvP prohibited zones");
return false;
}
@@ -139,7 +141,9 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/)
ObjectGuid guid = target->GetGUID();
bot->SetSelection(target->GetGUID());
context->GetValue<Unit*>("old target")->Set(oldTarget);
context->GetValue<Unit*>("old target")->Set(oldTarget);
context->GetValue<Unit*>("current target")->Set(target);
context->GetValue<LootObjectStack*>("available loot")->Get()->Add(guid);
@@ -153,6 +157,7 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/)
bot->StopMoving();
}
if (IsMovingAllowed() && !bot->HasInArc(CAST_ANGLE_IN_FRONT, target))
{
sServerFacade->SetFacingTo(bot, target);

View File

@@ -157,23 +157,15 @@ void AutoMaintenanceOnLevelupAction::LearnSpell(uint32 spellId, std::ostringstre
void AutoMaintenanceOnLevelupAction::AutoUpgradeEquip()
{
if (!sPlayerbotAIConfig->autoUpgradeEquip || !sRandomPlayerbotMgr->IsRandomBot(bot))
{
return;
}
PlayerbotFactory factory(bot, bot->GetLevel());
// Clean up old consumables before adding new ones
factory.CleanupConsumables();
factory.InitAmmo();
factory.InitReagents();
factory.InitFood();
factory.InitConsumables();
factory.InitPotions();
if (!sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel)
{
if (sPlayerbotAIConfig->incrementalGearInit)
factory.InitEquipment(true);
factory.InitEquipment(true);
}
factory.InitAmmo();
return;
}

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,6 @@
#ifndef _PLAYERBOT_BATTLEGROUNDTACTICSACTION_H
#define _PLAYERBOT_BATTLEGROUNDTACTICSACTION_H
#include "BattlegroundAV.h"
#include "MovementActions.h"
class ChatHandler;
@@ -16,49 +15,8 @@ struct Position;
#define SPELL_CAPTURE_BANNER 21651
enum WSBotStrategy : uint8
{
WS_STRATEGY_BALANCED = 0,
WS_STRATEGY_OFFENSIVE = 1,
WS_STRATEGY_DEFENSIVE = 2,
WS_STRATEGY_MAX = 3,
};
enum ABBotStrategy : uint8
{
AB_STRATEGY_BALANCED = 0,
AB_STRATEGY_OFFENSIVE = 1,
AB_STRATEGY_DEFENSIVE = 2,
AB_STRATEGY_MAX = 3,
};
enum AVBotStrategy : uint8
{
AV_STRATEGY_BALANCED = 0,
AV_STRATEGY_OFFENSIVE = 1,
AV_STRATEGY_DEFENSIVE = 2,
AV_STRATEGY_MAX = 3,
};
enum EYBotStrategy : uint8
{
EY_STRATEGY_BALANCED = 0,
EY_STRATEGY_FRONT_FOCUS = 1,
EY_STRATEGY_BACK_FOCUS = 2,
EY_STRATEGY_FLAG_FOCUS = 3,
EY_STRATEGY_MAX = 4
};
typedef void (*BattleBotWaypointFunc)();
struct BGStrategyData
{
uint8 allianceStrategy = 0;
uint8 hordeStrategy = 0;
};
extern std::unordered_map<uint32, BGStrategyData> bgStrategies;
struct BattleBotWaypoint
{
BattleBotWaypoint(float x_, float y_, float z_, BattleBotWaypointFunc func) : x(x_), y(y_), z(z_), pFunc(func){};
@@ -69,31 +27,6 @@ struct BattleBotWaypoint
BattleBotWaypointFunc pFunc = nullptr;
};
struct AVNodePositionData
{
Position pos;
float maxRadius;
};
// Added to fix bot stuck at objectives
static std::unordered_map<uint8, AVNodePositionData> AVNodeMovementTargets = {
{BG_AV_NODES_FIRSTAID_STATION, {Position(640.364f, -36.535f, 45.625f), 15.0f}},
{BG_AV_NODES_STORMPIKE_GRAVE, {Position(665.598f, -292.976f, 30.291f), 15.0f}},
{BG_AV_NODES_STONEHEART_GRAVE, {Position(76.108f, -399.602f, 45.730f), 15.0f}},
{BG_AV_NODES_SNOWFALL_GRAVE, {Position(-201.298f, -119.661f, 78.291f), 15.0f}},
{BG_AV_NODES_ICEBLOOD_GRAVE, {Position(-617.858f, -400.654f, 59.692f), 15.0f}},
{BG_AV_NODES_FROSTWOLF_GRAVE, {Position(-1083.803f, -341.520f, 55.304f), 15.0f}},
{BG_AV_NODES_FROSTWOLF_HUT, {Position(-1405.678f, -309.108f, 89.377f, 0.392f), 10.0f}},
{BG_AV_NODES_DUNBALDAR_SOUTH, {Position(556.551f, -77.240f, 51.931f), 0.0f}},
{BG_AV_NODES_DUNBALDAR_NORTH, {Position(670.664f, -142.031f, 63.666f), 0.0f}},
{BG_AV_NODES_ICEWING_BUNKER, {Position(200.310f, -361.232f, 56.387f), 0.0f}},
{BG_AV_NODES_STONEHEART_BUNKER, {Position(-156.302f, -440.032f, 40.403f), 0.0f}},
{BG_AV_NODES_ICEBLOOD_TOWER, {Position(-569.702f, -265.362f, 75.009f), 0.0f}},
{BG_AV_NODES_TOWER_POINT, {Position(-767.439f, -360.200f, 90.895f), 0.0f}},
{BG_AV_NODES_FROSTWOLF_ETOWER, {Position(-1303.737f, -314.070f, 113.868f), 0.0f}},
{BG_AV_NODES_FROSTWOLF_WTOWER, {Position(-1300.648f, -267.356f, 114.151f), 0.0f}},
};
typedef std::vector<BattleBotWaypoint> BattleBotPath;
extern std::vector<BattleBotPath*> const vPaths_WS;
@@ -106,7 +39,6 @@ class BGTactics : public MovementAction
{
public:
static bool HandleConsoleCommand(ChatHandler* handler, char const* args);
uint8 static GetBotStrategyForTeam(Battleground* bg, TeamId teamId);
BGTactics(PlayerbotAI* botAI, std::string const name = "bg tactics") : MovementAction(botAI, name) {}
@@ -116,13 +48,13 @@ private:
static std::string const HandleConsoleCommandPrivate(WorldSession* session, char const* args);
bool moveToStart(bool force = false);
bool selectObjective(bool reset = false);
bool moveToObjective(bool ignoreDist);
bool moveToObjective();
bool selectObjectiveWp(std::vector<BattleBotPath*> const& vPaths);
bool moveToObjectiveWp(BattleBotPath* const& currentPath, uint32 currentPoint, bool reverse = false);
bool startNewPathBegin(std::vector<BattleBotPath*> const& vPaths);
bool startNewPathFree(std::vector<BattleBotPath*> const& vPaths);
bool resetObjective();
bool wsJumpDown();
bool wsgPaths();
bool eyJumpDown();
bool atFlag(std::vector<BattleBotPath*> const& vPaths, std::vector<uint32> const& vFlagIds);
bool flagTaken();

View File

@@ -50,16 +50,3 @@ bool BossNatureResistanceAction::Execute(Event event)
botAI->ChangeStrategy(ADD_STRATEGY_CHAR + hunterNatureResistanceStrategy.getName(), BotState::BOT_STATE_COMBAT);
return true;
}
bool BossShadowResistanceAction::isUseful()
{
BossShadowResistanceTrigger bossShadowResistanceTrigger(botAI, bossName);
return bossShadowResistanceTrigger.IsActive();
}
bool BossShadowResistanceAction::Execute(Event event)
{
PaladinShadowResistanceStrategy paladinShadowResistanceStrategy(botAI);
botAI->ChangeStrategy(ADD_STRATEGY_CHAR + paladinShadowResistanceStrategy.getName(), BotState::BOT_STATE_COMBAT);
return true;
}

View File

@@ -54,18 +54,4 @@ private:
std::string bossName;
};
class BossShadowResistanceAction : public Action
{
public:
BossShadowResistanceAction(PlayerbotAI* botAI, std::string const bossName)
: Action(botAI, bossName + " shadow resistance action"), bossName(bossName)
{
}
bool Execute(Event event) override;
bool isUseful() override;
private:
std::string bossName;
};
#endif

View File

@@ -1,18 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "CancelChannelAction.h"
#include "Player.h"
#include "PlayerbotAI.h"
bool CancelChannelAction::Execute(Event event)
{
if (bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL))
{
bot->InterruptSpell(CURRENT_CHANNELED_SPELL);
return true;
}
return false;
}

View File

@@ -1,21 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_CANCELCHANNELACTION_H
#define _PLAYERBOT_CANCELCHANNELACTION_H
#include "Action.h"
class PlayerbotAI;
class CancelChannelAction : public Action
{
public:
CancelChannelAction(PlayerbotAI* botAI) : Action(botAI, "cancel channel") {}
bool Execute(Event event) override;
};
#endif

View File

@@ -11,18 +11,9 @@
#include "PlayerbotAIConfig.h"
#include "PlayerbotFactory.h"
#include "Playerbots.h"
#include "AiObjectContext.h"
#include "Log.h"
bool ChangeTalentsAction::Execute(Event event)
{
auto* flag = botAI->GetAiObjectContext()->GetValue<bool>("custom_glyphs"); // Added for custom Glyphs
if (flag->Get()) // Added for custom Glyphs
{
flag->Set(false);
LOG_INFO("playerbots", "Custom Glyph Flag set to OFF");
}
std::string param = event.getParam();
std::ostringstream out;
@@ -144,10 +135,6 @@ std::string ChangeTalentsAction::SpecPick(std::string param)
if (sPlayerbotAIConfig->premadeSpecName[cls][specNo] == param)
{
PlayerbotFactory::InitTalentsBySpecNo(bot, specNo, true);
PlayerbotFactory factory(bot, bot->GetLevel());
factory.InitGlyphs(false);
std::ostringstream out;
out << "Picking " << sPlayerbotAIConfig->premadeSpecName[cls][specNo];
return out.str();

View File

@@ -36,7 +36,6 @@
#include "ListSpellsAction.h"
#include "LogLevelAction.h"
#include "LootStrategyAction.h"
#include "LootRollAction.h"
#include "MailAction.h"
#include "NamedObjectContext.h"
#include "NewRpgAction.h"
@@ -74,14 +73,10 @@
#include "UseItemAction.h"
#include "UseMeetingStoneAction.h"
#include "WhoAction.h"
#include "WipeAction.h"
#include "WtsAction.h"
#include "OpenItemAction.h"
#include "UnlockItemAction.h"
#include "UnlockTradedItemAction.h"
#include "PetAction.h"
#include "TellGlyphsAction.h"
#include "EquipGlyphsAction.h"
class ChatActionContext : public NamedObjectContext<Action>
{
@@ -190,11 +185,6 @@ public:
creators["join"] = &ChatActionContext::join;
creators["lfg"] = &ChatActionContext::lfg;
creators["calc"] = &ChatActionContext::calc;
creators["wipe"] = &ChatActionContext::wipe;
creators["pet"] = &ChatActionContext::pet;
creators["glyphs"] = &ChatActionContext::glyphs; // Added for custom Glyphs
creators["glyph equip"] = &ChatActionContext::glyph_equip; // Added for custom Glyphs
creators["roll"] = &ChatActionContext::roll_action;
}
private:
@@ -300,11 +290,6 @@ private:
static Action* tell_estimated_dps(PlayerbotAI* ai) { return new TellEstimatedDpsAction(ai); }
static Action* join(PlayerbotAI* ai) { return new JoinGroupAction(ai); }
static Action* calc(PlayerbotAI* ai) { return new TellCalculateItemAction(ai); }
static Action* wipe(PlayerbotAI* ai) { return new WipeAction(ai); }
static Action* pet(PlayerbotAI* botAI) { return new PetAction(botAI); }
static Action* glyphs(PlayerbotAI* botAI) { return new TellGlyphsAction(botAI); } // Added for custom Glyphs
static Action* glyph_equip(PlayerbotAI* ai) { return new EquipGlyphsAction(ai); } // Added for custom Glyphs
static Action* roll_action(PlayerbotAI* botAI) { return new RollAction(botAI); }
};
#endif

View File

@@ -53,9 +53,6 @@ BotCheatMask CheatAction::GetCheatMask(std::string const cheat)
if (cheat == "power")
return BotCheatMask::power;
if (cheat == "raid")
return BotCheatMask::raid;
return BotCheatMask::none;
}
@@ -73,8 +70,6 @@ std::string const CheatAction::GetCheatName(BotCheatMask cheatMask)
return "mana";
case BotCheatMask::power:
return "power";
case BotCheatMask::raid:
return "raid";
default:
return "none";
}

View File

@@ -4,8 +4,6 @@
*/
#include "CheckMountStateAction.h"
#include "BattleGroundTactics.h"
#include "BattlegroundEY.h"
#include "BattlegroundWS.h"
#include "Event.h"
#include "PlayerbotAI.h"
@@ -100,7 +98,7 @@ bool CheckMountStateAction::isUseful()
if (bot->InBattleground())
{
// Do not use when carrying BG Flags
if (bot->HasAura(BG_WS_SPELL_WARSONG_FLAG) || bot->HasAura(BG_WS_SPELL_SILVERWING_FLAG) || bot->HasAura(BG_EY_NETHERSTORM_FLAG_SPELL))
if (bot->HasAura(23333) || bot->HasAura(23335) || bot->HasAura(34976))
return false;
// Only mount if BG starts in less than 30 sec

View File

@@ -25,7 +25,7 @@ bool AttackEnemyPlayerAction::isUseful()
bool AttackEnemyFlagCarrierAction::isUseful()
{
Unit* target = context->GetValue<Unit*>("enemy flag carrier")->Get();
return target && sServerFacade->IsDistanceLessOrEqualThan(sServerFacade->GetDistance2d(bot, target), 100.0f) &&
return target && sServerFacade->IsDistanceLessOrEqualThan(sServerFacade->GetDistance2d(bot, target), 75.0f) &&
PlayerHasFlag::IsCapturingFlag(bot);
}
@@ -83,14 +83,7 @@ bool DropTargetAction::Execute(Event event)
bot->SetTarget(ObjectGuid::Empty);
bot->SetSelection(ObjectGuid());
botAI->ChangeEngine(BOT_STATE_NON_COMBAT);
if (bot->getClass() == CLASS_HUNTER) // Check for Hunter Class
{
Spell const* spell = bot->GetCurrentSpell(CURRENT_AUTOREPEAT_SPELL); // Get the current spell being cast by the bot
if (spell && spell->m_spellInfo->Id == 75) //Check spell is not nullptr before accessing m_spellInfo
{
bot->InterruptSpell(CURRENT_AUTOREPEAT_SPELL); // Interrupt Auto Shot
}
}
// botAI->InterruptSpell();
bot->AttackStop();
// if (Pet* pet = bot->GetPet())

View File

@@ -187,8 +187,7 @@ void EquipAction::EquipItem(Item* item)
// Priority 1: Replace main hand if the new weapon is strictly better
// and if conditions allow (e.g. no conflicting 2H logic)
bool betterThanMH = (newItemScore > mainHandScore);
// If a one-handed weapon is better, we can still use it instead of a two-handed weapon
bool mhConditionOK = (invType != INVTYPE_2HWEAPON ||
bool mhConditionOK = ((invType != INVTYPE_2HWEAPON && !have2HWeaponEquipped) ||
(isTwoHander && !canTitanGrip) ||
(canTitanGrip && isValidTGWeapon));

View File

@@ -1,159 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "EquipGlyphsAction.h"
#include "Playerbots.h"
#include "ObjectMgr.h"
#include "SpellMgr.h"
#include "DBCStores.h"
#include "AiObjectContext.h"
#include "Log.h"
#include <unordered_map>
#include <sstream>
#include <unordered_set>
namespace
{
// itemId -> GlyphInfo
std::unordered_map<uint32, EquipGlyphsAction::GlyphInfo> s_GlyphCache;
}
void EquipGlyphsAction::BuildGlyphCache()
{
if (!s_GlyphCache.empty())
return;
ItemTemplateContainer const* store = sObjectMgr->GetItemTemplateStore();
for (auto const& kv : *store)
{
uint32 itemId = kv.first;
ItemTemplate const* proto = &kv.second;
if (!proto || proto->Class != ITEM_CLASS_GLYPH)
continue;
// inspect item spell
for (uint32 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
{
uint32 spellId = proto->Spells[i].SpellId;
if (!spellId) continue;
SpellInfo const* si = sSpellMgr->GetSpellInfo(spellId);
if (!si) continue;
for (uint8 eff = 0; eff <= EFFECT_2; ++eff)
{
if (si->Effects[eff].Effect != SPELL_EFFECT_APPLY_GLYPH)
continue;
uint32 glyphId = si->Effects[eff].MiscValue;
if (!glyphId) continue;
if (auto const* gp = sGlyphPropertiesStore.LookupEntry(glyphId))
s_GlyphCache[itemId] = {gp, proto};
}
}
}
}
EquipGlyphsAction::GlyphInfo const* EquipGlyphsAction::GetGlyphInfo(uint32 itemId)
{
BuildGlyphCache();
auto it = s_GlyphCache.find(itemId);
return (it == s_GlyphCache.end()) ? nullptr : &it->second;
}
/// -----------------------------------------------------------------
/// Validation and collect
/// -----------------------------------------------------------------
bool EquipGlyphsAction::CollectGlyphs(std::vector<uint32> const& itemIds,
std::vector<GlyphInfo const*>& out) const
{
std::unordered_set<uint32> seen;
for (uint32 itemId : itemIds)
{
if (!seen.insert(itemId).second)
return false; // double
auto const* info = GetGlyphInfo(itemId);
if (!info) // no good glyph
return false;
// check class by AllowableClass
if ((info->proto->AllowableClass & bot->getClassMask()) == 0)
return false;
out.push_back(info);
}
return out.size() <= 6 && !out.empty();
}
/// -----------------------------------------------------------------
/// Action
/// -----------------------------------------------------------------
bool EquipGlyphsAction::Execute(Event event)
{
// 1) parse IDs
std::vector<uint32> itemIds;
std::istringstream iss(event.getParam());
for (uint32 id; iss >> id; ) itemIds.push_back(id);
std::vector<GlyphInfo const*> glyphs;
if (!CollectGlyphs(itemIds, glyphs))
{
botAI->TellMaster("Usage: glyph equip <6 glyph item IDs> (3 major, 3 minor).");
return false;
}
// 2) prepare a empty slots table ?
bool used[6] = {false,false,false,false,false,false};
// 3) for each glyph, find the first available and compatible socket
for (auto const* g : glyphs)
{
bool placed = false;
for (uint8 i = 0; i < MAX_GLYPH_SLOT_INDEX; ++i)
{
if (used[i]) continue;
uint32 slotId = bot->GetGlyphSlot(i);
auto const* gs = sGlyphSlotStore.LookupEntry(slotId);
if (!gs || gs->TypeFlags != g->prop->TypeFlags)
continue; // major/minor don't match
// Remove aura if exist
uint32 cur = bot->GetGlyph(i);
if (cur)
if (auto* old = sGlyphPropertiesStore.LookupEntry(cur))
bot->RemoveAurasDueToSpell(old->SpellId);
// Apply new one
bot->CastSpell(bot, g->prop->SpellId, true);
bot->SetGlyph(i, g->prop->Id, true);
used[i] = true;
placed = true;
break;
}
if (!placed)
{
botAI->TellMaster("Not enought empty sockets for all glyphs.");
return false;
}
}
botAI->TellMaster("Glyphs updated.");
// Flag for custom glyphs
botAI->GetAiObjectContext()->GetValue<bool>("custom_glyphs")->Set(true);
LOG_INFO("playerbots", "Custom Glyph Flag set to ON");
return true;
}

View File

@@ -1,37 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_EQUIPGLYPHSACTION_H
#define _PLAYERBOT_EQUIPGLYPHSACTION_H
#include "Action.h"
// 1 = major, 2 = minor dans GlyphProperties.dbc
enum class GlyphKind : uint32 { MAJOR = 1, MINOR = 2 };
class EquipGlyphsAction : public Action
{
public:
EquipGlyphsAction(PlayerbotAI* ai) : Action(ai, "glyph equip") {}
bool Execute(Event event) override;
/// ---- Rendu public pour être utilisable par le cache global ----
struct GlyphInfo
{
GlyphPropertiesEntry const* prop; ///< entrée GlyphProperties.dbc
ItemTemplate const* proto; ///< template de lobjet glyphe
};
private:
/// Construit la cache {itemId -> GlyphInfo}
static void BuildGlyphCache();
static GlyphInfo const* GetGlyphInfo(uint32 itemId);
/// Parse & valide la liste ditems glyphes
bool CollectGlyphs(std::vector<uint32> const& itemIds,
std::vector<GlyphInfo const*>& out) const;
};
#endif

View File

@@ -14,25 +14,10 @@ enum PetSpells
PET_PROWL_2 = 24452,
PET_PROWL_3 = 24453,
PET_COWER = 1742,
PET_LEAP = 47482,
PET_SPELL_LOCK_1 = 19244,
PET_SPELL_LOCK_2 = 19647,
PET_DEVOUR_MAGIC_1 = 19505,
PET_DEVOUR_MAGIC_2 = 19731,
PET_DEVOUR_MAGIC_3 = 19734,
PET_DEVOUR_MAGIC_4 = 19736,
PET_DEVOUR_MAGIC_5 = 27276,
PET_DEVOUR_MAGIC_6 = 27277,
PET_DEVOUR_MAGIC_7 = 48011
PET_LEAP = 47482
};
static std::vector<uint32> disabledPetSpells = {
PET_PROWL_1, PET_PROWL_2, PET_PROWL_3,
PET_COWER, PET_LEAP,
PET_SPELL_LOCK_1, PET_SPELL_LOCK_2,
PET_DEVOUR_MAGIC_1, PET_DEVOUR_MAGIC_2, PET_DEVOUR_MAGIC_3,
PET_DEVOUR_MAGIC_4, PET_DEVOUR_MAGIC_5, PET_DEVOUR_MAGIC_6, PET_DEVOUR_MAGIC_7
};
static std::vector<uint32> disabledPetSpells = {PET_PROWL_1, PET_PROWL_2, PET_PROWL_3, PET_COWER, PET_LEAP};
bool MeleeAction::isUseful()
{

View File

@@ -304,6 +304,7 @@ bool UseTrinketAction::Execute(Event event)
return true;
Item* trinket2 = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_TRINKET2);
if (trinket2 && UseTrinket(trinket2))
return true;
@@ -332,33 +333,6 @@ bool UseTrinketAction::UseTrinket(Item* item)
if (item->GetTemplate()->Spells[i].SpellId > 0 && item->GetTemplate()->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_USE)
{
spellId = item->GetTemplate()->Spells[i].SpellId;
const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId);
if (!spellInfo || !spellInfo->IsPositive())
return false;
bool applyAura = false;
for (int i = 0; i < MAX_SPELL_EFFECTS; i++)
{
const SpellEffectInfo& effectInfo = spellInfo->Effects[i];
if (effectInfo.Effect == SPELL_EFFECT_APPLY_AURA) {
applyAura = true;
break;
}
}
if (!applyAura)
return false;
uint32 spellProcFlag = spellInfo->ProcFlags;
// Handle items with procflag "if you kill a target that grants honor or experience"
// Bots will "learn" the trinket proc, so CanCastSpell() will be true
// e.g. on Item https://www.wowhead.com/wotlk/item=44074/oracle-talisman-of-ablution leading to
// constant casting of the proc spell onto themselfes https://www.wowhead.com/wotlk/spell=59787/oracle-ablutions
// This will lead to multiple hundreds of entries in m_appliedAuras -> Once killing an enemy -> Big diff time spikes
if (spellProcFlag != 0) return false;
if (!botAI->CanCastSpell(spellId, bot, false))
{
return false;
@@ -371,8 +345,17 @@ bool UseTrinketAction::UseTrinket(Item* item)
WorldPacket packet(CMSG_USE_ITEM);
packet << bagIndex << slot << cast_count << spellId << item_guid << glyphIndex << castFlags;
targetFlag = TARGET_FLAG_NONE;
packet << targetFlag << bot->GetPackGUID();
Unit* target = AI_VALUE(Unit*, "current target");
if (target)
{
targetFlag = TARGET_FLAG_UNIT;
packet << targetFlag << target->GetGUID().WriteAsPacked();
}
else
{
targetFlag = TARGET_FLAG_NONE;
packet << targetFlag << bot->GetPackGUID();
}
bot->GetSession()->HandleUseItemOpcode(packet);
return true;
}

View File

@@ -19,51 +19,22 @@ bool ImbueWithPoisonAction::Execute(Event event)
if (bot->HasAura(SPELL_AURA_MOD_STEALTH))
bot->RemoveAurasByType(SPELL_AURA_MOD_STEALTH);
// hp check
if (bot->getStandState() != UNIT_STAND_STATE_STAND)
bot->SetStandState(UNIT_STAND_STATE_STAND);
static const std::vector<uint32_t> prioritizedInstantPoisons = {
INSTANT_POISON_IX, INSTANT_POISON_VIII, INSTANT_POISON_VII, INSTANT_POISON_VI, INSTANT_POISON_V, INSTANT_POISON_IV,
INSTANT_POISON_III, INSTANT_POISON_II, INSTANT_POISON
};
static const std::vector<uint32_t> prioritizedDeadlyPoisons = {
DEADLY_POISON_IX, DEADLY_POISON_VIII, DEADLY_POISON_VII, DEADLY_POISON_VI, DEADLY_POISON_V, DEADLY_POISON_IV,
DEADLY_POISON_III, DEADLY_POISON_II, DEADLY_POISON
};
// Check if we have any deadly or instant poisons
Item* deadlyPoison = nullptr;
for (auto id : prioritizedDeadlyPoisons)
{
deadlyPoison = botAI->FindConsumable(id);
if (deadlyPoison) break;
}
Item* instantPoison = nullptr;
for (auto id : prioritizedInstantPoisons)
{
instantPoison = botAI->FindConsumable(id);
if (instantPoison) break;
}
// Mainhand
// Search and apply poison to weapons
// Mainhand ...
Item* poison = nullptr;
Item* weapon = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
if (weapon && weapon->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT) == 0)
{
if (instantPoison && deadlyPoison)
{
poison = instantPoison;
}
else if (deadlyPoison)
{
poison = deadlyPoison;
}
else if (instantPoison)
{
poison = instantPoison;
}
poison = botAI->FindConsumable(INSTANT_POISON_DISPLAYID);
if (!poison)
poison = botAI->FindConsumable(DEADLY_POISON_DISPLAYID);
if (!poison)
poison = botAI->FindConsumable(WOUND_POISON_DISPLAYID);
if (poison)
{
@@ -72,23 +43,16 @@ bool ImbueWithPoisonAction::Execute(Event event)
}
}
// Offhand
poison = nullptr;
//... and offhand
weapon = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
if (weapon && weapon->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT) == 0)
{
if (deadlyPoison && instantPoison)
{
poison = deadlyPoison;
}
else if (instantPoison)
{
poison = instantPoison;
}
else if (deadlyPoison)
{
poison = deadlyPoison;
}
poison = botAI->FindConsumable(DEADLY_POISON_DISPLAYID);
if (!poison)
poison = botAI->FindConsumable(WOUND_POISON_DISPLAYID);
if (!poison)
poison = botAI->FindConsumable(INSTANT_POISON_DISPLAYID);
if (poison)
{
@@ -177,8 +141,8 @@ bool ImbueWithOilAction::Execute(Event event)
return true;
}
static const uint32 uPrioritizedHealingItemIds[19] = {
HEALTHSTONE,
static const uint32 uPriorizedHealingItemIds[19] = {
HEALTHSTONE_DISPLAYID,
FEL_REGENERATION_POTION,
SUPER_HEALING_POTION,
CRYSTAL_HEALING_POTION,
@@ -218,9 +182,9 @@ bool TryEmergencyAction::Execute(Event event)
}
// Else loop over the list of health consumable to pick one
for (uint8 i = 0; i < std::size(uPrioritizedHealingItemIds); ++i)
for (uint8 i = 0; i < std::size(uPriorizedHealingItemIds); ++i)
{
if (Item* healthItem = botAI->FindConsumable(uPrioritizedHealingItemIds[i]))
if (Item* healthItem = botAI->FindConsumable(uPriorizedHealingItemIds[i]))
{
botAI->ImbueItem(healthItem);
}

View File

@@ -98,10 +98,10 @@ bool InviteNearbyToGroupAction::Execute(Event event)
if (group && group->isRaidGroup())
bot->Say(BOT_TEXT2("join_raid", placeholders),
(bot->GetTeamId() == TEAM_ALLIANCE ? LANG_COMMON : LANG_ORCISH));
(bot->GetTeamId() == ALLIANCE ? LANG_COMMON : LANG_ORCISH));
else
bot->Say(BOT_TEXT2("join_group", placeholders),
(bot->GetTeamId() == TEAM_ALLIANCE ? LANG_COMMON : LANG_ORCISH));
(bot->GetTeamId() == ALLIANCE ? LANG_COMMON : LANG_ORCISH));
}
return Invite(bot, player);

View File

@@ -63,7 +63,7 @@ public:
}
bool Execute(Event event) override;
bool isUseful() override { return bot->GetGuildId() && InviteNearbyToGroupAction::isUseful(); };
bool isUseful() { return bot->GetGuildId() && InviteNearbyToGroupAction::isUseful(); };
private:
std::vector<Player*> getGuildMembers();

View File

@@ -224,37 +224,3 @@ bool RollUniqueCheck(ItemTemplate const* proto, Player* bot)
}
return false; // Item is not equipped or in bags, roll for it
}
bool RollAction::Execute(Event event)
{
std::string link = event.getParam();
if (link.empty())
{
bot->DoRandomRoll(0,100);
return false;
}
ItemIds itemIds = chat->parseItems(link);
if (itemIds.empty())
return false;
uint32 itemId = *itemIds.begin();
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
if (!proto)
{
return false;
}
std::string itemUsageParam;
itemUsageParam = std::to_string(itemId);
ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", itemUsageParam);
switch (proto->Class)
{
case ITEM_CLASS_WEAPON:
case ITEM_CLASS_ARMOR:
if (usage == ITEM_USAGE_EQUIP || usage == ITEM_USAGE_REPLACE || usage == ITEM_USAGE_BAD_EQUIP)
{
bot->DoRandomRoll(0,100);
}
}
return true;
}

View File

@@ -37,12 +37,4 @@ public:
bool Execute(Event event) override;
};
class RollAction : public Action
{
public:
RollAction(PlayerbotAI* botAI) : Action(botAI, "roll") {}
bool Execute(Event event) override;
};
#endif

View File

@@ -1801,6 +1801,7 @@ const Movement::PointsArray MovementAction::SearchForBestPath(float x, float y,
bool FleeAction::Execute(Event event)
{
// return Flee(AI_VALUE(Unit*, "current target"));
return MoveAway(AI_VALUE(Unit*, "current target"), sPlayerbotAIConfig->fleeDistance, true);
}
@@ -1810,10 +1811,6 @@ bool FleeAction::isUseful()
{
return false;
}
Unit* target = AI_VALUE(Unit*, "current target");
if (target && target->IsInWorld() && !bot->IsWithinMeleeRange(target))
return false;
return true;
}

View File

@@ -40,13 +40,13 @@ bool DrinkAction::Execute(Event event)
float delay;
if (!bot->InBattleground())
delay = 18000.0f * (100 - p) / 100.0f;
delay = 27000.0f * (100 - p) / 100.0f;
else
delay = 12000.0f * (100 - p) / 100.0f;
delay = 20000.0f * (100 - p) / 100.0f;
botAI->SetNextCheckDelay(delay);
bot->AddAura(25990, bot);
bot->AddAura(24707, bot);
return true;
// return botAI->CastSpell(24707, bot);
}
@@ -90,13 +90,13 @@ bool EatAction::Execute(Event event)
float delay;
if (!bot->InBattleground())
delay = 18000.0f * (100 - p) / 100.0f;
delay = 27000.0f * (100 - p) / 100.0f;
else
delay = 12000.0f * (100 - p) / 100.0f;
delay = 20000.0f * (100 - p) / 100.0f;
botAI->SetNextCheckDelay(delay);
bot->AddAura(25990, bot);
bot->AddAura(24707, bot);
return true;
}

View File

@@ -1,376 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "PetAction.h"
#include <algorithm>
#include <iomanip>
#include <sstream>
#include "Pet.h"
#include "SpellMgr.h"
#include "DBCStructure.h"
#include "Log.h"
#include "ObjectMgr.h"
#include "Player.h"
#include "PlayerbotAI.h"
#include "PlayerbotFactory.h"
#include <random>
#include <cctype>
#include "WorldSession.h"
bool IsExoticPet(const CreatureTemplate* creature)
{
// Use the IsExotic() method from CreatureTemplate
return creature && creature->IsExotic();
}
bool HasBeastMastery(Player* bot)
{
// Beast Mastery talent aura ID for WotLK is 53270
return bot->HasAura(53270);
}
bool PetAction::Execute(Event event)
{
std::string param = event.getParam();
std::istringstream iss(param);
std::string mode, value;
iss >> mode;
std::getline(iss, value);
value.erase(0, value.find_first_not_of(" ")); // trim leading spaces
bool found = false;
// Reset lastPetName/Id each time
lastPetName = "";
lastPetId = 0;
if (mode == "name" && !value.empty())
{
found = SetPetByName(value);
}
else if (mode == "id" && !value.empty())
{
try
{
uint32 id = std::stoul(value);
found = SetPetById(id);
}
catch (...)
{
botAI->TellError("Invalid pet id.");
}
}
else if (mode == "family" && !value.empty())
{
found = SetPetByFamily(value);
}
else if (mode == "rename" && !value.empty())
{
found = RenamePet(value);
}
else
{
botAI->TellError("Usage: pet name <name> | pet id <id> | pet family <family> | pet rename <new name> ");
return false;
}
if (!found)
return false;
// For non-rename commands, initialize pet and give feedback
if (mode != "rename")
{
Player* bot = botAI->GetBot();
PlayerbotFactory factory(bot, bot->GetLevel());
factory.InitPet();
factory.InitPetTalents();
if (!lastPetName.empty() && lastPetId != 0)
{
std::ostringstream oss;
oss << "Pet changed to " << lastPetName << ", ID: " << lastPetId << ".";
botAI->TellMaster(oss.str());
}
else
{
botAI->TellMaster("Pet changed and initialized!");
}
}
return true;
}
bool PetAction::SetPetByName(const std::string& name)
{
// Convert the input to lowercase for case-insensitive comparison
std::string lowerName = name;
std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), ::tolower);
CreatureTemplateContainer const* creatures = sObjectMgr->GetCreatureTemplates();
Player* bot = botAI->GetBot();
for (auto itr = creatures->begin(); itr != creatures->end(); ++itr)
{
const CreatureTemplate& creature = itr->second;
std::string creatureName = creature.Name;
std::transform(creatureName.begin(), creatureName.end(), creatureName.begin(), ::tolower);
// Only match if names match (case-insensitive)
if (creatureName == lowerName)
{
// Check if the pet is tameable at all
if (!creature.IsTameable(true))
continue;
// Exotic pet check with talent requirement
if (IsExoticPet(&creature) && !HasBeastMastery(bot))
{
botAI->TellError("I cannot use exotic pets unless I have the Beast Mastery talent.");
return false;
}
// Final tameable check based on hunter's actual ability
if (!creature.IsTameable(bot->CanTameExoticPets()))
continue;
lastPetName = creature.Name;
lastPetId = creature.Entry;
return CreateAndSetPet(creature.Entry);
}
}
botAI->TellError("No tameable pet found with name: " + name);
return false;
}
bool PetAction::SetPetById(uint32 id)
{
CreatureTemplate const* creature = sObjectMgr->GetCreatureTemplate(id);
Player* bot = botAI->GetBot();
if (creature)
{
// Check if the pet is tameable at all
if (!creature->IsTameable(true))
{
botAI->TellError("No tameable pet found with id: " + std::to_string(id));
return false;
}
// Exotic pet check with talent requirement
if (IsExoticPet(creature) && !HasBeastMastery(bot))
{
botAI->TellError("I cannot use exotic pets unless I have the Beast Mastery talent.");
return false;
}
// Final tameable check based on hunter's actual ability
if (!creature->IsTameable(bot->CanTameExoticPets()))
{
botAI->TellError("No tameable pet found with id: " + std::to_string(id));
return false;
}
lastPetName = creature->Name;
lastPetId = creature->Entry;
return CreateAndSetPet(creature->Entry);
}
botAI->TellError("No tameable pet found with id: " + std::to_string(id));
return false;
}
bool PetAction::SetPetByFamily(const std::string& family)
{
std::string lowerFamily = family;
std::transform(lowerFamily.begin(), lowerFamily.end(), lowerFamily.begin(), ::tolower);
CreatureTemplateContainer const* creatures = sObjectMgr->GetCreatureTemplates();
Player* bot = botAI->GetBot();
std::vector<const CreatureTemplate*> candidates;
bool foundExotic = false;
for (auto itr = creatures->begin(); itr != creatures->end(); ++itr)
{
const CreatureTemplate& creature = itr->second;
if (!creature.IsTameable(true)) // allow exotics for search
continue;
CreatureFamilyEntry const* familyEntry = sCreatureFamilyStore.LookupEntry(creature.family);
if (!familyEntry)
continue;
std::string familyName = familyEntry->Name[0];
std::transform(familyName.begin(), familyName.end(), familyName.begin(), ::tolower);
if (familyName != lowerFamily)
continue;
// Exotic/BM check
if (IsExoticPet(&creature))
{
foundExotic = true;
if (!HasBeastMastery(bot))
continue;
}
if (!creature.IsTameable(bot->CanTameExoticPets()))
continue;
candidates.push_back(&creature);
}
if (candidates.empty())
{
if (foundExotic && !HasBeastMastery(bot))
botAI->TellError("I cannot use exotic pets unless I have the Beast Mastery talent.");
else
botAI->TellError("No tameable pet found with family: " + family);
return false;
}
// Randomly select one from candidates
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, candidates.size() - 1);
const CreatureTemplate* selected = candidates[dis(gen)];
lastPetName = selected->Name;
lastPetId = selected->Entry;
return CreateAndSetPet(selected->Entry);
}
bool PetAction::RenamePet(const std::string& newName)
{
Player* bot = botAI->GetBot();
Pet* pet = bot->GetPet();
if (!pet)
{
botAI->TellError("You have no pet to rename.");
return false;
}
// Length check (WoW max pet name is 12 characters)
if (newName.empty() || newName.length() > 12)
{
botAI->TellError("Pet name must be between 1 and 12 alphabetic characters.");
return false;
}
// Alphabetic character check
for (char c : newName)
{
if (!std::isalpha(static_cast<unsigned char>(c)))
{
botAI->TellError("Pet name must only contain alphabetic characters (A-Z, a-z).");
return false;
}
}
// Normalize case: capitalize first letter, lower the rest
std::string normalized = newName;
normalized[0] = std::toupper(normalized[0]);
for (size_t i = 1; i < normalized.size(); ++i)
normalized[i] = std::tolower(normalized[i]);
// Forbidden name check
if (sObjectMgr->IsReservedName(normalized))
{
botAI->TellError("That pet name is forbidden. Please choose another name.");
return false;
}
// Set the pet's name, save to DB, and send instant client update
pet->SetName(normalized);
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
bot->GetSession()->SendPetNameQuery(pet->GetGUID(), pet->GetEntry());
botAI->TellMaster("Your pet has been renamed to " + normalized + "!");
botAI->TellMaster("If you do not see the new name, please dismiss and recall your pet.");
// Dismiss pet
bot->RemovePet(nullptr, PET_SAVE_AS_CURRENT, true);
// Recall pet using Hunter's Call Pet spell (spellId 883)
if (bot->getClass() == CLASS_HUNTER && bot->HasSpell(883))
{
bot->CastSpell(bot, 883, true);
}
return true;
}
bool PetAction::CreateAndSetPet(uint32 creatureEntry)
{
Player* bot = botAI->GetBot();
if (bot->getClass() != CLASS_HUNTER || bot->GetLevel() < 10)
{
botAI->TellError("Only level 10+ hunters can have pets.");
return false;
}
CreatureTemplate const* creature = sObjectMgr->GetCreatureTemplate(creatureEntry);
if (!creature)
{
botAI->TellError("Creature template not found.");
return false;
}
// Remove current pet(s)
if (bot->GetPetStable() && bot->GetPetStable()->CurrentPet)
{
bot->RemovePet(nullptr, PET_SAVE_AS_CURRENT);
bot->RemovePet(nullptr, PET_SAVE_NOT_IN_SLOT);
}
if (bot->GetPetStable() && bot->GetPetStable()->GetUnslottedHunterPet())
{
bot->GetPetStable()->UnslottedPets.clear();
bot->RemovePet(nullptr, PET_SAVE_AS_CURRENT);
bot->RemovePet(nullptr, PET_SAVE_NOT_IN_SLOT);
}
// Actually create the new pet
Pet* pet = bot->CreateTamedPetFrom(creatureEntry, 0);
if (!pet)
{
botAI->TellError("Failed to create pet.");
return false;
}
// Set pet level and add to world
pet->SetUInt32Value(UNIT_FIELD_LEVEL, bot->GetLevel() - 1);
pet->GetMap()->AddToMap(pet->ToCreature());
pet->SetUInt32Value(UNIT_FIELD_LEVEL, bot->GetLevel());
bot->SetMinion(pet, true);
pet->InitTalentForLevel();
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
bot->PetSpellInitialize();
// Set stats
pet->InitStatsForLevel(bot->GetLevel());
pet->SetLevel(bot->GetLevel());
pet->SetPower(POWER_HAPPINESS, pet->GetMaxPower(Powers(POWER_HAPPINESS)));
pet->SetHealth(pet->GetMaxHealth());
// Enable autocast for active spells
for (PetSpellMap::const_iterator itr = pet->m_spells.begin(); itr != pet->m_spells.end(); ++itr)
{
if (itr->second.state == PETSPELL_REMOVED)
continue;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first);
if (!spellInfo)
continue;
if (spellInfo->IsPassive())
continue;
pet->ToggleAutocast(spellInfo, true);
}
return true;
}

View File

@@ -1,34 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_PETACTION_H
#define _PLAYERBOT_PETACTION_H
#include <string>
#include "Action.h"
#include "PlayerbotFactory.h"
class PlayerbotAI;
class PetAction : public Action
{
public:
PetAction(PlayerbotAI* botAI) : Action(botAI, "pet") {}
bool Execute(Event event) override;
private:
bool SetPetByName(const std::string& name);
bool SetPetById(uint32 id);
bool SetPetByFamily(const std::string& family);
bool RenamePet(const std::string& newName);
bool CreateAndSetPet(uint32 creatureEntry);
std::string lastPetName;
uint32 lastPetId = 0;
};
#endif

View File

@@ -4,7 +4,7 @@
*/
#include "ReleaseSpiritAction.h"
#include "ServerFacade.h"
#include "Event.h"
#include "GameGraveyard.h"
#include "NearestNpcsValue.h"
@@ -13,7 +13,6 @@
#include "Playerbots.h"
#include "ServerFacade.h"
#include "Corpse.h"
#include "Log.h"
// ReleaseSpiritAction implementation
bool ReleaseSpiritAction::Execute(Event event)
@@ -248,19 +247,3 @@ void RepopAction::PerformGraveyardTeleport(const GraveyardStruct* graveyard) con
RESET_AI_VALUE(bool, "combat::self target");
RESET_AI_VALUE(WorldPosition, "current position");
}
// SelfResurrectAction implementation for Warlock's Soulstone Resurrection/Shaman's Reincarnation
bool SelfResurrectAction::Execute(Event event)
{
if (!bot->IsAlive() && bot->GetUInt32Value(PLAYER_SELF_RES_SPELL))
{
WorldPacket packet(CMSG_SELF_RES);
bot->GetSession()->HandleSelfResOpcode(packet);
return true;
}
return false;
}
bool SelfResurrectAction::isUseful()
{
return !bot->IsAlive() && bot->GetUInt32Value(PLAYER_SELF_RES_SPELL);
}

View File

@@ -56,13 +56,4 @@ private:
void PerformGraveyardTeleport(const GraveyardStruct* graveyard) const;
};
// SelfResurrectAction action registration
class SelfResurrectAction : public Action
{
public:
SelfResurrectAction(PlayerbotAI* ai) : Action(ai, "self resurrect") {}
virtual bool Execute(Event event) override;
bool isUseful() override;
};
#endif

View File

@@ -39,7 +39,7 @@ bool RevealGatheringItemAction::Execute(Event event)
std::list<GameObject*> targets;
AnyGameObjectInObjectRangeCheck u_check(bot, sPlayerbotAIConfig->grindDistance);
Acore::GameObjectListSearcher<AnyGameObjectInObjectRangeCheck> searcher(bot, targets, u_check);
Cell::VisitObjects(bot, searcher, sPlayerbotAIConfig->reactDistance);
Cell::VisitAllObjects(bot, searcher, sPlayerbotAIConfig->reactDistance);
std::vector<GameObject*> result;
for (GameObject* go : targets)

View File

@@ -73,7 +73,7 @@ bool TaxiAction::Execute(Event event)
{
if (Creature* npcPtr = ObjectAccessor::GetCreature(*bot, npcGuid))
if (!movement.taxiNodes.empty())
bot->ActivateTaxiPathTo(movement.taxiNodes, npcPtr, 0);
bot->ActivateTaxiPathTo(movement.taxiNodes, npcPtr);
},
delay);
botAI->SetNextCheckDelay(delay + 50);
@@ -114,7 +114,7 @@ bool TaxiAction::Execute(Event event)
return bot->ActivateTaxiPathTo({entry->from, entry->to}, npc, 0);
}
if (!movement.taxiNodes.empty() && !bot->ActivateTaxiPathTo(movement.taxiNodes, npc, 0))
if (!movement.taxiNodes.empty() && !bot->ActivateTaxiPathTo(movement.taxiNodes, npc))
{
movement.taxiNodes.clear();
movement.Set(nullptr);

View File

@@ -1,113 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "TellGlyphsAction.h"
#include "Event.h"
#include "Playerbots.h"
#include "ObjectMgr.h"
#include "SpellMgr.h"
#include "World.h"
#include <unordered_map>
#include <sstream>
namespace
{
// -----------------------------------------------------------------
// Cache : GlyphID (MiscValue) -> ItemTemplate*
// -----------------------------------------------------------------
std::unordered_map<uint32, ItemTemplate const*> s_GlyphItemCache;
void BuildGlyphItemCache()
{
if (!s_GlyphItemCache.empty())
return;
ItemTemplateContainer const* store = sObjectMgr->GetItemTemplateStore();
for (auto const& kv : *store) // C++17 : range-for sur map
{
ItemTemplate const* proto = &kv.second;
if (!proto || proto->Class != ITEM_CLASS_GLYPH)
continue;
for (uint32 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
{
uint32 spellId = proto->Spells[i].SpellId;
if (!spellId)
continue;
SpellInfo const* spell = sSpellMgr->GetSpellInfo(spellId);
if (!spell)
continue;
for (uint32 eff = 0; eff <= EFFECT_2; ++eff)
{
if (spell->Effects[eff].Effect != SPELL_EFFECT_APPLY_GLYPH)
continue;
uint32 glyphId = spell->Effects[eff].MiscValue;
if (glyphId)
s_GlyphItemCache[glyphId] = proto;
}
}
}
}
} // namespace
// -----------------------------------------------------------------
// Action
// -----------------------------------------------------------------
bool TellGlyphsAction::Execute(Event event)
{
//-----------------------------------------------------------------
// 1. who sended the wisp ? (source of event)
//-----------------------------------------------------------------
Player* sender = event.getOwner(); // API Event
if (!sender)
return false;
//-----------------------------------------------------------------
// 2. Generate glyphId cache -> item
//-----------------------------------------------------------------
BuildGlyphItemCache();
//-----------------------------------------------------------------
// 3. Look at the 6 glyphs sockets
//-----------------------------------------------------------------
std::ostringstream list;
bool first = true;
for (uint8 slot = 0; slot < MAX_GLYPH_SLOT_INDEX; ++slot)
{
uint32 glyphId = bot->GetGlyph(slot);
if (!glyphId)
continue;
auto it = s_GlyphItemCache.find(glyphId);
if (it == s_GlyphItemCache.end())
continue; // No glyph found (rare)
if (!first)
list << ", ";
// chat->FormatItem
list << chat->FormatItem(it->second);
first = false;
}
//-----------------------------------------------------------------
// 4. Send chat messages
//-----------------------------------------------------------------
if (first) // no glyphs
botAI->TellMaster("No glyphs equipped");
else
botAI->TellMaster(std::string("Glyphs: ") + list.str());
return true;
}

View File

@@ -1,21 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_TELLGLYPHSACTION_H
#define _PLAYERBOT_TELLGLYPHSACTION_H
#include "Action.h"
class TellGlyphsAction : public Action
{
public:
TellGlyphsAction(PlayerbotAI* ai, std::string const name = "glyphs")
: Action(ai, name) {}
bool Execute(Event event) override;
};
#endif

View File

@@ -4,6 +4,7 @@
*/
#include "TradeAction.h"
#include "ChatHelper.h"
#include "Event.h"
#include "ItemCountValue.h"
@@ -14,8 +15,11 @@ bool TradeAction::Execute(Event event)
{
std::string const text = event.getParam();
// Table with prefixes to be excluded from analysis
static const std::vector<std::string> excludedPrefixes = {"RPLL_H_"};
// If text starts with any excluded prefix, don't process it further.
for (const auto& prefix : sPlayerbotAIConfig->tradeActionExcludedPrefixes)
for (const auto& prefix : excludedPrefixes)
{
if (text.find(prefix) == 0)
return false;

View File

@@ -175,8 +175,6 @@ bool MaintenanceAction::Execute(Event event)
factory.InitAmmo();
factory.InitFood();
factory.InitReagents();
factory.InitConsumables();
factory.InitPotions();
factory.InitTalentsTree(true);
factory.InitPet();
factory.InitPetTalents();
@@ -186,8 +184,9 @@ bool MaintenanceAction::Execute(Event event)
factory.InitReputation();
factory.InitSpecialSpells();
factory.InitMounts();
factory.InitGlyphs(false);
factory.InitGlyphs(true);
factory.InitKeyring();
factory.InitPotions();
if (bot->GetLevel() >= sPlayerbotAIConfig->minEnchantingBotLevel)
factory.ApplyEnchantAndGemsNew();

View File

@@ -23,7 +23,7 @@ bool TravelAction::Execute(Event event)
std::list<Unit*> targets;
Acore::AnyUnitInObjectRangeCheck u_check(bot, sPlayerbotAIConfig->sightDistance * 2);
Acore::UnitListSearcher<Acore::AnyUnitInObjectRangeCheck> searcher(bot, targets, u_check);
Cell::VisitObjects(bot, searcher, sPlayerbotAIConfig->sightDistance);
Cell::VisitAllObjects(bot, searcher, sPlayerbotAIConfig->sightDistance);
for (Unit* unit : targets)
{

View File

@@ -238,24 +238,9 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni
{
targetFlag = TARGET_FLAG_NONE;
packet << targetFlag;
// Use the actual target if provided
if (unitTarget)
{
packet << unitTarget->GetGUID();
targetSelected = true;
// If the target is bot or is an enemy, say "on self"
if (unitTarget == bot || (unitTarget->IsHostileTo(bot)))
out << " on self";
else
out << " on " << unitTarget->GetName();
}
else
{
packet << bot->GetPackGUID();
targetSelected = true;
out << " on self";
}
packet << bot->GetPackGUID();
targetSelected = true;
out << " on self";
}
ItemTemplate const* proto = item->GetTemplate();

View File

@@ -110,7 +110,7 @@ bool SummonAction::SummonUsingGos(Player* summoner, Player* player)
std::list<GameObject*> targets;
AnyGameObjectInObjectRangeCheck u_check(summoner, sPlayerbotAIConfig->sightDistance);
Acore::GameObjectListSearcher<AnyGameObjectInObjectRangeCheck> searcher(summoner, targets, u_check);
Cell::VisitObjects(summoner, searcher, sPlayerbotAIConfig->sightDistance);
Cell::VisitAllObjects(summoner, searcher, sPlayerbotAIConfig->sightDistance);
for (GameObject* go : targets)
{
@@ -130,7 +130,7 @@ bool SummonAction::SummonUsingNpcs(Player* summoner, Player* player)
std::list<Unit*> targets;
Acore::AnyUnitInObjectRangeCheck u_check(summoner, sPlayerbotAIConfig->sightDistance);
Acore::UnitListSearcher<Acore::AnyUnitInObjectRangeCheck> searcher(summoner, targets, u_check);
Cell::VisitObjects(summoner, searcher, sPlayerbotAIConfig->sightDistance);
Cell::VisitAllObjects(summoner, searcher, sPlayerbotAIConfig->sightDistance);
for (Unit* unit : targets)
{

View File

@@ -1,18 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "PlayerbotAI.h"
#include "WipeAction.h"
bool WipeAction::Execute(Event event)
{
Player* master = event.getOwner();
if (botAI->GetMaster()->GetGUID() != event.getOwner()->GetGUID())
return false;
bot->Kill(bot, bot);
return true;
}

View File

@@ -1,24 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_WIPEACTION_H
#define _PLAYERBOT_WIPEACTION_H
#include "Action.h"
class PlayerbotAI;
class WipeAction : public Action
{
public:
WipeAction(PlayerbotAI* botAI) : Action(botAI, "wipe") {}
bool Execute(Event event) override;
private:
std::string bossName;
};
#endif

View File

@@ -4,6 +4,7 @@
*/
#include "WorldBuffAction.h"
#include "AiFactory.h"
#include "Event.h"
#include "Playerbots.h"
@@ -12,12 +13,11 @@ bool WorldBuffAction::Execute(Event event)
{
std::string const text = event.getParam();
std::vector<uint32> buffs = NeedWorldBuffs(bot); // Get matching buffs
for (auto& wb : buffs)
for (auto& wb : NeedWorldBuffs(bot))
{
bot->AddAura(wb, bot);
}
return false;
}
@@ -70,6 +70,7 @@ std::vector<uint32> WorldBuffAction::NeedWorldBuffs(Unit* unit)
// If tank, effectiveSpec remains unchanged
}
for (auto const& wb : sPlayerbotAIConfig->worldBuffs)
{
// Faction check

View File

@@ -40,7 +40,6 @@
#include "TradeStatusExtendedAction.h"
#include "UseMeetingStoneAction.h"
#include "NamedObjectContext.h"
#include "ReleaseSpiritAction.h"
class PlayerbotAI;
@@ -69,7 +68,6 @@ public:
creators["accept trade"] = &WorldPacketActionContext::accept_trade;
creators["trade status extended"] = &WorldPacketActionContext::trade_status_extended;
creators["store loot"] = &WorldPacketActionContext::store_loot;
creators["self resurrect"] = &WorldPacketActionContext::self_resurrect;
// quest
creators["talk to quest giver"] = &WorldPacketActionContext::turn_in_quest;
@@ -138,7 +136,6 @@ private:
static Action* tell_not_enough_money(PlayerbotAI* botAI) { return new TellMasterAction(botAI, "Not enough money"); }
static Action* tell_not_enough_reputation(PlayerbotAI* botAI) { return new TellMasterAction(botAI, "Not enough reputation"); }
static Action* tell_cannot_equip(PlayerbotAI* botAI) { return new InventoryChangeFailureAction(botAI); }
static Action* self_resurrect(PlayerbotAI* botAI) { return new SelfResurrectAction(botAI); }
// quest
static Action* quest_update_add_kill(PlayerbotAI* ai) { return new QuestUpdateAddKillAction(ai); }

View File

@@ -108,20 +108,14 @@ private:
static Trigger* blood_strike(PlayerbotAI* botAI) { return new BloodStrikeTrigger(botAI); }
static Trigger* plague_strike(PlayerbotAI* botAI) { return new PlagueStrikeDebuffTrigger(botAI); }
static Trigger* plague_strike_3s(PlayerbotAI* botAI) { return new PlagueStrike3sDebuffTrigger(botAI); }
static Trigger* dd_cd_and_plague_strike_3s(PlayerbotAI* botAI)
{
return new TwoTriggers(botAI, "death and decay cooldown", "plague strike 3s");
}
static Trigger* dd_cd_and_plague_strike_3s(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "death and decay cooldown", "plague strike 3s"); }
static Trigger* plague_strike_on_attacker(PlayerbotAI* botAI)
{
return new PlagueStrikeDebuffOnAttackerTrigger(botAI);
}
static Trigger* icy_touch(PlayerbotAI* botAI) { return new IcyTouchDebuffTrigger(botAI); }
static Trigger* icy_touch_3s(PlayerbotAI* botAI) { return new IcyTouch3sDebuffTrigger(botAI); }
static Trigger* dd_cd_and_icy_touch_3s(PlayerbotAI* botAI)
{
return new TwoTriggers(botAI, "death and decay cooldown", "icy touch 3s");
}
static Trigger* dd_cd_and_icy_touch_3s(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "death and decay cooldown", "icy touch 3s"); }
static Trigger* death_coil(PlayerbotAI* botAI) { return new DeathCoilTrigger(botAI); }
static Trigger* icy_touch_on_attacker(PlayerbotAI* botAI) { return new IcyTouchDebuffOnAttackerTrigger(botAI); }
static Trigger* improved_icy_talons(PlayerbotAI* botAI) { return new ImprovedIcyTalonsTrigger(botAI); }
@@ -146,10 +140,7 @@ private:
static Trigger* no_rune(PlayerbotAI* botAI) { return new NoRuneTrigger(botAI); }
static Trigger* freezing_fog(PlayerbotAI* botAI) { return new FreezingFogTrigger(botAI); }
static Trigger* no_desolation(PlayerbotAI* botAI) { return new DesolationTrigger(botAI); }
static Trigger* dd_cd_and_no_desolation(PlayerbotAI* botAI)
{
return new TwoTriggers(botAI, "death and decay cooldown", "no desolation");
}
static Trigger* dd_cd_and_no_desolation(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "death and decay cooldown", "no desolation"); }
static Trigger* death_and_decay_cooldown(PlayerbotAI* botAI) { return new DeathAndDecayCooldownTrigger(botAI); }
static Trigger* army_of_the_dead(PlayerbotAI* botAI) { return new ArmyOfTheDeadTrigger(botAI); }
};
@@ -274,45 +265,11 @@ private:
}
};
SharedNamedObjectContextList<Strategy> DKAiObjectContext::sharedStrategyContexts;
SharedNamedObjectContextList<Action> DKAiObjectContext::sharedActionContexts;
SharedNamedObjectContextList<Trigger> DKAiObjectContext::sharedTriggerContexts;
SharedNamedObjectContextList<UntypedValue> DKAiObjectContext::sharedValueContexts;
DKAiObjectContext::DKAiObjectContext(PlayerbotAI* botAI)
: AiObjectContext(botAI, sharedStrategyContexts, sharedActionContexts, sharedTriggerContexts, sharedValueContexts)
DKAiObjectContext::DKAiObjectContext(PlayerbotAI* botAI) : AiObjectContext(botAI)
{
}
void DKAiObjectContext::BuildSharedContexts()
{
BuildSharedStrategyContexts(sharedStrategyContexts);
BuildSharedActionContexts(sharedActionContexts);
BuildSharedTriggerContexts(sharedTriggerContexts);
BuildSharedValueContexts(sharedValueContexts);
}
void DKAiObjectContext::BuildSharedStrategyContexts(SharedNamedObjectContextList<Strategy>& strategyContexts)
{
AiObjectContext::BuildSharedStrategyContexts(strategyContexts);
strategyContexts.Add(new DeathKnightStrategyFactoryInternal());
strategyContexts.Add(new DeathKnightCombatStrategyFactoryInternal());
strategyContexts.Add(new DeathKnightDKBuffStrategyFactoryInternal());
}
void DKAiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList<Action>& actionContexts)
{
AiObjectContext::BuildSharedActionContexts(actionContexts);
actionContexts.Add(new DeathKnightAiObjectContextInternal());
}
void DKAiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextList<Trigger>& triggerContexts)
{
AiObjectContext::BuildSharedTriggerContexts(triggerContexts);
triggerContexts.Add(new DeathKnightTriggerFactoryInternal());
}
void DKAiObjectContext::BuildSharedValueContexts(SharedNamedObjectContextList<UntypedValue>& valueContexts)
{
AiObjectContext::BuildSharedValueContexts(valueContexts);
}

View File

@@ -14,17 +14,6 @@ class DKAiObjectContext : public AiObjectContext
{
public:
DKAiObjectContext(PlayerbotAI* botAI);
static void BuildSharedContexts();
static void BuildSharedStrategyContexts(SharedNamedObjectContextList<Strategy>& strategyContexts);
static void BuildSharedActionContexts(SharedNamedObjectContextList<Action>& actionContexts);
static void BuildSharedTriggerContexts(SharedNamedObjectContextList<Trigger>& triggerContexts);
static void BuildSharedValueContexts(SharedNamedObjectContextList<UntypedValue>& valueContexts);
static SharedNamedObjectContextList<Strategy> sharedStrategyContexts;
static SharedNamedObjectContextList<Action> sharedActionContexts;
static SharedNamedObjectContextList<Trigger> sharedTriggerContexts;
static SharedNamedObjectContextList<UntypedValue> sharedValueContexts;
};
#endif

View File

@@ -151,8 +151,6 @@ void CasterDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
void CasterDruidAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("hurricane channel check", NextAction::array(0, new NextAction("cancel channel", ACTION_HIGH + 2), nullptr)));
triggers.push_back(
new TriggerNode("medium aoe", NextAction::array(0, new NextAction("hurricane", ACTION_HIGH + 1), nullptr)));
triggers.push_back(new TriggerNode(

View File

@@ -8,6 +8,7 @@
#include "BearTankDruidStrategy.h"
#include "CasterDruidStrategy.h"
#include "CatDpsDruidStrategy.h"
#include "OffhealDruidCatStrategy.h"
#include "DruidActions.h"
#include "DruidBearActions.h"
#include "DruidCatActions.h"
@@ -17,7 +18,6 @@
#include "GenericDruidStrategy.h"
#include "HealDruidStrategy.h"
#include "MeleeDruidStrategy.h"
#include "OffhealDruidCatStrategy.h"
#include "Playerbots.h"
class DruidStrategyFactoryInternal : public NamedObjectContext<Strategy>
@@ -111,7 +111,6 @@ public:
creators["eclipse (lunar) cooldown"] = &DruidTriggerFactoryInternal::eclipse_lunar_cooldown;
creators["mangle (cat)"] = &DruidTriggerFactoryInternal::mangle_cat;
creators["ferocious bite time"] = &DruidTriggerFactoryInternal::ferocious_bite_time;
creators["hurricane channel check"] = &DruidTriggerFactoryInternal::hurricane_channel_check;
}
private:
@@ -148,7 +147,6 @@ private:
static Trigger* eclipse_lunar_cooldown(PlayerbotAI* ai) { return new EclipseLunarCooldownTrigger(ai); }
static Trigger* mangle_cat(PlayerbotAI* ai) { return new MangleCatTrigger(ai); }
static Trigger* ferocious_bite_time(PlayerbotAI* ai) { return new FerociousBiteTimeTrigger(ai); }
static Trigger* hurricane_channel_check(PlayerbotAI* ai) { return new HurricaneChannelCheckTrigger(ai); }
};
class DruidAiObjectContextInternal : public NamedObjectContext<Action>
@@ -326,44 +324,10 @@ private:
static Action* force_of_nature(PlayerbotAI* ai) { return new CastForceOfNatureAction(ai); }
};
SharedNamedObjectContextList<Strategy> DruidAiObjectContext::sharedStrategyContexts;
SharedNamedObjectContextList<Action> DruidAiObjectContext::sharedActionContexts;
SharedNamedObjectContextList<Trigger> DruidAiObjectContext::sharedTriggerContexts;
SharedNamedObjectContextList<UntypedValue> DruidAiObjectContext::sharedValueContexts;
DruidAiObjectContext::DruidAiObjectContext(PlayerbotAI* botAI)
: AiObjectContext(botAI, sharedStrategyContexts, sharedActionContexts, sharedTriggerContexts, sharedValueContexts)
DruidAiObjectContext::DruidAiObjectContext(PlayerbotAI* botAI) : AiObjectContext(botAI)
{
}
void DruidAiObjectContext::BuildSharedContexts()
{
BuildSharedStrategyContexts(sharedStrategyContexts);
BuildSharedActionContexts(sharedActionContexts);
BuildSharedTriggerContexts(sharedTriggerContexts);
BuildSharedValueContexts(sharedValueContexts);
}
void DruidAiObjectContext::BuildSharedStrategyContexts(SharedNamedObjectContextList<Strategy>& strategyContexts)
{
AiObjectContext::BuildSharedStrategyContexts(strategyContexts);
strategyContexts.Add(new DruidStrategyFactoryInternal());
strategyContexts.Add(new DruidDruidStrategyFactoryInternal());
}
void DruidAiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList<Action>& actionContexts)
{
AiObjectContext::BuildSharedActionContexts(actionContexts);
actionContexts.Add(new DruidAiObjectContextInternal());
}
void DruidAiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextList<Trigger>& triggerContexts)
{
AiObjectContext::BuildSharedTriggerContexts(triggerContexts);
triggerContexts.Add(new DruidTriggerFactoryInternal());
}
void DruidAiObjectContext::BuildSharedValueContexts(SharedNamedObjectContextList<UntypedValue>& valueContexts)
{
AiObjectContext::BuildSharedValueContexts(valueContexts);
}

View File

@@ -14,17 +14,6 @@ class DruidAiObjectContext : public AiObjectContext
{
public:
DruidAiObjectContext(PlayerbotAI* botAI);
static void BuildSharedContexts();
static void BuildSharedStrategyContexts(SharedNamedObjectContextList<Strategy>& strategyContexts);
static void BuildSharedActionContexts(SharedNamedObjectContextList<Action>& actionContexts);
static void BuildSharedTriggerContexts(SharedNamedObjectContextList<Trigger>& triggerContexts);
static void BuildSharedValueContexts(SharedNamedObjectContextList<UntypedValue>& valueContexts);
static SharedNamedObjectContextList<Strategy> sharedStrategyContexts;
static SharedNamedObjectContextList<Action> sharedActionContexts;
static SharedNamedObjectContextList<Trigger> sharedTriggerContexts;
static SharedNamedObjectContextList<UntypedValue> sharedValueContexts;
};
#endif

View File

@@ -4,7 +4,7 @@
*/
#include "DruidTriggers.h"
#include "Player.h"
#include "Playerbots.h"
bool MarkOfTheWildOnPartyTrigger::IsActive()
@@ -34,30 +34,3 @@ bool BearFormTrigger::IsActive() { return !botAI->HasAnyAuraOf(bot, "bear form",
bool TreeFormTrigger::IsActive() { return !botAI->HasAura(33891, bot); }
bool CatFormTrigger::IsActive() { return !botAI->HasAura("cat form", bot); }
const std::set<uint32> HurricaneChannelCheckTrigger::HURRICANE_SPELL_IDS = {
16914, // Hurricane Rank 1
17401, // Hurricane Rank 2
17402, // Hurricane Rank 3
27012, // Hurricane Rank 4
48467 // Hurricane Rank 5
};
bool HurricaneChannelCheckTrigger::IsActive()
{
Player* bot = botAI->GetBot();
// Check if the bot is channeling a spell
if (Spell* spell = bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL))
{
// Only trigger if the spell being channeled is Hurricane
if (HURRICANE_SPELL_IDS.count(spell->m_spellInfo->Id))
{
uint8 attackerCount = AI_VALUE(uint8, "attacker count");
return attackerCount < minEnemies;
}
}
// Not channeling Hurricane
return false;
}

View File

@@ -12,8 +12,6 @@
#include "PlayerbotAI.h"
#include "Playerbots.h"
#include "SharedDefines.h"
#include "Trigger.h"
#include <set>
class PlayerbotAI;
@@ -265,19 +263,4 @@ public:
}
};
class HurricaneChannelCheckTrigger : public Trigger
{
public:
HurricaneChannelCheckTrigger(PlayerbotAI* botAI, uint32 minEnemies = 2)
: Trigger(botAI, "hurricane channel check"), minEnemies(minEnemies)
{
}
bool IsActive() override;
protected:
uint32 minEnemies;
static const std::set<uint32> HURRICANE_SPELL_IDS;
};
#endif

View File

@@ -6,7 +6,6 @@
#include "GenericDruidNonCombatStrategy.h"
#include "Playerbots.h"
#include "AiFactory.h"
class GenericDruidNonCombatStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
@@ -110,43 +109,50 @@ void GenericDruidNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& trig
{
NonCombatStrategy::InitTriggers(triggers);
triggers.push_back(new TriggerNode("mark of the wild", NextAction::array(0, new NextAction("mark of the wild", 14.0f), nullptr)));
triggers.push_back(
new TriggerNode("mark of the wild", NextAction::array(0, new NextAction("mark of the wild", 14.0f), nullptr)));
// triggers.push_back(new TriggerNode("thorns", NextAction::array(0, new NextAction("thorns", 12.0f), nullptr)));
// triggers.push_back(new TriggerNode("cure poison", NextAction::array(0, new NextAction("abolish poison", 21.0f),
// nullptr)));
triggers.push_back(new TriggerNode("party member cure poison", NextAction::array(0, new NextAction("abolish poison on party", 20.0f), nullptr)));
triggers.push_back(new TriggerNode("party member dead", NextAction::array(0, new NextAction("revive", ACTION_CRITICAL_HEAL + 10), nullptr)));
triggers.push_back(new TriggerNode(
"party member cure poison", NextAction::array(0, new NextAction("abolish poison on party", 20.0f), nullptr)));
triggers.push_back(new TriggerNode(
"party member dead", NextAction::array(0, new NextAction("revive", ACTION_CRITICAL_HEAL + 10), nullptr)));
// triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("innervate", ACTION_EMERGENCY
// + 5), nullptr))); triggers.push_back(new TriggerNode("swimming", NextAction::array(0, new NextAction("aquatic
// form", 1.0f), nullptr)));
triggers.push_back(new TriggerNode("party member critical health", NextAction::array(0,
new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 7),
new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 6),
new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 5),
nullptr)));
triggers.push_back(new TriggerNode("party member low health", NextAction::array(0,
new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 5),
new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 4),
new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 3),
nullptr)));
triggers.push_back(new TriggerNode("party member medium health", NextAction::array(0,
new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 3),
new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 2),
new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 1),
nullptr)));
triggers.push_back(new TriggerNode("party member almost full health", NextAction::array(0,
new NextAction("wild growth on party", ACTION_LIGHT_HEAL + 3),
new NextAction("rejuvenation on party", ACTION_LIGHT_HEAL + 2),
nullptr)));
triggers.push_back(new TriggerNode("party member remove curse", NextAction::array(0,
new NextAction("remove curse on party", ACTION_DISPEL + 7),
nullptr)));
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("apply oil", 1.0f), nullptr)));
int specTab = AiFactory::GetPlayerSpecTab(botAI->GetBot());
if (specTab == 0 || specTab == 2) // Balance or Restoration
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("apply oil", 1.0f), nullptr)));
if (specTab == 1) // Feral
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("apply stone", 1.0f), nullptr)));
triggers.push_back(
new TriggerNode("party member critical health",
NextAction::array(0,
new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 7),
new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 6),
new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 5),
nullptr)));
triggers.push_back(
new TriggerNode("party member low health",
NextAction::array(0,
new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 5),
new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 4),
new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 3),
nullptr)));
triggers.push_back(
new TriggerNode("party member medium health",
NextAction::array(0, new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 3),
new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 2),
new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 1),
nullptr)));
triggers.push_back(
new TriggerNode("party member almost full health",
NextAction::array(0, new NextAction("wild growth on party", ACTION_LIGHT_HEAL + 3), new NextAction("rejuvenation on party", ACTION_LIGHT_HEAL + 2), NULL)));
triggers.push_back(
new TriggerNode("party member remove curse",
NextAction::array(0, new NextAction("remove curse on party", ACTION_DISPEL + 7), nullptr)));
}
GenericDruidBuffStrategy::GenericDruidBuffStrategy(PlayerbotAI* botAI) : NonCombatStrategy(botAI)
@@ -158,13 +164,11 @@ void GenericDruidBuffStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
NonCombatStrategy::InitTriggers(triggers);
triggers.push_back(new TriggerNode("mark of the wild on party", NextAction::array(0,
new NextAction("mark of the wild on party", 13.0f),
nullptr)));
triggers.push_back(new TriggerNode("thorns on main tank", NextAction::array(0,
new NextAction("thorns on main tank", 11.0f),
nullptr)));
triggers.push_back(new TriggerNode("thorns", NextAction::array(0,
new NextAction("thorns", 10.0f),
nullptr)));
triggers.push_back(
new TriggerNode("mark of the wild on party",
NextAction::array(0, new NextAction("mark of the wild on party", 13.0f), nullptr)));
triggers.push_back(new TriggerNode("thorns on main tank",
NextAction::array(0, new NextAction("thorns on main tank", 11.0f), nullptr)));
triggers.push_back(new TriggerNode("thorns",
NextAction::array(0, new NextAction("thorns", 10.0f), nullptr)));
}

View File

@@ -10,82 +10,106 @@
void BGStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("bg join", relevance), nullptr)));
triggers.push_back(new TriggerNode("bg invite active", NextAction::array(0, new NextAction("bg status check", relevance), nullptr)));
triggers.push_back(new TriggerNode("timer", NextAction::array(0, new NextAction("bg strategy check", relevance), nullptr)));
triggers.push_back(new TriggerNode("bg invite active",
NextAction::array(0, new NextAction("bg status check", relevance), nullptr)));
triggers.push_back(
new TriggerNode("timer", NextAction::array(0, new NextAction("bg strategy check", relevance), nullptr)));
}
BGStrategy::BGStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI) {}
void WarsongStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("bg active", NextAction::array(0, new NextAction("bg check flag", 70.0f), nullptr)));
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("bg use buff", 30.0f), nullptr)));
triggers.push_back(
new TriggerNode("low health", NextAction::array(0, new NextAction("bg use buff", 30.0f), nullptr)));
triggers.push_back(
new TriggerNode("low mana", NextAction::array(0, new NextAction("bg use buff", 30.0f), nullptr)));
triggers.push_back(new TriggerNode(
"enemy flagcarrier near", NextAction::array(0, new NextAction("attack enemy flag carrier", 80.0f), nullptr)));
triggers.push_back(new TriggerNode(
"team has flag", NextAction::array(0, new NextAction("bg protect fc", 75.0f), nullptr)));
triggers.push_back(new TriggerNode("player has flag",
NextAction::array(0, new NextAction("bg move to objective", 90.0f), nullptr)));
}
void AlteracStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{ /* placeholder */
}
void ArathiStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("bg active", NextAction::array(0, new NextAction("bg check flag", 70.0f), nullptr)));
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("bg use buff", 30.0f), nullptr)));
triggers.push_back(
new TriggerNode("low health", NextAction::array(0, new NextAction("bg use buff", 30.0f), nullptr)));
triggers.push_back(
new TriggerNode("low mana", NextAction::array(0, new NextAction("bg use buff", 30.0f), nullptr)));
}
void BattlegroundStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("bg waiting", NextAction::array(0, new NextAction("bg move to start", ACTION_BG), nullptr)));
triggers.push_back(new TriggerNode("bg active", NextAction::array(0, new NextAction("bg move to objective", ACTION_BG), nullptr)));
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("bg check objective", ACTION_BG + 1), nullptr)));
triggers.push_back(new TriggerNode("dead", NextAction::array(0, new NextAction("bg reset objective force", ACTION_EMERGENCY), nullptr)));
triggers.push_back(
new TriggerNode("bg waiting", NextAction::array(0, new NextAction("bg move to start", 1.0f), nullptr)));
triggers.push_back(
new TriggerNode("bg active", NextAction::array(0, new NextAction("bg move to objective", 1.0f), nullptr)));
triggers.push_back(
new TriggerNode("often", NextAction::array(0, new NextAction("bg check objective", 10.0f), nullptr)));
// triggers.push_back(new TriggerNode("enemy flagcarrier near", NextAction::array(0, new NextAction("attack enemy
// flag carrier", 80.0f), nullptr))); triggers.push_back(new TriggerNode("team flagcarrier near",
// NextAction::array(0, new NextAction("bg protect fc", 40.0f), nullptr))); triggers.push_back(new
// TriggerNode("player has flag", NextAction::array(0, new NextAction("bg move to objective", 90.0f), nullptr)));
}
void WarsongStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("bg active", NextAction::array(0, new NextAction("bg check flag", ACTION_EMERGENCY ), nullptr)));
triggers.push_back(new TriggerNode("enemy flagcarrier near", NextAction::array(0, new NextAction("attack enemy flag carrier", ACTION_RAID + 1.0f), nullptr)));
triggers.push_back(new TriggerNode("team flagcarrier near", NextAction::array(0, new NextAction("bg protect fc", ACTION_RAID), nullptr)));
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("bg use buff", ACTION_BG), nullptr)));
triggers.push_back(new TriggerNode("low health", NextAction::array(0, new NextAction("bg use buff", ACTION_MOVE), nullptr)));
triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("bg use buff", ACTION_MOVE), nullptr)));
triggers.push_back(new TriggerNode("player has flag", NextAction::array(0, new NextAction("bg move to objective", ACTION_EMERGENCY), nullptr)));
triggers.push_back(new TriggerNode("timer bg", NextAction::array(0, new NextAction("bg reset objective force", ACTION_EMERGENCY), nullptr)));
}
void AlteracStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("alliance no snowfall gy", NextAction::array(0, new NextAction("bg move to objective", ACTION_EMERGENCY), nullptr)));
triggers.push_back(new TriggerNode("timer bg", NextAction::array(0, new NextAction("bg reset objective force", ACTION_EMERGENCY), nullptr)));
}
void ArathiStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("bg active", NextAction::array(0, new NextAction("bg check flag", ACTION_EMERGENCY), nullptr)));
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("bg use buff", ACTION_BG), nullptr)));
triggers.push_back(new TriggerNode("low health", NextAction::array(0, new NextAction("bg use buff", ACTION_MOVE), nullptr)));
triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("bg use buff", ACTION_MOVE), nullptr)));
}
void EyeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("bg active", NextAction::array(0, new NextAction("bg check flag", ACTION_EMERGENCY), nullptr)));
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("bg use buff", ACTION_BG), nullptr)));
triggers.push_back(new TriggerNode("low health", NextAction::array(0, new NextAction("bg use buff", ACTION_MOVE), nullptr)));
triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("bg use buff", ACTION_MOVE), nullptr)));
triggers.push_back(new TriggerNode("enemy flagcarrier near", NextAction::array(0, new NextAction("attack enemy flag carrier", ACTION_RAID), nullptr)));
triggers.push_back(new TriggerNode("player has flag",NextAction::array(0, new NextAction("bg move to objective", ACTION_EMERGENCY), nullptr)));
triggers.push_back(
new TriggerNode("bg active", NextAction::array(0, new NextAction("bg check flag", ACTION_MOVE + 7.0f), nullptr)));
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("bg use buff", ACTION_MOVE), nullptr)));
triggers.push_back(
new TriggerNode("low health", NextAction::array(0, new NextAction("bg use buff", ACTION_MOVE), nullptr)));
triggers.push_back(
new TriggerNode("low mana", NextAction::array(0, new NextAction("bg use buff", ACTION_MOVE), nullptr)));
triggers.push_back(new TriggerNode(
"enemy flagcarrier near", NextAction::array(0, new NextAction("attack enemy flag carrier", ACTION_MOVE + 8.0f), nullptr)));
triggers.push_back(new TriggerNode("player has flag",
NextAction::array(0, new NextAction("bg move to objective", ACTION_MOVE + 9.0f), nullptr)));
}
//TODO: Do Priorities
void IsleStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("bg active", NextAction::array(0, new NextAction("bg check flag", ACTION_MOVE), nullptr)));
triggers.push_back(new TriggerNode("timer", NextAction::array(0, new NextAction("enter vehicle", ACTION_MOVE + 8.0f), nullptr)));
triggers.push_back(new TriggerNode("random", NextAction::array(0, new NextAction("leave vehicle", ACTION_MOVE + 7.0f), nullptr)));
triggers.push_back(new TriggerNode("in vehicle", NextAction::array(0, new NextAction("hurl boulder", ACTION_MOVE + 9.0f), nullptr)));
triggers.push_back(new TriggerNode("in vehicle", NextAction::array(0, new NextAction("fire cannon", ACTION_MOVE + 9.0f), nullptr)));
triggers.push_back(new TriggerNode("in vehicle", NextAction::array(0, new NextAction("napalm", ACTION_MOVE + 9.0f), nullptr)));
triggers.push_back(new TriggerNode("enemy is close", NextAction::array(0, new NextAction("steam blast", ACTION_MOVE + 9.0f), nullptr)));
triggers.push_back(new TriggerNode("in vehicle", NextAction::array(0, new NextAction("ram", ACTION_MOVE + 9.0f), nullptr)));
triggers.push_back(new TriggerNode("enemy is close", NextAction::array(0, new NextAction("ram", ACTION_MOVE + 9.1f), nullptr)));
triggers.push_back(new TriggerNode("enemy out of melee", NextAction::array(0, new NextAction("steam rush", ACTION_MOVE + 9.2f), nullptr)));
triggers.push_back(new TriggerNode("in vehicle", NextAction::array(0, new NextAction("incendiary rocket", ACTION_MOVE + 9.0f), nullptr)));
triggers.push_back(new TriggerNode("in vehicle", NextAction::array(0, new NextAction("rocket blast", ACTION_MOVE + 9.0f), nullptr)));
triggers.push_back(
new TriggerNode("bg active", NextAction::array(0, new NextAction("bg check flag", 70.0f), nullptr)));
triggers.push_back(new TriggerNode("timer", NextAction::array(0, new NextAction("enter vehicle", 85.0f), nullptr)));
triggers.push_back(
new TriggerNode("random", NextAction::array(0, new NextAction("leave vehicle", 80.0f), nullptr)));
triggers.push_back(
new TriggerNode("in vehicle", NextAction::array(0, new NextAction("hurl boulder", 70.0f), nullptr)));
triggers.push_back(
new TriggerNode("in vehicle", NextAction::array(0, new NextAction("fire cannon", 70.0f), nullptr)));
triggers.push_back(new TriggerNode("in vehicle", NextAction::array(0, new NextAction("napalm", 70.0f), nullptr)));
triggers.push_back(
new TriggerNode("enemy is close", NextAction::array(0, new NextAction("steam blast", 80.0f), nullptr)));
triggers.push_back(new TriggerNode("in vehicle", NextAction::array(0, new NextAction("ram", 70.0f), nullptr)));
triggers.push_back(new TriggerNode("enemy is close", NextAction::array(0, new NextAction("ram", 79.0f), nullptr)));
triggers.push_back(
new TriggerNode("enemy out of melee", NextAction::array(0, new NextAction("steam rush", 81.0f), nullptr)));
triggers.push_back(
new TriggerNode("in vehicle", NextAction::array(0, new NextAction("incendiary rocket", 70.0f), nullptr)));
triggers.push_back(
new TriggerNode("in vehicle", NextAction::array(0, new NextAction("rocket blast", 70.0f), nullptr)));
// this is bugged: it doesn't work, and stops glaive throw working (which is needed to take down gate)
// triggers.push_back(new TriggerNode("in vehicle", NextAction::array(0, new NextAction("blade salvo", ACTION_MOVE + 9.0f), nullptr)));
triggers.push_back(new TriggerNode("in vehicle", NextAction::array(0, new NextAction("glaive throw", ACTION_MOVE + 9.0f), nullptr)));
// triggers.push_back(
// new TriggerNode("in vehicle", NextAction::array(0, new NextAction("blade salvo", 71.0f), nullptr)));
triggers.push_back(
new TriggerNode("in vehicle", NextAction::array(0, new NextAction("glaive throw", 70.0f), nullptr)));
}
void ArenaStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("no possible targets", NextAction::array(0, new NextAction("arena tactics", ACTION_BG), nullptr)));
new TriggerNode("no possible targets", NextAction::array(0, new NextAction("arena tactics", 1.0f), nullptr)));
}

View File

@@ -100,12 +100,6 @@ void ChatCommandHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
new TriggerNode("unlock items", NextAction::array(0, new NextAction("unlock items", relevance), nullptr)));
triggers.push_back(
new TriggerNode("unlock traded item", NextAction::array(0, new NextAction("unlock traded item", relevance), nullptr)));
triggers.push_back(
new TriggerNode("wipe", NextAction::array(0, new NextAction("wipe", relevance), nullptr)));
triggers.push_back(new TriggerNode("pet", NextAction::array(0, new NextAction("pet", relevance), nullptr)));
triggers.push_back(new TriggerNode("glyphs", NextAction::array(0, new NextAction("glyphs", relevance), nullptr))); // Added for custom Glyphs
triggers.push_back(new TriggerNode("glyph equip", NextAction::array(0, new NextAction("glyph equip", relevance), nullptr))); // Added for custom Glyphs
triggers.push_back(new TriggerNode("roll", NextAction::array(0, new NextAction("roll", relevance), nullptr)));
}
ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI)
@@ -185,7 +179,4 @@ ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : Pas
supported.push_back("qi");
supported.push_back("unlock items");
supported.push_back("unlock traded item");
supported.push_back("pet");
supported.push_back("glyphs"); // Added for custom Glyphs
supported.push_back("glyph equip"); // Added for custom Glyphs
}

View File

@@ -25,8 +25,6 @@ void DeadStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
new TriggerNode("falling far", NextAction::array(0, new NextAction("repop", relevance + 1.f), nullptr)));
triggers.push_back(
new TriggerNode("location stuck", NextAction::array(0, new NextAction("repop", relevance + 1), nullptr)));
triggers.push_back(new TriggerNode(
"can self resurrect", NextAction::array(0, new NextAction("self resurrect", relevance + 2.0f), nullptr)));
}
DeadStrategy::DeadStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI) {}

View File

@@ -1,69 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "BeastMasteryHunterStrategy.h"
#include "Playerbots.h"
// ===== Action Node Factory =====
class BeastMasteryHunterStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
BeastMasteryHunterStrategyActionNodeFactory()
{
creators["auto shot"] = &auto_shot;
creators["kill command"] = &kill_command;
creators["kill shot"] = &kill_shot;
creators["viper sting"] = &viper_sting;
creators["serpent sting"] = serpent_sting;
creators["aimed shot"] = &aimed_shot;
creators["arcane shot"] = &arcane_shot;
creators["steady shot"] = &steady_shot;
creators["multi-shot"] = &multi_shot;
creators["volley"] = &volley;
}
private:
static ActionNode* auto_shot(PlayerbotAI*) { return new ActionNode("auto shot", nullptr, nullptr, nullptr); }
static ActionNode* kill_command(PlayerbotAI*) { return new ActionNode("kill command", nullptr, nullptr, nullptr); }
static ActionNode* kill_shot(PlayerbotAI*) { return new ActionNode("kill shot", nullptr, nullptr, nullptr); }
static ActionNode* viper_sting(PlayerbotAI*) { return new ActionNode("viper sting", nullptr, nullptr, nullptr); }
static ActionNode* serpent_sting(PlayerbotAI*) { return new ActionNode("serpent sting", nullptr, nullptr, nullptr); }
static ActionNode* aimed_shot(PlayerbotAI*) { return new ActionNode("aimed shot", nullptr, nullptr, nullptr); }
static ActionNode* arcane_shot(PlayerbotAI*) { return new ActionNode("arcane shot", nullptr, nullptr, nullptr); }
static ActionNode* steady_shot(PlayerbotAI*) { return new ActionNode("steady shot", nullptr, nullptr, nullptr); }
static ActionNode* multi_shot(PlayerbotAI*) { return new ActionNode("multi shot", nullptr, nullptr, nullptr); }
static ActionNode* volley(PlayerbotAI*) { return new ActionNode("volley", nullptr, nullptr, nullptr); }
};
// ===== Single Target Strategy =====
BeastMasteryHunterStrategy::BeastMasteryHunterStrategy(PlayerbotAI* botAI) : GenericHunterStrategy(botAI)
{
actionNodeFactories.Add(new BeastMasteryHunterStrategyActionNodeFactory());
}
// ===== Default Actions =====
NextAction** BeastMasteryHunterStrategy::getDefaultActions()
{
return NextAction::array(0, new NextAction("bestial wrath", 19.0f),
new NextAction("kill command", 5.7f),
new NextAction("kill shot", 5.6f),
new NextAction("serpent sting", 5.5f),
new NextAction("aimed shot", 5.4f),
new NextAction("arcane shot", 5.3f),
new NextAction("steady shot", 5.2f),
new NextAction("auto shot", 5.1f), nullptr);
}
// ===== Trigger Initialization ===
void BeastMasteryHunterStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
GenericHunterStrategy::InitTriggers(triggers);
triggers.push_back(new TriggerNode("intimidation", NextAction::array(0, new NextAction("intimidation", 40.0f), nullptr)));
triggers.push_back(new TriggerNode("kill command", NextAction::array(0, new NextAction("kill command", 18.5f), nullptr)));
triggers.push_back(new TriggerNode("target critical health", NextAction::array(0, new NextAction("kill shot", 18.0f), nullptr)));
triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("viper sting", 17.5f), nullptr)));
triggers.push_back(new TriggerNode("no stings", NextAction::array(0, new NextAction("serpent sting", 17.0f), nullptr)));
triggers.push_back(new TriggerNode("serpent sting on attacker", NextAction::array(0, new NextAction("serpent sting on attacker", 16.5f), nullptr)));
}

View File

@@ -1,24 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_BEASTMASTERYHUNTERSTRATEGY_H
#define _PLAYERBOT_BEASTMASTERYHUNTERSTRATEGY_H
#include "GenericHunterStrategy.h"
#include "CombatStrategy.h"
class PlayerbotAI;
class BeastMasteryHunterStrategy : public GenericHunterStrategy
{
public:
BeastMasteryHunterStrategy(PlayerbotAI* botAI);
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "bm"; }
NextAction** getDefaultActions() override;
};
#endif

View File

@@ -0,0 +1,89 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "DpsHunterStrategy.h"
#include "Playerbots.h"
class DpsHunterStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
DpsHunterStrategyActionNodeFactory()
{
creators["aimed shot"] = &aimed_shot;
creators["steady shot"] = &steady_shot;
}
private:
static ActionNode* aimed_shot([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("aimed shot",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("multi-shot"), nullptr),
/*C*/ nullptr);
}
static ActionNode* steady_shot([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("steady shot",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("arcane shot"), nullptr),
/*C*/ nullptr);
}
};
DpsHunterStrategy::DpsHunterStrategy(PlayerbotAI* botAI) : GenericHunterStrategy(botAI)
{
actionNodeFactories.Add(new DpsHunterStrategyActionNodeFactory());
}
NextAction** DpsHunterStrategy::getDefaultActions()
{
return NextAction::array(
0,
new NextAction("explosive shot", ACTION_HIGH + 1.0f),
new NextAction("kill shot", ACTION_DEFAULT + 0.8f),
new NextAction("chimera shot", ACTION_DEFAULT + 0.6f),
new NextAction("aimed shot", ACTION_DEFAULT + 0.5f),
new NextAction("silencing shot", ACTION_DEFAULT + 0.4f),
new NextAction("kill command", ACTION_DEFAULT + 0.3f),
// new NextAction("arcane shot", ACTION_DEFAULT + 0.2f),
new NextAction("steady shot", ACTION_DEFAULT + 0.1f),
new NextAction("auto shot", ACTION_DEFAULT), nullptr);
}
void DpsHunterStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
GenericHunterStrategy::InitTriggers(triggers);
triggers.push_back(
new TriggerNode("black arrow", NextAction::array(0, new NextAction("black arrow", 19.0f), nullptr)));
triggers.push_back(
new TriggerNode("low mana", NextAction::array(0, new NextAction("viper sting", 23.0f), nullptr)));
triggers.push_back(
new TriggerNode("hunter's mark", NextAction::array(0, new NextAction("hunter's mark", 31.0f), nullptr)));
triggers.push_back(new TriggerNode("concussive shot on snare target",
NextAction::array(0, new NextAction("concussive shot", 20.0f), nullptr)));
// triggers.push_back(new TriggerNode("no pet", NextAction::array(0, new NextAction("call pet", 21.0f), NULL)));
// triggers.push_back(new TriggerNode("hunters pet low health", NextAction::array(0, new NextAction("mend
// pet", 21.0f), NULL)));
triggers.push_back(
new TriggerNode("has aggro", NextAction::array(0, new NextAction("concussive shot", 20.0f), nullptr)));
}
void DpsAoeHunterStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("light aoe", NextAction::array(0, new NextAction("multi-shot", 20.0f), nullptr)));
triggers.push_back(new TriggerNode("medium aoe", NextAction::array(0, new NextAction("volley", 21.0f), nullptr)));
triggers.push_back(
new TriggerNode("serpent sting on attacker",
NextAction::array(0, new NextAction("serpent sting on attacker", 17.0f), nullptr)));
}
void DpsHunterDebuffStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("no stings", NextAction::array(0, new NextAction("serpent sting", 18.0f), nullptr)));
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_DPSHUNTERSTRATEGY_H
#define _PLAYERBOT_DPSHUNTERSTRATEGY_H
#include "GenericHunterStrategy.h"
class PlayerbotAI;
class DpsHunterStrategy : public GenericHunterStrategy
{
public:
DpsHunterStrategy(PlayerbotAI* botAI);
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "dps"; }
NextAction** getDefaultActions() override;
};
class DpsAoeHunterStrategy : public CombatStrategy
{
public:
DpsAoeHunterStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) {}
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "aoe"; }
};
class DpsHunterDebuffStrategy : public CombatStrategy
{
public:
DpsHunterDebuffStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) {}
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "dps debuff"; }
};
#endif

View File

@@ -44,14 +44,15 @@ void GenericHunterNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& tri
{
NonCombatStrategy::InitTriggers(triggers);
triggers.push_back(new TriggerNode("trueshot aura", NextAction::array(0, new NextAction("trueshot aura", 2.0f), nullptr)));
triggers.push_back(new TriggerNode("often", NextAction::array(0,
new NextAction("apply stone", 1.0f),
new NextAction("apply oil", 1.0f),
nullptr)));
triggers.push_back(new TriggerNode("low ammo", NextAction::array(0, new NextAction("say::low ammo", ACTION_NORMAL), nullptr)));
triggers.push_back(new TriggerNode("no track", NextAction::array(0, new NextAction("track humanoids", ACTION_NORMAL), nullptr)));
triggers.push_back(new TriggerNode("no ammo", NextAction::array(0, new NextAction("equip upgrades", ACTION_HIGH + 1), nullptr)));
triggers.push_back(
new TriggerNode("trueshot aura", NextAction::array(0, new NextAction("trueshot aura", 2.0f), nullptr)));
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("apply oil", 1.0f), nullptr)));
triggers.push_back(
new TriggerNode("low ammo", NextAction::array(0, new NextAction("say::low ammo", ACTION_NORMAL), nullptr)));
triggers.push_back(
new TriggerNode("no track", NextAction::array(0, new NextAction("track humanoids", ACTION_NORMAL), nullptr)));
triggers.push_back(new TriggerNode("no ammo",
NextAction::array(0, new NextAction("equip upgrades", ACTION_HIGH + 1), nullptr)));
// triggers.push_back(new TriggerNode("no ammo", NextAction::array(0, new NextAction("switch to melee",
// ACTION_NORMAL + 1), new NextAction("say::no ammo", ACTION_NORMAL), nullptr))); triggers.push_back(new
// TriggerNode("has ammo", NextAction::array(0, new NextAction("switch to ranged", ACTION_NORMAL), nullptr)));
@@ -60,8 +61,12 @@ void GenericHunterNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& tri
void HunterPetStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("no pet", NextAction::array(0, new NextAction("call pet", 60.0f), nullptr)));
triggers.push_back(new TriggerNode("has pet", NextAction::array(0, new NextAction("toggle pet spell", 60.0f), nullptr)));
triggers.push_back(new TriggerNode("pet not happy", NextAction::array(0, new NextAction("feed pet", 60.0f), nullptr)));
triggers.push_back(new TriggerNode("hunters pet medium health", NextAction::array(0, new NextAction("mend pet", 60.0f), nullptr)));
triggers.push_back(new TriggerNode("hunters pet dead", NextAction::array(0, new NextAction("revive pet", 60.0f), nullptr)));
triggers.push_back(
new TriggerNode("has pet", NextAction::array(0, new NextAction("toggle pet spell", 60.0f), nullptr)));
triggers.push_back(
new TriggerNode("pet not happy", NextAction::array(0, new NextAction("feed pet", 60.0f), nullptr)));
triggers.push_back(
new TriggerNode("hunters pet medium health", NextAction::array(0, new NextAction("mend pet", 60.0f), nullptr)));
triggers.push_back(
new TriggerNode("hunters pet dead", NextAction::array(0, new NextAction("revive pet", 60.0f), nullptr)));
}

View File

@@ -16,7 +16,6 @@ public:
creators["rapid fire"] = &rapid_fire;
creators["boost"] = &rapid_fire;
creators["aspect of the pack"] = &aspect_of_the_pack;
creators["aspect of the dragonhawk"] = &aspect_of_the_dragonhawk;
creators["feign death"] = &feign_death;
creators["wing clip"] = &wing_clip;
creators["mongoose bite"] = &mongoose_bite;
@@ -41,14 +40,6 @@ private:
/*C*/ nullptr);
}
static ActionNode* aspect_of_the_dragonhawk([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("aspect of the dragonhawk",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("aspect of the hawk"), nullptr),
/*C*/ nullptr);
}
static ActionNode* feign_death([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("feign death",
@@ -89,6 +80,7 @@ private:
/*A*/ NextAction::array(0, new NextAction("immolation trap"), nullptr),
/*C*/ nullptr);
}
};
GenericHunterStrategy::GenericHunterStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI)
@@ -100,58 +92,58 @@ void GenericHunterStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
CombatStrategy::InitTriggers(triggers);
// Mark/Ammo/Mana Triggers
triggers.push_back(new TriggerNode("no ammo", NextAction::array(0, new NextAction("equip upgrades", 30.0f), nullptr)));
triggers.push_back(new TriggerNode("hunter's mark", NextAction::array(0, new NextAction("hunter's mark", 29.5f), nullptr)));
triggers.push_back(new TriggerNode("rapid fire", NextAction::array(0, new NextAction("rapid fire", 29.0f), nullptr)));
triggers.push_back(new TriggerNode("aspect of the viper", NextAction::array(0, new NextAction("aspect of the viper", 28.0f), NULL)));
triggers.push_back(new TriggerNode("aspect of the hawk", NextAction::array(0, new NextAction("aspect of the dragonhawk", 27.5f), nullptr)));
// Aggro/Threat/Defensive Triggers
triggers.push_back(new TriggerNode("has aggro", NextAction::array(0, new NextAction("concussive shot", 20.0f), nullptr)));
triggers.push_back(new TriggerNode("low tank threat", NextAction::array(0, new NextAction("misdirection on main tank", 27.0f), NULL)));
triggers.push_back(new TriggerNode("low health", NextAction::array(0, new NextAction("deterrence", 35.0f), nullptr)));
triggers.push_back(new TriggerNode("concussive shot on snare target", NextAction::array(0, new NextAction("concussive shot", 20.0f), nullptr)));
triggers.push_back(new TriggerNode("medium threat", NextAction::array(0, new NextAction("feign death", 35.0f), nullptr)));
triggers.push_back(new TriggerNode("hunters pet medium health", NextAction::array(0, new NextAction("mend pet", 22.0f), nullptr)));
triggers.push_back(new TriggerNode("hunters pet low health", NextAction::array(0, new NextAction("mend pet", 21.0f), nullptr)));
// Dispel Triggers
triggers.push_back(new TriggerNode("tranquilizing shot enrage", NextAction::array(0, new NextAction("tranquilizing shot", 61.0f), NULL)));
triggers.push_back(new TriggerNode("tranquilizing shot magic", NextAction::array(0, new NextAction("tranquilizing shot", 61.0f), NULL)));
// Ranged-based Triggers
triggers.push_back(new TriggerNode("enemy within melee", NextAction::array(0,
new NextAction("explosive trap", 37.0f),
new NextAction("mongoose bite", 22.0f),
new NextAction("wing clip", 21.0f), nullptr)));
triggers.push_back(new TriggerNode("enemy too close for auto shot", NextAction::array(0,
new NextAction("disengage", 35.0f),
new NextAction("flee", 34.0f), nullptr)));
triggers.push_back(new TriggerNode("enemy within melee",
NextAction::array(0,
new NextAction("explosive trap", ACTION_MOVE + 7),
new NextAction("mongoose bite", ACTION_HIGH + 2),
new NextAction("wing clip", ACTION_HIGH + 1),
nullptr)));
triggers.push_back(
new TriggerNode("medium threat", NextAction::array(0, new NextAction("feign death", 35.0f), nullptr)));
triggers.push_back(new TriggerNode("hunters pet medium health",
NextAction::array(0, new NextAction("mend pet", ACTION_HIGH + 2), nullptr)));
triggers.push_back(new TriggerNode("no ammo",
NextAction::array(0, new NextAction("equip upgrades", ACTION_HIGH + 9), nullptr)));
triggers.push_back(new TriggerNode("aspect of the viper",
NextAction::array(0, new NextAction("aspect of the viper", ACTION_HIGH), NULL)));
triggers.push_back(new TriggerNode("enemy too close for auto shot",
NextAction::array(0,
new NextAction("disengage", ACTION_MOVE + 5),
new NextAction("flee", ACTION_MOVE + 4),
nullptr)));
triggers.push_back(
new TriggerNode("low tank threat",
NextAction::array(0, new NextAction("misdirection on main tank", ACTION_HIGH + 7), NULL)));
triggers.push_back(
new TriggerNode("low health", NextAction::array(0, new NextAction("deterrence", ACTION_HIGH + 5), nullptr)));
triggers.push_back(new TriggerNode("tranquilizing shot enrage",
NextAction::array(0, new NextAction("tranquilizing shot", 61.0f), NULL)));
triggers.push_back(new TriggerNode("tranquilizing shot magic",
NextAction::array(0, new NextAction("tranquilizing shot", 61.0f), NULL)));
}
// ===== AoE Strategy, 2/3+ enemies =====
AoEHunterStrategy::AoEHunterStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) {}
void AoEHunterStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
NextAction** HunterBoostStrategy::getDefaultActions()
{
triggers.push_back(new TriggerNode("volley channel check", NextAction::array(0, new NextAction("cancel channel", 23.0f), nullptr)));
triggers.push_back(new TriggerNode("medium aoe", NextAction::array(0, new NextAction("volley", 22.0f), nullptr)));
triggers.push_back(new TriggerNode("light aoe", NextAction::array(0, new NextAction("multi-shot", 21.0f), nullptr)));
return NextAction::array(0, new NextAction("bestial wrath", 15.0f), nullptr);
}
void HunterBoostStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("rapid fire", NextAction::array(0, new NextAction("rapid fire", 16.0f), nullptr)));
}
void HunterCcStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("scare beast", NextAction::array(0, new NextAction("scare beast on cc", 23.0f), nullptr)));
triggers.push_back(new TriggerNode("freezing trap", NextAction::array(0, new NextAction("freezing trap on cc", 23.0f), nullptr)));
triggers.push_back(new TriggerNode(
"scare beast", NextAction::array(0, new NextAction("scare beast on cc", ACTION_HIGH + 3), nullptr)));
triggers.push_back(new TriggerNode(
"freezing trap", NextAction::array(0, new NextAction("freezing trap on cc", ACTION_HIGH + 3), nullptr)));
}
void HunterTrapWeaveStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("immolation trap no cd", NextAction::array(0, new NextAction("reach melee", 23.0f), nullptr)));
triggers.push_back(new TriggerNode(
"immolation trap no cd", NextAction::array(0, new NextAction("reach melee", ACTION_HIGH + 3), nullptr)));
}

View File

@@ -21,22 +21,13 @@ public:
uint32 GetType() const override { return CombatStrategy::GetType() | STRATEGY_TYPE_RANGED | STRATEGY_TYPE_DPS; }
};
class AoEHunterStrategy : public CombatStrategy
{
public:
AoEHunterStrategy(PlayerbotAI* botAI);
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "aoe"; }
};
class HunterBoostStrategy : public Strategy
{
public:
HunterBoostStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
std::string const getName() override { return "boost"; }
NextAction** getDefaultActions() override;
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
};

View File

@@ -10,10 +10,11 @@
#include "PlayerbotAI.h"
#include "Playerbots.h"
bool CastHuntersMarkAction::isUseful() { return CastDebuffSpellAction::isUseful(); }
bool CastViperStingAction::isUseful()
{
return CastAuraSpellAction::isUseful() && AI_VALUE2(uint8, "mana", "self target") < 50 &&
AI_VALUE2(uint8, "mana", "current target") >= 30;
return AI_VALUE2(uint8, "mana", "self target") < 50 && AI_VALUE2(uint8, "mana", "current target") >= 30;
}
bool CastAspectOfTheCheetahAction::isUseful()
@@ -21,42 +22,6 @@ bool CastAspectOfTheCheetahAction::isUseful()
return !botAI->HasAnyAuraOf(GetTarget(), "aspect of the cheetah", "aspect of the pack", nullptr);
}
bool CastAspectOfTheHawkAction::isUseful()
{
Unit* target = GetTarget();
if (!target)
return false;
if (bot->HasSpell(61846) || bot->HasSpell(61847)) // Aspect of the Dragonhawk spell IDs
return false;
return true;
}
bool CastArcaneShotAction::isUseful()
{
Unit* target = GetTarget();
if (!target)
return false;
if (bot->HasSpell(53301) || bot->HasSpell(60051) || bot->HasSpell(60052) || bot->HasSpell(60053)) // Explosive Shot spell IDs
return false;
// Armor Penetration rating check - will not cast Arcane Shot above 435 ArP
int32 armorPenRating = bot->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + CR_ARMOR_PENETRATION);
if (armorPenRating > 435)
return false;
return true;
}
bool CastImmolationTrapAction::isUseful()
{
Unit* target = GetTarget();
if (!target)
return false;
if (bot->HasSpell(13813) || bot->HasSpell(14316) || bot->HasSpell(14317) || bot->HasSpell(27025) || bot->HasSpell(49066) || bot->HasSpell(49067)) // Explosive Trap spell IDs
return false;
return true;
}
Value<Unit*>* CastFreezingTrap::GetTargetValue() { return context->GetValue<Unit*>("cc target", "freezing trap"); }
bool FeedPetAction::Execute(Event event)
@@ -97,6 +62,7 @@ bool CastDisengageAction::isUseful()
return !botAI->HasStrategy("trap weave", BOT_STATE_COMBAT);
}
Value<Unit*>* CastScareBeastCcAction::GetTargetValue() { return context->GetValue<Unit*>("cc target", "scare beast"); }
bool CastScareBeastCcAction::Execute(Event event) { return botAI->CastSpell("scare beast", GetTarget()); }

View File

@@ -9,24 +9,107 @@
#include "AiObject.h"
#include "Event.h"
#include "GenericSpellActions.h"
#include "Unit.h"
class PlayerbotAI;
class Unit;
// Buff and Out of Combat Spells
// BEGIN_RANGED_SPELL_ACTION(CastHuntersMarkAction, "hunter's mark")
// END_SPELL_ACTION()
class CastTrueshotAuraAction : public CastBuffSpellAction
class CastHuntersMarkAction : public CastDebuffSpellAction
{
public:
CastTrueshotAuraAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "trueshot aura") {}
CastHuntersMarkAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "hunter's mark") {}
bool isUseful() override;
};
class CastAutoShotAction : public CastSpellAction
{
public:
CastAutoShotAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "auto shot") {}
ActionThreatType getThreatType() override { return ActionThreatType::None; }
bool isUseful() override;
};
BEGIN_RANGED_SPELL_ACTION(CastArcaneShotAction, "arcane shot")
END_SPELL_ACTION()
class CastExplosiveShotAction : public CastDebuffSpellAction
{
public:
CastExplosiveShotAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "explosive shot", true, 0.0f) {}
};
BEGIN_RANGED_SPELL_ACTION(CastAimedShotAction, "aimed shot")
END_SPELL_ACTION()
BEGIN_RANGED_SPELL_ACTION(CastChimeraShotAction, "chimera shot")
END_SPELL_ACTION()
class CastConcussiveShotAction : public CastSnareSpellAction
{
public:
CastConcussiveShotAction(PlayerbotAI* botAI) : CastSnareSpellAction(botAI, "concussive shot") {}
};
BEGIN_RANGED_SPELL_ACTION(CastDistractingShotAction, "distracting shot")
END_SPELL_ACTION()
BEGIN_RANGED_SPELL_ACTION(CastMultiShotAction, "multi-shot")
END_SPELL_ACTION()
BEGIN_RANGED_SPELL_ACTION(CastVolleyAction, "volley")
ActionThreatType getThreatType() override { return ActionThreatType::Aoe; }
END_SPELL_ACTION()
DEBUFF_CHECKISOWNER_ACTION(CastSerpentStingAction, "serpent sting");
BEGIN_RANGED_SPELL_ACTION(CastWyvernStingAction, "wyvern sting")
END_SPELL_ACTION()
BEGIN_RANGED_SPELL_ACTION(CastViperStingAction, "viper sting")
bool isUseful() override;
END_SPELL_ACTION()
BEGIN_RANGED_SPELL_ACTION(CastScorpidStingAction, "scorpid sting")
END_SPELL_ACTION()
BEGIN_RANGED_SPELL_ACTION(CastSteadyShotAction, "steady shot")
END_SPELL_ACTION()
BEGIN_RANGED_SPELL_ACTION(CastKillShotAction, "kill shot")
END_SPELL_ACTION()
BEGIN_RANGED_SPELL_ACTION(CastSilencingShotAction, "silencing shot")
END_SPELL_ACTION()
BEGIN_RANGED_SPELL_ACTION(CastTranquilizingShotAction, "tranquilizing shot")
END_SPELL_ACTION()
class CastDisengageAction : public CastSpellAction
{
public:
CastDisengageAction(PlayerbotAI* botAI): CastSpellAction(botAI, "disengage") {}
bool Execute(Event event) override;
bool isUseful() override;
};
class CastImmolationTrapAction : public CastSpellAction
{
public:
CastImmolationTrapAction(PlayerbotAI* botAI): CastSpellAction(botAI, "immolation trap") {}
};
class CastExplosiveTrapAction : public CastSpellAction
{
public:
CastExplosiveTrapAction(PlayerbotAI* botAI): CastSpellAction(botAI, "explosive trap") {}
};
class CastAspectOfTheHawkAction : public CastBuffSpellAction
{
public:
CastAspectOfTheHawkAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "aspect of the hawk") {}
bool isUseful() override;
};
class CastAspectOfTheMonkeyAction : public CastBuffSpellAction
@@ -67,7 +150,45 @@ public:
CastAspectOfTheViperAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "aspect of the viper") {}
};
// Cooldown Spells
class CastCallPetAction : public CastBuffSpellAction
{
public:
CastCallPetAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "call pet") {}
};
class CastMendPetAction : public CastAuraSpellAction
{
public:
CastMendPetAction(PlayerbotAI* botAI) : CastAuraSpellAction(botAI, "mend pet") {}
std::string const GetTargetName() override { return "pet target"; }
};
class CastKillCommandAction : public CastAuraSpellAction
{
public:
CastKillCommandAction(PlayerbotAI* botAI) : CastAuraSpellAction(botAI, "kill command") {}
std::string const GetTargetName() override { return "pet target"; }
};
class CastRevivePetAction : public CastBuffSpellAction
{
public:
CastRevivePetAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "revive pet") {}
};
class CastTrueshotAuraAction : public CastBuffSpellAction
{
public:
CastTrueshotAuraAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "trueshot aura") {}
};
class CastFeignDeathAction : public CastBuffSpellAction
{
public:
CastFeignDeathAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "feign death") {}
};
class CastRapidFireAction : public CastBuffSpellAction
{
@@ -87,30 +208,10 @@ public:
CastReadinessAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "readiness") {}
};
class CastDisengageAction : public CastSpellAction
class CastBlackArrow : public CastDebuffSpellAction
{
public:
CastDisengageAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "disengage") {}
bool Execute(Event event) override;
bool isUseful() override;
};
// CC Spells
class CastScareBeastAction : public CastSpellAction
{
public:
CastScareBeastAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "scare beast") {}
};
class CastScareBeastCcAction : public CastSpellAction
{
public:
CastScareBeastCcAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "scare beast on cc") {}
Value<Unit*>* GetTargetValue() override;
bool Execute(Event event) override;
CastBlackArrow(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "black arrow", true) {}
};
class CastFreezingTrap : public CastDebuffSpellAction
@@ -121,297 +222,6 @@ public:
Value<Unit*>* GetTargetValue() override;
};
class CastWyvernStingAction : public CastDebuffSpellAction
{
public:
CastWyvernStingAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "wyvern sting", true) {}
};
class CastSilencingShotAction : public CastSpellAction
{
public:
CastSilencingShotAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "silencing shot") {}
};
class CastConcussiveShotAction : public CastSnareSpellAction
{
public:
CastConcussiveShotAction(PlayerbotAI* botAI) : CastSnareSpellAction(botAI, "concussive shot") {}
};
class CastIntimidationAction : public CastBuffSpellAction
{
public:
CastIntimidationAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "intimidation", false, 5000) {}
std::string const GetTargetName() override { return "pet target"; }
};
// Threat Spells
class CastDistractingShotAction : public CastSpellAction
{
public:
CastDistractingShotAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "distracting shot") {}
};
class CastMisdirectionOnMainTankAction : public BuffOnMainTankAction
{
public:
CastMisdirectionOnMainTankAction(PlayerbotAI* ai) : BuffOnMainTankAction(ai, "misdirection", true) {}
};
class CastFeignDeathAction : public CastBuffSpellAction
{
public:
CastFeignDeathAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "feign death") {}
};
// Pet Spells
class FeedPetAction : public Action
{
public:
FeedPetAction(PlayerbotAI* botAI) : Action(botAI, "feed pet") {}
bool Execute(Event event) override;
};
class CastCallPetAction : public CastBuffSpellAction
{
public:
CastCallPetAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "call pet") {}
};
class CastMendPetAction : public CastAuraSpellAction
{
public:
CastMendPetAction(PlayerbotAI* botAI) : CastAuraSpellAction(botAI, "mend pet") {}
std::string const GetTargetName() override { return "pet target"; }
};
class CastRevivePetAction : public CastBuffSpellAction
{
public:
CastRevivePetAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "revive pet") {}
};
class CastKillCommandAction : public CastBuffSpellAction
{
public:
CastKillCommandAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "kill command", false, 5000) {}
std::string const GetTargetName() override { return "pet target"; }
};
class CastBestialWrathAction : public CastBuffSpellAction
{
public:
CastBestialWrathAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "bestial wrath", false, 5000) {}
std::string const GetTargetName() override { return "pet target"; }
};
// Direct Damage Spells
class CastAutoShotAction : public CastSpellAction
{
public:
CastAutoShotAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "auto shot") {}
ActionThreatType getThreatType() override { return ActionThreatType::None; }
bool isUseful() override;
};
class CastArcaneShotAction : public CastSpellAction
{
public:
CastArcaneShotAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "arcane shot") {}
bool isUseful() override;
};
class CastAimedShotAction : public CastSpellAction
{
public:
CastAimedShotAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "aimed shot") {}
};
class CastChimeraShotAction : public CastSpellAction
{
public:
CastChimeraShotAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "chimera shot") {}
};
class CastSteadyShotAction : public CastSpellAction
{
public:
CastSteadyShotAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "steady shot") {}
};
class CastKillShotAction : public CastSpellAction
{
public:
CastKillShotAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "kill shot") {}
};
// DoT/Debuff Spells
class CastHuntersMarkAction : public CastDebuffSpellAction
{
public:
CastHuntersMarkAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "hunter's mark") {}
bool isUseful() override
{
// Bypass TTL check
return CastAuraSpellAction::isUseful();
}
};
class CastTranquilizingShotAction : public CastSpellAction
{
public:
CastTranquilizingShotAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "tranquilizing shot") {}
};
class CastViperStingAction : public CastDebuffSpellAction
{
public:
CastViperStingAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "viper sting", true) {}
bool isUseful() override;
};
class CastSerpentStingAction : public CastDebuffSpellAction
{
public:
CastSerpentStingAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "serpent sting", true) {}
bool isUseful() override
{
// Bypass TTL check
return CastAuraSpellAction::isUseful();
}
};
class CastScorpidStingAction : public CastDebuffSpellAction
{
public:
CastScorpidStingAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "scorpid sting", true) {}
bool isUseful() override
{
// Bypass TTL check
return CastAuraSpellAction::isUseful();
}
};
class CastSerpentStingOnAttackerAction : public CastDebuffSpellOnAttackerAction
{
public:
CastSerpentStingOnAttackerAction(PlayerbotAI* botAI) : CastDebuffSpellOnAttackerAction(botAI, "serpent sting", true) {}
bool isUseful() override
{
// Bypass TTL check
return CastAuraSpellAction::isUseful();
}
};
class CastImmolationTrapAction : public CastSpellAction
{
public:
CastImmolationTrapAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "immolation trap") {}
bool isUseful() override;
};
class CastExplosiveTrapAction : public CastSpellAction
{
public:
CastExplosiveTrapAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "explosive trap") {}
};
class CastBlackArrow : public CastDebuffSpellAction
{
public:
CastBlackArrow(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "black arrow", true) {}
bool isUseful() override
{
if (botAI->HasStrategy("trap weave", BOT_STATE_COMBAT))
return false;
// Bypass TTL check
return CastAuraSpellAction::isUseful();
}
};
class CastExplosiveShotAction : public CastDebuffSpellAction
{
public:
CastExplosiveShotAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "explosive shot", true, 0.0f) {}
bool isUseful() override
{
// Bypass TTL check
return CastAuraSpellAction::isUseful();
}
};
// Rank 4
class CastExplosiveShotRank4Action : public CastDebuffSpellAction
{
public:
CastExplosiveShotRank4Action(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "explosive shot", true, 0.0f) {}
bool Execute(Event event) override { return botAI->CastSpell(60053, GetTarget()); }
bool isUseful() override
{
Unit* target = GetTarget();
if (!target)
return false;
return !target->HasAura(60053);
}
};
// Rank 3
class CastExplosiveShotRank3Action : public CastDebuffSpellAction
{
public:
CastExplosiveShotRank3Action(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "explosive shot", true, 0.0f) {}
bool Execute(Event event) override { return botAI->CastSpell(60052, GetTarget()); }
bool isUseful() override
{
Unit* target = GetTarget();
if (!target)
return false;
return !target->HasAura(60052);
}
};
// Rank 2
class CastExplosiveShotRank2Action : public CastDebuffSpellAction
{
public:
CastExplosiveShotRank2Action(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "explosive shot", true, 0.0f) {}
bool Execute(Event event) override { return botAI->CastSpell(60051, GetTarget()); }
bool isUseful() override
{
Unit* target = GetTarget();
if (!target)
return false;
return !target->HasAura(60051);
}
};
// Rank 1
class CastExplosiveShotRank1Action : public CastDebuffSpellAction
{
public:
CastExplosiveShotRank1Action(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "explosive shot", true, 0.0f) {}
bool Execute(Event event) override { return botAI->CastSpell(53301, GetTarget()); }
bool isUseful() override
{
Unit* target = GetTarget();
if (!target)
return false;
return !target->HasAura(53301);
}
};
// Melee Spells
class CastWingClipAction : public CastSpellAction
{
public:
@@ -433,19 +243,47 @@ public:
CastMongooseBiteAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "mongoose bite") {}
};
// AoE Spells
class CastMultiShotAction : public CastSpellAction
class CastSerpentStingOnAttackerAction : public CastDebuffSpellOnAttackerAction
{
public:
CastMultiShotAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "multi-shot") {}
CastSerpentStingOnAttackerAction(PlayerbotAI* botAI) : CastDebuffSpellOnAttackerAction(botAI, "serpent sting", true)
{
}
};
class CastVolleyAction : public CastSpellAction
class FeedPetAction : public Action
{
public:
CastVolleyAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "volley") {}
ActionThreatType getThreatType() override { return ActionThreatType::Aoe; }
FeedPetAction(PlayerbotAI* botAI) : Action(botAI, "feed pet") {}
bool Execute(Event event) override;
};
class CastBestialWrathAction : public CastBuffSpellAction
{
public:
CastBestialWrathAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "bestial wrath") {}
std::string const GetTargetName() override { return "pet target"; }
};
class CastScareBeastAction : public CastSpellAction
{
public:
CastScareBeastAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "scare beast") {}
};
class CastScareBeastCcAction : public CastSpellAction
{
public:
CastScareBeastCcAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "scare beast on cc") {}
Value<Unit*>* GetTargetValue() override;
bool Execute(Event event) override;
};
class CastMisdirectionOnMainTankAction : public BuffOnMainTankAction
{
public:
CastMisdirectionOnMainTankAction(PlayerbotAI* ai) : BuffOnMainTankAction(ai, "misdirection", true) {}
};
#endif

View File

@@ -5,43 +5,38 @@
#include "HunterAiObjectContext.h"
#include "BeastMasteryHunterStrategy.h"
#include "DpsHunterStrategy.h"
#include "GenericHunterNonCombatStrategy.h"
#include "GenericHunterStrategy.h"
#include "HunterActions.h"
#include "HunterBuffStrategies.h"
#include "HunterTriggers.h"
#include "MarksmanshipHunterStrategy.h"
#include "NamedObjectContext.h"
#include "Playerbots.h"
#include "SurvivalHunterStrategy.h"
class HunterStrategyFactoryInternal : public NamedObjectContext<Strategy>
{
public:
HunterStrategyFactoryInternal()
{
creators["dps"] = &HunterStrategyFactoryInternal::dps;
creators["nc"] = &HunterStrategyFactoryInternal::nc;
creators["aoe"] = &HunterStrategyFactoryInternal::aoe;
creators["dps debuff"] = &HunterStrategyFactoryInternal::dps_debuff;
creators["boost"] = &HunterStrategyFactoryInternal::boost;
creators["pet"] = &HunterStrategyFactoryInternal::pet;
creators["cc"] = &HunterStrategyFactoryInternal::cc;
creators["trap weave"] = &HunterStrategyFactoryInternal::trap_weave;
creators["bm"] = &HunterStrategyFactoryInternal::beast_mastery;
creators["mm"] = &HunterStrategyFactoryInternal::marksmanship;
creators["surv"] = &HunterStrategyFactoryInternal::survival;
creators["aoe"] = &HunterStrategyFactoryInternal::aoe;
}
private:
static Strategy* aoe(PlayerbotAI* botAI) { return new DpsAoeHunterStrategy(botAI); }
static Strategy* dps(PlayerbotAI* botAI) { return new DpsHunterStrategy(botAI); }
static Strategy* nc(PlayerbotAI* botAI) { return new GenericHunterNonCombatStrategy(botAI); }
static Strategy* dps_debuff(PlayerbotAI* botAI) { return new DpsHunterDebuffStrategy(botAI); }
static Strategy* boost(PlayerbotAI* botAI) { return new HunterBoostStrategy(botAI); }
static Strategy* pet(PlayerbotAI* botAI) { return new HunterPetStrategy(botAI); }
static Strategy* cc(PlayerbotAI* botAI) { return new HunterCcStrategy(botAI); }
static Strategy* trap_weave(PlayerbotAI* botAI) { return new HunterTrapWeaveStrategy(botAI); }
static Strategy* beast_mastery(PlayerbotAI* botAI) { return new BeastMasteryHunterStrategy(botAI); }
static Strategy* marksmanship(PlayerbotAI* botAI) { return new MarksmanshipHunterStrategy(botAI); }
static Strategy* survival(PlayerbotAI* botAI) { return new SurvivalHunterStrategy(botAI); }
static Strategy* aoe(PlayerbotAI* botAI) { return new AoEHunterStrategy(botAI); }
};
class HunterBuffStrategyFactoryInternal : public NamedObjectContext<Strategy>
@@ -96,12 +91,6 @@ public:
creators["tranquilizing shot enrage"] = &HunterTriggerFactoryInternal::remove_enrage;
creators["tranquilizing shot magic"] = &HunterTriggerFactoryInternal::remove_magic;
creators["immolation trap no cd"] = &HunterTriggerFactoryInternal::immolation_trap_no_cd;
creators["kill command"] = &HunterTriggerFactoryInternal::kill_command;
creators["explosive shot"] = &HunterTriggerFactoryInternal::explosive_shot;
creators["lock and load"] = &HunterTriggerFactoryInternal::lock_and_load;
creators["silencing shot"] = &HunterTriggerFactoryInternal::silencing_shot;
creators["intimidation"] = &HunterTriggerFactoryInternal::intimidation;
creators["volley channel check"] = &HunterTriggerFactoryInternal::volley_channel_check;
}
private:
@@ -137,12 +126,6 @@ private:
static Trigger* remove_enrage(PlayerbotAI* ai) { return new TargetRemoveEnrageTrigger(ai); }
static Trigger* remove_magic(PlayerbotAI* ai) { return new TargetRemoveMagicTrigger(ai); }
static Trigger* immolation_trap_no_cd(PlayerbotAI* ai) { return new ImmolationTrapNoCdTrigger(ai); }
static Trigger* kill_command(PlayerbotAI* botAI) { return new KillCommandTrigger(botAI); }
static Trigger* explosive_shot(PlayerbotAI* botAI) { return new ExplosiveShotTrigger(botAI); }
static Trigger* lock_and_load(PlayerbotAI* botAI) { return new LockAndLoadTrigger(botAI); }
static Trigger* silencing_shot(PlayerbotAI* botAI) { return new SilencingShotTrigger(botAI); }
static Trigger* intimidation(PlayerbotAI* botAI) { return new IntimidationTrigger(botAI); }
static Trigger* volley_channel_check(PlayerbotAI* botAI) { return new VolleyChannelCheckTrigger(botAI); }
};
class HunterAiObjectContextInternal : public NamedObjectContext<Action>
@@ -200,11 +183,6 @@ public:
creators["disengage"] = &HunterAiObjectContextInternal::disengage;
creators["immolation trap"] = &HunterAiObjectContextInternal::immolation_trap;
creators["explosive trap"] = &HunterAiObjectContextInternal::explosive_trap;
creators["explosive shot rank 4"] = &HunterAiObjectContextInternal::explosive_shot_rank_4;
creators["explosive shot rank 3"] = &HunterAiObjectContextInternal::explosive_shot_rank_3;
creators["explosive shot rank 2"] = &HunterAiObjectContextInternal::explosive_shot_rank_2;
creators["explosive shot rank 1"] = &HunterAiObjectContextInternal::explosive_shot_rank_1;
creators["intimidation"] = &HunterAiObjectContextInternal::intimidation;
}
private:
@@ -257,51 +235,12 @@ private:
static Action* disengage(PlayerbotAI* ai) { return new CastDisengageAction(ai); }
static Action* immolation_trap(PlayerbotAI* ai) { return new CastImmolationTrapAction(ai); }
static Action* explosive_trap(PlayerbotAI* ai) { return new CastExplosiveTrapAction(ai); }
static Action* explosive_shot_rank_4(PlayerbotAI* ai) { return new CastExplosiveShotRank4Action(ai); }
static Action* explosive_shot_rank_3(PlayerbotAI* ai) { return new CastExplosiveShotRank3Action(ai); }
static Action* explosive_shot_rank_2(PlayerbotAI* ai) { return new CastExplosiveShotRank2Action(ai); }
static Action* explosive_shot_rank_1(PlayerbotAI* ai) { return new CastExplosiveShotRank1Action(ai); }
static Action* intimidation(PlayerbotAI* ai) { return new CastIntimidationAction(ai); }
};
SharedNamedObjectContextList<Strategy> HunterAiObjectContext::sharedStrategyContexts;
SharedNamedObjectContextList<Action> HunterAiObjectContext::sharedActionContexts;
SharedNamedObjectContextList<Trigger> HunterAiObjectContext::sharedTriggerContexts;
SharedNamedObjectContextList<UntypedValue> HunterAiObjectContext::sharedValueContexts;
HunterAiObjectContext::HunterAiObjectContext(PlayerbotAI* botAI)
: AiObjectContext(botAI, sharedStrategyContexts, sharedActionContexts, sharedTriggerContexts, sharedValueContexts)
HunterAiObjectContext::HunterAiObjectContext(PlayerbotAI* botAI) : AiObjectContext(botAI)
{
}
void HunterAiObjectContext::BuildSharedContexts()
{
BuildSharedStrategyContexts(sharedStrategyContexts);
BuildSharedActionContexts(sharedActionContexts);
BuildSharedTriggerContexts(sharedTriggerContexts);
BuildSharedValueContexts(sharedValueContexts);
}
void HunterAiObjectContext::BuildSharedStrategyContexts(SharedNamedObjectContextList<Strategy>& strategyContexts)
{
AiObjectContext::BuildSharedStrategyContexts(strategyContexts);
strategyContexts.Add(new HunterStrategyFactoryInternal());
strategyContexts.Add(new HunterBuffStrategyFactoryInternal());
}
void HunterAiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList<Action>& actionContexts)
{
AiObjectContext::BuildSharedActionContexts(actionContexts);
actionContexts.Add(new HunterAiObjectContextInternal());
}
void HunterAiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextList<Trigger>& triggerContexts)
{
AiObjectContext::BuildSharedTriggerContexts(triggerContexts);
triggerContexts.Add(new HunterTriggerFactoryInternal());
}
void HunterAiObjectContext::BuildSharedValueContexts(SharedNamedObjectContextList<UntypedValue>& valueContexts)
{
AiObjectContext::BuildSharedValueContexts(valueContexts);
}

View File

@@ -14,17 +14,6 @@ class HunterAiObjectContext : public AiObjectContext
{
public:
HunterAiObjectContext(PlayerbotAI* botAI);
static void BuildSharedContexts();
static void BuildSharedStrategyContexts(SharedNamedObjectContextList<Strategy>& strategyContexts);
static void BuildSharedActionContexts(SharedNamedObjectContextList<Action>& actionContexts);
static void BuildSharedTriggerContexts(SharedNamedObjectContextList<Trigger>& triggerContexts);
static void BuildSharedValueContexts(SharedNamedObjectContextList<UntypedValue>& valueContexts);
static SharedNamedObjectContextList<Strategy> sharedStrategyContexts;
static SharedNamedObjectContextList<Action> sharedActionContexts;
static SharedNamedObjectContextList<Trigger> sharedTriggerContexts;
static SharedNamedObjectContextList<UntypedValue> sharedValueContexts;
};
#endif

View File

@@ -4,6 +4,7 @@
*/
#include "HunterTriggers.h"
#include "GenericSpellActions.h"
#include "GenericTriggers.h"
#include "HunterActions.h"
@@ -12,13 +13,6 @@
#include "Playerbots.h"
#include "ServerFacade.h"
#include "SharedDefines.h"
#include "Player.h"
bool KillCommandTrigger::IsActive()
{
Unit* target = GetTarget();
return !botAI->HasAura("kill command", target);
}
bool BlackArrowTrigger::IsActive()
{
@@ -26,7 +20,6 @@ bool BlackArrowTrigger::IsActive()
return false;
return DebuffTrigger::IsActive();
return BuffTrigger::IsActive();
}
bool HunterAspectOfTheHawkTrigger::IsActive()
@@ -42,7 +35,6 @@ bool HunterNoStingsActiveTrigger::IsActive()
Unit* target = AI_VALUE(Unit*, "current target");
return DebuffTrigger::IsActive() && target && !botAI->HasAura("serpent sting", target, false, true) &&
!botAI->HasAura("scorpid sting", target, false, true) && !botAI->HasAura("viper sting", target, false, true);
return BuffTrigger::IsActive();
}
bool HuntersPetDeadTrigger::IsActive()
@@ -138,33 +130,4 @@ bool SerpentStingOnAttackerTrigger::IsActive()
}
return !botAI->HasAura("scorpid sting", target, false, true) &&
!botAI->HasAura("viper sting", target, false, true);
return BuffTrigger::IsActive();
}
const std::set<uint32> VolleyChannelCheckTrigger::VOLLEY_SPELL_IDS = {
1510, // Volley Rank 1
14294, // Volley Rank 2
14295, // Volley Rank 3
27022, // Volley Rank 4
58431, // Volley Rank 5
58434 // Volley Rank 6
};
bool VolleyChannelCheckTrigger::IsActive()
{
Player* bot = botAI->GetBot();
// Check if the bot is channeling a spell
if (Spell* spell = bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL))
{
// Only trigger if the spell being channeled is Volley
if (VOLLEY_SPELL_IDS.count(spell->m_spellInfo->Id))
{
uint8 attackerCount = AI_VALUE(uint8, "attacker count");
return attackerCount < minEnemies;
}
}
// Not channeling Volley
return false;
}
}

View File

@@ -9,12 +9,23 @@
#include "CureTriggers.h"
#include "GenericTriggers.h"
#include "Trigger.h"
#include "PlayerbotAI.h"
#include <set>
class PlayerbotAI;
// Buff and Out of Combat Triggers
class HunterNoStingsActiveTrigger : public DebuffTrigger
{
public:
HunterNoStingsActiveTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "no stings") {}
bool IsActive() override;
};
class AutoShotTrigger : public Trigger
{
public:
AutoShotTrigger(PlayerbotAI* botAI) : Trigger(botAI, "auto shot") {}
bool IsActive() override;
};
class HunterAspectOfTheMonkeyTrigger : public BuffTrigger
{
@@ -39,6 +50,7 @@ class HunterAspectOfTheViperTrigger : public BuffTrigger
{
public:
HunterAspectOfTheViperTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "aspect of the viper") {}
bool IsActive() override;
};
@@ -46,9 +58,44 @@ class HunterAspectOfThePackTrigger : public BuffTrigger
{
public:
HunterAspectOfThePackTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "aspect of the pack") {}
bool IsActive() override;
};
BEGIN_TRIGGER(HuntersPetDeadTrigger, Trigger)
END_TRIGGER()
BEGIN_TRIGGER(HuntersPetLowHealthTrigger, Trigger)
END_TRIGGER()
BEGIN_TRIGGER(HuntersPetMediumHealthTrigger, Trigger)
END_TRIGGER()
class BlackArrowTrigger : public DebuffTrigger
{
public:
BlackArrowTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "black arrow", 1, true) {}
bool IsActive() override;
};
class HuntersMarkTrigger : public DebuffTrigger
{
public:
HuntersMarkTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "hunter's mark", 1, false, 25.0f) {}
};
class FreezingTrapTrigger : public HasCcTargetTrigger
{
public:
FreezingTrapTrigger(PlayerbotAI* botAI) : HasCcTargetTrigger(botAI, "freezing trap") {}
};
class RapidFireTrigger : public BoostTrigger
{
public:
RapidFireTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "rapid fire") {}
};
class TrueshotAuraTrigger : public BuffTrigger
{
public:
@@ -62,6 +109,28 @@ public:
bool IsActive() override;
};
class SerpentStingOnAttackerTrigger : public DebuffOnAttackerTrigger
{
public:
SerpentStingOnAttackerTrigger(PlayerbotAI* botAI) : DebuffOnAttackerTrigger(botAI, "serpent sting", true) {}
bool IsActive() override;
};
BEGIN_TRIGGER(HunterPetNotHappy, Trigger)
END_TRIGGER()
class ConsussiveShotSnareTrigger : public SnareTargetTrigger
{
public:
ConsussiveShotSnareTrigger(PlayerbotAI* botAI) : SnareTargetTrigger(botAI, "concussive shot") {}
};
class ScareBeastTrigger : public HasCcTargetTrigger
{
public:
ScareBeastTrigger(PlayerbotAI* botAI) : HasCcTargetTrigger(botAI, "scare beast") {}
};
class HunterLowAmmoTrigger : public AmmoCountTrigger
{
public:
@@ -84,115 +153,6 @@ public:
bool IsActive() override;
};
// Cooldown Triggers
class RapidFireTrigger : public BoostTrigger
{
public:
RapidFireTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "rapid fire") {}
};
class BestialWrathTrigger : public BuffTrigger
{
public:
BestialWrathTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "bestial wrath") {}
};
class IntimidationTrigger : public BuffTrigger
{
public:
IntimidationTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "intimidation") {}
};
class KillCommandTrigger : public BuffTrigger
{
public:
KillCommandTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "kill command") {}
bool IsActive() override;
};
class LockAndLoadTrigger : public BuffTrigger
{
public:
LockAndLoadTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "lock and load") {}
bool IsActive() override
{
return botAI->HasAura("lock and load", botAI->GetBot());
}
};
// CC Triggers
class FreezingTrapTrigger : public HasCcTargetTrigger
{
public:
FreezingTrapTrigger(PlayerbotAI* botAI) : HasCcTargetTrigger(botAI, "freezing trap") {}
};
class ConsussiveShotSnareTrigger : public SnareTargetTrigger
{
public:
ConsussiveShotSnareTrigger(PlayerbotAI* botAI) : SnareTargetTrigger(botAI, "concussive shot") {}
};
class ScareBeastTrigger : public HasCcTargetTrigger
{
public:
ScareBeastTrigger(PlayerbotAI* botAI) : HasCcTargetTrigger(botAI, "scare beast") {}
};
class SilencingShotTrigger : public InterruptSpellTrigger
{
public:
SilencingShotTrigger(PlayerbotAI* botAI) : InterruptSpellTrigger(botAI, "silencing shot") {}
};
// DoT/Debuff Triggers
class HuntersMarkTrigger : public DebuffTrigger
{
public:
HuntersMarkTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "hunter's mark", 1, true, 0.5f) {}
bool IsActive() override { return BuffTrigger::IsActive(); }
};
class ExplosiveShotTrigger : public DebuffTrigger
{
public:
ExplosiveShotTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "explosive shot", 1, true) {}
bool IsActive() override { return BuffTrigger::IsActive(); }
};
class BlackArrowTrigger : public DebuffTrigger
{
public:
BlackArrowTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "black arrow", 1, true) {}
bool IsActive() override;
};
class HunterNoStingsActiveTrigger : public DebuffTrigger
{
public:
HunterNoStingsActiveTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "no stings") {}
bool IsActive() override;
};
class SerpentStingOnAttackerTrigger : public DebuffOnAttackerTrigger
{
public:
SerpentStingOnAttackerTrigger(PlayerbotAI* botAI) : DebuffOnAttackerTrigger(botAI, "serpent sting", true) {}
bool IsActive() override;
};
// Damage/Combat Triggers
class AutoShotTrigger : public Trigger
{
public:
AutoShotTrigger(PlayerbotAI* botAI) : Trigger(botAI, "auto shot") {}
};
class SwitchToRangedTrigger : public Trigger
{
public:
@@ -233,31 +193,4 @@ public:
ImmolationTrapNoCdTrigger(PlayerbotAI* ai) : SpellNoCooldownTrigger(ai, "immolation trap") {}
};
BEGIN_TRIGGER(HuntersPetDeadTrigger, Trigger)
END_TRIGGER()
BEGIN_TRIGGER(HuntersPetLowHealthTrigger, Trigger)
END_TRIGGER()
BEGIN_TRIGGER(HuntersPetMediumHealthTrigger, Trigger)
END_TRIGGER()
BEGIN_TRIGGER(HunterPetNotHappy, Trigger)
END_TRIGGER()
class VolleyChannelCheckTrigger : public Trigger
{
public:
VolleyChannelCheckTrigger(PlayerbotAI* botAI, uint32 minEnemies = 2)
: Trigger(botAI, "volley channel check"), minEnemies(minEnemies)
{
}
bool IsActive() override;
protected:
uint32 minEnemies;
static const std::set<uint32> VOLLEY_SPELL_IDS;
};
#endif

View File

@@ -1,74 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "MarksmanshipHunterStrategy.h"
#include "Playerbots.h"
// ===== Action Node Factory =====
class MarksmanshipHunterStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
MarksmanshipHunterStrategyActionNodeFactory()
{
creators["auto shot"] = &auto_shot;
creators["silencing shot"] = &silencing_shot;
creators["kill command"] = &kill_command;
creators["kill shot"] = &kill_shot;
creators["viper sting"] = &viper_sting;
creators["serpent sting"] = serpent_sting;
creators["chimera shot"] = &chimera_shot;
creators["aimed shot"] = &aimed_shot;
creators["arcane shot"] = &arcane_shot;
creators["steady shot"] = &steady_shot;
creators["multi-shot"] = &multi_shot;
creators["volley"] = &volley;
}
private:
static ActionNode* auto_shot(PlayerbotAI*) { return new ActionNode("auto shot", nullptr, nullptr, nullptr); }
static ActionNode* silencing_shot(PlayerbotAI*) { return new ActionNode("silencing shot", nullptr, nullptr, nullptr); }
static ActionNode* kill_command(PlayerbotAI*) { return new ActionNode("kill command", nullptr, nullptr, nullptr); }
static ActionNode* kill_shot(PlayerbotAI*) { return new ActionNode("kill shot", nullptr, nullptr, nullptr); }
static ActionNode* viper_sting(PlayerbotAI*) { return new ActionNode("viper sting", nullptr, nullptr, nullptr); }
static ActionNode* serpent_sting(PlayerbotAI*) { return new ActionNode("serpent sting", nullptr, nullptr, nullptr); }
static ActionNode* chimera_shot(PlayerbotAI*) { return new ActionNode("chimera shot", nullptr, nullptr, nullptr); }
static ActionNode* aimed_shot(PlayerbotAI*) { return new ActionNode("aimed shot", nullptr, nullptr, nullptr); }
static ActionNode* arcane_shot(PlayerbotAI*) { return new ActionNode("arcane shot", nullptr, nullptr, nullptr); }
static ActionNode* steady_shot(PlayerbotAI*) { return new ActionNode("steady shot", nullptr, nullptr, nullptr); }
static ActionNode* multi_shot(PlayerbotAI*) { return new ActionNode("multi shot", nullptr, nullptr, nullptr); }
static ActionNode* volley(PlayerbotAI*) { return new ActionNode("volley", nullptr, nullptr, nullptr); }
};
// ===== Single Target Strategy =====
MarksmanshipHunterStrategy::MarksmanshipHunterStrategy(PlayerbotAI* botAI) : GenericHunterStrategy(botAI)
{
actionNodeFactories.Add(new MarksmanshipHunterStrategyActionNodeFactory());
}
// ===== Default Actions =====
NextAction** MarksmanshipHunterStrategy::getDefaultActions()
{
return NextAction::array(0,
new NextAction("kill command", 5.8f),
new NextAction("kill shot", 5.7f),
new NextAction("serpent sting", 5.6f),
new NextAction("chimera shot", 5.5f),
new NextAction("aimed shot", 5.4f),
new NextAction("arcane shot", 5.3f),
new NextAction("steady shot", 5.2f),
new NextAction("auto shot", 5.1f), nullptr);
}
// ===== Trigger Initialization ===
void MarksmanshipHunterStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
GenericHunterStrategy::InitTriggers(triggers);
triggers.push_back(new TriggerNode("silencing shot", NextAction::array(0, new NextAction("silencing shot", 40.0f), nullptr)));
triggers.push_back(new TriggerNode("kill command", NextAction::array(0, new NextAction("kill command", 18.5f), nullptr)));
triggers.push_back(new TriggerNode("target critical health", NextAction::array(0, new NextAction("kill shot", 18.0f), nullptr)));
triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("viper sting", 17.5f), nullptr)));
triggers.push_back(new TriggerNode("no stings", NextAction::array(0, new NextAction("serpent sting", 17.0f), nullptr)));
triggers.push_back(new TriggerNode("serpent sting on attacker", NextAction::array(0, new NextAction("serpent sting on attacker", 16.5f), nullptr)));
}

Some files were not shown because too many files have changed in this diff Show More