mirror of
https://github.com/mod-playerbots/mod-playerbots.git
synced 2026-02-16 00:26:10 +00:00
Compare commits
2 Commits
6cf7f1aaef
...
master_v16
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6620def962 | ||
|
|
cddd406b1c |
6
.github/workflows/windows_build.yml
vendored
6
.github/workflows/windows_build.yml
vendored
@@ -25,25 +25,21 @@ jobs:
|
||||
with:
|
||||
repository: 'mod-playerbots/azerothcore-wotlk'
|
||||
ref: 'Playerbot'
|
||||
path: 'ac'
|
||||
- name: Checkout Playerbot Module
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: 'mod-playerbots/mod-playerbots'
|
||||
#path: 'modules/mod-playerbots'
|
||||
path: ac/modules/mod-playerbots
|
||||
path: 'modules/mod-playerbots'
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2.13
|
||||
- name: Configure OS
|
||||
shell: bash
|
||||
working-directory: ac
|
||||
env:
|
||||
CONTINUOUS_INTEGRATION: true
|
||||
run: |
|
||||
./acore.sh install-deps
|
||||
- name: Build
|
||||
shell: bash
|
||||
working-directory: ac
|
||||
run: |
|
||||
export CTOOLS_BUILD=all
|
||||
./acore.sh compiler build
|
||||
|
||||
@@ -21,11 +21,10 @@
|
||||
# THRESHOLDS
|
||||
# QUESTS
|
||||
# COMBAT
|
||||
# GREATER BUFFS STRATEGIES
|
||||
# PALADIN BUFFS STRATEGIES
|
||||
# CHEATS
|
||||
# SPELLS
|
||||
# FLIGHTPATH
|
||||
# PROFESSIONS
|
||||
# RANDOMBOT-SPECIFIC SETTINGS
|
||||
# GENERAL
|
||||
# LEVELS
|
||||
@@ -45,7 +44,7 @@
|
||||
# HUNTER
|
||||
# ROGUE
|
||||
# PRIEST
|
||||
# DEATH KNIGHT
|
||||
# DEATHKNIGHT
|
||||
# SHAMAN
|
||||
# MAGE
|
||||
# WARLOCK
|
||||
@@ -56,7 +55,7 @@
|
||||
# HUNTER
|
||||
# ROGUE
|
||||
# PRIEST
|
||||
# DEATH KNIGHT
|
||||
# DEATHKNIGHT
|
||||
# SHAMAN
|
||||
# MAGE
|
||||
# WARLOCK
|
||||
@@ -91,20 +90,17 @@ AiPlayerbot.MinRandomBots = 500
|
||||
AiPlayerbot.MaxRandomBots = 500
|
||||
|
||||
# Randombot accounts
|
||||
# If you are not using any expansion at all, you may have to set this manually, in which case please
|
||||
# ensure that RandomBotAccountCount is at least greater than (MaxRandomBots / 10 + AddClassAccountPoolSize)
|
||||
# If you are not using any expansion at all, you may have to set this manually, in which case please ensure that RandomBotAccountCount is at least greater than (MaxRandomBots / 10 + AddClassAccountPoolSize)
|
||||
# Default: 0 (automatic)
|
||||
AiPlayerbot.RandomBotAccountCount = 0
|
||||
|
||||
# Delete all randombot accounts
|
||||
# To apply this, set the number to 1 and run the Worldserver. Once deletion is complete, if you would
|
||||
# like to recreate randombots, set the number back to 0 and rerun the Worldserver.
|
||||
# To apply this, set the number to 1 and run the Worldserver. Once deletion is complete, if you would like to recreate randombots, set the number back to 0 and rerun the Worldserver.
|
||||
AiPlayerbot.DeleteRandomBotAccounts = 0
|
||||
|
||||
# Disable randombots when no real players are logged in
|
||||
# Default: 0 (randombots will login when server starts)
|
||||
# If enabled, randombots will only log in 30 seconds (default) after a real player logs in, and will
|
||||
# log out 300 seconds (default) after all real players log out
|
||||
# If enabled, randombots will only log in 30 seconds (default) after a real player logs in, and will log out 300 seconds (default) after all real players log out
|
||||
AiPlayerbot.DisabledWithoutRealPlayer = 0
|
||||
AiPlayerbot.DisabledWithoutRealPlayerLoginDelay = 30
|
||||
AiPlayerbot.DisabledWithoutRealPlayerLogoutDelay = 300
|
||||
@@ -156,8 +152,7 @@ AiPlayerbot.AllowGuildBots = 1
|
||||
AiPlayerbot.AllowTrustedAccountBots = 1
|
||||
|
||||
# Randombots will create guilds with nearby randombots
|
||||
# Note: currently, randombots will not invite more bots after a guild is created,
|
||||
# meaning randombot guilds will have only the 10 initial randombots needed to sign the charter
|
||||
# Note: currently, randombots will not invite more bots after a guild is created (i.e., randombot guilds will have only the 10 initial randombots needed to sign the charter)
|
||||
# Default: 0 (disabled)
|
||||
AiPlayerbot.RandomBotGuildNearby = 0
|
||||
|
||||
@@ -191,8 +186,7 @@ AiPlayerbot.AutoInitOnly = 0
|
||||
# Default: 1.0 (same with the player)
|
||||
AiPlayerbot.AutoInitEquipLevelLimitRatio = 1.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
|
||||
# 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
|
||||
|
||||
#
|
||||
@@ -269,7 +263,7 @@ AiPlayerbot.UseFastFlyMountAtMinLevel = 70
|
||||
AiPlayerbot.RandomBotShowHelmet = 1
|
||||
AiPlayerbot.RandomBotShowCloak = 1
|
||||
|
||||
# Randombots and altbots automatically equip any items in their inventory that are sufficient upgrades
|
||||
# Randombots and altbots automatically equip upgrades (bots will equip any item obtained from looting or a quest if they are sufficient upgrades)
|
||||
# Default: 1 (enabled)
|
||||
AiPlayerbot.AutoEquipUpgradeLoot = 1
|
||||
|
||||
@@ -317,8 +311,7 @@ AiPlayerbot.GlobalCooldown = 500
|
||||
# Max wait time when moving
|
||||
AiPlayerbot.MaxWaitForMove = 5000
|
||||
|
||||
# Enable/disable use of MoveSplinePath for bot movement
|
||||
# Disabling will result in more erratic movement but is required for stuns, snares, and roots to work on bots
|
||||
# Disable 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
|
||||
@@ -412,11 +405,10 @@ AiPlayerbot.HighMana = 65
|
||||
#
|
||||
#
|
||||
|
||||
# Bots pick their quest rewards
|
||||
# yes = picks the most useful item, no = list all rewards, ask = pick useful item and lists if multiple
|
||||
# Bots pick their quest rewards (yes = picks the most useful item, no = list all rewards, ask = pick useful item and lists if multiple)
|
||||
AiPlayerbot.AutoPickReward = yes
|
||||
|
||||
# Sync quests with player (bots will complete quests the moment you hand them in and will not loot quest items.)
|
||||
# Sync quests with player (Bots will complete quests the moment you hand them in and will not loot quest items.)
|
||||
# Default: 1 (enabled)
|
||||
AiPlayerbot.SyncQuestWithPlayer = 1
|
||||
|
||||
@@ -441,7 +433,7 @@ AiPlayerbot.DropObsoleteQuests = 1
|
||||
# Auto add dungeon/raid strategies when entering the instance if implemented
|
||||
AiPlayerbot.ApplyInstanceStrategies = 1
|
||||
|
||||
# Enable auto avoid aoe strategy
|
||||
# Enable auto avoid aoe strategy (experimental)
|
||||
# Default: 1 (enabled)
|
||||
AiPlayerbot.AutoAvoidAoe = 1
|
||||
|
||||
@@ -468,7 +460,7 @@ AiPlayerbot.FleeingEnabled = 1
|
||||
####################################################################################################
|
||||
|
||||
####################################################################################################
|
||||
# GREATER BUFFS STRATEGIES
|
||||
# PALADIN BUFFS STRATEGIES
|
||||
#
|
||||
#
|
||||
|
||||
@@ -491,13 +483,12 @@ AiPlayerbot.RPWarningCooldown = 30
|
||||
#
|
||||
|
||||
# Enable/Disable maintenance command
|
||||
# Learn all available spells and skills, assign talent points, refresh consumables, repair, enchant equipment, socket gems, etc.
|
||||
# Applies if bot's level is above AiPlayerbot.MinEnchantingBotLevel
|
||||
# Learn all available spells and skills, refresh consumables, repair, enchant equipment and socket gems if bot's level is above AiPlayerbot.MinEnchantingBotLevel
|
||||
# Default: 1 (enabled)
|
||||
AiPlayerbot.MaintenanceCommand = 1
|
||||
|
||||
# Enable/Disable specific maintenance command functionality for alt bots
|
||||
# Disable to prevent players from giving free bags, spells, skill levels, etc. to their alt bots
|
||||
# Disable to prevent players from giving free bags, spells, skill levels etc to their alt bots
|
||||
# Default: 1 (enabled)
|
||||
AiPlayerbot.AltMaintenanceAmmo = 1
|
||||
AiPlayerbot.AltMaintenanceFood = 1
|
||||
@@ -509,7 +500,6 @@ AiPlayerbot.AltMaintenanceBags = 1
|
||||
AiPlayerbot.AltMaintenanceMounts = 1
|
||||
AiPlayerbot.AltMaintenanceSkills = 1
|
||||
|
||||
# "Special Spells" consist of any spells listed in AiPlayerbot.RandomBotSpellIds and Death Gate for Death Knights
|
||||
AiPlayerbot.AltMaintenanceClassSpells = 1
|
||||
AiPlayerbot.AltMaintenanceAvailableSpells = 1
|
||||
AiPlayerbot.AltMaintenanceSpecialSpells = 1
|
||||
@@ -524,8 +514,8 @@ AiPlayerbot.AltMaintenanceReputation = 1
|
||||
AiPlayerbot.AltMaintenanceAttunementQuests = 1
|
||||
AiPlayerbot.AltMaintenanceKeyring = 1
|
||||
|
||||
# Enable/Disable autogear command, which automatically upgrades bots' gear
|
||||
# The quality is limited by AutoGearQualityLimit and AutoGearScoreLimit
|
||||
|
||||
# Enable/Disable autogear command, which automatically upgrades bots' gear; the quality is limited by AutoGearQualityLimit and AutoGearScoreLimit
|
||||
# Default: 1 (enabled)
|
||||
AiPlayerbot.AutoGearCommand = 1
|
||||
|
||||
@@ -589,30 +579,6 @@ AiPlayerbot.BotTaxiGapJitterMs = 100
|
||||
#
|
||||
####################################################################################################
|
||||
|
||||
####################################################################################################
|
||||
# PROFESSIONS
|
||||
# Note: Random bots currently do not get professions
|
||||
#
|
||||
|
||||
# Automatically adds the 'master fishing' strategy to bots that have the fishing skill when the bots master fishes.
|
||||
# Default: 1 (Enabled)
|
||||
AiPlayerbot.EnableFishingWithMaster = 1
|
||||
|
||||
# Distance from itself (in yards) that a bot with a master will search for water to fish
|
||||
AiPlayerbot.FishingDistanceFromMaster = 10.0
|
||||
|
||||
# Distance from itself (in yards) that a bot without a master will search for water to fish
|
||||
# Currently not relevant since masterless bots will not fish
|
||||
AiPlayerbot.FishingDistance = 40.0
|
||||
|
||||
# Distance from water (in yards) beyond which a bot will remove the 'master fishing' strategy
|
||||
AiPlayerbot.EndFishingWithMaster = 30.0
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
####################################################################################################
|
||||
|
||||
#######################################
|
||||
# #
|
||||
# RANDOMBOT-SPECIFIC SETTINGS #
|
||||
@@ -664,7 +630,7 @@ AiPlayerbot.RandomBotHordeRatio = 50
|
||||
AiPlayerbot.DisableDeathKnightLogin = 0
|
||||
|
||||
# Enable simulated expansion limitation for talents and glyphs
|
||||
# If enabled, limits talent trees to 5 rows plus the middle talent of the 6th row for bots until level 61
|
||||
# If enabled, limits talent trees to 5 rows plus the middle talent of the 6th row for bots until level 61
|
||||
# and 7 rows plus the middle talent of the 8th row for bots from level 61 until level 71
|
||||
# Default: 0 (disabled)
|
||||
AiPlayerbot.LimitTalentsExpansion = 0
|
||||
@@ -734,7 +700,7 @@ AiPlayerbot.RandomGearQualityLimit = 3
|
||||
# Max iLVL Phase 1(MC, Ony, ZG) = 78 | Phase 2(BWL) = 83 | Phase 2.5(AQ40) = 88 | Phase 3(Naxx40) = 92
|
||||
# TBC
|
||||
# Max iLVL Tier 4 = 120 | Tier 5 = 133 | Tier 6 = 164
|
||||
# Max iLVL Phase 1(Kara, Gruul, Mag) = 125 | Phase 2(SSC, TK, ZA) = 141 | Phase 3(Hyjal, BT) = 156 | Phase 4(Sunwell) = 164
|
||||
# Max iLVL Phase 1(Kara, Gruul, Mag) = 125 | Phase 1.5(ZA) = 138 | Phase 2(SC, TK) = 141 | Phase 3(Hyjal, BT) = 156 | Phase 4(Sunwell) = 164
|
||||
# Wotlk
|
||||
# Max iLVL Tier 7(10/25) = 200/213 | Tier 8(10/25) = 225/232 | Tier 9(10/25) = 232/245 | Tier 10(10/25/HC) = 251/264/290
|
||||
# Max iLVL Phase 1(Naxx) = 224 | Phase 2(Ulduar) = 245 | Phase 3(ToC) = 258 | Phase 4(ICC) = 290
|
||||
@@ -745,8 +711,7 @@ AiPlayerbot.RandomGearScoreLimit = 0
|
||||
# Default: 1 (enabled)
|
||||
AiPlayerbot.IncrementalGearInit = 1
|
||||
|
||||
# Set minimum level of bots that will enchant and socket gems into their equipment with maintenance
|
||||
# If greater than RandomBotMaxlevel, bots will not automatically enchant equipment or socket gems
|
||||
# Set minimum level of bots that will enchant their equipment (if greater than RandomBotMaxlevel, bots will not enchant equipment)
|
||||
# Default: 60
|
||||
AiPlayerbot.MinEnchantingBotLevel = 60
|
||||
|
||||
@@ -898,15 +863,13 @@ AiPlayerbot.OpenGoSpell = 6477
|
||||
#
|
||||
|
||||
# Additional randombot strategies
|
||||
# Strategies added here are applied to all randombots, in addition (or subtraction) to spec/role-based default strategies.
|
||||
# These rules are processed after the defaults.
|
||||
# Strategies added here are applied to all randombots, in addition (or subtraction) to spec/role-based default strategies. These rules are processed after the defaults.
|
||||
# Example: "+threat,-potions"
|
||||
AiPlayerbot.RandomBotCombatStrategies = ""
|
||||
AiPlayerbot.RandomBotNonCombatStrategies = ""
|
||||
|
||||
# Additional altbot strategies
|
||||
# Strategies added here are applied to all altbots, in addition (or subtraction) to spec/role-based default strategies.
|
||||
# These rules are processed after the defaults.
|
||||
# Strategies added here are applied to all altbots, in addition (or subtraction) to spec/role-based default strategies. These rules are processed after the defaults.
|
||||
AiPlayerbot.CombatStrategies = ""
|
||||
AiPlayerbot.NonCombatStrategies = ""
|
||||
|
||||
@@ -1222,7 +1185,7 @@ AiPlayerbot.DeleteRandomBotArenaTeams = 0
|
||||
AiPlayerbot.PvpProhibitedZoneIds = "2255,656,2361,2362,2363,976,35,2268,3425,392,541,1446,3828,3712,3738,3565,3539,3623,4152,3988,4658,4284,4418,4436,4275,4323,4395,3703,4298,3951"
|
||||
|
||||
# PvP Restricted Areas (bots don't pvp)
|
||||
AiPlayerbot.PvpProhibitedAreaIds = "976,35,392,2268,4161,4010,4317,4312,3649,3887,3958,3724,4080,3938,3754,3786,3973"
|
||||
AiPlayerbot.PvpProhibitedAreaIds = "976,35,392,2268,4161,4010,4317,4312,3649,3887,3958,3724,4080,3938,3754"
|
||||
|
||||
# Improve reaction speeds in battlegrounds and arenas (may cause lag)
|
||||
AiPlayerbot.FastReactInBG = 1
|
||||
@@ -1492,7 +1455,7 @@ AiPlayerbot.PremadeSpecLink.5.5.80 = 50332031003--005323241223112003102311351
|
||||
####################################################################################################
|
||||
|
||||
####################################################################################################
|
||||
# DEATH KNIGHT
|
||||
# DEATHKNIGHT
|
||||
#
|
||||
#
|
||||
|
||||
@@ -1815,7 +1778,7 @@ AiPlayerbot.RandomClassSpecIndex.5.2 = 2
|
||||
####################################################################################################
|
||||
|
||||
####################################################################################################
|
||||
# DEATH KNIGHT
|
||||
# DEATHKNIGHT
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
@@ -1,255 +0,0 @@
|
||||
DELETE FROM ai_playerbot_texts WHERE name IN (
|
||||
'pet_usage_error',
|
||||
'pet_no_pet_error',
|
||||
'pet_stance_report',
|
||||
'pet_no_target_error',
|
||||
'pet_target_dead_error',
|
||||
'pet_invalid_target_error',
|
||||
'pet_pvp_prohibited_error',
|
||||
'pet_attack_success',
|
||||
'pet_attack_failed',
|
||||
'pet_follow_success',
|
||||
'pet_stay_success',
|
||||
'pet_unknown_command_error',
|
||||
'pet_stance_set_success',
|
||||
'pet_type_pet',
|
||||
'pet_type_guardian',
|
||||
'pet_stance_aggressive',
|
||||
'pet_stance_defensive',
|
||||
'pet_stance_passive',
|
||||
'pet_stance_unknown'
|
||||
);
|
||||
|
||||
DELETE FROM ai_playerbot_texts_chance WHERE name IN (
|
||||
'pet_usage_error',
|
||||
'pet_no_pet_error',
|
||||
'pet_stance_report',
|
||||
'pet_no_target_error',
|
||||
'pet_target_dead_error',
|
||||
'pet_invalid_target_error',
|
||||
'pet_pvp_prohibited_error',
|
||||
'pet_attack_success',
|
||||
'pet_attack_failed',
|
||||
'pet_follow_success',
|
||||
'pet_stay_success',
|
||||
'pet_unknown_command_error',
|
||||
'pet_stance_set_success',
|
||||
'pet_type_pet',
|
||||
'pet_type_guardian',
|
||||
'pet_stance_aggressive',
|
||||
'pet_stance_defensive',
|
||||
'pet_stance_passive',
|
||||
'pet_stance_unknown'
|
||||
);
|
||||
|
||||
INSERT INTO ai_playerbot_texts (id, name, text, say_type, reply_type, text_loc1, text_loc2, text_loc3, text_loc4, text_loc5, text_loc6, text_loc7, text_loc8) VALUES
|
||||
(1717, 'pet_usage_error', "Usage: pet <aggressive|defensive|passive|stance|attack|follow|stay>", 0, 0,
|
||||
"사용법: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
|
||||
"Utilisation: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
|
||||
"Verwendung: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
|
||||
"用法: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
|
||||
"用法: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
|
||||
"Uso: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
|
||||
"Uso: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
|
||||
"Использование: pet <aggressive|defensive|passive|stance|attack|follow|stay>"),
|
||||
|
||||
(1718, 'pet_no_pet_error', "You have no pet or guardian pet.", 0, 0,
|
||||
"펫이나 수호자 펫이 없습니다.",
|
||||
"Vous n'avez pas de familier ou gardien.",
|
||||
"Du hast kein Tier oder Wächter.",
|
||||
"你没有宠物或守护者宠物。",
|
||||
"你沒有寵物或守護者寵物。",
|
||||
"No tienes mascota o mascota guardián.",
|
||||
"No tienes mascota o mascota guardián.",
|
||||
"У вас нет питомца или защитника."),
|
||||
|
||||
(1719, 'pet_stance_report', "Current stance of %type \"%name\": %stance.", 0, 0,
|
||||
"%type \"%name\"의 현재 태세: %stance.",
|
||||
"Position actuelle du %type \"%name\": %stance.",
|
||||
"Aktuelle Haltung des %type \"%name\": %stance.",
|
||||
"%type \"%name\" 的当前姿态: %stance。",
|
||||
"%type \"%name\" 的當前姿態: %stance。",
|
||||
"Postura actual del %type \"%name\": %stance.",
|
||||
"Postura actual del %type \"%name\": %stance.",
|
||||
"Текущая позиция %type \"%name\": %stance."),
|
||||
|
||||
(1720, 'pet_no_target_error', "No valid target selected by master.", 0, 0,
|
||||
"주인이 유효한 대상을 선택하지 않았습니다.",
|
||||
"Aucune cible valide sélectionnée par le maître.",
|
||||
"Kein gültiges Ziel vom Meister ausgewählt.",
|
||||
"主人未选择有效目标。",
|
||||
"主人未選擇有效目標。",
|
||||
"No hay objetivo válido seleccionado por el maestro.",
|
||||
"No hay objetivo válido seleccionado por el maestro.",
|
||||
"Хозяин не выбрал действительную цель."),
|
||||
|
||||
(1721, 'pet_target_dead_error', "Target is not alive.", 0, 0,
|
||||
"대상이 살아있지 않습니다.",
|
||||
"La cible n'est pas vivante.",
|
||||
"Das Ziel ist nicht am Leben.",
|
||||
"目标未存活。",
|
||||
"目標未存活。",
|
||||
"El objetivo no está vivo.",
|
||||
"El objetivo no está vivo.",
|
||||
"Цель не жива."),
|
||||
|
||||
(1722, 'pet_invalid_target_error', "Target is not a valid attack target for the bot.", 0, 0,
|
||||
"대상이 봇에게 유효한 공격 대상이 아닙니다.",
|
||||
"La cible n'est pas une cible d'attaque valide pour le bot.",
|
||||
"Das Ziel ist kein gültiges Angriffsziel für den Bot.",
|
||||
"目标不是机器人的有效攻击目标。",
|
||||
"目標不是機器人的有效攻擊目標。",
|
||||
"El objetivo no es un objetivo de ataque válido para el bot.",
|
||||
"El objetivo no es un objetivo de ataque válido para el bot.",
|
||||
"Цель не является допустимой целью атаки для бота."),
|
||||
|
||||
(1723, 'pet_pvp_prohibited_error', "I cannot command my pet to attack players in PvP prohibited areas.", 0, 0,
|
||||
"PvP 금지 지역에서는 펫에게 플레이어 공격 명령을 내릴 수 없습니다.",
|
||||
"Je ne peux pas commander à mon familier d'attaquer des joueurs dans les zones où le PvP est interdit.",
|
||||
"Ich kann meinem Tier nicht befehlen, Spieler in PvP-verbotenen Gebieten anzugreifen.",
|
||||
"我不能命令我的宠物在禁止PvP的区域攻击玩家。",
|
||||
"我不能命令我的寵物在禁止PvP的區域攻擊玩家。",
|
||||
"No puedo ordenar a mi mascota atacar jugadores en áreas donde el PvP está prohibido.",
|
||||
"No puedo ordenar a mi mascota atacar jugadores en áreas donde el PvP está prohibido.",
|
||||
"Я не могу приказать своему питомцу атаковать игроков в зонах, где PvP запрещено."),
|
||||
|
||||
(1724, 'pet_attack_success', "Pet commanded to attack your target.", 0, 0,
|
||||
"펫이 당신의 대상을 공격하도록 명령했습니다.",
|
||||
"Le familier a reçu l'ordre d'attaquer votre cible.",
|
||||
"Tier wurde befohlen, dein Ziel anzugreifen.",
|
||||
"宠物已命令攻击你的目标。",
|
||||
"寵物已命令攻擊你的目標。",
|
||||
"Mascota ordenada a atacar tu objetivo.",
|
||||
"Mascota ordenada a atacar tu objetivo.",
|
||||
"Питомцу приказано атаковать вашу цель."),
|
||||
|
||||
(1725, 'pet_attack_failed', "Pet did not attack. (Already attacking or unable to attack target)", 0, 0,
|
||||
"펫이 공격하지 않았습니다. (이미 공격 중이거나 대상 공격 불가)",
|
||||
"Le familier n'a pas attaqué. (Attaque déjà en cours ou impossible d'attaquer la cible)",
|
||||
"Tier hat nicht angegriffen. (Greift bereits an oder kann Ziel nicht angreifen)",
|
||||
"宠物未攻击。(已在攻击或无法攻击目标)",
|
||||
"寵物未攻擊。(已在攻擊或無法攻擊目標)",
|
||||
"La mascota no atacó. (Ya está atacando o no puede atacar al objetivo)",
|
||||
"La mascota no atacó. (Ya está atacando o no puede atacar al objetivo)",
|
||||
"Питомец не атаковал. (Уже атакует или не может атаковать цель)"),
|
||||
|
||||
(1726, 'pet_follow_success', "Pet commanded to follow.", 0, 0,
|
||||
"펫이 따라오도록 명령했습니다.",
|
||||
"Le familier a reçu l'ordre de suivre.",
|
||||
"Tier wurde befohlen zu folgen.",
|
||||
"宠物已命令跟随。",
|
||||
"寵物已命令跟隨。",
|
||||
"Mascota ordenada a seguir.",
|
||||
"Mascota ordenada a seguir.",
|
||||
"Питомцу приказано следовать."),
|
||||
|
||||
(1727, 'pet_stay_success', "Pet commanded to stay.", 0, 0,
|
||||
"펫이 머물도록 명령했습니다.",
|
||||
"Le familier a reçu l'ordre de rester.",
|
||||
"Tier wurde befohlen zu bleiben.",
|
||||
"宠物已命令停留。",
|
||||
"寵物已命令停留。",
|
||||
"Mascota ordenada a quedarse.",
|
||||
"Mascota ordenada a quedarse.",
|
||||
"Питомцу приказано остаться."),
|
||||
|
||||
(1728, 'pet_unknown_command_error', "Unknown pet command: %param. Use: pet <aggressive|defensive|passive|stance|attack|follow|stay>", 0, 0,
|
||||
"알 수 없는 펫 명령: %param. 사용법: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
|
||||
"Commande de familier inconnue: %param. Utilisation: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
|
||||
"Unbekannter Tierbefehl: %param. Verwendung: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
|
||||
"未知宠物命令: %param。用法: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
|
||||
"未知寵物命令: %param。用法: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
|
||||
"Comando de mascota desconocido: %param. Uso: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
|
||||
"Comando de mascota desconocido: %param. Uso: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
|
||||
"Неизвестная команда питомца: %param. Использование: pet <aggressive|defensive|passive|stance|attack|follow|stay>"),
|
||||
|
||||
(1729, 'pet_stance_set_success', "Pet stance set to %stance.", 0, 0,
|
||||
"펫 태세가 %stance(으)로 설정되었습니다.",
|
||||
"Position du familier définie sur %stance.",
|
||||
"Tierhaltung auf %stance gesetzt.",
|
||||
"宠物姿态设置为 %stance。",
|
||||
"寵物姿態設置為 %stance。",
|
||||
"Postura de mascota establecida en %stance.",
|
||||
"Postura de mascota establecida en %stance.",
|
||||
"Позиция питомца установлена на %stance."),
|
||||
|
||||
(1730, 'pet_type_pet', "pet", 0, 0,
|
||||
"펫",
|
||||
"familier",
|
||||
"Tier",
|
||||
"宠物",
|
||||
"寵物",
|
||||
"mascota",
|
||||
"mascota",
|
||||
"питомец"),
|
||||
|
||||
(1731, 'pet_type_guardian', "guardian", 0, 0,
|
||||
"수호자",
|
||||
"gardien",
|
||||
"Wächter",
|
||||
"守护者",
|
||||
"守護者",
|
||||
"guardián",
|
||||
"guardián",
|
||||
"защитник"),
|
||||
|
||||
(1732, 'pet_stance_aggressive', "aggressive", 0, 0,
|
||||
"공격적",
|
||||
"agressif",
|
||||
"aggressiv",
|
||||
"进攻",
|
||||
"進攻",
|
||||
"agresivo",
|
||||
"agresivo",
|
||||
"агрессивная"),
|
||||
|
||||
(1733, 'pet_stance_defensive', "defensive", 0, 0,
|
||||
"방어적",
|
||||
"défensif",
|
||||
"defensiv",
|
||||
"防御",
|
||||
"防禦",
|
||||
"defensivo",
|
||||
"defensivo",
|
||||
"защитная"),
|
||||
|
||||
(1734, 'pet_stance_passive', "passive", 0, 0,
|
||||
"수동적",
|
||||
"passif",
|
||||
"passiv",
|
||||
"被动",
|
||||
"被動",
|
||||
"pasivo",
|
||||
"pasivo",
|
||||
"пассивная"),
|
||||
|
||||
(1735, 'pet_stance_unknown', "unknown", 0, 0,
|
||||
"알 수 없음",
|
||||
"inconnu",
|
||||
"unbekannt",
|
||||
"未知",
|
||||
"未知",
|
||||
"desconocido",
|
||||
"desconocido",
|
||||
"неизвестная");
|
||||
|
||||
INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES
|
||||
('pet_usage_error', 100),
|
||||
('pet_no_pet_error', 100),
|
||||
('pet_stance_report', 100),
|
||||
('pet_no_target_error', 100),
|
||||
('pet_target_dead_error', 100),
|
||||
('pet_invalid_target_error', 100),
|
||||
('pet_pvp_prohibited_error', 100),
|
||||
('pet_attack_success', 100),
|
||||
('pet_attack_failed', 100),
|
||||
('pet_follow_success', 100),
|
||||
('pet_stay_success', 100),
|
||||
('pet_unknown_command_error', 100),
|
||||
('pet_stance_set_success', 100),
|
||||
('pet_type_pet', 100),
|
||||
('pet_type_guardian', 100),
|
||||
('pet_stance_aggressive', 100),
|
||||
('pet_stance_defensive', 100),
|
||||
('pet_stance_passive', 100),
|
||||
('pet_stance_unknown', 100);
|
||||
@@ -1,15 +0,0 @@
|
||||
DELETE FROM ai_playerbot_texts WHERE name IN ('no_fishing_pole_error');
|
||||
DELETE FROM ai_playerbot_texts_chance WHERE name IN ('no_fishing_pole_error');
|
||||
|
||||
INSERT INTO ai_playerbot_texts (id, name, text, say_type, reply_type, text_loc1, text_loc2, text_loc3, text_loc4, text_loc5, text_loc6, text_loc7, text_loc8) VALUES
|
||||
(1736, 'no_fishing_pole_error', "I don't have a Fishing Pole", 0, 0,
|
||||
"낚싯대가 없습니다",
|
||||
"Je n’ai pas de canne à pêche",
|
||||
"Ich habe keine Angelrute",
|
||||
"我沒有釣魚竿",
|
||||
"我没有钓鱼竿",
|
||||
"No tengo una caña de pescar",
|
||||
"No tengo una caña de pescar",
|
||||
"У меня нет удочки");
|
||||
|
||||
INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES ('no_fishing_pole_error', 100);
|
||||
@@ -140,37 +140,37 @@ BotRoles AiFactory::GetPlayerRoles(Player* player)
|
||||
switch (player->getClass())
|
||||
{
|
||||
case CLASS_PRIEST:
|
||||
if (tab == PRIEST_TAB_SHADOW)
|
||||
if (tab == 2)
|
||||
role = BOT_ROLE_DPS;
|
||||
else
|
||||
role = BOT_ROLE_HEALER;
|
||||
break;
|
||||
case CLASS_SHAMAN:
|
||||
if (tab == SHAMAN_TAB_RESTORATION)
|
||||
if (tab == 2)
|
||||
role = BOT_ROLE_HEALER;
|
||||
else
|
||||
role = BOT_ROLE_DPS;
|
||||
break;
|
||||
case CLASS_WARRIOR:
|
||||
if (tab == WARRIOR_TAB_PROTECTION)
|
||||
if (tab == 2)
|
||||
role = BOT_ROLE_TANK;
|
||||
else
|
||||
role = BOT_ROLE_DPS;
|
||||
break;
|
||||
case CLASS_PALADIN:
|
||||
if (tab == PALADIN_TAB_HOLY)
|
||||
if (tab == 0)
|
||||
role = BOT_ROLE_HEALER;
|
||||
else if (tab == PALADIN_TAB_PROTECTION)
|
||||
else if (tab == 1)
|
||||
role = BOT_ROLE_TANK;
|
||||
else if (tab == PALADIN_TAB_RETRIBUTION)
|
||||
else if (tab == 2)
|
||||
role = BOT_ROLE_DPS;
|
||||
break;
|
||||
case CLASS_DRUID:
|
||||
if (tab == DRUID_TAB_BALANCE)
|
||||
if (tab == 0)
|
||||
role = BOT_ROLE_DPS;
|
||||
else if (tab == DRUID_TAB_FERAL)
|
||||
else if (tab == 1)
|
||||
role = (BotRoles)(BOT_ROLE_TANK | BOT_ROLE_DPS);
|
||||
else if (tab == DRUID_TAB_RESTORATION)
|
||||
else if (tab == 2)
|
||||
role = BOT_ROLE_HEALER;
|
||||
break;
|
||||
default:
|
||||
@@ -188,83 +188,84 @@ std::string AiFactory::GetPlayerSpecName(Player* player)
|
||||
switch (player->getClass())
|
||||
{
|
||||
case CLASS_PRIEST:
|
||||
if (tab == PRIEST_TAB_SHADOW)
|
||||
if (tab == 2)
|
||||
specName = "shadow";
|
||||
else if (tab == PRIEST_TAB_HOLY)
|
||||
else if (tab == 1)
|
||||
specName = "holy";
|
||||
else
|
||||
specName = "disc";
|
||||
;
|
||||
break;
|
||||
case CLASS_SHAMAN:
|
||||
if (tab == SHAMAN_TAB_RESTORATION)
|
||||
if (tab == 2)
|
||||
specName = "resto";
|
||||
else if (tab == SHAMAN_TAB_ENHANCEMENT)
|
||||
else if (tab == 1)
|
||||
specName = "enhance";
|
||||
else
|
||||
specName = "elem";
|
||||
break;
|
||||
case CLASS_WARRIOR:
|
||||
if (tab == WARRIOR_TAB_PROTECTION)
|
||||
if (tab == 2)
|
||||
specName = "prot";
|
||||
else if (tab == WARRIOR_TAB_FURY)
|
||||
else if (tab == 1)
|
||||
specName = "fury";
|
||||
else
|
||||
specName = "arms";
|
||||
break;
|
||||
case CLASS_PALADIN:
|
||||
if (tab == PALADIN_TAB_HOLY)
|
||||
if (tab == 0)
|
||||
specName = "holy";
|
||||
else if (tab == PALADIN_TAB_PROTECTION)
|
||||
else if (tab == 1)
|
||||
specName = "prot";
|
||||
else if (tab == PALADIN_TAB_RETRIBUTION)
|
||||
else if (tab == 2)
|
||||
specName = "retrib";
|
||||
break;
|
||||
case CLASS_DRUID:
|
||||
if (tab == DRUID_TAB_BALANCE)
|
||||
if (tab == 0)
|
||||
specName = "balance";
|
||||
else if (tab == DRUID_TAB_FERAL)
|
||||
else if (tab == 1)
|
||||
specName = "feraldps";
|
||||
else if (tab == DRUID_TAB_RESTORATION)
|
||||
else if (tab == 2)
|
||||
specName = "resto";
|
||||
break;
|
||||
case CLASS_ROGUE:
|
||||
if (tab == ROGUE_TAB_ASSASSINATION)
|
||||
if (tab == 0)
|
||||
specName = "assas";
|
||||
else if (tab == ROGUE_TAB_COMBAT)
|
||||
else if (tab == 1)
|
||||
specName = "combat";
|
||||
else if (tab == ROGUE_TAB_SUBTLETY)
|
||||
else if (tab == 2)
|
||||
specName = "subtle";
|
||||
break;
|
||||
case CLASS_HUNTER:
|
||||
if (tab == HUNTER_TAB_BEAST_MASTERY)
|
||||
if (tab == 0)
|
||||
specName = "beast";
|
||||
else if (tab == HUNTER_TAB_MARKSMANSHIP)
|
||||
else if (tab == 1)
|
||||
specName = "marks";
|
||||
else if (tab == HUNTER_TAB_SURVIVAL)
|
||||
else if (tab == 2)
|
||||
specName = "surv";
|
||||
break;
|
||||
case CLASS_DEATH_KNIGHT:
|
||||
if (tab == DEATH_KNIGHT_TAB_BLOOD)
|
||||
if (tab == 0)
|
||||
specName = "blooddps";
|
||||
else if (tab == DEATH_KNIGHT_TAB_FROST)
|
||||
else if (tab == 1)
|
||||
specName = "frostdps";
|
||||
else if (tab == DEATH_KNIGHT_TAB_UNHOLY)
|
||||
else if (tab == 2)
|
||||
specName = "unholydps";
|
||||
break;
|
||||
case CLASS_MAGE:
|
||||
if (tab == MAGE_TAB_ARCANE)
|
||||
if (tab == 0)
|
||||
specName = "arcane";
|
||||
else if (tab == MAGE_TAB_FIRE)
|
||||
else if (tab == 1)
|
||||
specName = "fire";
|
||||
else if (tab == MAGE_TAB_FROST)
|
||||
else if (tab == 2)
|
||||
specName = "frost";
|
||||
break;
|
||||
case CLASS_WARLOCK:
|
||||
if (tab == WARLOCK_TAB_AFFLICTION)
|
||||
if (tab == 0)
|
||||
specName = "afflic";
|
||||
else if (tab == WARLOCK_TAB_DEMONOLOGY)
|
||||
else if (tab == 1)
|
||||
specName = "demo";
|
||||
else if (tab == WARLOCK_TAB_DESTRUCTION)
|
||||
else if (tab == 2)
|
||||
specName = "destro";
|
||||
break;
|
||||
default:
|
||||
@@ -279,124 +280,147 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
|
||||
uint8 tab = GetPlayerSpecTab(player);
|
||||
|
||||
if (!player->InBattleground())
|
||||
{
|
||||
engine->addStrategiesNoInit("racials", "chat", "default", "cast time", "potions", "duel", "boost", nullptr);
|
||||
|
||||
}
|
||||
if (sPlayerbotAIConfig->autoAvoidAoe && facade->HasRealPlayerMaster())
|
||||
{
|
||||
engine->addStrategy("avoid aoe", false);
|
||||
|
||||
}
|
||||
engine->addStrategy("formation", false);
|
||||
|
||||
switch (player->getClass())
|
||||
{
|
||||
case CLASS_PRIEST:
|
||||
if (tab == PRIEST_TAB_SHADOW)
|
||||
if (tab == 2)
|
||||
{
|
||||
engine->addStrategiesNoInit("dps", "shadow debuff", "shadow aoe", nullptr);
|
||||
}
|
||||
else if (tab == PRIEST_TAB_DISCIPLINE)
|
||||
{
|
||||
engine->addStrategiesNoInit("heal", nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
engine->addStrategiesNoInit("holy heal", nullptr);
|
||||
}
|
||||
|
||||
engine->addStrategiesNoInit("dps assist", "cure", nullptr);
|
||||
break;
|
||||
case CLASS_MAGE:
|
||||
if (tab == MAGE_TAB_ARCANE)
|
||||
if (tab == 0) // Arcane
|
||||
engine->addStrategiesNoInit("arcane", nullptr);
|
||||
else if (tab == MAGE_TAB_FIRE)
|
||||
else if (tab == 1) // Fire
|
||||
{
|
||||
if (player->HasSpell(44614) /*Frostfire Bolt*/ && player->HasAura(15047) /*Ice Shards*/)
|
||||
{
|
||||
engine->addStrategiesNoInit("frostfire", nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
engine->addStrategiesNoInit("fire", nullptr);
|
||||
}
|
||||
}
|
||||
else
|
||||
else // Frost
|
||||
engine->addStrategiesNoInit("frost", nullptr);
|
||||
|
||||
engine->addStrategiesNoInit("dps", "dps assist", "cure", "aoe", nullptr);
|
||||
break;
|
||||
case CLASS_WARRIOR:
|
||||
if (tab == WARRIOR_TAB_PROTECTION)
|
||||
if (tab == 2)
|
||||
engine->addStrategiesNoInit("tank", "tank assist", "aoe", nullptr);
|
||||
else if (tab == WARRIOR_TAB_ARMS || !player->HasSpell(1680)) // Whirlwind
|
||||
engine->addStrategiesNoInit("arms", "aoe", "dps assist", nullptr);
|
||||
else if (tab == 0 || !player->HasSpell(1680)) // Whirlwind
|
||||
engine->addStrategiesNoInit("arms", "aoe", "dps assist", /*"behind",*/ nullptr);
|
||||
else
|
||||
engine->addStrategiesNoInit("fury", "aoe", "dps assist", nullptr);
|
||||
engine->addStrategiesNoInit("fury", "aoe", "dps assist", /*"behind",*/ nullptr);
|
||||
break;
|
||||
case CLASS_SHAMAN:
|
||||
if (tab == SHAMAN_TAB_ELEMENTAL)
|
||||
if (tab == 0) // Elemental
|
||||
engine->addStrategiesNoInit("ele", "stoneskin", "wrath", "mana spring", "wrath of air", nullptr);
|
||||
else if (tab == SHAMAN_TAB_RESTORATION)
|
||||
else if (tab == 2) // Restoration
|
||||
engine->addStrategiesNoInit("resto", "stoneskin", "flametongue", "mana spring", "wrath of air", nullptr);
|
||||
else
|
||||
else // Enhancement
|
||||
engine->addStrategiesNoInit("enh", "strength of earth", "magma", "healing stream", "windfury", nullptr);
|
||||
|
||||
engine->addStrategiesNoInit("dps assist", "cure", "aoe", nullptr);
|
||||
break;
|
||||
case CLASS_PALADIN:
|
||||
if (tab == PALADIN_TAB_PROTECTION)
|
||||
if (tab == 1)
|
||||
engine->addStrategiesNoInit("tank", "tank assist", "bthreat", "barmor", "cure", nullptr);
|
||||
else if (tab == PALADIN_TAB_HOLY)
|
||||
else if (tab == 0)
|
||||
engine->addStrategiesNoInit("heal", "dps assist", "cure", "bcast", nullptr);
|
||||
else
|
||||
engine->addStrategiesNoInit("dps", "dps assist", "cure", "baoe", nullptr);
|
||||
|
||||
break;
|
||||
case CLASS_DRUID:
|
||||
if (tab == DRUID_TAB_BALANCE)
|
||||
if (tab == 0)
|
||||
{
|
||||
engine->addStrategiesNoInit("caster", "cure", "caster aoe", "dps assist", nullptr);
|
||||
engine->addStrategy("caster debuff", false);
|
||||
}
|
||||
else if (tab == DRUID_TAB_RESTORATION)
|
||||
else if (tab == 2)
|
||||
engine->addStrategiesNoInit("heal", "cure", "dps assist", nullptr);
|
||||
else
|
||||
{
|
||||
if (player->HasSpell(768) /*cat form*/ && !player->HasAura(16931) /*thick hide*/)
|
||||
if (player->HasSpell(768) /*cat form*/&& !player->HasAura(16931) /*thick hide*/)
|
||||
{
|
||||
engine->addStrategiesNoInit("cat", "dps assist", nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
engine->addStrategiesNoInit("bear", "tank assist", nullptr);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CLASS_HUNTER:
|
||||
if (tab == HUNTER_TAB_BEAST_MASTERY)
|
||||
if (tab == 0) // Beast Mastery
|
||||
engine->addStrategiesNoInit("bm", nullptr);
|
||||
else if (tab == HUNTER_TAB_MARKSMANSHIP)
|
||||
else if (tab == 1) // Marksmanship
|
||||
engine->addStrategiesNoInit("mm", nullptr);
|
||||
else
|
||||
else if (tab == 2) // Survival
|
||||
engine->addStrategiesNoInit("surv", nullptr);
|
||||
|
||||
engine->addStrategiesNoInit("cc", "dps assist", "aoe", nullptr);
|
||||
break;
|
||||
case CLASS_ROGUE:
|
||||
if (tab == ROGUE_TAB_ASSASSINATION || tab == ROGUE_TAB_SUBTLETY)
|
||||
{
|
||||
engine->addStrategiesNoInit("melee", "dps assist", "aoe", nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
engine->addStrategiesNoInit("dps", "dps assist", "aoe", nullptr);
|
||||
}
|
||||
break;
|
||||
case CLASS_WARLOCK:
|
||||
if (tab == WARLOCK_TAB_AFFLICTION)
|
||||
if (tab == 0) // Affliction
|
||||
engine->addStrategiesNoInit("affli", "curse of agony", nullptr);
|
||||
else if (tab == WARLOCK_TAB_DEMONOLOGY)
|
||||
else if (tab == 1) // Demonology
|
||||
engine->addStrategiesNoInit("demo", "curse of agony", "meta melee", nullptr);
|
||||
else
|
||||
else if (tab == 2) // Destruction
|
||||
engine->addStrategiesNoInit("destro", "curse of elements", nullptr);
|
||||
|
||||
engine->addStrategiesNoInit("cc", "dps assist", "aoe", nullptr);
|
||||
break;
|
||||
|
||||
case CLASS_DEATH_KNIGHT:
|
||||
if (tab == DEATH_KNIGHT_TAB_BLOOD)
|
||||
if (tab == 0)
|
||||
engine->addStrategiesNoInit("blood", "tank assist", nullptr);
|
||||
else if (tab == DEATH_KNIGHT_TAB_FROST)
|
||||
else if (tab == 1)
|
||||
engine->addStrategiesNoInit("frost", "frost aoe", "dps assist", nullptr);
|
||||
else
|
||||
engine->addStrategiesNoInit("unholy", "unholy aoe", "dps assist", nullptr);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (PlayerbotAI::IsTank(player, true))
|
||||
{
|
||||
engine->addStrategy("tank face", false);
|
||||
|
||||
}
|
||||
if (PlayerbotAI::IsMelee(player, true) && PlayerbotAI::IsDps(player, true))
|
||||
{
|
||||
engine->addStrategy("behind", false);
|
||||
|
||||
}
|
||||
if (PlayerbotAI::IsHeal(player, true))
|
||||
{
|
||||
if (sPlayerbotAIConfig->autoSaveMana)
|
||||
@@ -404,7 +428,6 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
|
||||
if (!sPlayerbotAIConfig->IsRestrictedHealerDPSMap(player->GetMapId()))
|
||||
engine->addStrategy("healer dps", false);
|
||||
}
|
||||
|
||||
if (facade->IsRealPlayer() || sRandomPlayerbotMgr->IsRandomBot(player))
|
||||
{
|
||||
if (!player->GetGroup())
|
||||
@@ -413,13 +436,15 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
|
||||
engine->addStrategy("boost", false);
|
||||
engine->addStrategy("dps assist", false);
|
||||
engine->removeStrategy("threat", false);
|
||||
|
||||
// engine-
|
||||
switch (player->getClass())
|
||||
{
|
||||
case CLASS_PRIEST:
|
||||
{
|
||||
if (tab != PRIEST_TAB_SHADOW)
|
||||
{
|
||||
engine->addStrategiesNoInit("holy dps", "shadow debuff", "shadow aoe", nullptr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CLASS_DRUID:
|
||||
@@ -434,13 +459,17 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
|
||||
case CLASS_SHAMAN:
|
||||
{
|
||||
if (tab == SHAMAN_TAB_RESTORATION)
|
||||
{
|
||||
engine->addStrategiesNoInit("caster", "caster aoe", "bmana", nullptr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CLASS_PALADIN:
|
||||
{
|
||||
if (tab == PALADIN_TAB_HOLY)
|
||||
{
|
||||
engine->addStrategiesNoInit("dps", "dps assist", "baoe", nullptr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -449,9 +478,13 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
|
||||
}
|
||||
}
|
||||
if (sRandomPlayerbotMgr->IsRandomBot(player))
|
||||
{
|
||||
engine->ChangeStrategy(sPlayerbotAIConfig->randomBotCombatStrategies);
|
||||
}
|
||||
else
|
||||
{
|
||||
engine->ChangeStrategy(sPlayerbotAIConfig->combatStrategies);
|
||||
}
|
||||
|
||||
// Battleground switch
|
||||
if (player->InBattleground() && player->GetBattleground())
|
||||
@@ -478,15 +511,23 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
|
||||
if (player->InArena())
|
||||
{
|
||||
engine->addStrategy("arena", false);
|
||||
engine->addStrategiesNoInit("boost", "racials", "chat", "default", "aoe", "cast time", "dps assist", nullptr);
|
||||
}
|
||||
else
|
||||
engine->addStrategiesNoInit("boost", "racials", "chat", "default", "aoe", "potions", "cast time", "dps assist", nullptr);
|
||||
|
||||
engine->addStrategiesNoInit("boost", "racials", "chat", "default", "aoe", "potions", "cast time", "dps assist",
|
||||
nullptr);
|
||||
engine->removeStrategy("custom::say", false);
|
||||
engine->removeStrategy("flee", false);
|
||||
engine->removeStrategy("threat", false);
|
||||
engine->addStrategy("boost", false);
|
||||
|
||||
// if ((player->getClass() == CLASS_DRUID && tab == 2) || (player->getClass() == CLASS_SHAMAN && tab == 2))
|
||||
// engine->addStrategiesNoInit("caster", "caster aoe", nullptr);
|
||||
|
||||
// if (player->getClass() == CLASS_DRUID && tab == 1)
|
||||
// engine->addStrategiesNoInit(/*"behind",*/ "dps", nullptr);
|
||||
|
||||
// if (player->getClass() == CLASS_ROGUE)
|
||||
// engine->addStrategiesNoInit(/*"behind",*/ "stealth", nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -508,15 +549,19 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
|
||||
nonCombatEngine->addStrategiesNoInit("dps assist", "cure", nullptr);
|
||||
break;
|
||||
case CLASS_PALADIN:
|
||||
if (tab == PALADIN_TAB_PROTECTION)
|
||||
if (tab == 1)
|
||||
{
|
||||
nonCombatEngine->addStrategiesNoInit("bthreat", "tank assist", "barmor", nullptr);
|
||||
if (player->GetLevel() >= 20)
|
||||
{
|
||||
nonCombatEngine->addStrategy("bhealth", false);
|
||||
}
|
||||
else
|
||||
{
|
||||
nonCombatEngine->addStrategy("bdps", false);
|
||||
}
|
||||
}
|
||||
else if (tab == PALADIN_TAB_HOLY)
|
||||
else if (tab == 0)
|
||||
nonCombatEngine->addStrategiesNoInit("dps assist", "bmana", "bcast", nullptr);
|
||||
else
|
||||
nonCombatEngine->addStrategiesNoInit("dps assist", "bdps", "baoe", nullptr);
|
||||
@@ -527,7 +572,7 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
|
||||
nonCombatEngine->addStrategiesNoInit("bdps", "dps assist", "pet", nullptr);
|
||||
break;
|
||||
case CLASS_SHAMAN:
|
||||
if (tab == SHAMAN_TAB_ELEMENTAL || tab == SHAMAN_TAB_RESTORATION)
|
||||
if (tab == 0 || tab == 2)
|
||||
nonCombatEngine->addStrategy("bmana", false);
|
||||
else
|
||||
nonCombatEngine->addStrategy("bdps", false);
|
||||
@@ -543,34 +588,43 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
|
||||
nonCombatEngine->addStrategiesNoInit("dps assist", "cure", nullptr);
|
||||
break;
|
||||
case CLASS_DRUID:
|
||||
if (tab == DRUID_TAB_FERAL)
|
||||
if (tab == 1)
|
||||
{
|
||||
if (player->GetLevel() >= 20 && !player->HasAura(16931) /*thick hide*/)
|
||||
{
|
||||
nonCombatEngine->addStrategy("dps assist", false);
|
||||
}
|
||||
else
|
||||
{
|
||||
nonCombatEngine->addStrategy("tank assist", false);
|
||||
}
|
||||
}
|
||||
else
|
||||
nonCombatEngine->addStrategiesNoInit("dps assist", "cure", nullptr);
|
||||
break;
|
||||
case CLASS_WARRIOR:
|
||||
if (tab == WARRIOR_TAB_PROTECTION)
|
||||
if (tab == 2)
|
||||
nonCombatEngine->addStrategy("tank assist", false);
|
||||
else
|
||||
nonCombatEngine->addStrategy("dps assist", false);
|
||||
break;
|
||||
case CLASS_WARLOCK:
|
||||
if (tab == WARLOCK_TAB_AFFLICTION)
|
||||
{
|
||||
nonCombatEngine->addStrategiesNoInit("felhunter", "spellstone", nullptr);
|
||||
}
|
||||
else if (tab == WARLOCK_TAB_DEMONOLOGY)
|
||||
{
|
||||
nonCombatEngine->addStrategiesNoInit("felguard", "spellstone", nullptr);
|
||||
}
|
||||
else if (tab == WARLOCK_TAB_DESTRUCTION)
|
||||
{
|
||||
nonCombatEngine->addStrategiesNoInit("imp", "firestone", nullptr);
|
||||
|
||||
}
|
||||
nonCombatEngine->addStrategiesNoInit("dps assist", "ss self", nullptr);
|
||||
break;
|
||||
case CLASS_DEATH_KNIGHT:
|
||||
if (tab == DEATH_KNIGHT_TAB_BLOOD)
|
||||
if (tab == 0)
|
||||
nonCombatEngine->addStrategy("tank assist", false);
|
||||
else
|
||||
nonCombatEngine->addStrategy("dps assist", false);
|
||||
@@ -587,7 +641,9 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
|
||||
}
|
||||
|
||||
if (sPlayerbotAIConfig->autoSaveMana && PlayerbotAI::IsHeal(player, true))
|
||||
{
|
||||
nonCombatEngine->addStrategy("save mana", false);
|
||||
}
|
||||
|
||||
if ((sRandomPlayerbotMgr->IsRandomBot(player)) && !player->InBattleground())
|
||||
{
|
||||
@@ -613,14 +669,18 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
|
||||
nonCombatEngine->addStrategy("grind", false);
|
||||
|
||||
if (sPlayerbotAIConfig->enableNewRpgStrategy)
|
||||
{
|
||||
nonCombatEngine->addStrategy("new rpg", false);
|
||||
}
|
||||
else if (sPlayerbotAIConfig->autoDoQuests)
|
||||
{
|
||||
// nonCombatEngine->addStrategy("travel");
|
||||
nonCombatEngine->addStrategy("rpg", false);
|
||||
}
|
||||
else
|
||||
{
|
||||
nonCombatEngine->addStrategy("move random", false);
|
||||
}
|
||||
|
||||
if (sPlayerbotAIConfig->randomBotJoinBG)
|
||||
nonCombatEngine->addStrategy("bg", false);
|
||||
@@ -669,8 +729,11 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nonCombatEngine->ChangeStrategy(sPlayerbotAIConfig->nonCombatStrategies);
|
||||
|
||||
}
|
||||
// nonCombatEngine->addStrategy("battleground");
|
||||
// nonCombatEngine->addStrategy("warsong");
|
||||
// Battleground switch
|
||||
if (player->InBattleground() && player->GetBattleground())
|
||||
{
|
||||
@@ -727,7 +790,9 @@ void AiFactory::AddDefaultDeadStrategies(Player* player, PlayerbotAI* const faca
|
||||
deadEngine->addStrategiesNoInit("dead", "stay", "chat", "default", "follow", nullptr);
|
||||
|
||||
if (sRandomPlayerbotMgr->IsRandomBot(player) && !player->GetGroup())
|
||||
{
|
||||
deadEngine->removeStrategy("follow", false);
|
||||
}
|
||||
}
|
||||
|
||||
Engine* AiFactory::createDeadEngine(Player* player, PlayerbotAI* const facade, AiObjectContext* AiObjectContext)
|
||||
@@ -1,510 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "FishingAction.h"
|
||||
#include "FishValues.h"
|
||||
#include "Event.h"
|
||||
|
||||
#include "GridNotifiers.h"
|
||||
#include "GridNotifiersImpl.h"
|
||||
#include "ItemPackets.h"
|
||||
#include "LastMovementValue.h"
|
||||
#include "Map.h"
|
||||
#include "MovementActions.h"
|
||||
#include "Object.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "PlayerbotTextMgr.h"
|
||||
#include "Playerbots.h"
|
||||
#include "Position.h"
|
||||
|
||||
uint32 const FISHING_SPELL = 7620;
|
||||
uint32 const FISHING_POLE = 6256;
|
||||
uint32 const FISHING_BOBBER = 35591;
|
||||
float const MIN_DISTANCE_TO_WATER = 10.0f; // Minimum spell distance
|
||||
float const MAX_DISTANCE_TO_WATER = 20.0f; // Maximum spell distance
|
||||
float const HEIGHT_ABOVE_WATER_TOLERANCE = 1.0f; // Can stand in up to 1 unit of water and still fish.
|
||||
float const SEARCH_INCREMENT = 2.5f;
|
||||
float const HEIGHT_SEARCH_BUFFER = 10.0f; // Height buffer to prevent potentially missing the model the bot is standing on.
|
||||
float const SEARCH_LAND_BUFFER = 0.5f;
|
||||
uint32 const FISHING_LOCATION_TIMEOUT = 180000; //Three minutes
|
||||
|
||||
static bool IsFishingPole(Item* const item)
|
||||
{
|
||||
if (!item)
|
||||
return false;
|
||||
const ItemTemplate* proto = item->GetTemplate();
|
||||
return proto && proto->Class == ITEM_CLASS_WEAPON &&
|
||||
proto->SubClass == ITEM_SUBCLASS_WEAPON_FISHING_POLE;
|
||||
}
|
||||
|
||||
float HasFishableWaterOrLand(float x, float y, float z, Map* map, uint32 phaseMask, bool checkForLand=false)
|
||||
{
|
||||
if (!map)
|
||||
return INVALID_HEIGHT;
|
||||
|
||||
LiquidData const& liq = map->GetLiquidData(phaseMask, x, y, z+HEIGHT_ABOVE_WATER_TOLERANCE, DEFAULT_COLLISION_HEIGHT, MAP_ALL_LIQUIDS);
|
||||
float ground = map->GetHeight(phaseMask, x, y, z + HEIGHT_SEARCH_BUFFER, true);
|
||||
if (liq.Entry == MAP_LIQUID_TYPE_NO_WATER)
|
||||
{
|
||||
if (checkForLand)
|
||||
return ground;
|
||||
return INVALID_HEIGHT;
|
||||
}
|
||||
if (checkForLand)
|
||||
{
|
||||
if (ground > liq.Level - HEIGHT_ABOVE_WATER_TOLERANCE)
|
||||
return ground;
|
||||
return INVALID_HEIGHT;
|
||||
}
|
||||
|
||||
if (liq.Level + HEIGHT_ABOVE_WATER_TOLERANCE > ground)
|
||||
{
|
||||
if (abs(liq.DepthLevel) < 0.5f) // too shallow to fish in.
|
||||
return INVALID_HEIGHT;
|
||||
return liq.Level;
|
||||
}
|
||||
return INVALID_HEIGHT;
|
||||
}
|
||||
|
||||
bool HasLosToWater(Player* bot, float wx, float wy, float waterZ)
|
||||
{
|
||||
float z = bot->GetCollisionHeight() + bot->GetPositionZ();
|
||||
return bot->GetMap()->isInLineOfSight(
|
||||
bot->GetPositionX(), bot->GetPositionY(), z,
|
||||
wx, wy, waterZ,
|
||||
bot->GetPhaseMask(),
|
||||
LINEOFSIGHT_ALL_CHECKS,
|
||||
VMAP::ModelIgnoreFlags::Nothing);
|
||||
}
|
||||
|
||||
WorldPosition FindLandFromPosition(PlayerbotAI* botAI, float startDistance, float endDistance, float increment, float orientation, WorldPosition targetPos, float fishingSearchWindow, bool checkLOS = true)
|
||||
{
|
||||
Player* bot = botAI->GetBot();
|
||||
Map* map = bot->GetMap();
|
||||
uint32 phaseMask = bot->GetPhaseMask();
|
||||
Player* master = botAI->GetMaster();
|
||||
|
||||
float targetX = targetPos.GetPositionX();
|
||||
float targetY = targetPos.GetPositionY();
|
||||
float targetZ = targetPos.GetPositionZ();
|
||||
|
||||
for (float dist = startDistance; dist <= endDistance; dist += increment)
|
||||
{
|
||||
//step backwards from position to bot to find edge of shore.
|
||||
float checkX = targetX - dist * cos(orientation);
|
||||
float checkY = targetY - dist * sin(orientation);
|
||||
|
||||
float groundZ = map->GetHeight(phaseMask, checkX, checkY, targetZ + HEIGHT_SEARCH_BUFFER, true);
|
||||
|
||||
if (groundZ == INVALID_HEIGHT)
|
||||
continue;
|
||||
|
||||
LiquidData const& liq = map->GetLiquidData(phaseMask, checkX, checkY, targetZ, DEFAULT_COLLISION_HEIGHT, MAP_ALL_LIQUIDS);
|
||||
if (liq.Entry == MAP_LIQUID_TYPE_NO_WATER || groundZ > liq.DepthLevel + HEIGHT_ABOVE_WATER_TOLERANCE)
|
||||
{
|
||||
if (checkLOS)
|
||||
{
|
||||
bool hasLOS = map->isInLineOfSight(checkX, checkY, groundZ, targetX, targetY, targetZ, phaseMask, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::Nothing);
|
||||
if (!hasLOS)
|
||||
continue;
|
||||
}
|
||||
// Add a distance check for the position to prevent the bot from moving out of range to the master.
|
||||
if (master && botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT) && master->GetDistance(checkX, checkY, groundZ) > fishingSearchWindow - SEARCH_LAND_BUFFER)
|
||||
continue;
|
||||
|
||||
return WorldPosition(bot->GetMapId(), checkX, checkY, groundZ);
|
||||
}
|
||||
}
|
||||
|
||||
return WorldPosition();
|
||||
}
|
||||
|
||||
WorldPosition FindLandRadialFromPosition (PlayerbotAI* botAI, WorldPosition targetPos, float startDistance, float endDistance, float increment, float fishingSearchWindow, int angles = 16)
|
||||
{
|
||||
Player* bot = botAI->GetBot();
|
||||
const int numDirections = angles;
|
||||
std::vector<WorldPosition> boundaryPoints;
|
||||
Player* master = botAI->GetMaster();
|
||||
if (!master)
|
||||
return WorldPosition();
|
||||
|
||||
Map* map = bot->GetMap();
|
||||
uint32 phaseMask = bot->GetPhaseMask();
|
||||
|
||||
float targetX = targetPos.GetPositionX();
|
||||
float targetY = targetPos.GetPositionY();
|
||||
float targetZ = targetPos.GetPositionZ();
|
||||
|
||||
for (float dist = startDistance; dist <= endDistance; dist += increment)
|
||||
{
|
||||
for (int i = 0; i < numDirections; ++i)
|
||||
{
|
||||
float angle = (2.0f * M_PI * i) / numDirections;
|
||||
float checkX = targetX - cos(angle) * dist;
|
||||
float checkY = targetY - sin(angle) * dist;
|
||||
|
||||
float groundZ = HasFishableWaterOrLand(checkX, checkY, targetZ, map, phaseMask, true);
|
||||
|
||||
if (groundZ == INVALID_HEIGHT)
|
||||
continue;
|
||||
|
||||
if (map->isInLineOfSight(checkX, checkY, groundZ, targetX, targetY, targetZ, phaseMask, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::Nothing) && master->GetDistance(checkX, checkY, groundZ) > fishingSearchWindow - SEARCH_LAND_BUFFER)
|
||||
continue;
|
||||
|
||||
boundaryPoints.emplace_back(WorldPosition(bot->GetMapId(), checkX, checkY, groundZ));
|
||||
}
|
||||
|
||||
if (!boundaryPoints.empty())
|
||||
break;
|
||||
}
|
||||
|
||||
if (boundaryPoints.empty())
|
||||
return WorldPosition();
|
||||
|
||||
if (boundaryPoints.size() == 1)
|
||||
return boundaryPoints[0];
|
||||
|
||||
float minDistance = FLT_MAX;
|
||||
WorldLocation closestPoint = WorldPosition();
|
||||
for (auto const& pos : boundaryPoints)
|
||||
{
|
||||
float distance = bot->GetExactDist2d(&pos);
|
||||
if (distance < minDistance)
|
||||
{
|
||||
minDistance = distance;
|
||||
closestPoint = pos;
|
||||
}
|
||||
}
|
||||
return closestPoint;
|
||||
}
|
||||
|
||||
WorldPosition FindWaterRadial(Player* bot, float x, float y, float z, Map* map, uint32 phaseMask, float minDistance, float maxDistance, float increment, bool checkLOS, int numDirections)
|
||||
{
|
||||
std::vector<WorldPosition> boundaryPoints;
|
||||
|
||||
float dist = minDistance;
|
||||
while (dist <= maxDistance)
|
||||
{
|
||||
for (int i = 0; i < numDirections; ++i)
|
||||
{
|
||||
float angle = (2.0f * M_PI * i) / numDirections;
|
||||
float checkX = x + cos(angle) * dist;
|
||||
float checkY = y + sin(angle) * dist;
|
||||
|
||||
float waterZ = HasFishableWaterOrLand(checkX, checkY, z, map, phaseMask);
|
||||
|
||||
if (waterZ == INVALID_HEIGHT)
|
||||
continue;
|
||||
|
||||
if (checkLOS && !HasLosToWater(bot, checkX, checkY, waterZ))
|
||||
continue;
|
||||
|
||||
boundaryPoints.emplace_back(WorldPosition(bot->GetMapId(), checkX, checkY, waterZ));
|
||||
}
|
||||
|
||||
if (!boundaryPoints.empty())
|
||||
break;
|
||||
|
||||
dist += increment;
|
||||
}
|
||||
|
||||
if (boundaryPoints.empty())
|
||||
return WorldPosition();
|
||||
|
||||
if (boundaryPoints.size() == 1)
|
||||
return boundaryPoints[0];
|
||||
// return the central point in the identified positions in to try to be perpendicular to the shore.
|
||||
return boundaryPoints[boundaryPoints.size() / 2];
|
||||
}
|
||||
|
||||
WorldPosition FindFishingHole(PlayerbotAI* botAI)
|
||||
{
|
||||
Player* player = botAI->GetBot();
|
||||
GuidVector gos = PAI_VALUE(GuidVector, "nearest game objects no los");
|
||||
GameObject* nearestFishingHole = nullptr;
|
||||
float minDist = std::numeric_limits<float>::max();
|
||||
for (auto const& guid : gos)
|
||||
{
|
||||
GameObject* go = botAI->GetGameObject(guid);
|
||||
if (!go)
|
||||
continue;
|
||||
if (go->GetGoType() == GAMEOBJECT_TYPE_FISHINGHOLE)
|
||||
{
|
||||
float dist = player->GetDistance2d(go);
|
||||
if (dist < minDist)
|
||||
{
|
||||
minDist = dist;
|
||||
nearestFishingHole = go;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nearestFishingHole)
|
||||
return WorldPosition(nearestFishingHole->GetMapId(), nearestFishingHole->GetPositionX(), nearestFishingHole->GetPositionY(), nearestFishingHole->GetPositionZ());
|
||||
|
||||
return WorldPosition();
|
||||
}
|
||||
|
||||
bool MoveNearWaterAction::Execute(Event event)
|
||||
{
|
||||
WorldPosition landSpot = AI_VALUE(WorldPosition, "fishing spot");
|
||||
if (landSpot.IsValid())
|
||||
return MoveTo(landSpot.GetMapId(), landSpot.GetPositionX(), landSpot.GetPositionY(), landSpot.GetPositionZ());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MoveNearWaterAction::isUseful()
|
||||
{
|
||||
if (!AI_VALUE(bool, "can fish"))
|
||||
return false;
|
||||
FishingSpotValue* fishingSpotValueObject = (FishingSpotValue*)context->GetValue<WorldPosition>("fishing spot");
|
||||
WorldPosition pos = fishingSpotValueObject->Get();
|
||||
return !pos.IsValid() || fishingSpotValueObject->IsStale(FISHING_LOCATION_TIMEOUT) ||
|
||||
bot->GetExactDist(&pos) < 0.1f;
|
||||
|
||||
}
|
||||
|
||||
bool MoveNearWaterAction::isPossible()
|
||||
{
|
||||
Player* master = botAI->GetMaster();
|
||||
float fishingSearchWindow;
|
||||
|
||||
if (master)
|
||||
fishingSearchWindow = sPlayerbotAIConfig->fishingDistanceFromMaster;
|
||||
else
|
||||
fishingSearchWindow = sPlayerbotAIConfig->fishingDistance;
|
||||
|
||||
WorldPosition fishingHole = FindFishingHole(botAI);
|
||||
|
||||
if (fishingHole.IsValid())
|
||||
{
|
||||
float distance = bot->GetExactDist2d(&fishingHole);
|
||||
bool hasLOS = bot->IsWithinLOS(fishingHole.GetPositionX(), fishingHole.GetPositionY(), fishingHole.GetPositionZ());
|
||||
// Water spot is in range, and we have LOS to it. Set bot position to fishing spot and do not move
|
||||
if (distance >= MIN_DISTANCE_TO_WATER &&
|
||||
distance <= MAX_DISTANCE_TO_WATER && hasLOS)
|
||||
{
|
||||
SET_AI_VALUE(WorldPosition, "fishing spot", WorldPosition(WorldPosition(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ())));
|
||||
return false;
|
||||
}
|
||||
// Water spot is out of range, lets look for a spot to move to for the fishing hole.
|
||||
if (distance > MAX_DISTANCE_TO_WATER || distance < MIN_DISTANCE_TO_WATER)
|
||||
{
|
||||
float angle = bot->GetAngle(fishingHole.GetPositionX(), fishingHole.GetPositionY());
|
||||
WorldPosition landSpot = FindLandRadialFromPosition(botAI, fishingHole, MIN_DISTANCE_TO_WATER, MAX_DISTANCE_TO_WATER, SEARCH_INCREMENT, fishingSearchWindow, 32);
|
||||
if (landSpot.IsValid())
|
||||
{
|
||||
SET_AI_VALUE(WorldPosition, "fishing spot", landSpot);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Can the bot fish from current position?
|
||||
WorldPosition waterAtCurrentPos =
|
||||
FindWaterRadial(bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), bot->GetMap(),
|
||||
bot->GetPhaseMask(), MIN_DISTANCE_TO_WATER, MAX_DISTANCE_TO_WATER, SEARCH_INCREMENT, true);
|
||||
if (waterAtCurrentPos.IsValid())
|
||||
{
|
||||
SET_AI_VALUE(WorldPosition, "fishing spot",
|
||||
WorldPosition(WorldPosition(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(),
|
||||
bot->GetPositionZ())));
|
||||
return false;
|
||||
}
|
||||
// Lets find some water where we can fish.
|
||||
WorldPosition water = FindWaterRadial(
|
||||
bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(),
|
||||
bot->GetMap(), bot->GetPhaseMask(),
|
||||
MIN_DISTANCE_TO_WATER,
|
||||
fishingSearchWindow + MAX_DISTANCE_TO_WATER,
|
||||
SEARCH_INCREMENT, false);
|
||||
|
||||
if (!water.IsValid())
|
||||
return false;
|
||||
|
||||
bool hasLOS = bot->IsWithinLOS(water.GetPositionX(), water.GetPositionY(), water.GetPositionZ());
|
||||
float angle = bot->GetAngle(water.GetPositionX(), water.GetPositionY());
|
||||
WorldPosition landSpot =
|
||||
FindLandFromPosition(botAI, 0.0f, MAX_DISTANCE_TO_WATER, 1.0f, angle, water, fishingSearchWindow, false);
|
||||
|
||||
if (landSpot.IsValid())
|
||||
{
|
||||
SET_AI_VALUE(WorldPosition, "fishing spot", landSpot);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EquipFishingPoleAction::Execute(Event event)
|
||||
{
|
||||
if (!_pole)
|
||||
return false;
|
||||
|
||||
WorldPacket eqPacket(CMSG_AUTOEQUIP_ITEM_SLOT, 2);
|
||||
eqPacket << _pole->GetGUID() << uint8(EQUIPMENT_SLOT_MAINHAND);
|
||||
WorldPackets::Item::AutoEquipItemSlot nicePacket(std::move(eqPacket));
|
||||
nicePacket.Read();
|
||||
bot->GetSession()->HandleAutoEquipItemSlotOpcode(nicePacket);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EquipFishingPoleAction::isUseful()
|
||||
{
|
||||
Item* mainHand = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
|
||||
if (IsFishingPole(mainHand))
|
||||
return false;
|
||||
|
||||
for (uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; ++slot)
|
||||
{
|
||||
if (Item* item = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
|
||||
{
|
||||
if (IsFishingPole(item))
|
||||
{
|
||||
_pole = item;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag)
|
||||
{
|
||||
if (Bag* pBag = bot->GetBagByPos(bag))
|
||||
{
|
||||
for (uint32 j = 0; j < pBag->GetBagSize(); ++j)
|
||||
{
|
||||
if (Item* item = pBag->GetItemByPos(j))
|
||||
{
|
||||
if (IsFishingPole(item))
|
||||
{
|
||||
_pole = item;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sRandomPlayerbotMgr->IsRandomBot(bot))
|
||||
{
|
||||
bot->StoreNewItemInBestSlots(FISHING_POLE, 1); // Try to get a fishing pole
|
||||
return true;
|
||||
}
|
||||
|
||||
Player* master = botAI->GetMaster();
|
||||
if (!master)
|
||||
return false;
|
||||
|
||||
std::string masterName = master->GetName();
|
||||
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
||||
"no_fishing_pole_error", "I don't have a Fishing Pole",{});
|
||||
botAI->Whisper(text, masterName);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FishingAction::Execute(Event event)
|
||||
{
|
||||
WorldPosition target = WorldPosition();
|
||||
WorldPosition fishingHole = FindFishingHole(botAI);
|
||||
if (fishingHole.IsValid())
|
||||
{
|
||||
Position pos = fishingHole;
|
||||
float distance = bot->GetExactDist2d(&pos);
|
||||
bool hasLOS = bot->IsWithinLOS(fishingHole.GetPositionX(), fishingHole.GetPositionY(), fishingHole.GetPositionZ());
|
||||
if (distance < MAX_DISTANCE_TO_WATER &&
|
||||
distance > MIN_DISTANCE_TO_WATER && hasLOS)
|
||||
target = fishingHole;
|
||||
}
|
||||
if (!target.IsValid())
|
||||
{
|
||||
target = FindWaterRadial(bot, bot->GetPositionX(), bot->GetPositionY(),
|
||||
bot->GetPositionZ(), bot->GetMap(), bot->GetPhaseMask(),
|
||||
MIN_DISTANCE_TO_WATER, MAX_DISTANCE_TO_WATER, SEARCH_INCREMENT, true, 32);
|
||||
if (!target.IsValid())
|
||||
return false;
|
||||
}
|
||||
Position pos = target;
|
||||
|
||||
if (!bot->HasInArc(1.0, &pos, 1.0))
|
||||
{
|
||||
float angle = bot->GetAngle(pos.GetPositionX(), pos.GetPositionY());
|
||||
bot->SetOrientation(angle);
|
||||
if (!bot->IsRooted())
|
||||
bot->SendMovementFlagUpdate();
|
||||
}
|
||||
|
||||
EquipFishingPoleAction equipAction(botAI);
|
||||
if (equipAction.isUseful())
|
||||
return equipAction.Execute(event);
|
||||
|
||||
botAI->CastSpell(FISHING_SPELL, bot);
|
||||
botAI->ChangeStrategy("+use bobber", BOT_STATE_NON_COMBAT);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FishingAction::isUseful()
|
||||
{
|
||||
if (!AI_VALUE(bool, "can fish"))
|
||||
return false;
|
||||
|
||||
FishingSpotValue* fishingSpotValueObject = (FishingSpotValue*)context->GetValue<WorldPosition>("fishing spot");
|
||||
WorldPosition pos = fishingSpotValueObject->Get();
|
||||
|
||||
if (!pos.IsValid() || fishingSpotValueObject->IsStale(FISHING_LOCATION_TIMEOUT))
|
||||
return false;
|
||||
|
||||
return bot->GetExactDist(&pos) < 0.1f;
|
||||
}
|
||||
|
||||
bool UseBobberAction::isUseful()
|
||||
{
|
||||
return AI_VALUE(bool, "can use fishing bobber");
|
||||
}
|
||||
|
||||
bool UseBobberAction::Execute(Event event)
|
||||
{
|
||||
GuidVector gos = AI_VALUE(GuidVector, "nearest game objects no los");
|
||||
for (auto const& guid : gos)
|
||||
{
|
||||
if (GameObject* go = botAI->GetGameObject(guid))
|
||||
{
|
||||
if (go->GetEntry() != FISHING_BOBBER)
|
||||
continue;
|
||||
if (go->GetOwnerGUID() != bot->GetGUID())
|
||||
continue;
|
||||
if (go->getLootState() == GO_READY)
|
||||
{
|
||||
go->Use(bot);
|
||||
botAI->ChangeStrategy("-use bobber", BOT_STATE_NON_COMBAT);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EndMasterFishingAction::Execute(Event event)
|
||||
{
|
||||
botAI->ChangeStrategy("-master fishing", BOT_STATE_NON_COMBAT);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EndMasterFishingAction::isUseful()
|
||||
{
|
||||
FishingSpotValue* fishingSpotValueObject = (FishingSpotValue*)context->GetValue<WorldPosition>("fishing spot");
|
||||
WorldPosition pos = fishingSpotValueObject->Get();
|
||||
if (pos.IsValid() && !fishingSpotValueObject->IsStale(FISHING_LOCATION_TIMEOUT) && pos == bot->GetPosition())
|
||||
return false;
|
||||
|
||||
WorldPosition nearWater = FindWaterRadial(bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(),
|
||||
bot->GetMap(), bot->GetPhaseMask(), MIN_DISTANCE_TO_WATER, sPlayerbotAIConfig->endFishingWithMaster, 10.0f);
|
||||
return !nearWater.IsValid();
|
||||
}
|
||||
|
||||
bool RemoveBobberStrategyAction::Execute(Event event)
|
||||
{
|
||||
botAI->ChangeStrategy("-use bobber", BOT_STATE_NON_COMBAT);
|
||||
return true;
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_FISHINGACTION_H
|
||||
#define _PLAYERBOT_FISHINGACTION_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "MovementActions.h"
|
||||
#include "Event.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
extern const uint32 FISHING_SPELL;
|
||||
extern const uint32 FISHING_POLE;
|
||||
extern const uint32 FISHING_BOBBER;
|
||||
|
||||
WorldPosition FindWaterRadial(Player* bot, float x, float y, float z, Map* map, uint32 phaseMask, float minDistance, float maxDistance, float increment, bool checkLOS=false, int numDirections = 16);
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class FishingAction : public Action
|
||||
{
|
||||
public:
|
||||
FishingAction(PlayerbotAI* botAI) : Action(botAI, "go fishing"){}
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class EquipFishingPoleAction : public Action
|
||||
{
|
||||
public:
|
||||
EquipFishingPoleAction(PlayerbotAI* botAI) : Action(botAI, "equip fishing pole") {}
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
private:
|
||||
Item* _pole = nullptr;
|
||||
};
|
||||
|
||||
class MoveNearWaterAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
MoveNearWaterAction(PlayerbotAI* botAI): MovementAction(botAI, "move near water") {}
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
bool isPossible() override;
|
||||
};
|
||||
|
||||
class UseBobberAction : public Action
|
||||
{
|
||||
public:
|
||||
UseBobberAction(PlayerbotAI* botAI) : Action(botAI, "use fishing bobber") {}
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class EndMasterFishingAction : public Action
|
||||
{
|
||||
public:
|
||||
EndMasterFishingAction(PlayerbotAI* botAI) : Action(botAI, "end master fishing") {}
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class RemoveBobberStrategyAction : public Action
|
||||
{
|
||||
public:
|
||||
RemoveBobberStrategyAction(PlayerbotAI* botAI) : Action(botAI, "remove bobber strategy") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
#endif
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_FISHING_TRIGGER_H
|
||||
#define _PLAYERBOT_FISHING_TRIGGER_H
|
||||
|
||||
#include "GenericTriggers.h"
|
||||
|
||||
class CanFishTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
CanFishTrigger(PlayerbotAI* ai) : Trigger(ai, "can fish") {};
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class CanUseFishingBobberTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
CanUseFishingBobberTrigger(PlayerbotAI* ai) : Trigger(ai, "can use fishing bobber") {};
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "RtiTriggers.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
bool NoRtiTrigger::IsActive()
|
||||
{
|
||||
// Do not auto-react to raid icons while out of combat.
|
||||
// Out-of-combat RTI usage (explicit chat commands) is handled by chat triggers,
|
||||
// not by this generic trigger.
|
||||
if (!bot->IsInCombat())
|
||||
return false;
|
||||
|
||||
Unit* target = AI_VALUE(Unit*, "rti target");
|
||||
return target != nullptr;
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "FishValues.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "RandomPlayerbotMgr.h"
|
||||
#include "Map.h"
|
||||
#include "Spell.h"
|
||||
#include "FishingAction.h"
|
||||
|
||||
bool CanFishValue::Calculate()
|
||||
{
|
||||
int32 SkillFishing = bot->GetSkillValue(SKILL_FISHING);
|
||||
|
||||
if (SkillFishing == 0)
|
||||
return false;
|
||||
|
||||
if (bot->isSwimming())
|
||||
return false;
|
||||
|
||||
if (bot->IsInCombat())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CanUseFishingBobberValue::Calculate()
|
||||
{
|
||||
GuidVector gos = AI_VALUE(GuidVector, "nearest game objects no los");
|
||||
for (auto const& guid : gos)
|
||||
{
|
||||
if (GameObject* go = botAI->GetGameObject(guid))
|
||||
{
|
||||
if (go->GetEntry() != FISHING_BOBBER)
|
||||
continue;
|
||||
if (go->GetOwnerGUID() != bot->GetGUID())
|
||||
continue;
|
||||
|
||||
if (go->getLootState() == GO_READY)
|
||||
return true;
|
||||
|
||||
// Not ready yet → delay next check
|
||||
time_t bobberActiveTime = go->GetRespawnTime() - FISHING_BOBBER_READY_TIME;
|
||||
if (bobberActiveTime > time(0))
|
||||
botAI->SetNextCheckDelay((bobberActiveTime - time(0)) * IN_MILLISECONDS + 500);
|
||||
else
|
||||
botAI->SetNextCheckDelay(1000);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_FISHVALUES_H
|
||||
#define _PLAYERBOT_FISHVALUES_H
|
||||
|
||||
#include "Value.h"
|
||||
#include "TravelMgr.h"
|
||||
#include "NamedObjectContext.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class CanFishValue : public BoolCalculatedValue
|
||||
{
|
||||
public:
|
||||
CanFishValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "can fish") {};
|
||||
bool Calculate() override;
|
||||
};
|
||||
|
||||
class CanUseFishingBobberValue : public BoolCalculatedValue
|
||||
{
|
||||
public:
|
||||
CanUseFishingBobberValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "can use fishing bobber") {};
|
||||
bool Calculate() override;
|
||||
};
|
||||
|
||||
class FishingSpotValue : public ManualSetValue<WorldPosition>
|
||||
{
|
||||
public:
|
||||
FishingSpotValue(PlayerbotAI* botAI, WorldPosition const& pos = WorldPosition(), std::string const& name = "fishing spot")
|
||||
: ManualSetValue<WorldPosition>(botAI, pos, name) {}
|
||||
|
||||
void Set(WorldPosition val) override
|
||||
{
|
||||
value = val;
|
||||
_setTime = getMSTime();
|
||||
}
|
||||
uint32 lastUpdated() const {return _setTime;}
|
||||
bool IsStale(uint32 maxDuration) const { return getMSTime() - _setTime > maxDuration; }
|
||||
|
||||
private:
|
||||
uint32 _setTime = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "Action.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
#include "Timer.h"
|
||||
|
||||
Value<Unit*>* Action::GetTargetValue() { return context->GetValue<Unit*>(GetTargetName()); }
|
||||
|
||||
Unit* Action::GetTarget() { return GetTargetValue()->Get(); }
|
||||
|
||||
ActionBasket::ActionBasket(ActionNode* action, float relevance, bool skipPrerequisites, Event event)
|
||||
: action(action), relevance(relevance), skipPrerequisites(skipPrerequisites), event(event), created(getMSTime())
|
||||
{
|
||||
}
|
||||
|
||||
bool ActionBasket::isExpired(uint32_t msecs) { return getMSTime() - created >= msecs; }
|
||||
@@ -1,145 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "Strategy.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
class ActionNodeFactoryInternal : public NamedObjectFactory<ActionNode>
|
||||
{
|
||||
public:
|
||||
ActionNodeFactoryInternal()
|
||||
{
|
||||
creators["melee"] = &melee;
|
||||
creators["healthstone"] = &healthstone;
|
||||
creators["be near"] = &follow_master_random;
|
||||
creators["attack anything"] = &attack_anything;
|
||||
creators["move random"] = &move_random;
|
||||
creators["move to loot"] = &move_to_loot;
|
||||
creators["food"] = &food;
|
||||
creators["drink"] = &drink;
|
||||
creators["mana potion"] = &mana_potion;
|
||||
creators["healing potion"] = &healing_potion;
|
||||
creators["flee"] = &flee;
|
||||
}
|
||||
|
||||
private:
|
||||
static ActionNode* melee([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"melee",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* healthstone([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"healthstone",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("healing potion") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* follow_master_random([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"be near",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("follow") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* attack_anything([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"attack anything",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* move_random([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"move random",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("stay line") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* move_to_loot([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"move to loot",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* food([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"food",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* drink([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"drink",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* mana_potion([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"mana potion",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* healing_potion([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"healing potion",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("food") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* flee([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"flee",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
Strategy::Strategy(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
|
||||
{
|
||||
actionNodeFactories.Add(new ActionNodeFactoryInternal());
|
||||
}
|
||||
|
||||
ActionNode* Strategy::GetAction(std::string const name) { return actionNodeFactories.GetContextObject(name, botAI); }
|
||||
@@ -1,46 +0,0 @@
|
||||
#include "PlayerbotSpellRepository.h"
|
||||
|
||||
// caches the result set
|
||||
void PlayerbotSpellRepository::Initialize()
|
||||
{
|
||||
LOG_INFO("playerbots", "Playerbots: ListSpellsAction caches initialized");
|
||||
|
||||
for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j)
|
||||
{
|
||||
if (SkillLineAbilityEntry const* skillLine = sSkillLineAbilityStore.LookupEntry(j))
|
||||
skillSpells[skillLine->Spell] = skillLine;
|
||||
}
|
||||
|
||||
// Fill the vendorItems cache once from the world database.
|
||||
QueryResult results = WorldDatabase.Query("SELECT item FROM npc_vendor WHERE maxcount = 0");
|
||||
if (results)
|
||||
{
|
||||
do
|
||||
{
|
||||
Field* fields = results->Fetch();
|
||||
int32 entry = fields[0].Get<int32>();
|
||||
if (entry <= 0)
|
||||
continue;
|
||||
|
||||
vendorItems.insert(static_cast<uint32>(entry));
|
||||
}
|
||||
while (results->NextRow());
|
||||
}
|
||||
|
||||
LOG_DEBUG("playerbots",
|
||||
"ListSpellsAction: initialized caches (skillSpells={}, vendorItems={}).",
|
||||
skillSpells.size(), vendorItems.size());
|
||||
}
|
||||
|
||||
SkillLineAbilityEntry const* PlayerbotSpellRepository::GetSkillLine(uint32 spellId) const
|
||||
{
|
||||
auto itr = skillSpells.find(spellId);
|
||||
if (itr != skillSpells.end())
|
||||
return itr->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool PlayerbotSpellRepository::IsItemBuyable(uint32 itemId) const
|
||||
{
|
||||
return vendorItems.find(itemId) != vendorItems.end();
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_PLAYERBOTSPELLREPOSITORY_H
|
||||
#define _PLAYERBOT_PLAYERBOTSPELLREPOSITORY_H
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
class PlayerbotSpellRepository
|
||||
{
|
||||
public:
|
||||
static PlayerbotSpellRepository* Instance()
|
||||
{
|
||||
static PlayerbotSpellRepository instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
void Initialize(); // call once on startup
|
||||
|
||||
SkillLineAbilityEntry const* GetSkillLine(uint32 spellId) const;
|
||||
bool IsItemBuyable(uint32 itemId) const;
|
||||
|
||||
private:
|
||||
PlayerbotSpellRepository() = default;
|
||||
|
||||
std::map<uint32, SkillLineAbilityEntry const*> skillSpells;
|
||||
std::set<uint32> vendorItems;
|
||||
};
|
||||
|
||||
#define sPlayerbotSpellRepository PlayerbotSpellRepository::Instance()
|
||||
|
||||
#endif
|
||||
50
src/Helpers.cpp
Normal file
50
src/Helpers.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "Helpers.h"
|
||||
|
||||
char* strstri(char const* haystack, char const* needle)
|
||||
{
|
||||
if (!*needle)
|
||||
{
|
||||
return (char*)haystack;
|
||||
}
|
||||
|
||||
for (; *haystack; ++haystack)
|
||||
{
|
||||
if (tolower(*haystack) == tolower(*needle))
|
||||
{
|
||||
char const *h = haystack, *n = needle;
|
||||
for (; *h && *n; ++h, ++n)
|
||||
{
|
||||
if (tolower(*h) != tolower(*n))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!*n)
|
||||
{
|
||||
return (char*)haystack;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string& ltrim(std::string& s)
|
||||
{
|
||||
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int c) { return !std::isspace(c); }));
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string& rtrim(std::string& s)
|
||||
{
|
||||
s.erase(std::find_if(s.rbegin(), s.rend(), [](int c) { return !std::isspace(c); }).base(), s.end());
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string& trim(std::string& s) { return ltrim(rtrim(s)); }
|
||||
55
src/Helpers.h
Normal file
55
src/Helpers.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_HELPERS_H
|
||||
#define _PLAYERBOT_HELPERS_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <functional>
|
||||
#include <locale>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
void split(std::vector<std::string>& dest, std::string const str, char const* delim)
|
||||
{
|
||||
char* pTempStr = strdup(str.c_str());
|
||||
char* pWord = strtok(pTempStr, delim);
|
||||
|
||||
while (pWord != nullptr)
|
||||
{
|
||||
dest.push_back(pWord);
|
||||
pWord = strtok(nullptr, delim);
|
||||
}
|
||||
|
||||
free(pTempStr);
|
||||
}
|
||||
|
||||
std::vector<std::string>& split(std::string const s, char delim, std::vector<std::string>& elems)
|
||||
{
|
||||
std::stringstream ss(s);
|
||||
std::string item;
|
||||
|
||||
while (getline(ss, item, delim))
|
||||
{
|
||||
elems.push_back(item);
|
||||
}
|
||||
|
||||
return elems;
|
||||
}
|
||||
|
||||
std::vector<std::string> split(std::string const s, char delim)
|
||||
{
|
||||
std::vector<std::string> elems;
|
||||
return split(s, delim, elems);
|
||||
}
|
||||
|
||||
#endif
|
||||
39
src/LazyCalculatedValue.h
Normal file
39
src/LazyCalculatedValue.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_LAZYCALCULATEDVALUE_H
|
||||
#define _PLAYERBOT_LAZYCALCULATEDVALUE_H
|
||||
|
||||
template <class TValue, class TOwner>
|
||||
class LazyCalculatedValue
|
||||
{
|
||||
public:
|
||||
typedef TValue (TOwner::*Calculator)();
|
||||
|
||||
public:
|
||||
LazyCalculatedValue(TOwner* owner, Calculator calculator) : calculator(calculator), owner(owner) { Reset(); }
|
||||
|
||||
public:
|
||||
TValue GetValue()
|
||||
{
|
||||
if (!calculated)
|
||||
{
|
||||
value = (owner->*calculator)();
|
||||
calculated = true;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void Reset() { calculated = false; }
|
||||
|
||||
protected:
|
||||
Calculator calculator;
|
||||
TOwner* owner;
|
||||
bool calculated;
|
||||
TValue value;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,322 +0,0 @@
|
||||
#include "PlayerbotGuildMgr.h"
|
||||
#include "Player.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "Guild.h"
|
||||
#include "GuildMgr.h"
|
||||
#include "RandomPlayerbotMgr.h"
|
||||
#include "ScriptMgr.h"
|
||||
|
||||
PlayerbotGuildMgr::PlayerbotGuildMgr(){}
|
||||
|
||||
void PlayerbotGuildMgr::Init()
|
||||
{
|
||||
_guildCache.clear();
|
||||
if (sPlayerbotAIConfig->deleteRandomBotGuilds)
|
||||
DeleteBotGuilds();
|
||||
|
||||
LoadGuildNames();
|
||||
ValidateGuildCache();
|
||||
}
|
||||
|
||||
bool PlayerbotGuildMgr::CreateGuild(Player* player, std::string guildName)
|
||||
{
|
||||
Guild* guild = new Guild();
|
||||
if (!guild->Create(player, guildName))
|
||||
{
|
||||
LOG_ERROR("playerbots", "Error creating guild [ {} ] with leader [ {} ]", guildName,
|
||||
player->GetName());
|
||||
delete guild;
|
||||
return false;
|
||||
}
|
||||
sGuildMgr->AddGuild(guild);
|
||||
|
||||
LOG_DEBUG("playerbots", "Guild created: id={} name='{}'", guild->GetId(), guildName);
|
||||
SetGuildEmblem(guild->GetId());
|
||||
|
||||
GuildCache entry;
|
||||
entry.name = guildName;
|
||||
entry.memberCount = 1;
|
||||
entry.status = 1;
|
||||
entry.maxMembers = sPlayerbotAIConfig->randomBotGuildSizeMax;
|
||||
entry.faction = player->GetTeamId();
|
||||
|
||||
_guildCache[guild->GetId()] = entry;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PlayerbotGuildMgr::SetGuildEmblem(uint32 guildId)
|
||||
{
|
||||
Guild* guild = sGuildMgr->GetGuildById(guildId);
|
||||
if (!guild)
|
||||
return false;
|
||||
|
||||
// create random emblem
|
||||
uint32 st, cl, br, bc, bg;
|
||||
bg = urand(0, 51);
|
||||
bc = urand(0, 17);
|
||||
cl = urand(0, 17);
|
||||
br = urand(0, 7);
|
||||
st = urand(0, 180);
|
||||
|
||||
LOG_DEBUG("playerbots",
|
||||
"[TABARD] new guild id={} random -> style={}, color={}, borderStyle={}, borderColor={}, bgColor={}",
|
||||
guild->GetId(), st, cl, br, bc, bg);
|
||||
|
||||
// populate guild table with a random tabard design
|
||||
CharacterDatabase.Execute(
|
||||
"UPDATE guild SET EmblemStyle={}, EmblemColor={}, BorderStyle={}, BorderColor={}, BackgroundColor={} "
|
||||
"WHERE guildid={}",
|
||||
st, cl, br, bc, bg, guild->GetId());
|
||||
LOG_DEBUG("playerbots", "[TABARD] UPDATE done for guild id={}", guild->GetId());
|
||||
|
||||
// Immediate reading for log
|
||||
if (QueryResult qr = CharacterDatabase.Query(
|
||||
"SELECT EmblemStyle,EmblemColor,BorderStyle,BorderColor,BackgroundColor FROM guild WHERE guildid={}",
|
||||
guild->GetId()))
|
||||
{
|
||||
Field* f = qr->Fetch();
|
||||
LOG_DEBUG("playerbots",
|
||||
"[TABARD] DB check guild id={} => style={}, color={}, borderStyle={}, borderColor={}, bgColor={}",
|
||||
guild->GetId(), f[0].Get<uint8>(), f[1].Get<uint8>(), f[2].Get<uint8>(), f[3].Get<uint8>(), f[4].Get<uint8>());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string PlayerbotGuildMgr::AssignToGuild(Player* player)
|
||||
{
|
||||
if (!player)
|
||||
return "";
|
||||
|
||||
uint8_t playerFaction = player->GetTeamId();
|
||||
std::vector<GuildCache*> partiallyfilledguilds;
|
||||
partiallyfilledguilds.reserve(_guildCache.size());
|
||||
|
||||
for (auto& keyValue : _guildCache)
|
||||
{
|
||||
GuildCache& cached = keyValue.second;
|
||||
if (!cached.hasRealPlayer && cached.status == 1 && cached.faction == playerFaction)
|
||||
partiallyfilledguilds.push_back(&cached);
|
||||
}
|
||||
|
||||
if (!partiallyfilledguilds.empty())
|
||||
{
|
||||
size_t idx = static_cast<size_t>(urand(0, static_cast<int>(partiallyfilledguilds.size()) - 1));
|
||||
return (partiallyfilledguilds[idx]->name);
|
||||
}
|
||||
|
||||
size_t count = std::count_if(
|
||||
_guildCache.begin(), _guildCache.end(),
|
||||
[](const std::pair<const uint32, GuildCache>& pair)
|
||||
{
|
||||
return !pair.second.hasRealPlayer;
|
||||
}
|
||||
);
|
||||
|
||||
if (count < sPlayerbotAIConfig->randomBotGuildCount)
|
||||
{
|
||||
for (auto& key : _shuffled_guild_keys)
|
||||
{
|
||||
if (_guildNames[key])
|
||||
{
|
||||
LOG_INFO("playerbots","Assigning player [{}] to guild [{}]", player->GetName(), key);
|
||||
return key;
|
||||
}
|
||||
}
|
||||
LOG_ERROR("playerbots","No available guild names left.");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void PlayerbotGuildMgr::OnGuildUpdate(Guild* guild)
|
||||
{
|
||||
auto it = _guildCache.find(guild->GetId());
|
||||
if (it == _guildCache.end())
|
||||
return;
|
||||
|
||||
GuildCache& entry = it->second;
|
||||
entry.memberCount = guild->GetMemberCount();
|
||||
if (entry.memberCount < entry.maxMembers)
|
||||
entry.status = 1;
|
||||
else if (entry.memberCount >= entry.maxMembers)
|
||||
entry.status = 2; // Full
|
||||
std::string guildName = guild->GetName();
|
||||
for (auto& it : _guildNames)
|
||||
{
|
||||
if (it.first == guildName)
|
||||
{
|
||||
it.second = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerbotGuildMgr::ResetGuildCache()
|
||||
{
|
||||
for (auto it = _guildCache.begin(); it != _guildCache.end();)
|
||||
{
|
||||
GuildCache& cached = it->second;
|
||||
cached.memberCount = 0;
|
||||
cached.faction = 2;
|
||||
cached.status = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerbotGuildMgr::LoadGuildNames()
|
||||
{
|
||||
LOG_INFO("playerbots", "Loading guild names from playerbots_guild_names...");
|
||||
|
||||
QueryResult result = CharacterDatabase.Query("SELECT name_id, name FROM playerbots_guild_names");
|
||||
|
||||
if (!result)
|
||||
{
|
||||
LOG_ERROR("playerbots", "No entries found in playerbots_guild_names. List is empty.");
|
||||
return;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
_guildNames[fields[1].Get<std::string>()] = true;
|
||||
} while (result->NextRow());
|
||||
|
||||
for (auto& pair : _guildNames)
|
||||
_shuffled_guild_keys.push_back(pair.first);
|
||||
|
||||
std::random_device rd;
|
||||
std::mt19937 g(rd());
|
||||
|
||||
std::shuffle(_shuffled_guild_keys.begin(), _shuffled_guild_keys.end(), g);
|
||||
LOG_INFO("playerbots", "Loaded {} guild entries from playerbots_guild_names table.", _guildNames.size());
|
||||
}
|
||||
|
||||
void PlayerbotGuildMgr::ValidateGuildCache()
|
||||
{
|
||||
QueryResult result = CharacterDatabase.Query("SELECT guildid, name FROM guild");
|
||||
if (!result)
|
||||
{
|
||||
LOG_ERROR("playerbots", "No guilds found in database, resetting guild cache");
|
||||
ResetGuildCache();
|
||||
return;
|
||||
}
|
||||
|
||||
std::unordered_map<uint32, std::string> dbGuilds;
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
uint32 guildId = fields[0].Get<uint32>();
|
||||
std::string guildName = fields[1].Get<std::string>();
|
||||
dbGuilds[guildId] = guildName;
|
||||
} while (result->NextRow());
|
||||
|
||||
for (auto it = dbGuilds.begin(); it != dbGuilds.end(); it++)
|
||||
{
|
||||
uint32 guildId = it->first;
|
||||
GuildCache cache;
|
||||
cache.name = it->second;
|
||||
cache.maxMembers = sPlayerbotAIConfig->randomBotGuildSizeMax;
|
||||
|
||||
Guild* guild = sGuildMgr ->GetGuildById(guildId);
|
||||
if (!guild)
|
||||
continue;
|
||||
|
||||
cache.memberCount = guild->GetMemberCount();
|
||||
ObjectGuid leaderGuid = guild->GetLeaderGUID();
|
||||
CharacterCacheEntry const* leaderEntry = sCharacterCache->GetCharacterCacheByGuid(leaderGuid);
|
||||
uint32 leaderAccount = leaderEntry->AccountId;
|
||||
cache.hasRealPlayer = !(sPlayerbotAIConfig->IsInRandomAccountList(leaderAccount));
|
||||
cache.faction = Player::TeamIdForRace(leaderEntry->Race);
|
||||
if (cache.memberCount == 0)
|
||||
cache.status = 0; // empty
|
||||
else if (cache.memberCount < cache.maxMembers)
|
||||
cache.status = 1; // partially filled
|
||||
else
|
||||
cache.status = 2; // full
|
||||
|
||||
_guildCache.insert_or_assign(guildId, cache);
|
||||
for (auto& it : _guildNames)
|
||||
{
|
||||
if (it.first == cache.name)
|
||||
{
|
||||
it.second = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerbotGuildMgr::DeleteBotGuilds()
|
||||
{
|
||||
LOG_INFO("playerbots", "Deleting random bot guilds...");
|
||||
std::vector<uint32> randomBots;
|
||||
|
||||
PlayerbotsDatabasePreparedStatement* stmt = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_SEL_RANDOM_BOTS_BOT);
|
||||
stmt->SetData(0, "add");
|
||||
if (PreparedQueryResult result = PlayerbotsDatabase.Query(stmt))
|
||||
{
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
uint32 bot = fields[0].Get<uint32>();
|
||||
randomBots.push_back(bot);
|
||||
} while (result->NextRow());
|
||||
}
|
||||
|
||||
for (std::vector<uint32>::iterator i = randomBots.begin(); i != randomBots.end(); ++i)
|
||||
{
|
||||
if (Guild* guild = sGuildMgr->GetGuildByLeader(ObjectGuid::Create<HighGuid::Player>(*i)))
|
||||
guild->Disband();
|
||||
}
|
||||
LOG_INFO("playerbots", "Random bot guilds deleted");
|
||||
}
|
||||
|
||||
bool PlayerbotGuildMgr::IsRealGuild(Player* bot)
|
||||
{
|
||||
if (!bot)
|
||||
return false;
|
||||
uint32 guildId = bot->GetGuildId();
|
||||
if (!guildId)
|
||||
return false;
|
||||
|
||||
return IsRealGuild(guildId);
|
||||
}
|
||||
|
||||
bool PlayerbotGuildMgr::IsRealGuild(uint32 guildId)
|
||||
{
|
||||
if (!guildId)
|
||||
return false;
|
||||
|
||||
auto it = _guildCache.find(guildId);
|
||||
if (it == _guildCache.end())
|
||||
return false;
|
||||
|
||||
return it->second.hasRealPlayer;
|
||||
}
|
||||
|
||||
class BotGuildCacheWorldScript : public WorldScript
|
||||
{
|
||||
public:
|
||||
|
||||
BotGuildCacheWorldScript() : WorldScript("BotGuildCacheWorldScript"), _validateTimer(0){}
|
||||
|
||||
void OnUpdate(uint32 diff) override
|
||||
{
|
||||
_validateTimer += diff;
|
||||
|
||||
if (_validateTimer >= _validateInterval) // Validate every hour
|
||||
{
|
||||
_validateTimer = 0;
|
||||
sPlayerbotGuildMgr->ValidateGuildCache();
|
||||
LOG_INFO("playerbots", "Scheduled guild cache validation");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
uint32 _validateInterval = HOUR*IN_MILLISECONDS;
|
||||
uint32 _validateTimer;
|
||||
};
|
||||
|
||||
void PlayerBotsGuildValidationScript()
|
||||
{
|
||||
new BotGuildCacheWorldScript();
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
#ifndef _PLAYERBOT_PLAYERBOTGUILDMGR_H
|
||||
#define _PLAYERBOT_PLAYERBOTGUILDMGR_H
|
||||
|
||||
#include "Guild.h"
|
||||
#include "Player.h"
|
||||
#include "PlayerbotAI.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class PlayerbotGuildMgr
|
||||
{
|
||||
public:
|
||||
static PlayerbotGuildMgr* instance()
|
||||
{
|
||||
static PlayerbotGuildMgr instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
void Init();
|
||||
std::string AssignToGuild(Player* player);
|
||||
void LoadGuildNames();
|
||||
void ValidateGuildCache();
|
||||
void ResetGuildCache();
|
||||
bool CreateGuild(Player* player, std::string guildName);
|
||||
void OnGuildUpdate (Guild* guild);
|
||||
bool SetGuildEmblem(uint32 guildId);
|
||||
void DeleteBotGuilds();
|
||||
bool IsRealGuild(uint32 guildId);
|
||||
bool IsRealGuild(Player* bot);
|
||||
|
||||
private:
|
||||
PlayerbotGuildMgr();
|
||||
std::unordered_map<std::string, bool> _guildNames;
|
||||
|
||||
struct GuildCache
|
||||
{
|
||||
std::string name;
|
||||
uint8 status;
|
||||
uint32 maxMembers = 0;
|
||||
uint32 memberCount = 0;
|
||||
uint8 faction = 0;
|
||||
bool hasRealPlayer = false;
|
||||
};
|
||||
std::unordered_map<uint32 , GuildCache> _guildCache;
|
||||
std::vector<std::string> _shuffled_guild_keys;
|
||||
};
|
||||
|
||||
void PlayerBotsGuildValidationScript();
|
||||
|
||||
#define sPlayerbotGuildMgr PlayerbotGuildMgr::instance()
|
||||
|
||||
#endif
|
||||
@@ -3,11 +3,11 @@
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "PerfMonitor.h"
|
||||
#include "PerformanceMonitor.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
PerfMonitorOperation* PerfMonitor::start(PerformanceMetric metric, std::string const name,
|
||||
PerformanceMonitorOperation* PerformanceMonitor::start(PerformanceMetric metric, std::string const name,
|
||||
PerformanceStack* stack)
|
||||
{
|
||||
if (!sPlayerbotAIConfig->perfMonEnabled)
|
||||
@@ -45,10 +45,10 @@ PerfMonitorOperation* PerfMonitor::start(PerformanceMetric metric, std::string c
|
||||
data[metric][stackName] = pd;
|
||||
}
|
||||
|
||||
return new PerfMonitorOperation(pd, name, stack);
|
||||
return new PerformanceMonitorOperation(pd, name, stack);
|
||||
}
|
||||
|
||||
void PerfMonitor::PrintStats(bool perTick, bool fullStack)
|
||||
void PerformanceMonitor::PrintStats(bool perTick, bool fullStack)
|
||||
{
|
||||
if (data.empty())
|
||||
return;
|
||||
@@ -247,7 +247,7 @@ void PerfMonitor::PrintStats(bool perTick, bool fullStack)
|
||||
}
|
||||
}
|
||||
|
||||
void PerfMonitor::Reset()
|
||||
void PerformanceMonitor::Reset()
|
||||
{
|
||||
for (std::map<PerformanceMetric, std::map<std::string, PerformanceData*>>::iterator i = data.begin();
|
||||
i != data.end(); ++i)
|
||||
@@ -265,7 +265,7 @@ void PerfMonitor::Reset()
|
||||
}
|
||||
}
|
||||
|
||||
PerfMonitorOperation::PerfMonitorOperation(PerformanceData* data, std::string const name,
|
||||
PerformanceMonitorOperation::PerformanceMonitorOperation(PerformanceData* data, std::string const name,
|
||||
PerformanceStack* stack)
|
||||
: data(data), name(name), stack(stack)
|
||||
{
|
||||
@@ -273,7 +273,7 @@ PerfMonitorOperation::PerfMonitorOperation(PerformanceData* data, std::string co
|
||||
.time_since_epoch();
|
||||
}
|
||||
|
||||
void PerfMonitorOperation::finish()
|
||||
void PerformanceMonitorOperation::finish()
|
||||
{
|
||||
std::chrono::microseconds finished =
|
||||
(std::chrono::time_point_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now()))
|
||||
@@ -34,10 +34,10 @@ enum PerformanceMetric
|
||||
PERF_MON_TOTAL
|
||||
};
|
||||
|
||||
class PerfMonitorOperation
|
||||
class PerformanceMonitorOperation
|
||||
{
|
||||
public:
|
||||
PerfMonitorOperation(PerformanceData* data, std::string const name, PerformanceStack* stack);
|
||||
PerformanceMonitorOperation(PerformanceData* data, std::string const name, PerformanceStack* stack);
|
||||
void finish();
|
||||
|
||||
private:
|
||||
@@ -47,19 +47,19 @@ private:
|
||||
std::chrono::microseconds started;
|
||||
};
|
||||
|
||||
class PerfMonitor
|
||||
class PerformanceMonitor
|
||||
{
|
||||
public:
|
||||
PerfMonitor(){};
|
||||
virtual ~PerfMonitor(){};
|
||||
static PerfMonitor* instance()
|
||||
PerformanceMonitor(){};
|
||||
virtual ~PerformanceMonitor(){};
|
||||
static PerformanceMonitor* instance()
|
||||
{
|
||||
static PerfMonitor instance;
|
||||
static PerformanceMonitor instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
public:
|
||||
PerfMonitorOperation* start(PerformanceMetric metric, std::string const name,
|
||||
PerformanceMonitorOperation* start(PerformanceMetric metric, std::string const name,
|
||||
PerformanceStack* stack = nullptr);
|
||||
void PrintStats(bool perTick = false, bool fullStack = false);
|
||||
void Reset();
|
||||
@@ -69,6 +69,6 @@ private:
|
||||
std::mutex lock;
|
||||
};
|
||||
|
||||
#define sPerfMonitor PerfMonitor::instance()
|
||||
#define sPerformanceMonitor PerformanceMonitor::instance()
|
||||
|
||||
#endif
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
#include "Common.h"
|
||||
#include "Player.h"
|
||||
#include "PlayerbotDungeonRepository.h"
|
||||
#include "PlayerbotDungeonSuggestionMgr.h"
|
||||
|
||||
typedef std::map<std::string, std::string> PlaceholderMap;
|
||||
|
||||
@@ -38,12 +38,11 @@
|
||||
#include "NewRpgStrategy.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "PerfMonitor.h"
|
||||
#include "PerformanceMonitor.h"
|
||||
#include "Player.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "PlayerbotRepository.h"
|
||||
#include "PlayerbotDbStore.h"
|
||||
#include "PlayerbotMgr.h"
|
||||
#include "PlayerbotGuildMgr.h"
|
||||
#include "Playerbots.h"
|
||||
#include "PointMovementGenerator.h"
|
||||
#include "PositionValue.h"
|
||||
@@ -243,8 +242,8 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal)
|
||||
nextAICheckDelay = 0;
|
||||
|
||||
// Early return if bot is in invalid state
|
||||
if (!bot || !bot->GetSession() || !bot->IsInWorld() || bot->IsBeingTeleported() ||
|
||||
bot->GetSession()->isLogingOut() || bot->IsDuringRemoveFromWorld())
|
||||
if (!bot || !bot->IsInWorld() || !bot->GetSession() || bot->GetSession()->isLogingOut() ||
|
||||
bot->IsDuringRemoveFromWorld())
|
||||
return;
|
||||
|
||||
// Handle cheat options (set bot health and power if cheats are enabled)
|
||||
@@ -366,7 +365,7 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal)
|
||||
}
|
||||
|
||||
// Update the bot's group status (moved to helper function)
|
||||
UpdateAIGroupMaster();
|
||||
UpdateAIGroupAndMaster();
|
||||
|
||||
// Update internal AI
|
||||
UpdateAIInternal(elapsed, minimal);
|
||||
@@ -374,7 +373,7 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal)
|
||||
}
|
||||
|
||||
// Helper function for UpdateAI to check group membership and handle removal if necessary
|
||||
void PlayerbotAI::UpdateAIGroupMaster()
|
||||
void PlayerbotAI::UpdateAIGroupAndMaster()
|
||||
{
|
||||
if (!bot)
|
||||
return;
|
||||
@@ -421,7 +420,7 @@ void PlayerbotAI::UpdateAIGroupMaster()
|
||||
{
|
||||
botAI->ChangeStrategy("+follow", BOT_STATE_NON_COMBAT);
|
||||
|
||||
if (botAI->GetMaster() == botAI->GetGroupLeader())
|
||||
if (botAI->GetMaster() == botAI->GetGroupMaster())
|
||||
botAI->TellMaster("Hello, I follow you!");
|
||||
else
|
||||
botAI->TellMaster(!urand(0, 2) ? "Hello!" : "Hi!");
|
||||
@@ -432,17 +431,19 @@ void PlayerbotAI::UpdateAIGroupMaster()
|
||||
botAI->ChangeStrategy("-follow", BOT_STATE_NON_COMBAT);
|
||||
}
|
||||
}
|
||||
else if (!newMaster && !bot->InBattleground())
|
||||
LeaveOrDisbandGroup();
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerbotAI::UpdateAIInternal([[maybe_unused]] uint32 elapsed, bool minimal)
|
||||
{
|
||||
if (!bot || bot->IsBeingTeleported() || !bot->IsInWorld())
|
||||
if (bot->IsBeingTeleported() || !bot->IsInWorld())
|
||||
return;
|
||||
|
||||
std::string const mapString = WorldPosition(bot).isOverworld() ? std::to_string(bot->GetMapId()) : "I";
|
||||
PerfMonitorOperation* pmo =
|
||||
sPerfMonitor->start(PERF_MON_TOTAL, "PlayerbotAI::UpdateAIInternal " + mapString);
|
||||
PerformanceMonitorOperation* pmo =
|
||||
sPerformanceMonitor->start(PERF_MON_TOTAL, "PlayerbotAI::UpdateAIInternal " + mapString);
|
||||
ExternalEventHelper helper(aiObjectContext);
|
||||
|
||||
// chat replies
|
||||
@@ -516,37 +517,23 @@ void PlayerbotAI::UpdateAIInternal([[maybe_unused]] uint32 elapsed, bool minimal
|
||||
void PlayerbotAI::HandleCommands()
|
||||
{
|
||||
ExternalEventHelper helper(aiObjectContext);
|
||||
|
||||
for (auto it = chatCommands.begin(); it != chatCommands.end();)
|
||||
{
|
||||
time_t& checkTime = it->GetTime();
|
||||
if (checkTime && time(nullptr) < checkTime)
|
||||
if (checkTime && time(0) < checkTime)
|
||||
{
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
Player* owner = it->GetOwner();
|
||||
if (!owner)
|
||||
{
|
||||
it = chatCommands.erase(it);
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string& command = it->GetCommand();
|
||||
if (command.empty())
|
||||
{
|
||||
it = chatCommands.erase(it);
|
||||
continue;
|
||||
}
|
||||
|
||||
Player* owner = it->GetOwner();
|
||||
if (!helper.ParseChatCommand(command, owner) && it->GetType() == CHAT_MSG_WHISPER)
|
||||
{
|
||||
// ostringstream out; out << "Unknown command " << command;
|
||||
// TellPlayer(out);
|
||||
// helper.ParseChatCommand("help");
|
||||
}
|
||||
|
||||
it = chatCommands.erase(it);
|
||||
}
|
||||
}
|
||||
@@ -554,9 +541,6 @@ void PlayerbotAI::HandleCommands()
|
||||
std::map<std::string, ChatMsg> chatMap;
|
||||
void PlayerbotAI::HandleCommand(uint32 type, const std::string& text, Player& fromPlayer, const uint32 lang)
|
||||
{
|
||||
if (!bot)
|
||||
return;
|
||||
|
||||
std::string filtered = text;
|
||||
|
||||
if (!IsAllowedCommand(filtered) && !GetSecurity()->CheckLevelFor(PlayerbotSecurityLevel::PLAYERBOT_SECURITY_INVITE,
|
||||
@@ -728,82 +712,45 @@ void PlayerbotAI::HandleCommand(uint32 type, const std::string& text, Player& fr
|
||||
|
||||
void PlayerbotAI::HandleTeleportAck()
|
||||
{
|
||||
if (!bot || !bot->GetSession())
|
||||
return;
|
||||
|
||||
// only for bots
|
||||
if (IsRealPlayer())
|
||||
return;
|
||||
|
||||
/*
|
||||
* FAR TELEPORT (worldport / map change)
|
||||
* Player may NOT be in world or grid here.
|
||||
* Handle this FIRST.
|
||||
*/
|
||||
if (bot->IsBeingTeleportedFar())
|
||||
{
|
||||
bot->GetSession()->HandleMoveWorldportAck();
|
||||
|
||||
// after worldport ACK the player should be in a valid map
|
||||
if (!bot->GetMap())
|
||||
{
|
||||
LOG_ERROR("playerbot", "Bot {} has no map after worldport ACK", bot->GetGUID().ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
// apply instance-related strategies after map attach
|
||||
if (sPlayerbotAIConfig->applyInstanceStrategies)
|
||||
ApplyInstanceStrategies(bot->GetMapId(), true);
|
||||
|
||||
if (sPlayerbotAIConfig->restrictHealerDPS)
|
||||
EvaluateHealerDpsStrategy();
|
||||
|
||||
// reset AI state after teleport
|
||||
Reset(true);
|
||||
|
||||
// clear movement only AFTER teleport is finalized and bot is in world
|
||||
if (bot->IsInWorld() && bot->GetMotionMaster())
|
||||
{
|
||||
bot->GetMotionMaster()->Clear(true);
|
||||
bot->StopMoving();
|
||||
}
|
||||
|
||||
// simulate far teleport latency (cmangos-style)
|
||||
SetNextCheckDelay(urand(2000, 5000));
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* NEAR TELEPORT (same map / instance)
|
||||
* Player MUST be in world (and in grid).
|
||||
*/
|
||||
bot->GetMotionMaster()->Clear(true);
|
||||
bot->StopMoving();
|
||||
if (bot->IsBeingTeleportedNear())
|
||||
{
|
||||
// Temporary fix for instance can not enter
|
||||
if (!bot->IsInWorld())
|
||||
return;
|
||||
|
||||
Player* plMover = bot->m_mover ? bot->m_mover->ToPlayer() : nullptr;
|
||||
if (!plMover)
|
||||
return;
|
||||
|
||||
WorldPacket p(MSG_MOVE_TELEPORT_ACK, 20);
|
||||
p << plMover->GetPackGUID();
|
||||
p << uint32(0); // flags
|
||||
p << uint32(0); // time
|
||||
|
||||
bot->GetSession()->HandleMoveTeleportAck(p);
|
||||
|
||||
// clear movement after successful relocation
|
||||
if (bot->GetMotionMaster())
|
||||
{
|
||||
bot->GetMotionMaster()->Clear(true);
|
||||
bot->StopMoving();
|
||||
bot->GetMap()->AddPlayerToMap(bot);
|
||||
}
|
||||
|
||||
// simulate near teleport latency
|
||||
SetNextCheckDelay(urand(1000, 2000));
|
||||
return;
|
||||
while (bot->IsInWorld() && bot->IsBeingTeleportedNear())
|
||||
{
|
||||
Player* plMover = bot->m_mover->ToPlayer();
|
||||
if (!plMover)
|
||||
return;
|
||||
WorldPacket p = WorldPacket(MSG_MOVE_TELEPORT_ACK, 20);
|
||||
p << plMover->GetPackGUID();
|
||||
p << (uint32)0; // supposed to be flags? not used currently
|
||||
p << (uint32)0; // time - not currently used
|
||||
bot->GetSession()->HandleMoveTeleportAck(p);
|
||||
};
|
||||
}
|
||||
if (bot->IsBeingTeleportedFar())
|
||||
{
|
||||
while (bot->IsBeingTeleportedFar())
|
||||
{
|
||||
bot->GetSession()->HandleMoveWorldportAck();
|
||||
}
|
||||
// SetNextCheckDelay(urand(2000, 5000));
|
||||
if (sPlayerbotAIConfig->applyInstanceStrategies)
|
||||
ApplyInstanceStrategies(bot->GetMapId(), true);
|
||||
if (sPlayerbotAIConfig->restrictHealerDPS)
|
||||
EvaluateHealerDpsStrategy();
|
||||
Reset(true);
|
||||
}
|
||||
|
||||
SetNextCheckDelay(sPlayerbotAIConfig->globalCoolDown);
|
||||
}
|
||||
|
||||
void PlayerbotAI::Reset(bool full)
|
||||
@@ -965,6 +912,7 @@ void PlayerbotAI::HandleCommand(uint32 type, std::string const text, Player* fro
|
||||
fromPlayer->SendDirectMessage(&data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsAllowedCommand(filtered) &&
|
||||
(!GetSecurity()->CheckLevelFor(PLAYERBOT_SECURITY_ALLOW_ALL, type != CHAT_MSG_WHISPER, fromPlayer)))
|
||||
return;
|
||||
@@ -1042,10 +990,10 @@ void PlayerbotAI::HandleBotOutgoingPacket(WorldPacket const& packet)
|
||||
{
|
||||
if (packet.empty())
|
||||
return;
|
||||
|
||||
if (!bot || !bot->IsInWorld() || bot->IsDuringRemoveFromWorld())
|
||||
{
|
||||
return;
|
||||
|
||||
}
|
||||
switch (packet.GetOpcode())
|
||||
{
|
||||
case SMSG_SPELL_FAILURE:
|
||||
@@ -1213,26 +1161,7 @@ void PlayerbotAI::HandleBotOutgoingPacket(WorldPacket const& packet)
|
||||
|
||||
return;
|
||||
}
|
||||
case SMSG_FORCE_MOVE_ROOT: // CMSG_FORCE_MOVE_ROOT_ACK
|
||||
case SMSG_FORCE_MOVE_UNROOT: // CMSG_FORCE_MOVE_UNROOT_ACK
|
||||
{
|
||||
// Quick fix for CMSG_FORCE_MOVE_ROOT_ACK and CMSG_FORCE_MOVE_UNROOT_ACK:
|
||||
// this should resolve issues with MOVEMENTFLAG_ROOT being permanently set
|
||||
// when rooted during lost client control (charm + root effects)
|
||||
// @see https://github.com/azerothcore/azerothcore-wotlk/pull/23147
|
||||
bool forceRoot = (packet.GetOpcode() == SMSG_FORCE_MOVE_ROOT);
|
||||
if (forceRoot)
|
||||
{
|
||||
bot->m_movementInfo.RemoveMovementFlag(MOVEMENTFLAG_MASK_MOVING_FLY);
|
||||
bot->m_movementInfo.AddMovementFlag(MOVEMENTFLAG_ROOT);
|
||||
bot->StopMoving();
|
||||
}
|
||||
else
|
||||
bot->m_movementInfo.RemoveMovementFlag(MOVEMENTFLAG_ROOT);
|
||||
|
||||
return;
|
||||
}
|
||||
case SMSG_MOVE_KNOCK_BACK: // CMSG_MOVE_KNOCK_BACK_ACK
|
||||
case SMSG_MOVE_KNOCK_BACK: // handle knockbacks
|
||||
{
|
||||
WorldPacket p(packet);
|
||||
p.rpos(0);
|
||||
@@ -1320,12 +1249,7 @@ void PlayerbotAI::SpellInterrupted(uint32 spellid)
|
||||
Spell* spell = bot->GetCurrentSpell((CurrentSpellTypes)type);
|
||||
if (!spell)
|
||||
continue;
|
||||
|
||||
SpellInfo const* spellInfo = spell->GetSpellInfo();
|
||||
if (!spellInfo)
|
||||
continue;
|
||||
|
||||
if (spellInfo->Id == spellid)
|
||||
if (spell->GetSpellInfo()->Id == spellid)
|
||||
bot->InterruptSpell((CurrentSpellTypes)type);
|
||||
}
|
||||
// LastSpellCast& lastSpell = aiObjectContext->GetValue<LastSpellCast&>("last spell cast")->Get();
|
||||
@@ -1409,6 +1333,10 @@ void PlayerbotAI::DoNextAction(bool min)
|
||||
bool isBotAlive = bot->IsAlive();
|
||||
if (currentEngine != engines[BOT_STATE_DEAD] && !isBotAlive)
|
||||
{
|
||||
bot->StopMoving();
|
||||
bot->GetMotionMaster()->Clear();
|
||||
bot->GetMotionMaster()->MoveIdle();
|
||||
|
||||
// Death Count to prevent skeleton piles
|
||||
// Player* master = GetMaster(); // warning here - whipowill
|
||||
if (!HasActivePlayerMaster() && !bot->InBattleground())
|
||||
@@ -1426,11 +1354,9 @@ void PlayerbotAI::DoNextAction(bool min)
|
||||
return;
|
||||
}
|
||||
|
||||
// Change engine if just ressed (no movement update when rooted)
|
||||
if (currentEngine == engines[BOT_STATE_DEAD] && isBotAlive && !bot->IsRooted())
|
||||
// Change engine if just ressed
|
||||
if (currentEngine == engines[BOT_STATE_DEAD] && isBotAlive)
|
||||
{
|
||||
bot->SendMovementFlagUpdate();
|
||||
|
||||
ChangeEngine(BOT_STATE_NON_COMBAT);
|
||||
return;
|
||||
}
|
||||
@@ -1460,6 +1386,9 @@ void PlayerbotAI::DoNextAction(bool min)
|
||||
else if (bot->isAFK())
|
||||
bot->ToggleAFK();
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
PlayerbotAI* masterBotAI = nullptr;
|
||||
|
||||
if (master && master->IsInWorld())
|
||||
{
|
||||
float distance = sServerFacade->GetDistance2d(bot, master);
|
||||
@@ -1532,7 +1461,7 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
|
||||
strategyName = "onyxia"; // Onyxia's Lair
|
||||
break;
|
||||
case 409:
|
||||
strategyName = "moltencore"; // Molten Core
|
||||
strategyName = "mc"; // Molten Core
|
||||
break;
|
||||
case 469:
|
||||
strategyName = "bwl"; // Blackwing Lair
|
||||
@@ -1543,9 +1472,11 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
|
||||
case 532:
|
||||
strategyName = "karazhan"; // Karazhan
|
||||
break;
|
||||
case 533:
|
||||
strategyName = "naxx"; // Naxxramas
|
||||
break;
|
||||
case 544:
|
||||
strategyName = "magtheridon"; // Magtheridon's Lair
|
||||
break;
|
||||
case 565:
|
||||
strategyName = "gruulslair"; // Gruul's Lair
|
||||
break;
|
||||
@@ -1731,7 +1662,7 @@ void PlayerbotAI::ResetStrategies(bool load)
|
||||
engines[i]->Init();
|
||||
|
||||
// if (load)
|
||||
// sPlayerbotRepository->Load(this);
|
||||
// sPlayerbotDbStore->Load(this);
|
||||
}
|
||||
|
||||
bool PlayerbotAI::IsRanged(Player* player, bool bySpec)
|
||||
@@ -1767,7 +1698,6 @@ bool PlayerbotAI::IsRanged(Player* player, bool bySpec)
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1861,9 +1791,10 @@ bool PlayerbotAI::IsRangedDpsAssistantOfIndex(Player* player, int index)
|
||||
|
||||
bool PlayerbotAI::HasAggro(Unit* unit)
|
||||
{
|
||||
if (!IsValidUnit(unit))
|
||||
if (!unit)
|
||||
{
|
||||
return false;
|
||||
|
||||
}
|
||||
bool isMT = IsMainTank(bot);
|
||||
Unit* victim = unit->GetVictim();
|
||||
if (victim && (victim->GetGUID() == bot->GetGUID() || (!isMT && victim->ToPlayer() && IsTank(victim->ToPlayer()))))
|
||||
@@ -2071,7 +2002,7 @@ bool PlayerbotAI::IsTank(Player* player, bool bySpec)
|
||||
switch (player->getClass())
|
||||
{
|
||||
case CLASS_DEATH_KNIGHT:
|
||||
if (tab == DEATH_KNIGHT_TAB_BLOOD)
|
||||
if (tab == DEATHKNIGHT_TAB_BLOOD)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -2179,7 +2110,7 @@ bool PlayerbotAI::IsDps(Player* player, bool bySpec)
|
||||
}
|
||||
break;
|
||||
case CLASS_DEATH_KNIGHT:
|
||||
if (tab != DEATH_KNIGHT_TAB_BLOOD)
|
||||
if (tab != DEATHKNIGHT_TAB_BLOOD)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -2317,7 +2248,7 @@ uint32 PlayerbotAI::GetGroupTankNum(Player* player)
|
||||
|
||||
bool PlayerbotAI::IsAssistTank(Player* player) { return IsTank(player) && !IsMainTank(player); }
|
||||
|
||||
bool PlayerbotAI::IsAssistTankOfIndex(Player* player, int index, bool ignoreDeadPlayers)
|
||||
bool PlayerbotAI::IsAssistTankOfIndex(Player* player, int index)
|
||||
{
|
||||
Group* group = player->GetGroup();
|
||||
if (!group)
|
||||
@@ -2334,9 +2265,6 @@ bool PlayerbotAI::IsAssistTankOfIndex(Player* player, int index, bool ignoreDead
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ignoreDeadPlayers && !member->IsAlive())
|
||||
continue;
|
||||
|
||||
if (group->IsAssistant(member->GetGUID()) && IsAssistTank(member))
|
||||
{
|
||||
if (index == counter)
|
||||
@@ -2356,9 +2284,6 @@ bool PlayerbotAI::IsAssistTankOfIndex(Player* player, int index, bool ignoreDead
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ignoreDeadPlayers && !member->IsAlive())
|
||||
continue;
|
||||
|
||||
if (!group->IsAssistant(member->GetGUID()) && IsAssistTank(member))
|
||||
{
|
||||
if (index == counter)
|
||||
@@ -2839,12 +2764,7 @@ bool PlayerbotAI::TellMaster(std::ostringstream& stream, PlayerbotSecurityLevel
|
||||
|
||||
bool PlayerbotAI::TellMaster(std::string const text, PlayerbotSecurityLevel securityLevel)
|
||||
{
|
||||
if (!master)
|
||||
{
|
||||
if (sPlayerbotAIConfig->randomBotSayWithoutMaster)
|
||||
return TellMasterNoFacing(text, securityLevel);
|
||||
}
|
||||
if (!TellMasterNoFacing(text, securityLevel))
|
||||
if (!master || !TellMasterNoFacing(text, securityLevel))
|
||||
return false;
|
||||
|
||||
if (!bot->isMoving() && !bot->IsInCombat() && bot->GetMapId() == master->GetMapId() &&
|
||||
@@ -2861,9 +2781,6 @@ bool PlayerbotAI::TellMaster(std::string const text, PlayerbotSecurityLevel secu
|
||||
|
||||
bool IsRealAura(Player* bot, AuraEffect const* aurEff, Unit const* unit)
|
||||
{
|
||||
if (!unit || !unit->IsInWorld() || unit->IsDuringRemoveFromWorld())
|
||||
return false;
|
||||
|
||||
if (!aurEff)
|
||||
return false;
|
||||
|
||||
@@ -2871,8 +2788,6 @@ bool IsRealAura(Player* bot, AuraEffect const* aurEff, Unit const* unit)
|
||||
return true;
|
||||
|
||||
SpellInfo const* spellInfo = aurEff->GetSpellInfo();
|
||||
if (!spellInfo)
|
||||
return false;
|
||||
|
||||
uint32 stacks = aurEff->GetBase()->GetStackAmount();
|
||||
if (stacks >= spellInfo->StackAmount)
|
||||
@@ -2888,7 +2803,7 @@ bool IsRealAura(Player* bot, AuraEffect const* aurEff, Unit const* unit)
|
||||
bool PlayerbotAI::HasAura(std::string const name, Unit* unit, bool maxStack, bool checkIsOwner, int maxAuraAmount,
|
||||
bool checkDuration)
|
||||
{
|
||||
if (!IsValidUnit(unit))
|
||||
if (!unit)
|
||||
return false;
|
||||
|
||||
std::wstring wnamepart;
|
||||
@@ -2984,7 +2899,7 @@ bool PlayerbotAI::HasAura(uint32 spellId, Unit const* unit)
|
||||
|
||||
Aura* PlayerbotAI::GetAura(std::string const name, Unit* unit, bool checkIsOwner, bool checkDuration, int checkStack)
|
||||
{
|
||||
if (!IsValidUnit(unit))
|
||||
if (!unit)
|
||||
return nullptr;
|
||||
|
||||
std::wstring wnamepart;
|
||||
@@ -3002,9 +2917,6 @@ Aura* PlayerbotAI::GetAura(std::string const name, Unit* unit, bool checkIsOwner
|
||||
for (AuraEffect const* aurEff : auras)
|
||||
{
|
||||
SpellInfo const* spellInfo = aurEff->GetSpellInfo();
|
||||
if (!spellInfo)
|
||||
continue;
|
||||
|
||||
std::string const& auraName = spellInfo->SpellName[0];
|
||||
|
||||
// Directly skip if name mismatch (both length and content)
|
||||
@@ -3085,9 +2997,6 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell,
|
||||
if (!target)
|
||||
target = bot;
|
||||
|
||||
if (!IsValidUnit(target))
|
||||
return false;
|
||||
|
||||
if (Pet* pet = bot->GetPet())
|
||||
if (pet->HasSpell(spellid))
|
||||
return true;
|
||||
@@ -3349,9 +3258,6 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, float x, float y, float z, bool c
|
||||
|
||||
bool PlayerbotAI::CastSpell(std::string const name, Unit* target, Item* itemTarget)
|
||||
{
|
||||
if (!IsValidUnit(target))
|
||||
return false;
|
||||
|
||||
bool result = CastSpell(aiObjectContext->GetValue<uint32>("spell id", name)->Get(), target, itemTarget);
|
||||
if (result)
|
||||
{
|
||||
@@ -3364,19 +3270,15 @@ bool PlayerbotAI::CastSpell(std::string const name, Unit* target, Item* itemTarg
|
||||
bool PlayerbotAI::CastSpell(uint32 spellId, Unit* target, Item* itemTarget)
|
||||
{
|
||||
if (!spellId)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!target)
|
||||
target = bot;
|
||||
|
||||
if (!IsValidUnit(target))
|
||||
return false;
|
||||
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||
if (!spellInfo)
|
||||
return false;
|
||||
|
||||
Pet* pet = bot->GetPet();
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||
if (pet && pet->HasSpell(spellId))
|
||||
{
|
||||
// List of spell IDs for which we do NOT want to toggle auto-cast or send message
|
||||
@@ -3779,9 +3681,6 @@ bool PlayerbotAI::CanCastVehicleSpell(uint32 spellId, Unit* target)
|
||||
if (!spellId)
|
||||
return false;
|
||||
|
||||
if (!IsValidUnit(target))
|
||||
return false;
|
||||
|
||||
Vehicle* vehicle = bot->GetVehicle();
|
||||
if (!vehicle)
|
||||
return false;
|
||||
@@ -3792,12 +3691,12 @@ bool PlayerbotAI::CanCastVehicleSpell(uint32 spellId, Unit* target)
|
||||
return false;
|
||||
|
||||
Unit* vehicleBase = vehicle->GetBase();
|
||||
Unit* spellTarget = target;
|
||||
|
||||
Unit* spellTarget = target;
|
||||
if (!spellTarget)
|
||||
spellTarget = vehicleBase;
|
||||
|
||||
if (!IsValidUnit(spellTarget))
|
||||
if (!spellTarget)
|
||||
return false;
|
||||
|
||||
if (vehicleBase->HasSpellCooldown(spellId))
|
||||
@@ -3864,9 +3763,6 @@ bool PlayerbotAI::CastVehicleSpell(uint32 spellId, Unit* target)
|
||||
if (!spellId)
|
||||
return false;
|
||||
|
||||
if (!IsValidUnit(target))
|
||||
return false;
|
||||
|
||||
Vehicle* vehicle = bot->GetVehicle();
|
||||
if (!vehicle)
|
||||
return false;
|
||||
@@ -3877,12 +3773,12 @@ bool PlayerbotAI::CastVehicleSpell(uint32 spellId, Unit* target)
|
||||
return false;
|
||||
|
||||
Unit* vehicleBase = vehicle->GetBase();
|
||||
Unit* spellTarget = target;
|
||||
|
||||
Unit* spellTarget = target;
|
||||
if (!spellTarget)
|
||||
spellTarget = vehicleBase;
|
||||
|
||||
if (!IsValidUnit(spellTarget))
|
||||
if (!spellTarget)
|
||||
return false;
|
||||
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||
@@ -4035,13 +3931,9 @@ bool PlayerbotAI::IsInVehicle(bool canControl, bool canCast, bool canAttack, boo
|
||||
|
||||
void PlayerbotAI::WaitForSpellCast(Spell* spell)
|
||||
{
|
||||
if (!spell)
|
||||
return;
|
||||
|
||||
SpellInfo const* spellInfo = spell->GetSpellInfo();
|
||||
uint32 castTime = spell->GetCastTime();
|
||||
|
||||
if (spellInfo && spellInfo->IsChanneled())
|
||||
if (spellInfo->IsChanneled())
|
||||
{
|
||||
int32 duration = spellInfo->GetDuration();
|
||||
bot->ApplySpellMod(spellInfo->Id, SPELLMOD_DURATION, duration);
|
||||
@@ -4089,9 +3981,6 @@ void PlayerbotAI::RemoveAura(std::string const name)
|
||||
|
||||
bool PlayerbotAI::IsInterruptableSpellCasting(Unit* target, std::string const spell)
|
||||
{
|
||||
if (!IsValidUnit(target))
|
||||
return false;
|
||||
|
||||
uint32 spellid = aiObjectContext->GetValue<uint32>("spell id", spell)->Get();
|
||||
if (!spellid || !target->IsNonMeleeSpellCast(true))
|
||||
return false;
|
||||
@@ -4120,25 +4009,17 @@ bool PlayerbotAI::IsInterruptableSpellCasting(Unit* target, std::string const sp
|
||||
|
||||
bool PlayerbotAI::HasAuraToDispel(Unit* target, uint32 dispelType)
|
||||
{
|
||||
if (!IsValidUnit(target) || !target->IsAlive())
|
||||
if (!target->IsInWorld())
|
||||
{
|
||||
return false;
|
||||
|
||||
if (!IsValidPlayer(bot))
|
||||
return false;
|
||||
|
||||
}
|
||||
bool isFriend = bot->IsFriendlyTo(target);
|
||||
|
||||
Unit::VisibleAuraMap const* visibleAuras = target->GetVisibleAuras();
|
||||
if (!visibleAuras)
|
||||
return false;
|
||||
|
||||
for (Unit::VisibleAuraMap::const_iterator itr = visibleAuras->begin(); itr != visibleAuras->end(); ++itr)
|
||||
{
|
||||
if (!itr->second)
|
||||
continue;
|
||||
|
||||
Aura* aura = itr->second->GetBase();
|
||||
if (!aura || aura->IsPassive() || aura->IsRemoved())
|
||||
|
||||
if (aura->IsPassive())
|
||||
continue;
|
||||
|
||||
if (sPlayerbotAIConfig->dispelAuraDuration && aura->GetDuration() &&
|
||||
@@ -4146,8 +4027,6 @@ bool PlayerbotAI::HasAuraToDispel(Unit* target, uint32 dispelType)
|
||||
continue;
|
||||
|
||||
SpellInfo const* spellInfo = aura->GetSpellInfo();
|
||||
if (!spellInfo)
|
||||
continue;
|
||||
|
||||
bool isPositiveSpell = spellInfo->IsPositive();
|
||||
if (isPositiveSpell && isFriend)
|
||||
@@ -4159,7 +4038,6 @@ bool PlayerbotAI::HasAuraToDispel(Unit* target, uint32 dispelType)
|
||||
if (canDispel(spellInfo, dispelType))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -4214,7 +4092,7 @@ Player* PlayerbotAI::FindNewMaster()
|
||||
if (!group)
|
||||
return nullptr;
|
||||
|
||||
Player* groupLeader = GetGroupLeader();
|
||||
Player* groupLeader = GetGroupMaster();
|
||||
PlayerbotAI* leaderBotAI = GET_PLAYERBOT_AI(groupLeader);
|
||||
if (!leaderBotAI || leaderBotAI->IsRealPlayer())
|
||||
return groupLeader;
|
||||
@@ -4223,7 +4101,8 @@ Player* PlayerbotAI::FindNewMaster()
|
||||
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
||||
{
|
||||
Player* member = gref->GetSource();
|
||||
if (!member || member == bot || !member->IsInWorld() || !member->IsInSameRaidWith(bot))
|
||||
if (!member || member == bot || !member->IsInWorld() ||
|
||||
!member->IsInSameRaidWith(bot))
|
||||
continue;
|
||||
|
||||
PlayerbotAI* memberBotAI = GET_PLAYERBOT_AI(member);
|
||||
@@ -4264,7 +4143,7 @@ bool PlayerbotAI::HasActivePlayerMaster() { return master && !GET_PLAYERBOT_AI(m
|
||||
|
||||
bool PlayerbotAI::IsAlt() { return HasRealPlayerMaster() && !sRandomPlayerbotMgr->IsRandomBot(bot); }
|
||||
|
||||
Player* PlayerbotAI::GetGroupLeader()
|
||||
Player* PlayerbotAI::GetGroupMaster()
|
||||
{
|
||||
if (!bot->InBattleground())
|
||||
if (Group* group = bot->GetGroup())
|
||||
@@ -4458,11 +4337,6 @@ inline bool ZoneHasRealPlayers(Player* bot)
|
||||
|
||||
bool PlayerbotAI::AllowActive(ActivityType activityType)
|
||||
{
|
||||
// Early return if bot is in invalid state
|
||||
if (!bot || !bot->GetSession() || !bot->IsInWorld() || bot->IsBeingTeleported() ||
|
||||
bot->GetSession()->isLogingOut() || bot->IsDuringRemoveFromWorld())
|
||||
return false;
|
||||
|
||||
// when botActiveAlone is 100% and smartScale disabled
|
||||
if (sPlayerbotAIConfig->botActiveAlone >= 100 && !sPlayerbotAIConfig->botActiveAloneSmartScale)
|
||||
{
|
||||
@@ -4553,8 +4427,10 @@ bool PlayerbotAI::AllowActive(ActivityType activityType)
|
||||
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
||||
{
|
||||
Player* member = gref->GetSource();
|
||||
if (!member || !member->IsInWorld() || member->GetMapId() != bot->GetMapId())
|
||||
if ((!member || !member->IsInWorld()) && member->GetMapId() != bot->GetMapId())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (member == bot)
|
||||
{
|
||||
@@ -4605,23 +4481,23 @@ bool PlayerbotAI::AllowActive(ActivityType activityType)
|
||||
// HasFriend
|
||||
if (sPlayerbotAIConfig->BotActiveAloneForceWhenIsFriend)
|
||||
{
|
||||
// shouldnt be needed analyse in future
|
||||
if (!bot->GetGUID())
|
||||
if (!bot || !bot->IsInWorld() || !bot->GetGUID())
|
||||
return false;
|
||||
|
||||
for (auto& player : sRandomPlayerbotMgr->GetPlayers())
|
||||
{
|
||||
if (!player || !player->GetSession() || !player->IsInWorld() || player->IsDuringRemoveFromWorld() ||
|
||||
player->GetSession()->isLogingOut())
|
||||
if (!player || !player->IsInWorld())
|
||||
continue;
|
||||
|
||||
PlayerbotAI* playerAI = GET_PLAYERBOT_AI(player);
|
||||
if (!playerAI || !playerAI->IsRealPlayer())
|
||||
Player* connectedPlayer = ObjectAccessor::FindPlayer(player->GetGUID());
|
||||
if (!connectedPlayer)
|
||||
continue;
|
||||
|
||||
// if a real player has the bot as a friend
|
||||
PlayerSocial* social = player->GetSocial();
|
||||
if (social && social->HasFriend(bot->GetGUID()))
|
||||
if (!social)
|
||||
continue;
|
||||
|
||||
if (social->HasFriend(bot->GetGUID()))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -4635,7 +4511,7 @@ bool PlayerbotAI::AllowActive(ActivityType activityType)
|
||||
}
|
||||
}
|
||||
|
||||
// Bots don't need react to PathGenerator activities
|
||||
// Bots don't need to move using PathGenerator.
|
||||
if (activityType == DETAILED_MOVE_ACTIVITY)
|
||||
{
|
||||
return false;
|
||||
@@ -4671,25 +4547,15 @@ bool PlayerbotAI::AllowActive(ActivityType activityType)
|
||||
|
||||
bool PlayerbotAI::AllowActivity(ActivityType activityType, bool checkNow)
|
||||
{
|
||||
const int activityIndex = static_cast<int>(activityType);
|
||||
if (!allowActiveCheckTimer[activityType])
|
||||
allowActiveCheckTimer[activityType] = time(nullptr);
|
||||
|
||||
// Unknown/out-of-range avoid blocking, added logging for further analysing should not happen in the first place.
|
||||
if (activityIndex <= 0 || activityIndex >= MAX_ACTIVITY_TYPE)
|
||||
{
|
||||
LOG_ERROR("playerbots", "AllowActivity received invalid activity type value: {}", activityIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!allowActiveCheckTimer[activityIndex])
|
||||
allowActiveCheckTimer[activityIndex] = time(nullptr);
|
||||
|
||||
if (!checkNow && time(nullptr) < (allowActiveCheckTimer[activityIndex] + 5))
|
||||
return allowActive[activityIndex];
|
||||
|
||||
const bool allowed = AllowActive(activityType);
|
||||
allowActive[activityIndex] = allowed;
|
||||
allowActiveCheckTimer[activityIndex] = time(nullptr);
|
||||
if (!checkNow && time(nullptr) < (allowActiveCheckTimer[activityType] + 5))
|
||||
return allowActive[activityType];
|
||||
|
||||
bool allowed = AllowActive(activityType);
|
||||
allowActive[activityType] = allowed;
|
||||
allowActiveCheckTimer[activityType] = time(nullptr);
|
||||
return allowed;
|
||||
}
|
||||
|
||||
@@ -5473,13 +5339,15 @@ Item* PlayerbotAI::FindStoneFor(Item* weapon) const
|
||||
if (!item_template)
|
||||
return nullptr;
|
||||
|
||||
static const std::vector<uint32_t> uPrioritizedSharpStoneIds = {
|
||||
ADAMANTITE_SHARPENING_STONE, FEL_SHARPENING_STONE, ELEMENTAL_SHARPENING_STONE, DENSE_SHARPENING_STONE,
|
||||
SOLID_SHARPENING_STONE, HEAVY_SHARPENING_STONE, COARSE_SHARPENING_STONE, ROUGH_SHARPENING_STONE};
|
||||
static const std::vector<uint32_t> uPrioritizedSharpStoneIds = {
|
||||
ADAMANTITE_SHARPENING_STONE, FEL_SHARPENING_STONE, ELEMENTAL_SHARPENING_STONE, DENSE_SHARPENING_STONE,
|
||||
SOLID_SHARPENING_STONE, HEAVY_SHARPENING_STONE, COARSE_SHARPENING_STONE, ROUGH_SHARPENING_STONE
|
||||
};
|
||||
|
||||
static const std::vector<uint32_t> uPrioritizedWeightStoneIds = {
|
||||
ADAMANTITE_WEIGHTSTONE, FEL_WEIGHTSTONE, DENSE_WEIGHTSTONE, SOLID_WEIGHTSTONE,
|
||||
HEAVY_WEIGHTSTONE, COARSE_WEIGHTSTONE, ROUGH_WEIGHTSTONE};
|
||||
static const std::vector<uint32_t> uPrioritizedWeightStoneIds = {
|
||||
ADAMANTITE_WEIGHTSTONE, FEL_WEIGHTSTONE, DENSE_WEIGHTSTONE, SOLID_WEIGHTSTONE,
|
||||
HEAVY_WEIGHTSTONE, COARSE_WEIGHTSTONE, ROUGH_WEIGHTSTONE
|
||||
};
|
||||
|
||||
Item* stone = nullptr;
|
||||
ItemTemplate const* pProto = weapon->GetTemplate();
|
||||
@@ -5515,6 +5383,7 @@ Item* PlayerbotAI::FindStoneFor(Item* weapon) const
|
||||
|
||||
Item* PlayerbotAI::FindOilFor(Item* weapon) const
|
||||
{
|
||||
|
||||
if (!weapon)
|
||||
return nullptr;
|
||||
|
||||
@@ -5523,12 +5392,12 @@ Item* PlayerbotAI::FindOilFor(Item* weapon) const
|
||||
return nullptr;
|
||||
|
||||
static const std::vector<uint32_t> uPrioritizedWizardOilIds = {
|
||||
BRILLIANT_WIZARD_OIL, SUPERIOR_WIZARD_OIL, WIZARD_OIL, LESSER_WIZARD_OIL, MINOR_WIZARD_OIL,
|
||||
BRILLIANT_MANA_OIL, SUPERIOR_MANA_OIL, LESSER_MANA_OIL, MINOR_MANA_OIL};
|
||||
BRILLIANT_WIZARD_OIL, SUPERIOR_WIZARD_OIL, WIZARD_OIL, LESSER_WIZARD_OIL, MINOR_WIZARD_OIL,
|
||||
BRILLIANT_MANA_OIL, SUPERIOR_MANA_OIL, LESSER_MANA_OIL, MINOR_MANA_OIL};
|
||||
|
||||
static const std::vector<uint32_t> uPrioritizedManaOilIds = {
|
||||
BRILLIANT_MANA_OIL, SUPERIOR_MANA_OIL, LESSER_MANA_OIL, MINOR_MANA_OIL, BRILLIANT_WIZARD_OIL,
|
||||
SUPERIOR_WIZARD_OIL, WIZARD_OIL, LESSER_WIZARD_OIL, MINOR_WIZARD_OIL};
|
||||
BRILLIANT_MANA_OIL, SUPERIOR_MANA_OIL, LESSER_MANA_OIL, MINOR_MANA_OIL,
|
||||
BRILLIANT_WIZARD_OIL, SUPERIOR_WIZARD_OIL, WIZARD_OIL, LESSER_WIZARD_OIL, MINOR_WIZARD_OIL};
|
||||
|
||||
Item* oil = nullptr;
|
||||
int botClass = bot->getClass();
|
||||
@@ -5544,22 +5413,22 @@ Item* PlayerbotAI::FindOilFor(Item* weapon) const
|
||||
prioritizedOils = &uPrioritizedWizardOilIds;
|
||||
break;
|
||||
case CLASS_DRUID:
|
||||
if (specTab == 0) // Balance
|
||||
if (specTab == 0) // Balance
|
||||
prioritizedOils = &uPrioritizedWizardOilIds;
|
||||
else if (specTab == 1) // Feral
|
||||
else if (specTab == 1) // Feral
|
||||
prioritizedOils = nullptr;
|
||||
else // Restoration (specTab == 2) or any other/unspecified spec
|
||||
else // Restoration (specTab == 2) or any other/unspecified spec
|
||||
prioritizedOils = &uPrioritizedManaOilIds;
|
||||
break;
|
||||
case CLASS_HUNTER:
|
||||
prioritizedOils = &uPrioritizedManaOilIds;
|
||||
break;
|
||||
case CLASS_PALADIN:
|
||||
if (specTab == 1) // Protection
|
||||
if (specTab == 1) // Protection
|
||||
prioritizedOils = &uPrioritizedWizardOilIds;
|
||||
else if (specTab == 2) // Retribution
|
||||
else if (specTab == 2) // Retribution
|
||||
prioritizedOils = nullptr;
|
||||
else // Holy (specTab == 0) or any other/unspecified spec
|
||||
else // Holy (specTab == 0) or any other/unspecified spec
|
||||
prioritizedOils = &uPrioritizedManaOilIds;
|
||||
break;
|
||||
default:
|
||||
@@ -5790,7 +5659,7 @@ void PlayerbotAI::ImbueItem(Item* item) { ImbueItem(item, TARGET_FLAG_NONE, Obje
|
||||
// item on unit
|
||||
void PlayerbotAI::ImbueItem(Item* item, Unit* target)
|
||||
{
|
||||
if (!IsValidUnit(target))
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
ImbueItem(item, TARGET_FLAG_UNIT, target->GetGUID());
|
||||
@@ -5932,38 +5801,30 @@ int32 PlayerbotAI::GetNearGroupMemberCount(float dis)
|
||||
|
||||
bool PlayerbotAI::CanMove()
|
||||
{
|
||||
// Most common checks: confused, stunned, fleeing, jumping, charging. All these
|
||||
// states are set when handling certain aura effects. We don't check against
|
||||
// UNIT_STATE_ROOT here, because this state is used by vehicles.
|
||||
if (bot->HasUnitState(UNIT_STATE_LOST_CONTROL))
|
||||
// do not allow if not vehicle driver
|
||||
if (IsInVehicle() && !IsInVehicle(true))
|
||||
return false;
|
||||
|
||||
// Death state (w/o spirit release) and Spirit of Redemption aura (priest)
|
||||
if ((bot->isDead() && !bot->HasPlayerFlag(PLAYER_FLAGS_GHOST)) || bot->HasSpiritOfRedemptionAura())
|
||||
if (bot->isFrozen() || bot->IsPolymorphed() || (bot->isDead() && !bot->HasPlayerFlag(PLAYER_FLAGS_GHOST)) ||
|
||||
bot->IsBeingTeleported() || bot->HasRootAura() || bot->HasSpiritOfRedemptionAura() || bot->HasConfuseAura() ||
|
||||
bot->IsCharmed() || bot->HasStunAura() || bot->IsInFlight() || bot->HasUnitState(UNIT_STATE_LOST_CONTROL))
|
||||
return false;
|
||||
|
||||
// Common CC effects, ordered by frequency: rooted > charmed > frozen > polymorphed.
|
||||
// NOTE: Can't find proper way to check if bot is rooted or charmed w/o additional
|
||||
// vehicle check -- when a passenger is added, they become rooted and charmed.
|
||||
if (!bot->GetVehicle() && (bot->IsRooted() || bot->IsCharmed()))
|
||||
return bot->GetMotionMaster()->GetCurrentMovementGeneratorType() != FLIGHT_MOTION_TYPE;
|
||||
}
|
||||
|
||||
bool PlayerbotAI::IsRealGuild(uint32 guildId)
|
||||
{
|
||||
Guild* guild = sGuildMgr->GetGuildById(guildId);
|
||||
if (!guild)
|
||||
{
|
||||
return false;
|
||||
if (bot->isFrozen() || bot->IsPolymorphed())
|
||||
}
|
||||
uint32 leaderAccount = sCharacterCache->GetCharacterAccountIdByGuid(guild->GetLeaderGUID());
|
||||
if (!leaderAccount)
|
||||
return false;
|
||||
|
||||
// Check for the MM controlled slot types: feared, confused, fleeing, etc.
|
||||
if (bot->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_CONTROLLED) != NULL_MOTION_TYPE)
|
||||
return false;
|
||||
|
||||
// Traveling state: taxi flight and being teleported (relatively rare)
|
||||
if (bot->IsInFlight() || bot->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE ||
|
||||
bot->IsBeingTeleported())
|
||||
return false;
|
||||
|
||||
// Vehicle state: is in the vehicle and can control it (rare, content-specific)
|
||||
if ((bot->GetVehicle() && !IsInVehicle(true)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
return !(sPlayerbotAIConfig->IsInRandomAccountList(leaderAccount));
|
||||
}
|
||||
|
||||
bool PlayerbotAI::IsInRealGuild()
|
||||
@@ -5971,7 +5832,7 @@ bool PlayerbotAI::IsInRealGuild()
|
||||
if (!bot->GetGuildId())
|
||||
return false;
|
||||
|
||||
return sPlayerbotGuildMgr->IsRealGuild(bot->GetGuildId());
|
||||
return IsRealGuild(bot->GetGuildId());
|
||||
}
|
||||
|
||||
void PlayerbotAI::QueueChatResponse(const ChatQueuedReply chatReply) { chatReplies.push_back(std::move(chatReply)); }
|
||||
@@ -276,7 +276,7 @@ enum BotRoles : uint8
|
||||
|
||||
enum HUNTER_TABS
|
||||
{
|
||||
HUNTER_TAB_BEAST_MASTERY,
|
||||
HUNTER_TAB_BEASTMASTERY,
|
||||
HUNTER_TAB_MARKSMANSHIP,
|
||||
HUNTER_TAB_SURVIVAL,
|
||||
};
|
||||
@@ -295,11 +295,11 @@ enum PRIEST_TABS
|
||||
PRIEST_TAB_SHADOW,
|
||||
};
|
||||
|
||||
enum DEATH_KNIGHT_TABS
|
||||
enum DEATHKNIGHT_TABS
|
||||
{
|
||||
DEATH_KNIGHT_TAB_BLOOD,
|
||||
DEATH_KNIGHT_TAB_FROST,
|
||||
DEATH_KNIGHT_TAB_UNHOLY,
|
||||
DEATHKNIGHT_TAB_BLOOD,
|
||||
DEATHKNIGHT_TAB_FROST,
|
||||
DEATHKNIGHT_TAB_UNHOLY,
|
||||
};
|
||||
|
||||
enum DRUID_TABS
|
||||
@@ -428,7 +428,7 @@ public:
|
||||
static bool IsMainTank(Player* player);
|
||||
static uint32 GetGroupTankNum(Player* player);
|
||||
static bool IsAssistTank(Player* player);
|
||||
static bool IsAssistTankOfIndex(Player* player, int index, bool ignoreDeadPlayers = false);
|
||||
static bool IsAssistTankOfIndex(Player* player, int index);
|
||||
static bool IsHealAssistantOfIndex(Player* player, int index);
|
||||
static bool IsRangedDpsAssistantOfIndex(Player* player, int index);
|
||||
bool HasAggro(Unit* unit);
|
||||
@@ -540,7 +540,7 @@ public:
|
||||
// Get the group leader or the master of the bot.
|
||||
// Checks if the bot is summoned as alt of a player
|
||||
bool IsAlt();
|
||||
Player* GetGroupLeader();
|
||||
Player* GetGroupMaster();
|
||||
// Returns a semi-random (cycling) number that is fixed for each bot.
|
||||
uint32 GetFixedBotNumer(uint32 maxNum = 100, float cyclePerMin = 1);
|
||||
GrouperType GetGrouperType();
|
||||
@@ -579,6 +579,7 @@ public:
|
||||
void ResetJumpDestination() { jumpDestination = Position(); }
|
||||
|
||||
bool CanMove();
|
||||
static bool IsRealGuild(uint32 guildId);
|
||||
bool IsInRealGuild();
|
||||
static std::vector<std::string> dispel_whitelist;
|
||||
bool EqualLowercaseName(std::string s1, std::string s2);
|
||||
@@ -611,20 +612,12 @@ private:
|
||||
static void _fillGearScoreData(Player* player, Item* item, std::vector<uint32>* gearScore, uint32& twoHandScore,
|
||||
bool mixed = false);
|
||||
bool IsTellAllowed(PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL);
|
||||
void UpdateAIGroupMaster();
|
||||
void UpdateAIGroupAndMaster();
|
||||
Item* FindItemInInventory(std::function<bool(ItemTemplate const*)> checkItem) const;
|
||||
void HandleCommands();
|
||||
void HandleCommand(uint32 type, const std::string& text, Player& fromPlayer, const uint32 lang = LANG_UNIVERSAL);
|
||||
bool _isBotInitializing = false;
|
||||
inline bool IsValidUnit(const Unit* unit) const
|
||||
{
|
||||
return unit && unit->IsInWorld() && !unit->IsDuringRemoveFromWorld();
|
||||
}
|
||||
inline bool IsValidPlayer(const Player* player) const
|
||||
{
|
||||
return player && player->GetSession() && player->IsInWorld() && !player->IsDuringRemoveFromWorld() &&
|
||||
!player->IsBeingTeleported();
|
||||
}
|
||||
|
||||
protected:
|
||||
Player* bot;
|
||||
Player* master;
|
||||
@@ -14,7 +14,7 @@ void PlayerbotAIBase::UpdateAI(uint32 elapsed, bool minimal)
|
||||
if (totalPmo)
|
||||
totalPmo->finish();
|
||||
|
||||
totalPmo = sPerfMonitor->start(PERF_MON_TOTAL, "PlayerbotAIBase::FullTick");
|
||||
totalPmo = sPerformanceMonitor->start(PERF_MON_TOTAL, "PlayerbotAIBase::FullTick");
|
||||
|
||||
if (nextAICheckDelay > elapsed)
|
||||
nextAICheckDelay -= elapsed;
|
||||
@@ -25,7 +25,7 @@ public:
|
||||
|
||||
protected:
|
||||
uint32 nextAICheckDelay;
|
||||
class PerfMonitorOperation* totalPmo = nullptr;
|
||||
class PerformanceMonitorOperation* totalPmo = nullptr;
|
||||
|
||||
private:
|
||||
bool _isBotAI;
|
||||
@@ -7,10 +7,9 @@
|
||||
#include <iostream>
|
||||
#include "Config.h"
|
||||
#include "NewRpgInfo.h"
|
||||
#include "PlayerbotDungeonRepository.h"
|
||||
#include "PlayerbotDungeonSuggestionMgr.h"
|
||||
#include "PlayerbotFactory.h"
|
||||
#include "Playerbots.h"
|
||||
#include "PlayerbotGuildMgr.h"
|
||||
#include "RandomItemMgr.h"
|
||||
#include "RandomPlayerbotFactory.h"
|
||||
#include "RandomPlayerbotMgr.h"
|
||||
@@ -166,7 +165,7 @@ bool PlayerbotAIConfig::Initialize()
|
||||
pvpProhibitedZoneIds);
|
||||
LoadList<std::vector<uint32>>(
|
||||
sConfigMgr->GetOption<std::string>("AiPlayerbot.PvpProhibitedAreaIds",
|
||||
"976,35,392,2268,4161,4010,4317,4312,3649,3887,3958,3724,4080,3938,3754,3786,3973"),
|
||||
"976,35,392,2268,4161,4010,4317,4312,3649,3887,3958,3724,4080,3938,3754"),
|
||||
pvpProhibitedAreaIds);
|
||||
fastReactInBG = sConfigMgr->GetOption<bool>("AiPlayerbot.FastReactInBG", true);
|
||||
LoadList<std::vector<uint32>>(
|
||||
@@ -223,11 +222,6 @@ bool PlayerbotAIConfig::Initialize()
|
||||
|
||||
EnableICCBuffs = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableICCBuffs", true);
|
||||
|
||||
//////////////////////////// Professions
|
||||
fishingDistanceFromMaster = sConfigMgr->GetOption<float>("AiPlayerbot.FishingDistanceFromMaster", 10.0f);
|
||||
endFishingWithMaster = sConfigMgr->GetOption<float>("AiPlayerbot.EndFishingWithMaster", 30.0f);
|
||||
fishingDistance = sConfigMgr->GetOption<float>("AiPlayerbot.FishingDistance", 40.0f);
|
||||
enableFishingWithMaster = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableFishingWithMaster", true);
|
||||
//////////////////////////// CHAT
|
||||
enableBroadcasts = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableBroadcasts", true);
|
||||
randomBotTalk = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotTalk", false);
|
||||
@@ -667,7 +661,6 @@ bool PlayerbotAIConfig::Initialize()
|
||||
sRandomPlayerbotMgr->Init();
|
||||
}
|
||||
|
||||
sPlayerbotGuildMgr->Init();
|
||||
sRandomItemMgr->Init();
|
||||
sRandomItemMgr->InitAfterAhBot();
|
||||
sPlayerbotTextMgr->LoadBotTexts();
|
||||
@@ -678,7 +671,7 @@ bool PlayerbotAIConfig::Initialize()
|
||||
|
||||
if (sPlayerbotAIConfig->randomBotSuggestDungeons)
|
||||
{
|
||||
sPlayerbotDungeonRepository->LoadDungeonSuggestions();
|
||||
sPlayerbotDungeonSuggestionMgr->LoadDungeonSuggestions();
|
||||
}
|
||||
|
||||
excludedHunterPetFamilies.clear();
|
||||
@@ -145,10 +145,6 @@ public:
|
||||
// Cooldown (seconds) between reagent-missing RP warnings, per bot & per buff. Default: 30
|
||||
int32 rpWarningCooldown;
|
||||
|
||||
// Professions
|
||||
bool enableFishingWithMaster;
|
||||
float fishingDistanceFromMaster, fishingDistance, endFishingWithMaster;
|
||||
|
||||
// chat
|
||||
bool randomBotTalk;
|
||||
bool randomBotEmote;
|
||||
@@ -273,6 +269,7 @@ public:
|
||||
bool deleteRandomBotAccounts;
|
||||
uint32 randomBotGuildCount, randomBotGuildSizeMax;
|
||||
bool deleteRandomBotGuilds;
|
||||
std::vector<uint32> randomBotGuilds;
|
||||
std::vector<uint32> pvpProhibitedZoneIds;
|
||||
std::vector<uint32> pvpProhibitedAreaIds;
|
||||
bool fastReactInBG;
|
||||
@@ -3,13 +3,13 @@
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "PlayerbotRepository.h"
|
||||
#include "PlayerbotDbStore.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
void PlayerbotRepository::Load(PlayerbotAI* botAI)
|
||||
void PlayerbotDbStore::Load(PlayerbotAI* botAI)
|
||||
{
|
||||
ObjectGuid::LowType guid = botAI->GetBot()->GetGUID().GetCounter();
|
||||
|
||||
@@ -46,7 +46,7 @@ void PlayerbotRepository::Load(PlayerbotAI* botAI)
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerbotRepository::Save(PlayerbotAI* botAI)
|
||||
void PlayerbotDbStore::Save(PlayerbotAI* botAI)
|
||||
{
|
||||
ObjectGuid::LowType guid = botAI->GetBot()->GetGUID().GetCounter();
|
||||
|
||||
@@ -68,7 +68,7 @@ void PlayerbotRepository::Save(PlayerbotAI* botAI)
|
||||
SaveValue(guid, "dead", FormatStrategies("dead", botAI->GetStrategies(BOT_STATE_DEAD)));
|
||||
}
|
||||
|
||||
std::string const PlayerbotRepository::FormatStrategies(std::string const type, std::vector<std::string> strategies)
|
||||
std::string const PlayerbotDbStore::FormatStrategies(std::string const type, std::vector<std::string> strategies)
|
||||
{
|
||||
std::ostringstream out;
|
||||
for (std::vector<std::string>::iterator i = strategies.begin(); i != strategies.end(); ++i)
|
||||
@@ -78,7 +78,7 @@ std::string const PlayerbotRepository::FormatStrategies(std::string const type,
|
||||
return res.substr(0, res.size() - 1);
|
||||
}
|
||||
|
||||
void PlayerbotRepository::Reset(PlayerbotAI* botAI)
|
||||
void PlayerbotDbStore::Reset(PlayerbotAI* botAI)
|
||||
{
|
||||
ObjectGuid::LowType guid = botAI->GetBot()->GetGUID().GetCounter();
|
||||
|
||||
@@ -87,7 +87,7 @@ void PlayerbotRepository::Reset(PlayerbotAI* botAI)
|
||||
PlayerbotsDatabase.Execute(stmt);
|
||||
}
|
||||
|
||||
void PlayerbotRepository::SaveValue(uint32 guid, std::string const key, std::string const value)
|
||||
void PlayerbotDbStore::SaveValue(uint32 guid, std::string const key, std::string const value)
|
||||
{
|
||||
PlayerbotsDatabasePreparedStatement* stmt = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_INS_DB_STORE);
|
||||
stmt->SetData(0, guid);
|
||||
@@ -3,8 +3,8 @@
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_PLAYERBOTREPOSITORY_H
|
||||
#define _PLAYERBOT_PLAYERBOTREPOSITORY_H
|
||||
#ifndef _PLAYERBOT_PLAYERBOTDBSTORE_H
|
||||
#define _PLAYERBOT_PLAYERBOTDBSTORE_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
@@ -12,14 +12,14 @@
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class PlayerbotRepository
|
||||
class PlayerbotDbStore
|
||||
{
|
||||
public:
|
||||
PlayerbotRepository() {}
|
||||
virtual ~PlayerbotRepository() {}
|
||||
static PlayerbotRepository* instance()
|
||||
PlayerbotDbStore() {}
|
||||
virtual ~PlayerbotDbStore() {}
|
||||
static PlayerbotDbStore* instance()
|
||||
{
|
||||
static PlayerbotRepository instance;
|
||||
static PlayerbotDbStore instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,6 @@ private:
|
||||
std::string const FormatStrategies(std::string const type, std::vector<std::string> strategies);
|
||||
};
|
||||
|
||||
#define sPlayerbotRepository PlayerbotRepository::instance()
|
||||
#define sPlayerbotDbStore PlayerbotDbStore::instance()
|
||||
|
||||
#endif
|
||||
@@ -3,16 +3,16 @@
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "PlayerbotDungeonRepository.h"
|
||||
#include "PlayerbotDungeonSuggestionMgr.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
std::vector<DungeonSuggestion> const PlayerbotDungeonRepository::GetDungeonSuggestions()
|
||||
std::vector<DungeonSuggestion> const PlayerbotDungeonSuggestionMgr::GetDungeonSuggestions()
|
||||
{
|
||||
return m_dungeonSuggestions;
|
||||
}
|
||||
|
||||
void PlayerbotDungeonRepository::LoadDungeonSuggestions()
|
||||
void PlayerbotDungeonSuggestionMgr::LoadDungeonSuggestions()
|
||||
{
|
||||
LOG_INFO("server.loading", "Loading playerbots dungeon suggestions...");
|
||||
uint32 oldMSTime = getMSTime();
|
||||
@@ -3,8 +3,8 @@
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_PLAYERBOTDUNGEONREPOSITORY_H
|
||||
#define _PLAYERBOT_PLAYERBOTDUNGEONREPOSITORY_H
|
||||
#ifndef _PLAYERBOT_PLAYERBOTDUNGEONSUGGESTIONMGR_H
|
||||
#define _PLAYERBOT_PLAYERBOTDUNGEONSUGGESTIONMGR_H
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
@@ -22,14 +22,14 @@ struct DungeonSuggestion
|
||||
std::string strategy;
|
||||
};
|
||||
|
||||
class PlayerbotDungeonRepository
|
||||
class PlayerbotDungeonSuggestionMgr
|
||||
{
|
||||
public:
|
||||
PlayerbotDungeonRepository(){};
|
||||
~PlayerbotDungeonRepository(){};
|
||||
static PlayerbotDungeonRepository* instance()
|
||||
PlayerbotDungeonSuggestionMgr(){};
|
||||
~PlayerbotDungeonSuggestionMgr(){};
|
||||
static PlayerbotDungeonSuggestionMgr* instance()
|
||||
{
|
||||
static PlayerbotDungeonRepository instance;
|
||||
static PlayerbotDungeonSuggestionMgr instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
@@ -40,6 +40,6 @@ private:
|
||||
std::vector<DungeonSuggestion> m_dungeonSuggestions;
|
||||
};
|
||||
|
||||
#define sPlayerbotDungeonRepository PlayerbotDungeonRepository::instance()
|
||||
#define sPlayerbotDungeonSuggestionMgr PlayerbotDungeonSuggestionMgr::instance()
|
||||
|
||||
#endif
|
||||
@@ -9,10 +9,9 @@
|
||||
#include <cstring>
|
||||
#include <istream>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <openssl/sha.h>
|
||||
#include <unordered_set>
|
||||
#include <iomanip>
|
||||
#include <algorithm>
|
||||
|
||||
#include "ChannelMgr.h"
|
||||
#include "CharacterCache.h"
|
||||
@@ -26,19 +25,21 @@
|
||||
#include "ObjectGuid.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "PlayerbotRepository.h"
|
||||
#include "PlayerbotDbStore.h"
|
||||
#include "PlayerbotFactory.h"
|
||||
#include "PlayerbotOperations.h"
|
||||
#include "PlayerbotSecurity.h"
|
||||
#include "PlayerbotWorldThreadProcessor.h"
|
||||
#include "Playerbots.h"
|
||||
#include "PlayerbotGuildMgr.h"
|
||||
#include "RandomPlayerbotMgr.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "WorldSession.h"
|
||||
#include "ChannelMgr.h"
|
||||
#include "BroadcastHelper.h"
|
||||
#include "PlayerbotDbStore.h"
|
||||
#include "WorldSessionMgr.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "DatabaseEnv.h" // Added for gender choice
|
||||
#include <algorithm> // Added for gender choice
|
||||
|
||||
class BotInitGuard
|
||||
{
|
||||
@@ -67,7 +68,6 @@ private:
|
||||
};
|
||||
|
||||
std::unordered_set<ObjectGuid> BotInitGuard::botsBeingInitialized;
|
||||
std::unordered_set<ObjectGuid> PlayerbotHolder::botLoading;
|
||||
|
||||
PlayerbotHolder::PlayerbotHolder() : PlayerbotAIBase(false) {}
|
||||
class PlayerbotLoginQueryHolder : public LoginQueryHolder
|
||||
@@ -76,12 +76,13 @@ private:
|
||||
uint32 masterAccountId;
|
||||
PlayerbotHolder* playerbotHolder;
|
||||
public:
|
||||
PlayerbotLoginQueryHolder(uint32 masterAccount, uint32 accountId, ObjectGuid guid)
|
||||
: LoginQueryHolder(accountId, guid), masterAccountId(masterAccount)
|
||||
PlayerbotLoginQueryHolder(PlayerbotHolder* playerbotHolder, uint32 masterAccount, uint32 accountId, ObjectGuid guid)
|
||||
: LoginQueryHolder(accountId, guid), masterAccountId(masterAccount), playerbotHolder(playerbotHolder)
|
||||
{
|
||||
}
|
||||
|
||||
uint32 GetMasterAccountId() const { return masterAccountId; }
|
||||
PlayerbotHolder* GetPlayerbotHolder() { return playerbotHolder; }
|
||||
};
|
||||
|
||||
void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId)
|
||||
@@ -142,7 +143,7 @@ void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId
|
||||
return;
|
||||
}
|
||||
std::shared_ptr<PlayerbotLoginQueryHolder> holder =
|
||||
std::make_shared<PlayerbotLoginQueryHolder>(masterAccountId, accountId, playerGuid);
|
||||
std::make_shared<PlayerbotLoginQueryHolder>(this, masterAccountId, accountId, playerGuid);
|
||||
if (!holder->Initialize())
|
||||
{
|
||||
return;
|
||||
@@ -152,27 +153,8 @@ void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId
|
||||
|
||||
// Always login in with world session to avoid race condition
|
||||
sWorld->AddQueryHolderCallback(CharacterDatabase.DelayQueryHolder(holder))
|
||||
.AfterComplete(
|
||||
[](SQLQueryHolderBase const& queryHolder)
|
||||
{
|
||||
PlayerbotLoginQueryHolder const& holder = static_cast<PlayerbotLoginQueryHolder const&>(queryHolder);
|
||||
PlayerbotHolder* mgr = sRandomPlayerbotMgr; // could be null
|
||||
uint32 masterAccountId = holder.GetMasterAccountId();
|
||||
|
||||
if (masterAccountId)
|
||||
{
|
||||
// verify and find current world session of master
|
||||
WorldSession* masterSession = sWorldSessionMgr->FindSession(masterAccountId);
|
||||
Player* masterPlayer = masterSession ? masterSession->GetPlayer() : nullptr;
|
||||
if (masterPlayer)
|
||||
mgr = GET_PLAYERBOT_MGR(masterPlayer);
|
||||
}
|
||||
|
||||
if (mgr)
|
||||
mgr->HandlePlayerBotLoginCallback(holder);
|
||||
else
|
||||
PlayerbotHolder::botLoading.erase(holder.GetGuid());
|
||||
});
|
||||
.AfterComplete([this](SQLQueryHolderBase const& holder)
|
||||
{ HandlePlayerBotLoginCallback(static_cast<PlayerbotLoginQueryHolder const&>(holder)); });
|
||||
}
|
||||
|
||||
bool PlayerbotHolder::IsAccountLinked(uint32 accountId, uint32 linkedAccountId)
|
||||
@@ -187,9 +169,8 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con
|
||||
uint32 botAccountId = holder.GetAccountId();
|
||||
// At login DBC locale should be what the server is set to use by default (as spells etc are hardcoded to ENUS this
|
||||
// allows channels to work as intended)
|
||||
WorldSession* botSession =
|
||||
new WorldSession(botAccountId, "", 0x0, nullptr, SEC_PLAYER, EXPANSION_WRATH_OF_THE_LICH_KING, time_t(0),
|
||||
sWorld->GetDefaultDbcLocale(), 0, false, false, 0, true);
|
||||
WorldSession* botSession = new WorldSession(botAccountId, "", 0x0, nullptr, SEC_PLAYER, EXPANSION_WRATH_OF_THE_LICH_KING,
|
||||
time_t(0), sWorld->GetDefaultDbcLocale(), 0, false, false, 0, true);
|
||||
|
||||
botSession->HandlePlayerLoginFromDB(holder); // will delete lqh
|
||||
|
||||
@@ -200,27 +181,26 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con
|
||||
LOG_DEBUG("mod-playerbots", "Bot player could not be loaded for account ID: {}", botAccountId);
|
||||
botSession->LogoutPlayer(true);
|
||||
delete botSession;
|
||||
PlayerbotHolder::botLoading.erase(holder.GetGuid());
|
||||
|
||||
botLoading.erase(holder.GetGuid());
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 masterAccountId = holder.GetMasterAccountId();
|
||||
WorldSession* masterSession = masterAccountId ? sWorldSessionMgr->FindSession(masterAccountId) : nullptr;
|
||||
uint32 masterAccount = holder.GetMasterAccountId();
|
||||
WorldSession* masterSession = masterAccount ? sWorldSessionMgr->FindSession(masterAccount) : nullptr;
|
||||
|
||||
// Check if masterSession->GetPlayer() is valid
|
||||
Player* masterPlayer = masterSession ? masterSession->GetPlayer() : nullptr;
|
||||
if (masterSession && !masterPlayer)
|
||||
{
|
||||
LOG_DEBUG("mod-playerbots", "Master session found but no player is associated for master account ID: {}",
|
||||
masterAccountId);
|
||||
LOG_DEBUG("mod-playerbots", "Master session found but no player is associated for master account ID: {}", masterAccount);
|
||||
}
|
||||
|
||||
sRandomPlayerbotMgr->OnPlayerLogin(bot);
|
||||
auto op = std::make_unique<OnBotLoginOperation>(bot->GetGUID(), masterAccountId);
|
||||
|
||||
auto op = std::make_unique<OnBotLoginOperation>(bot->GetGUID(), this);
|
||||
sPlayerbotWorldProcessor->QueueOperation(std::move(op));
|
||||
|
||||
PlayerbotHolder::botLoading.erase(holder.GetGuid());
|
||||
botLoading.erase(holder.GetGuid());
|
||||
}
|
||||
|
||||
void PlayerbotHolder::UpdateSessions()
|
||||
@@ -442,7 +422,7 @@ void PlayerbotHolder::DisablePlayerBot(ObjectGuid guid)
|
||||
Group* group = bot->GetGroup();
|
||||
if (group && !bot->InBattleground() && !bot->InBattlegroundQueue() && botAI->HasActivePlayerMaster())
|
||||
{
|
||||
sPlayerbotRepository->Save(botAI);
|
||||
sPlayerbotDbStore->Save(botAI);
|
||||
}
|
||||
|
||||
LOG_DEBUG("playerbots", "Bot {} logged out", bot->GetName().c_str());
|
||||
@@ -554,7 +534,7 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
|
||||
{
|
||||
botAI->ResetStrategies(!sRandomPlayerbotMgr->IsRandomBot(bot));
|
||||
}
|
||||
sPlayerbotRepository->Load(botAI);
|
||||
sPlayerbotDbStore->Load(botAI);
|
||||
|
||||
if (master && !master->HasUnitState(UNIT_STATE_IN_FLIGHT))
|
||||
{
|
||||
@@ -1194,7 +1174,7 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
|
||||
if (ObjectAccessor::FindConnectedPlayer(guid))
|
||||
continue;
|
||||
uint32 guildId = sCharacterCache->GetCharacterGuildIdByGuid(guid);
|
||||
if (guildId && sPlayerbotGuildMgr->IsRealGuild(guildId))
|
||||
if (guildId && PlayerbotAI::IsRealGuild(guildId))
|
||||
continue;
|
||||
AddPlayerBot(guid, master->GetSession()->GetAccountId());
|
||||
messages.push_back("Add class " + std::string(charname));
|
||||
@@ -60,7 +60,7 @@ protected:
|
||||
virtual void OnBotLoginInternal(Player* const bot) = 0;
|
||||
|
||||
PlayerBotMap playerBots;
|
||||
static std::unordered_set<ObjectGuid> botLoading;
|
||||
std::unordered_set<ObjectGuid> botLoading;
|
||||
};
|
||||
|
||||
class PlayerbotMgr : public PlayerbotHolder
|
||||
@@ -9,16 +9,13 @@
|
||||
#include "Group.h"
|
||||
#include "GroupMgr.h"
|
||||
#include "GuildMgr.h"
|
||||
#include "Playerbots.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "PlayerbotOperation.h"
|
||||
#include "Player.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "PlayerbotMgr.h"
|
||||
#include "PlayerbotRepository.h"
|
||||
#include "PlayerbotDbStore.h"
|
||||
#include "RandomPlayerbotMgr.h"
|
||||
#include "WorldSession.h"
|
||||
#include "WorldSessionMgr.h"
|
||||
|
||||
// Group invite operation
|
||||
class GroupInviteOperation : public PlayerbotOperation
|
||||
@@ -418,7 +415,7 @@ public:
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
if (group && !bot->InBattleground() && !bot->InBattlegroundQueue() && botAI->HasActivePlayerMaster())
|
||||
sPlayerbotRepository->Save(botAI);
|
||||
sPlayerbotDbStore->Save(botAI);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -471,31 +468,18 @@ private:
|
||||
class OnBotLoginOperation : public PlayerbotOperation
|
||||
{
|
||||
public:
|
||||
OnBotLoginOperation(ObjectGuid botGuid, uint32 masterAccountId)
|
||||
: m_botGuid(botGuid), m_masterAccountId(masterAccountId)
|
||||
OnBotLoginOperation(ObjectGuid botGuid, PlayerbotHolder* holder)
|
||||
: m_botGuid(botGuid), m_holder(holder)
|
||||
{
|
||||
}
|
||||
|
||||
bool Execute() override
|
||||
{
|
||||
// find and verify bot still exists
|
||||
Player* bot = ObjectAccessor::FindConnectedPlayer(m_botGuid);
|
||||
if (!bot)
|
||||
if (!bot || !m_holder)
|
||||
return false;
|
||||
|
||||
PlayerbotHolder* holder = sRandomPlayerbotMgr;
|
||||
if (m_masterAccountId)
|
||||
{
|
||||
WorldSession* masterSession = sWorldSessionMgr->FindSession(m_masterAccountId);
|
||||
Player* masterPlayer = masterSession ? masterSession->GetPlayer() : nullptr;
|
||||
if (masterPlayer)
|
||||
holder = GET_PLAYERBOT_MGR(masterPlayer);
|
||||
}
|
||||
|
||||
if (!holder)
|
||||
return false;
|
||||
|
||||
holder->OnBotLogin(bot);
|
||||
m_holder->OnBotLogin(bot);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -503,11 +487,14 @@ public:
|
||||
uint32 GetPriority() const override { return 100; }
|
||||
std::string GetName() const override { return "OnBotLogin"; }
|
||||
|
||||
bool IsValid() const override { return ObjectAccessor::FindConnectedPlayer(m_botGuid) != nullptr; }
|
||||
bool IsValid() const override
|
||||
{
|
||||
return ObjectAccessor::FindConnectedPlayer(m_botGuid) != nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
ObjectGuid m_botGuid;
|
||||
uint32 m_masterAccountId = 0;
|
||||
PlayerbotHolder* m_holder;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -17,28 +17,14 @@ PlayerbotSecurity::PlayerbotSecurity(Player* const bot) : bot(bot)
|
||||
|
||||
PlayerbotSecurityLevel PlayerbotSecurity::LevelFor(Player* from, DenyReason* reason, bool ignoreGroup)
|
||||
{
|
||||
// Basic pointer validity checks
|
||||
if (!bot || !from || !from->GetSession())
|
||||
{
|
||||
if (reason)
|
||||
*reason = PLAYERBOT_DENY_NONE;
|
||||
|
||||
return PLAYERBOT_SECURITY_DENY_ALL;
|
||||
}
|
||||
|
||||
// GMs always have full access
|
||||
if (from->GetSession()->GetSecurity() >= SEC_GAMEMASTER)
|
||||
return PLAYERBOT_SECURITY_ALLOW_ALL;
|
||||
|
||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||
if (!botAI)
|
||||
{
|
||||
if (reason)
|
||||
*reason = PLAYERBOT_DENY_NONE;
|
||||
|
||||
return PLAYERBOT_SECURITY_DENY_ALL;
|
||||
}
|
||||
|
||||
if (botAI->IsOpposing(from))
|
||||
{
|
||||
if (reason)
|
||||
@@ -49,7 +35,6 @@ PlayerbotSecurityLevel PlayerbotSecurity::LevelFor(Player* from, DenyReason* rea
|
||||
|
||||
if (sPlayerbotAIConfig->IsInRandomAccountList(account))
|
||||
{
|
||||
// (duplicate check in case of faction change)
|
||||
if (botAI->IsOpposing(from))
|
||||
{
|
||||
if (reason)
|
||||
@@ -58,17 +43,27 @@ PlayerbotSecurityLevel PlayerbotSecurity::LevelFor(Player* from, DenyReason* rea
|
||||
return PLAYERBOT_SECURITY_DENY_ALL;
|
||||
}
|
||||
|
||||
Group* fromGroup = from->GetGroup();
|
||||
Group* botGroup = bot->GetGroup();
|
||||
// if (sLFGMgr->GetState(bot->GetGUID()) != lfg::LFG_STATE_NONE)
|
||||
// {
|
||||
// if (!bot->GetGuildId() || bot->GetGuildId() != from->GetGuildId())
|
||||
// {
|
||||
// if (reason)
|
||||
// *reason = PLAYERBOT_DENY_LFG;
|
||||
|
||||
if (fromGroup && botGroup && fromGroup == botGroup && !ignoreGroup)
|
||||
// return PLAYERBOT_SECURITY_TALK;
|
||||
// }
|
||||
// }
|
||||
|
||||
Group* group = from->GetGroup();
|
||||
if (group && group == bot->GetGroup() && !ignoreGroup && botAI->GetMaster() == from)
|
||||
{
|
||||
if (botAI->GetMaster() == from)
|
||||
return PLAYERBOT_SECURITY_ALLOW_ALL;
|
||||
return PLAYERBOT_SECURITY_ALLOW_ALL;
|
||||
}
|
||||
|
||||
if (group && group == bot->GetGroup() && !ignoreGroup && botAI->GetMaster() != from)
|
||||
{
|
||||
if (reason)
|
||||
*reason = PLAYERBOT_DENY_NOT_YOURS;
|
||||
|
||||
return PLAYERBOT_SECURITY_TALK;
|
||||
}
|
||||
|
||||
@@ -80,34 +75,27 @@ PlayerbotSecurityLevel PlayerbotSecurity::LevelFor(Player* from, DenyReason* rea
|
||||
return PLAYERBOT_SECURITY_TALK;
|
||||
}
|
||||
|
||||
if (sPlayerbotAIConfig->groupInvitationPermission <= 1)
|
||||
if (sPlayerbotAIConfig->groupInvitationPermission <= 1 && (int32)bot->GetLevel() - (int8)from->GetLevel() > 5)
|
||||
{
|
||||
int32 levelDiff = int32(bot->GetLevel()) - int32(from->GetLevel());
|
||||
if (levelDiff > 5)
|
||||
if (!bot->GetGuildId() || bot->GetGuildId() != from->GetGuildId())
|
||||
{
|
||||
if (!bot->GetGuildId() || bot->GetGuildId() != from->GetGuildId())
|
||||
{
|
||||
if (reason)
|
||||
*reason = PLAYERBOT_DENY_LOW_LEVEL;
|
||||
if (reason)
|
||||
*reason = PLAYERBOT_DENY_LOW_LEVEL;
|
||||
|
||||
return PLAYERBOT_SECURITY_TALK;
|
||||
}
|
||||
return PLAYERBOT_SECURITY_TALK;
|
||||
}
|
||||
}
|
||||
|
||||
int32 botGS = static_cast<int32>(botAI->GetEquipGearScore(bot));
|
||||
int32 fromGS = static_cast<int32>(botAI->GetEquipGearScore(from));
|
||||
|
||||
if (sPlayerbotAIConfig->gearscorecheck && botGS && bot->GetLevel() > 15 && botGS > fromGS)
|
||||
int32 botGS = (int32)botAI->GetEquipGearScore(bot/*, false, false*/);
|
||||
int32 fromGS = (int32)botAI->GetEquipGearScore(from/*, false, false*/);
|
||||
if (sPlayerbotAIConfig->gearscorecheck)
|
||||
{
|
||||
uint32 diffPct = uint32(100 * (botGS - fromGS) / botGS);
|
||||
uint32 reqPct = uint32(12 * sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) / from->GetLevel());
|
||||
|
||||
if (diffPct >= reqPct)
|
||||
if (botGS && bot->GetLevel() > 15 && botGS > fromGS &&
|
||||
static_cast<float>(100 * (botGS - fromGS) / botGS) >=
|
||||
static_cast<float>(12 * sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) / from->GetLevel()))
|
||||
{
|
||||
if (reason)
|
||||
*reason = PLAYERBOT_DENY_GEARSCORE;
|
||||
|
||||
return PLAYERBOT_SECURITY_TALK;
|
||||
}
|
||||
}
|
||||
@@ -123,17 +111,35 @@ PlayerbotSecurityLevel PlayerbotSecurity::LevelFor(Player* from, DenyReason* rea
|
||||
}
|
||||
}
|
||||
|
||||
// If the bot is not in the group, we offer an invite
|
||||
botGroup = bot->GetGroup();
|
||||
if (!botGroup)
|
||||
/*if (bot->isDead())
|
||||
{
|
||||
if (reason)
|
||||
*reason = PLAYERBOT_DENY_DEAD;
|
||||
|
||||
return PLAYERBOT_SECURITY_TALK;
|
||||
}*/
|
||||
|
||||
group = bot->GetGroup();
|
||||
if (!group)
|
||||
{
|
||||
/*if (bot->GetMapId() != from->GetMapId() || bot->GetDistance(from) > sPlayerbotAIConfig->whisperDistance)
|
||||
{
|
||||
if (!bot->GetGuildId() || bot->GetGuildId() != from->GetGuildId())
|
||||
{
|
||||
if (reason)
|
||||
*reason = PLAYERBOT_DENY_FAR;
|
||||
|
||||
return PLAYERBOT_SECURITY_TALK;
|
||||
}
|
||||
}*/
|
||||
|
||||
if (reason)
|
||||
*reason = PLAYERBOT_DENY_INVITE;
|
||||
|
||||
return PLAYERBOT_SECURITY_INVITE;
|
||||
}
|
||||
|
||||
if (!ignoreGroup && botGroup->IsFull())
|
||||
if (!ignoreGroup && group->IsFull())
|
||||
{
|
||||
if (reason)
|
||||
*reason = PLAYERBOT_DENY_FULL_GROUP;
|
||||
@@ -141,22 +147,27 @@ PlayerbotSecurityLevel PlayerbotSecurity::LevelFor(Player* from, DenyReason* rea
|
||||
return PLAYERBOT_SECURITY_TALK;
|
||||
}
|
||||
|
||||
if (!ignoreGroup && botGroup->GetLeaderGUID() != bot->GetGUID())
|
||||
if (!ignoreGroup && group->GetLeaderGUID() != bot->GetGUID())
|
||||
{
|
||||
if (reason)
|
||||
*reason = PLAYERBOT_DENY_NOT_LEADER;
|
||||
|
||||
return PLAYERBOT_SECURITY_TALK;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (reason)
|
||||
*reason = PLAYERBOT_DENY_IS_LEADER;
|
||||
|
||||
return PLAYERBOT_SECURITY_INVITE;
|
||||
}
|
||||
|
||||
// The bot is the group leader, you can invite the initiator
|
||||
if (reason)
|
||||
*reason = PLAYERBOT_DENY_IS_LEADER;
|
||||
*reason = PLAYERBOT_DENY_INVITE;
|
||||
|
||||
return PLAYERBOT_SECURITY_INVITE;
|
||||
}
|
||||
|
||||
// Non-random bots: only their master has full access
|
||||
if (botAI->GetMaster() == from)
|
||||
return PLAYERBOT_SECURITY_ALLOW_ALL;
|
||||
|
||||
@@ -168,13 +179,8 @@ PlayerbotSecurityLevel PlayerbotSecurity::LevelFor(Player* from, DenyReason* rea
|
||||
|
||||
bool PlayerbotSecurity::CheckLevelFor(PlayerbotSecurityLevel level, bool silent, Player* from, bool ignoreGroup)
|
||||
{
|
||||
// If something is wrong with the pointers, we silently refuse
|
||||
if (!bot || !from || !from->GetSession())
|
||||
return false;
|
||||
|
||||
DenyReason reason = PLAYERBOT_DENY_NONE;
|
||||
PlayerbotSecurityLevel realLevel = LevelFor(from, &reason, ignoreGroup);
|
||||
|
||||
if (realLevel >= level || from == bot)
|
||||
return true;
|
||||
|
||||
@@ -183,17 +189,11 @@ bool PlayerbotSecurity::CheckLevelFor(PlayerbotSecurityLevel level, bool silent,
|
||||
return false;
|
||||
|
||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||
if (!botAI)
|
||||
Player* master = botAI->GetMaster();
|
||||
if (master && botAI && botAI->IsOpposing(master) && master->GetSession()->GetSecurity() < SEC_GAMEMASTER)
|
||||
return false;
|
||||
|
||||
Player* master = botAI->GetMaster();
|
||||
if (master && botAI->IsOpposing(master))
|
||||
if (WorldSession* session = master->GetSession())
|
||||
if (session->GetSecurity() < SEC_GAMEMASTER)
|
||||
return false;
|
||||
|
||||
std::ostringstream out;
|
||||
|
||||
switch (realLevel)
|
||||
{
|
||||
case PLAYERBOT_SECURITY_DENY_ALL:
|
||||
@@ -206,20 +206,19 @@ bool PlayerbotSecurity::CheckLevelFor(PlayerbotSecurityLevel level, bool silent,
|
||||
out << "I'll do it later";
|
||||
break;
|
||||
case PLAYERBOT_DENY_LOW_LEVEL:
|
||||
out << "You are too low level: |cffff0000" << uint32(from->GetLevel()) << "|cffffffff/|cff00ff00"
|
||||
<< uint32(bot->GetLevel());
|
||||
out << "You are too low level: |cffff0000" << (uint32)from->GetLevel() << "|cffffffff/|cff00ff00"
|
||||
<< (uint32)bot->GetLevel();
|
||||
break;
|
||||
case PLAYERBOT_DENY_GEARSCORE:
|
||||
{
|
||||
int botGS = int(botAI->GetEquipGearScore(bot));
|
||||
int fromGS = int(botAI->GetEquipGearScore(from));
|
||||
int botGS = (int)botAI->GetEquipGearScore(bot/*, false, false*/);
|
||||
int fromGS = (int)botAI->GetEquipGearScore(from/*, false, false*/);
|
||||
int diff = (100 * (botGS - fromGS) / botGS);
|
||||
int req = 12 * sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) / from->GetLevel();
|
||||
|
||||
out << "Your gearscore is too low: |cffff0000" << fromGS << "|cffffffff/|cff00ff00" << botGS
|
||||
<< " |cffff0000" << diff << "%|cffffffff/|cff00ff00" << req << "%";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case PLAYERBOT_DENY_NOT_YOURS:
|
||||
out << "I have a master already";
|
||||
break;
|
||||
@@ -238,10 +237,13 @@ bool PlayerbotSecurity::CheckLevelFor(PlayerbotSecurityLevel level, bool silent,
|
||||
case PLAYERBOT_DENY_FAR:
|
||||
{
|
||||
out << "You must be closer to invite me to your group. I am in ";
|
||||
|
||||
if (AreaTableEntry const* entry = sAreaTableStore.LookupEntry(bot->GetAreaId()))
|
||||
{
|
||||
out << " |cffffffff(|cffff0000" << entry->area_name[0] << "|cffffffff)";
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PLAYERBOT_DENY_FULL_GROUP:
|
||||
out << "I am in a full group. Will do it later";
|
||||
break;
|
||||
@@ -249,10 +251,15 @@ bool PlayerbotSecurity::CheckLevelFor(PlayerbotSecurityLevel level, bool silent,
|
||||
out << "I am currently leading a group. I can invite you if you want.";
|
||||
break;
|
||||
case PLAYERBOT_DENY_NOT_LEADER:
|
||||
if (Player* leader = botAI->GetGroupLeader())
|
||||
out << "I am in a group with " << leader->GetName() << ". You can ask him for invite.";
|
||||
if (botAI->GetGroupMaster())
|
||||
{
|
||||
out << "I am in a group with " << botAI->GetGroupMaster()->GetName()
|
||||
<< ". You can ask him for invite.";
|
||||
}
|
||||
else
|
||||
{
|
||||
out << "I am in a group with someone else. You can ask him for invite.";
|
||||
}
|
||||
break;
|
||||
case PLAYERBOT_DENY_BG:
|
||||
out << "I am in a queue for BG. Will do it later";
|
||||
@@ -276,14 +283,10 @@ bool PlayerbotSecurity::CheckLevelFor(PlayerbotSecurityLevel level, bool silent,
|
||||
std::string const text = out.str();
|
||||
ObjectGuid guid = from->GetGUID();
|
||||
time_t lastSaid = whispers[guid][text];
|
||||
|
||||
if (!lastSaid || (time(nullptr) - lastSaid) >= sPlayerbotAIConfig->repeatDelay / 1000)
|
||||
{
|
||||
whispers[guid][text] = time(nullptr);
|
||||
|
||||
// Additional protection against crashes during logout
|
||||
if (bot->IsInWorld() && from->IsInWorld())
|
||||
bot->Whisper(text, LANG_UNIVERSAL, from);
|
||||
bot->Whisper(text, LANG_UNIVERSAL, from);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -25,12 +25,10 @@
|
||||
#include "Metric.h"
|
||||
#include "PlayerScript.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "PlayerbotGuildMgr.h"
|
||||
#include "PlayerbotSpellRepository.h"
|
||||
#include "PlayerbotWorldThreadProcessor.h"
|
||||
#include "RandomPlayerbotMgr.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "PlayerbotCommandScript.h"
|
||||
#include "cs_playerbots.h"
|
||||
#include "cmath"
|
||||
#include "BattleGroundTactics.h"
|
||||
|
||||
@@ -84,12 +82,12 @@ public:
|
||||
PlayerbotsPlayerScript() : PlayerScript("PlayerbotsPlayerScript", {
|
||||
PLAYERHOOK_ON_LOGIN,
|
||||
PLAYERHOOK_ON_AFTER_UPDATE,
|
||||
PLAYERHOOK_ON_CHAT,
|
||||
PLAYERHOOK_ON_CHAT_WITH_CHANNEL,
|
||||
PLAYERHOOK_ON_CHAT_WITH_GROUP,
|
||||
PLAYERHOOK_ON_BEFORE_CRITERIA_PROGRESS,
|
||||
PLAYERHOOK_ON_BEFORE_ACHI_COMPLETE,
|
||||
PLAYERHOOK_CAN_PLAYER_USE_PRIVATE_CHAT,
|
||||
PLAYERHOOK_CAN_PLAYER_USE_GROUP_CHAT,
|
||||
PLAYERHOOK_CAN_PLAYER_USE_GUILD_CHAT,
|
||||
PLAYERHOOK_CAN_PLAYER_USE_CHANNEL_CHAT,
|
||||
PLAYERHOOK_ON_GIVE_EXP,
|
||||
PLAYERHOOK_ON_BEFORE_TELEPORT
|
||||
}) {}
|
||||
@@ -125,49 +123,24 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
bool OnPlayerBeforeTeleport(Player* /*player*/, uint32 /*mapid*/, float /*x*/, float /*y*/, float /*z*/, float /*orientation*/, uint32 /*options*/, Unit* /*target*/) override
|
||||
bool OnPlayerBeforeTeleport(Player* player, uint32 mapid, float /*x*/, float /*y*/, float /*z*/, float /*orientation*/, uint32 /*options*/, Unit* /*target*/) override
|
||||
{
|
||||
/* for now commmented out until proven its actually required
|
||||
* havent seen any proof CleanVisibilityReferences() is needed
|
||||
|
||||
// If the player is not safe to touch, do nothing
|
||||
if (!player)
|
||||
// Only apply to bots to prevent affecting real players
|
||||
if (!player || !player->GetSession()->IsBot())
|
||||
return true;
|
||||
|
||||
// If same map or not in world do nothing
|
||||
if (!player->IsInWorld() || player->GetMapId() == mapid)
|
||||
return true;
|
||||
// If changing maps, proactively clean visibility references to prevent
|
||||
// stale pointers in other players' visibility maps during the teleport.
|
||||
// This fixes a race condition where:
|
||||
// 1. Bot A teleports and its visible objects start getting cleaned up
|
||||
// 2. Bot B is simultaneously updating visibility and tries to access objects in Bot A's old visibility map
|
||||
// 3. Those objects may already be freed, causing a segmentation fault
|
||||
if (player->GetMapId() != mapid && player->IsInWorld())
|
||||
{
|
||||
player->GetObjectVisibilityContainer().CleanVisibilityReferences();
|
||||
}
|
||||
|
||||
// If real player do nothing
|
||||
PlayerbotAI* ai = GET_PLAYERBOT_AI(player);
|
||||
if (!ai || ai->IsRealPlayer())
|
||||
return true;
|
||||
|
||||
// Cross-map bot teleport: defer visibility reference cleanup.
|
||||
// CleanVisibilityReferences() erases this bot's GUID from other objects' visibility containers.
|
||||
// This is intentionally done via the event queue (instead of directly here) because erasing
|
||||
// from other players' visibility maps inside the teleport call stack can hit unsafe re-entrancy
|
||||
// or iterator invalidation while visibility updates are in progress
|
||||
ObjectGuid guid = player->GetGUID();
|
||||
player->m_Events.AddEventAtOffset(
|
||||
[guid, mapid]()
|
||||
{
|
||||
// do nothing, if the player is not safe to touch
|
||||
Player* p = ObjectAccessor::FindPlayer(guid);
|
||||
if (!p || !p->IsInWorld() || p->IsDuringRemoveFromWorld())
|
||||
return;
|
||||
|
||||
// do nothing if we are already on the target map
|
||||
if (p->GetMapId() == mapid)
|
||||
return;
|
||||
|
||||
p->GetObjectVisibilityContainer().CleanVisibilityReferences();
|
||||
},
|
||||
Milliseconds(0));
|
||||
|
||||
*/
|
||||
|
||||
return true;
|
||||
return true; // Allow teleport to continue
|
||||
}
|
||||
|
||||
void OnPlayerAfterUpdate(Player* player, uint32 diff) override
|
||||
@@ -191,17 +164,14 @@ public:
|
||||
{
|
||||
botAI->HandleCommand(type, msg, player);
|
||||
|
||||
// hotfix; otherwise the server will crash when whispering logout
|
||||
// https://github.com/mod-playerbots/mod-playerbots/pull/1838
|
||||
// TODO: find the root cause and solve it. (does not happen in party chat)
|
||||
if (msg == "logout")
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Group* group) override
|
||||
void OnPlayerChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Group* group) override
|
||||
{
|
||||
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
|
||||
{
|
||||
@@ -213,10 +183,9 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Guild* guild) override
|
||||
void OnPlayerChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg) override
|
||||
{
|
||||
if (type == CHAT_MSG_GUILD)
|
||||
{
|
||||
@@ -235,10 +204,9 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Channel* channel) override
|
||||
void OnPlayerChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Channel* channel) override
|
||||
{
|
||||
if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player))
|
||||
{
|
||||
@@ -249,7 +217,6 @@ public:
|
||||
}
|
||||
|
||||
sRandomPlayerbotMgr->HandleCommand(type, msg, player);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OnPlayerBeforeAchievementComplete(Player* player, AchievementEntry const* achievement) override
|
||||
@@ -364,9 +331,6 @@ public:
|
||||
|
||||
LOG_INFO("server.loading", ">> Loaded playerbots config in {} ms", GetMSTimeDiffToNow(oldMSTime));
|
||||
LOG_INFO("server.loading", " ");
|
||||
|
||||
sPlayerbotSpellRepository->Initialize();
|
||||
|
||||
LOG_INFO("server.loading", "Playerbots World Thread Processor initialized");
|
||||
}
|
||||
|
||||
@@ -503,8 +467,6 @@ public:
|
||||
void OnBattlegroundEnd(Battleground* bg, TeamId /*winnerTeam*/) override { bgStrategies.erase(bg->GetInstanceID()); }
|
||||
};
|
||||
|
||||
void AddPlayerbotsSecureLoginScripts();
|
||||
|
||||
void AddPlayerbotsScripts()
|
||||
{
|
||||
new PlayerbotsDatabaseScript();
|
||||
@@ -514,7 +476,6 @@ void AddPlayerbotsScripts()
|
||||
new PlayerbotsWorldScript();
|
||||
new PlayerbotsScript();
|
||||
new PlayerBotsBGScript();
|
||||
AddPlayerbotsSecureLoginScripts();
|
||||
AddPlayerbotsCommandscripts();
|
||||
PlayerBotsGuildValidationScript();
|
||||
|
||||
AddSC_playerbots_commandscript();
|
||||
}
|
||||
@@ -2834,20 +2834,22 @@ inline bool ContainsInternal(ItemTemplate const* proto, uint32 skillId)
|
||||
CreatureTemplateContainer const* creatures = sObjectMgr->GetCreatureTemplates();
|
||||
for (CreatureTemplateContainer::const_iterator itr = creatures->begin(); itr != creatures->end(); ++itr)
|
||||
{
|
||||
Trainer::Trainer* trainer = sObjectMgr->GetTrainer(itr->first);
|
||||
|
||||
if (!trainer)
|
||||
if (itr->second.trainer_type != TRAINER_TYPE_TRADESKILLS)
|
||||
continue;
|
||||
|
||||
if (trainer->GetTrainerType() != Trainer::Type::Tradeskill)
|
||||
uint32 trainerId = itr->second.Entry;
|
||||
TrainerSpellData const* trainer_spells = sObjectMgr->GetNpcTrainerSpells(trainerId);
|
||||
if (!trainer_spells)
|
||||
continue;
|
||||
|
||||
for (auto& spell : trainer->GetSpells())
|
||||
for (TrainerSpellMap::const_iterator iter = trainer_spells->spellList.begin();
|
||||
iter != trainer_spells->spellList.end(); ++iter)
|
||||
{
|
||||
if (spell.ReqSkillLine != skillId)
|
||||
TrainerSpell const* tSpell = &iter->second;
|
||||
if (!tSpell || tSpell->reqSkill != skillId)
|
||||
continue;
|
||||
|
||||
if (IsCraftedBy(proto, spell.SpellId))
|
||||
if (IsCraftedBy(proto, tSpell->spell))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "GuildMgr.h"
|
||||
#include "PlayerbotFactory.h"
|
||||
#include "Playerbots.h"
|
||||
#include "PlayerbotGuildMgr.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "SocialMgr.h"
|
||||
@@ -755,6 +754,187 @@ void RandomPlayerbotFactory::CreateRandomBots()
|
||||
sPlayerbotAIConfig->randomBotAccounts.size(), totalRandomBotChars);
|
||||
}
|
||||
|
||||
void RandomPlayerbotFactory::CreateRandomGuilds()
|
||||
{
|
||||
std::vector<uint32> randomBots;
|
||||
|
||||
PlayerbotsDatabasePreparedStatement* stmt = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_SEL_RANDOM_BOTS_BOT);
|
||||
stmt->SetData(0, "add");
|
||||
if (PreparedQueryResult result = PlayerbotsDatabase.Query(stmt))
|
||||
{
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
uint32 bot = fields[0].Get<uint32>();
|
||||
randomBots.push_back(bot);
|
||||
} while (result->NextRow());
|
||||
}
|
||||
|
||||
if (sPlayerbotAIConfig->deleteRandomBotGuilds)
|
||||
{
|
||||
LOG_INFO("playerbots", "Deleting random bot guilds...");
|
||||
for (std::vector<uint32>::iterator i = randomBots.begin(); i != randomBots.end(); ++i)
|
||||
{
|
||||
if (Guild* guild = sGuildMgr->GetGuildByLeader(ObjectGuid::Create<HighGuid::Player>(*i)))
|
||||
guild->Disband();
|
||||
}
|
||||
|
||||
LOG_INFO("playerbots", "Random bot guilds deleted");
|
||||
}
|
||||
|
||||
std::unordered_set<uint32> botAccounts;
|
||||
botAccounts.reserve(sPlayerbotAIConfig->randomBotAccounts.size());
|
||||
for (uint32 acc : sPlayerbotAIConfig->randomBotAccounts)
|
||||
botAccounts.insert(acc);
|
||||
|
||||
// Recount bot guilds directly from the database (does not depend on connected bots)
|
||||
uint32 guildNumber = 0;
|
||||
sPlayerbotAIConfig->randomBotGuilds.clear();
|
||||
sPlayerbotAIConfig->randomBotGuilds.shrink_to_fit(); // avoids accumulating old capacity
|
||||
|
||||
if (!botAccounts.empty())
|
||||
{
|
||||
if (QueryResult res = CharacterDatabase.Query(
|
||||
// We only retrieve what is necessary (guildid, leader account)
|
||||
"SELECT g.guildid, c.account "
|
||||
"FROM guild g JOIN characters c ON g.leaderguid = c.guid"))
|
||||
{
|
||||
do
|
||||
{
|
||||
Field* f = res->Fetch();
|
||||
const uint32 guildId = f[0].Get<uint32>();
|
||||
const uint32 accountId = f[1].Get<uint32>();
|
||||
|
||||
// Determine if guild leader's account is a bot account.
|
||||
if (botAccounts.find(accountId) != botAccounts.end())
|
||||
{
|
||||
++guildNumber;
|
||||
sPlayerbotAIConfig->randomBotGuilds.push_back(guildId);
|
||||
}
|
||||
} while (res->NextRow());
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INFO("playerbots", "{}/{} random bot guilds exist in guild table",guildNumber, sPlayerbotAIConfig->randomBotGuildCount);
|
||||
if (guildNumber >= sPlayerbotAIConfig->randomBotGuildCount)
|
||||
{
|
||||
LOG_DEBUG("playerbots", "No new random guilds required");
|
||||
return;
|
||||
}
|
||||
|
||||
// We list the available leaders (online bots, not in guilds)
|
||||
GuidVector availableLeaders;
|
||||
availableLeaders.reserve(randomBots.size()); // limit reallocs
|
||||
for (const uint32 botLowGuid : randomBots)
|
||||
{
|
||||
ObjectGuid leader = ObjectGuid::Create<HighGuid::Player>(botLowGuid);
|
||||
if (sGuildMgr->GetGuildByLeader(leader))
|
||||
{
|
||||
// already GuildLeader -> ignored
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Player* player = ObjectAccessor::FindPlayer(leader))
|
||||
{
|
||||
if (!player->GetGuildId())
|
||||
availableLeaders.push_back(leader);
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG_DEBUG("playerbots", "{} available leaders for new guilds found", availableLeaders.size());
|
||||
|
||||
// Create up to randomBotGuildCount by counting only EFFECTIVE creations
|
||||
uint32 createdThisRun = 0;
|
||||
for (; guildNumber < sPlayerbotAIConfig->randomBotGuildCount; /* ++guildNumber -> done only if creation */)
|
||||
{
|
||||
std::string const guildName = CreateRandomGuildName();
|
||||
if (guildName.empty())
|
||||
break; // no more names available in playerbots_guild_names
|
||||
|
||||
if (sGuildMgr->GetGuildByName(guildName))
|
||||
continue; // name already taken, skip
|
||||
|
||||
if (availableLeaders.empty())
|
||||
{
|
||||
LOG_ERROR("playerbots", "No leaders for random guilds available");
|
||||
break; // no more leaders: we can no longer progress without distorting the counter
|
||||
}
|
||||
|
||||
uint32 index = urand(0, availableLeaders.size() - 1);
|
||||
ObjectGuid leader = availableLeaders[index];
|
||||
availableLeaders.erase(availableLeaders.begin() + index); // Removes the chosen leader to avoid re-selecting it repeatedly
|
||||
|
||||
Player* player = ObjectAccessor::FindPlayer(leader);
|
||||
if (!player)
|
||||
{
|
||||
LOG_ERROR("playerbots", "ObjectAccessor Cannot find player to set leader for guild {} . Skipped...",
|
||||
guildName.c_str());
|
||||
// we will try with other leaders in the next round (guildNumber is not incremented)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (player->GetGuildId())
|
||||
{
|
||||
// leader already in guild -> we don't advance the counter, we move on to the next one
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG_DEBUG("playerbots", "Creating guild name='{}' leader='{}'...", guildName.c_str(), player->GetName().c_str());
|
||||
|
||||
Guild* guild = new Guild();
|
||||
if (!guild->Create(player, guildName))
|
||||
{
|
||||
LOG_ERROR("playerbots", "Error creating guild [ {} ] with leader [ {} ]", guildName.c_str(),
|
||||
player->GetName().c_str());
|
||||
delete guild;
|
||||
continue;
|
||||
}
|
||||
|
||||
sGuildMgr->AddGuild(guild);
|
||||
|
||||
LOG_DEBUG("playerbots", "Guild created: id={} name='{}'", guild->GetId(), guildName.c_str());
|
||||
|
||||
// create random emblem
|
||||
uint32 st, cl, br, bc, bg;
|
||||
bg = urand(0, 51);
|
||||
bc = urand(0, 17);
|
||||
cl = urand(0, 17);
|
||||
br = urand(0, 7);
|
||||
st = urand(0, 180);
|
||||
|
||||
LOG_DEBUG("playerbots",
|
||||
"[TABARD] new guild id={} random -> style={}, color={}, borderStyle={}, borderColor={}, bgColor={}",
|
||||
guild->GetId(), st, cl, br, bc, bg);
|
||||
|
||||
// populate guild table with a random tabard design
|
||||
CharacterDatabase.Execute(
|
||||
"UPDATE guild SET EmblemStyle={}, EmblemColor={}, BorderStyle={}, BorderColor={}, BackgroundColor={} "
|
||||
"WHERE guildid={}",
|
||||
st, cl, br, bc, bg, guild->GetId());
|
||||
LOG_DEBUG("playerbots", "[TABARD] UPDATE done for guild id={}", guild->GetId());
|
||||
|
||||
// Immediate reading for log
|
||||
if (QueryResult qr = CharacterDatabase.Query(
|
||||
"SELECT EmblemStyle,EmblemColor,BorderStyle,BorderColor,BackgroundColor FROM guild WHERE guildid={}",
|
||||
guild->GetId()))
|
||||
{
|
||||
Field* f = qr->Fetch();
|
||||
LOG_DEBUG("playerbots",
|
||||
"[TABARD] DB check guild id={} => style={}, color={}, borderStyle={}, borderColor={}, bgColor={}",
|
||||
guild->GetId(), f[0].Get<uint8>(), f[1].Get<uint8>(), f[2].Get<uint8>(), f[3].Get<uint8>(), f[4].Get<uint8>());
|
||||
}
|
||||
|
||||
sPlayerbotAIConfig->randomBotGuilds.push_back(guild->GetId());
|
||||
// The guild is only counted if it is actually created
|
||||
++guildNumber;
|
||||
++createdThisRun;
|
||||
}
|
||||
|
||||
// Shows the true total and how many were created during this run
|
||||
LOG_INFO("playerbots", "{} random bot guilds created this run)", createdThisRun);
|
||||
}
|
||||
|
||||
std::string const RandomPlayerbotFactory::CreateRandomGuildName()
|
||||
{
|
||||
std::string guildName = "";
|
||||
@@ -51,6 +51,7 @@ public:
|
||||
|
||||
Player* CreateRandomBot(WorldSession* session, uint8 cls, std::unordered_map<NameRaceAndGender, std::vector<std::string>>& names);
|
||||
static void CreateRandomBots();
|
||||
static void CreateRandomGuilds();
|
||||
static void CreateRandomArenaTeams(ArenaType slot, uint32 count);
|
||||
static std::string const CreateRandomGuildName();
|
||||
static uint32 CalculateTotalAccountCount();
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "DatabaseEnv.h"
|
||||
#include "Define.h"
|
||||
#include "FleeManager.h"
|
||||
#include "GameTime.h"
|
||||
#include "GridNotifiers.h"
|
||||
#include "GridNotifiersImpl.h"
|
||||
#include "GuildMgr.h"
|
||||
@@ -35,7 +36,7 @@
|
||||
#include "NewRpgInfo.h"
|
||||
#include "NewRpgStrategy.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "PerfMonitor.h"
|
||||
#include "PerformanceMonitor.h"
|
||||
#include "Player.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
@@ -358,7 +359,7 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/)
|
||||
if (totalPmo)
|
||||
totalPmo->finish();
|
||||
|
||||
totalPmo = sPerfMonitor->start(PERF_MON_TOTAL, "RandomPlayerbotMgr::FullTick");
|
||||
totalPmo = sPerformanceMonitor->start(PERF_MON_TOTAL, "RandomPlayerbotMgr::FullTick");
|
||||
|
||||
if (!sPlayerbotAIConfig->randomBotAutologin || !sPlayerbotAIConfig->enabled)
|
||||
return;
|
||||
@@ -401,7 +402,7 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/)
|
||||
uint32 updateIntervalTurboBoost = _isBotInitializing ? 1 : sPlayerbotAIConfig->randomBotUpdateInterval;
|
||||
SetNextCheckDelay(updateIntervalTurboBoost * (onlineBotFocus + 25) * 10);
|
||||
|
||||
PerfMonitorOperation* pmo = sPerfMonitor->start(
|
||||
PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(
|
||||
PERF_MON_TOTAL,
|
||||
onlineBotCount < maxAllowedBotCount ? "RandomPlayerbotMgr::Login" : "RandomPlayerbotMgr::UpdateAIInternal");
|
||||
|
||||
@@ -669,9 +670,9 @@ void RandomPlayerbotMgr::AssignAccountTypes()
|
||||
uint32 toAssign = neededAddClassAccounts - existingAddClassAccounts;
|
||||
uint32 assigned = 0;
|
||||
|
||||
for (size_t idx = allRandomBotAccounts.size(); idx-- > 0 && assigned < toAssign;)
|
||||
for (int i = allRandomBotAccounts.size() - 1; i >= 0 && assigned < toAssign; i--)
|
||||
{
|
||||
uint32 accountId = allRandomBotAccounts[idx];
|
||||
uint32 accountId = allRandomBotAccounts[i];
|
||||
if (currentAssignments[accountId] == 0) // Unassigned
|
||||
{
|
||||
PlayerbotsDatabase.Execute("UPDATE playerbots_account_type SET account_type = 2, assignment_date = NOW() WHERE account_id = {}", accountId);
|
||||
@@ -1424,7 +1425,7 @@ bool RandomPlayerbotMgr::ProcessBot(uint32 bot)
|
||||
LOG_DEBUG("playerbots", "Bot #{}: log out", bot);
|
||||
|
||||
SetEventValue(bot, "add", 0, 0);
|
||||
currentBots.remove(bot);
|
||||
currentBots.erase(std::remove(currentBots.begin(), currentBots.end(), bot), currentBots.end());
|
||||
|
||||
if (player)
|
||||
LogoutPlayerBot(botGUID);
|
||||
@@ -1479,10 +1480,10 @@ bool RandomPlayerbotMgr::ProcessBot(uint32 bot)
|
||||
if (!sRandomPlayerbotMgr->IsRandomBot(player))
|
||||
update = false;
|
||||
|
||||
if (player->GetGroup() && botAI->GetGroupLeader())
|
||||
if (player->GetGroup() && botAI->GetGroupMaster())
|
||||
{
|
||||
PlayerbotAI* groupLeaderBotAI = GET_PLAYERBOT_AI(botAI->GetGroupLeader());
|
||||
if (!groupLeaderBotAI || groupLeaderBotAI->IsRealPlayer())
|
||||
PlayerbotAI* groupMasterBotAI = GET_PLAYERBOT_AI(botAI->GetGroupMaster());
|
||||
if (!groupMasterBotAI || groupMasterBotAI->IsRealPlayer())
|
||||
{
|
||||
update = false;
|
||||
}
|
||||
@@ -1654,10 +1655,6 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector<WorldLocation>&
|
||||
if (bot->IsBeingTeleported() || !bot->IsInWorld())
|
||||
return;
|
||||
|
||||
// no teleport / movement update when rooted.
|
||||
if (bot->IsRooted())
|
||||
return;
|
||||
|
||||
// ignore when in queue for battle grounds.
|
||||
if (bot->InBattlegroundQueue())
|
||||
return;
|
||||
@@ -1707,7 +1704,7 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector<WorldLocation>&
|
||||
return;
|
||||
}
|
||||
|
||||
PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "RandomTeleportByLocations");
|
||||
PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "RandomTeleportByLocations");
|
||||
|
||||
std::shuffle(std::begin(tlocs), std::end(tlocs), RandomEngine::Instance());
|
||||
for (uint32 i = 0; i < tlocs.size(); i++)
|
||||
@@ -2300,7 +2297,7 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot)
|
||||
if (bot->InBattleground())
|
||||
return;
|
||||
|
||||
PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "RandomTeleport");
|
||||
PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "RandomTeleport");
|
||||
std::vector<WorldLocation> locs;
|
||||
|
||||
std::list<Unit*> targets;
|
||||
@@ -2362,7 +2359,7 @@ void RandomPlayerbotMgr::IncreaseLevel(Player* bot)
|
||||
if (maxLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
|
||||
maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
|
||||
|
||||
PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "IncreaseLevel");
|
||||
PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "IncreaseLevel");
|
||||
uint32 lastLevel = GetValue(bot, "level");
|
||||
uint8 level = bot->GetLevel() + 1;
|
||||
if (level > maxLevel)
|
||||
@@ -2401,7 +2398,7 @@ void RandomPlayerbotMgr::RandomizeFirst(Player* bot)
|
||||
minLevel = std::max(minLevel, sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL));
|
||||
}
|
||||
|
||||
PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "RandomizeFirst");
|
||||
PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "RandomizeFirst");
|
||||
|
||||
uint32 level;
|
||||
|
||||
@@ -2480,7 +2477,7 @@ void RandomPlayerbotMgr::RandomizeMin(Player* bot)
|
||||
if (!botAI)
|
||||
return;
|
||||
|
||||
PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "RandomizeMin");
|
||||
PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "RandomizeMin");
|
||||
uint32 level = sPlayerbotAIConfig->randomBotMinLevel;
|
||||
SetValue(bot, "level", level);
|
||||
PlayerbotFactory factory(bot, level);
|
||||
@@ -2569,7 +2566,7 @@ void RandomPlayerbotMgr::Refresh(Player* bot)
|
||||
|
||||
LOG_DEBUG("playerbots", "Refreshing bot {} <{}>", bot->GetGUID().ToString().c_str(), bot->GetName().c_str());
|
||||
|
||||
PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "Refresh");
|
||||
PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "Refresh");
|
||||
|
||||
botAI->Reset();
|
||||
|
||||
@@ -2715,73 +2712,69 @@ std::vector<uint32> RandomPlayerbotMgr::GetBgBots(uint32 bracket)
|
||||
return std::move(BgBots);
|
||||
}
|
||||
|
||||
CachedEvent* RandomPlayerbotMgr::FindEvent(uint32 bot, std::string const& event)
|
||||
uint32 RandomPlayerbotMgr::GetEventValue(uint32 bot, std::string const event)
|
||||
{
|
||||
BotEventCache& cache = eventCache[bot];
|
||||
|
||||
// Load once
|
||||
if (!cache.loaded)
|
||||
// load all events at once on first event load
|
||||
if (eventCache[bot].empty())
|
||||
{
|
||||
cache.events.clear();
|
||||
|
||||
PlayerbotsDatabasePreparedStatement* stmt =
|
||||
PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_SEL_RANDOM_BOTS_BY_OWNER_AND_BOT);
|
||||
stmt->SetData(0, 0);
|
||||
stmt->SetData(1, bot);
|
||||
|
||||
if (PreparedQueryResult result = PlayerbotsDatabase.Query(stmt))
|
||||
{
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
std::string const eventName = fields[0].Get<std::string>();
|
||||
|
||||
CachedEvent e;
|
||||
e.value = fields[1].Get<uint32>();
|
||||
e.lastChangeTime = fields[2].Get<uint32>();
|
||||
e.validIn = fields[3].Get<uint32>();
|
||||
e.data = fields[4].Get<std::string>();
|
||||
|
||||
cache.events.emplace(fields[0].Get<std::string>(), std::move(e));
|
||||
eventCache[bot][eventName] = std::move(e);
|
||||
} while (result->NextRow());
|
||||
}
|
||||
|
||||
cache.loaded = true;
|
||||
}
|
||||
|
||||
auto it = cache.events.find(event);
|
||||
if (it == cache.events.end())
|
||||
return nullptr;
|
||||
|
||||
CachedEvent& e = it->second;
|
||||
|
||||
// remove expired events
|
||||
if (e.validIn && (NowSeconds() - e.lastChangeTime) >= e.validIn && event != "specNo" && event != "specLink")
|
||||
CachedEvent& e = eventCache[bot][event];
|
||||
/*if (e.IsEmpty())
|
||||
{
|
||||
cache.events.erase(it);
|
||||
return nullptr;
|
||||
QueryResult results = PlayerbotsDatabase.Query("SELECT `value`, `time`, validIn, `data` FROM
|
||||
playerbots_random_bots WHERE owner = 0 AND bot = {} AND event = {}", bot, event.c_str());
|
||||
|
||||
if (results)
|
||||
{
|
||||
Field* fields = results->Fetch();
|
||||
e.value = fields[0].Get<uint32>();
|
||||
e.lastChangeTime = fields[1].Get<uint32>();
|
||||
e.validIn = fields[2].Get<uint32>();
|
||||
e.data = fields[3].Get<std::string>();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if ((time(0) - e.lastChangeTime) >= e.validIn && event != "specNo" && event != "specLink")
|
||||
e.value = 0;
|
||||
|
||||
return e.value;
|
||||
}
|
||||
|
||||
std::string const RandomPlayerbotMgr::GetEventData(uint32 bot, std::string const event)
|
||||
{
|
||||
std::string data = "";
|
||||
if (GetEventValue(bot, event))
|
||||
{
|
||||
CachedEvent e = eventCache[bot][event];
|
||||
data = e.data;
|
||||
}
|
||||
|
||||
return &e;
|
||||
return data;
|
||||
}
|
||||
|
||||
uint32 RandomPlayerbotMgr::GetEventValue(uint32 bot, std::string const& event)
|
||||
{
|
||||
if (CachedEvent* e = FindEvent(bot, event))
|
||||
return e->value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string RandomPlayerbotMgr::GetEventData(uint32 bot, std::string const& event)
|
||||
{
|
||||
if (CachedEvent* e = FindEvent(bot, event))
|
||||
return e->data;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
uint32 RandomPlayerbotMgr::SetEventValue(uint32 bot, std::string const& event, uint32 value, uint32 validIn,
|
||||
std::string const& data)
|
||||
uint32 RandomPlayerbotMgr::SetEventValue(uint32 bot, std::string const event, uint32 value, uint32 validIn,
|
||||
std::string const data)
|
||||
{
|
||||
PlayerbotsDatabaseTransaction trans = PlayerbotsDatabase.BeginTransaction();
|
||||
|
||||
@@ -2797,55 +2790,43 @@ uint32 RandomPlayerbotMgr::SetEventValue(uint32 bot, std::string const& event, u
|
||||
stmt = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_INS_RANDOM_BOTS);
|
||||
stmt->SetData(0, 0);
|
||||
stmt->SetData(1, bot);
|
||||
stmt->SetData(2, NowSeconds());
|
||||
stmt->SetData(2, static_cast<uint32>(GameTime::GetGameTime().count()));
|
||||
stmt->SetData(3, validIn);
|
||||
stmt->SetData(4, event.c_str());
|
||||
stmt->SetData(5, value);
|
||||
|
||||
if (!data.empty())
|
||||
if (data != "")
|
||||
{
|
||||
stmt->SetData(6, data.c_str());
|
||||
}
|
||||
else
|
||||
stmt->SetData(6); // NULL
|
||||
|
||||
{
|
||||
stmt->SetData(6);
|
||||
}
|
||||
trans->Append(stmt);
|
||||
}
|
||||
|
||||
PlayerbotsDatabase.CommitTransaction(trans);
|
||||
|
||||
// Update in-memory cache
|
||||
BotEventCache& cache = eventCache[bot];
|
||||
cache.loaded = true;
|
||||
|
||||
if (!value)
|
||||
{
|
||||
cache.events.erase(event);
|
||||
return 0;
|
||||
}
|
||||
|
||||
CachedEvent& e = cache.events[event]; // create-on-write is OK here
|
||||
e.value = value;
|
||||
e.lastChangeTime = NowSeconds();
|
||||
e.validIn = validIn;
|
||||
e.data = data;
|
||||
|
||||
CachedEvent e(value, (uint32)time(nullptr), validIn, data);
|
||||
eventCache[bot][event] = std::move(e);
|
||||
return value;
|
||||
}
|
||||
|
||||
uint32 RandomPlayerbotMgr::GetValue(uint32 bot, std::string const& type) { return GetEventValue(bot, type); }
|
||||
uint32 RandomPlayerbotMgr::GetValue(uint32 bot, std::string const type) { return GetEventValue(bot, type); }
|
||||
|
||||
uint32 RandomPlayerbotMgr::GetValue(Player* bot, std::string const& type)
|
||||
uint32 RandomPlayerbotMgr::GetValue(Player* bot, std::string const type)
|
||||
{
|
||||
return GetValue(bot->GetGUID().GetCounter(), type);
|
||||
}
|
||||
|
||||
std::string RandomPlayerbotMgr::GetData(uint32 bot, std::string const& type) { return GetEventData(bot, type); }
|
||||
std::string const RandomPlayerbotMgr::GetData(uint32 bot, std::string const type) { return GetEventData(bot, type); }
|
||||
|
||||
void RandomPlayerbotMgr::SetValue(uint32 bot, std::string const& type, uint32 value, std::string const& data)
|
||||
void RandomPlayerbotMgr::SetValue(uint32 bot, std::string const type, uint32 value, std::string const data)
|
||||
{
|
||||
SetEventValue(bot, type, value, sPlayerbotAIConfig->maxRandomBotInWorldTime, data);
|
||||
}
|
||||
|
||||
void RandomPlayerbotMgr::SetValue(Player* bot, std::string const& type, uint32 value, std::string const& data)
|
||||
void RandomPlayerbotMgr::SetValue(Player* bot, std::string const type, uint32 value, std::string const data)
|
||||
{
|
||||
SetValue(bot->GetGUID().GetCounter(), type, value, data);
|
||||
}
|
||||
@@ -3134,7 +3115,7 @@ void RandomPlayerbotMgr::OnPlayerLogin(Player* player)
|
||||
void RandomPlayerbotMgr::OnPlayerLoginError(uint32 bot)
|
||||
{
|
||||
SetEventValue(bot, "add", 0, 0);
|
||||
currentBots.remove(bot);
|
||||
currentBots.erase(std::remove(currentBots.begin(), currentBots.end(), bot), currentBots.end());
|
||||
}
|
||||
|
||||
Player* RandomPlayerbotMgr::GetRandomPlayer()
|
||||
@@ -3214,12 +3195,6 @@ void RandomPlayerbotMgr::PrintStats()
|
||||
lvlPerRace[bot->getRace()] += bot->GetLevel();
|
||||
|
||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||
if (!botAI)
|
||||
{
|
||||
LOG_ERROR("playerbots", "Player/Bot {} is registered in sRandomPlayerbotMgr playerBots and has no bot AI!", bot->GetName().c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (botAI->AllowActivity())
|
||||
++active;
|
||||
|
||||
@@ -3522,8 +3497,7 @@ void RandomPlayerbotMgr::Remove(Player* bot)
|
||||
stmt->SetData(1, owner.GetCounter());
|
||||
PlayerbotsDatabase.Execute(stmt);
|
||||
|
||||
uint32 botId = owner.GetCounter();
|
||||
eventCache.erase(botId);
|
||||
eventCache[owner.GetCounter()].clear();
|
||||
|
||||
LogoutPlayerBot(owner);
|
||||
}
|
||||
@@ -3540,7 +3514,7 @@ CreatureData const* RandomPlayerbotMgr::GetCreatureDataByEntry(uint32 entry)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ObjectGuid RandomPlayerbotMgr::GetBattleMasterGUID(Player* bot, BattlegroundTypeId bgTypeId)
|
||||
ObjectGuid const RandomPlayerbotMgr::GetBattleMasterGUID(Player* bot, BattlegroundTypeId bgTypeId)
|
||||
{
|
||||
ObjectGuid battleMasterGUID = ObjectGuid::Empty;
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "NewRpgInfo.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "PlayerbotMgr.h"
|
||||
#include "GameTime.h"
|
||||
|
||||
struct BattlegroundInfo
|
||||
{
|
||||
@@ -43,23 +42,28 @@ struct BattlegroundInfo
|
||||
};
|
||||
|
||||
class ChatHandler;
|
||||
class PerfMonitorOperation;
|
||||
class PerformanceMonitorOperation;
|
||||
class WorldLocation;
|
||||
|
||||
struct CachedEvent
|
||||
class CachedEvent
|
||||
{
|
||||
uint32 value = 0;
|
||||
uint32 lastChangeTime = 0;
|
||||
uint32 validIn = 0;
|
||||
public:
|
||||
CachedEvent() : value(0), lastChangeTime(0), validIn(0), data("") {}
|
||||
CachedEvent(const CachedEvent& other)
|
||||
: value(other.value), lastChangeTime(other.lastChangeTime), validIn(other.validIn), data(other.data)
|
||||
{
|
||||
}
|
||||
CachedEvent(uint32 value, uint32 lastChangeTime, uint32 validIn, std::string const data = "")
|
||||
: value(value), lastChangeTime(lastChangeTime), validIn(validIn), data(data)
|
||||
{
|
||||
}
|
||||
|
||||
bool IsEmpty() { return !lastChangeTime; }
|
||||
|
||||
uint32 value;
|
||||
uint32 lastChangeTime;
|
||||
uint32 validIn;
|
||||
std::string data;
|
||||
|
||||
bool IsEmpty() const { return !lastChangeTime; }
|
||||
};
|
||||
|
||||
struct BotEventCache
|
||||
{
|
||||
bool loaded = false;
|
||||
std::unordered_map<std::string, CachedEvent> events;
|
||||
};
|
||||
|
||||
// https://gist.github.com/bradley219/5373998
|
||||
@@ -135,13 +139,13 @@ public:
|
||||
void Revive(Player* player);
|
||||
void ChangeStrategy(Player* player);
|
||||
void ChangeStrategyOnce(Player* player);
|
||||
uint32 GetValue(Player* bot, std::string const& type);
|
||||
uint32 GetValue(uint32 bot, std::string const& type);
|
||||
std::string GetData(uint32 bot, std::string const& type);
|
||||
void SetValue(uint32 bot, std::string const& type, uint32 value, std::string const& data = "");
|
||||
void SetValue(Player* bot, std::string const& type, uint32 value, std::string const& data = "");
|
||||
uint32 GetValue(Player* bot, std::string const type);
|
||||
uint32 GetValue(uint32 bot, std::string const type);
|
||||
std::string const GetData(uint32 bot, std::string const type);
|
||||
void SetValue(uint32 bot, std::string const type, uint32 value, std::string const data = "");
|
||||
void SetValue(Player* bot, std::string const type, uint32 value, std::string const data = "");
|
||||
void Remove(Player* bot);
|
||||
ObjectGuid GetBattleMasterGUID(Player* bot, BattlegroundTypeId bgTypeId);
|
||||
ObjectGuid const GetBattleMasterGUID(Player* bot, BattlegroundTypeId bgTypeId);
|
||||
CreatureData const* GetCreatureDataByEntry(uint32 entry);
|
||||
void LoadBattleMastersCache();
|
||||
std::map<uint32, std::map<uint32, BattlegroundInfo>> BattlegroundData;
|
||||
@@ -199,11 +203,10 @@ private:
|
||||
bool _isBotInitializing = true;
|
||||
bool _isBotLogging = true;
|
||||
NewRpgStatistic rpgStasticTotal;
|
||||
CachedEvent* FindEvent(uint32 bot, std::string const& event);
|
||||
uint32 GetEventValue(uint32 bot, std::string const& event);
|
||||
std::string GetEventData(uint32 bot, std::string const& event);
|
||||
uint32 SetEventValue(uint32 bot, std::string const& event, uint32 value, uint32 validIn,
|
||||
std::string const& data = "");
|
||||
uint32 GetEventValue(uint32 bot, std::string const event);
|
||||
std::string const GetEventData(uint32 bot, std::string const event);
|
||||
uint32 SetEventValue(uint32 bot, std::string const event, uint32 value, uint32 validIn,
|
||||
std::string const data = "");
|
||||
void GetBots();
|
||||
std::vector<uint32> GetBgBots(uint32 bracket);
|
||||
time_t BgCheckTimer;
|
||||
@@ -225,7 +228,7 @@ private:
|
||||
// std::map<uint32, std::vector<WorldLocation>> rpgLocsCache;
|
||||
std::map<uint32, std::map<uint32, std::vector<WorldLocation>>> rpgLocsCacheLevel;
|
||||
std::map<TeamId, std::map<BattlegroundTypeId, std::vector<uint32>>> BattleMastersCache;
|
||||
std::unordered_map<uint32, BotEventCache> eventCache;
|
||||
std::map<uint32, std::map<std::string, CachedEvent>> eventCache;
|
||||
std::list<uint32> currentBots;
|
||||
uint32 bgBotsCount;
|
||||
uint32 playersLevel;
|
||||
@@ -235,7 +238,6 @@ private:
|
||||
std::vector<uint32> addClassTypeAccounts; // Accounts marked as AddClass (type 2)
|
||||
|
||||
//void ScaleBotActivity(); // Deprecated function
|
||||
static inline uint32 NowSeconds() { return static_cast<uint32>(GameTime::GetGameTime().count()); }
|
||||
};
|
||||
|
||||
#define sRandomPlayerbotMgr RandomPlayerbotMgr::instance()
|
||||
@@ -1,165 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "BloodDKStrategy.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
class BloodDKStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
|
||||
{
|
||||
public:
|
||||
BloodDKStrategyActionNodeFactory()
|
||||
{
|
||||
creators["rune strike"] = &rune_strike;
|
||||
creators["heart strike"] = &heart_strike;
|
||||
creators["death strike"] = &death_strike;
|
||||
creators["icy touch"] = &icy_touch;
|
||||
creators["dark command"] = &dark_command;
|
||||
creators["taunt spell"] = &dark_command;
|
||||
}
|
||||
|
||||
private:
|
||||
static ActionNode* rune_strike([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"rune strike",
|
||||
{
|
||||
NextAction("frost presence")
|
||||
},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
static ActionNode* icy_touch([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"icy touch",
|
||||
{
|
||||
NextAction("frost presence")
|
||||
},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
static ActionNode* heart_strike([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"heart strike",
|
||||
{
|
||||
NextAction("frost presence")
|
||||
},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* death_strike([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"death strike",
|
||||
{
|
||||
NextAction("frost presence")
|
||||
},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
static ActionNode* dark_command([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"dark command",
|
||||
{
|
||||
NextAction("frost presence")
|
||||
},
|
||||
/*A*/ {
|
||||
NextAction("death grip")
|
||||
},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
BloodDKStrategy::BloodDKStrategy(PlayerbotAI* botAI) : GenericDKStrategy(botAI)
|
||||
{
|
||||
actionNodeFactories.Add(new BloodDKStrategyActionNodeFactory());
|
||||
}
|
||||
|
||||
std::vector<NextAction> BloodDKStrategy::getDefaultActions()
|
||||
{
|
||||
return {
|
||||
NextAction("rune strike", ACTION_DEFAULT + 0.8f),
|
||||
NextAction("icy touch", ACTION_DEFAULT + 0.7f),
|
||||
NextAction("heart strike", ACTION_DEFAULT + 0.6f),
|
||||
NextAction("blood strike", ACTION_DEFAULT + 0.5f),
|
||||
NextAction("dancing rune weapon", ACTION_DEFAULT + 0.4f),
|
||||
NextAction("death coil", ACTION_DEFAULT + 0.3f),
|
||||
NextAction("plague strike", ACTION_DEFAULT + 0.2f),
|
||||
NextAction("horn of winter", ACTION_DEFAULT + 0.1f),
|
||||
NextAction("melee", ACTION_DEFAULT)
|
||||
};
|
||||
}
|
||||
|
||||
void BloodDKStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
GenericDKStrategy::InitTriggers(triggers);
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"rune strike",
|
||||
{
|
||||
NextAction("rune strike", ACTION_NORMAL + 3)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"blood tap",
|
||||
{
|
||||
NextAction("blood tap", ACTION_HIGH + 5)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"lose aggro",
|
||||
{
|
||||
NextAction("dark command", ACTION_HIGH + 3)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"low health",
|
||||
{
|
||||
NextAction("army of the dead", ACTION_HIGH + 4),
|
||||
NextAction("death strike", ACTION_HIGH + 3)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"critical health",
|
||||
{
|
||||
NextAction("vampiric blood", ACTION_HIGH + 5)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"icy touch",
|
||||
{
|
||||
NextAction("icy touch", ACTION_HIGH + 2)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"plague strike",
|
||||
{
|
||||
NextAction("plague strike", ACTION_HIGH + 2)
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "FrostDKStrategy.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
class FrostDKStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
|
||||
{
|
||||
public:
|
||||
FrostDKStrategyActionNodeFactory()
|
||||
{
|
||||
creators["icy touch"] = &icy_touch;
|
||||
creators["obliterate"] = &obliterate;
|
||||
creators["howling blast"] = &howling_blast;
|
||||
creators["frost strike"] = &frost_strike;
|
||||
creators["rune strike"] = &rune_strike;
|
||||
creators["unbreakable armor"] = &unbreakable_armor;
|
||||
}
|
||||
|
||||
private:
|
||||
static ActionNode* icy_touch([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"icy touch",
|
||||
/*P*/ { NextAction("blood presence") },
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* obliterate([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"obliterate",
|
||||
/*P*/ { NextAction("blood presence") },
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* rune_strike([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"rune strike",
|
||||
/*P*/ { NextAction("blood presence") },
|
||||
/*A*/ { NextAction("melee") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* frost_strike([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"frost strike",
|
||||
/*P*/ { NextAction("blood presence") },
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* howling_blast([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"howling blast",
|
||||
/*P*/ { NextAction("blood presence") },
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
static ActionNode* unbreakable_armor([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"unbreakable armor",
|
||||
/*P*/ { NextAction("blood tap") },
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
FrostDKStrategy::FrostDKStrategy(PlayerbotAI* botAI) : GenericDKStrategy(botAI)
|
||||
{
|
||||
actionNodeFactories.Add(new FrostDKStrategyActionNodeFactory());
|
||||
}
|
||||
|
||||
std::vector<NextAction> FrostDKStrategy::getDefaultActions()
|
||||
{
|
||||
return {
|
||||
NextAction("obliterate", ACTION_DEFAULT + 0.7f),
|
||||
NextAction("frost strike", ACTION_DEFAULT + 0.4f),
|
||||
NextAction("empower rune weapon", ACTION_DEFAULT + 0.3f),
|
||||
NextAction("horn of winter", ACTION_DEFAULT + 0.1f),
|
||||
NextAction("melee", ACTION_DEFAULT)
|
||||
};
|
||||
}
|
||||
|
||||
void FrostDKStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
GenericDKStrategy::InitTriggers(triggers);
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"unbreakable armor",
|
||||
{
|
||||
NextAction("unbreakable armor", ACTION_DEFAULT + 0.6f)
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"freezing fog",
|
||||
{
|
||||
NextAction("howling blast", ACTION_DEFAULT + 0.5f)
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"high blood rune",
|
||||
{
|
||||
NextAction("blood strike", ACTION_DEFAULT + 0.2f)
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"army of the dead",
|
||||
{
|
||||
NextAction("army of the dead", ACTION_HIGH + 6)
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"icy touch",
|
||||
{
|
||||
NextAction("icy touch", ACTION_HIGH + 2)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"plague strike",
|
||||
{
|
||||
NextAction("plague strike", ACTION_HIGH + 2)
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
void FrostDKAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"medium aoe",
|
||||
{
|
||||
NextAction("howling blast", ACTION_HIGH + 4)
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -1,192 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "UnholyDKStrategy.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
class UnholyDKStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
|
||||
{
|
||||
public:
|
||||
UnholyDKStrategyActionNodeFactory()
|
||||
{
|
||||
creators["death strike"] = &death_strike;
|
||||
creators["scourge strike"] = &scourge_strike;
|
||||
creators["ghoul frenzy"] = &ghoul_frenzy;
|
||||
creators["corpse explosion"] = &corpse_explosion;
|
||||
creators["icy touch"] = &icy_touch;
|
||||
}
|
||||
|
||||
private:
|
||||
static ActionNode* death_strike([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"death strike",
|
||||
/*P*/ { NextAction("blood presence") },
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
static ActionNode* ghoul_frenzy([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"ghoul frenzy",
|
||||
/*P*/ { NextAction("blood presence") },
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
static ActionNode* corpse_explosion([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"corpse explosion",
|
||||
/*P*/ { NextAction("blood presence") },
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* scourge_strike([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"scourge strike",
|
||||
/*P*/ { NextAction("blood presence") },
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
static ActionNode* icy_touch([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"icy touch",
|
||||
/*P*/ { NextAction("blood presence") },
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
UnholyDKStrategy::UnholyDKStrategy(PlayerbotAI* botAI) : GenericDKStrategy(botAI)
|
||||
{
|
||||
actionNodeFactories.Add(new UnholyDKStrategyActionNodeFactory());
|
||||
}
|
||||
|
||||
std::vector<NextAction> UnholyDKStrategy::getDefaultActions()
|
||||
{
|
||||
return {
|
||||
NextAction("death and decay", ACTION_HIGH + 5),
|
||||
NextAction("summon gargoyle", ACTION_DEFAULT + 0.4f),
|
||||
NextAction("horn of winter", ACTION_DEFAULT + 0.2f),
|
||||
NextAction("death coil", ACTION_DEFAULT + 0.1f),
|
||||
NextAction("melee", ACTION_DEFAULT)
|
||||
};
|
||||
}
|
||||
|
||||
void UnholyDKStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
GenericDKStrategy::InitTriggers(triggers);
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"death and decay cooldown",
|
||||
{
|
||||
NextAction("ghoul frenzy", ACTION_DEFAULT + 0.9f),
|
||||
NextAction("scourge strike", ACTION_DEFAULT + 0.8f),
|
||||
NextAction("icy touch", ACTION_DEFAULT + 0.7f),
|
||||
NextAction("blood strike", ACTION_DEFAULT + 0.6f),
|
||||
NextAction("plague strike", ACTION_DEFAULT + 0.5f),
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"dd cd and no desolation",
|
||||
{
|
||||
NextAction("blood strike", ACTION_DEFAULT + 0.75f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"high frost rune",
|
||||
{
|
||||
NextAction("icy touch", ACTION_NORMAL + 3)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"high blood rune",
|
||||
{
|
||||
NextAction("blood strike", ACTION_NORMAL + 2)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"high unholy rune",
|
||||
{
|
||||
NextAction("plague strike", ACTION_NORMAL + 1)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode("dd cd and plague strike 3s",
|
||||
{
|
||||
NextAction("plague strike", ACTION_HIGH + 1)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode("dd cd and icy touch 3s",
|
||||
{
|
||||
NextAction("icy touch", ACTION_HIGH + 2)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode("no rune",
|
||||
{
|
||||
NextAction("empower rune weapon", ACTION_HIGH + 1)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"army of the dead",
|
||||
{
|
||||
NextAction("army of the dead", ACTION_HIGH + 6)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode("bone shield",
|
||||
{
|
||||
NextAction("bone shield", ACTION_HIGH + 3)
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void UnholyDKAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"loot available",
|
||||
{
|
||||
NextAction("corpse explosion", ACTION_NORMAL + 1)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"medium aoe",
|
||||
{
|
||||
NextAction("death and decay", ACTION_NORMAL + 3),
|
||||
NextAction("corpse explosion", ACTION_NORMAL + 3)
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -1,256 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "BearTankDruidStrategy.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
class BearTankDruidStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
|
||||
{
|
||||
public:
|
||||
BearTankDruidStrategyActionNodeFactory()
|
||||
{
|
||||
creators["melee"] = &melee;
|
||||
creators["feral charge - bear"] = &feral_charge_bear;
|
||||
creators["swipe (bear)"] = &swipe_bear;
|
||||
creators["faerie fire (feral)"] = &faerie_fire_feral;
|
||||
creators["bear form"] = &bear_form;
|
||||
creators["dire bear form"] = &dire_bear_form;
|
||||
creators["mangle (bear)"] = &mangle_bear;
|
||||
creators["maul"] = &maul;
|
||||
creators["bash"] = &bash;
|
||||
creators["swipe"] = &swipe;
|
||||
creators["lacerate"] = &lacerate;
|
||||
creators["demoralizing roar"] = &demoralizing_roar;
|
||||
creators["taunt spell"] = &growl;
|
||||
}
|
||||
|
||||
private:
|
||||
static ActionNode* melee([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"melee",
|
||||
/*P*/ { NextAction("feral charge - bear") },
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* feral_charge_bear([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"feral charge - bear",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("reach melee") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* swipe_bear([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"swipe (bear)",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* faerie_fire_feral([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"faerie fire (feral)",
|
||||
/*P*/ { NextAction("feral charge - bear") },
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* bear_form([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"bear form",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* dire_bear_form([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"dire bear form",
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ { NextAction("bear form") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* mangle_bear([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"mangle (bear)",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* maul([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"maul",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("melee") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* bash([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"bash",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("melee") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* swipe([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"swipe",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("melee") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* lacerate([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"lacerate",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("maul") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* growl([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"growl",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* demoralizing_roar([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"demoralizing roar",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
BearTankDruidStrategy::BearTankDruidStrategy(PlayerbotAI* botAI) : FeralDruidStrategy(botAI)
|
||||
{
|
||||
actionNodeFactories.Add(new BearTankDruidStrategyActionNodeFactory());
|
||||
}
|
||||
|
||||
std::vector<NextAction> BearTankDruidStrategy::getDefaultActions()
|
||||
{
|
||||
return {
|
||||
NextAction("mangle (bear)", ACTION_DEFAULT + 0.5f),
|
||||
NextAction("faerie fire (feral)", ACTION_DEFAULT + 0.4f),
|
||||
NextAction("lacerate", ACTION_DEFAULT + 0.3f),
|
||||
NextAction("maul", ACTION_DEFAULT + 0.2f),
|
||||
NextAction("enrage", ACTION_DEFAULT + 0.1f),
|
||||
NextAction("melee", ACTION_DEFAULT)
|
||||
};
|
||||
}
|
||||
|
||||
void BearTankDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
FeralDruidStrategy::InitTriggers(triggers);
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"enemy out of melee",
|
||||
{
|
||||
NextAction("feral charge - bear", ACTION_NORMAL + 8)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"bear form",
|
||||
{
|
||||
NextAction("dire bear form", ACTION_HIGH + 8)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"low health",
|
||||
{
|
||||
NextAction("frenzied regeneration", ACTION_HIGH + 7)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"faerie fire (feral)",
|
||||
{
|
||||
NextAction("faerie fire (feral)", ACTION_HIGH + 7)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"lose aggro",
|
||||
{
|
||||
NextAction("growl", ACTION_HIGH + 8)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"medium aoe",
|
||||
{
|
||||
NextAction("demoralizing roar", ACTION_HIGH + 6),
|
||||
NextAction("swipe (bear)", ACTION_HIGH + 6)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"light aoe",
|
||||
{
|
||||
NextAction("swipe (bear)", ACTION_HIGH + 5)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"bash",
|
||||
{
|
||||
NextAction("bash", ACTION_INTERRUPT + 2)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"bash on enemy healer",
|
||||
{
|
||||
NextAction("bash on enemy healer", ACTION_INTERRUPT + 1)
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -1,254 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "CasterDruidStrategy.h"
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "FeralDruidStrategy.h"
|
||||
|
||||
class CasterDruidStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
|
||||
{
|
||||
public:
|
||||
CasterDruidStrategyActionNodeFactory()
|
||||
{
|
||||
creators["faerie fire"] = &faerie_fire;
|
||||
creators["hibernate"] = &hibernate;
|
||||
creators["entangling roots"] = &entangling_roots;
|
||||
creators["entangling roots on cc"] = &entangling_roots_on_cc;
|
||||
creators["wrath"] = &wrath;
|
||||
creators["starfall"] = &starfall;
|
||||
creators["insect swarm"] = &insect_swarm;
|
||||
creators["moonfire"] = &moonfire;
|
||||
creators["starfire"] = &starfire;
|
||||
creators["moonkin form"] = &moonkin_form;
|
||||
}
|
||||
|
||||
private:
|
||||
static ActionNode* faerie_fire([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"faerie fire",
|
||||
/*P*/ { NextAction("moonkin form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* hibernate([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"hibernate",
|
||||
/*P*/ { NextAction("moonkin form") },
|
||||
/*A*/ { NextAction("entangling roots") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* entangling_roots([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"entangling roots",
|
||||
/*P*/ { NextAction("moonkin form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* entangling_roots_on_cc([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"entangling roots on cc",
|
||||
/*P*/ { NextAction("moonkin form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* wrath([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"wrath",
|
||||
/*P*/ { NextAction("moonkin form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* starfall([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"starfall",
|
||||
/*P*/ { NextAction("moonkin form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* insect_swarm([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"insect swarm",
|
||||
/*P*/ { NextAction("moonkin form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* moonfire([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"moonfire",
|
||||
/*P*/ { NextAction("moonkin form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* starfire([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"starfire",
|
||||
/*P*/ { NextAction("moonkin form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* moonkin_form([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"moonkin form",
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
CasterDruidStrategy::CasterDruidStrategy(PlayerbotAI* botAI) : GenericDruidStrategy(botAI)
|
||||
{
|
||||
actionNodeFactories.Add(new CasterDruidStrategyActionNodeFactory());
|
||||
actionNodeFactories.Add(new ShapeshiftDruidStrategyActionNodeFactory());
|
||||
}
|
||||
|
||||
std::vector<NextAction> CasterDruidStrategy::getDefaultActions()
|
||||
{
|
||||
return {
|
||||
NextAction("starfall", ACTION_HIGH + 1.0f),
|
||||
NextAction("force of nature", ACTION_DEFAULT + 1.0f),
|
||||
NextAction("wrath", ACTION_DEFAULT + 0.1f),
|
||||
};
|
||||
}
|
||||
|
||||
void CasterDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
GenericDruidStrategy::InitTriggers(triggers);
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"eclipse (lunar) cooldown",
|
||||
{
|
||||
NextAction("starfire", ACTION_DEFAULT + 0.2f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"eclipse (solar) cooldown",
|
||||
{
|
||||
NextAction("wrath", ACTION_DEFAULT + 0.2f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"insect swarm",
|
||||
{
|
||||
NextAction("insect swarm", ACTION_NORMAL + 5)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"moonfire",
|
||||
{
|
||||
NextAction("moonfire", ACTION_NORMAL + 4)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"eclipse (solar)",
|
||||
{
|
||||
NextAction("wrath", ACTION_NORMAL + 6)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"eclipse (lunar)",
|
||||
{
|
||||
NextAction("starfire", ACTION_NORMAL + 6)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"medium mana",
|
||||
{
|
||||
NextAction("innervate", ACTION_HIGH + 9)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"enemy too close for spell",
|
||||
{
|
||||
NextAction("flee", ACTION_MOVE + 9)
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void CasterDruidAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"hurricane channel check",
|
||||
{
|
||||
NextAction("cancel channel", ACTION_HIGH + 2)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"medium aoe",
|
||||
{
|
||||
NextAction("hurricane", ACTION_HIGH + 1)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"light aoe",
|
||||
{
|
||||
NextAction("insect swarm on attacker", ACTION_NORMAL + 3),
|
||||
NextAction("moonfire on attacker", ACTION_NORMAL + 3)
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void CasterDruidDebuffStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"faerie fire",
|
||||
{
|
||||
NextAction("faerie fire", ACTION_HIGH)
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -1,314 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "CatDpsDruidStrategy.h"
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
|
||||
class CatDpsDruidStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
|
||||
{
|
||||
public:
|
||||
CatDpsDruidStrategyActionNodeFactory()
|
||||
{
|
||||
creators["faerie fire (feral)"] = &faerie_fire_feral;
|
||||
creators["melee"] = &melee;
|
||||
creators["feral charge - cat"] = &feral_charge_cat;
|
||||
creators["cat form"] = &cat_form;
|
||||
creators["claw"] = &claw;
|
||||
creators["mangle (cat)"] = &mangle_cat;
|
||||
creators["rake"] = &rake;
|
||||
creators["ferocious bite"] = &ferocious_bite;
|
||||
creators["rip"] = &rip;
|
||||
creators["pounce"] = &pounce;
|
||||
creators["ravage"] = &ravage;
|
||||
}
|
||||
|
||||
private:
|
||||
static ActionNode* faerie_fire_feral([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"faerie fire (feral)",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* melee([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"melee",
|
||||
/*P*/ { NextAction("feral charge - cat") },
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* feral_charge_cat([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"feral charge - cat",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("reach melee") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* cat_form([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"cat form",
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* claw([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"claw",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("melee") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* mangle_cat([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"mangle (cat)",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* rake([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"rake",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* ferocious_bite([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"ferocious bite",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("rip") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* rip([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"rip",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* pounce([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"pounce",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("ravage") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* ravage([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"ravage",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("shred") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
CatDpsDruidStrategy::CatDpsDruidStrategy(PlayerbotAI* botAI) : FeralDruidStrategy(botAI)
|
||||
{
|
||||
actionNodeFactories.Add(new CatDpsDruidStrategyActionNodeFactory());
|
||||
}
|
||||
|
||||
std::vector<NextAction> CatDpsDruidStrategy::getDefaultActions()
|
||||
{
|
||||
return {
|
||||
NextAction("tiger's fury", ACTION_DEFAULT + 0.1f)
|
||||
};
|
||||
}
|
||||
|
||||
void CatDpsDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
FeralDruidStrategy::InitTriggers(triggers);
|
||||
|
||||
// Default priority
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"almost full energy available",
|
||||
{
|
||||
NextAction("shred", ACTION_DEFAULT + 0.4f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"combo points not full",
|
||||
{
|
||||
NextAction("shred", ACTION_DEFAULT + 0.4f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"almost full energy available",
|
||||
{
|
||||
NextAction("mangle (cat)", ACTION_DEFAULT + 0.3f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"combo points not full and high energy",
|
||||
{
|
||||
NextAction("mangle (cat)", ACTION_DEFAULT + 0.3f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"almost full energy available",
|
||||
{
|
||||
NextAction("claw", ACTION_DEFAULT + 0.2f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"combo points not full and high energy",
|
||||
{
|
||||
NextAction("claw", ACTION_DEFAULT + 0.2f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"faerie fire (feral)",
|
||||
{
|
||||
NextAction("faerie fire (feral)", ACTION_DEFAULT + 0.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Main spell
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"cat form", {
|
||||
NextAction("cat form", ACTION_HIGH + 8)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"savage roar", {
|
||||
NextAction("savage roar", ACTION_HIGH + 7)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"combo points available",
|
||||
{
|
||||
NextAction("rip", ACTION_HIGH + 6)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"ferocious bite time",
|
||||
{
|
||||
NextAction("ferocious bite", ACTION_HIGH + 5)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"target with combo points almost dead",
|
||||
{
|
||||
NextAction("ferocious bite", ACTION_HIGH + 4)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"mangle (cat)",
|
||||
{
|
||||
NextAction("mangle (cat)", ACTION_HIGH + 3)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"rake",
|
||||
{
|
||||
NextAction("rake", ACTION_HIGH + 2)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"medium threat",
|
||||
{
|
||||
NextAction("cower", ACTION_HIGH + 1)
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// AOE
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"medium aoe",
|
||||
{
|
||||
NextAction("swipe (cat)", ACTION_HIGH + 3)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"light aoe",
|
||||
{
|
||||
NextAction("rake on attacker", ACTION_HIGH + 2)
|
||||
}
|
||||
)
|
||||
);
|
||||
// Reach target
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"enemy out of melee",
|
||||
{
|
||||
NextAction("feral charge - cat", ACTION_HIGH + 9)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"enemy out of melee",
|
||||
{
|
||||
NextAction("dash", ACTION_HIGH + 8)
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void CatAoeDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers) {}
|
||||
@@ -1,194 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "GenericDruidNonCombatStrategy.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
#include "AiFactory.h"
|
||||
|
||||
class GenericDruidNonCombatStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
|
||||
{
|
||||
public:
|
||||
GenericDruidNonCombatStrategyActionNodeFactory()
|
||||
{
|
||||
creators["thorns"] = þs;
|
||||
creators["thorns on party"] = þs_on_party;
|
||||
creators["mark of the wild"] = &mark_of_the_wild;
|
||||
creators["mark of the wild on party"] = &mark_of_the_wild_on_party;
|
||||
// creators["innervate"] = &innervate;
|
||||
creators["regrowth_on_party"] = ®rowth_on_party;
|
||||
creators["rejuvenation on party"] = &rejuvenation_on_party;
|
||||
creators["remove curse on party"] = &remove_curse_on_party;
|
||||
creators["abolish poison on party"] = &abolish_poison_on_party;
|
||||
creators["revive"] = &revive;
|
||||
}
|
||||
|
||||
private:
|
||||
static ActionNode* thorns([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("thorns",
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* thorns_on_party([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("thorns on party",
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* mark_of_the_wild([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("mark of the wild",
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* mark_of_the_wild_on_party([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("mark of the wild on party",
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
static ActionNode* regrowth_on_party([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("regrowth on party",
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
static ActionNode* rejuvenation_on_party([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("rejuvenation on party",
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
static ActionNode* remove_curse_on_party([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("remove curse on party",
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
static ActionNode* abolish_poison_on_party([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("abolish poison on party",
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
static ActionNode* revive([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("revive",
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
};
|
||||
|
||||
GenericDruidNonCombatStrategy::GenericDruidNonCombatStrategy(PlayerbotAI* botAI) : NonCombatStrategy(botAI)
|
||||
{
|
||||
actionNodeFactories.Add(new GenericDruidNonCombatStrategyActionNodeFactory());
|
||||
}
|
||||
|
||||
void GenericDruidNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
NonCombatStrategy::InitTriggers(triggers);
|
||||
|
||||
triggers.push_back(new TriggerNode("mark of the wild", { NextAction("mark of the wild", 14.0f) }));
|
||||
triggers.push_back(new TriggerNode("party member cure poison", { NextAction("abolish poison on party", 20.0f) }));
|
||||
triggers.push_back(new TriggerNode("party member dead", { NextAction("revive", ACTION_CRITICAL_HEAL + 10) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("often", { NextAction("apply oil", 1.0f) }));
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode("party member critical health",
|
||||
{
|
||||
NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 7),
|
||||
NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 6),
|
||||
NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 5),
|
||||
}));
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode("party member low health",
|
||||
{
|
||||
NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 5),
|
||||
NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 4),
|
||||
NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 3),
|
||||
}));
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode("party member medium health",
|
||||
{ NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 3),
|
||||
NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 2),
|
||||
NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 1),
|
||||
}));
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode("party member almost full health",
|
||||
{ NextAction("wild growth on party", ACTION_LIGHT_HEAL + 3), NextAction("rejuvenation on party", ACTION_LIGHT_HEAL + 2) }));
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode("party member remove curse",
|
||||
{ NextAction("remove curse on party", ACTION_DISPEL + 7) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("new pet", { NextAction("set pet stance", 60.0f) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("party member critical health", {
|
||||
NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 7),
|
||||
NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 6),
|
||||
NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 5),
|
||||
}));
|
||||
triggers.push_back(new TriggerNode("party member low health", {
|
||||
NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 5),
|
||||
NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 4),
|
||||
NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 3),
|
||||
}));
|
||||
triggers.push_back(new TriggerNode("party member medium health", {
|
||||
NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 3),
|
||||
NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 2),
|
||||
NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 1),
|
||||
}));
|
||||
triggers.push_back(new TriggerNode("party member almost full health", {
|
||||
NextAction("wild growth on party", ACTION_LIGHT_HEAL + 3),
|
||||
NextAction("rejuvenation on party", ACTION_LIGHT_HEAL + 2),
|
||||
}));
|
||||
triggers.push_back(new TriggerNode("party member remove curse", {
|
||||
NextAction("remove curse on party", ACTION_DISPEL + 7),
|
||||
}));
|
||||
|
||||
int specTab = AiFactory::GetPlayerSpecTab(botAI->GetBot());
|
||||
if (specTab == 0 || specTab == 2) // Balance or Restoration
|
||||
triggers.push_back(new TriggerNode("often", { NextAction("apply oil", 1.0f) }));
|
||||
if (specTab == 1) // Feral
|
||||
triggers.push_back(new TriggerNode("often", { NextAction("apply stone", 1.0f) }));
|
||||
|
||||
}
|
||||
|
||||
GenericDruidBuffStrategy::GenericDruidBuffStrategy(PlayerbotAI* botAI) : NonCombatStrategy(botAI)
|
||||
{
|
||||
actionNodeFactories.Add(new GenericDruidNonCombatStrategyActionNodeFactory());
|
||||
}
|
||||
|
||||
void GenericDruidBuffStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
NonCombatStrategy::InitTriggers(triggers);
|
||||
|
||||
triggers.push_back(new TriggerNode("mark of the wild on party", {
|
||||
NextAction("mark of the wild on party", 13.0f),
|
||||
}));
|
||||
triggers.push_back(new TriggerNode("thorns on main tank", {
|
||||
NextAction("thorns on main tank", 11.0f),
|
||||
}));
|
||||
triggers.push_back(new TriggerNode("thorns", {
|
||||
NextAction("thorns", 10.0f),
|
||||
}));
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "GenericDruidStrategy.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
class GenericDruidStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
|
||||
{
|
||||
public:
|
||||
GenericDruidStrategyActionNodeFactory()
|
||||
{
|
||||
creators["melee"] = &melee;
|
||||
creators["caster form"] = &caster_form;
|
||||
creators["cure poison"] = &cure_poison;
|
||||
creators["cure poison on party"] = &cure_poison_on_party;
|
||||
creators["abolish poison"] = &abolish_poison;
|
||||
creators["abolish poison on party"] = &abolish_poison_on_party;
|
||||
creators["rebirth"] = &rebirth;
|
||||
creators["entangling roots on cc"] = &entangling_roots_on_cc;
|
||||
creators["innervate"] = &innervate;
|
||||
}
|
||||
|
||||
private:
|
||||
static ActionNode* melee([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("melee",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* caster_form([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("caster form",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* cure_poison([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("cure poison",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* cure_poison_on_party([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("cure poison on party",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* abolish_poison([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("abolish poison",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* abolish_poison_on_party([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("abolish poison on party",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* rebirth([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("rebirth",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* entangling_roots_on_cc([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("entangling roots on cc",
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* innervate([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("innervate",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("mana potion") },
|
||||
/*C*/ {});
|
||||
}
|
||||
};
|
||||
|
||||
GenericDruidStrategy::GenericDruidStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI)
|
||||
{
|
||||
actionNodeFactories.Add(new GenericDruidStrategyActionNodeFactory());
|
||||
}
|
||||
|
||||
void GenericDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
CombatStrategy::InitTriggers(triggers);
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode("low health", { NextAction("barkskin", ACTION_HIGH + 7) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("combat party member dead",
|
||||
{ NextAction("rebirth", ACTION_HIGH + 9) }));
|
||||
triggers.push_back(new TriggerNode("being attacked",
|
||||
{ NextAction("nature's grasp", ACTION_HIGH + 1) }));
|
||||
triggers.push_back(new TriggerNode("new pet", { NextAction("set pet stance", 60.0f) }));
|
||||
}
|
||||
|
||||
void DruidCureStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(
|
||||
new TriggerNode("party member cure poison",
|
||||
{ NextAction("abolish poison on party", ACTION_DISPEL + 1) }));
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode("party member remove curse",
|
||||
{ NextAction("remove curse on party", ACTION_DISPEL + 7) }));
|
||||
|
||||
}
|
||||
|
||||
void DruidBoostStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode(
|
||||
"nature's swiftness", { NextAction("nature's swiftness", ACTION_HIGH + 9) }));
|
||||
}
|
||||
|
||||
void DruidCcStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode(
|
||||
"entangling roots", { NextAction("entangling roots on cc", ACTION_HIGH + 2) }));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"entangling roots kite", { NextAction("entangling roots", ACTION_HIGH + 2) }));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"hibernate", { NextAction("hibernate on cc", ACTION_HIGH + 3) }));
|
||||
}
|
||||
|
||||
void DruidHealerDpsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(
|
||||
new TriggerNode("healer should attack",
|
||||
{
|
||||
NextAction("cancel tree form", ACTION_DEFAULT + 0.3f),
|
||||
NextAction("moonfire", ACTION_DEFAULT + 0.2f),
|
||||
NextAction("wrath", ACTION_DEFAULT + 0.1f),
|
||||
NextAction("starfire", ACTION_DEFAULT),
|
||||
}));
|
||||
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "HealDruidStrategy.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
class HealDruidStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
|
||||
{
|
||||
public:
|
||||
HealDruidStrategyActionNodeFactory() {
|
||||
creators["nourish on party"] = &nourtish_on_party;
|
||||
}
|
||||
|
||||
private:
|
||||
static ActionNode* nourtish_on_party([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("nourish on party",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("healing touch on party") },
|
||||
/*C*/ {});
|
||||
}
|
||||
};
|
||||
|
||||
HealDruidStrategy::HealDruidStrategy(PlayerbotAI* botAI) : GenericDruidStrategy(botAI)
|
||||
{
|
||||
actionNodeFactories.Add(new HealDruidStrategyActionNodeFactory());
|
||||
}
|
||||
|
||||
void HealDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
GenericDruidStrategy::InitTriggers(triggers);
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"party member to heal out of spell range",
|
||||
{ NextAction("reach party member to heal", ACTION_CRITICAL_HEAL + 9) }));
|
||||
|
||||
// CRITICAL
|
||||
triggers.push_back(
|
||||
new TriggerNode("party member critical health",
|
||||
{
|
||||
NextAction("tree form", ACTION_CRITICAL_HEAL + 4.1f),
|
||||
NextAction("swiftmend on party", ACTION_CRITICAL_HEAL + 4),
|
||||
NextAction("regrowth on party", ACTION_CRITICAL_HEAL + 3),
|
||||
NextAction("wild growth on party", ACTION_CRITICAL_HEAL + 2),
|
||||
NextAction("nourish on party", ACTION_CRITICAL_HEAL + 1),
|
||||
}));
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode("party member critical health",
|
||||
{ NextAction("nature's swiftness", ACTION_CRITICAL_HEAL + 4) }));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"group heal setting",
|
||||
{
|
||||
NextAction("tree form", ACTION_MEDIUM_HEAL + 2.3f),
|
||||
NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 2.2f),
|
||||
NextAction("rejuvenation on not full", ACTION_MEDIUM_HEAL + 2.1f),
|
||||
}));
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode("medium group heal setting",
|
||||
{
|
||||
NextAction("tree form", ACTION_CRITICAL_HEAL + 0.6f),
|
||||
NextAction("tranquility", ACTION_CRITICAL_HEAL + 0.5f) }));
|
||||
|
||||
// LOW
|
||||
triggers.push_back(
|
||||
new TriggerNode("party member low health",
|
||||
{ NextAction("tree form", ACTION_MEDIUM_HEAL + 1.5f),
|
||||
NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 1.4f),
|
||||
NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 1.3f),
|
||||
NextAction("swiftmend on party", ACTION_MEDIUM_HEAL + 1.2),
|
||||
NextAction("nourish on party", ACTION_MEDIUM_HEAL + 1.1f),
|
||||
}));
|
||||
|
||||
// MEDIUM
|
||||
triggers.push_back(
|
||||
new TriggerNode("party member medium health",
|
||||
{
|
||||
NextAction("tree form", ACTION_MEDIUM_HEAL + 0.5f),
|
||||
NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 0.4f),
|
||||
NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 0.3f),
|
||||
NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 0.2f),
|
||||
NextAction("nourish on party", ACTION_MEDIUM_HEAL + 0.1f) }));
|
||||
|
||||
// almost full
|
||||
triggers.push_back(
|
||||
new TriggerNode("party member almost full health",
|
||||
{ NextAction("wild growth on party", ACTION_LIGHT_HEAL + 0.3f),
|
||||
NextAction("rejuvenation on party", ACTION_LIGHT_HEAL + 0.2f),
|
||||
NextAction("regrowth on party", ACTION_LIGHT_HEAL + 0.1f) }));
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode("medium mana", { NextAction("innervate", ACTION_HIGH + 5) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("enemy too close for spell",
|
||||
{ NextAction("flee", ACTION_MOVE + 9) }));
|
||||
}
|
||||
@@ -1,307 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "OffhealDruidCatStrategy.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
class OffhealDruidCatStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
|
||||
{
|
||||
public:
|
||||
OffhealDruidCatStrategyActionNodeFactory()
|
||||
{
|
||||
creators["cat form"] = &cat_form;
|
||||
creators["mangle (cat)"] = &mangle_cat;
|
||||
creators["shred"] = &shred;
|
||||
creators["rake"] = &rake;
|
||||
creators["rip"] = &rip;
|
||||
creators["ferocious bite"] = &ferocious_bite;
|
||||
creators["savage roar"] = &savage_roar;
|
||||
creators["faerie fire (feral)"] = &faerie_fire_feral;
|
||||
creators["healing touch on party"] = &healing_touch_on_party;
|
||||
creators["regrowth on party"] = ®rowth_on_party;
|
||||
creators["rejuvenation on party"] = &rejuvenation_on_party;
|
||||
}
|
||||
|
||||
private:
|
||||
static ActionNode* cat_form([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"cat form",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* mangle_cat([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"mangle (cat)",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* shred([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"shred",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("claw") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* rake([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"rake",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* rip([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"rip",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* ferocious_bite([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"ferocious bite",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("rip") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* savage_roar([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"savage roar",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* faerie_fire_feral([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"faerie fire (feral)",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* healing_touch_on_party([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"healing touch on party",
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ {},
|
||||
/*C*/ { NextAction("cat form") }
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* regrowth_on_party([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"regrowth on party",
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ {},
|
||||
/*C*/ { NextAction("cat form") }
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* rejuvenation_on_party([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"rejuvenation on party",
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ {},
|
||||
/*C*/ { NextAction("cat form") }
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
OffhealDruidCatStrategy::OffhealDruidCatStrategy(PlayerbotAI* botAI) : FeralDruidStrategy(botAI)
|
||||
{
|
||||
actionNodeFactories.Add(new OffhealDruidCatStrategyActionNodeFactory());
|
||||
}
|
||||
|
||||
std::vector<NextAction> OffhealDruidCatStrategy::getDefaultActions()
|
||||
{
|
||||
return {
|
||||
NextAction("mangle (cat)", ACTION_DEFAULT + 0.5f),
|
||||
NextAction("shred", ACTION_DEFAULT + 0.4f),
|
||||
NextAction("rake", ACTION_DEFAULT + 0.3f),
|
||||
NextAction("melee", ACTION_DEFAULT),
|
||||
NextAction("cat form", ACTION_DEFAULT - 0.1f)
|
||||
};
|
||||
}
|
||||
|
||||
void OffhealDruidCatStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
FeralDruidStrategy::InitTriggers(triggers);
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"cat form",
|
||||
{
|
||||
NextAction("cat form", ACTION_HIGH + 8)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"savage roar",
|
||||
{
|
||||
NextAction("savage roar", ACTION_HIGH + 7)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"combo points available",
|
||||
{
|
||||
NextAction("rip", ACTION_HIGH + 6)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"ferocious bite time",
|
||||
{
|
||||
NextAction("ferocious bite", ACTION_HIGH + 5)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"target with combo points almost dead",
|
||||
{
|
||||
NextAction("ferocious bite", ACTION_HIGH + 4)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"mangle (cat)",
|
||||
{
|
||||
NextAction("mangle (cat)", ACTION_HIGH + 3)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"rake",
|
||||
{
|
||||
NextAction("rake", ACTION_HIGH + 2)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"almost full energy available",
|
||||
{
|
||||
NextAction("shred", ACTION_DEFAULT + 0.4f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"combo points not full",
|
||||
{
|
||||
NextAction("shred", ACTION_DEFAULT + 0.4f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"faerie fire (feral)",
|
||||
{
|
||||
NextAction("faerie fire (feral)", ACTION_NORMAL)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"enemy out of melee",
|
||||
{
|
||||
NextAction("feral charge - cat", ACTION_HIGH + 9),
|
||||
NextAction("dash", ACTION_HIGH + 8)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"medium aoe",
|
||||
{
|
||||
NextAction("swipe (cat)", ACTION_HIGH + 3)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"low energy",
|
||||
{
|
||||
NextAction("tiger's fury", ACTION_NORMAL + 1)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"party member critical health",
|
||||
{
|
||||
NextAction("regrowth on party", ACTION_CRITICAL_HEAL + 6),
|
||||
NextAction("healing touch on party", ACTION_CRITICAL_HEAL + 5)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"party member low health",
|
||||
{
|
||||
NextAction("healing touch on party", ACTION_MEDIUM_HEAL + 5)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"party member medium health",
|
||||
{
|
||||
NextAction("rejuvenation on party", ACTION_LIGHT_HEAL + 8)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"party member to heal out of spell range",
|
||||
{
|
||||
NextAction("reach party member to heal", ACTION_EMERGENCY + 3)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"low mana",
|
||||
{
|
||||
NextAction("innervate", ACTION_HIGH + 4)
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "BattlegroundStrategy.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
void BGStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("often", { NextAction("bg join", relevance)}));
|
||||
triggers.push_back(new TriggerNode("bg invite active", { NextAction("bg status check", relevance)}));
|
||||
triggers.push_back(new TriggerNode("timer", { NextAction("bg strategy check", relevance)}));
|
||||
}
|
||||
|
||||
BGStrategy::BGStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI) {}
|
||||
|
||||
void BattlegroundStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("bg waiting", { NextAction("bg move to start", ACTION_BG)}));
|
||||
triggers.push_back(new TriggerNode("bg active", { NextAction("bg move to objective", ACTION_BG)}));
|
||||
triggers.push_back(new TriggerNode("often", { NextAction("bg check objective", ACTION_BG + 1)}));
|
||||
triggers.push_back(new TriggerNode("dead", { NextAction("bg reset objective force", ACTION_EMERGENCY)}));
|
||||
}
|
||||
|
||||
void WarsongStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("bg active", { NextAction("bg check flag", ACTION_EMERGENCY )}));
|
||||
triggers.push_back(new TriggerNode("enemy flagcarrier near", { NextAction("attack enemy flag carrier", ACTION_RAID + 1.0f)}));
|
||||
triggers.push_back(new TriggerNode("team flagcarrier near", { NextAction("bg protect fc", ACTION_RAID)}));
|
||||
triggers.push_back(new TriggerNode("often", { NextAction("bg use buff", ACTION_BG)}));
|
||||
triggers.push_back(new TriggerNode("low health", { NextAction("bg use buff", ACTION_MOVE)}));
|
||||
triggers.push_back(new TriggerNode("low mana", { NextAction("bg use buff", ACTION_MOVE)}));
|
||||
triggers.push_back(new TriggerNode("player has flag", { NextAction("bg move to objective", ACTION_EMERGENCY)}));
|
||||
triggers.push_back(new TriggerNode("timer bg", { NextAction("bg reset objective force", ACTION_EMERGENCY)}));
|
||||
}
|
||||
|
||||
void AlteracStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("alliance no snowfall gy", { NextAction("bg move to objective", ACTION_EMERGENCY)}));
|
||||
triggers.push_back(new TriggerNode("timer bg", { NextAction("bg reset objective force", ACTION_EMERGENCY)}));
|
||||
}
|
||||
|
||||
void ArathiStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("bg active", { NextAction("bg check flag", ACTION_EMERGENCY)}));
|
||||
triggers.push_back(new TriggerNode("often", { NextAction("bg use buff", ACTION_BG)}));
|
||||
triggers.push_back(new TriggerNode("low health", { NextAction("bg use buff", ACTION_MOVE)}));
|
||||
triggers.push_back(new TriggerNode("low mana", { NextAction("bg use buff", ACTION_MOVE)}));
|
||||
}
|
||||
|
||||
void EyeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("bg active", { NextAction("bg check flag", ACTION_EMERGENCY)}));
|
||||
triggers.push_back(new TriggerNode("often", { NextAction("bg use buff", ACTION_BG)}));
|
||||
triggers.push_back(new TriggerNode("low health", { NextAction("bg use buff", ACTION_MOVE)}));
|
||||
triggers.push_back(new TriggerNode("low mana", { NextAction("bg use buff", ACTION_MOVE)}));
|
||||
triggers.push_back(new TriggerNode("enemy flagcarrier near", { NextAction("attack enemy flag carrier", ACTION_RAID)}));
|
||||
triggers.push_back(new TriggerNode("player has flag",{ NextAction("bg move to objective", ACTION_EMERGENCY)}));
|
||||
}
|
||||
|
||||
//TODO: Do Priorities
|
||||
void IsleStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("bg active", { NextAction("bg check flag", ACTION_MOVE)}));
|
||||
triggers.push_back(new TriggerNode("timer", { NextAction("enter vehicle", ACTION_MOVE + 8.0f)}));
|
||||
triggers.push_back(new TriggerNode("random", { NextAction("leave vehicle", ACTION_MOVE + 7.0f)}));
|
||||
triggers.push_back(new TriggerNode("in vehicle", { NextAction("hurl boulder", ACTION_MOVE + 9.0f)}));
|
||||
triggers.push_back(new TriggerNode("in vehicle", { NextAction("fire cannon", ACTION_MOVE + 9.0f)}));
|
||||
triggers.push_back(new TriggerNode("in vehicle", { NextAction("napalm", ACTION_MOVE + 9.0f)}));
|
||||
triggers.push_back(new TriggerNode("enemy is close", { NextAction("steam blast", ACTION_MOVE + 9.0f)}));
|
||||
triggers.push_back(new TriggerNode("in vehicle", { NextAction("ram", ACTION_MOVE + 9.0f)}));
|
||||
triggers.push_back(new TriggerNode("enemy is close", { NextAction("ram", ACTION_MOVE + 9.1f)}));
|
||||
triggers.push_back(new TriggerNode("enemy out of melee", { NextAction("steam rush", ACTION_MOVE + 9.2f)}));
|
||||
triggers.push_back(new TriggerNode("in vehicle", { NextAction("incendiary rocket", ACTION_MOVE + 9.0f)}));
|
||||
triggers.push_back(new TriggerNode("in vehicle", { NextAction("rocket blast", ACTION_MOVE + 9.0f)}));
|
||||
// this is bugged: it doesn't work, and stops glaive throw working (which is needed to take down gate)
|
||||
// triggers.push_back(new TriggerNode("in vehicle", { NextAction("blade salvo", ACTION_MOVE + 9.0f)}));
|
||||
triggers.push_back(new TriggerNode("in vehicle", { NextAction("glaive throw", ACTION_MOVE + 9.0f)}));
|
||||
}
|
||||
|
||||
void ArenaStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(
|
||||
new TriggerNode("no possible targets", { NextAction("arena tactics", ACTION_BG)}));
|
||||
}
|
||||
@@ -1,193 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "ChatCommandHandlerStrategy.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
class ChatCommandActionNodeFactoryInternal : public NamedObjectFactory<ActionNode>
|
||||
{
|
||||
public:
|
||||
ChatCommandActionNodeFactoryInternal() { creators["tank attack chat shortcut"] = &tank_attack_chat_shortcut; }
|
||||
|
||||
private:
|
||||
static ActionNode* tank_attack_chat_shortcut(PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("tank attack chat shortcut",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ { NextAction("attack my target", 100.0f) });
|
||||
}
|
||||
};
|
||||
|
||||
void ChatCommandHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
PassTroughStrategy::InitTriggers(triggers);
|
||||
|
||||
triggers.push_back(new TriggerNode("rep", { NextAction("reputation", relevance) }));
|
||||
triggers.push_back(new TriggerNode("q", { NextAction("query quest", relevance),
|
||||
NextAction("query item usage", relevance) }));
|
||||
triggers.push_back(new TriggerNode("add all loot", { NextAction("add all loot", relevance),
|
||||
NextAction("loot", relevance) }));
|
||||
triggers.push_back(new TriggerNode("u", { NextAction("use", relevance) }));
|
||||
triggers.push_back(new TriggerNode("c", { NextAction("item count", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("items", { NextAction("item count", relevance) }));
|
||||
triggers.push_back(new TriggerNode("inv", { NextAction("item count", relevance) }));
|
||||
triggers.push_back(new TriggerNode("e", { NextAction("equip", relevance) }));
|
||||
triggers.push_back(new TriggerNode("ue", { NextAction("unequip", relevance) }));
|
||||
triggers.push_back(new TriggerNode("t", { NextAction("trade", relevance) }));
|
||||
triggers.push_back(new TriggerNode("nt", { NextAction("trade", relevance) }));
|
||||
triggers.push_back(new TriggerNode("s", { NextAction("sell", relevance) }));
|
||||
triggers.push_back(new TriggerNode("b", { NextAction("buy", relevance) }));
|
||||
triggers.push_back(new TriggerNode("r", { NextAction("reward", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("attack", { NextAction("attack my target", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("accept", { NextAction("accept quest", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("follow", { NextAction("follow chat shortcut", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("stay", { NextAction("stay chat shortcut", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("move from group", { NextAction("move from group chat shortcut", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("flee", { NextAction("flee chat shortcut", relevance) }));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"tank attack", { NextAction("tank attack chat shortcut", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("grind", { NextAction("grind chat shortcut", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("talk", { NextAction("gossip hello", relevance),
|
||||
NextAction("talk to quest giver", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("enter vehicle", { NextAction("enter vehicle", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("leave vehicle", { NextAction("leave vehicle", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("cast", { NextAction("cast custom spell", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("castnc", { NextAction("cast custom nc spell", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("revive", { NextAction("spirit healer", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("runaway", { NextAction("runaway chat shortcut", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("warning", { NextAction("runaway chat shortcut", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("max dps", { NextAction("max dps chat shortcut", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("attackers", { NextAction("tell attackers", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("target", { NextAction("tell target", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("ready", { NextAction("ready check", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("bwl", { NextAction("bwl chat shortcut", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("dps", { NextAction("tell estimated dps", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("disperse", { NextAction("disperse set", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("open items", { NextAction("open items", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("qi", { NextAction("query item usage", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("unlock items", { NextAction("unlock items", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("unlock traded item", { NextAction("unlock traded item", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("wipe", { NextAction("wipe", relevance) }));
|
||||
triggers.push_back(new TriggerNode("tame", { NextAction("tame", relevance) }));
|
||||
triggers.push_back(new TriggerNode("glyphs", { NextAction("glyphs", relevance) })); // Added for custom Glyphs
|
||||
triggers.push_back(new TriggerNode("glyph equip", { NextAction("glyph equip", relevance) })); // Added for custom Glyphs
|
||||
triggers.push_back(new TriggerNode("pet", { NextAction("pet", relevance) }));
|
||||
triggers.push_back(new TriggerNode("pet attack", { NextAction("pet attack", relevance) }));
|
||||
triggers.push_back(new TriggerNode("roll", { NextAction("roll", relevance) }));
|
||||
}
|
||||
|
||||
ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI)
|
||||
{
|
||||
actionNodeFactories.Add(new ChatCommandActionNodeFactoryInternal());
|
||||
|
||||
supported.push_back("quests");
|
||||
supported.push_back("stats");
|
||||
supported.push_back("leave");
|
||||
supported.push_back("reputation");
|
||||
supported.push_back("log");
|
||||
supported.push_back("los");
|
||||
supported.push_back("rpg status");
|
||||
supported.push_back("rpg do quest");
|
||||
supported.push_back("aura");
|
||||
supported.push_back("drop");
|
||||
supported.push_back("share");
|
||||
supported.push_back("ll");
|
||||
supported.push_back("ss");
|
||||
supported.push_back("release");
|
||||
supported.push_back("teleport");
|
||||
supported.push_back("taxi");
|
||||
supported.push_back("repair");
|
||||
supported.push_back("talents");
|
||||
supported.push_back("spells");
|
||||
supported.push_back("co");
|
||||
supported.push_back("nc");
|
||||
supported.push_back("de");
|
||||
supported.push_back("trainer");
|
||||
supported.push_back("maintenance");
|
||||
supported.push_back("remove glyph");
|
||||
supported.push_back("autogear");
|
||||
supported.push_back("equip upgrade");
|
||||
supported.push_back("chat");
|
||||
supported.push_back("home");
|
||||
supported.push_back("destroy");
|
||||
supported.push_back("reset botAI");
|
||||
supported.push_back("emote");
|
||||
supported.push_back("buff");
|
||||
supported.push_back("help");
|
||||
supported.push_back("gb");
|
||||
supported.push_back("bank");
|
||||
supported.push_back("invite");
|
||||
supported.push_back("lfg");
|
||||
supported.push_back("spell");
|
||||
supported.push_back("rti");
|
||||
supported.push_back("position");
|
||||
supported.push_back("summon");
|
||||
supported.push_back("who");
|
||||
supported.push_back("save mana");
|
||||
supported.push_back("formation");
|
||||
supported.push_back("stance");
|
||||
supported.push_back("sendmail");
|
||||
supported.push_back("mail");
|
||||
supported.push_back("outfit");
|
||||
supported.push_back("go");
|
||||
supported.push_back("debug");
|
||||
supported.push_back("cdebug");
|
||||
supported.push_back("cs");
|
||||
supported.push_back("wts");
|
||||
supported.push_back("hire");
|
||||
supported.push_back("craft");
|
||||
supported.push_back("flag");
|
||||
supported.push_back("range");
|
||||
supported.push_back("ra");
|
||||
supported.push_back("give leader");
|
||||
supported.push_back("cheat");
|
||||
supported.push_back("ginvite");
|
||||
supported.push_back("guild promote");
|
||||
supported.push_back("guild demote");
|
||||
supported.push_back("guild remove");
|
||||
supported.push_back("guild leave");
|
||||
supported.push_back("rtsc");
|
||||
supported.push_back("drink");
|
||||
supported.push_back("calc");
|
||||
supported.push_back("open items");
|
||||
supported.push_back("qi");
|
||||
supported.push_back("unlock items");
|
||||
supported.push_back("unlock traded item");
|
||||
supported.push_back("tame");
|
||||
supported.push_back("glyphs"); // Added for custom Glyphs
|
||||
supported.push_back("glyph equip"); // Added for custom Glyphs
|
||||
supported.push_back("pet");
|
||||
supported.push_back("pet attack");
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "CombatStrategy.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
void CombatStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"enemy out of spell",
|
||||
{
|
||||
NextAction("reach spell", ACTION_HIGH)
|
||||
}
|
||||
)
|
||||
);
|
||||
// drop target relevance 99 (lower than Worldpacket triggers)
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"invalid target",
|
||||
{
|
||||
NextAction("drop target", 99)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"mounted",
|
||||
{
|
||||
NextAction("check mount state", 54)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"combat stuck",
|
||||
{
|
||||
NextAction("reset", 1.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"not facing target",
|
||||
{
|
||||
NextAction("set facing", ACTION_MOVE + 7)
|
||||
}
|
||||
)
|
||||
);
|
||||
// The pet-attack trigger is commented out because it was forcing the bot's pet to attack, overriding stay and follow commands.
|
||||
// Pets will automatically attack the bot's enemy if they are in "defensive" or "aggressive"
|
||||
// stance, or if the master issues an attack command.
|
||||
}
|
||||
|
||||
AvoidAoeStrategy::AvoidAoeStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
|
||||
|
||||
std::vector<NextAction> AvoidAoeStrategy::getDefaultActions()
|
||||
{
|
||||
return {
|
||||
NextAction("avoid aoe", ACTION_EMERGENCY)
|
||||
};
|
||||
}
|
||||
|
||||
void AvoidAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
}
|
||||
|
||||
void AvoidAoeStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||
{
|
||||
}
|
||||
|
||||
TankFaceStrategy::TankFaceStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
|
||||
|
||||
std::vector<NextAction> TankFaceStrategy::getDefaultActions()
|
||||
{
|
||||
return {
|
||||
NextAction("tank face", ACTION_MOVE)
|
||||
};
|
||||
}
|
||||
|
||||
void TankFaceStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<NextAction> CombatFormationStrategy::getDefaultActions()
|
||||
{
|
||||
return {
|
||||
NextAction("combat formation move", ACTION_NORMAL)
|
||||
};
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "DeadStrategy.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
void DeadStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
PassTroughStrategy::InitTriggers(triggers);
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode("often", { NextAction("auto release", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("bg active", { NextAction("auto release", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("dead", { NextAction("find corpse", relevance) }));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"corpse near", { NextAction("revive from corpse", relevance - 1.0f) }));
|
||||
triggers.push_back(new TriggerNode("resurrect request",
|
||||
{ NextAction("accept resurrect", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("falling far", { NextAction("repop", relevance + 1.f) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("location stuck", { NextAction("repop", relevance + 1) }));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"can self resurrect", { NextAction("self resurrect", relevance + 2.0f) }));
|
||||
}
|
||||
|
||||
DeadStrategy::DeadStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI) {}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "EmoteStrategy.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
void EmoteStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
if (sPlayerbotAIConfig->randomBotEmote)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("often", { NextAction("talk", 1.0f) }));
|
||||
triggers.push_back(new TriggerNode("seldom", { NextAction("emote", 1.0f) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("receive text emote", { NextAction("emote", 10.0f) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("receive emote", { NextAction("emote", 10.0f) }));
|
||||
}
|
||||
|
||||
if (sPlayerbotAIConfig->randomBotTalk)
|
||||
{
|
||||
triggers.push_back(new TriggerNode(
|
||||
"often",
|
||||
{ NextAction("suggest what to do", 10.0f), NextAction("suggest dungeon", 3.0f),
|
||||
NextAction("suggest trade", 3.0f) }));
|
||||
}
|
||||
|
||||
if (sPlayerbotAIConfig->enableGreet)
|
||||
triggers.push_back(
|
||||
new TriggerNode("new player nearby", { NextAction("greet", 1.0f) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("often", { NextAction("rpg mount anim", 1.0f) }));
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "GroupStrategy.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
void GroupStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("often", { NextAction("invite nearby", 4.0f) }));
|
||||
triggers.push_back(new TriggerNode("random", { NextAction("invite guild", 4.0f) }));
|
||||
triggers.push_back(new TriggerNode("random", { NextAction("leave far away", 4.0f) }));
|
||||
triggers.push_back(new TriggerNode("seldom", { NextAction("reset instances", 1.0f) }));
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "GuildStrategy.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
void GuildStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(
|
||||
new TriggerNode("often", { NextAction("offer petition nearby", 4.0f) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("often", { NextAction("guild manage nearby", 4.0f) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("petition signed", { NextAction("turn in petition", 10.0f) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("buy tabard", { NextAction("buy tabard", 10.0f) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("leave large guild", { NextAction("guild leave", 4.0f) }));
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "LootNonCombatStrategy.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
void LootNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("loot available", { NextAction("loot", 6.0f) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("far from loot target", { NextAction("move to loot", 7.0f) }));
|
||||
triggers.push_back(new TriggerNode("can loot", { NextAction("open loot", 8.0f) }));
|
||||
triggers.push_back(new TriggerNode("often", { NextAction("add all loot", 5.0f) }));
|
||||
}
|
||||
|
||||
void GatherStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(
|
||||
new TriggerNode("timer", { NextAction("add gathering loot", 5.0f) }));
|
||||
}
|
||||
|
||||
void RevealStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(
|
||||
new TriggerNode("often", { NextAction("reveal gathering item", 50.0f) }));
|
||||
}
|
||||
|
||||
void UseBobberStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(
|
||||
new TriggerNode("can use fishing bobber", { NextAction("use fishing bobber", 20.0f) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("random", { NextAction("remove bobber strategy", 20.0f) }));
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "MaintenanceStrategy.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
std::vector<NextAction> MaintenanceStrategy::getDefaultActions() { return {}; }
|
||||
|
||||
void MaintenanceStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"random",
|
||||
{
|
||||
NextAction("clean quest log", 6.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"random",
|
||||
{
|
||||
NextAction("use random recipe", 1.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"random",
|
||||
{
|
||||
NextAction("disenchant random item", 1.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"random",
|
||||
{
|
||||
NextAction("enchant random item", 1.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"random",
|
||||
{
|
||||
NextAction("smart destroy item", 1.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"move stuck",
|
||||
{
|
||||
NextAction("reset", 1.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"random",
|
||||
{
|
||||
NextAction("use random quest item", 0.9f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"random",
|
||||
{
|
||||
NextAction("auto share quest", 0.9f)
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "NonCombatStrategy.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
void NonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("random", { NextAction("clean quest log", 1.0f) }));
|
||||
triggers.push_back(new TriggerNode("timer", { NextAction("check mount state", 1.0f) }));
|
||||
}
|
||||
|
||||
void CollisionStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(
|
||||
new TriggerNode("collision", { NextAction("move out of collision", 2.0f) }));
|
||||
}
|
||||
|
||||
void MountStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
}
|
||||
|
||||
void WorldBuffStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"need world buff",
|
||||
{
|
||||
NextAction("world buff", 1.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void MasterFishingStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"very often",
|
||||
{
|
||||
NextAction("move near water" , 10.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"very often",
|
||||
{
|
||||
NextAction("go fishing" , 10.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"random",
|
||||
{
|
||||
NextAction("end master fishing", 12.0f),
|
||||
NextAction("equip upgrades", 6.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "RpgStrategy.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
#include "RpgSubActions.h"
|
||||
|
||||
float RpgActionMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (action == nullptr)
|
||||
return 1.0f;
|
||||
|
||||
std::string const nextAction = AI_VALUE(std::string, "next rpg action");
|
||||
std::string const name = action->getName();
|
||||
|
||||
if (!nextAction.empty() && dynamic_cast<RpgEnabled*>(action) && name != nextAction)
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
RpgStrategy::RpgStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
|
||||
|
||||
std::vector<NextAction> RpgStrategy::getDefaultActions()
|
||||
{
|
||||
return {
|
||||
NextAction("rpg", 1.0f)
|
||||
};
|
||||
}
|
||||
|
||||
void RpgStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"no rpg target",
|
||||
{
|
||||
NextAction("choose rpg target", 5.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"often",
|
||||
{
|
||||
NextAction("move random", 1.10f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"far from rpg target",
|
||||
{
|
||||
NextAction("move to rpg target", 5.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Sub actions
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"rpg",
|
||||
{
|
||||
NextAction("rpg stay", 1.101f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"rpg",
|
||||
{
|
||||
NextAction("rpg work", 1.101f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"rpg",
|
||||
{
|
||||
NextAction("rpg emote", 1.101f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"has rpg target",
|
||||
{
|
||||
NextAction("rpg cancel", 1.101f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"rpg discover",
|
||||
{
|
||||
NextAction("rpg discover", 1.210f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"rpg start quest",
|
||||
{
|
||||
NextAction("rpg start quest", 1.180f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"rpg end quest",
|
||||
{
|
||||
NextAction("rpg end quest", 1.190f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"rpg buy",
|
||||
{
|
||||
NextAction("rpg buy", 1.130f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"rpg repair",
|
||||
{
|
||||
NextAction("rpg repair", 1.195f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"rpg heal",
|
||||
{
|
||||
NextAction("rpg heal", 1.125f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"rpg home bind",
|
||||
{
|
||||
NextAction("rpg home bind", 1.160f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"rpg buy petition",
|
||||
{
|
||||
NextAction("rpg buy petition", 1.140f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"rpg use",
|
||||
{
|
||||
NextAction("rpg use", 1.102f)
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void RpgStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||
{
|
||||
multipliers.push_back(new RpgActionMultiplier(botAI));
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "SayStrategy.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
void SayStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("critical health",
|
||||
{ NextAction("say::critical health", 99.0f) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("low health", { NextAction("say::low health", 99.0f) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("low mana", { NextAction("say::low mana", 99.0f) }));
|
||||
triggers.push_back(new TriggerNode("tank aoe", { NextAction("say::taunt", 99.0f) }));
|
||||
triggers.push_back(new TriggerNode("medium aoe", { NextAction("say::aoe", 99.0f) }));
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "StayStrategy.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
void StayStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"return to stay position",
|
||||
{
|
||||
NextAction("return to stay position", ACTION_MOVE)
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
std::vector<NextAction> StayStrategy::getDefaultActions()
|
||||
{
|
||||
return {
|
||||
NextAction("stay", 1.0f)
|
||||
};
|
||||
}
|
||||
|
||||
void SitStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"sit",
|
||||
{
|
||||
NextAction("sit", 1.5f)
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "TravelStrategy.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
TravelStrategy::TravelStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
|
||||
|
||||
std::vector<NextAction> TravelStrategy::getDefaultActions()
|
||||
{
|
||||
return {
|
||||
NextAction("travel", 1.0f)
|
||||
};
|
||||
}
|
||||
|
||||
void TravelStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"no travel target",
|
||||
{
|
||||
NextAction("choose travel target", 6.f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"far from travel target",
|
||||
{
|
||||
NextAction("move to travel target", 1)
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "WorldPacketHandlerStrategy.h"
|
||||
|
||||
void WorldPacketHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
PassTroughStrategy::InitTriggers(triggers);
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode("group invite", { NextAction("accept invitation", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("uninvite", { NextAction("uninvite", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("uninvite guid", { NextAction("uninvite", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("group set leader", { /*NextAction("leader", relevance),*/ }));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"not enough money", { NextAction("tell not enough money", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("not enough reputation",
|
||||
{ NextAction("tell not enough reputation", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("cannot equip", { NextAction("tell cannot equip", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("use game object", { NextAction("add loot", relevance),
|
||||
NextAction("use meeting stone", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("gossip hello", { NextAction("trainer", relevance) }));
|
||||
triggers.push_back(new TriggerNode("activate taxi", { NextAction("remember taxi", relevance),
|
||||
NextAction("taxi", relevance) }));
|
||||
triggers.push_back(new TriggerNode("taxi done", { NextAction("taxi", relevance) }));
|
||||
triggers.push_back(new TriggerNode("trade status", { NextAction("accept trade", relevance), NextAction("equip upgrades", relevance) }));
|
||||
triggers.push_back(new TriggerNode("trade status extended", { NextAction("trade status extended", relevance) }));
|
||||
triggers.push_back(new TriggerNode("area trigger", { NextAction("reach area trigger", relevance) }));
|
||||
triggers.push_back(new TriggerNode("within area trigger", { NextAction("area trigger", relevance) }));
|
||||
triggers.push_back(new TriggerNode("loot response", { NextAction("store loot", relevance) }));
|
||||
triggers.push_back(new TriggerNode("item push result", { NextAction("unlock items", relevance),
|
||||
NextAction("open items", relevance),
|
||||
NextAction("query item usage", relevance),
|
||||
NextAction("equip upgrades", relevance) }));
|
||||
triggers.push_back(new TriggerNode("item push result", { NextAction("quest item push result", relevance) }));
|
||||
triggers.push_back(new TriggerNode("ready check finished", { NextAction("finish ready check", relevance) }));
|
||||
// triggers.push_back(new TriggerNode("often", { NextAction("security check", relevance), NextAction("check mail", relevance) }));
|
||||
triggers.push_back(new TriggerNode("guild invite", { NextAction("guild accept", relevance) }));
|
||||
triggers.push_back(new TriggerNode("petition offer", { NextAction("petition sign", relevance) }));
|
||||
triggers.push_back(new TriggerNode("lfg proposal", { NextAction("lfg accept", relevance) }));
|
||||
triggers.push_back(new TriggerNode("lfg proposal active", { NextAction("lfg accept", relevance) }));
|
||||
triggers.push_back(new TriggerNode("arena team invite", { NextAction("arena team accept", relevance) }));
|
||||
//triggers.push_back(new TriggerNode("no non bot players around", { NextAction("delay", relevance) }));
|
||||
triggers.push_back(new TriggerNode("bg status", { NextAction("bg status", relevance) }));
|
||||
triggers.push_back(new TriggerNode("xpgain", { NextAction("xp gain", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("levelup", { NextAction("auto maintenance on levelup", relevance + 3) }));
|
||||
// triggers.push_back(new TriggerNode("group destroyed", { NextAction("reset botAI",
|
||||
// relevance) }));
|
||||
triggers.push_back(new TriggerNode("group list", { NextAction("reset botAI", relevance) }));
|
||||
triggers.push_back(new TriggerNode("see spell", { NextAction("see spell", relevance) }));
|
||||
triggers.push_back(new TriggerNode("release spirit", { NextAction("release", relevance) }));
|
||||
triggers.push_back(new TriggerNode("revive from corpse", { NextAction("revive from corpse", relevance) }));
|
||||
triggers.push_back(new TriggerNode("master loot roll", { NextAction("master loot roll", relevance) }));
|
||||
|
||||
// quest ?
|
||||
//triggers.push_back(new TriggerNode("quest confirm", { NextAction("quest confirm", relevance) }));
|
||||
triggers.push_back(new TriggerNode("questgiver quest details", { NextAction("turn in query quest", relevance) }));
|
||||
|
||||
// loot roll
|
||||
triggers.push_back(new TriggerNode("very often", { NextAction("loot roll", relevance) }));
|
||||
}
|
||||
|
||||
WorldPacketHandlerStrategy::WorldPacketHandlerStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI)
|
||||
{
|
||||
supported.push_back("loot roll");
|
||||
supported.push_back("check mount state");
|
||||
supported.push_back("party command");
|
||||
supported.push_back("ready check");
|
||||
supported.push_back("uninvite");
|
||||
supported.push_back("lfg role check");
|
||||
supported.push_back("lfg teleport");
|
||||
supported.push_back("random bot update");
|
||||
supported.push_back("inventory change failure");
|
||||
supported.push_back("bg status");
|
||||
|
||||
// quests
|
||||
supported.push_back("quest update add kill");
|
||||
// supported.push_back("quest update add item");
|
||||
supported.push_back("quest update failed");
|
||||
supported.push_back("quest update failed timer");
|
||||
supported.push_back("quest update complete");
|
||||
supported.push_back("confirm quest");
|
||||
}
|
||||
|
||||
void ReadyCheckStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(
|
||||
new TriggerNode("timer", { NextAction("ready check", relevance) }));
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "GenericHunterNonCombatStrategy.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
class GenericHunterNonCombatStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
|
||||
{
|
||||
public:
|
||||
GenericHunterNonCombatStrategyActionNodeFactory()
|
||||
{
|
||||
creators["rapid fire"] = &rapid_fire;
|
||||
creators["boost"] = &rapid_fire;
|
||||
creators["aspect of the pack"] = &aspect_of_the_pack;
|
||||
}
|
||||
|
||||
private:
|
||||
static ActionNode* rapid_fire([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("rapid fire",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("readiness")},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* aspect_of_the_pack([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("aspect of the pack",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("aspect of the cheetah")},
|
||||
/*C*/ {});
|
||||
}
|
||||
};
|
||||
|
||||
GenericHunterNonCombatStrategy::GenericHunterNonCombatStrategy(PlayerbotAI* botAI) : NonCombatStrategy(botAI)
|
||||
{
|
||||
actionNodeFactories.Add(new GenericHunterNonCombatStrategyActionNodeFactory());
|
||||
}
|
||||
|
||||
void GenericHunterNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
NonCombatStrategy::InitTriggers(triggers);
|
||||
|
||||
triggers.push_back(new TriggerNode("trueshot aura", { NextAction("trueshot aura", 2.0f)}));
|
||||
triggers.push_back(new TriggerNode("often", {
|
||||
NextAction("apply stone", 1.0f),
|
||||
NextAction("apply oil", 1.0f),
|
||||
}));
|
||||
triggers.push_back(new TriggerNode("low ammo", { NextAction("say::low ammo", ACTION_NORMAL)}));
|
||||
triggers.push_back(new TriggerNode("no track", { NextAction("track humanoids", ACTION_NORMAL)}));
|
||||
triggers.push_back(new TriggerNode("no ammo", { NextAction("equip upgrades", ACTION_HIGH + 1)}));
|
||||
}
|
||||
|
||||
void HunterPetStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("no pet", { NextAction("call pet", 60.0f)}));
|
||||
triggers.push_back(new TriggerNode("has pet", { NextAction("toggle pet spell", 60.0f)}));
|
||||
triggers.push_back(new TriggerNode("new pet", { NextAction("set pet stance", 60.0f)}));
|
||||
triggers.push_back(new TriggerNode("pet not happy", { NextAction("feed pet", 60.0f)}));
|
||||
triggers.push_back(new TriggerNode("hunters pet medium health", { NextAction("mend pet", 60.0f)}));
|
||||
triggers.push_back(new TriggerNode("hunters pet dead", { NextAction("revive pet", 60.0f)}));
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "GenericHunterStrategy.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
class GenericHunterStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
|
||||
{
|
||||
public:
|
||||
GenericHunterStrategyActionNodeFactory()
|
||||
{
|
||||
creators["rapid fire"] = &rapid_fire;
|
||||
creators["boost"] = &rapid_fire;
|
||||
creators["aspect of the pack"] = &aspect_of_the_pack;
|
||||
creators["aspect of the dragonhawk"] = &aspect_of_the_dragonhawk;
|
||||
creators["feign death"] = &feign_death;
|
||||
creators["wing clip"] = &wing_clip;
|
||||
creators["mongoose bite"] = &mongoose_bite;
|
||||
creators["raptor strike"] = &raptor_strike;
|
||||
creators["explosive trap"] = &explosive_trap;
|
||||
}
|
||||
|
||||
private:
|
||||
static ActionNode* rapid_fire([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("rapid fire",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("readiness") },
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* aspect_of_the_pack([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("aspect of the pack",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("aspect of the cheetah") },
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* aspect_of_the_dragonhawk([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("aspect of the dragonhawk",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("aspect of the hawk") },
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* feign_death([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("feign death",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* wing_clip([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("wing clip",
|
||||
/*P*/ {},
|
||||
// /*A*/ { NextAction("mongoose bite") },
|
||||
{},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* mongoose_bite([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("mongoose bite",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("raptor strike") },
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* raptor_strike([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("raptor strike",
|
||||
/*P*/ { NextAction("melee") },
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* explosive_trap([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("explosive trap",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("immolation trap") },
|
||||
/*C*/ {});
|
||||
}
|
||||
};
|
||||
|
||||
GenericHunterStrategy::GenericHunterStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI)
|
||||
{
|
||||
actionNodeFactories.Add(new GenericHunterStrategyActionNodeFactory());
|
||||
}
|
||||
|
||||
void GenericHunterStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
CombatStrategy::InitTriggers(triggers);
|
||||
|
||||
// Mark/Ammo/Mana Triggers
|
||||
triggers.push_back(new TriggerNode("no ammo", { NextAction("equip upgrades", 30.0f) }));
|
||||
triggers.push_back(new TriggerNode("hunter's mark", { NextAction("hunter's mark", 29.5f) }));
|
||||
triggers.push_back(new TriggerNode("rapid fire", { NextAction("rapid fire", 29.0f) }));
|
||||
triggers.push_back(new TriggerNode("aspect of the viper", { NextAction("aspect of the viper", 28.0f) }));
|
||||
triggers.push_back(new TriggerNode("aspect of the hawk", { NextAction("aspect of the dragonhawk", 27.5f) }));
|
||||
|
||||
// Aggro/Threat/Defensive Triggers
|
||||
triggers.push_back(new TriggerNode("has aggro", { NextAction("concussive shot", 20.0f) }));
|
||||
triggers.push_back(new TriggerNode("low tank threat", { NextAction("misdirection on main tank", 27.0f) }));
|
||||
triggers.push_back(new TriggerNode("low health", { NextAction("deterrence", 35.0f) }));
|
||||
triggers.push_back(new TriggerNode("concussive shot on snare target", { NextAction("concussive shot", 20.0f) }));
|
||||
triggers.push_back(new TriggerNode("medium threat", { NextAction("feign death", 35.0f) }));
|
||||
triggers.push_back(new TriggerNode("hunters pet medium health", { NextAction("mend pet", 22.0f) }));
|
||||
triggers.push_back(new TriggerNode("hunters pet low health", { NextAction("mend pet", 21.0f) }));
|
||||
|
||||
// Dispel Triggers
|
||||
triggers.push_back(new TriggerNode("tranquilizing shot enrage", { NextAction("tranquilizing shot", 61.0f) }));
|
||||
triggers.push_back(new TriggerNode("tranquilizing shot magic", { NextAction("tranquilizing shot", 61.0f) }));
|
||||
|
||||
// Ranged-based Triggers
|
||||
triggers.push_back(new TriggerNode("enemy within melee", {
|
||||
NextAction("explosive trap", 37.0f),
|
||||
NextAction("mongoose bite", 22.0f),
|
||||
NextAction("wing clip", 21.0f) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("enemy too close for auto shot", {
|
||||
NextAction("disengage", 35.0f),
|
||||
NextAction("flee", 34.0f) }));
|
||||
}
|
||||
|
||||
// ===== AoE Strategy, 2/3+ enemies =====
|
||||
AoEHunterStrategy::AoEHunterStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) {}
|
||||
|
||||
void AoEHunterStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("volley channel check", { NextAction("cancel channel", 23.0f) }));
|
||||
triggers.push_back(new TriggerNode("medium aoe", { NextAction("volley", 22.0f) }));
|
||||
triggers.push_back(new TriggerNode("light aoe", { NextAction("multi-shot", 21.0f) }));
|
||||
}
|
||||
|
||||
void HunterBoostStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
}
|
||||
|
||||
void HunterCcStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("scare beast", { NextAction("scare beast on cc", 23.0f) }));
|
||||
triggers.push_back(new TriggerNode("freezing trap", { NextAction("freezing trap on cc", 23.0f) }));
|
||||
}
|
||||
|
||||
void HunterTrapWeaveStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("immolation trap no cd", { NextAction("reach melee", 23.0f) }));
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "SurvivalHunterStrategy.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
// ===== Action Node Factory =====
|
||||
class SurvivalHunterStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
|
||||
{
|
||||
public:
|
||||
SurvivalHunterStrategyActionNodeFactory()
|
||||
{
|
||||
creators["auto shot"] = &auto_shot;
|
||||
creators["kill command"] = &kill_command;
|
||||
creators["kill shot"] = &kill_shot;
|
||||
creators["explosive shot"] = &explosive_shot;
|
||||
creators["black arrow"] = &black_arrow;
|
||||
creators["viper sting"] = &viper_sting;
|
||||
creators["serpent sting"] = serpent_sting;
|
||||
creators["aimed shot"] = &aimed_shot;
|
||||
creators["arcane shot"] = &arcane_shot;
|
||||
creators["steady shot"] = &steady_shot;
|
||||
creators["multi-shot"] = &multi_shot;
|
||||
creators["volley"] = &volley;
|
||||
}
|
||||
|
||||
private:
|
||||
static ActionNode* auto_shot(PlayerbotAI*) { return new ActionNode("auto shot", {}, {}, {}); }
|
||||
static ActionNode* kill_command(PlayerbotAI*) { return new ActionNode("kill command", {}, {}, {}); }
|
||||
static ActionNode* kill_shot(PlayerbotAI*) { return new ActionNode("kill shot", {}, {}, {}); }
|
||||
static ActionNode* explosive_shot(PlayerbotAI*) { return new ActionNode("explosive shot", {}, {}, {}); }
|
||||
static ActionNode* black_arrow(PlayerbotAI*) { return new ActionNode("black arrow", {}, {}, {}); }
|
||||
static ActionNode* viper_sting(PlayerbotAI*) { return new ActionNode("viper sting", {}, {}, {}); }
|
||||
static ActionNode* serpent_sting(PlayerbotAI*) { return new ActionNode("serpent sting", {}, {}, {}); }
|
||||
static ActionNode* aimed_shot(PlayerbotAI*) { return new ActionNode("aimed shot", {}, {}, {}); }
|
||||
static ActionNode* arcane_shot(PlayerbotAI*) { return new ActionNode("arcane shot", {}, {}, {}); }
|
||||
static ActionNode* steady_shot(PlayerbotAI*) { return new ActionNode("steady shot", {}, {}, {}); }
|
||||
static ActionNode* multi_shot(PlayerbotAI*) { return new ActionNode("multi shot", {}, {}, {}); }
|
||||
static ActionNode* volley(PlayerbotAI*) { return new ActionNode("volley", {}, {}, {}); }
|
||||
};
|
||||
|
||||
// ===== Single Target Strategy =====
|
||||
SurvivalHunterStrategy::SurvivalHunterStrategy(PlayerbotAI* botAI) : GenericHunterStrategy(botAI)
|
||||
{
|
||||
actionNodeFactories.Add(new SurvivalHunterStrategyActionNodeFactory());
|
||||
}
|
||||
|
||||
// ===== Default Actions =====
|
||||
std::vector<NextAction> SurvivalHunterStrategy::getDefaultActions()
|
||||
{
|
||||
return {
|
||||
NextAction("kill command", 5.9f),
|
||||
NextAction("kill shot", 5.8f),
|
||||
NextAction("explosive shot", 5.7f),
|
||||
NextAction("black arrow", 5.6f),
|
||||
NextAction("serpent sting", 5.5f),
|
||||
NextAction("aimed shot", 5.4f),
|
||||
NextAction("arcane shot", 5.3f),
|
||||
NextAction("steady shot", 5.2f),
|
||||
NextAction("auto shot", 5.1f)
|
||||
};
|
||||
}
|
||||
|
||||
// ===== Trigger Initialization ===
|
||||
void SurvivalHunterStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
GenericHunterStrategy::InitTriggers(triggers);
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"lock and load",
|
||||
{
|
||||
NextAction("explosive shot rank 4", 28.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"lock and load",
|
||||
{
|
||||
NextAction("explosive shot rank 3", 27.5f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"lock and load",
|
||||
{
|
||||
NextAction("explosive shot rank 2", 27.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"lock and load",
|
||||
{
|
||||
NextAction("explosive shot rank 1", 26.5f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"kill command",
|
||||
{
|
||||
NextAction("kill command", 18.5f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"target critical health",
|
||||
{
|
||||
NextAction("kill shot", 18.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"explosive shot",
|
||||
{
|
||||
NextAction("explosive shot", 17.5f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"black arrow",
|
||||
{
|
||||
NextAction("black arrow", 16.5f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"low mana",
|
||||
{
|
||||
NextAction("viper sting", 16.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"no stings",
|
||||
{
|
||||
NextAction("serpent sting", 15.5f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"serpent sting on attacker",
|
||||
{
|
||||
NextAction("serpent sting on attacker", 15.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "FrostMageStrategy.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
// ===== Action Node Factory =====
|
||||
class FrostMageStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
|
||||
{
|
||||
public:
|
||||
FrostMageStrategyActionNodeFactory()
|
||||
{
|
||||
creators["cold snap"] = &cold_snap;
|
||||
creators["ice barrier"] = &ice_barrier;
|
||||
creators["summon water elemental"] = &summon_water_elemental;
|
||||
creators["deep freeze"] = &deep_freeze;
|
||||
creators["icy veins"] = &icy_veins;
|
||||
creators["frostbolt"] = &frostbolt;
|
||||
creators["ice lance"] = &ice_lance;
|
||||
creators["fire blast"] = &fire_blast;
|
||||
creators["fireball"] = &fireball;
|
||||
creators["frostfire bolt"] = &frostfire_bolt;
|
||||
}
|
||||
|
||||
private:
|
||||
static ActionNode* cold_snap(PlayerbotAI*) { return new ActionNode("cold snap", {}, {}, {}); }
|
||||
static ActionNode* ice_barrier(PlayerbotAI*) { return new ActionNode("ice barrier", {}, {}, {}); }
|
||||
static ActionNode* summon_water_elemental(PlayerbotAI*) { return new ActionNode("summon water elemental", {}, {}, {}); }
|
||||
static ActionNode* deep_freeze(PlayerbotAI*) { return new ActionNode("deep freeze", {}, {}, {}); }
|
||||
static ActionNode* icy_veins(PlayerbotAI*) { return new ActionNode("icy veins", {}, {}, {}); }
|
||||
static ActionNode* frostbolt(PlayerbotAI*) { return new ActionNode("frostbolt", {}, {}, {}); }
|
||||
static ActionNode* ice_lance(PlayerbotAI*) { return new ActionNode("ice lance", {}, {}, {}); }
|
||||
static ActionNode* fire_blast(PlayerbotAI*) { return new ActionNode("fire blast", {}, {}, {}); }
|
||||
static ActionNode* fireball(PlayerbotAI*) { return new ActionNode("fireball", {}, {}, {}); }
|
||||
static ActionNode* frostfire_bolt(PlayerbotAI*) { return new ActionNode("frostfire bolt", {}, {}, {}); }
|
||||
};
|
||||
|
||||
// ===== Single Target Strategy =====
|
||||
FrostMageStrategy::FrostMageStrategy(PlayerbotAI* botAI) : GenericMageStrategy(botAI)
|
||||
{
|
||||
actionNodeFactories.Add(new FrostMageStrategyActionNodeFactory());
|
||||
}
|
||||
|
||||
// ===== Default Actions =====
|
||||
std::vector<NextAction> FrostMageStrategy::getDefaultActions()
|
||||
{
|
||||
return {
|
||||
NextAction("frostbolt", 5.4f),
|
||||
NextAction("ice lance", 5.3f), // cast during movement
|
||||
NextAction("fire blast", 5.2f), // cast during movement if ice lance is not learned
|
||||
NextAction("shoot", 5.1f),
|
||||
NextAction("fireball", 5.0f)
|
||||
};
|
||||
}
|
||||
|
||||
// ===== Trigger Initialization ===
|
||||
void FrostMageStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
GenericMageStrategy::InitTriggers(triggers);
|
||||
|
||||
// Pet/Defensive triggers
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"no pet",
|
||||
{
|
||||
NextAction("summon water elemental", 30.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"has pet",
|
||||
{
|
||||
NextAction("toggle pet spell", 60.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"new pet",
|
||||
{
|
||||
NextAction("set pet stance", 60.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"medium health",
|
||||
{
|
||||
NextAction("ice barrier", 29.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"being attacked",
|
||||
{
|
||||
NextAction("ice barrier", 29.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Proc/Freeze triggers
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"brain freeze",
|
||||
{
|
||||
NextAction("frostfire bolt", 19.5f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"fingers of frost",
|
||||
{
|
||||
NextAction("deep freeze", 19.0f),
|
||||
NextAction("frostbolt", 18.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"frostbite on target",
|
||||
{
|
||||
NextAction("deep freeze", 19.0f),
|
||||
NextAction("frostbolt", 18.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"frost nova on target",
|
||||
{
|
||||
NextAction("deep freeze", 19.0f),
|
||||
NextAction("frostbolt", 18.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -1,278 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "GenericMageStrategy.h"
|
||||
#include "AiFactory.h"
|
||||
#include "Playerbots.h"
|
||||
#include "RangedCombatStrategy.h"
|
||||
|
||||
class GenericMageStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
|
||||
{
|
||||
public:
|
||||
GenericMageStrategyActionNodeFactory()
|
||||
{
|
||||
creators["frostbolt"] = &frostbolt;
|
||||
creators["frostfire bolt"] = &frostfire_bolt;
|
||||
creators["ice lance"] = &ice_lance;
|
||||
creators["fire blast"] = &fire_blast;
|
||||
creators["scorch"] = &scorch;
|
||||
creators["frost nova"] = &frost_nova;
|
||||
creators["cone of cold"] = &cone_of_cold;
|
||||
creators["icy veins"] = &icy_veins;
|
||||
creators["combustion"] = &combustion;
|
||||
creators["evocation"] = &evocation;
|
||||
creators["dragon's breath"] = &dragons_breath;
|
||||
creators["blast wave"] = &blast_wave;
|
||||
creators["remove curse"] = &remove_curse;
|
||||
creators["remove curse on party"] = &remove_curse_on_party;
|
||||
creators["fireball"] = &fireball;
|
||||
}
|
||||
|
||||
private:
|
||||
static ActionNode* frostbolt([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("frostbolt",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("shoot") },
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* frostfire_bolt([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("frostfire bolt",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("fireball") },
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* ice_lance([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("ice lance",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* fire_blast([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("fire blast",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* scorch([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("scorch",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("shoot") },
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* frost_nova([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("frost nova",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* cone_of_cold([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("cone of cold",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* icy_veins([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("icy veins",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* combustion([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("combustion",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* evocation([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("evocation",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("mana potion") },
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* dragons_breath([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("dragon's breath",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* blast_wave([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("blast wave",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* remove_curse([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("remove curse",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("remove lesser curse") },
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* remove_curse_on_party([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("remove curse on party",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("remove lesser curse on party") },
|
||||
/*C*/ {});
|
||||
}
|
||||
static ActionNode* fireball([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("fireball",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("shoot") },
|
||||
/*C*/ {});
|
||||
}
|
||||
};
|
||||
|
||||
GenericMageStrategy::GenericMageStrategy(PlayerbotAI* botAI) : RangedCombatStrategy(botAI)
|
||||
{
|
||||
actionNodeFactories.Add(new GenericMageStrategyActionNodeFactory());
|
||||
}
|
||||
|
||||
void GenericMageStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
RangedCombatStrategy::InitTriggers(triggers);
|
||||
|
||||
// Threat Triggers
|
||||
triggers.push_back(new TriggerNode("high threat", { NextAction("mirror image", 60.0f) }));
|
||||
triggers.push_back(new TriggerNode("medium threat", { NextAction("invisibility", 30.0f) }));
|
||||
|
||||
// Defensive Triggers
|
||||
triggers.push_back(new TriggerNode("critical health", { NextAction("ice block", 90.0f) }));
|
||||
triggers.push_back(new TriggerNode("low health", { NextAction("mana shield", 85.0f) }));
|
||||
triggers.push_back(new TriggerNode("fire ward", { NextAction("fire ward", 90.0f) }));
|
||||
triggers.push_back(new TriggerNode("frost ward", { NextAction("frost ward", 90.0f) }));
|
||||
triggers.push_back(new TriggerNode("enemy is close and no firestarter strategy", { NextAction("frost nova", 50.0f) }));
|
||||
triggers.push_back(new TriggerNode("enemy too close for spell and no firestarter strategy", { NextAction("blink back", 35.0f) }));
|
||||
|
||||
// Mana Threshold Triggers
|
||||
Player* bot = botAI->GetBot();
|
||||
if (bot->HasSpell(42985)) // Mana Sapphire
|
||||
triggers.push_back(new TriggerNode("high mana", { NextAction("use mana sapphire", 90.0f) }));
|
||||
else if (bot->HasSpell(27101)) // Mana Emerald
|
||||
triggers.push_back(new TriggerNode("high mana", { NextAction("use mana emerald", 90.0f) }));
|
||||
else if (bot->HasSpell(10054)) // Mana Ruby
|
||||
triggers.push_back(new TriggerNode("high mana", { NextAction("use mana ruby", 90.0f) }));
|
||||
else if (bot->HasSpell(10053)) // Mana Citrine
|
||||
triggers.push_back(new TriggerNode("high mana", { NextAction("use mana citrine", 90.0f) }));
|
||||
else if (bot->HasSpell(3552)) // Mana Jade
|
||||
triggers.push_back(new TriggerNode("high mana", { NextAction("use mana jade", 90.0f) }));
|
||||
else if (bot->HasSpell(759)) // Mana Agate
|
||||
triggers.push_back(new TriggerNode("high mana", { NextAction("use mana agate", 90.0f) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("medium mana", { NextAction("mana potion", 90.0f) }));
|
||||
triggers.push_back(new TriggerNode("low mana", { NextAction("evocation", 90.0f) }));
|
||||
|
||||
// Counterspell / Spellsteal Triggers
|
||||
triggers.push_back(new TriggerNode("spellsteal", { NextAction("spellsteal", 40.0f) }));
|
||||
triggers.push_back(new TriggerNode("counterspell on enemy healer", { NextAction("counterspell on enemy healer", 40.0f) }));
|
||||
}
|
||||
|
||||
void MageCureStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("remove curse", { NextAction("remove curse", 41.0f) }));
|
||||
triggers.push_back(new TriggerNode("remove curse on party", { NextAction("remove curse on party", 40.0f) }));
|
||||
}
|
||||
|
||||
void MageBoostStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
Player* bot = botAI->GetBot();
|
||||
int tab = AiFactory::GetPlayerSpecTab(bot);
|
||||
|
||||
if (tab == 0) // Arcane
|
||||
{
|
||||
triggers.push_back(new TriggerNode("arcane power", { NextAction("arcane power", 29.0f) }));
|
||||
triggers.push_back(new TriggerNode("icy veins", { NextAction("icy veins", 28.5f) }));
|
||||
triggers.push_back(new TriggerNode("mirror image", { NextAction("mirror image", 28.0f) }));
|
||||
}
|
||||
else if (tab == 1)
|
||||
{
|
||||
if (bot->HasSpell(44614) /*Frostfire Bolt*/ && bot->HasAura(15047) /*Ice Shards*/)
|
||||
{ // Frostfire
|
||||
triggers.push_back(new TriggerNode("combustion", { NextAction("combustion", 18.0f) }));
|
||||
triggers.push_back(new TriggerNode("icy veins", { NextAction("icy veins", 17.5f) }));
|
||||
triggers.push_back(new TriggerNode("mirror image", { NextAction("mirror image", 17.0f) }));
|
||||
}
|
||||
else
|
||||
{ // Fire
|
||||
triggers.push_back(new TriggerNode("combustion", { NextAction("combustion", 18.0f) }));
|
||||
triggers.push_back(new TriggerNode("mirror image", { NextAction("mirror image", 17.5f) }));
|
||||
}
|
||||
}
|
||||
else if (tab == 2) // Frost
|
||||
{
|
||||
triggers.push_back(new TriggerNode("cold snap", { NextAction("cold snap", 28.0f) }));
|
||||
triggers.push_back(new TriggerNode("icy veins", { NextAction("icy veins", 27.5f) }));
|
||||
triggers.push_back(new TriggerNode("mirror image", { NextAction("mirror image", 26.0f) }));
|
||||
}
|
||||
}
|
||||
|
||||
void MageCcStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("polymorph", { NextAction("polymorph", 30.0f) }));
|
||||
}
|
||||
|
||||
void MageAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("blizzard channel check", { NextAction("cancel channel", 26.0f) }));
|
||||
|
||||
Player* bot = botAI->GetBot();
|
||||
int tab = AiFactory::GetPlayerSpecTab(bot);
|
||||
|
||||
if (tab == 0) // Arcane
|
||||
{
|
||||
triggers.push_back(new TriggerNode("flamestrike active and medium aoe", { NextAction("blizzard", 24.0f) }));
|
||||
triggers.push_back(new TriggerNode("medium aoe", {
|
||||
NextAction("flamestrike", 23.0f),
|
||||
NextAction("blizzard", 22.0f) }));
|
||||
triggers.push_back(new TriggerNode("light aoe", { NextAction("arcane explosion", 21.0f) }));
|
||||
}
|
||||
else if (tab == 1) // Fire and Frostfire
|
||||
{
|
||||
triggers.push_back(
|
||||
new TriggerNode("medium aoe", {
|
||||
NextAction("dragon's breath", 39.0f),
|
||||
NextAction("blast wave", 38.0f),
|
||||
NextAction("flamestrike", 23.0f),
|
||||
NextAction("blizzard", 22.0f) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("flamestrike active and medium aoe", { NextAction("blizzard", 24.0f) }));
|
||||
triggers.push_back(new TriggerNode("firestarter", { NextAction("flamestrike", 40.0f) }));
|
||||
triggers.push_back(new TriggerNode("living bomb on attackers", { NextAction("living bomb on attackers", 21.0f) }));
|
||||
}
|
||||
else if (tab == 2) // Frost
|
||||
{
|
||||
triggers.push_back(new TriggerNode("flamestrike active and medium aoe", { NextAction("blizzard", 24.0f) }));
|
||||
triggers.push_back(new TriggerNode("medium aoe", {
|
||||
NextAction("flamestrike", 23.0f),
|
||||
NextAction("blizzard", 22.0f) }));
|
||||
triggers.push_back(new TriggerNode("light aoe", { NextAction("cone of cold", 21.0f) }));
|
||||
}
|
||||
}
|
||||
@@ -1,212 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "DpsPaladinStrategy.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
class DpsPaladinStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
|
||||
{
|
||||
public:
|
||||
DpsPaladinStrategyActionNodeFactory()
|
||||
{
|
||||
creators["sanctity aura"] = &sanctity_aura;
|
||||
creators["retribution aura"] = &retribution_aura;
|
||||
creators["seal of corruption"] = &seal_of_corruption;
|
||||
creators["seal of vengeance"] = &seal_of_vengeance;
|
||||
creators["seal of command"] = &seal_of_command;
|
||||
creators["blessing of might"] = &blessing_of_might;
|
||||
creators["crusader strike"] = &crusader_strike;
|
||||
creators["repentance"] = &repentance;
|
||||
creators["repentance on enemy healer"] = &repentance_on_enemy_healer;
|
||||
creators["repentance on snare target"] = &repentance_on_snare_target;
|
||||
creators["repentance of shield"] = &repentance_or_shield;
|
||||
}
|
||||
|
||||
private:
|
||||
static ActionNode* seal_of_corruption([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"seal of corruption",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("seal of vengeance") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* seal_of_vengeance([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"seal of vengeance",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("seal of command") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* seal_of_command([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"seal of command",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("seal of righteousness") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* blessing_of_might([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"blessing of might",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("blessing of kings") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* crusader_strike([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"crusader strike",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* repentance([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"repentance",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("hammer of justice") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* repentance_on_enemy_healer([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"repentance on enemy healer",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("hammer of justice on enemy healer") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* repentance_on_snare_target([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"repentance on snare target",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("hammer of justice on snare target") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* sanctity_aura([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"sanctity aura",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("retribution aura") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* retribution_aura([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"retribution aura",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("devotion aura") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* repentance_or_shield([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode(
|
||||
"repentance",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("divine shield") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
DpsPaladinStrategy::DpsPaladinStrategy(PlayerbotAI* botAI) : GenericPaladinStrategy(botAI)
|
||||
{
|
||||
actionNodeFactories.Add(new DpsPaladinStrategyActionNodeFactory());
|
||||
}
|
||||
|
||||
std::vector<NextAction> DpsPaladinStrategy::getDefaultActions()
|
||||
{
|
||||
return {
|
||||
NextAction("hammer of wrath", ACTION_DEFAULT + 0.6f),
|
||||
NextAction("judgement of wisdom", ACTION_DEFAULT + 0.5f),
|
||||
NextAction("crusader strike", ACTION_DEFAULT + 0.4f),
|
||||
NextAction("divine storm", ACTION_DEFAULT + 0.3f),
|
||||
NextAction("consecration", ACTION_DEFAULT + 0.1f),
|
||||
NextAction("melee", ACTION_DEFAULT)
|
||||
};
|
||||
}
|
||||
|
||||
void DpsPaladinStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
GenericPaladinStrategy::InitTriggers(triggers);
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"art of war",
|
||||
{
|
||||
NextAction("exorcism", ACTION_DEFAULT + 0.2f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"seal",
|
||||
{
|
||||
NextAction("seal of corruption", ACTION_HIGH)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"low mana",
|
||||
{
|
||||
NextAction("seal of wisdom", ACTION_HIGH + 5)
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"avenging wrath",
|
||||
{
|
||||
NextAction("avenging wrath", ACTION_HIGH + 2)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"medium aoe",
|
||||
{
|
||||
NextAction("divine storm", ACTION_HIGH + 4),
|
||||
NextAction("consecration", ACTION_HIGH + 3)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"enemy out of melee",
|
||||
{
|
||||
NextAction("reach melee", ACTION_HIGH + 1)
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "GenericPaladinStrategy.h"
|
||||
|
||||
#include "GenericPaladinStrategyActionNodeFactory.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
GenericPaladinStrategy::GenericPaladinStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI)
|
||||
{
|
||||
actionNodeFactories.Add(new GenericPaladinStrategyActionNodeFactory());
|
||||
}
|
||||
|
||||
void GenericPaladinStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
CombatStrategy::InitTriggers(triggers);
|
||||
|
||||
triggers.push_back(new TriggerNode("critical health", { NextAction("divine shield",
|
||||
ACTION_HIGH + 5) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("hammer of justice interrupt",
|
||||
{ NextAction("hammer of justice", ACTION_INTERRUPT) }));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"hammer of justice on enemy healer",
|
||||
{ NextAction("hammer of justice on enemy healer", ACTION_INTERRUPT) }));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"hammer of justice on snare target",
|
||||
{ NextAction("hammer of justice on snare target", ACTION_INTERRUPT) }));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"critical health", { NextAction("lay on hands", ACTION_EMERGENCY) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("party member critical health",
|
||||
{ NextAction("lay on hands on party", ACTION_EMERGENCY + 1) }));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"protect party member",
|
||||
{ NextAction("blessing of protection on party", ACTION_EMERGENCY + 2) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("high mana", { NextAction("divine plea", ACTION_HIGH) }));
|
||||
}
|
||||
|
||||
void PaladinCureStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode(
|
||||
"cleanse cure disease", { NextAction("cleanse disease", ACTION_DISPEL + 2) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("cleanse party member cure disease",
|
||||
{ NextAction("cleanse disease on party", ACTION_DISPEL + 1) }));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"cleanse cure poison", { NextAction("cleanse poison", ACTION_DISPEL + 2) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("cleanse party member cure poison",
|
||||
{ NextAction("cleanse poison on party", ACTION_DISPEL + 1) }));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"cleanse cure magic", { NextAction("cleanse magic", ACTION_DISPEL + 2) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("cleanse party member cure magic",
|
||||
{ NextAction("cleanse magic on party", ACTION_DISPEL + 1) }));
|
||||
}
|
||||
|
||||
void PaladinBoostStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
|
||||
// triggers.push_back(new TriggerNode("divine favor", { NextAction("divine favor",
|
||||
// ACTION_HIGH + 1) }));
|
||||
}
|
||||
|
||||
void PaladinCcStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(
|
||||
new TriggerNode("turn undead", { NextAction("turn undead", ACTION_HIGH + 1) }));
|
||||
}
|
||||
|
||||
void PaladinHealerDpsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(
|
||||
new TriggerNode("healer should attack",
|
||||
{
|
||||
NextAction("hammer of wrath", ACTION_DEFAULT + 0.6f),
|
||||
NextAction("holy shock", ACTION_DEFAULT + 0.5f),
|
||||
NextAction("shield of righteousness", ACTION_DEFAULT + 0.4f),
|
||||
NextAction("judgement of light", ACTION_DEFAULT + 0.3f),
|
||||
NextAction("consecration", ACTION_DEFAULT + 0.2f),
|
||||
NextAction("exorcism", ACTION_DEFAULT+ 0.1f),
|
||||
}));
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user