diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 7d4f9a34..eab4de36 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -1,6 +1,77 @@ -########################################## -# Playerbot Configuration file # -########################################## +################################################## +# PLAYERBOT CONFIGURATION FILE # +################################################## + +################################################################################################### +# SECTION INDEX +# GENERAL SETTINGS +# PLAYERBOT SETTINGS +# GENERAL +# SUMMON OPTIONS +# GEAR +# LOOTING +# TIMERS +# DISTANCES +# THRESHOLDS +# QUESTS +# COMBAT +# CHEATS +# SPELLS +# PLAYERBOT RNDBOT SPECIFIC SETTINGS +# GENERAL +# LEVELS +# GEAR +# QUESTS +# SPELLS +# STRATEGIES +# TELEPORTS +# BATTLEGROUND & ARENA & PVP +# INTERVALS +# PREMADE SPECS +# INFORMATION +# WARRIOR +# PALADIN +# HUNTER +# ROGUE +# PRIEST +# DEATHKNIGHT +# SHAMAN +# MAGE +# WARLOCK +# DRUID +# RANDOM BOT DEFAULT TALENT SPEC +# WARRIOR +# PALADIN +# HUNTER +# ROGUE +# PRIEST +# DEATHKNIGHT +# SHAMAN +# MAGE +# WARLOCK +# DRUID +# PLAYERBOT SYSTEM SETTINGS +# DATABASE & CONNECTIONS +# DEBUG +# CHAT SETTINGS +# LOGS +# DEPRECIATED (TEMPORARY) +# +# +# +# +# +################################################################################################### + +################################################################################################### + +################################### +# # +# GENERAL SETTINGS # +# # +################################### + +#################################################################################################### # Enable or disable AI Playerbot AiPlayerbot.Enabled = 1 @@ -8,12 +79,39 @@ AiPlayerbot.Enabled = 1 # Enable random bot system AiPlayerbot.RandomBotAutologin = 1 -# Log on all random bots on start -AiPlayerbot.RandomBotLoginAtStartup = 1 +# Random bot account +AiPlayerbot.RandomBotAccountCount = 200 + +# Random bot count +AiPlayerbot.MinRandomBots = 50 +AiPlayerbot.MaxRandomBots = 50 # Delete all random bot accounts (reset randombots) AiPlayerbot.DeleteRandomBotAccounts = 0 +################################################################################################### + +################################### +# # +# PLAYERBOT SETTINGS # +# # +################################### + +#################################################################################################### + +#################################################################################################### +# GENERAL +# +# + +# Enable/Disable create bot by addclass command (0 = GM only, 1 = enable) +# default: 1 (enable) +AiPlayerbot.AddClassCommand = 1 + +# Bot group invitation permission level (0 = GM only, 1 = accept based on level, 2 = always accept) +# default: 1 (accept based on level) +AiPlayerbot.GroupInvitationPermission = 1 + # auto-login all player alts as bots on player login AiPlayerbot.BotAutologin = 0 @@ -21,146 +119,19 @@ AiPlayerbot.BotAutologin = 0 # Default: 0 (disabled) AiPlayerbot.AllowPlayerBots = 0 -# Guild Task system -AiPlayerbot.EnableGuildTasks = 0 - -# Enable LFG for random bots -AiPlayerbot.RandomBotJoinLfg = 1 - -# Enable dungeon suggestions for random bots -AiPlayerbot.RandomBotSuggestDungeons = 1 - -# Enable dungeon suggestions in lower case randomly -AiPlayerbot.SuggestDungeonsInLowerCaseRandomly = 0 - -# Enable BG/Arena for random Bots -AiPlayerbot.RandomBotJoinBG = 1 - -# Enable Auto join BG - bots randomly join WSG and 2v2 Arena if server is not lagging -AiPlayerbot.RandomBotAutoJoinBG = 0 - -# Mark many quests <= Bot level as complete (slows down bot creation) -AiPlayerbot.PreQuests = 0 - -# Random bot count -AiPlayerbot.MinRandomBots = 50 -AiPlayerbot.MaxRandomBots = 50 -AiPlayerbot.RandomBotMinLevel = 1 -AiPlayerbot.RandomBotMaxLevel = 80 - -# Enable/Disable rotation of bots (randomly select a bot from the bots pool to go online and rotate them periodically) -# Need to reset rndbot after changing the setting (.playerbot rndbot reset) -# default: 0 (disable, the online bots are fixed) -AiPlayerbot.EnableRotation = 0 - -# Bots pool size for rotation (should be less than RandomBotAccountCount * 10) -AiPlayerbot.RotationPoolSize = 500 - -# Accounts to create for random bots -AiPlayerbot.RandomBotAccountPrefix = "rndbot" -AiPlayerbot.RandomBotAccountCount = 200 - -# Random bot guild count -AiPlayerbot.RandomBotGuildCount = 20 - -# Delete all random bot guilds -AiPlayerbot.DeleteRandomBotGuilds = 0 - -# Random bot arena team count -AiPlayerbot.RandomBotArenaTeamCount = 20 - -# Delete all random bot arena teams -AiPlayerbot.DeleteRandomBotArenaTeams = 0 - -# Change random bot has lower gear -AiPlayerbot.RandomGearLoweringChance = 0 - -# Chance random bot has max level on first randomize (default 0.15) -AiPlayerbot.RandomBotMaxLevelChance = 0.15 - -# Chance bot chooses RPG (Teleport to random camp for their level) instead of grinding -AiPlayerbot.RandomBotRpgChance = 0.20 #unused now - -# Set randombots movement speed to walking anywhere -AiPlayerbot.RandombotsWalkingRPG = 0 - -# Set randombots movement speed to walking only inside buildings -AiPlayerbot.RandombotsWalkingRPG.InDoors = 0 - -# Bots greet to the players -AiPlayerbot.EnableGreet = 0 - # Bots will be summoned to player when accept group invitation AiPlayerbot.SummonWhenGroup = 1 -# Show helmet and cloak on randombots (reset required) -AiPlayerbot.RandomBotShowHelmet = 1 -AiPlayerbot.RandomBotShowCloak = 1 +# Allow/deny bots from your guild +AiPlayerbot.AllowGuildBots = 1 -# Fix the level of random bot (won't level up by grinding) -# Default: 0 (disable) -AiPlayerbot.RandomBotFixedLevel = 0 +# Added following config +# Selfbot permission level (0 = disabled, 1 = gm only (default), 2 = all players, 3 = activate on login) +AiPlayerbot.SelfBotLevel = 1 -# Disable random levels for randombots -# Every bots started on the specified level and level up by killing mobs. -AiPlayerbot.DisableRandomLevels = 0 - -# Set randombots starting level here if "AiPlayerbot.DisableRandomLevels" enabled -AiPlayerbot.RandombotStartingLevel = 5 - -# Set kill XP rate for bots (default: 1) -# Server XP Rate * AiPlayerbot.KillXPRate -AiPlayerbot.KillXPRate = 1 - -# Disable death knight for bots login -# Need to reset rndbot after changing the setting (.playerbot rndbot reset) -AiPlayerbot.DisableDeathKnightLogin = 0 - -# Specify percent of active bots -# The default is 10. With 10% of all bots going active or inactive each minute. -AiPlayerbot.BotActiveAlone = 100 - -# Set minimum level of randombots where gets enchants on items (Maxlevel + 1 to disable) -# Default: 60 -AiPlayerbot.MinEnchantingBotLevel = 60 - -# Enable expansion limitation -# Default: 1 -AiPlayerbot.LimitEnchantExpansion = 1 - -# Randombots checking players gear score level and deny the group invite if it's too low -# Default: 0 (disabled) -AiPlayerbot.GearScoreCheck = 0 - -# Quest that will be completed and rewarded to all random bots -AiPlayerbot.RandomBotQuestIds = "7848,3802,5505,6502,7761,10277,10285,11492,24499,24511,24710,24712" - -# Randombots will group with nearby bots to do shared quests -AiPlayerbot.RandomBotGroupNearby = 0 - -# Bots without a master will say their lines -AiPlayerbot.RandomBotSayWithoutMaster = 0 - -# Bots will say information about items when collecting them -AiPlayerbot.SayWhenCollectingItems = 1 - -# Set RandomBotMaxLevel bots to RandomBotMinLevel or not -AiPlayerbot.DowngradeMaxLevelBot = 0 - -# Enable/Disable bot equipments persistence (stop random initialization) after certain level (EquipmentPersistenceLevel) -# default: 0 (disable) -AiPlayerbot.EquipmentPersistence = 0 - -# default: 80 -AiPlayerbot.EquipmentPersistenceLevel = 80 - -# Bot group invitation permission level (0 = GM only, 1 = accept based on level, 2 = always accept) -# default: 1 (accept based on level) -AiPlayerbot.GroupInvitationPermission = 1 - -# Enable/Disable bot revive and repair gear when summon (0 = never, 1 = enable when non-combat and alive, 2 = enable always) -# default: 1 (enable for non-combat) -AiPlayerbot.BotReviveWhenSummon = 1 +# Give free food to bots +# Default: 1 (enabled) +AiPlayerbot.FreeFood = 1 # Non-GM player can only use init=auto to initialize bots based on their own level and gear score # default: 0 (non-gm player can use any intialization commands) @@ -170,38 +141,52 @@ AiPlayerbot.AutoInitOnly = 0 # default: 1.0 (same with the player) AiPlayerbot.AutoInitEquipLevelLimitRatio = 1.0 -# Enable/Disable create bot by addclass command (0 = GM only, 1 = enable) +# Bot automatically trains spells when talking to trainer (yes = train all available spells as long as the bot has the money, free = auto trains with no money cost, no = only list spells) +AiPlayerbot.AutoTrainSpells = yes + +# +# +# +#################################################################################################### + +#################################################################################################### +# SUMMON OPTIONS +# +# + +# Enable/Disable summoning bots when the master is in combat +# default: 1 (enabled) +AiPlayerbot.AllowSummonInCombat = 1 + +# Enable/Disable summoning bots when the master is dead +# default: 1 (enabled) +AiPlayerbot.AllowSummonWhenMasterIsDead = 1 + +# Enable/Disable summoning bots when they are dead (0 = only when the bots are ghosts, 1 = always) +# default: 1 (always) +AiPlayerbot.AllowSummonWhenBotIsDead = 1 + +# Enable/Disable reviving the bots when summoning them # default: 1 (enable) -AiPlayerbot.AddClassCommand = 1 +AiPlayerbot.ReviveBotWhenSummoned = 1 -# Enable/Disable maintenance command, learn all available spells and skills, supplement consumables, repair, etc. -# default: 1 (enable) -AiPlayerbot.MaintenanceCommand = 1 +# Enable/Disable bot repair gear when summon (0 = no, 1 = yes) +# default: 1 +AiPlayerbot.BotRepairWhenSummon = 1 -# Enable/Disable autogear command, auto upgrade player bots gears, the quality is limited by AutoGearQualityLimit and AutoGearScoreLimit -# default: 1 (enable) -AiPlayerbot.AutoGearCommand = 1 +# +# +# +#################################################################################################### -# Equips quality limitation for auto gear command (1 = normal, 2 = uncommon, 3 = rare, 4 = epic, 5 = legendary) -# default: 3 (rare) -AiPlayerbot.AutoGearQualityLimit = 3 +#################################################################################################### +# GEAR +# +# -# Equips gear score limitation for auto gear command (0 = no limit) -# default: 0 (no limit) -AiPlayerbot.AutoGearScoreLimit = 0 - -# Automation - -# Bots keep looting when group loop method is free for all -# Default: 0 (disabled) -AiPlayerbot.FreeMethodLoot = 0 - -# Bots loot roll level (0 = pass, 1 = greed, 2 = need) -# Default: 1 (greed) -AiPlayerbot.LootRollLevel = 1 - -# Bots pick their quest reward (yes = picks first useful item, no = list all rewards, ask = pick useful item and lists if multiple) -AiPlayerbot.AutoPickReward = no +# Show helmet and cloak on randombots (reset required) +AiPlayerbot.RandomBotShowHelmet = 1 +AiPlayerbot.RandomBotShowCloak = 1 # Bots equip upgrades (Bots will equip any item obtained from looting or a quest if they are upgrades) # Default: 1 (enable) @@ -211,74 +196,53 @@ AiPlayerbot.AutoEquipUpgradeLoot = 1 # Default: 1.1 (Equip when the equipment score is 1.1 times higher than the current) AiPlayerbot.EquipUpgradeThreshold = 1.1 -# Sync quests with player (Bots will complete quests the moment you hand them in. Bots will ignore looting quest items.) -# Default: 1 (enable) -AiPlayerbot.SyncQuestWithPlayer = 1 +# +# +# +#################################################################################################### -# Bots will auto-complete quests for the player when handing in +#################################################################################################### +# LOOTING +# +# + +# Bots will say information about items when collecting them +AiPlayerbot.SayWhenCollectingItems = 1 + +# Bots keep looting when group loop method is free for all # Default: 0 (disabled) -AiPlayerbot.SyncQuestForPlayer = 0 +AiPlayerbot.FreeMethodLoot = 0 -# Sync max random bot level with max level of online players -# Default: 0 (disabled) -AiPlayerbot.SyncLevelWithPlayers = 0 +# Bots loot roll level (0 = pass, 1 = greed, 2 = need) +# Default: 1 (greed) +AiPlayerbot.LootRollLevel = 1 -# Give free food to random bots -# Default: 0 (disabled) -AiPlayerbot.FreeFood = 0 +# +# +# +#################################################################################################### -# Bot automatically trains spells when talking to trainer (yes = train all available spells as long as the bot has the money, free = auto trains with no money cost, no = only list spells) -# Only for random bots -AiPlayerbot.AutoTrainSpells = yes - -# Bots automatically learn classquest reward spells on levelup -# Only for random bots -# Default: 0 (disabled) -AiPlayerbot.AutoLearnQuestSpells = 0 - -# Bots automatically learn trainable spells on levelup -# Only for random bots -# Default: 1 (enabled) -AiPlayerbot.AutoLearnTrainerSpells = 1 - -# Bots automatically teleport to another place for leveling on levelup -# Only for random bots -# Default: 1 (enabled) -AiPlayerbot.AutoTeleportForLevel = 1 - -# Bot automatically picks talent points on levelup -# Only for random bots -# Default: 1 (enabled) -AiPlayerbot.AutoPickTalents = 1 - -# Bot automatically upgrade equipments on levelup -# Only for random bots -# Default: 1 (enabled) -AiPlayerbot.AutoUpgradeEquip = 1 - -# Random Bots will pick quests on their own and try to complete -# Only for random bots -# Default: 1 (enabled) -AiPlayerbot.AutoDoQuests = 1 - -# Prefix for bot chat commands (e.g. follow, stay) -AiPlayerbot.CommandPrefix = "" - -# Separator for bot chat commands -AiPlayerbot.CommandSeparator = "\\\\" +#################################################################################################### +# TIMERS +# +# # Max AI iterations per tick AiPlayerbot.IterationsPerTick = 10 -# Allow/deny bots from your guild -AiPlayerbot.AllowGuildBots = 1 - # Delay between two short-time spells cast AiPlayerbot.GlobalCooldown = 500 # Max wait time when moving AiPlayerbot.MaxWaitForMove = 5000 +# Disables use of MoveSplinePath for bot movement, will result in more erratic bot movement but means stun/snare/root/etc +# will work on bots (they wont reliably work when MoveSplinePath is enabled, though slowing effects still work ok) +# Default: 0 - MoveSplinePath enabled +# 1 - MoveSplinePath disabled in BG/Arena only +# 2 - MoveSplinePath disabled everywhere +AiPlayerbot.DisableMoveSplinePath = 0 + # Max search time for movement (higher for better movement on slopes) # default: 3 AiPlayerbot.MaxMovementSearchTime = 3 @@ -296,7 +260,7 @@ AiPlayerbot.ReactDelay = 100 AiPlayerbot.PassiveDelay = 10000 # Minimum delay between repeating actions (chat messages, emotes etc) -AiPlayerbot.RepeatDelay = 5000 +AiPlayerbot.RepeatDelay = 2000 # Delay timers AiPlayerbot.ErrorDelay = 100 @@ -306,7 +270,16 @@ AiPlayerbot.SitDelay = 20000 AiPlayerbot.ReturnDelay = 2000 AiPlayerbot.LootDelay = 1000 -# Distances +# +# +# +#################################################################################################### + +#################################################################################################### +# DISTANCES +# +# + AiPlayerbot.FarDistance = 20.0 AiPlayerbot.SightDistance = 75.0 AiPlayerbot.SpellDistance = 28.5 @@ -325,8 +298,19 @@ AiPlayerbot.AoeRadius = 10 AiPlayerbot.RpgDistance = 200 AiPlayerbot.AggroDistance = 22 -# Bot can flee for enemy -AiPlayerbot.FleeingEnabled = 1 +# +# +# +#################################################################################################### + +#################################################################################################### +#THRESHOLDS +# +# + +# Set kill XP rate for bots (default: 1) +# Server XP Rate * AiPlayerbot.KillXPRate +AiPlayerbot.KillXPRate = 1 # Health/Mana levels AiPlayerbot.CriticalHealth = 25 @@ -336,13 +320,39 @@ AiPlayerbot.AlmostFullHealth = 85 AiPlayerbot.LowMana = 15 AiPlayerbot.MediumMana = 40 -# Enable healer bot save mana -# Default: 1 (enable) -AiPlayerbot.AutoSaveMana = 1 +# +# +# +#################################################################################################### -# Healer bot save mana threshold -# Default: 60 (60%) -AiPlayerbot.SaveManaThreshold = 60 +#################################################################################################### +# QUESTS +# +# + +# Bots pick their quest reward (yes = picks first useful item, no = list all rewards, ask = pick useful item and lists if multiple) +AiPlayerbot.AutoPickReward = no + +# Sync quests with player (Bots will complete quests the moment you hand them in. Bots will ignore looting quest items.) +# Default: 1 (enable) +AiPlayerbot.SyncQuestWithPlayer = 1 + +# Bots will auto-complete quests for the player when handing in +# Default: 0 (disabled) +AiPlayerbot.SyncQuestForPlayer = 0 + +# +# +# +#################################################################################################### + +#################################################################################################### +# COMBAT +# +# + +# Bot can flee for enemy +AiPlayerbot.FleeingEnabled = 1 # Enable auto avoid aoe (experimental) # Default: 1 (enable) @@ -352,6 +362,241 @@ AiPlayerbot.AutoAvoidAoe = 1 # Default: 1 (enable) AiPlayerbot.TellWhenAvoidAoe = 1 +# Enable healer bot save mana +# Default: 1 (enable) +AiPlayerbot.AutoSaveMana = 1 + +# Healer bot save mana threshold +# Default: 60 (60%) +AiPlayerbot.SaveManaThreshold = 60 + +# +# +# +#################################################################################################### + +#################################################################################################### +# CHEATS +# +# + +# Enable/Disable maintenance command, learn all available spells and skills, supplement consumables, repair, etc. +# default: 1 (enable) +AiPlayerbot.MaintenanceCommand = 1 + +# Enable/Disable autogear command, auto upgrade player bots gears, the quality is limited by AutoGearQualityLimit and AutoGearScoreLimit +# default: 1 (enable) +AiPlayerbot.AutoGearCommand = 1 + +# Equips quality limitation for auto gear command (1 = normal, 2 = uncommon, 3 = rare, 4 = epic, 5 = legendary) +# default: 3 (rare) +AiPlayerbot.AutoGearQualityLimit = 3 + +# Equips gear score limitation for auto gear command (0 = no limit) +# default: 0 (no limit) +AiPlayerbot.AutoGearScoreLimit = 0 + +# Enables/Disables bot cheating +AiPlayerbot.BotCheats = "taxi" + +# +# +# +#################################################################################################### + +#################################################################################################### +# SPELLS +# +# + +# ID of spell to open lootable chests +AiPlayerbot.OpenGoSpell = 6477 + +# +# +# +################################################################################################### + +####################################### +# # +# PLAYERBOT RNDBOT SPECIFIC SETTINGS # +# # +####################################### + +#################################################################################################### +# GENERAL +# +# + +# Enables/Disables password to bot account +AiPlayerbot.RandomBotRandomPassword = 0 + +# Accounts to create for random bots +AiPlayerbot.RandomBotAccountPrefix = "rndbot" + +# Enable/Disable rotation of bots (randomly select a bot from the bots pool to go online and rotate them periodically) +# Need to reset rndbot after changing the setting (.playerbot rndbot reset) +# default: 0 (disable, the online bots are fixed) +AiPlayerbot.EnableRotation = 0 + +# Bots pool size for rotation (should be less than RandomBotAccountCount * 10) +AiPlayerbot.RotationPoolSize = 500 + +AiPlayerbot.RandomBotMinLevel = 1 +AiPlayerbot.RandomBotMaxLevel = 80 + +# Sync max random bot level with max level of online players +# Default: 0 (disabled) +AiPlayerbot.SyncLevelWithPlayers = 0 + +# Mark many quests <= Bot level as complete (slows down bot creation) +AiPlayerbot.PreQuests = 0 + +# Bots without a master will say their lines +AiPlayerbot.RandomBotSayWithoutMaster = 0 + +# Enable LFG for random bots +AiPlayerbot.RandomBotJoinLfg = 1 + +# Disable death knight for bots login +# Need to reset rndbot after changing the setting (.playerbot rndbot reset) +AiPlayerbot.DisableDeathKnightLogin = 0 + +# +# +# +#################################################################################################### + +#################################################################################################### +# LEVELS +# +# + +# Disable random levels for randombots +# Every bots started on the specified level and level up by killing mobs. +AiPlayerbot.DisableRandomLevels = 0 + +# Set randombots starting level here if "AiPlayerbot.DisableRandomLevels" enabled +AiPlayerbot.RandombotStartingLevel = 5 + +# Chance random bot has max level on first randomize (default 0.15) +AiPlayerbot.RandomBotMaxLevelChance = 0.15 + +# Fix the level of random bot (won't level up by grinding) +# Default: 0 (disable) +AiPlayerbot.RandomBotFixedLevel = 0 + +# Set RandomBotMaxLevel bots to RandomBotMinLevel or not +AiPlayerbot.DowngradeMaxLevelBot = 0 + +# +# +# +#################################################################################################### + +#################################################################################################### +# GEAR +# +# + +# Equips quality limitation for random bots (1 = normal, 2 = uncommon, 3 = rare, 4 = epic, 5 = legendary) +# default: 3 (rare) +AiPlayerbot.RandomGearQualityLimit = 3 + +# Equips gear score limitation for random bots (0 = no limit) +# default: 0 (no limit) +AiPlayerbot.RandomGearScoreLimit = 0 + +# Set minimum level of randombots where gets enchants on items (Maxlevel + 1 to disable) +# Default: 60 +AiPlayerbot.MinEnchantingBotLevel = 60 + +# Enable expansion limitation for enchants - ie: level <= 60 bot only uses enchants +# available in vanilla, level <= 70 bot only uses enchants available in TBC) +# Default: 0 +AiPlayerbot.LimitEnchantExpansion = 0 + +# Enable expansion limitation for gear - ie: level <= 60 bot only uses gear +# available in vanilla, level <= 70 bot only uses gear available in TBC) +# Default: 0 +AiPlayerbot.LimitGearExpansion = 0 + +# Change random bot has lower gear +AiPlayerbot.RandomGearLoweringChance = 0 + +# Randombots checking players gear score level and deny the group invite if it's too low +# Default: 0 (disabled) +AiPlayerbot.GearScoreCheck = 0 + +# Enable/Disable bot equipments persistence (stop random initialization) after certain level (EquipmentPersistenceLevel) +# default: 0 (disable) +AiPlayerbot.EquipmentPersistence = 0 + +# default: 80 +AiPlayerbot.EquipmentPersistenceLevel = 80 + +# Bot automatically upgrade equipments on levelup +# Default: 1 (enabled) +AiPlayerbot.AutoUpgradeEquip = 1 + +# +# +# +#################################################################################################### + +#################################################################################################### +# QUESTS +# +# + +# Quest that will be completed and rewarded to all random bots +AiPlayerbot.RandomBotQuestIds = "7848,3802,5505,6502,7761,10277,10285,11492,24499,24511,24710,24712" + +# Randombots will group with nearby bots to do shared quests +AiPlayerbot.RandomBotGroupNearby = 0 + +# Random Bots will pick quests on their own and try to complete +# Default: 1 (enabled) +AiPlayerbot.AutoDoQuests = 1 + +# Quest items to leave (do not destroy) +AiPlayerbot.RandomBotQuestItems = "6948,5175,5176,5177,5178,16309,12382,13704,11000" + +# +# +# +#################################################################################################### + +#################################################################################################### +# SPELLS +# +# + +# Bots automatically learn classquest reward spells on levelup +# Default: 0 (disabled) +AiPlayerbot.AutoLearnQuestSpells = 0 + +# Bots automatically learn trainable spells on levelup +# Default: 1 (enabled) +AiPlayerbot.AutoLearnTrainerSpells = 1 + +# Bot automatically picks talent points on levelup +# Default: 1 (enabled) +AiPlayerbot.AutoPickTalents = 1 + +# Spells every random bot will learn on randomize (54197 - cold weather flying) +AiPlayerbot.RandomBotSpellIds = "54197" + +# +# +# +#################################################################################################### + +#################################################################################################### +# STRATEGIES +# +# + # Random bot default strategies (applied after defaults) AiPlayerbot.RandomBotCombatStrategies = "+dps,+dps assist,-threat" # AiPlayerbot.RandomBotNonCombatStrategies = "+grind,+loot,+rpg,+custom::say" @@ -359,26 +604,15 @@ AiPlayerbot.RandomBotNonCombatStrategies = "" AiPlayerbot.CombatStrategies = "" AiPlayerbot.NonCombatStrategies = "" -# How often tasks are changed -AiPlayerbot.MinGuildTaskChangeTime = 172800 -AiPlayerbot.MaxGuildTaskChangeTime = 432000 +# +# +# +#################################################################################################### -# Mail spam interval -AiPlayerbot.MinGuildTaskAdvertisementTime = 300 -AiPlayerbot.MaxGuildTaskAdvertisementTime = 28800 - -# Delay before reward is sent -AiPlayerbot.MinGuildTaskRewardTime = 300 -AiPlayerbot.MaxGuildTaskRewardTime = 3600 - -# Cleanup of guild tasks interval -AiPlayerbot.GuildTaskAdvertCleanupTime = 300 - -# Specify max distance between victim and bot when creating guild kill task -AiPlayerbot.GuildTaskKillTaskDistance = 200 - -# Distance margin for facade calculations -AiPlayerbot.TargetPosRecalcDistance = 0.1 +#################################################################################################### +# TELEPORTS +# +# # Maps where bots can be teleported to AiPlayerbot.RandomBotMaps = 0,1,530,571 @@ -387,91 +621,540 @@ AiPlayerbot.RandomBotMaps = 0,1,530,571 # default: 0.25 AiPlayerbot.ProbTeleToBankers = 0.25 -# Quest items to leave (do not destroy) -AiPlayerbot.RandomBotQuestItems = "6948,5175,5176,5177,5178,16309,12382,13704,11000" - -# 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" # 33(stranglethorn vale),440(tanaris) - -# PvP Restricted Areas (bots don't pvp) -AiPlayerbot.PvpProhibitedAreaIds = "976,35,392" - -# Spells every random bot will learn on randomize (54197 - cold weather flying) -AiPlayerbot.RandomBotSpellIds = "54197" +# How far random bots are teleported after death +AiPlayerbot.RandomBotTeleportDistance = 100 # Level diff between random bots and nearby creatures for random teleports AiPlayerbot.RandomBotTeleLowerLevel = 3 AiPlayerbot.RandomBotTeleHigherLevel = 1 -# ID of spell to open lootable chests -AiPlayerbot.OpenGoSpell = 6477 +# Bots automatically teleport to another place for leveling on levelup +# Only for random bots +# Default: 1 (enabled) +AiPlayerbot.AutoTeleportForLevel = 1 -# Intervals +# +# +# +#################################################################################################### + +#################################################################################################### +# BATTLEGROUNDS & ARENAS & PVP +# +# + +# Enable BG/Arena for random Bots +AiPlayerbot.RandomBotJoinBG = 1 + +# Enable Auto join BG - bots randomly join WSG and 2v2 Arena if server is not lagging +AiPlayerbot.RandomBotAutoJoinBG = 0 + +# Random bot arena team count +AiPlayerbot.RandomBotArenaTeamCount = 20 + +# Delete all random bot arena teams +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" # 33(stranglethorn vale),440(tanaris) + +# PvP Restricted Areas (bots don't pvp) +AiPlayerbot.PvpProhibitedAreaIds = "976,35,392" + +# +# +# +#################################################################################################### + +#################################################################################################### +# INTERVALS +# +# + +# All In seconds AiPlayerbot.RandomBotUpdateInterval = 20 AiPlayerbot.RandomBotCountChangeMinInterval = 1800 AiPlayerbot.RandomBotCountChangeMaxInterval = 7200 AiPlayerbot.MinRandomBotInWorldTime = 3600 AiPlayerbot.MaxRandomBotInWorldTime = 43200 AiPlayerbot.MinRandomBotRandomizeTime = 302400 -AiPlayerbot.MaxRandomRandomizeTime = 1209600 +AiPlayerbot.MaxRandomBotRandomizeTime = 1209600 AiPlayerbot.RandomBotsPerInterval = 500 -AiPlayerbot.MinRandomBotsPriceChangeInterval = 7200 -AiPlayerbot.MaxRandomBotsPriceChangeInterval = 172800 -AiPlayerbot.MinRandomBotChangeStrategyTime = 180 -AiPlayerbot.MaxRandomBotChangeStrategyTime = 720 AiPlayerbot.MinRandomBotReviveTime = 60 AiPlayerbot.MaxRandomBotReviveTime = 300 AiPlayerbot.MinRandomBotTeleportInterval = 3600 AiPlayerbot.MaxRandomBotTeleportInterval = 18000 AiPlayerbot.RandomBotInWorldWithRotationDisabled = 31104000 -# How far random bots are teleported after death -AiPlayerbot.RandomBotTeleportDistance = 100 +# +# +# +#################################################################################################### -# Debug switches -AiPlayerbot.SpellDump = 0 -AiPlayerbot.LogInGroupOnly = 1 -AiPlayerbot.LogValuesPerTick = 0 -AiPlayerbot.RandomChangeMultiplier = 1 +################################### +# # +# PREMADE SPECS # +# # +################################### -# Command server port, 0 - disabled -AiPlayerbot.CommandServerPort = 8888 +################################################################################################### +# INFORMATION +# +# -# Enables/Disables performance monitor -AiPlayerbot.PerfMonEnabled = 0 - -# Allow bots to be summoned near innkeepers -AiPlayerbot.SummonAtInnkeepersEnabled = 1 - -# Custom config to allow logfiles to be created. -# Example: AiPlayerbot.AllowedLogFiles = travelNodes.csv,travelPaths.csv,TravelNodeStore.h,bot_movement.csv,bot_location.csv -AiPlayerbot.AllowedLogFiles = "" - -# Applies a permanent buff to all bots. -# WorldBuff.Faction.Class.MinLevel.MaxLevel - -# Added following config -# Selfbot permission level (0 = disabled, 1 = gm only (default), 2 = all players, 3 = activate on login) -AiPlayerbot.SelfBotLevel = 1 - -# Enables/Disables bot cheating -AiPlayerbot.BotCheats = "taxi" - -# Enables/Disables password to bot account -AiPlayerbot.RandomBotRandomPassword = 0 - -# Diff with/without player in server. The server will tune bot activity to reach the desired server tick speed (in ms). -AiPlayerbot.EnablePrototypePerformanceDiff = 0 -AiPlayerbot.DiffWithPlayer = 100 -AiPlayerbot.DiffEmpty = 200 - -################################################################################## -# # -# Database Stuff # -# # -################################################################################## +# AiPlayerbot.PremadeSpecName.. = #Name of the talent specialisation +# AiPlayerbot.PremadeSpecLink... = #Wowhead style link the bot should work towards at given level. +# AiPlayerbot.PremadeSpecGlyph.. = ,,,,, #ItemId of the glyphs +# e.g., formulate the link on https://www.wowhead.com/wotlk/talent-calc/warrior/3022032123335100202012013031251-32505010002 +# 0 <= specno < 20, 1 <= level <= 80 # +# +# +################################################################################################### + +################################################################################################### +# WARRIOR +# +# + +AiPlayerbot.PremadeSpecName.1.0 = arms pve +AiPlayerbot.PremadeSpecGlyph.1.0 = 43418,43395,43423,43399,49084,43421 +AiPlayerbot.PremadeSpecLink.1.0.60 = 3022032023335100202012013031241 +AiPlayerbot.PremadeSpecLink.1.0.80 = 3022032123335100202012013031251-32505010002 +AiPlayerbot.PremadeSpecName.1.1 = fury pve +AiPlayerbot.PremadeSpecGlyph.1.1 = 43418,43395,43414,43399,49084,43432 +AiPlayerbot.PremadeSpecLink.1.1.60 = -305053000500310053120501351 +AiPlayerbot.PremadeSpecLink.1.1.80 = 30202300233-305053000500310153120511351 +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 + +# +# +# +################################################################################################### + +################################################################################################### +# PALADIN +# +# + +AiPlayerbot.PremadeSpecName.2.0 = holy pve +AiPlayerbot.PremadeSpecGlyph.2.0 = 41106,43367,45741,43369,43365,41109 +AiPlayerbot.PremadeSpecLink.2.0.60 = 50350151020013053100515221 +AiPlayerbot.PremadeSpecLink.2.0.80 = 50350152220013053100515221-503201312 +AiPlayerbot.PremadeSpecName.2.1 = prot pve +AiPlayerbot.PremadeSpecGlyph.2.1 = 41100,43367,43869,43369,43365,45745 +AiPlayerbot.PremadeSpecLink.2.1.60 = -05005135203102311333112321 +AiPlayerbot.PremadeSpecLink.2.1.80 = -05005135203132311333312321-5023005 +AiPlayerbot.PremadeSpecName.2.2 = ret pve +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 + +# +# +# +################################################################################################### + +################################################################################################### +# HUNTER +# +# + +AiPlayerbot.PremadeSpecName.3.0 = bm pve +AiPlayerbot.PremadeSpecGlyph.3.0 = 42912,43350,42902,43351,43338,45732 +AiPlayerbot.PremadeSpecLink.3.0.60 = 51200201505112243100511351 +AiPlayerbot.PremadeSpecLink.3.0.80 = 51200201505112253100531351-015305021 +AiPlayerbot.PremadeSpecName.3.1 = mm pve +AiPlayerbot.PremadeSpecGlyph.3.1 = 42912,43350,42915,43351,43338,45732 +AiPlayerbot.PremadeSpecLink.3.1.60 = -015305101230013233135030051 +AiPlayerbot.PremadeSpecLink.3.1.80 = 502-035305101230013233135031351-5000002 +AiPlayerbot.PremadeSpecName.3.2 = surv pve +AiPlayerbot.PremadeSpecGlyph.3.2 = 42912,43350,45731,43351,43338,45732 +AiPlayerbot.PremadeSpecLink.3.2.60 = --5000032500033330502135001331 +AiPlayerbot.PremadeSpecLink.3.2.80 = -005305101-5000032500033330522135301331 + +# +# +# +################################################################################################### + +################################################################################################### +# ROGUE +# +# + +AiPlayerbot.PremadeSpecName.4.0 = as pve +AiPlayerbot.PremadeSpecGlyph.4.0 = 45768,43379,45761,43380,43378,45766 +AiPlayerbot.PremadeSpecLink.4.0.60 = 005323005350100520103331051 +AiPlayerbot.PremadeSpecLink.4.0.80 = 005323005350100520103331051-005005005003-2 +AiPlayerbot.PremadeSpecName.4.1 = combat pve +AiPlayerbot.PremadeSpecGlyph.4.1 = 42962,43379,45762,43380,43378,42969 +AiPlayerbot.PremadeSpecLink.4.1.60 = -0252051000035015223100501251 +AiPlayerbot.PremadeSpecLink.4.1.80 = 00532000523-0252051000035015223100501251 +AiPlayerbot.PremadeSpecName.4.2 = subtlety pve +AiPlayerbot.PremadeSpecGlyph.4.2 = 42967,43379,45764,43380,43378,45767 +AiPlayerbot.PremadeSpecLink.4.2.60 = --5120122030321121050135031241 +AiPlayerbot.PremadeSpecLink.4.2.80 = 0053231-2-5120222030321121050135231251 + +# +# +# +################################################################################################### + +################################################################################################### +# PRIEST +# +# + +AiPlayerbot.PremadeSpecName.5.0 = disc pve +AiPlayerbot.PremadeSpecGlyph.5.0 = 42408,43371,42400,43374,43342,45756 +AiPlayerbot.PremadeSpecLink.5.0.60 = 0503203130300512301323131051 +AiPlayerbot.PremadeSpecLink.5.0.80 = 0503203130300512331323231251-03520103 +AiPlayerbot.PremadeSpecName.5.1 = holy pve +AiPlayerbot.PremadeSpecGlyph.5.1 = 42408,43371,42400,43374,43342,42396 +AiPlayerbot.PremadeSpecLink.5.1.60 = -035050031301152530000331331 +AiPlayerbot.PremadeSpecLink.5.1.80 = 05032031-235050032302152530000331351 +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 + +# +# +# +################################################################################################### + +################################################################################################### +# DEATHKNIGHT +# +# + +AiPlayerbot.PremadeSpecName.6.0 = blood pve +AiPlayerbot.PremadeSpecGlyph.6.0 = 45805,43673,43827,43544,43672,43554 +AiPlayerbot.PremadeSpecLink.6.0.60 = 035502150300331320102013111-005 +AiPlayerbot.PremadeSpecLink.6.0.80 = 0355021533003313201020131351-005-005032 +AiPlayerbot.PremadeSpecName.6.1 = frost pve +AiPlayerbot.PremadeSpecGlyph.6.1 = 45805,43673,43547,43544,43672,43543 +AiPlayerbot.PremadeSpecLink.6.1.60 = -32003350332203012300023101351 +AiPlayerbot.PremadeSpecLink.6.1.80 = -32002350352203012300033101351-230200305003 +AiPlayerbot.PremadeSpecName.6.2 = unholy pve +AiPlayerbot.PremadeSpecGlyph.6.2 = 43542,43673,45804,43544,43672,43549 +AiPlayerbot.PremadeSpecLink.6.2.60 = --2300303050032152000150213130051 +AiPlayerbot.PremadeSpecLink.6.2.80 = -320053500002-2300303050032152000150213130051 +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 + +# +# +# +################################################################################################### + +################################################################################################### +# SHAMAN +# +# + +AiPlayerbot.PremadeSpecName.7.0 = ele pve +AiPlayerbot.PremadeSpecGlyph.7.0 = 41536,43385,41532,43386,44923,45776 +AiPlayerbot.PremadeSpecLink.7.0.60 = 4530001520213351102301351 +AiPlayerbot.PremadeSpecLink.7.0.80 = 3530001523213351322301351-005050031 +AiPlayerbot.PremadeSpecName.7.1 = enh pve +AiPlayerbot.PremadeSpecGlyph.7.1 = 41530,43385,41539,43386,44923,41540 +AiPlayerbot.PremadeSpecLink.7.1.60 = -30205033005001333031131131051 +AiPlayerbot.PremadeSpecLink.7.1.80 = 053030052-30205033005021333031131131051 +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 + +# +# +# +################################################################################################### + +################################################################################################### +# MAGE +# +# + +AiPlayerbot.PremadeSpecName.8.0 = arcane pve +AiPlayerbot.PremadeSpecGlyph.8.0 = 42735,43339,44955,43364,43361,42751 +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 +AiPlayerbot.PremadeSpecLink.8.1.80 = 23000503110003-0055030011302331053120321351 +AiPlayerbot.PremadeSpecName.8.2 = frost pve +AiPlayerbot.PremadeSpecGlyph.8.2 = 42742,43339,50045,43364,43361,42751 +AiPlayerbot.PremadeSpecLink.8.2.60 = --3533103310203100232102231151 +AiPlayerbot.PremadeSpecLink.8.2.80 = 23002322010203--0533003313203100232112231151 + +# +# +# +################################################################################################### + +################################################################################################### +# WARLOCK +# +# + +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 = 2350022001113510253500331151--5500000501 +AiPlayerbot.PremadeSpecName.9.1 = emo pve +AiPlayerbot.PremadeSpecGlyph.9.1 = 45785,43390,50077,43394,43393,42459 +AiPlayerbot.PremadeSpecLink.9.1.60 = -003203301135112530131201-55 +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 = --05203205210131051313230341 +AiPlayerbot.PremadeSpecLink.9.2.80 = -03310030003-05203205210331051335230351 + +# +# +# +################################################################################################### + +################################################################################################### +# DRUID +# +# + +AiPlayerbot.PremadeSpecName.11.0 = balance pve +AiPlayerbot.PremadeSpecGlyph.11.0 = 40916,43331,40921,43335,44922,40919 +AiPlayerbot.PremadeSpecLink.11.0.60 = 5012203115331003213302301231 +AiPlayerbot.PremadeSpecLink.11.0.80 = 5012203125331103213305301231--205003212 +AiPlayerbot.PremadeSpecName.11.1 = bear pve +AiPlayerbot.PremadeSpecGlyph.11.1 = 40897,43331,46372,43335,43332,40899 +AiPlayerbot.PremadeSpecLink.11.1.60 = -500232130322110353100301310501 +AiPlayerbot.PremadeSpecLink.11.1.80 = -501232130322110353120303313511-20350001 +AiPlayerbot.PremadeSpecName.11.2 = resto pve +AiPlayerbot.PremadeSpecGlyph.11.2 = 40913,43331,40906,43335,44922,45602 +AiPlayerbot.PremadeSpecLink.11.2.60 = --230033312031501531050013051 +AiPlayerbot.PremadeSpecLink.11.2.80 = 05320001--230033312031512531153313051 +AiPlayerbot.PremadeSpecName.11.3 = cat pve +AiPlayerbot.PremadeSpecGlyph.11.3 = 40902,43331,40901,43335,44922,45604 +AiPlayerbot.PremadeSpecLink.11.3.60 = -553202032322010052100030310501 +AiPlayerbot.PremadeSpecLink.11.3.80 = -553202032322010053100030310511-205503012 + +# +# +# +################################################################################################### + +################################### +# # +# RANDOM BOT DEFAULT TALENT SPEC # +# # +################################### + +#################################################################################################### +# +# +# + +# AiPlayerbot.RandomClassSpecProb.. # The probability to choose the spec +# AiPlayerbot.RandomClassSpecIndex.. # The spec index in PremadeSpec + +# +# +# +#################################################################################################### + +#################################################################################################### +# WARRIOR +# +# + +AiPlayerbot.RandomClassSpecProb.1.0 = 20 +AiPlayerbot.RandomClassSpecIndex.1.0 = 0 +AiPlayerbot.RandomClassSpecProb.1.1 = 40 +AiPlayerbot.RandomClassSpecIndex.1.1 = 1 +AiPlayerbot.RandomClassSpecProb.1.2 = 40 +AiPlayerbot.RandomClassSpecIndex.1.2 = 2 + +# +# +# +#################################################################################################### + +#################################################################################################### +# PALADIN +# +# + +AiPlayerbot.RandomClassSpecProb.2.0 = 30 +AiPlayerbot.RandomClassSpecIndex.2.0 = 0 +AiPlayerbot.RandomClassSpecProb.2.1 = 40 +AiPlayerbot.RandomClassSpecIndex.2.1 = 1 +AiPlayerbot.RandomClassSpecProb.2.2 = 30 +AiPlayerbot.RandomClassSpecIndex.2.2 = 2 + +# +# +# +#################################################################################################### + +#################################################################################################### +# HUNTER +# +# + +AiPlayerbot.RandomClassSpecProb.3.0 = 33 +AiPlayerbot.RandomClassSpecIndex.3.0 = 0 +AiPlayerbot.RandomClassSpecProb.3.1 = 33 +AiPlayerbot.RandomClassSpecIndex.3.1 = 1 +AiPlayerbot.RandomClassSpecProb.3.2 = 33 +AiPlayerbot.RandomClassSpecIndex.3.2 = 2 + +# +# +# +#################################################################################################### + +#################################################################################################### +# ROGUE +# +# + +AiPlayerbot.RandomClassSpecProb.4.0 = 45 +AiPlayerbot.RandomClassSpecIndex.4.0 = 0 +AiPlayerbot.RandomClassSpecProb.4.1 = 45 +AiPlayerbot.RandomClassSpecIndex.4.1 = 1 +AiPlayerbot.RandomClassSpecProb.4.2 = 10 +AiPlayerbot.RandomClassSpecIndex.4.2 = 2 + +# +# +# +#################################################################################################### + +#################################################################################################### +# PRIEST +# +# + +AiPlayerbot.RandomClassSpecProb.5.0 = 40 +AiPlayerbot.RandomClassSpecIndex.5.0 = 0 +AiPlayerbot.RandomClassSpecProb.5.1 = 35 +AiPlayerbot.RandomClassSpecIndex.5.1 = 1 +AiPlayerbot.RandomClassSpecProb.5.2 = 25 +AiPlayerbot.RandomClassSpecIndex.5.2 = 2 + +# +# +# +#################################################################################################### + +#################################################################################################### +# DEATHKNIGHT +# +# + +AiPlayerbot.RandomClassSpecProb.6.0 = 30 +AiPlayerbot.RandomClassSpecIndex.6.0 = 0 +AiPlayerbot.RandomClassSpecProb.6.1 = 40 +AiPlayerbot.RandomClassSpecIndex.6.1 = 1 +AiPlayerbot.RandomClassSpecProb.6.2 = 30 +AiPlayerbot.RandomClassSpecIndex.6.2 = 2 + +# +# +# +#################################################################################################### + +#################################################################################################### +# SHAMAN +# +# + +AiPlayerbot.RandomClassSpecProb.7.0 = 33 +AiPlayerbot.RandomClassSpecIndex.7.0 = 0 +AiPlayerbot.RandomClassSpecProb.7.1 = 33 +AiPlayerbot.RandomClassSpecIndex.7.1 = 1 +AiPlayerbot.RandomClassSpecProb.7.2 = 33 +AiPlayerbot.RandomClassSpecIndex.7.2 = 2 + +# +# +# +#################################################################################################### + +#################################################################################################### +# MAGE +# +# + +AiPlayerbot.RandomClassSpecProb.8.0 = 30 +AiPlayerbot.RandomClassSpecIndex.8.0 = 0 +AiPlayerbot.RandomClassSpecProb.8.1 = 30 +AiPlayerbot.RandomClassSpecIndex.8.1 = 1 +AiPlayerbot.RandomClassSpecProb.8.2 = 40 +AiPlayerbot.RandomClassSpecIndex.8.2 = 2 + +# +# +# +#################################################################################################### + +#################################################################################################### +# WARLOCK +# +# + +AiPlayerbot.RandomClassSpecProb.9.0 = 40 +AiPlayerbot.RandomClassSpecIndex.9.0 = 0 +AiPlayerbot.RandomClassSpecProb.9.1 = 40 +AiPlayerbot.RandomClassSpecIndex.9.1 = 1 +AiPlayerbot.RandomClassSpecProb.9.2 = 20 +AiPlayerbot.RandomClassSpecIndex.9.2 = 2 + +# +# +# +#################################################################################################### + +#################################################################################################### +# DRUID +# +# + +AiPlayerbot.RandomClassSpecProb.11.0 = 20 +AiPlayerbot.RandomClassSpecIndex.11.0 = 0 +AiPlayerbot.RandomClassSpecProb.11.1 = 40 +AiPlayerbot.RandomClassSpecIndex.11.1 = 1 +AiPlayerbot.RandomClassSpecProb.11.2 = 40 +AiPlayerbot.RandomClassSpecIndex.11.2 = 2 + +# +# +# +################################################################################################### + + +################################### +# # +# PLAYERBOT SYSTEM SETTINGS # +# # +################################### + +#################################################################################################### +# DATABASE & CONNECTIONS +# +# + # PlayerbotsDatabaseInfo # Description: Database connection settings for the playerbots server. # Example: "hostname;port;username;password;database" @@ -508,247 +1191,144 @@ PlayerbotsDatabase.SynchThreads = 1 Playerbots.Updates.EnableDatabases = 1 -############################################## -# PremadeSpec # -############################################## -# AiPlayerbot.PremadeSpecName.. = #Name of the talent specialisation -# AiPlayerbot.PremadeSpecLink... = #Wowhead style link the bot should work towards at given level. -# AiPlayerbot.PremadeSpecGlyph.. = ,,,,, #ItemId of the glyphs -# e.g., formulate the link on https://www.wowhead.com/wotlk/talent-calc/warrior/3022032123335100202012013031251-32505010002 -# 0 <= specno < 20, 1 <= level <= 80 +# Command server port, 0 - disabled +AiPlayerbot.CommandServerPort = 8888 -# Warrior -AiPlayerbot.PremadeSpecName.1.0 = arms pve -AiPlayerbot.PremadeSpecGlyph.1.0 = 43418,43395,43423,43399,49084,43421 -AiPlayerbot.PremadeSpecLink.1.0.60 = 3022032023335100202012013031241 -AiPlayerbot.PremadeSpecLink.1.0.80 = 3022032123335100202012013031251-32505010002 -AiPlayerbot.PremadeSpecName.1.1 = fury pve -AiPlayerbot.PremadeSpecGlyph.1.1 = 43418,43395,43414,43399,49084,43432 -AiPlayerbot.PremadeSpecLink.1.1.60 = -305053000500310053120501351 -AiPlayerbot.PremadeSpecLink.1.1.80 = 30202300233-305053000500310153120511351 -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 +# Diff with/without player in server. The server will tune bot activity to reach the desired server tick speed (in ms).# PLAYERBOT SYSTEM SETTINGS # +AiPlayerbot.EnablePrototypePerformanceDiff = 0 +AiPlayerbot.DiffWithPlayer = 100 +AiPlayerbot.DiffEmpty = 200 -# Paladin -AiPlayerbot.PremadeSpecName.2.0 = holy pve -AiPlayerbot.PremadeSpecGlyph.2.0 = 41106,43367,45741,43369,43365,41109 -AiPlayerbot.PremadeSpecLink.2.0.60 = 50350151020013053100515221 -AiPlayerbot.PremadeSpecLink.2.0.80 = 50350152220013053100515221-503201312 -AiPlayerbot.PremadeSpecName.2.1 = prot pve -AiPlayerbot.PremadeSpecGlyph.2.1 = 41100,43367,43869,43369,43365,45745 -AiPlayerbot.PremadeSpecLink.2.1.60 = -05005135203102311333112321 -AiPlayerbot.PremadeSpecLink.2.1.80 = -05005135203132311333312321-5023005 -AiPlayerbot.PremadeSpecName.2.2 = ret pve -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 +# +# +# +#################################################################################################### -# Hunter -AiPlayerbot.PremadeSpecName.3.0 = bm pve -AiPlayerbot.PremadeSpecGlyph.3.0 = 42912,43350,42902,43351,43338,45732 -AiPlayerbot.PremadeSpecLink.3.0.60 = 51200201505112243100511351 -AiPlayerbot.PremadeSpecLink.3.0.80 = 51200201505112253100531351-015305021 -AiPlayerbot.PremadeSpecName.3.1 = mm pve -AiPlayerbot.PremadeSpecGlyph.3.1 = 42912,43350,42915,43351,43338,45732 -AiPlayerbot.PremadeSpecLink.3.1.60 = -015305101230013233135030051 -AiPlayerbot.PremadeSpecLink.3.1.80 = 502-035305101230013233135031351-5000002 -AiPlayerbot.PremadeSpecName.3.2 = surv pve -AiPlayerbot.PremadeSpecGlyph.3.2 = 42912,43350,45731,43351,43338,45732 -AiPlayerbot.PremadeSpecLink.3.2.60 = --5000032500033330502135001331 -AiPlayerbot.PremadeSpecLink.3.2.80 = -005305101-5000032500033330522135301331 +#################################################################################################### +# DEBUG SWITCHES +# +# -# Rogue -AiPlayerbot.PremadeSpecName.4.0 = as pve -AiPlayerbot.PremadeSpecGlyph.4.0 = 45768,43379,45761,43380,43378,45767 -AiPlayerbot.PremadeSpecLink.4.0.60 = 005323005350100520103331051 -AiPlayerbot.PremadeSpecLink.4.0.80 = 005323005350100520103331051-005005005003-2 -AiPlayerbot.PremadeSpecName.4.1 = combat pve -AiPlayerbot.PremadeSpecGlyph.4.1 = 45762,43379,45767,43380,43378,45766 -AiPlayerbot.PremadeSpecLink.4.1.60 = -0252051000035015223100501251 -AiPlayerbot.PremadeSpecLink.4.1.80 = 00532000523-0252051000035015223100501251 -AiPlayerbot.PremadeSpecName.4.2 = subtlety pve -AiPlayerbot.PremadeSpecGlyph.4.2 = 42967,43379,45764,43380,43378,45767 -AiPlayerbot.PremadeSpecLink.4.2.60 = --5120122030321121050135031241 -AiPlayerbot.PremadeSpecLink.4.2.80 = 0053231-2-5120222030321121050135231251 +AiPlayerbot.SpellDump = 0 +AiPlayerbot.LogInGroupOnly = 1 +AiPlayerbot.LogValuesPerTick = 0 +AiPlayerbot.RandomChangeMultiplier = 1 -# Priest -AiPlayerbot.PremadeSpecName.5.0 = disc pve -AiPlayerbot.PremadeSpecGlyph.5.0 = 42408,43371,42400,43374,43342,45756 -AiPlayerbot.PremadeSpecLink.5.0.60 = 0503203130300512301323131051 -AiPlayerbot.PremadeSpecLink.5.0.80 = 0503203130300512331323231251-03520103 -AiPlayerbot.PremadeSpecName.5.1 = holy pve -AiPlayerbot.PremadeSpecGlyph.5.1 = 42408,43371,42400,43374,43342,42396 -AiPlayerbot.PremadeSpecLink.5.1.60 = -035050031301152530000331331 -AiPlayerbot.PremadeSpecLink.5.1.80 = 05032031-235050032302152530000331351 -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 +# Enables/Disables performance monitor +AiPlayerbot.PerfMonEnabled = 0 -# DeathKnight -AiPlayerbot.PremadeSpecName.6.0 = blood pve -AiPlayerbot.PremadeSpecGlyph.6.0 = 45805,43673,43827,43544,43672,43554 -AiPlayerbot.PremadeSpecLink.6.0.60 = 035502150300331320102013111-005 -AiPlayerbot.PremadeSpecLink.6.0.80 = 0355021533003313201020131351-005-005032 -AiPlayerbot.PremadeSpecName.6.1 = frost pve -AiPlayerbot.PremadeSpecGlyph.6.1 = 45805,43673,43547,43544,43672,43543 -AiPlayerbot.PremadeSpecLink.6.1.60 = -32003350332203012300023101351 -AiPlayerbot.PremadeSpecLink.6.1.80 = -32002350352203012300033101351-230200305003 -AiPlayerbot.PremadeSpecName.6.2 = unholy pve -AiPlayerbot.PremadeSpecGlyph.6.2 = 43542,43673,45804,43544,43672,43549 -AiPlayerbot.PremadeSpecLink.6.2.60 = --2300303050032152000150213130051 -AiPlayerbot.PremadeSpecLink.6.2.80 = -320053500002-2300303050032152000150213130051 -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 +# +# +# +#################################################################################################### -# Shaman -AiPlayerbot.PremadeSpecName.7.0 = ele pve -AiPlayerbot.PremadeSpecGlyph.7.0 = 41536,43385,41532,43386,44923,45776 -AiPlayerbot.PremadeSpecLink.7.0.60 = 4530001520213351102301351 -AiPlayerbot.PremadeSpecLink.7.0.80 = 3530001523213351322301351-005050031 -AiPlayerbot.PremadeSpecName.7.1 = enh pve -AiPlayerbot.PremadeSpecGlyph.7.1 = 41530,43385,41539,43386,44923,41540 -AiPlayerbot.PremadeSpecLink.7.1.60 = -30205033005001333031131131051 -AiPlayerbot.PremadeSpecLink.7.1.80 = 053030052-30205033005021333031131131051 -AiPlayerbot.PremadeSpecName.7.2 = resto pve -AiPlayerbot.PremadeSpecGlyph.7.2 = 41517,43385,41527,43386,44923,45775 -AiPlayerbot.PremadeSpecLink.7.2.60 = --50005301335310501002331241 -AiPlayerbot.PremadeSpecLink.7.2.80 = -00505031-50005331335310501022331251 +#################################################################################################### +# CHAT SETTINGS +# +# -# Mage -AiPlayerbot.PremadeSpecName.8.0 = arcane pve -AiPlayerbot.PremadeSpecGlyph.8.0 = 42735,43339,44955,43364,43361,42751 -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 = -0055030012303330053120300351 -AiPlayerbot.PremadeSpecLink.8.1.80 = 23000503310003-0055030012303330053120300351 -AiPlayerbot.PremadeSpecName.8.2 = frost pve -AiPlayerbot.PremadeSpecGlyph.8.2 = 42742,43339,50045,43364,43361,42751 -AiPlayerbot.PremadeSpecLink.8.2.60 = --3533103310203100232102231151 -AiPlayerbot.PremadeSpecLink.8.2.80 = 23002322010203--0533003313203100232112231151 +# Prefix for bot chat commands (e.g. follow, stay) +AiPlayerbot.CommandPrefix = "" -# Warlock -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 = 2350022001113510253500331151--5500000501 -AiPlayerbot.PremadeSpecName.9.1 = emo pve -AiPlayerbot.PremadeSpecGlyph.9.1 = 45785,43390,50077,43394,43393,42459 -AiPlayerbot.PremadeSpecLink.9.1.60 = -003203301135112530131201-55 -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 = --05203205210131051313230341 -AiPlayerbot.PremadeSpecLink.9.2.80 = -03310030003-05203205210331051335230351 +# Separator for bot chat commands +AiPlayerbot.CommandSeparator = "\\\\" -# Druid -AiPlayerbot.PremadeSpecName.11.0 = balance pve -AiPlayerbot.PremadeSpecGlyph.11.0 = 40916,43331,40921,43335,44922,40919 -AiPlayerbot.PremadeSpecLink.11.0.60 = 5012203115331003213302301231 -AiPlayerbot.PremadeSpecLink.11.0.80 = 5012203125331103213305301231--205003212 -AiPlayerbot.PremadeSpecName.11.1 = bear pve -AiPlayerbot.PremadeSpecGlyph.11.1 = 40897,43331,46372,43335,43332,40899 -AiPlayerbot.PremadeSpecLink.11.1.60 = -500232130322110353100301310501 -AiPlayerbot.PremadeSpecLink.11.1.80 = -501232130322110353120303313511-20350001 -AiPlayerbot.PremadeSpecName.11.2 = resto pve -AiPlayerbot.PremadeSpecGlyph.11.2 = 40913,43331,40906,43335,44922,45602 -AiPlayerbot.PremadeSpecLink.11.2.60 = --230033312031501531050013051 -AiPlayerbot.PremadeSpecLink.11.2.80 = 05320001--230033312031512531153313051 -AiPlayerbot.PremadeSpecName.11.3 = cat pve -AiPlayerbot.PremadeSpecGlyph.11.3 = 40902,43331,40901,43335,44922,45604 -AiPlayerbot.PremadeSpecLink.11.3.60 = -553202032322010052100030310501 -AiPlayerbot.PremadeSpecLink.11.3.80 = -553202032322010053100030310511-205503012 +# +# +# +#################################################################################################### -############################################## -# Default TalentSpec for random bots # -############################################## -# AiPlayerbot.RandomClassSpecProb.. # The probability to choose the spec -# AiPlayerbot.RandomClassSpecIndex.. # The spec index in PremadeSpec -# Warrior -AiPlayerbot.RandomClassSpecProb.1.0 = 20 -AiPlayerbot.RandomClassSpecIndex.1.0 = 0 -AiPlayerbot.RandomClassSpecProb.1.1 = 40 -AiPlayerbot.RandomClassSpecIndex.1.1 = 1 -AiPlayerbot.RandomClassSpecProb.1.2 = 40 -AiPlayerbot.RandomClassSpecIndex.1.2 = 2 -# Paladin -AiPlayerbot.RandomClassSpecProb.2.0 = 30 -AiPlayerbot.RandomClassSpecIndex.2.0 = 0 -AiPlayerbot.RandomClassSpecProb.2.1 = 40 -AiPlayerbot.RandomClassSpecIndex.2.1 = 1 -AiPlayerbot.RandomClassSpecProb.2.2 = 30 -AiPlayerbot.RandomClassSpecIndex.2.2 = 2 -# Hunter -AiPlayerbot.RandomClassSpecProb.3.0 = 33 -AiPlayerbot.RandomClassSpecIndex.3.0 = 0 -AiPlayerbot.RandomClassSpecProb.3.1 = 33 -AiPlayerbot.RandomClassSpecIndex.3.1 = 1 -AiPlayerbot.RandomClassSpecProb.3.2 = 33 -AiPlayerbot.RandomClassSpecIndex.3.2 = 2 -# Rogue -AiPlayerbot.RandomClassSpecProb.4.0 = 45 -AiPlayerbot.RandomClassSpecIndex.4.0 = 0 -AiPlayerbot.RandomClassSpecProb.4.1 = 45 -AiPlayerbot.RandomClassSpecIndex.4.1 = 1 -AiPlayerbot.RandomClassSpecProb.4.2 = 10 -AiPlayerbot.RandomClassSpecIndex.4.2 = 2 -# Priest -AiPlayerbot.RandomClassSpecProb.5.0 = 40 -AiPlayerbot.RandomClassSpecIndex.5.0 = 0 -AiPlayerbot.RandomClassSpecProb.5.1 = 35 -AiPlayerbot.RandomClassSpecIndex.5.1 = 1 -AiPlayerbot.RandomClassSpecProb.5.2 = 25 -AiPlayerbot.RandomClassSpecIndex.5.2 = 2 -# DeathKnight -AiPlayerbot.RandomClassSpecProb.6.0 = 30 -AiPlayerbot.RandomClassSpecIndex.6.0 = 0 -AiPlayerbot.RandomClassSpecProb.6.1 = 40 -AiPlayerbot.RandomClassSpecIndex.6.1 = 1 -AiPlayerbot.RandomClassSpecProb.6.2 = 30 -AiPlayerbot.RandomClassSpecIndex.6.2 = 2 -# Shaman -AiPlayerbot.RandomClassSpecProb.7.0 = 33 -AiPlayerbot.RandomClassSpecIndex.7.0 = 0 -AiPlayerbot.RandomClassSpecProb.7.1 = 33 -AiPlayerbot.RandomClassSpecIndex.7.1 = 1 -AiPlayerbot.RandomClassSpecProb.7.2 = 33 -AiPlayerbot.RandomClassSpecIndex.7.2 = 2 -# Mage -AiPlayerbot.RandomClassSpecProb.8.0 = 30 -AiPlayerbot.RandomClassSpecIndex.8.0 = 0 -AiPlayerbot.RandomClassSpecProb.8.1 = 30 -AiPlayerbot.RandomClassSpecIndex.8.1 = 1 -AiPlayerbot.RandomClassSpecProb.8.2 = 40 -AiPlayerbot.RandomClassSpecIndex.8.2 = 2 -# Warlock -AiPlayerbot.RandomClassSpecProb.9.0 = 40 -AiPlayerbot.RandomClassSpecIndex.9.0 = 0 -AiPlayerbot.RandomClassSpecProb.9.1 = 40 -AiPlayerbot.RandomClassSpecIndex.9.1 = 1 -AiPlayerbot.RandomClassSpecProb.9.2 = 20 -AiPlayerbot.RandomClassSpecIndex.9.2 = 2 -# Druid -AiPlayerbot.RandomClassSpecProb.11.0 = 20 -AiPlayerbot.RandomClassSpecIndex.11.0 = 0 -AiPlayerbot.RandomClassSpecProb.11.1 = 40 -AiPlayerbot.RandomClassSpecIndex.11.1 = 1 -AiPlayerbot.RandomClassSpecProb.11.2 = 40 -AiPlayerbot.RandomClassSpecIndex.11.2 = 2 +#################################################################################################### +# LOGS +# +# -################################################################################## -# # -# Logging Stuff # -# # -################################################################################## +# Custom config to allow logfiles to be created. +# Example: AiPlayerbot.AllowedLogFiles = travelNodes.csv,travelPaths.csv,TravelNodeStore.h,bot_movement.csv,bot_location.csv +AiPlayerbot.AllowedLogFiles = "" Appender.Playerbots=2,5,0,Playerbots.log,w -Logger.playerbots=5,Console Playerbots \ No newline at end of file +Logger.playerbots=5,Console Playerbots + +# +# +# +#################################################################################################### + +################################################################################################### +################################################################################################### +################################################################################################### +################################################################################################### +################################################################################################### + +############################################## +# Deprecated (temporary) # +############################################## + +# Log on all random bots on start +AiPlayerbot.RandomBotLoginAtStartup = 1 + +# Guild Task system +AiPlayerbot.EnableGuildTasks = 0 + +# Enable dungeon suggestions for random bots +AiPlayerbot.RandomBotSuggestDungeons = 1 + +# Enable dungeon suggestions in lower case randomly +AiPlayerbot.SuggestDungeonsInLowerCaseRandomly = 0 + +# Random bot guild count +AiPlayerbot.RandomBotGuildCount = 20 + +# Delete all random bot guilds +AiPlayerbot.DeleteRandomBotGuilds = 0 + +# Chance bot chooses RPG (Teleport to random camp for their level) instead of grinding +AiPlayerbot.RandomBotRpgChance = 0.20 #unused now + +# Set randombots movement speed to walking anywhere +AiPlayerbot.RandombotsWalkingRPG = 0 + +# Set randombots movement speed to walking only inside buildings +AiPlayerbot.RandombotsWalkingRPG.InDoors = 0 + +# Bots greet to the players +AiPlayerbot.EnableGreet = 0 + +# Specify percent of active bots +# The default is 10. With 10% of all bots going active or inactive each minute. +AiPlayerbot.BotActiveAlone = 100 + +# Premade spell to avoid (undetected spells) +# spellid-radius, ... +AiPlayerbot.PremadeAvoidAoe = 62234-4 + +AiPlayerbot.MinRandomBotsPriceChangeInterval = 7200 +AiPlayerbot.MaxRandomBotsPriceChangeInterval = 172800 +AiPlayerbot.MinRandomBotChangeStrategyTime = 180 +AiPlayerbot.MaxRandomBotChangeStrategyTime = 720 + + +# How often tasks are changed +AiPlayerbot.MinGuildTaskChangeTime = 172800 +AiPlayerbot.MaxGuildTaskChangeTime = 432000 + +# Mail spam interval +AiPlayerbot.MinGuildTaskAdvertisementTime = 300 +AiPlayerbot.MaxGuildTaskAdvertisementTime = 28800 + +# Delay before reward is sent +AiPlayerbot.MinGuildTaskRewardTime = 300 +AiPlayerbot.MaxGuildTaskRewardTime = 3600 + +# Cleanup of guild tasks interval +AiPlayerbot.GuildTaskAdvertCleanupTime = 300 + +# Specify max distance between victim and bot when creating guild kill task +AiPlayerbot.GuildTaskKillTaskDistance = 200 + +# Distance margin for facade calculations +AiPlayerbot.TargetPosRecalcDistance = 0.1 + +# Allow bots to be summoned near innkeepers +AiPlayerbot.SummonAtInnkeepersEnabled = 1 diff --git a/src/AiFactory.cpp b/src/AiFactory.cpp index 063f3411..d6baa1eb 100644 --- a/src/AiFactory.cpp +++ b/src/AiFactory.cpp @@ -274,11 +274,12 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa { engine->addStrategy("avoid aoe"); } + engine->addStrategy("combat formation"); switch (player->getClass()) { case CLASS_PRIEST: if (tab == 2) { - engine->addStrategies("dps", "shadow debuff", "shadow aoe", "threat", nullptr); + engine->addStrategies("dps", "shadow debuff", "shadow aoe", nullptr); } else if (tab == PRIEST_TAB_DISIPLINE) { engine->addStrategies("heal", nullptr); } else { @@ -289,11 +290,11 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa break; case CLASS_MAGE: if (tab == 0) - engine->addStrategies("arcane", "arcane aoe", "threat", nullptr); + engine->addStrategies("arcane", "arcane aoe", nullptr); else if (tab == 1) - engine->addStrategies("fire", "fire aoe", "threat", nullptr); + engine->addStrategies("fire", "fire aoe", nullptr); else - engine->addStrategies("frost", "frost aoe", "threat", nullptr); + engine->addStrategies("frost", "frost aoe", nullptr); engine->addStrategies("dps", "dps assist", "cure", nullptr); break; @@ -301,17 +302,17 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa if (tab == 2) engine->addStrategies("tank", "tank assist", "aoe", "mark rti", nullptr); else if (player->getLevel() < 36 || tab == 0) - engine->addStrategies("arms", "aoe", "dps assist", "threat", /*"behind",*/ nullptr); + engine->addStrategies("arms", "aoe", "dps assist",/*"behind",*/ nullptr); else - engine->addStrategies("fury", "aoe", "dps assist", "threat", /*"behind",*/ nullptr); + engine->addStrategies("fury", "aoe", "dps assist",/*"behind",*/ nullptr); break; case CLASS_SHAMAN: if (tab == 0) - engine->addStrategies("caster", "caster aoe", "bmana", "threat", nullptr); + engine->addStrategies("caster", "caster aoe", "bmana",nullptr); else if (tab == 2) engine->addStrategies("heal", "bmana", nullptr); else - engine->addStrategies("melee", "melee aoe", "bdps", "threat", nullptr); + engine->addStrategies("melee", "melee aoe", "bdps", nullptr); engine->addStrategies("dps assist", "cure", "totems", nullptr); break; @@ -327,38 +328,41 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa case CLASS_DRUID: if (tab == 0) { - engine->addStrategies("caster", "cure", "caster aoe", "threat", "dps assist", nullptr); + engine->addStrategies("caster", "cure", "caster aoe", "dps assist", nullptr); engine->addStrategy("caster debuff"); } else if (tab == 2) engine->addStrategies("heal", "cure", "dps assist", nullptr); else { - engine->removeStrategy("flee"); - engine->addStrategies("bear", "tank assist", nullptr); + if (player->GetLevel() >= 20 && !player->HasAura(16931)/*thick hide*/) { + engine->addStrategies("cat", "dps assist", nullptr); + } else { + engine->addStrategies("bear", "tank assist", nullptr); + } } break; case CLASS_HUNTER: - engine->addStrategies("dps", "aoe", "bdps", "threat", "dps assist", nullptr); + engine->addStrategies("dps", "aoe", "bdps", "dps assist", nullptr); engine->addStrategy("dps debuff"); break; case CLASS_ROGUE: if (tab == ROGUE_TAB_ASSASSINATION) { - engine->addStrategies("melee", "threat", "dps assist", "aoe", /*"behind",*/ nullptr); + engine->addStrategies("melee", "dps assist", "aoe", /*"behind",*/ nullptr); } else { - engine->addStrategies("dps", "threat", "dps assist", "aoe", /*"behind",*/ nullptr); + engine->addStrategies("dps", "dps assist", "aoe", /*"behind",*/ nullptr); } break; case CLASS_WARLOCK: - engine->addStrategies("dps assist", "dps", "dps debuff", "aoe", "threat", nullptr); + engine->addStrategies("dps assist", "dps", "dps debuff", "aoe", nullptr); break; case CLASS_DEATH_KNIGHT: if (tab == 0) engine->addStrategies("blood", "tank assist", nullptr); else if (tab == 1) - engine->addStrategies("frost", "frost aoe", "dps assist", "threat", nullptr); + engine->addStrategies("frost", "frost aoe", "dps assist", nullptr); else - engine->addStrategies("unholy", "unholy aoe", "dps assist", "threat", nullptr); + engine->addStrategies("unholy", "unholy aoe", "dps assist", nullptr); break; } @@ -504,8 +508,13 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const nonCombatEngine->addStrategies("dps assist", "cure", nullptr); break; case CLASS_DRUID: - if (tab == 1) - nonCombatEngine->addStrategy("tank assist"); + if (tab == 1) { + if (player->GetLevel() >= 20 && !player->HasAura(16931)/*thick hide*/) { + nonCombatEngine->addStrategy("dps assist"); + } else { + nonCombatEngine->addStrategy("tank assist"); + } + } else nonCombatEngine->addStrategies("dps assist", "cure", nullptr); break; diff --git a/src/Helpers.h b/src/Helpers.h index b2471c71..2d9e5454 100644 --- a/src/Helpers.h +++ b/src/Helpers.h @@ -15,6 +15,7 @@ #include #include #include +#include void split(std::vector& dest, std::string const str, char const* delim) { diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 25d92a1d..6996d460 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -352,10 +352,16 @@ void PlayerbotAI::UpdateAIInternal([[maybe_unused]] uint32 elapsed, bool minimal Player* owner = holder.GetOwner(); if (!helper.ParseChatCommand(command, owner) && holder.GetType() == CHAT_MSG_WHISPER) { - std::ostringstream out; - out << "Unknown command " << command; - TellMaster(out); - helper.ParseChatCommand("help"); + // To prevent spam caused by WIM + if (!(command.rfind("WIM", 0) == 0) && + !(command.rfind("QHpr", 0) == 0) + ) + { + std::ostringstream out; + out << "Unknown command " << command; + TellMaster(out); + helper.ParseChatCommand("help"); + } } chatCommands.pop(); @@ -1810,7 +1816,7 @@ Player* PlayerbotAI::GetPlayer(ObjectGuid guid) uint32 GetCreatureIdForCreatureTemplateId(uint32 creatureTemplateId) { - QueryResult results = WorldDatabase.Query("SELECT guid FROM `acore_world`.`creature` WHERE id1 = {} LIMIT 1;", creatureTemplateId); + QueryResult results = WorldDatabase.Query("SELECT guid FROM `creature` WHERE id1 = {} LIMIT 1;", creatureTemplateId); if (results) { Field* fields = results->Fetch(); return fields[0].Get(); @@ -1900,19 +1906,19 @@ bool PlayerbotAI::TellMasterNoFacing(std::string const text, PlayerbotSecurityLe return false; time_t lastSaid = whispers[text]; - // Yunfan: Remove tell cooldown - // if (!lastSaid || (time(nullptr) - lastSaid) >= sPlayerbotAIConfig->repeatDelay / 1000) - // { - whispers[text] = time(nullptr); + + if (!lastSaid || (time(nullptr) - lastSaid) >= sPlayerbotAIConfig->repeatDelay / 1000) + { + whispers[text] = time(nullptr); - ChatMsg type = CHAT_MSG_WHISPER; - if (currentChat.second - time(nullptr) >= 1) - type = currentChat.first; + ChatMsg type = CHAT_MSG_WHISPER; + if (currentChat.second - time(nullptr) >= 1) + type = currentChat.first; - WorldPacket data; - ChatHandler::BuildChatPacket(data, type == CHAT_MSG_ADDON ? CHAT_MSG_PARTY : type, type == CHAT_MSG_ADDON ? LANG_ADDON : LANG_UNIVERSAL, bot, nullptr, text.c_str()); - master->SendDirectMessage(&data); - // } + WorldPacket data; + ChatHandler::BuildChatPacket(data, type == CHAT_MSG_ADDON ? CHAT_MSG_PARTY : type, type == CHAT_MSG_ADDON ? LANG_ADDON : LANG_UNIVERSAL, bot, nullptr, text.c_str()); + master->SendDirectMessage(&data); + } return true; } diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index 34e7a1bb..a6bcf470 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -53,12 +53,13 @@ bool PlayerbotAIConfig::Initialize() globalCoolDown = sConfigMgr->GetOption("AiPlayerbot.GlobalCooldown", 1500); maxWaitForMove = sConfigMgr->GetOption("AiPlayerbot.MaxWaitForMove", 5000); + disableMoveSplinePath = sConfigMgr->GetOption("AiPlayerbot.DisableMoveSplinePath", 0); maxMovementSearchTime = sConfigMgr->GetOption("AiPlayerbot.MaxMovementSearchTime", 3); expireActionTime = sConfigMgr->GetOption("AiPlayerbot.ExpireActionTime", 5000); dispelAuraDuration = sConfigMgr->GetOption("AiPlayerbot.DispelAuraDuration", 7000); reactDelay = sConfigMgr->GetOption("AiPlayerbot.ReactDelay", 500); passiveDelay = sConfigMgr->GetOption("AiPlayerbot.PassiveDelay", 10000); - repeatDelay = sConfigMgr->GetOption("AiPlayerbot.RepeatDelay", 5000); + repeatDelay = sConfigMgr->GetOption("AiPlayerbot.RepeatDelay", 2000); errorDelay = sConfigMgr->GetOption("AiPlayerbot.ErrorDelay", 5000); rpgDelay = sConfigMgr->GetOption("AiPlayerbot.RpgDelay", 10000); sitDelay = sConfigMgr->GetOption("AiPlayerbot.SitDelay", 30000); @@ -94,7 +95,10 @@ bool PlayerbotAIConfig::Initialize() autoAvoidAoe = sConfigMgr->GetOption("AiPlayerbot.AutoAvoidAoe", true); tellWhenAvoidAoe = sConfigMgr->GetOption("AiPlayerbot.TellWhenAvoidAoe", true); - randomGearLoweringChance = sConfigMgr->GetOption("AiPlayerbot.RandomGearLoweringChance", 0.15f); + randomGearLoweringChance = sConfigMgr->GetOption("AiPlayerbot.RandomGearLoweringChance", 0.0f); + randomGearQualityLimit = sConfigMgr->GetOption("AiPlayerbot.RandomGearQualityLimit", 3); + randomGearScoreLimit = sConfigMgr->GetOption("AiPlayerbot.RandomGearScoreLimit", 0); + randomBotMaxLevelChance = sConfigMgr->GetOption("AiPlayerbot.RandomBotMaxLevelChance", 0.15f); randomBotRpgChance = sConfigMgr->GetOption("AiPlayerbot.RandomBotRpgChance", 0.20f); @@ -123,7 +127,7 @@ bool PlayerbotAIConfig::Initialize() minRandomBotInWorldTime = sConfigMgr->GetOption("AiPlayerbot.MinRandomBotInWorldTime", 2 * HOUR); maxRandomBotInWorldTime = sConfigMgr->GetOption("AiPlayerbot.MaxRandomBotInWorldTime", 12 * HOUR); minRandomBotRandomizeTime = sConfigMgr->GetOption("AiPlayerbot.MinRandomBotRandomizeTime", 2 * HOUR); - maxRandomBotRandomizeTime = sConfigMgr->GetOption("AiPlayerbot.MaxRandomRandomizeTime", 14 * 24 * HOUR); + maxRandomBotRandomizeTime = sConfigMgr->GetOption("AiPlayerbot.MaxRandomBotRandomizeTime", 14 * 24 * HOUR); minRandomBotChangeStrategyTime = sConfigMgr->GetOption("AiPlayerbot.MinRandomBotChangeStrategyTime", 30 * MINUTE); maxRandomBotChangeStrategyTime = sConfigMgr->GetOption("AiPlayerbot.MaxRandomBotChangeStrategyTime", 2 * HOUR); minRandomBotReviveTime = sConfigMgr->GetOption("AiPlayerbot.MinRandomBotReviveTime", MINUTE); @@ -272,7 +276,11 @@ bool PlayerbotAIConfig::Initialize() equipmentPersistence = sConfigMgr->GetOption("AiPlayerbot.EquipmentPersistence", false); equipmentPersistenceLevel = sConfigMgr->GetOption("AiPlayerbot.EquipmentPersistenceLevel", 80); groupInvitationPermission = sConfigMgr->GetOption("AiPlayerbot.GroupInvitationPermission", 1); - botReviveWhenSummon = sConfigMgr->GetOption("AiPlayerbot.BotReviveWhenSummon", 1); + allowSummonInCombat = sConfigMgr->GetOption("AiPlayerbot.AllowSummonInCombat", true); + allowSummonWhenMasterIsDead = sConfigMgr->GetOption("AiPlayerbot.AllowSummonWhenMasterIsDead", true); + allowSummonWhenBotIsDead = sConfigMgr->GetOption("AiPlayerbot.AllowSummonWhenBotIsDead", true); + reviveBotWhenSummoned = sConfigMgr->GetOption("AiPlayerbot.ReviveBotWhenSummoned", true); + botRepairWhenSummon = sConfigMgr->GetOption("AiPlayerbot.BotRepairWhenSummon", true); autoInitOnly = sConfigMgr->GetOption("AiPlayerbot.AutoInitOnly", false); autoInitEquipLevelLimitRatio = sConfigMgr->GetOption("AiPlayerbot.AutoInitEquipLevelLimitRatio", 1.0); addClassCommand = sConfigMgr->GetOption("AiPlayerbot.AddClassCommand", 1); @@ -292,7 +300,8 @@ bool PlayerbotAIConfig::Initialize() randombotsWalkingRPG = sConfigMgr->GetOption("AiPlayerbot.RandombotsWalkingRPG", false); randombotsWalkingRPGInDoors = sConfigMgr->GetOption("AiPlayerbot.RandombotsWalkingRPG.InDoors", false); minEnchantingBotLevel = sConfigMgr->GetOption("AiPlayerbot.MinEnchantingBotLevel", 60); - limitEnchantExpansion = sConfigMgr->GetOption("AiPlayerbot.LimitEnchantExpansion", 1); + limitEnchantExpansion = sConfigMgr->GetOption("AiPlayerbot.LimitEnchantExpansion", 0); + limitGearExpansion = sConfigMgr->GetOption("AiPlayerbot.LimitGearExpansion", 0); randombotStartingLevel = sConfigMgr->GetOption("AiPlayerbot.RandombotStartingLevel", 5); enableRotation = sConfigMgr->GetOption("AiPlayerbot.EnableRotation", false); rotationPoolSize = sConfigMgr->GetOption("AiPlayerbot.RotationPoolSize", 500); @@ -327,6 +336,9 @@ bool PlayerbotAIConfig::Initialize() selfBotLevel = sConfigMgr->GetOption("AiPlayerbot.SelfBotLevel", 1); RandomPlayerbotFactory::CreateRandomBots(); + if (World::IsStopped()) { + return true; + } PlayerbotFactory::Init(); sRandomItemMgr->Init(); sRandomItemMgr->InitAfterAhBot(); diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index 40a66034..17da007d 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -54,8 +54,8 @@ class PlayerbotAIConfig bool enabled; bool allowGuildBots, allowPlayerBots; - uint32 globalCoolDown, reactDelay, maxWaitForMove, maxMovementSearchTime, expireActionTime, - dispelAuraDuration, passiveDelay, repeatDelay, + uint32 globalCoolDown, reactDelay, maxWaitForMove, disableMoveSplinePath, maxMovementSearchTime, + expireActionTime, dispelAuraDuration, passiveDelay, repeatDelay, errorDelay, rpgDelay, sitDelay, returnDelay, lootDelay; float sightDistance, spellDistance, reactDistance, grindDistance, lootDistance, shootDistance, fleeDistance, tooCloseDistance, meleeDistance, followDistance, whisperDistance, contactDistance, @@ -79,6 +79,8 @@ class PlayerbotAIConfig std::vector randomBotQuestIds; uint32 randomBotTeleportDistance; float randomGearLoweringChance; + int32 randomGearQualityLimit; + int32 randomGearScoreLimit; float randomBotMaxLevelChance; float randomBotRpgChance; uint32 minRandomBots, maxRandomBots; @@ -134,6 +136,7 @@ class PlayerbotAIConfig bool randombotsWalkingRPGInDoors; uint32 minEnchantingBotLevel; uint32 limitEnchantExpansion; + uint32 limitGearExpansion; uint32 randombotStartingLevel; bool enableRotation; uint32 rotationPoolSize; @@ -214,7 +217,11 @@ class PlayerbotAIConfig bool equipmentPersistence; int32 equipmentPersistenceLevel; int32 groupInvitationPermission; - int32 botReviveWhenSummon; + bool allowSummonInCombat; + bool allowSummonWhenMasterIsDead; + bool allowSummonWhenBotIsDead; + bool reviveBotWhenSummoned; + bool botRepairWhenSummon; bool autoInitOnly; float autoInitEquipLevelLimitRatio; int32 addClassCommand; diff --git a/src/PlayerbotFactory.cpp b/src/PlayerbotFactory.cpp index 82ad1279..26f9e3a6 100644 --- a/src/PlayerbotFactory.cpp +++ b/src/PlayerbotFactory.cpp @@ -149,7 +149,10 @@ void PlayerbotFactory::Prepare() { if (!itemQuality) { - itemQuality = ITEM_QUALITY_RARE; + uint32 gs = sPlayerbotAIConfig->randomGearScoreLimit == 0 ? 0 : + PlayerbotFactory::CalcMixedGearScore(sPlayerbotAIConfig->randomGearScoreLimit, sPlayerbotAIConfig->randomGearQualityLimit); + itemQuality = sPlayerbotAIConfig->randomGearQualityLimit; + gearScoreLimit = gs; } if (bot->isDead()) @@ -1453,6 +1456,14 @@ void PlayerbotFactory::InitEquipment(bool incremental) if (itemId == 46978) { // shaman earth ring totem continue; } + + // disable next expansion gear + if (sPlayerbotAIConfig->limitGearExpansion && bot->GetLevel() <= 60 && itemId >= 23728) + continue; + + if (sPlayerbotAIConfig->limitGearExpansion && bot->GetLevel() <= 70 && itemId >= 35570 && itemId != 36737 && itemId != 37739 && itemId != 37740)//transition point from TBC -> WOTLK isn't as clear, and there are other wearable TBC items above 35570 but nothing of significance + continue; + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); if (!proto) continue; @@ -3451,15 +3462,12 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destoryOld) continue; } - // disable next expansion - if (sPlayerbotAIConfig->limitEnchantExpansion && bot->GetLevel() <= 69 && enchantSpell >= 25072) { + // disable next expansion enchantments + if (sPlayerbotAIConfig->limitEnchantExpansion && bot->GetLevel() <= 60 && enchantSpell >= 25072) continue; - } - if (sPlayerbotAIConfig->limitEnchantExpansion && bot->GetLevel() <= 79 && enchantSpell > 48557) { + if (sPlayerbotAIConfig->limitEnchantExpansion && bot->GetLevel() <= 70 && enchantSpell > 48557) continue; - } - for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j) { @@ -3821,9 +3829,10 @@ float PlayerbotFactory::CalculateItemScore(uint32 item_id, Player* bot) score += (agility + strength + intellect + spirit + stamina + defense + dodge + parry + block + resilience + hit + crit + haste + expertise + attack_power + mana_regeneration + spell_power + armor_penetration + spell_penetration + armor + rangeDps + meleeDps) * 0.001; + // todo: remove duplicate code if (cls == CLASS_HUNTER) { // AGILITY only - score += agility * 2.5 + attack_power + armor_penetration * 2 + rangeDps * 5 + hit * 2.5 + crit * 2 + haste * 2.5 + intellect; + score += agility * 2.5 + attack_power + armor_penetration * 2 + rangeDps * 5 + hit * 2 + crit * 2 + haste * 2 + intellect; } else if (cls == CLASS_WARLOCK || cls == CLASS_MAGE || (cls == CLASS_PRIEST && tab == 2) || // shadow @@ -3832,7 +3841,7 @@ float PlayerbotFactory::CalculateItemScore(uint32 item_id, Player* bot) ) { // SPELL DPS score += intellect * 0.5 + spirit * 0.5 + spell_power + spell_penetration - + hit * 1.2 + crit * 0.7 + haste * 1 + rangeDps; + + hit * 1 + crit * 0.7 + haste * 1 + rangeDps; } else if ((cls == CLASS_PALADIN && tab == 0) || // holy (cls == CLASS_PRIEST && tab != 2) || // discipline / holy (cls == CLASS_SHAMAN && tab == 2) || // heal @@ -3840,9 +3849,9 @@ float PlayerbotFactory::CalculateItemScore(uint32 item_id, Player* bot) ) { // HEALER score += intellect * 0.5 + spirit * 0.5 + spell_power + mana_regeneration * 0.5 + crit * 0.5 + haste * 1 + rangeDps; - } else if (cls == CLASS_ROGUE) { + } else if (cls == CLASS_ROGUE || (cls == CLASS_DRUID && tab == 2 && !PlayerbotAI::IsTank(bot))) { // AGILITY mainly (STRENGTH also) - score += agility * 2 + strength + attack_power + armor_penetration * 1 + meleeDps * 5 + hit * 2 + crit * 1.5 + haste * 1.5 + expertise * 2.5; + score += agility * 2 + strength + attack_power + armor_penetration * 1 + meleeDps * 5 + hit * 1.5 + crit * 1.5 + haste * 1.5 + expertise * 2.5; } else if ((cls == CLASS_PALADIN && tab == 2) || // retribution (cls == CLASS_WARRIOR && tab != 2) || // arm / fury (cls == CLASS_DEATH_KNIGHT && tab != 0) // ice / unholy @@ -3852,20 +3861,20 @@ float PlayerbotFactory::CalculateItemScore(uint32 item_id, Player* bot) } else if ((cls == CLASS_SHAMAN && tab == 1)) { // enhancement // STRENGTH mainly (AGILITY, INTELLECT also) score += strength * 1 + agility * 1.5 + intellect * 1.5 + attack_power + spell_power * 1.5 + armor_penetration * 0.5 + meleeDps * 5 - + hit * 2 + crit * 1.5 + haste * 1.5 + expertise * 2; + + hit * 1.5 + crit * 1.5 + haste * 1.5 + expertise * 2; } else if ((cls == CLASS_WARRIOR && tab == 2) || (cls == CLASS_PALADIN && tab == 1)) { // TANK WITH SHIELD score += strength * 1 + agility * 2 + attack_power * 0.2 + defense * 2.5 + parry * 2 + dodge * 2 + resilience * 2 + block * 2 + armor * 0.3 + stamina * 3 - + hit * 1 + crit * 0.2 + haste * 0.5 + expertise * 3; + + hit * 0.5 + crit * 0.2 + haste * 0.5 + expertise * 3; } else if (cls == CLASS_DEATH_KNIGHT && tab == 0){ // BLOOD DK TANK score += strength * 1 + agility * 2 + attack_power * 0.2 + defense * 3.5 + parry * 2 + dodge * 2 + resilience * 2 + armor * 0.3 + stamina * 2.5 - + hit * 2 + crit * 0.5 + haste * 0.5 + expertise * 3.5; + + hit * 0.5 + crit * 0.5 + haste * 0.5 + expertise * 3.5; } else { - // BEAR DRUID TANK (AND FERAL DRUID...?) + // BEAR DRUID TANK score += agility * 1.5 + strength * 1 + attack_power * 0.5 + armor_penetration * 0.5 + meleeDps * 2 + defense * 0.25 + dodge * 0.25 + armor * 0.3 + stamina * 1.5 + hit * 1 + crit * 1 + haste * 0.5 + expertise * 3; @@ -4242,9 +4251,10 @@ float PlayerbotFactory::CalculateEnchantScore(uint32 enchant_id, Player* bot) float score = (agility + strength + intellect + spirit + stamina + defense + dodge + parry + block + resilience + hit + crit + haste + expertise + attack_power + mana_regeneration + spell_power + armor_penetration + spell_penetration + armor + dps) * 0.001; + // todo: remove duplicate code if (cls == CLASS_HUNTER) { // AGILITY only - score += agility * 2.5 + attack_power + armor_penetration * 2 + dps * 5 + hit * 2.5 + crit * 2 + haste * 2.5 + intellect; + score += agility * 2.5 + attack_power + armor_penetration * 2 + dps * 5 + hit * 2 + crit * 2 + haste * 2.5 + intellect; } else if (cls == CLASS_WARLOCK || cls == CLASS_MAGE || (cls == CLASS_PRIEST && tab == 2) || // shadow @@ -4253,7 +4263,7 @@ float PlayerbotFactory::CalculateEnchantScore(uint32 enchant_id, Player* bot) ) { // SPELL DPS score += intellect * 0.5 + spirit * 0.5 + spell_power + spell_penetration - + hit * 1.2 + crit * 0.7 + haste * 1; + + hit * 1 + crit * 0.7 + haste * 1; } else if ((cls == CLASS_PALADIN && tab == 0) || // holy (cls == CLASS_PRIEST && tab != 2) || // discipline / holy (cls == CLASS_SHAMAN && tab == 2) || // heal @@ -4261,9 +4271,9 @@ float PlayerbotFactory::CalculateEnchantScore(uint32 enchant_id, Player* bot) ) { // HEALER score += intellect * 0.5 + spirit * 0.5 + spell_power + mana_regeneration * 0.5 + crit * 0.5 + haste * 1; - } else if (cls == CLASS_ROGUE) { + } else if (cls == CLASS_ROGUE || (cls == CLASS_DRUID && tab == 2 && !PlayerbotAI::IsTank(bot))) { // AGILITY mainly (STRENGTH also) - score += agility * 2 + strength + attack_power + armor_penetration * 1 + dps * 5 + hit * 2 + crit * 1.5 + haste * 1.5 + expertise * 2.5; + score += agility * 2 + strength + attack_power + armor_penetration * 1 + dps * 5 + hit * 1.5 + crit * 1.5 + haste * 1.5 + expertise * 2.5; } else if ((cls == CLASS_PALADIN && tab == 2) || // retribution (cls == CLASS_WARRIOR && tab != 2) || // arm / fury (cls == CLASS_DEATH_KNIGHT && tab != 0) // ice / unholy @@ -4273,20 +4283,20 @@ float PlayerbotFactory::CalculateEnchantScore(uint32 enchant_id, Player* bot) } else if ((cls == CLASS_SHAMAN && tab == 1)) { // enhancement // STRENGTH mainly (AGILITY, INTELLECT also) score += strength * 1 + agility * 1.5 + intellect * 1.5 + attack_power + spell_power * 1.5 + armor_penetration * 0.5 + dps * 5 - + hit * 2 + crit * 1.5 + haste * 1.5 + expertise * 2; + + hit * 1.5 + crit * 1.5 + haste * 1.5 + expertise * 2; } else if ((cls == CLASS_WARRIOR && tab == 2) || (cls == CLASS_PALADIN && tab == 1)) { // TANK WITH SHIELD score += strength * 1 + agility * 2 + attack_power * 0.2 + defense * 2.5 + parry * 2 + dodge * 2 + resilience * 2 + block * 2 + armor * 0.3 + stamina * 3 - + hit * 1 + crit * 0.2 + haste * 0.5 + expertise * 3; + + hit * 0.5 + crit * 0.2 + haste * 0.5 + expertise * 3; } else if (cls == CLASS_DEATH_KNIGHT && tab == 0){ // BLOOD DK TANK score += strength * 1 + agility * 2 + attack_power * 0.2 + defense * 3.5 + parry * 2 + dodge * 2 + resilience * 2 + armor * 0.3 + stamina * 2.5 - + hit * 2 + crit * 0.5 + haste * 0.5 + expertise * 3.5; + + hit * 0.5 + crit * 0.5 + haste * 0.5 + expertise * 3.5; } else { - // BEAR DRUID TANK (AND FERAL DRUID...?) + // BEAR DRUID TANK score += agility * 1.5 + strength * 1 + attack_power * 0.5 + armor_penetration * 0.5 + dps * 2 + defense * 0.25 + dodge * 0.25 + armor * 0.3 + stamina * 1.5 + hit * 1 + crit * 1 + haste * 0.5 + expertise * 3; @@ -4521,9 +4531,10 @@ float PlayerbotFactory::CalculateSpellScore(uint32 spell_id, Player* bot, uint32 } } float score = 0; + // todo: remove duplicate code if (cls == CLASS_HUNTER) { // AGILITY only - score += agility * 2.5 + attack_power + armor_penetration * 2 + rangeDps * 5 + hit * 2.5 + crit * 2 + haste * 2.5 + intellect; + score += agility * 2.5 + attack_power + armor_penetration * 2 + rangeDps * 5 + hit * 2 + crit * 2 + haste * 2.5 + intellect; } else if (cls == CLASS_WARLOCK || cls == CLASS_MAGE || (cls == CLASS_PRIEST && tab == 2) || // shadow @@ -4532,7 +4543,7 @@ float PlayerbotFactory::CalculateSpellScore(uint32 spell_id, Player* bot, uint32 ) { // SPELL DPS score += intellect * 0.5 + spirit * 0.5 + spell_power + spell_penetration - + hit * 1.2 + crit * 0.7 + haste * 1 + rangeDps; + + hit * 1 + crit * 0.7 + haste * 1 + rangeDps; } else if ((cls == CLASS_PALADIN && tab == 0) || // holy (cls == CLASS_PRIEST && tab != 2) || // discipline / holy (cls == CLASS_SHAMAN && tab == 2) || // heal @@ -4540,9 +4551,9 @@ float PlayerbotFactory::CalculateSpellScore(uint32 spell_id, Player* bot, uint32 ) { // HEALER score += intellect * 0.5 + spirit * 0.5 + spell_power + mana_regeneration * 0.5 + crit * 0.5 + haste * 1 + rangeDps; - } else if (cls == CLASS_ROGUE) { + } else if (cls == CLASS_ROGUE || (cls == CLASS_DRUID && tab == 2 && !PlayerbotAI::IsTank(bot))) { // AGILITY mainly (STRENGTH also) - score += agility * 2 + strength + attack_power + armor_penetration * 1 + meleeDps * 5 + hit * 2 + crit * 1.5 + haste * 1.5 + expertise * 2.5; + score += agility * 2 + strength + attack_power + armor_penetration * 1 + meleeDps * 5 + hit * 1.5 + crit * 1.5 + haste * 1.5 + expertise * 2.5; } else if ((cls == CLASS_PALADIN && tab == 2) || // retribution (cls == CLASS_WARRIOR && tab != 2) || // arm / fury (cls == CLASS_DEATH_KNIGHT && tab != 0) // ice / unholy @@ -4552,20 +4563,20 @@ float PlayerbotFactory::CalculateSpellScore(uint32 spell_id, Player* bot, uint32 } else if ((cls == CLASS_SHAMAN && tab == 1)) { // enhancement // STRENGTH mainly (AGILITY, INTELLECT also) score += strength * 1 + agility * 1.5 + intellect * 1.5 + attack_power + spell_power * 1.5 + armor_penetration * 0.5 + meleeDps * 5 - + hit * 2 + crit * 1.5 + haste * 1.5 + expertise * 2; + + hit * 1.5 + crit * 1.5 + haste * 1.5 + expertise * 2; } else if ((cls == CLASS_WARRIOR && tab == 2) || (cls == CLASS_PALADIN && tab == 1)) { // TANK WITH SHIELD score += strength * 1 + agility * 2 + attack_power * 0.2 + defense * 2.5 + parry * 2 + dodge * 2 + resilience * 2 + block * 2 + armor * 0.3 + stamina * 3 - + hit * 1 + crit * 0.2 + haste * 0.5 + expertise * 3; + + hit * 0.5 + crit * 0.2 + haste * 0.5 + expertise * 3; } else if (cls == CLASS_DEATH_KNIGHT && tab == 0){ // BLOOD DK TANK score += strength * 1 + agility * 2 + attack_power * 0.2 + defense * 3.5 + parry * 2 + dodge * 2 + resilience * 2 + armor * 0.3 + stamina * 2.5 - + hit * 2 + crit * 0.5 + haste * 0.5 + expertise * 3.5; + + hit * 0.5 + crit * 0.5 + haste * 0.5 + expertise * 3.5; } else { - // BEAR DRUID TANK (AND FERAL DRUID...?) + // BEAR DRUID TANK score += agility * 1.5 + strength * 1 + attack_power * 0.5 + armor_penetration * 0.5 + meleeDps * 2 + defense * 0.25 + dodge * 0.25 + armor * 0.3 + stamina * 1.5 + hit * 1 + crit * 1 + haste * 0.5 + expertise * 3; diff --git a/src/PlayerbotMgr.cpp b/src/PlayerbotMgr.cpp index 4c8f984f..665c7fdf 100644 --- a/src/PlayerbotMgr.cpp +++ b/src/PlayerbotMgr.cpp @@ -74,6 +74,12 @@ void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder const& holder) { + // has bot already been added? + Player* loginBot = ObjectAccessor::FindConnectedPlayer(holder.GetGuid()); + if (loginBot && loginBot->IsInWorld()) { + return; + } + uint32 botAccountId = holder.GetAccountId(); WorldSession* botSession = new WorldSession(botAccountId, "", nullptr, SEC_PLAYER, EXPANSION_WRATH_OF_THE_LICH_KING, time_t(0), LOCALE_enUS, 0, false, false, 0, true); @@ -363,6 +369,11 @@ Player* PlayerbotHolder::GetPlayerBot(ObjectGuid::LowType lowGuid) const void PlayerbotHolder::OnBotLogin(Player* const bot) { + // Prevent duplicate login + if (playerBots.find(bot->GetGUID()) != playerBots.end()) { + return; + } + sPlayerbotsMgr->AddPlayerbotData(bot, true); playerBots[bot->GetGUID()] = bot; OnBotLoginInternal(bot); @@ -564,6 +575,13 @@ std::string const PlayerbotHolder::ProcessBotCommand(std::string const cmd, Obje return "ERROR: You can not use this command on non-summoned random bot."; } + if (!admin) { + Player* master = ObjectAccessor::FindConnectedPlayer(masterguid); + if (master && (master->IsInCombat() || bot->IsInCombat())) { + return "ERROR: You can not use this command during combat."; + } + } + if (GET_PLAYERBOT_AI(bot)) { if (Player* master = GET_PLAYERBOT_AI(bot)->GetMaster()) { diff --git a/src/Playerbots.h b/src/Playerbots.h index 66ec850c..7e678af3 100644 --- a/src/Playerbots.h +++ b/src/Playerbots.h @@ -34,6 +34,8 @@ int strcmpi(char const* s1, char const* s2); #define AI_VALUE_LAZY(type, name) context->GetValue(name)->LazyGet() #define AI_VALUE2_LAZY(type, name, param) context->GetValue(name, param)->LazyGet() +#define AI_VALUE_REF(type, name) context->GetValue(name)->RefGet() + #define SET_AI_VALUE(type, name, value) context->GetValue(name)->Set(value) #define SET_AI_VALUE2(type, name, param, value) context->GetValue(name, param)->Set(value) #define RESET_AI_VALUE(type, name) context->GetValue(name)->Reset() diff --git a/src/RandomPlayerbotFactory.cpp b/src/RandomPlayerbotFactory.cpp index 20ef853b..6721c8e4 100644 --- a/src/RandomPlayerbotFactory.cpp +++ b/src/RandomPlayerbotFactory.cpp @@ -55,9 +55,9 @@ RandomPlayerbotFactory::RandomPlayerbotFactory(uint32 accountId) : accountId(acc availableRaces[CLASS_PRIEST].push_back(RACE_NIGHTELF); availableRaces[CLASS_PRIEST].push_back(RACE_TROLL); availableRaces[CLASS_PRIEST].push_back(RACE_UNDEAD_PLAYER); - availableRaces[CLASS_PRIEST].push_back(RACE_DRAENEI); if(expansion >= EXPANSION_THE_BURNING_CRUSADE) { + availableRaces[CLASS_PRIEST].push_back(RACE_DRAENEI); availableRaces[CLASS_PRIEST].push_back(RACE_BLOODELF); } diff --git a/src/RandomPlayerbotMgr.cpp b/src/RandomPlayerbotMgr.cpp index 334b6499..ae9a0c60 100644 --- a/src/RandomPlayerbotMgr.cpp +++ b/src/RandomPlayerbotMgr.cpp @@ -384,6 +384,12 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/) activateCheckBgQueueThread(); } + if (sPlayerbotAIConfig->randomBotJoinLfg/* && !players.empty()*/) + { + if (time(nullptr) > (LfgCheckTimer + 30)) + activateCheckLfgQueueThread(); + } + uint32 updateBots = sPlayerbotAIConfig->randomBotsPerInterval * onlineBotFocus / 100; uint32 maxNewBots = onlineBotCount < maxAllowedBotCount ? maxAllowedBotCount - onlineBotCount : 0; uint32 loginBots = std::min(sPlayerbotAIConfig->randomBotsPerInterval - updateBots, maxNewBots); diff --git a/src/strategy/StrategyContext.h b/src/strategy/StrategyContext.h index 55bdc4bc..82090440 100644 --- a/src/strategy/StrategyContext.h +++ b/src/strategy/StrategyContext.h @@ -112,6 +112,7 @@ class StrategyContext : public NamedObjectContext creators["grind"] = &StrategyContext::grind; creators["avoid aoe"] = &StrategyContext::avoid_aoe; creators["move random"] = &StrategyContext::move_random; + creators["combat formation"] = &StrategyContext::combat_formation; } private: @@ -174,6 +175,7 @@ class StrategyContext : public NamedObjectContext static Strategy* grind(PlayerbotAI* botAI) { return new GrindingStrategy(botAI); } static Strategy* avoid_aoe(PlayerbotAI* botAI) { return new AvoidAoeStrategy(botAI); } static Strategy* move_random(PlayerbotAI* ai) { return new MoveRandomStrategy(ai); } + static Strategy* combat_formation(PlayerbotAI* ai) { return new CombatFormationStrategy(ai); } }; class MovementStrategyContext : public NamedObjectContext diff --git a/src/strategy/Value.h b/src/strategy/Value.h index 9cba6efe..4d4196a3 100644 --- a/src/strategy/Value.h +++ b/src/strategy/Value.h @@ -16,6 +16,16 @@ class PlayerbotAI; class Unit; +class FleeInfo +{ + public: + Position fromPos; + float radius; + float angle; + uint32 timestamp; + int GetAngleRangeIndex() { return (angle + 2 * M_PI) / (M_PI / 2); } // [0, 7) +}; + struct CreatureData; class UntypedValue : public AiNamedObject @@ -37,6 +47,7 @@ class Value virtual ~Value() { } virtual T Get() = 0; virtual T LazyGet() = 0; + virtual T& RefGet() = 0; virtual void Reset() { } virtual void Set(T value) = 0; operator T() { return Get(); } @@ -79,7 +90,26 @@ class CalculatedValue : public UntypedValue, public Value return value; } - + T& RefGet() override + { + if (checkInterval < 2) { + // PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_VALUE, this->getName(), this->context ? &this->context->performanceStack : nullptr); + value = Calculate(); + // if (pmo) + // pmo->finish(); + } else { + time_t now = getMSTime(); + if (!lastCheckTime || now - lastCheckTime >= checkInterval) + { + lastCheckTime = now; + // PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_VALUE, this->getName(), this->context ? &this->context->performanceStack : nullptr); + value = Calculate(); + // if (pmo) + // pmo->finish(); + } + } + return value; + } void Set(T val) override { value = val; } void Update() override {} void Reset() override { lastCheckTime = 0; } @@ -304,6 +334,7 @@ class ManualSetValue : public UntypedValue, public Value T Get() override { return value; } T LazyGet() override { return value; } + T& RefGet() override { return value; } void Set(T val) override { value = val; } void Update() override {} void Reset() override @@ -326,4 +357,32 @@ class UnitManualSetValue : public ManualSetValue Unit* Get() override; }; +class DisperseDistanceValue : public ManualSetValue +{ + public: + DisperseDistanceValue(PlayerbotAI* botAI, float defaultValue = -1.0f, std::string const name = "disperse distance") : + ManualSetValue(botAI, defaultValue, name) { } +}; + +class LastFleeAngleValue : public ManualSetValue +{ + public: + LastFleeAngleValue(PlayerbotAI* botAI, float defaultValue = 0.0f, std::string const name = "last flee angle") : + ManualSetValue(botAI, defaultValue, name) { } +}; + +class LastFleeTimestampValue : public ManualSetValue +{ + public: + LastFleeTimestampValue(PlayerbotAI* botAI, uint32 defaultValue = 0, std::string const name = "last flee timestamp") : + ManualSetValue(botAI, defaultValue, name) { } +}; + +class RecentlyFleeInfo : public ManualSetValue> +{ + public: + RecentlyFleeInfo(PlayerbotAI* botAI, std::list defaultValue = {}, std::string const name = "recently flee info") : + ManualSetValue>(botAI, defaultValue, name) { } +}; + #endif diff --git a/src/strategy/actions/ActionContext.h b/src/strategy/actions/ActionContext.h index 6cdc68b1..ea8b3362 100644 --- a/src/strategy/actions/ActionContext.h +++ b/src/strategy/actions/ActionContext.h @@ -89,6 +89,8 @@ class ActionContext : public NamedObjectContext creators["flee"] = &ActionContext::flee; creators["flee with pet"] = &ActionContext::flee_with_pet; creators["avoid aoe"] = &ActionContext::avoid_aoe; + creators["combat formation move"] = &ActionContext::combat_formation_move; + creators["disperse set"] = &ActionContext::disperse_set; creators["gift of the naaru"] = &ActionContext::gift_of_the_naaru; creators["shoot"] = &ActionContext::shoot; creators["lifeblood"] = &ActionContext::lifeblood; @@ -150,6 +152,7 @@ class ActionContext : public NamedObjectContext creators["auto talents"] = &ActionContext::auto_talents; creators["auto learn spell"] = &ActionContext::auto_learn_spell; creators["auto teleport for level"] = &ActionContext::auto_teleport_for_level; + creators["auto upgrade equip"] = &ActionContext::auto_upgrade_equip; creators["xp gain"] = &ActionContext::xp_gain; creators["invite nearby"] = &ActionContext::invite_nearby; creators["invite guild"] = &ActionContext::invite_guild; @@ -265,6 +268,8 @@ class ActionContext : public NamedObjectContext static Action* flee(PlayerbotAI* botAI) { return new FleeAction(botAI); } static Action* flee_with_pet(PlayerbotAI* botAI) { return new FleeWithPetAction(botAI); } static Action* avoid_aoe(PlayerbotAI* botAI) { return new AvoidAoeAction(botAI); } + static Action* combat_formation_move(PlayerbotAI* botAI) { return new CombatFormationMoveAction(botAI); } + static Action* disperse_set(PlayerbotAI* botAI) { return new DisperseSetAction(botAI); } static Action* gift_of_the_naaru(PlayerbotAI* botAI) { return new CastGiftOfTheNaaruAction(botAI); } static Action* lifeblood(PlayerbotAI* botAI) { return new CastLifeBloodAction(botAI); } static Action* arcane_torrent(PlayerbotAI* botAI) { return new CastArcaneTorrentAction(botAI); } @@ -315,6 +320,7 @@ class ActionContext : public NamedObjectContext static Action* auto_talents(PlayerbotAI* botAI) { return new AutoSetTalentsAction(botAI); } static Action* auto_learn_spell(PlayerbotAI* botAI) { return new AutoLearnSpellAction(botAI); } static Action* auto_teleport_for_level(PlayerbotAI* botAI) { return new AutoTeleportForLevelAction(botAI); } + static Action* auto_upgrade_equip(PlayerbotAI* botAI) { return new AutoUpgradeEquipAction(botAI); } static Action* xp_gain(PlayerbotAI* botAI) { return new XpGainAction(botAI); } static Action* invite_nearby(PlayerbotAI* botAI) { return new InviteNearbyToGroupAction(botAI); } static Action* invite_guild(PlayerbotAI* botAI) { return new InviteGuildToGroupAction(botAI); } diff --git a/src/strategy/actions/AttackAction.cpp b/src/strategy/actions/AttackAction.cpp index f0d85a8c..1ba9f4b5 100644 --- a/src/strategy/actions/AttackAction.cpp +++ b/src/strategy/actions/AttackAction.cpp @@ -95,7 +95,7 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/) return false; } - if (bot->IsMounted() && bot->IsWithinLOSInMap(target) && sServerFacade->GetDistance2d(bot, target) < 40.0f) + if (bot->IsMounted() && bot->IsWithinLOSInMap(target)) { WorldPacket emptyPacket; bot->GetSession()->HandleCancelMountAuraOpcode(emptyPacket); diff --git a/src/strategy/actions/AutoLearnSpellAction.cpp b/src/strategy/actions/AutoLearnSpellAction.cpp index 3202eb96..d742d4ad 100644 --- a/src/strategy/actions/AutoLearnSpellAction.cpp +++ b/src/strategy/actions/AutoLearnSpellAction.cpp @@ -24,7 +24,6 @@ bool AutoLearnSpellAction::Execute(Event event) out << "."; botAI->TellMaster(out); } - return true; } @@ -181,3 +180,15 @@ void AutoLearnSpellAction::LearnSpell(uint32 spellId, std::ostringstream* out) } } } + +bool AutoUpgradeEquipAction::Execute(Event event) { + if (!sPlayerbotAIConfig->autoUpgradeEquip || !sRandomPlayerbotMgr->IsRandomBot(bot)) { + return false; + } + PlayerbotFactory factory(bot, bot->GetLevel(), ITEM_QUALITY_RARE); + if (!sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel) { + factory.InitEquipment(true); + } + factory.InitAmmo(); + return true; +} diff --git a/src/strategy/actions/AutoLearnSpellAction.h b/src/strategy/actions/AutoLearnSpellAction.h index 9e6267c3..1cefaf48 100644 --- a/src/strategy/actions/AutoLearnSpellAction.h +++ b/src/strategy/actions/AutoLearnSpellAction.h @@ -23,4 +23,12 @@ class AutoLearnSpellAction : public Action void LearnSpell(uint32 spellId, std::ostringstream* out); }; +class AutoUpgradeEquipAction : public Action +{ + public: + AutoUpgradeEquipAction(PlayerbotAI* botAI, std::string const name = "auto upgrade equip") : Action(botAI, name) { } + + bool Execute(Event event); +}; + #endif diff --git a/src/strategy/actions/AutoTeleportForLevelAction.cpp b/src/strategy/actions/AutoTeleportForLevelAction.cpp index 1ad4e0c4..0f4c8f63 100644 --- a/src/strategy/actions/AutoTeleportForLevelAction.cpp +++ b/src/strategy/actions/AutoTeleportForLevelAction.cpp @@ -6,8 +6,6 @@ #include "SharedDefines.h" bool AutoTeleportForLevelAction::Execute(Event event) { - AutoUpgradeEquip(); - if (!sPlayerbotAIConfig->autoTeleportForLevel || !sRandomPlayerbotMgr->IsRandomBot(bot)) { return false; } @@ -16,15 +14,4 @@ bool AutoTeleportForLevelAction::Execute(Event event) { } sRandomPlayerbotMgr->RandomTeleportForLevel(bot); return true; -} - -void AutoTeleportForLevelAction::AutoUpgradeEquip() { - if (!sPlayerbotAIConfig->autoUpgradeEquip || !sRandomPlayerbotMgr->IsRandomBot(bot)) { - return; - } - PlayerbotFactory factory(bot, bot->GetLevel(), ITEM_QUALITY_RARE); - if (!sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel) { - factory.InitEquipment(true); - } - factory.InitAmmo(); } \ No newline at end of file diff --git a/src/strategy/actions/AutoTeleportForLevelAction.h b/src/strategy/actions/AutoTeleportForLevelAction.h index c8397d53..ca13edf2 100644 --- a/src/strategy/actions/AutoTeleportForLevelAction.h +++ b/src/strategy/actions/AutoTeleportForLevelAction.h @@ -15,8 +15,6 @@ class AutoTeleportForLevelAction : public Action AutoTeleportForLevelAction(PlayerbotAI* botAI, std::string const name = "auto teleport for level") : Action(botAI, name) { } bool Execute(Event event); - private: - void AutoUpgradeEquip(); }; #endif diff --git a/src/strategy/actions/BattleGroundJoinAction.cpp b/src/strategy/actions/BattleGroundJoinAction.cpp index cfd552b4..c9c0c396 100644 --- a/src/strategy/actions/BattleGroundJoinAction.cpp +++ b/src/strategy/actions/BattleGroundJoinAction.cpp @@ -459,7 +459,7 @@ bool BGJoinAction::JoinQueue(uint32 type) // get BG MapId uint32 bgTypeId_ = bgTypeId; uint32 instanceId = 0; // 0 = First Available - bool joinAsGroup = bot->GetGroup() && bot->GetGroup()->GetLeaderGUID() == bot->GetGUID(); + bool isPremade = false; bool isArena = false; bool isRated = false; @@ -483,6 +483,11 @@ bool BGJoinAction::JoinQueue(uint32 type) return false; } + // refresh food/regs + sRandomPlayerbotMgr->Refresh(bot); + + bool joinAsGroup = bot->GetGroup() && bot->GetGroup()->GetLeaderGUID() == bot->GetGUID(); + // in wotlk only arena requires battlemaster guid ObjectGuid guid = isArena ? unit->GetGUID() : bot->GetGUID(); @@ -546,8 +551,6 @@ bool BGJoinAction::JoinQueue(uint32 type) bot->GetGUID().ToString().c_str(), bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->getLevel(), bot->GetName().c_str(), _bgType.c_str(), isRated ? "Rated Arena" : isArena ? "Arena" : ""); - // refresh food/regs - sRandomPlayerbotMgr->Refresh(bot); if (isArena) { diff --git a/src/strategy/actions/BattleGroundTactics.cpp b/src/strategy/actions/BattleGroundTactics.cpp index 226cce6f..a9e916d3 100644 --- a/src/strategy/actions/BattleGroundTactics.cpp +++ b/src/strategy/actions/BattleGroundTactics.cpp @@ -40,12 +40,16 @@ Position const WS_FLAG_HIDE_ALLIANCE_2 = { 1540.286f, 1476.026f, 352.692f, 2.91f Position const WS_FLAG_HIDE_ALLIANCE_3 = { 1495.807f, 1466.774f, 352.350f, 1.50f }; std::vector const WS_FLAG_HIDE_HORDE = { WS_FLAG_HIDE_HORDE_1 , WS_FLAG_HIDE_HORDE_2, WS_FLAG_HIDE_HORDE_3 }; std::vector const WS_FLAG_HIDE_ALLIANCE = { WS_FLAG_HIDE_ALLIANCE_1 , WS_FLAG_HIDE_ALLIANCE_2, WS_FLAG_HIDE_ALLIANCE_3 }; + Position const AB_WAITING_POS_HORDE = { 702.884f, 703.045f, -16.115f, 0.77f }; Position const AB_WAITING_POS_ALLIANCE = { 1286.054f, 1282.500f, -15.697f, 3.95f }; + Position const AV_WAITING_POS_ALLIANCE = { 793.627f, -493.814f, 99.689f, 3.09f }; Position const AV_WAITING_POS_HORDE = { -1381.865f, -544.872f, 54.773f, 0.76f }; -Position const AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE = { -492.17f, -187.077f, 57.1342f, 2.77f }; -Position const AV_STONEHEARTH_WAITING_HORDE = { 28.1264f, -302.593f, 15.076f, 2.96f }; +Position const AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE = { -523.105f, -182.178f, 57.956f, 2.77f }; +Position const AV_ICEBLOOD_GARRISON_ATTACKING_ALLIANCE = { -545.288f, -167.932f, 57.012f, 2.77f }; +Position const AV_STONEHEARTH_WAITING_HORDE = { -36.399f, -306.403f, 15.565f, 2.96f }; +Position const AV_STONEHEARTH_ATTACKING_HORDE = { -55.210f, -288.546f, 15.578f, 2.96f }; Position const EY_WAITING_POS_HORDE = { 1809.102f, 1540.854f, 1267.142f, 6.18f }; Position const EY_WAITING_POS_ALLIANCE = { 2526.020f, 1596.787f, 1270.127f, 3.14f }; @@ -799,7 +803,16 @@ BattleBotPath vPath_AB_Farm_to_LumberMill = BattleBotPath vPath_AV_Horde_Cave_to_Tower_Point_Crossroad = { - { -885.928f, -536.612f, 55.1936f, nullptr }, + { -1362.395f, -529.615f, 52.636f, nullptr }, + { -1327.036f, -511.374f, 51.138f, nullptr }, + { -1277.047f, -516.327f, 50.667f, nullptr }, + { -1214.901f, -529.350f, 52.251f, nullptr }, + { -1151.129f, -545.598f, 51.990f, nullptr }, + { -1085.775f, -538.719f, 47.905f, nullptr }, + { -1038.552f, -517.946f, 43.876f, nullptr }, + { -981.371f, -494.593f, 41.127f, nullptr }, + { -930.598f, -463.751f, 43.060f, nullptr }, + { -887.138f, -475.816f, 44.374f, nullptr }, { -880.957f, -525.119f, 53.6791f, nullptr }, { -839.408f, -499.746f, 49.7505f, nullptr }, { -820.21f, -469.193f, 49.4085f, nullptr }, @@ -2262,48 +2275,48 @@ std::vector const vPaths_HordeMine = &vPath_AV_Coldtooth_Mine_Entrance_to_Coldtooth_Mine_Boss, }; -static uint32 AV_HordeAttackObjectives[] = +static std::pair AV_HordeAttackObjectives[] = { // Attack - { BG_AV_NODES_STONEHEART_BUNKER }, - { BG_AV_NODES_STONEHEART_GRAVE }, - { BG_AV_NODES_ICEWING_BUNKER }, - { BG_AV_NODES_STORMPIKE_GRAVE }, - { BG_AV_NODES_DUNBALDAR_SOUTH }, - { BG_AV_NODES_DUNBALDAR_NORTH }, - { BG_AV_NODES_FIRSTAID_STATION } + { BG_AV_NODES_STONEHEART_BUNKER, BG_AV_OBJECT_FLAG_A_STONEHEART_BUNKER }, + { BG_AV_NODES_STONEHEART_GRAVE, BG_AV_OBJECT_FLAG_A_STONEHEART_GRAVE }, + { BG_AV_NODES_ICEWING_BUNKER, BG_AV_OBJECT_FLAG_A_ICEWING_BUNKER }, + { BG_AV_NODES_STORMPIKE_GRAVE, BG_AV_OBJECT_FLAG_A_STORMPIKE_GRAVE }, + { BG_AV_NODES_DUNBALDAR_SOUTH, BG_AV_OBJECT_FLAG_A_DUNBALDAR_SOUTH }, + { BG_AV_NODES_DUNBALDAR_NORTH, BG_AV_OBJECT_FLAG_A_DUNBALDAR_NORTH }, + { BG_AV_NODES_FIRSTAID_STATION, BG_AV_OBJECT_FLAG_A_FIRSTAID_STATION } }; -static uint32 AV_HordeDefendObjectives[] = +static std::pair AV_HordeDefendObjectives[] = { // Defend - { BG_AV_NODES_FROSTWOLF_GRAVE }, - { BG_AV_NODES_FROSTWOLF_ETOWER }, - { BG_AV_NODES_FROSTWOLF_WTOWER }, - { BG_AV_NODES_TOWER_POINT }, - { BG_AV_NODES_ICEBLOOD_TOWER }, + { BG_AV_NODES_FROSTWOLF_GRAVE, BG_AV_OBJECT_FLAG_H_FROSTWOLF_GRAVE }, + { BG_AV_NODES_FROSTWOLF_ETOWER, BG_AV_OBJECT_FLAG_H_FROSTWOLF_ETOWER }, + { BG_AV_NODES_FROSTWOLF_WTOWER, BG_AV_OBJECT_FLAG_H_FROSTWOLF_WTOWER }, + { BG_AV_NODES_TOWER_POINT, BG_AV_OBJECT_FLAG_H_TOWER_POINT }, + { BG_AV_NODES_ICEBLOOD_TOWER, BG_AV_OBJECT_FLAG_H_ICEBLOOD_TOWER }, }; -static uint32 AV_AllianceAttackObjectives[] = +static std::pair AV_AllianceAttackObjectives[] = { // Attack - { BG_AV_NODES_ICEBLOOD_TOWER }, - { BG_AV_NODES_ICEBLOOD_GRAVE }, - { BG_AV_NODES_TOWER_POINT }, - { BG_AV_NODES_FROSTWOLF_GRAVE }, - { BG_AV_NODES_FROSTWOLF_ETOWER }, - { BG_AV_NODES_FROSTWOLF_WTOWER }, - { BG_AV_NODES_FROSTWOLF_HUT }, + { BG_AV_NODES_ICEBLOOD_TOWER, BG_AV_OBJECT_FLAG_H_ICEBLOOD_TOWER}, + { BG_AV_NODES_ICEBLOOD_GRAVE, BG_AV_OBJECT_FLAG_H_ICEBLOOD_GRAVE}, + { BG_AV_NODES_TOWER_POINT, BG_AV_OBJECT_FLAG_H_TOWER_POINT }, + { BG_AV_NODES_FROSTWOLF_GRAVE, BG_AV_OBJECT_FLAG_H_FROSTWOLF_GRAVE }, + { BG_AV_NODES_FROSTWOLF_ETOWER, BG_AV_OBJECT_FLAG_H_FROSTWOLF_ETOWER }, + { BG_AV_NODES_FROSTWOLF_WTOWER, BG_AV_OBJECT_FLAG_H_FROSTWOLF_WTOWER }, + { BG_AV_NODES_FROSTWOLF_HUT, BG_AV_OBJECT_FLAG_H_FROSTWOLF_HUT }, }; -static uint32 AV_AllianceDefendObjectives[] = +static std::pair AV_AllianceDefendObjectives[] = { // Defend - { BG_AV_NODES_STORMPIKE_GRAVE }, - { BG_AV_NODES_DUNBALDAR_SOUTH }, - { BG_AV_NODES_DUNBALDAR_NORTH }, - { BG_AV_NODES_ICEWING_BUNKER }, - { BG_AV_NODES_STONEHEART_BUNKER }, + { BG_AV_NODES_STORMPIKE_GRAVE, BG_AV_OBJECT_FLAG_A_STORMPIKE_GRAVE }, + { BG_AV_NODES_DUNBALDAR_SOUTH, BG_AV_OBJECT_FLAG_A_DUNBALDAR_SOUTH }, + { BG_AV_NODES_DUNBALDAR_NORTH, BG_AV_OBJECT_FLAG_A_DUNBALDAR_NORTH }, + { BG_AV_NODES_ICEWING_BUNKER, BG_AV_OBJECT_FLAG_A_ICEWING_BUNKER }, + { BG_AV_NODES_STONEHEART_BUNKER, BG_AV_OBJECT_FLAG_A_STONEHEART_BUNKER }, }; static uint32 AB_AttackObjectives[] = @@ -2937,7 +2950,7 @@ bool BGTactics::selectObjective(bool reset) alterValleyBG->GetAVNodeInfo(BG_AV_NODES_STONEHEART_BUNKER).TotalOwnerId != TEAM_ALLIANCE && alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FIRSTAID_STATION).TotalOwnerId != TEAM_ALLIANCE) { - if (Creature* pVanndar = bg->GetBGCreature(AV_NPC_A_BOSS)) + if (Creature* pVanndar = bg->GetBGCreature(AV_CPLACE_TRIGGER17)) { BgObjective = pVanndar; endBoss = true; @@ -2952,8 +2965,9 @@ bool BGTactics::selectObjective(bool reset) // Only go to Snowfall Graveyard if already close to it. // Need to fix AV script - if (!BgObjective && supporter && (alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_ALLIANCE || - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_HORDE || alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_OTHER)) + if (!BgObjective && supporter && + (alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_ALLIANCE || + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_OTHER)) { if (GameObject* pGO = bg->GetBGObject(BG_AV_NODES_SNOWFALL_GRAVE)) if (bot->IsWithinDist(pGO, 200.f)) @@ -2967,19 +2981,20 @@ bool BGTactics::selectObjective(bool reset) if (!BgObjective && alterValleyBG->IsCaptainAlive(0)) { - if (Creature* pBalinda = bg->GetBGCreature(AV_NPC_A_CAPTAIN)) + if (Creature* pBalinda = bg->GetBGCreature(AV_CPLACE_TRIGGER16)) { if (pBalinda->getDeathState() != DeathState::Dead) { - uint32 attackCount = 0; - attackCount += getDefendersCount(AV_STONEHEARTH_WAITING_HORDE, 10.0f, false); + uint32 attackCount = getDefendersCount(AV_STONEHEARTH_WAITING_HORDE, 10.0f, false) + getDefendersCount(AV_STONEHEARTH_ATTACKING_HORDE, 10.0f, false); // prepare to attack Captain if (attackCount < 10 && !pBalinda->IsInCombat()) { // get in position to attack Captain - pos.Set(AV_STONEHEARTH_WAITING_HORDE.GetPositionX(), AV_STONEHEARTH_WAITING_HORDE.GetPositionY(), - AV_STONEHEARTH_WAITING_HORDE.GetPositionZ(), bg->GetMapId()); + pos.Set(AV_STONEHEARTH_WAITING_HORDE.GetPositionX(), + AV_STONEHEARTH_WAITING_HORDE.GetPositionY(), + AV_STONEHEARTH_WAITING_HORDE.GetPositionZ(), + bg->GetMapId()); std::ostringstream out; out << "Taking position at Stonehearth!"; @@ -2987,6 +3002,12 @@ bool BGTactics::selectObjective(bool reset) } else { + // they need help getting there (or did before I fixed the target creature, will leave in anyway, as it probably makes it more robust) + pos.Set(AV_STONEHEARTH_ATTACKING_HORDE.GetPositionX(), + AV_STONEHEARTH_ATTACKING_HORDE.GetPositionY(), + AV_STONEHEARTH_ATTACKING_HORDE.GetPositionZ(), + bg->GetMapId()); + std::ostringstream out; out << "Attacking Balinda!"; //bot->Say(out.str(), LANG_UNIVERSAL); @@ -3007,9 +3028,9 @@ bool BGTactics::selectObjective(bool reset) { for (const auto& objective : AV_HordeDefendObjectives) { - if (!BgObjective && alterValleyBG->GetAVNodeInfo(objective).OwnerId == TEAM_ALLIANCE) + if (!BgObjective && alterValleyBG->GetAVNodeInfo(objective.first).OwnerId == TEAM_ALLIANCE) { - if (GameObject* pGO = bg->GetBGObject(objective)) + if (GameObject* pGO = bg->GetBGObject(objective.second)) if (bot->IsWithinDist(pGO, 400.0f)) { BgObjective = pGO; @@ -3022,7 +3043,8 @@ bool BGTactics::selectObjective(bool reset) } // Mine capture (need paths & script fix) - if (!BgObjective && supporter && !endBoss && (alterValleyBG->GetMineOwner(AV_NORTH_MINE) == TEAM_ALLIANCE || alterValleyBG->GetMineOwner(AV_NORTH_MINE) == TEAM_OTHER) && + if (!BgObjective && supporter && !endBoss && + (alterValleyBG->GetMineOwner(AV_NORTH_MINE) == TEAM_ALLIANCE || alterValleyBG->GetMineOwner(AV_NORTH_MINE) == TEAM_OTHER) && alterValleyBG->GetAVNodeInfo(BG_AV_NODES_STORMPIKE_GRAVE).OwnerId != TEAM_ALLIANCE) { if (Creature* mBossNeutral = bg->GetBGCreature(AV_CPLACE_MINE_N_3)) @@ -3055,10 +3077,9 @@ bool BGTactics::selectObjective(bool reset) for (const auto& objective : AV_HordeAttackObjectives) { if ((!BgObjective/* || (supporter && objective.first == BG_AV_NODES_STONEHEART_BUNKER && !bg->IsActiveEvent(BG_AV_NODE_CAPTAIN_DEAD_A, 0))*/) && - (alterValleyBG->GetAVNodeInfo(objective).OwnerId == TEAM_ALLIANCE || alterValleyBG->GetAVNodeInfo(objective).TotalOwnerId == TEAM_ALLIANCE || - alterValleyBG->GetAVNodeInfo(objective).OwnerId == TEAM_OTHER)) + alterValleyBG->GetAVNodeInfo(objective.first).TotalOwnerId == TEAM_ALLIANCE)//need to check TotalOwnerId for attack objectives { - if (GameObject* pGO = bg->GetBGObject(objective)) + if (GameObject* pGO = bg->GetBGObject(objective.second)) { BgObjective = pGO; //std::ostringstream out; @@ -3073,11 +3094,13 @@ bool BGTactics::selectObjective(bool reset) { bool endBoss = false; // End boss - if (alterValleyBG->GetAVNodeInfo(BG_AV_NODES_ICEBLOOD_TOWER).OwnerId != TEAM_HORDE && alterValleyBG->GetAVNodeInfo(BG_AV_NODES_TOWER_POINT).OwnerId != TEAM_HORDE && - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_ETOWER).OwnerId != TEAM_HORDE && alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_WTOWER).OwnerId != TEAM_HORDE && - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_HUT).OwnerId != TEAM_HORDE) + if (alterValleyBG->GetAVNodeInfo(BG_AV_NODES_ICEBLOOD_TOWER).TotalOwnerId != TEAM_HORDE && + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_TOWER_POINT).TotalOwnerId != TEAM_HORDE && + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_ETOWER).TotalOwnerId != TEAM_HORDE && + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_WTOWER).TotalOwnerId != TEAM_HORDE && + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_HUT).TotalOwnerId != TEAM_HORDE) { - if (Creature* pDrek = bg->GetBGCreature(AV_NPC_H_BOSS)) + if (Creature* pDrek = bg->GetBGCreature(AV_CPLACE_TRIGGER19)) { BgObjective = pDrek; endBoss = true; @@ -3091,9 +3114,9 @@ bool BGTactics::selectObjective(bool reset) bool supporter = role < 3; // Only go to Snowfall Graveyard if already close to it. - if (!BgObjective && supporter && (alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_HORDE || - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).TotalOwnerId == TEAM_HORDE || - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).TotalOwnerId == TEAM_OTHER)) + if (!BgObjective && supporter && + (alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_HORDE || + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_OTHER)) { if (GameObject* pGO = bg->GetBGObject(BG_AV_NODES_SNOWFALL_GRAVE)) if (bot->IsWithinDist(pGO, 200.f)) @@ -3110,9 +3133,9 @@ bool BGTactics::selectObjective(bool reset) { for (const auto& objective : AV_AllianceDefendObjectives) { - if (!BgObjective && alterValleyBG->GetAVNodeInfo(objective).OwnerId == TEAM_HORDE) + if (!BgObjective && alterValleyBG->GetAVNodeInfo(objective.first).OwnerId == TEAM_HORDE) { - if (GameObject* pGO = bg->GetBGObject(objective)) + if (GameObject* pGO = bg->GetBGObject(objective.second)) { BgObjective = pGO; //std::ostringstream out; out << "Defending Node #" << objective.first; @@ -3123,8 +3146,8 @@ bool BGTactics::selectObjective(bool reset) } // Mine capture (need paths & script fix) - if (!BgObjective && supporter && !endBoss && (alterValleyBG->GetMineOwner(AV_SOUTH_MINE) == TEAM_HORDE || - alterValleyBG->GetMineOwner(AV_SOUTH_MINE) == TEAM_OTHER) && + if (!BgObjective && supporter && !endBoss && + (alterValleyBG->GetMineOwner(AV_SOUTH_MINE) == TEAM_HORDE || alterValleyBG->GetMineOwner(AV_SOUTH_MINE) == TEAM_OTHER) && alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_GRAVE).TotalOwnerId != TEAM_HORDE) { if (Creature* mBossNeutral = bg->GetBGCreature(AV_CPLACE_MINE_S_3)) @@ -3158,17 +3181,18 @@ bool BGTactics::selectObjective(bool reset) if (alterValleyBG->IsCaptainAlive(1)) { - if (Creature* pGalvangar = bg->GetBGCreature(AV_NPC_H_CAPTAIN)) + if (Creature* pGalvangar = bg->GetBGCreature(AV_CPLACE_TRIGGER18)) { - uint32 attackCount = 0; - attackCount += getDefendersCount(AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE, 10.0f, false); + uint32 attackCount = getDefendersCount(AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE, 10.0f, false) + getDefendersCount(AV_ICEBLOOD_GARRISON_ATTACKING_ALLIANCE, 10.0f, false); // prepare to attack Captain if (attackCount < 10 && !pGalvangar->IsInCombat()) { // get in position to attack Captain - pos.Set(AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE.GetPositionX(), AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE.GetPositionY(), - AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE.GetPositionZ(), bg->GetMapId()); + pos.Set(AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE.GetPositionX(), + AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE.GetPositionY(), + AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE.GetPositionZ(), + bg->GetMapId()); //std::ostringstream out; //out << "Taking position at Iceblood Outpost!"; @@ -3176,6 +3200,12 @@ bool BGTactics::selectObjective(bool reset) } else { + // they need help getting there (or did before I fixed the target creature, will leave in anyway, as it probably makes it more robust) + pos.Set(AV_ICEBLOOD_GARRISON_ATTACKING_ALLIANCE.GetPositionX(), + AV_ICEBLOOD_GARRISON_ATTACKING_ALLIANCE.GetPositionY(), + AV_ICEBLOOD_GARRISON_ATTACKING_ALLIANCE.GetPositionZ(), + bg->GetMapId()); + //std::ostringstream out; // out << "Attacking Galvangar!"; //bot->Say(out.str(), LANG_UNIVERSAL); @@ -3187,11 +3217,9 @@ bool BGTactics::selectObjective(bool reset) for (const auto& objective : AV_AllianceAttackObjectives) { - if (alterValleyBG->GetAVNodeInfo(objective).OwnerId == TEAM_HORDE || - alterValleyBG->GetAVNodeInfo(objective).TotalOwnerId == TEAM_HORDE || - alterValleyBG->GetAVNodeInfo(objective).TotalOwnerId == TEAM_OTHER) + if (alterValleyBG->GetAVNodeInfo(objective.first).TotalOwnerId == TEAM_HORDE)//need to check TotalOwnerId for attack objectives { - if (GameObject* pGO = bg->GetBGObject(objective)) + if (GameObject* pGO = bg->GetBGObject(objective.second)) { float const distance = sqrt(bot->GetDistance(pGO)); if (attackObjectiveDistance > distance) @@ -4087,7 +4115,8 @@ bool BGTactics::selectObjective(bool reset) } break; } - + default: + break; } return false; @@ -4124,7 +4153,7 @@ bool BGTactics::moveToObjective() } // don't try to move if already close - if (sqrt(bot->GetDistance(pos.x, pos.y, pos.z)) < 5.0f) + if (sqrt(bot->GetDistance(pos.x, pos.y, pos.z)) < 2.0f) { resetObjective(); @@ -4134,8 +4163,8 @@ bool BGTactics::moveToObjective() //std::ostringstream out; out << "Moving to objective " << pos.x << ", " << pos.y << ", Distance: " << sServerFacade->GetDistance2d(bot, pos.x, pos.y); //bot->Say(out.str(), LANG_UNIVERSAL); - // more precise position for wsg - if (bgType == BATTLEGROUND_WS) + // more precise position for wsg and AV (flags in AV towers require precision) + if (bgType == BATTLEGROUND_WS || bgType == BATTLEGROUND_AV) return MoveTo(bot->GetMapId(), pos.x, pos.y, pos.z); else return MoveNear(bot->GetMapId(), pos.x, pos.y, pos.z, 3.0f); @@ -4162,99 +4191,84 @@ bool BGTactics::selectObjectiveWp(std::vector const& vPaths) if (bgType == BATTLEGROUND_WS /* && (bot->HasAura(BG_WS_SPELL_WARSONG_FLAG) || bot->HasAura(BG_WS_SPELL_SILVERWING_FLAG))*/) return wsgPaths(); - BattleBotPath* pClosestPath = nullptr; - uint32 closestPoint = 0; - float closestDistanceToTarget = FLT_MAX; - bool reverse = false; - float maxDistanceToPoint = 50.0f; - if (bgType == BATTLEGROUND_IC) - maxDistanceToPoint = 80.0f; + float chosenPathScore = FLT_MAX;//lower score is better + BattleBotPath* chosenPath = nullptr; + uint32 chosenPathPoint = 0; + bool chosenPathReverse = false; - for (auto const& pPath : vPaths) + float botDistanceLimit = 50.0f; // limit for how far path can be from bot + float botDistanceScoreSubtract = 8.0f; // path score modifier - lower = less likely to chose a further path (it's basically the distance from bot that's ignored) + float botDistanceScoreMultiply = 3.0f; // path score modifier - higher = less likely to chose a further path (it's basically a multiplier on distance from bot - makes distance from bot more signifcant than distance from destination) + + if (bgType == BATTLEGROUND_IC) + botDistanceLimit = 80.0f; + else if (bgType == BATTLEGROUND_AB) + { + botDistanceScoreSubtract = 2.0f; + botDistanceScoreMultiply = 4.0f; + } + + for (auto const& path : vPaths) { // skip mine paths of own faction - if (bot->GetTeamId() == TEAM_ALLIANCE && std::find(vPaths_AllyMine.begin(), vPaths_AllyMine.end(), pPath) != vPaths_AllyMine.end()) + if (bot->GetTeamId() == TEAM_ALLIANCE && std::find(vPaths_AllyMine.begin(), vPaths_AllyMine.end(), path) != vPaths_AllyMine.end()) continue; - if (bot->GetTeamId() == TEAM_HORDE && std::find(vPaths_HordeMine.begin(), vPaths_HordeMine.end(), pPath) != vPaths_HordeMine.end()) + if (bot->GetTeamId() == TEAM_HORDE && std::find(vPaths_HordeMine.begin(), vPaths_HordeMine.end(), path) != vPaths_HordeMine.end()) continue; - BattleBotWaypoint& lastPoint = ((*pPath)[pPath->size() - 1]); - float const distanceFromPathEndToTarget = sqrt(Position(pos.x, pos.y, pos.z, 0.f).GetExactDist(lastPoint.x, lastPoint.y, lastPoint.z)); - if (closestDistanceToTarget > distanceFromPathEndToTarget) + BattleBotWaypoint& startPoint = ((*path)[0]); + float const startPointDistToDestination = sqrt(Position(pos.x, pos.y, pos.z, 0.f).GetExactDist(startPoint.x, startPoint.y, startPoint.z)); + BattleBotWaypoint& endPoint = ((*path)[path->size() - 1]); + float const endPointDistToDestination = sqrt(Position(pos.x, pos.y, pos.z, 0.f).GetExactDist(endPoint.x, endPoint.y, endPoint.z)); + + bool reverse = startPointDistToDestination < endPointDistToDestination; + + // dont travel reverse if it's a reverse paths + if (reverse && std::find(vPaths_NoReverseAllowed.begin(), vPaths_NoReverseAllowed.end(), path) != vPaths_NoReverseAllowed.end()) + continue; + + int closestPointIndex = -1; + float closestPointDistToBot = FLT_MAX; + for (uint32 i = 0; i < path->size(); i++) { - float closestDistanceFromMeToPoint = FLT_MAX; - - for (uint32 i = 0; i < pPath->size(); i++) + BattleBotWaypoint& waypoint = ((*path)[i]); + float const distToBot = sqrt(bot->GetDistance(waypoint.x, waypoint.y, waypoint.z)); + if (closestPointDistToBot > distToBot) { - BattleBotWaypoint& waypoint = ((*pPath)[i]); - float const distanceFromMeToPoint = sqrt(bot->GetDistance(waypoint.x, waypoint.y, waypoint.z)); - if (distanceFromMeToPoint < maxDistanceToPoint && closestDistanceFromMeToPoint > distanceFromMeToPoint) - { - reverse = false; - pClosestPath = pPath; - closestPoint = i; - closestDistanceToTarget = distanceFromPathEndToTarget; - closestDistanceFromMeToPoint = distanceFromMeToPoint; - } + closestPointDistToBot = distToBot; + closestPointIndex = i; } } - // skip no reverse paths - if (std::find(vPaths_NoReverseAllowed.begin(), vPaths_NoReverseAllowed.end(), pPath) != vPaths_NoReverseAllowed.end()) + // don't pick path where bot is already closest to the paths closest point to target (it means path cant lead it anywhere) + // don't pick path where closest point is too far away + if (closestPointIndex == (reverse ? 0 : path->size() - 1) || closestPointDistToBot > botDistanceLimit) continue; - // skip mine paths of own faction - if (bot->GetTeamId() == TEAM_ALLIANCE && std::find(vPaths_AllyMine.begin(), vPaths_AllyMine.end(), pPath) != vPaths_AllyMine.end()) - continue; - if (bot->GetTeamId() == TEAM_HORDE && std::find(vPaths_HordeMine.begin(), vPaths_HordeMine.end(), pPath) != vPaths_HordeMine.end()) - continue; + // creates a score based on dist-to-bot and dist-to-destination, where lower is better, and dist-to-bot is more important (when its beyond a certain distance) + // dist-to-bot is more important because otherwise they cant reach it at all (or will fly through air with MM::MovePoint()), also bot may need to use multiple + // paths (one after another) anyway + float distToDestination = reverse ? startPointDistToDestination : endPointDistToDestination; + float pathScore = (closestPointDistToBot < botDistanceScoreSubtract ? 0.0f : ((closestPointDistToBot - botDistanceScoreSubtract) * botDistanceScoreMultiply)) + distToDestination; - { - BattleBotWaypoint& firstPoint = ((*pPath)[0]); - float const distanceFromPathBeginToTarget = sqrt(Position(pos.x, pos.y, pos.z, 0).GetExactDist(firstPoint.x, firstPoint.y, firstPoint.z)); - if (closestDistanceToTarget > distanceFromPathBeginToTarget) - { - float closestDistanceFromMeToPoint = FLT_MAX; + //LOG_INFO("playerbots", "bot={}\t{:6.1f}\t{:4.1f}\t{:4.1f}\t{}", bot->GetName(), pathScore, closestPointDistToBot, distToDestination, vPaths_AB_name[pathNum]); - for (uint32 i = 0; i < pPath->size(); i++) - { - BattleBotWaypoint& waypoint = ((*pPath)[i]); - float const distanceFromMeToPoint = sqrt(bot->GetDistance(waypoint.x, waypoint.y, waypoint.z)); - if (distanceFromMeToPoint < maxDistanceToPoint && closestDistanceFromMeToPoint > distanceFromMeToPoint) - { - reverse = true; - pClosestPath = pPath; - closestPoint = i; - closestDistanceToTarget = distanceFromPathBeginToTarget; - closestDistanceFromMeToPoint = distanceFromMeToPoint; - } - } - } + if (chosenPathScore > pathScore) { + chosenPathScore = pathScore; + chosenPath = path; + chosenPathPoint = closestPointIndex; + chosenPathReverse = reverse; } } - if (!pClosestPath) + if (!chosenPath) return false; - // Prevent picking last point of path. - // It means we are already there. - if (reverse) - { - if (closestPoint == 0) - return false; - } - else - { - if (closestPoint == pClosestPath->size() - 1) - return false; - } + //LOG_INFO("playerbots", "bot={} {}", bot->GetName(), vPaths_AB_name[chosenPathNum]); - BattleBotPath* currentPath = pClosestPath; - uint32 currentPoint = reverse ? closestPoint + 1 : closestPoint - 1; - - return moveToObjectiveWp(currentPath, currentPoint, reverse); + return moveToObjectiveWp(chosenPath, chosenPathPoint, chosenPathReverse); return false; } @@ -4490,7 +4504,7 @@ bool BGTactics::atFlag(std::vector const& vPaths, std::vectorisSpawned() || !go->GetGoState() == GO_STATE_READY) + if (!go->isSpawned() || go->GetGoState() != GO_STATE_READY) continue; if (!bot->CanUseBattlegroundObject(go) && bgType != BATTLEGROUND_WS) @@ -4631,6 +4645,8 @@ bool BGTactics::atFlag(std::vector const& vPaths, std::vectorGetMapId(), 1291.58f + frand(-5, +5), 790.87f + frand(-5, +5), 7.8f, false, true)) { // they like to hang around at the tip of the pipes doing nothing, so we just teleport them down - if (bot->GetDistance(1333.07f, 817.18f, 13.35f) < 2) + if (bot->GetDistance(1333.07f, 817.18f, 13.35f) < 4) bot->TeleportTo(bg->GetMapId(), 1330.96f, 816.75f, 3.2f, bot->GetOrientation()); - if (bot->GetDistance(1250.13f, 764.79f, 13.34f) < 2) + if (bot->GetDistance(1250.13f, 764.79f, 13.34f) < 4) bot->TeleportTo(bg->GetMapId(), 1252.19f, 765.41f, 3.2f, bot->GetOrientation()); } break; diff --git a/src/strategy/actions/BuyAction.cpp b/src/strategy/actions/BuyAction.cpp index 73246fae..4346ba1c 100644 --- a/src/strategy/actions/BuyAction.cpp +++ b/src/strategy/actions/BuyAction.cpp @@ -91,6 +91,8 @@ bool BuyAction::Execute(Event event) case ITEM_USAGE_SKILL: needMoneyFor = NeedMoneyFor::tradeskill; break; + default: + break; } if (needMoneyFor == NeedMoneyFor::none) diff --git a/src/strategy/actions/CastCustomSpellAction.cpp b/src/strategy/actions/CastCustomSpellAction.cpp index a3914ea3..774c0cd8 100644 --- a/src/strategy/actions/CastCustomSpellAction.cpp +++ b/src/strategy/actions/CastCustomSpellAction.cpp @@ -11,7 +11,7 @@ uint32 FindLastSeparator(std::string const text, std::string const sep) { - uint32 pos = text.rfind(sep); + size_t pos = text.rfind(sep); if (pos == std::string::npos) return pos; @@ -64,7 +64,7 @@ bool CastCustomSpellAction::Execute(Event event) Item* itemTarget = nullptr; - uint32 pos = FindLastSeparator(text, " "); + size_t pos = FindLastSeparator(text, " "); uint32 castCount = 1; if (pos != std::string::npos) { diff --git a/src/strategy/actions/ChangeTalentsAction.cpp b/src/strategy/actions/ChangeTalentsAction.cpp index 69a6b19f..75de8e22 100644 --- a/src/strategy/actions/ChangeTalentsAction.cpp +++ b/src/strategy/actions/ChangeTalentsAction.cpp @@ -44,9 +44,11 @@ bool ChangeTalentsAction::Execute(Event event) } else if (param.find("spec ") != std::string::npos) { param = param.substr(5); out << SpecPick(param); + botAI->ResetStrategies(); } else if (param.find("apply ") != std::string::npos) { param = param.substr(6); out << SpecApply(param); + botAI->ResetStrategies(); } else { out << "Unknown command."; } diff --git a/src/strategy/actions/ChooseTravelTargetAction.cpp b/src/strategy/actions/ChooseTravelTargetAction.cpp index f6b37494..614a8b73 100644 --- a/src/strategy/actions/ChooseTravelTargetAction.cpp +++ b/src/strategy/actions/ChooseTravelTargetAction.cpp @@ -46,19 +46,19 @@ void ChooseTravelTargetAction::getNewTarget(TravelTarget* newTarget, TravelTarge foundTarget = SetNpcFlagTarget(newTarget, { UNIT_NPC_FLAG_BANKER,UNIT_NPC_FLAG_BATTLEMASTER,UNIT_NPC_FLAG_AUCTIONEER }); //Grind for money - if (!foundTarget && AI_VALUE(bool, "should get money")) + if (!foundTarget && AI_VALUE(bool, "should get money")) { if (urand(1, 100) > 66) { foundTarget = SetQuestTarget(newTarget, true); //Turn in quests for money. if (!foundTarget) foundTarget = SetQuestTarget(newTarget); //Do low level quests - } - else if (urand(1, 100) > 50) + } else if (urand(1, 100) > 50) { foundTarget = SetGrindTarget(newTarget); //Go grind mobs for money - else + } else { foundTarget = SetNewQuestTarget(newTarget); //Find a low level quest to do - + } + } //Continue if (!foundTarget && urand(1, 100) > 10) //90% chance diff --git a/src/strategy/actions/CustomStrategyEditAction.cpp b/src/strategy/actions/CustomStrategyEditAction.cpp index d4c89ee1..e1f5c572 100644 --- a/src/strategy/actions/CustomStrategyEditAction.cpp +++ b/src/strategy/actions/CustomStrategyEditAction.cpp @@ -10,7 +10,7 @@ bool CustomStrategyEditAction::Execute(Event event) { std::string text = event.getParam(); - uint32 pos = text.find(" "); + size_t pos = text.find(" "); if (pos == std::string::npos) return PrintHelp(); diff --git a/src/strategy/actions/LootRollAction.cpp b/src/strategy/actions/LootRollAction.cpp index 9f5db411..5b4835b4 100644 --- a/src/strategy/actions/LootRollAction.cpp +++ b/src/strategy/actions/LootRollAction.cpp @@ -147,6 +147,8 @@ RollVote LootRollAction::CalculateRollVote(ItemTemplate const* proto) case ITEM_USAGE_VENDOR: needVote = GREED; break; + default: + break; } return StoreLootAction::IsLootAllowed(proto->ItemId, GET_PLAYERBOT_AI(bot)) ? needVote : PASS; diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index f2652168..0dfea873 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -4,6 +4,7 @@ #include "MovementActions.h" #include "GameObject.h" +#include "Geometry.h" #include "Map.h" #include "MotionMaster.h" #include "MoveSplineInitArgs.h" @@ -12,6 +13,7 @@ #include "ObjectGuid.h" #include "PathGenerator.h" #include "PlayerbotAIConfig.h" +#include "Position.h" #include "Random.h" #include "SharedDefines.h" #include "SpellAuraEffects.h" @@ -25,10 +27,15 @@ #include "LootObjectStack.h" #include "Playerbots.h" #include "ServerFacade.h" +#include "Timer.h" #include "Transport.h" #include "Unit.h" #include "Vehicle.h" #include "WaypointMovementGenerator.h" +#include +#include +#include +#include MovementAction::MovementAction(PlayerbotAI* botAI, std::string const name) : Action(botAI, name) { @@ -156,7 +163,9 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // } bool generatePath = !bot->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) && !bot->IsFlying() && !bot->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING) && !bot->IsInWater(); - if (!generatePath) { + bool disableMoveSplinePath = sPlayerbotAIConfig->disableMoveSplinePath >= 2 || + (sPlayerbotAIConfig->disableMoveSplinePath == 1 && bot->InBattleground()); + if (disableMoveSplinePath || !generatePath) { float distance = bot->GetExactDist(x, y, z); if (distance > sPlayerbotAIConfig->contactDistance) { @@ -1495,6 +1504,9 @@ bool FleeWithPetAction::Execute(Event event) bool AvoidAoeAction::isUseful() { + if (getMSTime() - moveInterval < lastMoveTimer) { + return false; + } GuidVector traps = AI_VALUE(GuidVector, "nearest trap with damage"); GuidVector triggers = AI_VALUE(GuidVector, "possible triggers"); return AI_VALUE(Aura*, "area debuff") || !traps.empty() || !triggers.empty(); @@ -1523,6 +1535,9 @@ bool AvoidAoeAction::AvoidAuraWithDynamicObj() if (!aura || aura->IsRemoved() || aura->IsExpired()) { return false; } + if (!aura->GetOwner() || !aura->GetOwner()->IsInWorld()) { + return false; + } // Crash fix: maybe change owner due to check interval if (aura->GetType() != DYNOBJ_AURA_TYPE) { return false; @@ -1540,8 +1555,15 @@ bool AvoidAoeAction::AvoidAuraWithDynamicObj() return false; } std::ostringstream name; - name << spellInfo->SpellName[0]; // << "] (aura)"; - if (FleePosition(dynOwner->GetPosition(), radius, name.str())) { + name << spellInfo->SpellName[sWorld->GetDefaultDbcLocale()]; // << "] (aura)"; + if (FleePosition(dynOwner->GetPosition(), radius)) { + if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) { + lastTellTimer = time(NULL); + lastMoveTimer = getMSTime(); + std::ostringstream out; + out << "I'm avoiding " << name.str() << "..."; + bot->Say(out.str(), LANG_UNIVERSAL); + } return true; } return false; @@ -1591,8 +1613,15 @@ bool AvoidAoeAction::AvoidGameObjectWithDamage() continue; } std::ostringstream name; - name << spellInfo->SpellName[0]; // << "] (object)"; - if (FleePosition(go->GetPosition(), radius, name.str())) { + name << spellInfo->SpellName[sWorld->GetDefaultDbcLocale()]; // << "] (object)"; + if (FleePosition(go->GetPosition(), radius)) { + if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) { + lastTellTimer = time(NULL); + lastMoveTimer = getMSTime(); + std::ostringstream out; + out << "I'm avoiding " << name.str() << "..."; + bot->Say(out.str(), LANG_UNIVERSAL); + } return true; } @@ -1633,9 +1662,15 @@ bool AvoidAoeAction::AvoidUnitWithDamageAura() break; } std::ostringstream name; - name << triggerSpellInfo->SpellName[0]; //<< "] (unit)"; - if (FleePosition(unit->GetPosition(), radius, name.str())) { - return true; + name << triggerSpellInfo->SpellName[sWorld->GetDefaultDbcLocale()]; //<< "] (unit)"; + if (FleePosition(unit->GetPosition(), radius)) { + if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) { + lastTellTimer = time(NULL); + lastMoveTimer = getMSTime(); + std::ostringstream out; + out << "I'm avoiding " << name.str() << "..."; + bot->Say(out.str(), LANG_UNIVERSAL); + } } } } @@ -1645,7 +1680,7 @@ bool AvoidAoeAction::AvoidUnitWithDamageAura() return false; } -Position AvoidAoeAction::BestPositionForMelee(Position pos, float radius) +Position MovementAction::BestPositionForMeleeToFlee(Position pos, float radius) { Unit* currentTarget = AI_VALUE(Unit*, "current target"); std::vector possibleAngles; @@ -1670,13 +1705,18 @@ Position AvoidAoeAction::BestPositionForMelee(Position pos, float radius) Position bestPos; for (CheckAngle &checkAngle : possibleAngles) { float angle = checkAngle.angle; + auto& infoList = AI_VALUE_REF(std::list, "recently flee info"); + if (!CheckLastFlee(angle, infoList)) { + continue; + } bool strict = checkAngle.strict; - float fleeDis = sPlayerbotAIConfig->fleeDistance; + float fleeDis = std::min(radius + 1.0f, sPlayerbotAIConfig->fleeDistance); Position fleePos{bot->GetPositionX() + cos(angle) * fleeDis, bot->GetPositionY() + sin(angle) * fleeDis, bot->GetPositionZ()}; if (strict && currentTarget - && fleePos.GetExactDist(currentTarget) - currentTarget->GetCombatReach() > sPlayerbotAIConfig->tooCloseDistance) { + && fleePos.GetExactDist(currentTarget) - currentTarget->GetCombatReach() > sPlayerbotAIConfig->tooCloseDistance + && bot->IsWithinMeleeRange(currentTarget)) { continue; } if (pos.GetExactDist(fleePos) > farestDis) { @@ -1690,7 +1730,7 @@ Position AvoidAoeAction::BestPositionForMelee(Position pos, float radius) return Position(); } -Position AvoidAoeAction::BestPositionForRanged(Position pos, float radius) +Position MovementAction::BestPositionForRangedToFlee(Position pos, float radius) { Unit* currentTarget = AI_VALUE(Unit*, "current target"); std::vector possibleAngles; @@ -1713,8 +1753,12 @@ Position AvoidAoeAction::BestPositionForRanged(Position pos, float radius) Position bestPos; for (CheckAngle &checkAngle : possibleAngles) { float angle = checkAngle.angle; + auto& infoList = AI_VALUE_REF(std::list, "recently flee info"); + if (!CheckLastFlee(angle, infoList)) { + continue; + } bool strict = checkAngle.strict; - float fleeDis = sPlayerbotAIConfig->fleeDistance; + float fleeDis = std::min(radius + 1.0f, sPlayerbotAIConfig->fleeDistance); Position fleePos{bot->GetPositionX() + cos(angle) * fleeDis, bot->GetPositionY() + sin(angle) * fleeDis, bot->GetPositionZ()}; @@ -1737,28 +1781,197 @@ Position AvoidAoeAction::BestPositionForRanged(Position pos, float radius) return Position(); } -bool AvoidAoeAction::FleePosition(Position pos, float radius, std::string name) +bool MovementAction::FleePosition(Position pos, float radius) { Position bestPos; if (botAI->IsMelee(bot)) { - bestPos = BestPositionForMelee(pos, radius); + bestPos = BestPositionForMeleeToFlee(pos, radius); } else { - bestPos = BestPositionForRanged(pos, radius); + bestPos = BestPositionForRangedToFlee(pos, radius); } if (bestPos != Position()) { if (MoveTo(bot->GetMapId(), bestPos.GetPositionX(), bestPos.GetPositionY(), bestPos.GetPositionZ(), false, false, true)) { - if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) { - lastTellTimer = time(NULL); - std::ostringstream out; - out << "I'm avoiding " << name << "..."; - bot->Say(out.str(), LANG_UNIVERSAL); + auto& infoList = AI_VALUE_REF(std::list, "recently flee info"); + uint32 curTS = getMSTime(); + while (!infoList.empty()) { + if (infoList.size() > 10 || infoList.front().timestamp + 5000 < curTS) { + infoList.pop_front(); + } else { + break; + } } + infoList.push_back({pos, radius, bot->GetAngle(&bestPos), curTS}); return true; } } return false; } +bool MovementAction::CheckLastFlee(float curAngle, std::list& infoList) +{ + uint32 curTS = getMSTime(); + curAngle = fmod(curAngle, 2 * M_PI); + while (!infoList.empty()) { + if (infoList.size() > 10 || infoList.front().timestamp + 5000 < curTS) { + infoList.pop_front(); + } else { + break; + } + } + for (FleeInfo& info : infoList) { + // more than 5 sec + if (info.timestamp + 5000 < curTS) { + continue; + } + float revAngle = fmod(info.angle + M_PI, 2 * M_PI); + // angle too close + if (fabs(revAngle - curAngle) < M_PI / 4) { + return false; + } + } + return true; +} + +bool CombatFormationMoveAction::isUseful() +{ + if (getMSTime() - moveInterval < lastMoveTimer) { + return false; + } + if (bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL) != nullptr) { + return false; + } + float dis = AI_VALUE(float, "disperse distance"); + return dis > 0.0f; +} + +bool CombatFormationMoveAction::Execute(Event event) +{ + float dis = AI_VALUE(float, "disperse distance"); + Player* playerToLeave = NearestGroupMember(dis); + if (playerToLeave && bot->GetExactDist(playerToLeave) < dis) { + if (FleePosition(playerToLeave->GetPosition(), dis)) { + lastMoveTimer = getMSTime(); + } + } + return false; +} + +Position CombatFormationMoveAction::AverageGroupPos(float dis) +{ + float averageX = 0, averageY = 0, averageZ = 0; + int cnt = 0; + Group* group = bot->GetGroup(); + if (!group) { + return Position(); + } + Group::MemberSlotList const& groupSlot = group->GetMemberSlots(); + for (Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); itr++) + { + Player *member = ObjectAccessor::FindPlayer(itr->guid); + if (!member || !member->IsAlive() || member->GetMapId() != bot->GetMapId() || member->IsCharmed() || sServerFacade->GetDistance2d(bot, member) > dis) + continue; + cnt++; + averageX += member->GetPositionX(); + averageY += member->GetPositionY(); + averageZ += member->GetPositionZ(); + } + averageX /= cnt; + averageY /= cnt; + averageZ /= cnt; + return Position(averageX, averageY, averageZ); +} + +Player* CombatFormationMoveAction::NearestGroupMember(float dis) +{ + float nearestDis = 10000.0f; + Player* result = nullptr; + Group* group = bot->GetGroup(); + if (!group) { + return result; + } + Group::MemberSlotList const& groupSlot = group->GetMemberSlots(); + for (Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); itr++) + { + Player *member = ObjectAccessor::FindPlayer(itr->guid); + if (!member || !member->IsAlive() || member == bot || member->GetMapId() != bot->GetMapId() || member->IsCharmed() || sServerFacade->GetDistance2d(bot, member) > dis) + continue; + if (nearestDis > bot->GetExactDist(member)) { + result = member; + nearestDis = bot->GetExactDist(member); + } + } + return result; +} + +bool DisperseSetAction::Execute(Event event) +{ + std::string const text = event.getParam(); + if (text == "disable") { + RESET_AI_VALUE(float, "disperse distance"); + botAI->TellMasterNoFacing("Disable disperse"); + return true; + } + if (text == "enable" || text == "reset") { + if (botAI->IsMelee(bot)) { + SET_AI_VALUE(float, "disperse distance", DEFAULT_DISPERSE_DISTANCE_MELEE); + } else { + SET_AI_VALUE(float, "disperse distance", DEFAULT_DISPERSE_DISTANCE_RANGED); + } + float dis = AI_VALUE(float, "disperse distance"); + std::ostringstream out; + out << "Enable disperse distance " << std::setprecision(2) << dis; + botAI->TellMasterNoFacing(out.str()); + return true; + } + if (text == "increase") { + float dis = AI_VALUE(float, "disperse distance"); + std::ostringstream out; + if (dis <= 0.0f) { + out << "Enable disperse first"; + botAI->TellMasterNoFacing(out.str()); + return true; + } + dis += 1.0f; + SET_AI_VALUE(float, "disperse distance", dis); + out << "Increase disperse distance to " << std::setprecision(2) << dis; + botAI->TellMasterNoFacing(out.str()); + return true; + } + if (text == "decrease") { + float dis = AI_VALUE(float, "disperse distance"); + dis -= 1.0f; + if (dis <= 0.0f) { + dis += 1.0f; + } + SET_AI_VALUE(float, "disperse distance", dis); + std::ostringstream out; + out << "Increase disperse distance to " << std::setprecision(2) << dis; + botAI->TellMasterNoFacing(out.str()); + return true; + } + if (text.starts_with("set")) { + float dis = -1.0f;; + sscanf(text.c_str(), "set %f", &dis); + std::ostringstream out; + if (dis < 0 || dis > 100.0f) { + out << "Invalid disperse distance " << std::setprecision(2) << dis; + } else { + SET_AI_VALUE(float, "disperse distance", dis); + out << "Set disperse distance to " << std::setprecision(2) << dis; + } + botAI->TellMasterNoFacing(out.str()); + return true; + } + std::ostringstream out; + out << "Usage: disperse [enable | disable | increase | decrease | set {distance}]"; + float dis = AI_VALUE(float, "disperse distance"); + if (dis > 0.0f) { + out << "(Current disperse distance: " << std::setprecision(2) << dis << ")"; + } + botAI->TellMasterNoFacing(out.str()); + return true; +} + bool RunAwayAction::Execute(Event event) { return Flee(AI_VALUE(Unit*, "master target")); diff --git a/src/strategy/actions/MovementActions.h b/src/strategy/actions/MovementActions.h index f26991a4..c00ee5c8 100644 --- a/src/strategy/actions/MovementActions.h +++ b/src/strategy/actions/MovementActions.h @@ -13,6 +13,7 @@ class Player; class PlayerbotAI; class Unit; class WorldObject; +class Position; class MovementAction : public Action { @@ -41,6 +42,15 @@ class MovementAction : public Action bool MoveAway(Unit* target); bool MoveInside(uint32 mapId, float x, float y, float z, float distance = sPlayerbotAIConfig->followDistance); void CreateWp(Player* wpOwner, float x, float y, float z, float o, uint32 entry, bool important = false); + Position BestPositionForMeleeToFlee(Position pos, float radius); + Position BestPositionForRangedToFlee(Position pos, float radius); + bool FleePosition(Position pos, float radius); + bool CheckLastFlee(float curAngle, std::list& infoList); + protected: + struct CheckAngle { + float angle; + bool strict; + }; private: // float SearchBestGroundZForPath(float x, float y, float z, bool generatePath, float range = 20.0f, bool normal_only = false, float step = 8.0f); const Movement::PointsArray SearchForBestPath(float x, float y, float z, float &modified_z, int maxSearchCount = 5, bool normal_only = false, float step = 8.0f); @@ -69,7 +79,8 @@ class FleeWithPetAction : public MovementAction class AvoidAoeAction : public MovementAction { public: - AvoidAoeAction(PlayerbotAI* botAI) : MovementAction(botAI, "avoid aoe") { } + AvoidAoeAction(PlayerbotAI* botAI, int moveInterval = 1000) : MovementAction(botAI, "avoid aoe"), + moveInterval(moveInterval) { } bool isUseful() override; bool Execute(Event event) override; @@ -78,14 +89,36 @@ class AvoidAoeAction : public MovementAction bool AvoidAuraWithDynamicObj(); bool AvoidGameObjectWithDamage(); bool AvoidUnitWithDamageAura(); - Position BestPositionForMelee(Position pos, float radius); - Position BestPositionForRanged(Position pos, float radius); - bool FleePosition(Position pos, float radius, std::string name); time_t lastTellTimer = 0; - struct CheckAngle { - float angle; - bool strict; - }; + int lastMoveTimer = 0; + int moveInterval; + +}; + +class CombatFormationMoveAction : public MovementAction +{ + public: + CombatFormationMoveAction(PlayerbotAI* botAI, int moveInterval = 1000) : MovementAction(botAI, "combat formation move"), + moveInterval(moveInterval) { } + + bool isUseful() override; + bool Execute(Event event) override; + + protected: + Position AverageGroupPos(float dis = sPlayerbotAIConfig->sightDistance); + Player* NearestGroupMember(float dis = sPlayerbotAIConfig->sightDistance); + int lastMoveTimer = 0; + int moveInterval; +}; + +class DisperseSetAction : public Action +{ + public: + DisperseSetAction(PlayerbotAI* botAI, std::string const name = "disperse set") : Action(botAI, name) { } + + bool Execute(Event event) override; + float DEFAULT_DISPERSE_DISTANCE_RANGED = 5.0f; + float DEFAULT_DISPERSE_DISTANCE_MELEE = 2.0f; }; class RunAwayAction : public MovementAction diff --git a/src/strategy/actions/RangeAction.cpp b/src/strategy/actions/RangeAction.cpp index 9ac49520..c405d516 100644 --- a/src/strategy/actions/RangeAction.cpp +++ b/src/strategy/actions/RangeAction.cpp @@ -17,7 +17,7 @@ bool RangeAction::Execute(Event event) PrintRange("flee"); } - uint32 pos = param.find(" "); + size_t pos = param.find(" "); if (pos == std::string::npos) return false; diff --git a/src/strategy/actions/StayActions.cpp b/src/strategy/actions/StayActions.cpp index 1b810dd4..ca8a83b1 100644 --- a/src/strategy/actions/StayActions.cpp +++ b/src/strategy/actions/StayActions.cpp @@ -13,7 +13,6 @@ bool StayActionBase::Stay() //if (!urand(0, 10)) //botAI->PlaySound(TEXT_EMOTE_YAWN); - if (bot->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE) return false; @@ -26,12 +25,13 @@ bool StayActionBase::Stay() context->GetValue("stay time")->Set(stayTime); } - if (!bot->isMoving()) - return false; - - bot->StopMoving(); - bot->ClearUnitState(UNIT_STATE_CHASE); - bot->ClearUnitState(UNIT_STATE_FOLLOW); + // Stop the bot from moving immediately when action is called + if (bot->isMoving()) + { + bot->StopMoving(); + bot->ClearUnitState(UNIT_STATE_CHASE); + bot->ClearUnitState(UNIT_STATE_FOLLOW); + } return true; } @@ -43,7 +43,8 @@ bool StayAction::Execute(Event event) bool StayAction::isUseful() { - return !AI_VALUE2(bool, "moving", "self target"); + // Only useful if the bot is currently moving + return AI_VALUE2(bool, "moving", "self target"); } bool SitAction::Execute(Event event) diff --git a/src/strategy/actions/TalkToQuestGiverAction.cpp b/src/strategy/actions/TalkToQuestGiverAction.cpp index a0e1b62e..78f2879c 100644 --- a/src/strategy/actions/TalkToQuestGiverAction.cpp +++ b/src/strategy/actions/TalkToQuestGiverAction.cpp @@ -53,6 +53,8 @@ void TalkToQuestGiverAction::ProcessQuest(Quest const* quest, Object* questGiver case QUEST_STATUS_FAILED: out << "|cffff0000Failed|r"; break; + default: + break; } out << ": " << chat->FormatQuest(quest); @@ -257,6 +259,8 @@ bool TurnInQueryQuestAction::Execute(Event event) case QUEST_STATUS_REWARDED: out << "|cffff0000Rewarded|r"; break; + default: + break; } out << ": " << chat->FormatQuest(quest); diff --git a/src/strategy/actions/TrainerAction.cpp b/src/strategy/actions/TrainerAction.cpp index 2454e563..af1e7a7c 100644 --- a/src/strategy/actions/TrainerAction.cpp +++ b/src/strategy/actions/TrainerAction.cpp @@ -198,6 +198,7 @@ bool AutoGearAction::Execute(Event event) sPlayerbotAIConfig->autoGearQualityLimit, gs); factory.InitEquipment(true); + factory.InitAmmo(); if (bot->getLevel() >= sPlayerbotAIConfig->minEnchantingBotLevel) { factory.ApplyEnchantAndGemsNew(); } diff --git a/src/strategy/actions/UseMeetingStoneAction.cpp b/src/strategy/actions/UseMeetingStoneAction.cpp index 3ef46e21..f0267e0c 100644 --- a/src/strategy/actions/UseMeetingStoneAction.cpp +++ b/src/strategy/actions/UseMeetingStoneAction.cpp @@ -82,7 +82,8 @@ bool SummonAction::Execute(Event event) } if (master->GetSession()->GetSecurity() >= SEC_PLAYER) { - botAI->GetAiObjectContext()->GetValue("prioritized targets")->Set({}); + // botAI->GetAiObjectContext()->GetValue("prioritized targets")->Set({}); + SET_AI_VALUE(std::list, "recently flee info", {}); return Teleport(master, bot); } @@ -175,11 +176,30 @@ bool SummonAction::Teleport(Player* summoner, Player* player) if (summoner->IsWithinLOS(x, y, z)) { - bool allowed = sPlayerbotAIConfig->botReviveWhenSummon == 2 || (sPlayerbotAIConfig->botReviveWhenSummon == 1 && !master->IsInCombat() && master->IsAlive()); - if (allowed && bot->isDead()) + if (sPlayerbotAIConfig->botRepairWhenSummon) // .conf option to repair bot gear when summoned 0 = off, 1 = on + bot->DurabilityRepairAll(false, 1.0f, false); + + if (master->IsInCombat() && !sPlayerbotAIConfig->allowSummonInCombat) + { + botAI->TellError("You cannot summon me while you're in combat"); + return false; + } + + if (!master->IsAlive() && !sPlayerbotAIConfig->allowSummonWhenMasterIsDead) + { + botAI->TellError("You cannot summon me while you're dead"); + return false; + } + + if (bot->isDead() && !bot->HasPlayerFlag(PLAYER_FLAGS_GHOST) && !sPlayerbotAIConfig->allowSummonWhenBotIsDead) + { + botAI->TellError("You cannot summon me while I'm dead, you need to release my spirit first"); + return false; + } + + if (bot->isDead() && sPlayerbotAIConfig->reviveBotWhenSummoned) { bot->ResurrectPlayer(1.0f, false); - bot->DurabilityRepairAll(false, 1.0f, false); botAI->TellMasterNoFacing("I live, again!"); } diff --git a/src/strategy/actions/WhoAction.cpp b/src/strategy/actions/WhoAction.cpp index 76bfd608..d7b9f2bc 100644 --- a/src/strategy/actions/WhoAction.cpp +++ b/src/strategy/actions/WhoAction.cpp @@ -11,8 +11,8 @@ #ifndef WIN32 inline int strcmpi(char const* s1, char const* s2) { - for (; *s1 && *s2 && (toupper(*s1) == toupper(*s2)); ++s1, ++s2); - return *s1 - *s2; + for (; *s1 && *s2 && (toupper(*s1) == toupper(*s2)); ++s1, ++s2) {} + return *s1 - *s2; } #endif diff --git a/src/strategy/deathknight/DKActions.h b/src/strategy/deathknight/DKActions.h index 6106af18..e4907ac8 100644 --- a/src/strategy/deathknight/DKActions.h +++ b/src/strategy/deathknight/DKActions.h @@ -234,8 +234,6 @@ class CastDeathAndDecayAction : public CastSpellAction { public: CastDeathAndDecayAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "death and decay") { } - - ActionThreatType getThreatType() override { return ActionThreatType::Aoe; } }; class CastHornOfWinterAction : public CastSpellAction diff --git a/src/strategy/deathknight/DKAiObjectContext.cpp b/src/strategy/deathknight/DKAiObjectContext.cpp index 99883cbc..e7641e71 100644 --- a/src/strategy/deathknight/DKAiObjectContext.cpp +++ b/src/strategy/deathknight/DKAiObjectContext.cpp @@ -67,7 +67,7 @@ class DeathKnightTriggerFactoryInternal : public NamedObjectContext DeathKnightTriggerFactoryInternal() { creators["bone shield"] = &DeathKnightTriggerFactoryInternal::bone_shield; - creators["pestilence"] = &DeathKnightTriggerFactoryInternal::pestilence; + creators["pestilence glyph"] = &DeathKnightTriggerFactoryInternal::pestilence_glyph; creators["blood strike"] = &DeathKnightTriggerFactoryInternal::blood_strike; creators["plague strike"] = &DeathKnightTriggerFactoryInternal::plague_strike; creators["plague strike on attacker"] = &DeathKnightTriggerFactoryInternal::plague_strike_on_attacker; @@ -94,7 +94,7 @@ class DeathKnightTriggerFactoryInternal : public NamedObjectContext private: static Trigger* bone_shield(PlayerbotAI* botAI) { return new BoneShieldTrigger(botAI); } - static Trigger* pestilence(PlayerbotAI* botAI) { return new PestilenceTrigger(botAI); } + static Trigger* pestilence_glyph(PlayerbotAI* botAI) { return new PestilenceGlyphTrigger(botAI); } static Trigger* blood_strike(PlayerbotAI* botAI) { return new BloodStrikeTrigger(botAI); } static Trigger* plague_strike(PlayerbotAI* botAI) { return new PlagueStrikeDebuffTrigger(botAI); } static Trigger* plague_strike_on_attacker(PlayerbotAI* botAI) { return new PlagueStrikeDebuffOnAttackerTrigger(botAI); } diff --git a/src/strategy/deathknight/DKTriggers.cpp b/src/strategy/deathknight/DKTriggers.cpp index 8eee1045..3f7f696e 100644 --- a/src/strategy/deathknight/DKTriggers.cpp +++ b/src/strategy/deathknight/DKTriggers.cpp @@ -14,7 +14,7 @@ bool DKPresenceTrigger::IsActive() return !botAI->HasAura("blood presence", target) && !botAI->HasAura("unholy presence", target) && !botAI->HasAura("frost presence", target); } -bool PestilenceTrigger::IsActive() { +bool PestilenceGlyphTrigger::IsActive() { if (!SpellTrigger::IsActive()) { return false; } diff --git a/src/strategy/deathknight/DKTriggers.h b/src/strategy/deathknight/DKTriggers.h index 08b91a82..f9dc88dc 100644 --- a/src/strategy/deathknight/DKTriggers.h +++ b/src/strategy/deathknight/DKTriggers.h @@ -71,10 +71,10 @@ class DeathCoilTrigger : public SpellCanBeCastTrigger DeathCoilTrigger(PlayerbotAI* botAI) : SpellCanBeCastTrigger(botAI, "death coil") { } }; -class PestilenceTrigger : public DebuffTrigger +class PestilenceGlyphTrigger : public SpellTrigger { public: - PestilenceTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "pestilence") { } + PestilenceGlyphTrigger(PlayerbotAI* botAI) : SpellTrigger(botAI, "pestilence") { } virtual bool IsActive() override; }; diff --git a/src/strategy/deathknight/GenericDKStrategy.cpp b/src/strategy/deathknight/GenericDKStrategy.cpp index 42c4ffa4..3e26cae2 100644 --- a/src/strategy/deathknight/GenericDKStrategy.cpp +++ b/src/strategy/deathknight/GenericDKStrategy.cpp @@ -192,5 +192,5 @@ void GenericDKStrategy::InitTriggers(std::vector& triggers) // triggers.push_back(new TriggerNode("light aoe", NextAction::array(0, // new NextAction("pestilence", ACTION_NORMAL + 4), // nullptr))); - triggers.push_back(new TriggerNode("pestilence", NextAction::array(0, new NextAction("pestilence", ACTION_HIGH + 9), NULL))); + triggers.push_back(new TriggerNode("pestilence glyph", NextAction::array(0, new NextAction("pestilence", ACTION_HIGH + 9), NULL))); } diff --git a/src/strategy/druid/BearTankDruidStrategy.cpp b/src/strategy/druid/BearTankDruidStrategy.cpp index 2ddee32d..ab74c3f2 100644 --- a/src/strategy/druid/BearTankDruidStrategy.cpp +++ b/src/strategy/druid/BearTankDruidStrategy.cpp @@ -69,7 +69,7 @@ class BearTankDruidStrategyActionNodeFactory : public NamedObjectFactory& trigger triggers.push_back(new TriggerNode("naxx", NextAction::array(0, new NextAction("naxx chat shortcut", relevance), NULL))); triggers.push_back(new TriggerNode("bwl", NextAction::array(0, new NextAction("bwl chat shortcut", relevance), NULL))); triggers.push_back(new TriggerNode("dps", NextAction::array(0, new NextAction("tell expected dps", relevance), NULL))); + triggers.push_back(new TriggerNode("disperse", NextAction::array(0, new NextAction("disperse set", relevance), NULL))); } ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI) diff --git a/src/strategy/generic/CombatStrategy.cpp b/src/strategy/generic/CombatStrategy.cpp index 3b72af7f..b77277fd 100644 --- a/src/strategy/generic/CombatStrategy.cpp +++ b/src/strategy/generic/CombatStrategy.cpp @@ -8,7 +8,7 @@ void CombatStrategy::InitTriggers(std::vector &triggers) { - triggers.push_back(new TriggerNode("enemy out of spell", NextAction::array(0, new NextAction("reach spell", ACTION_MOVE + 11), nullptr))); + triggers.push_back(new TriggerNode("enemy out of spell", NextAction::array(0, new NextAction("reach spell", ACTION_HIGH), nullptr))); triggers.push_back(new TriggerNode("invalid target", NextAction::array(0, new NextAction("drop target", 100), nullptr))); triggers.push_back(new TriggerNode("mounted", NextAction::array(0, new NextAction("check mount state", 54), nullptr))); // triggers.push_back(new TriggerNode("out of react range", NextAction::array(0, new NextAction("flee to master", 55), nullptr))); @@ -24,44 +24,44 @@ AvoidAoeStrategy::AvoidAoeStrategy(PlayerbotAI* botAI) : Strategy(botAI) } -class AvoidAoeStrategyMultiplier : public Multiplier -{ -public: - AvoidAoeStrategyMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "run away on area debuff") {} +// class AvoidAoeStrategyMultiplier : public Multiplier +// { +// public: +// AvoidAoeStrategyMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "run away on area debuff") {} -public: - virtual float GetValue(Action* action); +// public: +// virtual float GetValue(Action* action); -private: -}; +// private: +// }; -float AvoidAoeStrategyMultiplier::GetValue(Action* action) -{ - if (!action) - return 1.0f; +// float AvoidAoeStrategyMultiplier::GetValue(Action* action) +// { +// if (!action) +// return 1.0f; - std::string name = action->getName(); - if (name == "follow" || name == "co" || name == "nc" || name == "drop target") - return 1.0f; +// std::string name = action->getName(); +// if (name == "follow" || name == "co" || name == "nc" || name == "drop target") +// return 1.0f; - uint32 spellId = AI_VALUE2(uint32, "spell id", name); - const SpellInfo* const pSpellInfo = sSpellMgr->GetSpellInfo(spellId); - if (!pSpellInfo) return 1.0f; +// uint32 spellId = AI_VALUE2(uint32, "spell id", name); +// const SpellInfo* const pSpellInfo = sSpellMgr->GetSpellInfo(spellId); +// if (!pSpellInfo) return 1.0f; - if (spellId && pSpellInfo->Targets & TARGET_FLAG_DEST_LOCATION) - return 1.0f; - else if (spellId && pSpellInfo->Targets & TARGET_FLAG_SOURCE_LOCATION) - return 1.0f; +// if (spellId && pSpellInfo->Targets & TARGET_FLAG_DEST_LOCATION) +// return 1.0f; +// else if (spellId && pSpellInfo->Targets & TARGET_FLAG_SOURCE_LOCATION) +// return 1.0f; - uint32 castTime = pSpellInfo->CalcCastTime(bot); +// uint32 castTime = pSpellInfo->CalcCastTime(bot); - if (AI_VALUE2(bool, "has area debuff", "self target") && spellId && castTime > 0) - { - return 0.0f; - } +// if (AI_VALUE2(bool, "has area debuff", "self target") && spellId && castTime > 0) +// { +// return 0.0f; +// } - return 1.0f; -} +// return 1.0f; +// } NextAction** AvoidAoeStrategy::getDefaultActions() { @@ -81,4 +81,11 @@ void AvoidAoeStrategy::InitTriggers(std::vector& triggers) void AvoidAoeStrategy::InitMultipliers(std::vector& multipliers) { // multipliers.push_back(new AvoidAoeStrategyMultiplier(botAI)); -} \ No newline at end of file +} + +NextAction** CombatFormationStrategy::getDefaultActions() +{ + return NextAction::array(0, + new NextAction("combat formation move", ACTION_NORMAL), + nullptr); +} diff --git a/src/strategy/generic/CombatStrategy.h b/src/strategy/generic/CombatStrategy.h index 33813615..7f618064 100644 --- a/src/strategy/generic/CombatStrategy.h +++ b/src/strategy/generic/CombatStrategy.h @@ -28,4 +28,12 @@ public: void InitTriggers(std::vector& triggers) override; }; +class CombatFormationStrategy : public Strategy +{ +public: + CombatFormationStrategy(PlayerbotAI* ai): Strategy(ai) {} + const std::string getName() override { return "combat formation"; } + NextAction** getDefaultActions() override; +}; + #endif diff --git a/src/strategy/generic/WorldPacketHandlerStrategy.cpp b/src/strategy/generic/WorldPacketHandlerStrategy.cpp index a5c1065d..2db5ed84 100644 --- a/src/strategy/generic/WorldPacketHandlerStrategy.cpp +++ b/src/strategy/generic/WorldPacketHandlerStrategy.cpp @@ -37,9 +37,10 @@ void WorldPacketHandlerStrategy::InitTriggers(std::vector& trigger triggers.push_back(new TriggerNode("bg status", NextAction::array(0, new NextAction("bg status", relevance), nullptr))); triggers.push_back(new TriggerNode("xpgain", NextAction::array(0, new NextAction("xp gain", relevance), nullptr))); triggers.push_back(new TriggerNode("levelup", NextAction::array(0, - new NextAction("auto talents", relevance), - new NextAction("auto learn spell", relevance), - new NextAction("auto teleport for level", relevance), + new NextAction("auto teleport for level", relevance + 3), + new NextAction("auto talents", relevance + 2), + new NextAction("auto learn spell", relevance + 1), + new NextAction("auto upgrade equip", relevance), nullptr))); // triggers.push_back(new TriggerNode("group destroyed", NextAction::array(0, new NextAction("reset botAI", relevance), nullptr))); triggers.push_back(new TriggerNode("questgiver quest details", NextAction::array(0, new NextAction("turn in query quest", relevance), nullptr))); diff --git a/src/strategy/mage/MageActions.h b/src/strategy/mage/MageActions.h index a2b83bf8..1303498a 100644 --- a/src/strategy/mage/MageActions.h +++ b/src/strategy/mage/MageActions.h @@ -203,12 +203,14 @@ class CastDragonsBreathAction : public CastSpellAction { public: CastDragonsBreathAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "dragon's breath") { } + ActionThreatType getThreatType() override { return ActionThreatType::Aoe; } }; class CastBlastWaveAction : public CastSpellAction { public: CastBlastWaveAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "blast wave") { } + ActionThreatType getThreatType() override { return ActionThreatType::Aoe; } }; class CastInvisibilityAction : public CastBuffSpellAction diff --git a/src/strategy/paladin/PaladinActions.cpp b/src/strategy/paladin/PaladinActions.cpp index b2291d3d..6a56478b 100644 --- a/src/strategy/paladin/PaladinActions.cpp +++ b/src/strategy/paladin/PaladinActions.cpp @@ -153,7 +153,7 @@ bool CastMeleeConsecrationAction::isUseful() { Unit* target = GetTarget(); // float dis = distance + CONTACT_DISTANCE; - return target && bot->IsWithinCombatRange(target, sPlayerbotAIConfig->meleeDistance); // sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", GetTargetName()), distance); + return target && bot->IsWithinMeleeRange(target); // sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", GetTargetName()), distance); } bool CastDivineSacrificeAction::isUseful() diff --git a/src/strategy/paladin/TankPaladinStrategy.cpp b/src/strategy/paladin/TankPaladinStrategy.cpp index bbbeac00..cb1ca7b5 100644 --- a/src/strategy/paladin/TankPaladinStrategy.cpp +++ b/src/strategy/paladin/TankPaladinStrategy.cpp @@ -94,6 +94,9 @@ void TankPaladinStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode( "medium group heal occasion", NextAction::array(0, new NextAction("divine sacrifice", ACTION_HIGH + 5), nullptr))); + triggers.push_back(new TriggerNode( + "enough mana", + NextAction::array(0, new NextAction("melee consecration", ACTION_HIGH + 4), nullptr))); triggers.push_back(new TriggerNode( "not facing target", NextAction::array(0, new NextAction("set facing", ACTION_NORMAL + 7), nullptr))); diff --git a/src/strategy/priest/PriestActions.h b/src/strategy/priest/PriestActions.h index 9aab4eb3..5e4737b9 100644 --- a/src/strategy/priest/PriestActions.h +++ b/src/strategy/priest/PriestActions.h @@ -161,5 +161,6 @@ class CastMindSearAction : public CastSpellAction { public: CastMindSearAction(PlayerbotAI* ai) : CastSpellAction(ai, "mind sear") {} + ActionThreatType getThreatType() override { return ActionThreatType::Aoe; } }; #endif diff --git a/src/strategy/shaman/CasterShamanStrategy.cpp b/src/strategy/shaman/CasterShamanStrategy.cpp index aa1fef0a..381e7070 100644 --- a/src/strategy/shaman/CasterShamanStrategy.cpp +++ b/src/strategy/shaman/CasterShamanStrategy.cpp @@ -49,9 +49,11 @@ void CasterShamanStrategy::InitTriggers(std::vector& triggers) GenericShamanStrategy::InitTriggers(triggers); // triggers.push_back(new TriggerNode("enemy out of spell", NextAction::array(0, new NextAction("reach spell", ACTION_NORMAL + 9), nullptr))); - triggers.push_back(new TriggerNode("shaman weapon", NextAction::array(0, new NextAction("flametongue weapon", 23.0f), nullptr))); + // triggers.push_back(new TriggerNode("shaman weapon", NextAction::array(0, new NextAction("flametongue weapon", 23.0f), nullptr))); + triggers.push_back(new TriggerNode("main hand weapon no imbue", NextAction::array(0, new NextAction("flametongue weapon", 22.0f), nullptr))); // triggers.push_back(new TriggerNode("searing totem", NextAction::array(0, new NextAction("searing totem", 19.0f), nullptr))); triggers.push_back(new TriggerNode("flame shock", NextAction::array(0, new NextAction("flame shock", 20.0f), nullptr))); + triggers.push_back(new TriggerNode("elemental mastery", NextAction::array(0, new NextAction("elemental mastery", 27.0f), nullptr))); // triggers.push_back(new TriggerNode("frost shock snare", NextAction::array(0, new NextAction("frost shock", 21.0f), nullptr))); triggers.push_back(new TriggerNode( "no fire totem", diff --git a/src/strategy/shaman/GenericShamanStrategy.cpp b/src/strategy/shaman/GenericShamanStrategy.cpp index de5229e1..96a31cbf 100644 --- a/src/strategy/shaman/GenericShamanStrategy.cpp +++ b/src/strategy/shaman/GenericShamanStrategy.cpp @@ -35,7 +35,7 @@ class GenericShamanStrategyActionNodeFactory : public NamedObjectFactory& triggers) GenericShamanStrategy::InitTriggers(triggers); // triggers.push_back(new TriggerNode("enemy out of spell", NextAction::array(0, new NextAction("reach spell", ACTION_NORMAL + 9), nullptr))); - triggers.push_back(new TriggerNode("shaman weapon", NextAction::array(0, new NextAction("earthliving weapon", 22.0f), nullptr))); + // triggers.push_back(new TriggerNode("shaman weapon", NextAction::array(0, new NextAction("earthliving weapon", 22.0f), nullptr))); + triggers.push_back(new TriggerNode("main hand weapon no imbue", NextAction::array(0, new NextAction("earthliving weapon", 22.0f), nullptr))); triggers.push_back(new TriggerNode( "group heal occasion", NextAction::array(0, new NextAction("riptide on party", 23.0f), new NextAction("chain heal", 22.0f), NULL))); diff --git a/src/strategy/shaman/MeleeShamanStrategy.cpp b/src/strategy/shaman/MeleeShamanStrategy.cpp index fff1d05c..6b8bce24 100644 --- a/src/strategy/shaman/MeleeShamanStrategy.cpp +++ b/src/strategy/shaman/MeleeShamanStrategy.cpp @@ -63,7 +63,9 @@ void MeleeShamanStrategy::InitTriggers(std::vector& triggers) { GenericShamanStrategy::InitTriggers(triggers); - triggers.push_back(new TriggerNode("shaman weapon", NextAction::array(0, new NextAction("flametongue weapon", 22.0f), nullptr))); + //triggers.push_back(new TriggerNode("shaman weapon", NextAction::array(0, new NextAction("flametongue weapon", 22.0f), nullptr))); + triggers.push_back(new TriggerNode("main hand weapon no imbue", NextAction::array(0, new NextAction("windfury weapon", 22.0f), nullptr))); + triggers.push_back(new TriggerNode("off hand weapon no imbue", NextAction::array(0, new NextAction("flametongue weapon", 21.0f), nullptr))); // triggers.push_back(new TriggerNode("searing totem", NextAction::array(0, new NextAction("reach melee", 22.0f), new NextAction("searing totem", 22.0f), nullptr))); triggers.push_back(new TriggerNode("flame shock", NextAction::array(0, new NextAction("flame shock", 20.0f), nullptr))); triggers.push_back(new TriggerNode( diff --git a/src/strategy/shaman/ShamanActions.h b/src/strategy/shaman/ShamanActions.h index 24226b7a..2db469fb 100644 --- a/src/strategy/shaman/ShamanActions.h +++ b/src/strategy/shaman/ShamanActions.h @@ -323,6 +323,7 @@ class CastChainLightningAction : public CastSpellAction { public: CastChainLightningAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "chain lightning") { } + ActionThreatType getThreatType() override { return ActionThreatType::Aoe; } }; class CastLightningBoltAction : public CastSpellAction @@ -349,6 +350,12 @@ class CastBloodlustAction : public CastBuffSpellAction CastBloodlustAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "bloodlust") { } }; +class CastElementalMasteryAction : public CastBuffSpellAction +{ + public: + CastElementalMasteryAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "elemental mastery") { } +}; + class CastWindShearOnEnemyHealerAction : public CastSpellOnEnemyHealerAction { public: diff --git a/src/strategy/shaman/ShamanAiObjectContext.cpp b/src/strategy/shaman/ShamanAiObjectContext.cpp index c40f4957..cee208c2 100644 --- a/src/strategy/shaman/ShamanAiObjectContext.cpp +++ b/src/strategy/shaman/ShamanAiObjectContext.cpp @@ -79,7 +79,9 @@ class ShamanATriggerFactoryInternal : public NamedObjectContext creators["searing totem"] = &ShamanATriggerFactoryInternal::searing_totem; creators["wind shear"] = &ShamanATriggerFactoryInternal::wind_shear; creators["purge"] = &ShamanATriggerFactoryInternal::purge; - creators["shaman weapon"] = &ShamanATriggerFactoryInternal::shaman_weapon; + //creators["shaman weapon"] = &ShamanATriggerFactoryInternal::shaman_weapon; + creators["main hand weapon no imbue"] = &ShamanATriggerFactoryInternal::main_hand_weapon_no_imbue; + creators["off hand weapon no imbue"] = &ShamanATriggerFactoryInternal::off_hand_weapon_no_imbue; creators["water shield"] = &ShamanATriggerFactoryInternal::water_shield; creators["lightning shield"] = &ShamanATriggerFactoryInternal::lightning_shield; creators["water breathing"] = &ShamanATriggerFactoryInternal::water_breathing; @@ -96,6 +98,7 @@ class ShamanATriggerFactoryInternal : public NamedObjectContext creators["frost shock snare"] = &ShamanATriggerFactoryInternal::frost_shock_snare; creators["heroism"] = &ShamanATriggerFactoryInternal::heroism; creators["bloodlust"] = &ShamanATriggerFactoryInternal::bloodlust; + creators["elemental mastery"] = &ShamanATriggerFactoryInternal::elemental_mastery; creators["wind shear on enemy healer"] = &ShamanATriggerFactoryInternal::wind_shear_on_enemy_healer; creators["cure poison"] = &ShamanATriggerFactoryInternal::cure_poison; creators["party member cure poison"] = &ShamanATriggerFactoryInternal::party_member_cure_poison; @@ -114,6 +117,7 @@ class ShamanATriggerFactoryInternal : public NamedObjectContext static Trigger* maelstrom_weapon(PlayerbotAI* botAI) { return new MaelstromWeaponTrigger(botAI); } static Trigger* heroism(PlayerbotAI* botAI) { return new HeroismTrigger(botAI); } static Trigger* bloodlust(PlayerbotAI* botAI) { return new BloodlustTrigger(botAI); } + static Trigger* elemental_mastery(PlayerbotAI* botAI) { return new ElementalMasteryTrigger(botAI); } static Trigger* party_member_cleanse_disease(PlayerbotAI* botAI) { return new PartyMemberCleanseSpiritDiseaseTrigger(botAI); } static Trigger* party_member_cleanse_curse(PlayerbotAI* botAI) { return new PartyMemberCleanseSpiritCurseTrigger(botAI); } static Trigger* party_member_cleanse_poison(PlayerbotAI* botAI) { return new PartyMemberCleanseSpiritPoisonTrigger(botAI); } @@ -134,7 +138,9 @@ class ShamanATriggerFactoryInternal : public NamedObjectContext static Trigger* searing_totem(PlayerbotAI* botAI) { return new SearingTotemTrigger(botAI); } static Trigger* wind_shear(PlayerbotAI* botAI) { return new WindShearInterruptSpellTrigger(botAI); } static Trigger* purge(PlayerbotAI* botAI) { return new PurgeTrigger(botAI); } - static Trigger* shaman_weapon(PlayerbotAI* botAI) { return new ShamanWeaponTrigger(botAI); } + //static Trigger* shaman_weapon(PlayerbotAI* botAI) { return new ShamanWeaponTrigger(botAI); } + static Trigger* main_hand_weapon_no_imbue(PlayerbotAI* botAI) { return new MainHandWeaponNoImbueTrigger(botAI); } + static Trigger* off_hand_weapon_no_imbue(PlayerbotAI* botAI) { return new OffHandWeaponNoImbueTrigger(botAI); } static Trigger* water_shield(PlayerbotAI* botAI) { return new WaterShieldTrigger(botAI); } static Trigger* lightning_shield(PlayerbotAI* botAI) { return new LightningShieldTrigger(botAI); } static Trigger* shock(PlayerbotAI* botAI) { return new ShockTrigger(botAI); } @@ -206,6 +212,7 @@ class ShamanAiObjectContextInternal : public NamedObjectContext creators["thunderstorm"] = &ShamanAiObjectContextInternal::thunderstorm; creators["heroism"] = &ShamanAiObjectContextInternal::heroism; creators["bloodlust"] = &ShamanAiObjectContextInternal::bloodlust; + creators["elemental mastery"] = &ShamanAiObjectContextInternal::elemental_mastery; // creators["cure disease"] = &ShamanAiObjectContextInternal::cure_disease; // creators["cure disease on party"] = &ShamanAiObjectContextInternal::cure_disease_on_party; // creators["cure poison"] = &ShamanAiObjectContextInternal::cure_poison; @@ -222,6 +229,7 @@ class ShamanAiObjectContextInternal : public NamedObjectContext private: static Action* heroism(PlayerbotAI* botAI) { return new CastHeroismAction(botAI); } static Action* bloodlust(PlayerbotAI* botAI) { return new CastBloodlustAction(botAI); } + static Action* elemental_mastery(PlayerbotAI* botAI) { return new CastElementalMasteryAction(botAI); } static Action* thunderstorm(PlayerbotAI* botAI) { return new CastThunderstormAction(botAI); } static Action* lightning_bolt(PlayerbotAI* botAI) { return new CastLightningBoltAction(botAI); } static Action* chain_lightning(PlayerbotAI* botAI) { return new CastChainLightningAction(botAI); } diff --git a/src/strategy/shaman/ShamanTriggers.cpp b/src/strategy/shaman/ShamanTriggers.cpp index 459b9bc6..d0651378 100644 --- a/src/strategy/shaman/ShamanTriggers.cpp +++ b/src/strategy/shaman/ShamanTriggers.cpp @@ -5,6 +5,7 @@ #include "ShamanTriggers.h" #include "Playerbots.h" +/* std::vector ShamanWeaponTrigger::spells; bool ShamanWeaponTrigger::IsActive() @@ -30,6 +31,21 @@ bool ShamanWeaponTrigger::IsActive() return false; } +*/ + +bool MainHandWeaponNoImbueTrigger::IsActive() { + Item* const itemForSpell = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND ); + if (!itemForSpell || itemForSpell->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT)) + return false; + return true; +} + +bool OffHandWeaponNoImbueTrigger::IsActive() { + Item* const itemForSpell = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND ); + if (!itemForSpell || itemForSpell->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT)) + return false; + return true; +} bool ShockTrigger::IsActive() { diff --git a/src/strategy/shaman/ShamanTriggers.h b/src/strategy/shaman/ShamanTriggers.h index 54395b4b..c4350f79 100644 --- a/src/strategy/shaman/ShamanTriggers.h +++ b/src/strategy/shaman/ShamanTriggers.h @@ -11,6 +11,7 @@ class PlayerbotAI; +/* class ShamanWeaponTrigger : public BuffTrigger { public: @@ -21,6 +22,21 @@ class ShamanWeaponTrigger : public BuffTrigger private: static std::vector spells; }; +*/ + +class MainHandWeaponNoImbueTrigger : public BuffTrigger +{ + public: + MainHandWeaponNoImbueTrigger(PlayerbotAI* ai) : BuffTrigger(ai, "main hand", 1) {} + virtual bool IsActive(); +}; + +class OffHandWeaponNoImbueTrigger : public BuffTrigger +{ + public: + OffHandWeaponNoImbueTrigger(PlayerbotAI* ai) : BuffTrigger(ai, "off hand", 1) {} + virtual bool IsActive(); +}; class TotemTrigger : public Trigger { @@ -201,6 +217,12 @@ class BloodlustTrigger : public BoostTrigger BloodlustTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "bloodlust") { } }; +class ElementalMasteryTrigger : public BoostTrigger +{ + public: + ElementalMasteryTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "elemental mastery") { } +}; + class MaelstromWeaponTrigger : public HasAuraStackTrigger { public: diff --git a/src/strategy/triggers/ChatTriggerContext.h b/src/strategy/triggers/ChatTriggerContext.h index af52cd7e..598154ef 100644 --- a/src/strategy/triggers/ChatTriggerContext.h +++ b/src/strategy/triggers/ChatTriggerContext.h @@ -119,6 +119,7 @@ class ChatTriggerContext : public NamedObjectContext creators["naxx"] = &ChatTriggerContext::naxx; creators["bwl"] = &ChatTriggerContext::bwl; creators["dps"] = &ChatTriggerContext::dps; + creators["disperse"] = &ChatTriggerContext::disperse; } private: @@ -218,6 +219,7 @@ class ChatTriggerContext : public NamedObjectContext static Trigger* naxx(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "naxx"); } static Trigger* bwl(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "bwl"); } static Trigger* dps(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "dps"); } + static Trigger* disperse(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "disperse"); } }; #endif diff --git a/src/strategy/triggers/GenericTriggers.cpp b/src/strategy/triggers/GenericTriggers.cpp index 6d2a9d0a..dff60e0d 100644 --- a/src/strategy/triggers/GenericTriggers.cpp +++ b/src/strategy/triggers/GenericTriggers.cpp @@ -64,6 +64,11 @@ bool AlmostFullManaTrigger::IsActive() return AI_VALUE2(bool, "has mana", "self target") && AI_VALUE2(uint8, "mana", "self target") > 85; } +bool EnoughManaTrigger::IsActive() +{ + return AI_VALUE2(bool, "has mana", "self target") && AI_VALUE2(uint8, "mana", "self target") > 65; +} + bool RageAvailable::IsActive() { return AI_VALUE2(uint8, "rage", "self target") >= amount; diff --git a/src/strategy/triggers/GenericTriggers.h b/src/strategy/triggers/GenericTriggers.h index 2731f9ef..4f5cee27 100644 --- a/src/strategy/triggers/GenericTriggers.h +++ b/src/strategy/triggers/GenericTriggers.h @@ -30,6 +30,14 @@ class HighManaTrigger : public Trigger bool IsActive() override; }; +class EnoughManaTrigger : public Trigger +{ + public: + EnoughManaTrigger(PlayerbotAI* botAI) : Trigger(botAI, "enough mana") { } + + bool IsActive() override; +}; + class AlmostFullManaTrigger : public Trigger { public: diff --git a/src/strategy/triggers/TriggerContext.h b/src/strategy/triggers/TriggerContext.h index 34ef3480..df04b983 100644 --- a/src/strategy/triggers/TriggerContext.h +++ b/src/strategy/triggers/TriggerContext.h @@ -45,6 +45,7 @@ class TriggerContext : public NamedObjectContext creators["medium mana"] = &TriggerContext::MediumMana; creators["high mana"] = &TriggerContext::HighMana; creators["almost full mana"] = &TriggerContext::AlmostFullMana; + creators["enough mana"] = &TriggerContext::EnoughMana; creators["party member critical health"] = &TriggerContext::PartyMemberCriticalHealth; creators["party member low health"] = &TriggerContext::PartyMemberLowHealth; @@ -253,6 +254,7 @@ class TriggerContext : public NamedObjectContext static Trigger* MediumMana(PlayerbotAI* botAI) { return new MediumManaTrigger(botAI); } static Trigger* HighMana(PlayerbotAI* botAI) { return new HighManaTrigger(botAI); } static Trigger* AlmostFullMana(PlayerbotAI* botAI) { return new AlmostFullManaTrigger(botAI); } + static Trigger* EnoughMana(PlayerbotAI* botAI) { return new EnoughManaTrigger(botAI); } static Trigger* LightRageAvailable(PlayerbotAI* botAI) { return new LightRageAvailableTrigger(botAI); } static Trigger* MediumRageAvailable(PlayerbotAI* botAI) { return new MediumRageAvailableTrigger(botAI); } static Trigger* HighRageAvailable(PlayerbotAI* botAI) { return new HighRageAvailableTrigger(botAI); } diff --git a/src/strategy/values/AttackersValue.cpp b/src/strategy/values/AttackersValue.cpp index 527b8def..177ff89f 100644 --- a/src/strategy/values/AttackersValue.cpp +++ b/src/strategy/values/AttackersValue.cpp @@ -47,8 +47,21 @@ GuidVector AttackersValue::Calculate() if (bot->duel && bot->duel->Opponent) result.push_back(bot->duel->Opponent->GetGUID()); - - return result; + + // workaround for bots of same faction not fighting in arena + if (bot->InArena()) + { + GuidVector possibleTargets = AI_VALUE(GuidVector, "possible targets"); + for (ObjectGuid const guid : possibleTargets) + { + Unit* unit = botAI->GetUnit(guid); + if (unit && unit->IsPlayer() && IsValidTarget(unit, bot)) { + result.push_back(unit->GetGUID()); + } + } + } + + return result; } void AttackersValue::AddAttackersOf(Group* group, std::unordered_set& targets) @@ -159,7 +172,7 @@ bool AttackersValue::IsPossibleTarget(Unit* attacker, Player* bot, float range) // (inCannon || !attacker->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) && attacker->CanSeeOrDetect(bot) && // !(attacker->HasUnitState(UNIT_STATE_STUNNED) && botAI->HasAura("shackle undead", attacker)) && !((attacker->IsPolymorphed() || botAI->HasAura("sap", attacker) || /*attacker->IsCharmed() ||*/ attacker->isFeared()) && !rti) && /*!sServerFacade->IsInRoots(attacker) &&*/ - !attacker->IsFriendlyTo(bot) && bot->IsWithinDistInMap(attacker, range) && + !attacker->IsFriendlyTo(bot) && !attacker->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION) && // !(attacker->GetGUID().IsPet() && enemy) && !(attacker->GetCreatureType() == CREATURE_TYPE_CRITTER && !attacker->IsInCombat()) && diff --git a/src/strategy/values/BudgetValues.cpp b/src/strategy/values/BudgetValues.cpp index b1045d1f..d9806087 100644 --- a/src/strategy/values/BudgetValues.cpp +++ b/src/strategy/values/BudgetValues.cpp @@ -180,6 +180,8 @@ uint32 MoneyNeededForValue::Calculate() case NeedMoneyFor::tradeskill: moneyWanted = (level * level * level); //Or level^3 (10s @ lvl10, 3g @ lvl30, 20g @ lvl60, 50g @ lvl80): Todo replace (Should be buyable reagents that combined allow crafting of usefull items) break; + default: + break; } return moneyWanted; diff --git a/src/strategy/values/ItemForSpellValue.cpp b/src/strategy/values/ItemForSpellValue.cpp index e52bbbf6..58c4fae1 100644 --- a/src/strategy/values/ItemForSpellValue.cpp +++ b/src/strategy/values/ItemForSpellValue.cpp @@ -8,8 +8,8 @@ #ifndef WIN32 inline int strcmpi(char const* s1, char const* s2) { - for (; *s1 && *s2 && (toupper(*s1) == toupper(*s2)); ++s1, ++s2); - return *s1 - *s2; + for (; *s1 && *s2 && (toupper(*s1) == toupper(*s2)); ++s1, ++s2) {} + return *s1 - *s2; } #endif diff --git a/src/strategy/values/PartyMemberToDispel.h b/src/strategy/values/PartyMemberToDispel.h index 36197ea9..ac2c441f 100644 --- a/src/strategy/values/PartyMemberToDispel.h +++ b/src/strategy/values/PartyMemberToDispel.h @@ -14,7 +14,7 @@ class Unit; class PartyMemberToDispel : public PartyMemberValue, public Qualified { public: - PartyMemberToDispel(PlayerbotAI* botAI, std::string const name = "party member to dispel") : PartyMemberValue(botAI, name, 2 * 1000), Qualified() { } + PartyMemberToDispel(PlayerbotAI* botAI, std::string const name = "party member to dispel") : PartyMemberValue(botAI, name, 1000), Qualified() { } protected: Unit* Calculate() override; diff --git a/src/strategy/values/PartyMemberToHeal.cpp b/src/strategy/values/PartyMemberToHeal.cpp index 0f76b6e7..a7cba48e 100644 --- a/src/strategy/values/PartyMemberToHeal.cpp +++ b/src/strategy/values/PartyMemberToHeal.cpp @@ -74,7 +74,7 @@ bool PartyMemberToHeal::Check(Unit* player) { // return player && player != bot && player->GetMapId() == bot->GetMapId() && player->IsInWorld() && // sServerFacade->GetDistance2d(bot, player) < (player->IsPlayer() && botAI->IsTank((Player*)player) ? 50.0f : 40.0f); - return player->GetMapId() == bot->GetMapId() && + return player->GetMapId() == bot->GetMapId() && !player->IsCharmed() && bot->GetDistance2d(player) < sPlayerbotAIConfig->healDistance * 2 && bot->IsWithinLOS(player->GetPositionX(), player->GetPositionY(), player->GetPositionZ()); } diff --git a/src/strategy/values/ValueContext.h b/src/strategy/values/ValueContext.h index 4298b44e..2888b246 100644 --- a/src/strategy/values/ValueContext.h +++ b/src/strategy/values/ValueContext.h @@ -301,6 +301,10 @@ class ValueContext : public NamedObjectContext creators["expected group dps"] = &ValueContext::expected_group_dps; creators["area debuff"] = &ValueContext::area_debuff; creators["nearest trap with damage"] = &ValueContext::nearest_trap_with_damange; + creators["disperse distance"] = &ValueContext::disperse_distance; + creators["last flee angle"] = &ValueContext::last_flee_angle; + creators["last flee timestamp"] = &ValueContext::last_flee_timestamp; + creators["recently flee info"] = &ValueContext::recently_flee_info; } private: @@ -505,6 +509,10 @@ class ValueContext : public NamedObjectContext static UntypedValue* expected_group_dps(PlayerbotAI* ai) { return new ExpectedGroupDpsValue(ai); } static UntypedValue* area_debuff(PlayerbotAI* ai) { return new AreaDebuffValue(ai); } static UntypedValue* nearest_trap_with_damange(PlayerbotAI* ai) { return new NearestTrapWithDamageValue(ai); } + static UntypedValue* disperse_distance(PlayerbotAI* ai) { return new DisperseDistanceValue(ai); } + static UntypedValue* last_flee_angle(PlayerbotAI* ai) { return new LastFleeAngleValue(ai); } + static UntypedValue* last_flee_timestamp(PlayerbotAI* ai) { return new LastFleeTimestampValue(ai); } + static UntypedValue* recently_flee_info(PlayerbotAI* ai) { return new RecentlyFleeInfo(ai); } }; #endif diff --git a/src/strategy/warlock/DpsWarlockStrategy.cpp b/src/strategy/warlock/DpsWarlockStrategy.cpp index f5247c3f..3e79dfa8 100644 --- a/src/strategy/warlock/DpsWarlockStrategy.cpp +++ b/src/strategy/warlock/DpsWarlockStrategy.cpp @@ -87,7 +87,10 @@ void DpsWarlockStrategy::InitTriggers(std::vector& triggers) void DpsAoeWarlockStrategy::InitTriggers(std::vector& triggers) { - triggers.push_back(new TriggerNode("medium aoe", NextAction::array(0, new NextAction("rain of fire", 37.0f), nullptr))); + triggers.push_back(new TriggerNode("medium aoe", NextAction::array(0, + new NextAction("seed of corruption", 39.0f), + new NextAction("seed of corruption on attacker", 38.0f), + new NextAction("rain of fire", 37.0f), nullptr))); triggers.push_back(new TriggerNode("corruption on attacker", NextAction::array(0, new NextAction("corruption on attacker", 27.0f), nullptr))); triggers.push_back(new TriggerNode("unstable affliction on attacker", NextAction::array(0, new NextAction("unstable affliction on attacker", 26.0f), NULL))); triggers.push_back(new TriggerNode("curse of agony on attacker", NextAction::array(0, new NextAction("curse of agony on attacker", 25.0f), nullptr))); diff --git a/src/strategy/warlock/WarlockActions.h b/src/strategy/warlock/WarlockActions.h index 0ec8cfbb..28f689d3 100644 --- a/src/strategy/warlock/WarlockActions.h +++ b/src/strategy/warlock/WarlockActions.h @@ -67,12 +67,18 @@ class CastCorruptionAction : public CastDebuffSpellAction { public: CastCorruptionAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "corruption", true) { } + bool isUseful() override { + return CastDebuffSpellAction::isUseful() && !botAI->HasAura("seed of corruption", GetTarget(), false, true) ; + } }; class CastCorruptionOnAttackerAction : public CastDebuffSpellOnAttackerAction { public: CastCorruptionOnAttackerAction(PlayerbotAI* botAI) : CastDebuffSpellOnAttackerAction(botAI, "corruption", true) { } + bool isUseful() override { + return CastDebuffSpellOnAttackerAction::isUseful() && !botAI->HasAura("seed of corruption", GetTarget(), false, true) ; + } }; class CastCurseOfAgonyOnAttackerAction : public CastDebuffSpellOnAttackerAction @@ -142,6 +148,20 @@ class CastSeedOfCorruptionAction : public CastDebuffSpellAction { public: CastSeedOfCorruptionAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "seed of corruption", true, 0) { } + bool isUseful() override { + return CastDebuffSpellAction::isUseful() && !botAI->HasAura("corruption", GetTarget(), false, true) ; + } + ActionThreatType getThreatType() override { return ActionThreatType::Aoe; } +}; + +class CastSeedOfCorruptionOnAttackerAction : public CastDebuffSpellOnAttackerAction +{ + public: + CastSeedOfCorruptionOnAttackerAction(PlayerbotAI* botAI) : CastDebuffSpellOnAttackerAction(botAI, "seed of corruption", true, 0) { } + bool isUseful() override { + return CastDebuffSpellOnAttackerAction::isUseful() && !botAI->HasAura("corruption", GetTarget(), false, true) ; + } + ActionThreatType getThreatType() override { return ActionThreatType::Aoe; } }; class CastRainOfFireAction : public CastSpellAction diff --git a/src/strategy/warlock/WarlockAiObjectContext.cpp b/src/strategy/warlock/WarlockAiObjectContext.cpp index 52e3a414..486c2cc2 100644 --- a/src/strategy/warlock/WarlockAiObjectContext.cpp +++ b/src/strategy/warlock/WarlockAiObjectContext.cpp @@ -158,6 +158,7 @@ class WarlockAiObjectContextInternal : public NamedObjectContext creators["banish"] = &WarlockAiObjectContextInternal::banish; creators["banish on cc"] = &WarlockAiObjectContextInternal::banish_on_cc; creators["seed of corruption"] = &WarlockAiObjectContextInternal::seed_of_corruption; + creators["seed of corruption on attacker"] = &WarlockAiObjectContextInternal::seed_of_corruption_on_attacker; creators["rain of fire"] = &WarlockAiObjectContextInternal::rain_of_fire; creators["shadowfury"] = &WarlockAiObjectContextInternal::shadowfury; creators["life tap"] = &WarlockAiObjectContextInternal::life_tap; @@ -209,6 +210,7 @@ class WarlockAiObjectContextInternal : public NamedObjectContext static Action* banish(PlayerbotAI* botAI) { return new CastBanishAction(botAI); } static Action* banish_on_cc(PlayerbotAI* botAI) { return new CastBanishAction(botAI); } static Action* seed_of_corruption(PlayerbotAI* botAI) { return new CastSeedOfCorruptionAction(botAI); } + static Action* seed_of_corruption_on_attacker(PlayerbotAI* botAI) { return new CastSeedOfCorruptionOnAttackerAction(botAI); } static Action* rain_of_fire(PlayerbotAI* botAI) { return new CastRainOfFireAction(botAI); } static Action* shadowfury(PlayerbotAI* botAI) { return new CastShadowfuryAction(botAI); } static Action* life_tap(PlayerbotAI* botAI) { return new CastLifeTapAction(botAI); } diff --git a/src/strategy/warlock/WarlockTriggers.h b/src/strategy/warlock/WarlockTriggers.h index 8578e43a..42c773f7 100644 --- a/src/strategy/warlock/WarlockTriggers.h +++ b/src/strategy/warlock/WarlockTriggers.h @@ -6,6 +6,7 @@ #define _PLAYERBOT_WARLOCKTRIGGERS_H #include "GenericTriggers.h" +#include "PlayerbotAI.h" class PlayerbotAI; @@ -32,13 +33,24 @@ class CurseOfAgonyTrigger : public DebuffTrigger CurseOfAgonyTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "curse of agony", 1, true, 20.0f) { } }; -DEBUFF_CHECKISOWNER_TRIGGER(CorruptionTrigger, "corruption"); +class CorruptionTrigger : public DebuffTrigger +{ + public: + CorruptionTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "corruption", 1, true) { } \ + bool IsActive() override { + return DebuffTrigger::IsActive() && !botAI->HasAura("seed of corruption", GetTarget(), false, true) ; + } +}; + DEBUFF_CHECKISOWNER_TRIGGER(SiphonLifeTrigger, "siphon life"); class CorruptionOnAttackerTrigger : public DebuffOnAttackerTrigger { public: CorruptionOnAttackerTrigger(PlayerbotAI* botAI) : DebuffOnAttackerTrigger(botAI, "corruption", true) { } + bool IsActive() override { + return DebuffOnAttackerTrigger::IsActive() && !botAI->HasAura("seed of corruption", GetTarget(), false, true) ; + } }; class CastCurseOfAgonyOnAttackerTrigger : public DebuffOnAttackerTrigger diff --git a/src/strategy/warrior/ArmsWarriorStrategy.cpp b/src/strategy/warrior/ArmsWarriorStrategy.cpp index c6314bae..ef27b800 100644 --- a/src/strategy/warrior/ArmsWarriorStrategy.cpp +++ b/src/strategy/warrior/ArmsWarriorStrategy.cpp @@ -32,7 +32,9 @@ ArmsWarriorStrategy::ArmsWarriorStrategy(PlayerbotAI* botAI) : GenericWarriorStr NextAction** ArmsWarriorStrategy::getDefaultActions() { - return NextAction::array(0, new NextAction("heroic strike", ACTION_DEFAULT), nullptr); + return NextAction::array(0, + new NextAction("bladestorm", ACTION_DEFAULT + 0.1f), + new NextAction("melee", ACTION_DEFAULT), nullptr); } void ArmsWarriorStrategy::InitTriggers(std::vector& triggers) @@ -56,4 +58,5 @@ void ArmsWarriorStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode("rend", NextAction::array(0, new NextAction("rend", ACTION_HIGH + 5), nullptr))); triggers.push_back(new TriggerNode("rend on attacker", NextAction::array(0, new NextAction("rend on attacker", ACTION_HIGH + 5), nullptr))); triggers.push_back(new TriggerNode("critical health", NextAction::array(0, new NextAction("intimidating shout", ACTION_EMERGENCY), nullptr))); + triggers.push_back(new TriggerNode("medium rage available", NextAction::array(0, new NextAction("thunder clap", ACTION_HIGH + 1), nullptr))); } diff --git a/src/strategy/warrior/FuryWarriorStrategy.cpp b/src/strategy/warrior/FuryWarriorStrategy.cpp index 60cece7b..57cf118e 100644 --- a/src/strategy/warrior/FuryWarriorStrategy.cpp +++ b/src/strategy/warrior/FuryWarriorStrategy.cpp @@ -61,7 +61,7 @@ void FuryWarriorStrategy::InitTriggers(std::vector &triggers) triggers.push_back(new TriggerNode("bloodthirst", NextAction::array(0, new NextAction("bloodthirst", ACTION_HIGH + 7), nullptr))); triggers.push_back(new TriggerNode("instant slam", NextAction::array(0, new NextAction("slam", ACTION_HIGH + 5), nullptr))); triggers.push_back(new TriggerNode("bloodrage", NextAction::array(0, new NextAction("bloodrage", ACTION_HIGH + 2), nullptr))); - triggers.push_back(new TriggerNode("high rage available", NextAction::array(0, new NextAction("heroic strike", ACTION_HIGH + 1), NULL))); + triggers.push_back(new TriggerNode("medium rage available", NextAction::array(0, new NextAction("heroic strike", ACTION_HIGH + 1), NULL))); // triggers.push_back(new TriggerNode("berserker rage", NextAction::array(0, new NextAction("berserker rage", ACTION_HIGH + 2), nullptr))); // triggers.push_back(new TriggerNode("light aoe", NextAction::array(0, // new NextAction("whirlwind", ACTION_HIGH + 2), diff --git a/src/strategy/warrior/TankWarriorStrategy.cpp b/src/strategy/warrior/TankWarriorStrategy.cpp index 42f71fa2..bbdfb740 100644 --- a/src/strategy/warrior/TankWarriorStrategy.cpp +++ b/src/strategy/warrior/TankWarriorStrategy.cpp @@ -88,4 +88,6 @@ void TankWarriorStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode("rend", NextAction::array(0, new NextAction("rend", ACTION_NORMAL + 1), nullptr))); triggers.push_back(new TriggerNode("rend on attacker", NextAction::array(0, new NextAction("rend on attacker", ACTION_NORMAL + 1), nullptr))); triggers.push_back(new TriggerNode("protect party member", NextAction::array(0, new NextAction("intervene", ACTION_EMERGENCY), nullptr))); + triggers.push_back(new TriggerNode("high rage available", NextAction::array(0, new NextAction("heroic strike", ACTION_HIGH + 1), nullptr))); + triggers.push_back(new TriggerNode("medium rage available", NextAction::array(0, new NextAction("thunder clap", ACTION_HIGH + 1), nullptr))); } diff --git a/src/strategy/warrior/WarriorAiObjectContext.cpp b/src/strategy/warrior/WarriorAiObjectContext.cpp index 90917300..1cabf441 100644 --- a/src/strategy/warrior/WarriorAiObjectContext.cpp +++ b/src/strategy/warrior/WarriorAiObjectContext.cpp @@ -117,7 +117,7 @@ class WarriorTriggerFactoryInternal : public NamedObjectContext static Trigger* SwordAndBoard(PlayerbotAI* botAI) { return new SwordAndBoardTrigger(botAI); } static Trigger* shield_bash_on_enemy_healer(PlayerbotAI* botAI) { return new ShieldBashInterruptEnemyHealerSpellTrigger(botAI); } - static Trigger* thunderclap_and_rage(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "thunderclap", "light rage available"); } + static Trigger* thunderclap_and_rage(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "thunder clap", "light rage available"); } static Trigger* intercept_can_cast(PlayerbotAI* botAI) { return new InterceptCanCastTrigger(botAI); } static Trigger* intercept_and_far_enemy(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "enemy is out of melee", "intercept can cast"); } static Trigger* intercept_and_rage(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "intercept and far enemy", "light rage available"); }