mirror of
https://github.com/mod-playerbots/mod-playerbots.git
synced 2026-01-13 00:58:33 +00:00
Compare commits
20 Commits
hermensbas
...
59d6eb139e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
59d6eb139e | ||
|
|
00171a8c82 | ||
|
|
02e8465a3b | ||
|
|
f53a8704eb | ||
|
|
e5525958c8 | ||
|
|
9ae457d069 | ||
|
|
c9e98a6b4e | ||
|
|
3d9623f119 | ||
|
|
c9cc4324d3 | ||
|
|
b13fb7d12a | ||
|
|
962fdeb3d1 | ||
|
|
83c6977de5 | ||
|
|
686fe513b2 | ||
|
|
61402e83a1 | ||
|
|
b16789fa54 | ||
|
|
8f638b6a66 | ||
|
|
33f5e733dc | ||
|
|
9917863ca1 | ||
|
|
2317652d72 | ||
|
|
1fcd6c5cda |
@@ -21,10 +21,11 @@
|
||||
# THRESHOLDS
|
||||
# QUESTS
|
||||
# COMBAT
|
||||
# PALADIN BUFFS STRATEGIES
|
||||
# GREATER BUFFS STRATEGIES
|
||||
# CHEATS
|
||||
# SPELLS
|
||||
# FLIGHTPATH
|
||||
# PROFESSIONS
|
||||
# RANDOMBOT-SPECIFIC SETTINGS
|
||||
# GENERAL
|
||||
# LEVELS
|
||||
@@ -44,7 +45,7 @@
|
||||
# HUNTER
|
||||
# ROGUE
|
||||
# PRIEST
|
||||
# DEATHKNIGHT
|
||||
# DEATH KNIGHT
|
||||
# SHAMAN
|
||||
# MAGE
|
||||
# WARLOCK
|
||||
@@ -55,7 +56,7 @@
|
||||
# HUNTER
|
||||
# ROGUE
|
||||
# PRIEST
|
||||
# DEATHKNIGHT
|
||||
# DEATH KNIGHT
|
||||
# SHAMAN
|
||||
# MAGE
|
||||
# WARLOCK
|
||||
@@ -90,17 +91,20 @@ 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
|
||||
@@ -152,7 +156,8 @@ 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 (i.e., 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,
|
||||
# meaning randombot guilds will have only the 10 initial randombots needed to sign the charter
|
||||
# Default: 0 (disabled)
|
||||
AiPlayerbot.RandomBotGuildNearby = 0
|
||||
|
||||
@@ -186,7 +191,8 @@ 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
|
||||
|
||||
#
|
||||
@@ -263,7 +269,7 @@ AiPlayerbot.UseFastFlyMountAtMinLevel = 70
|
||||
AiPlayerbot.RandomBotShowHelmet = 1
|
||||
AiPlayerbot.RandomBotShowCloak = 1
|
||||
|
||||
# Randombots and altbots automatically equip upgrades (bots will equip any item obtained from looting or a quest if they are sufficient upgrades)
|
||||
# Randombots and altbots automatically equip any items in their inventory that are sufficient upgrades
|
||||
# Default: 1 (enabled)
|
||||
AiPlayerbot.AutoEquipUpgradeLoot = 1
|
||||
|
||||
@@ -311,7 +317,8 @@ AiPlayerbot.GlobalCooldown = 500
|
||||
# Max wait time when moving
|
||||
AiPlayerbot.MaxWaitForMove = 5000
|
||||
|
||||
# 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)
|
||||
# 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
|
||||
# Default: 0 - MoveSplinePath enabled
|
||||
# 1 - MoveSplinePath disabled in BG/Arena only
|
||||
# 2 - MoveSplinePath disabled everywhere
|
||||
@@ -405,10 +412,11 @@ 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
|
||||
|
||||
@@ -433,7 +441,7 @@ AiPlayerbot.DropObsoleteQuests = 1
|
||||
# Auto add dungeon/raid strategies when entering the instance if implemented
|
||||
AiPlayerbot.ApplyInstanceStrategies = 1
|
||||
|
||||
# Enable auto avoid aoe strategy (experimental)
|
||||
# Enable auto avoid aoe strategy
|
||||
# Default: 1 (enabled)
|
||||
AiPlayerbot.AutoAvoidAoe = 1
|
||||
|
||||
@@ -460,7 +468,7 @@ AiPlayerbot.FleeingEnabled = 1
|
||||
####################################################################################################
|
||||
|
||||
####################################################################################################
|
||||
# PALADIN BUFFS STRATEGIES
|
||||
# GREATER BUFFS STRATEGIES
|
||||
#
|
||||
#
|
||||
|
||||
@@ -483,12 +491,13 @@ AiPlayerbot.RPWarningCooldown = 30
|
||||
#
|
||||
|
||||
# Enable/Disable maintenance command
|
||||
# Learn all available spells and skills, refresh consumables, repair, enchant equipment and socket gems if bot's level is above AiPlayerbot.MinEnchantingBotLevel
|
||||
# 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
|
||||
# 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
|
||||
@@ -500,6 +509,7 @@ 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
|
||||
@@ -514,8 +524,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
|
||||
|
||||
@@ -579,6 +589,30 @@ 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 #
|
||||
@@ -700,7 +734,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 1.5(ZA) = 138 | Phase 2(SC, TK) = 141 | Phase 3(Hyjal, BT) = 156 | Phase 4(Sunwell) = 164
|
||||
# Max iLVL Phase 1(Kara, Gruul, Mag) = 125 | Phase 2(SSC, TK, ZA) = 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
|
||||
@@ -711,7 +745,8 @@ AiPlayerbot.RandomGearScoreLimit = 0
|
||||
# Default: 1 (enabled)
|
||||
AiPlayerbot.IncrementalGearInit = 1
|
||||
|
||||
# Set minimum level of bots that will enchant their equipment (if greater than RandomBotMaxlevel, bots will not enchant equipment)
|
||||
# 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
|
||||
# Default: 60
|
||||
AiPlayerbot.MinEnchantingBotLevel = 60
|
||||
|
||||
@@ -863,13 +898,15 @@ 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 = ""
|
||||
|
||||
@@ -1455,7 +1492,7 @@ AiPlayerbot.PremadeSpecLink.5.5.80 = 50332031003--005323241223112003102311351
|
||||
####################################################################################################
|
||||
|
||||
####################################################################################################
|
||||
# DEATHKNIGHT
|
||||
# DEATH KNIGHT
|
||||
#
|
||||
#
|
||||
|
||||
@@ -1778,7 +1815,7 @@ AiPlayerbot.RandomClassSpecIndex.5.2 = 2
|
||||
####################################################################################################
|
||||
|
||||
####################################################################################################
|
||||
# DEATHKNIGHT
|
||||
# DEATH KNIGHT
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
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 == 2)
|
||||
if (tab == PRIEST_TAB_SHADOW)
|
||||
role = BOT_ROLE_DPS;
|
||||
else
|
||||
role = BOT_ROLE_HEALER;
|
||||
break;
|
||||
case CLASS_SHAMAN:
|
||||
if (tab == 2)
|
||||
if (tab == SHAMAN_TAB_RESTORATION)
|
||||
role = BOT_ROLE_HEALER;
|
||||
else
|
||||
role = BOT_ROLE_DPS;
|
||||
break;
|
||||
case CLASS_WARRIOR:
|
||||
if (tab == 2)
|
||||
if (tab == WARRIOR_TAB_PROTECTION)
|
||||
role = BOT_ROLE_TANK;
|
||||
else
|
||||
role = BOT_ROLE_DPS;
|
||||
break;
|
||||
case CLASS_PALADIN:
|
||||
if (tab == 0)
|
||||
if (tab == PALADIN_TAB_HOLY)
|
||||
role = BOT_ROLE_HEALER;
|
||||
else if (tab == 1)
|
||||
else if (tab == PALADIN_TAB_PROTECTION)
|
||||
role = BOT_ROLE_TANK;
|
||||
else if (tab == 2)
|
||||
else if (tab == PALADIN_TAB_RETRIBUTION)
|
||||
role = BOT_ROLE_DPS;
|
||||
break;
|
||||
case CLASS_DRUID:
|
||||
if (tab == 0)
|
||||
if (tab == DRUID_TAB_BALANCE)
|
||||
role = BOT_ROLE_DPS;
|
||||
else if (tab == 1)
|
||||
else if (tab == DRUID_TAB_FERAL)
|
||||
role = (BotRoles)(BOT_ROLE_TANK | BOT_ROLE_DPS);
|
||||
else if (tab == 2)
|
||||
else if (tab == DRUID_TAB_RESTORATION)
|
||||
role = BOT_ROLE_HEALER;
|
||||
break;
|
||||
default:
|
||||
@@ -188,84 +188,83 @@ std::string AiFactory::GetPlayerSpecName(Player* player)
|
||||
switch (player->getClass())
|
||||
{
|
||||
case CLASS_PRIEST:
|
||||
if (tab == 2)
|
||||
if (tab == PRIEST_TAB_SHADOW)
|
||||
specName = "shadow";
|
||||
else if (tab == 1)
|
||||
else if (tab == PRIEST_TAB_HOLY)
|
||||
specName = "holy";
|
||||
else
|
||||
specName = "disc";
|
||||
;
|
||||
break;
|
||||
case CLASS_SHAMAN:
|
||||
if (tab == 2)
|
||||
if (tab == SHAMAN_TAB_RESTORATION)
|
||||
specName = "resto";
|
||||
else if (tab == 1)
|
||||
else if (tab == SHAMAN_TAB_ENHANCEMENT)
|
||||
specName = "enhance";
|
||||
else
|
||||
specName = "elem";
|
||||
break;
|
||||
case CLASS_WARRIOR:
|
||||
if (tab == 2)
|
||||
if (tab == WARRIOR_TAB_PROTECTION)
|
||||
specName = "prot";
|
||||
else if (tab == 1)
|
||||
else if (tab == WARRIOR_TAB_FURY)
|
||||
specName = "fury";
|
||||
else
|
||||
specName = "arms";
|
||||
break;
|
||||
case CLASS_PALADIN:
|
||||
if (tab == 0)
|
||||
if (tab == PALADIN_TAB_HOLY)
|
||||
specName = "holy";
|
||||
else if (tab == 1)
|
||||
else if (tab == PALADIN_TAB_PROTECTION)
|
||||
specName = "prot";
|
||||
else if (tab == 2)
|
||||
else if (tab == PALADIN_TAB_RETRIBUTION)
|
||||
specName = "retrib";
|
||||
break;
|
||||
case CLASS_DRUID:
|
||||
if (tab == 0)
|
||||
if (tab == DRUID_TAB_BALANCE)
|
||||
specName = "balance";
|
||||
else if (tab == 1)
|
||||
else if (tab == DRUID_TAB_FERAL)
|
||||
specName = "feraldps";
|
||||
else if (tab == 2)
|
||||
else if (tab == DRUID_TAB_RESTORATION)
|
||||
specName = "resto";
|
||||
break;
|
||||
case CLASS_ROGUE:
|
||||
if (tab == 0)
|
||||
if (tab == ROGUE_TAB_ASSASSINATION)
|
||||
specName = "assas";
|
||||
else if (tab == 1)
|
||||
else if (tab == ROGUE_TAB_COMBAT)
|
||||
specName = "combat";
|
||||
else if (tab == 2)
|
||||
else if (tab == ROGUE_TAB_SUBTLETY)
|
||||
specName = "subtle";
|
||||
break;
|
||||
case CLASS_HUNTER:
|
||||
if (tab == 0)
|
||||
if (tab == HUNTER_TAB_BEAST_MASTERY)
|
||||
specName = "beast";
|
||||
else if (tab == 1)
|
||||
else if (tab == HUNTER_TAB_MARKSMANSHIP)
|
||||
specName = "marks";
|
||||
else if (tab == 2)
|
||||
else if (tab == HUNTER_TAB_SURVIVAL)
|
||||
specName = "surv";
|
||||
break;
|
||||
case CLASS_DEATH_KNIGHT:
|
||||
if (tab == 0)
|
||||
if (tab == DEATH_KNIGHT_TAB_BLOOD)
|
||||
specName = "blooddps";
|
||||
else if (tab == 1)
|
||||
else if (tab == DEATH_KNIGHT_TAB_FROST)
|
||||
specName = "frostdps";
|
||||
else if (tab == 2)
|
||||
else if (tab == DEATH_KNIGHT_TAB_UNHOLY)
|
||||
specName = "unholydps";
|
||||
break;
|
||||
case CLASS_MAGE:
|
||||
if (tab == 0)
|
||||
if (tab == MAGE_TAB_ARCANE)
|
||||
specName = "arcane";
|
||||
else if (tab == 1)
|
||||
else if (tab == MAGE_TAB_FIRE)
|
||||
specName = "fire";
|
||||
else if (tab == 2)
|
||||
else if (tab == MAGE_TAB_FROST)
|
||||
specName = "frost";
|
||||
break;
|
||||
case CLASS_WARLOCK:
|
||||
if (tab == 0)
|
||||
if (tab == WARLOCK_TAB_AFFLICTION)
|
||||
specName = "afflic";
|
||||
else if (tab == 1)
|
||||
else if (tab == WARLOCK_TAB_DEMONOLOGY)
|
||||
specName = "demo";
|
||||
else if (tab == 2)
|
||||
else if (tab == WARLOCK_TAB_DESTRUCTION)
|
||||
specName = "destro";
|
||||
break;
|
||||
default:
|
||||
@@ -280,147 +279,124 @@ 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 == 2)
|
||||
{
|
||||
if (tab == PRIEST_TAB_SHADOW)
|
||||
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 == 0) // Arcane
|
||||
if (tab == MAGE_TAB_ARCANE)
|
||||
engine->addStrategiesNoInit("arcane", nullptr);
|
||||
else if (tab == 1) // Fire
|
||||
else if (tab == MAGE_TAB_FIRE)
|
||||
{
|
||||
if (player->HasSpell(44614) /*Frostfire Bolt*/ && player->HasAura(15047) /*Ice Shards*/)
|
||||
{
|
||||
engine->addStrategiesNoInit("frostfire", nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
engine->addStrategiesNoInit("fire", nullptr);
|
||||
}
|
||||
}
|
||||
else // Frost
|
||||
else
|
||||
engine->addStrategiesNoInit("frost", nullptr);
|
||||
|
||||
engine->addStrategiesNoInit("dps", "dps assist", "cure", "aoe", nullptr);
|
||||
break;
|
||||
case CLASS_WARRIOR:
|
||||
if (tab == 2)
|
||||
if (tab == WARRIOR_TAB_PROTECTION)
|
||||
engine->addStrategiesNoInit("tank", "tank assist", "aoe", nullptr);
|
||||
else if (tab == 0 || !player->HasSpell(1680)) // Whirlwind
|
||||
engine->addStrategiesNoInit("arms", "aoe", "dps assist", /*"behind",*/ nullptr);
|
||||
else if (tab == WARRIOR_TAB_ARMS || !player->HasSpell(1680)) // Whirlwind
|
||||
engine->addStrategiesNoInit("arms", "aoe", "dps assist", nullptr);
|
||||
else
|
||||
engine->addStrategiesNoInit("fury", "aoe", "dps assist", /*"behind",*/ nullptr);
|
||||
engine->addStrategiesNoInit("fury", "aoe", "dps assist", nullptr);
|
||||
break;
|
||||
case CLASS_SHAMAN:
|
||||
if (tab == 0) // Elemental
|
||||
if (tab == SHAMAN_TAB_ELEMENTAL)
|
||||
engine->addStrategiesNoInit("ele", "stoneskin", "wrath", "mana spring", "wrath of air", nullptr);
|
||||
else if (tab == 2) // Restoration
|
||||
else if (tab == SHAMAN_TAB_RESTORATION)
|
||||
engine->addStrategiesNoInit("resto", "stoneskin", "flametongue", "mana spring", "wrath of air", nullptr);
|
||||
else // Enhancement
|
||||
else
|
||||
engine->addStrategiesNoInit("enh", "strength of earth", "magma", "healing stream", "windfury", nullptr);
|
||||
|
||||
engine->addStrategiesNoInit("dps assist", "cure", "aoe", nullptr);
|
||||
break;
|
||||
case CLASS_PALADIN:
|
||||
if (tab == 1)
|
||||
if (tab == PALADIN_TAB_PROTECTION)
|
||||
engine->addStrategiesNoInit("tank", "tank assist", "bthreat", "barmor", "cure", nullptr);
|
||||
else if (tab == 0)
|
||||
else if (tab == PALADIN_TAB_HOLY)
|
||||
engine->addStrategiesNoInit("heal", "dps assist", "cure", "bcast", nullptr);
|
||||
else
|
||||
engine->addStrategiesNoInit("dps", "dps assist", "cure", "baoe", nullptr);
|
||||
|
||||
break;
|
||||
case CLASS_DRUID:
|
||||
if (tab == 0)
|
||||
if (tab == DRUID_TAB_BALANCE)
|
||||
{
|
||||
engine->addStrategiesNoInit("caster", "cure", "caster aoe", "dps assist", nullptr);
|
||||
engine->addStrategy("caster debuff", false);
|
||||
}
|
||||
else if (tab == 2)
|
||||
else if (tab == DRUID_TAB_RESTORATION)
|
||||
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 == 0) // Beast Mastery
|
||||
if (tab == HUNTER_TAB_BEAST_MASTERY)
|
||||
engine->addStrategiesNoInit("bm", nullptr);
|
||||
else if (tab == 1) // Marksmanship
|
||||
else if (tab == HUNTER_TAB_MARKSMANSHIP)
|
||||
engine->addStrategiesNoInit("mm", nullptr);
|
||||
else if (tab == 2) // Survival
|
||||
else
|
||||
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 == 0) // Affliction
|
||||
if (tab == WARLOCK_TAB_AFFLICTION)
|
||||
engine->addStrategiesNoInit("affli", "curse of agony", nullptr);
|
||||
else if (tab == 1) // Demonology
|
||||
else if (tab == WARLOCK_TAB_DEMONOLOGY)
|
||||
engine->addStrategiesNoInit("demo", "curse of agony", "meta melee", nullptr);
|
||||
else if (tab == 2) // Destruction
|
||||
else
|
||||
engine->addStrategiesNoInit("destro", "curse of elements", nullptr);
|
||||
|
||||
engine->addStrategiesNoInit("cc", "dps assist", "aoe", nullptr);
|
||||
break;
|
||||
|
||||
case CLASS_DEATH_KNIGHT:
|
||||
if (tab == 0)
|
||||
if (tab == DEATH_KNIGHT_TAB_BLOOD)
|
||||
engine->addStrategiesNoInit("blood", "tank assist", nullptr);
|
||||
else if (tab == 1)
|
||||
else if (tab == DEATH_KNIGHT_TAB_FROST)
|
||||
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)
|
||||
@@ -428,6 +404,7 @@ 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())
|
||||
@@ -436,15 +413,13 @@ 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:
|
||||
@@ -459,17 +434,13 @@ 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:
|
||||
@@ -478,13 +449,9 @@ 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())
|
||||
@@ -511,23 +478,15 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -549,19 +508,15 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
|
||||
nonCombatEngine->addStrategiesNoInit("dps assist", "cure", nullptr);
|
||||
break;
|
||||
case CLASS_PALADIN:
|
||||
if (tab == 1)
|
||||
if (tab == PALADIN_TAB_PROTECTION)
|
||||
{
|
||||
nonCombatEngine->addStrategiesNoInit("bthreat", "tank assist", "barmor", nullptr);
|
||||
if (player->GetLevel() >= 20)
|
||||
{
|
||||
nonCombatEngine->addStrategy("bhealth", false);
|
||||
}
|
||||
else
|
||||
{
|
||||
nonCombatEngine->addStrategy("bdps", false);
|
||||
}
|
||||
}
|
||||
else if (tab == 0)
|
||||
else if (tab == PALADIN_TAB_HOLY)
|
||||
nonCombatEngine->addStrategiesNoInit("dps assist", "bmana", "bcast", nullptr);
|
||||
else
|
||||
nonCombatEngine->addStrategiesNoInit("dps assist", "bdps", "baoe", nullptr);
|
||||
@@ -572,7 +527,7 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
|
||||
nonCombatEngine->addStrategiesNoInit("bdps", "dps assist", "pet", nullptr);
|
||||
break;
|
||||
case CLASS_SHAMAN:
|
||||
if (tab == 0 || tab == 2)
|
||||
if (tab == SHAMAN_TAB_ELEMENTAL || tab == SHAMAN_TAB_RESTORATION)
|
||||
nonCombatEngine->addStrategy("bmana", false);
|
||||
else
|
||||
nonCombatEngine->addStrategy("bdps", false);
|
||||
@@ -588,43 +543,34 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
|
||||
nonCombatEngine->addStrategiesNoInit("dps assist", "cure", nullptr);
|
||||
break;
|
||||
case CLASS_DRUID:
|
||||
if (tab == 1)
|
||||
if (tab == DRUID_TAB_FERAL)
|
||||
{
|
||||
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 == 2)
|
||||
if (tab == WARRIOR_TAB_PROTECTION)
|
||||
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 == 0)
|
||||
if (tab == DEATH_KNIGHT_TAB_BLOOD)
|
||||
nonCombatEngine->addStrategy("tank assist", false);
|
||||
else
|
||||
nonCombatEngine->addStrategy("dps assist", false);
|
||||
@@ -641,9 +587,7 @@ 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())
|
||||
{
|
||||
@@ -669,18 +613,14 @@ 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);
|
||||
@@ -729,11 +669,8 @@ 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())
|
||||
{
|
||||
@@ -790,9 +727,7 @@ 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)
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "PlayerbotDbStore.h"
|
||||
#include "PlayerbotMgr.h"
|
||||
#include "PlayerbotGuildMgr.h"
|
||||
#include "Playerbots.h"
|
||||
#include "PointMovementGenerator.h"
|
||||
#include "PositionValue.h"
|
||||
@@ -436,7 +437,7 @@ void PlayerbotAI::UpdateAIGroupMaster()
|
||||
|
||||
void PlayerbotAI::UpdateAIInternal([[maybe_unused]] uint32 elapsed, bool minimal)
|
||||
{
|
||||
if (bot->IsBeingTeleported() || !bot->IsInWorld())
|
||||
if (!bot || bot->IsBeingTeleported() || !bot->IsInWorld())
|
||||
return;
|
||||
|
||||
std::string const mapString = WorldPosition(bot).isOverworld() ? std::to_string(bot->GetMapId()) : "I";
|
||||
@@ -515,23 +516,37 @@ 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(0) < checkTime)
|
||||
if (checkTime && time(nullptr) < checkTime)
|
||||
{
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string& command = it->GetCommand();
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -539,6 +554,9 @@ 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,
|
||||
@@ -710,65 +728,82 @@ 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;
|
||||
|
||||
// Clearing motion generators and stopping movement which prevents
|
||||
// conflicts between teleport and any active motion (walk, run, swim, flight, etc.)
|
||||
bot->GetMotionMaster()->Clear(true);
|
||||
bot->StopMoving();
|
||||
|
||||
// Near teleport (within map/instance)
|
||||
if (bot->IsBeingTeleportedNear())
|
||||
{
|
||||
// Previous versions manually added the bot to the map if it was not in the world.
|
||||
// not needed: HandleMoveTeleportAckOpcode() safely attaches the player to the map
|
||||
// and clears IsBeingTeleportedNear().
|
||||
|
||||
Player* plMover = bot->m_mover->ToPlayer();
|
||||
if (!plMover)
|
||||
return;
|
||||
|
||||
// Send the near teleport ACK packet
|
||||
WorldPacket p(MSG_MOVE_TELEPORT_ACK, 20);
|
||||
p << plMover->GetPackGUID();
|
||||
p << uint32(0);
|
||||
p << uint32(0);
|
||||
bot->GetSession()->HandleMoveTeleportAck(p);
|
||||
|
||||
// Simulate teleport latency and prevent AI from running too early (used cmangos delays)
|
||||
SetNextCheckDelay(urand(1000, 2000));
|
||||
}
|
||||
|
||||
// Far teleport (worldport / different map)
|
||||
/*
|
||||
* FAR TELEPORT (worldport / map change)
|
||||
* Player may NOT be in world or grid here.
|
||||
* Handle this FIRST.
|
||||
*/
|
||||
if (bot->IsBeingTeleportedFar())
|
||||
{
|
||||
// Handle far teleport ACK:
|
||||
// Moves the bot to the new map, clears IsBeingTeleportedFar(), updates session/map references
|
||||
bot->GetSession()->HandleMoveWorldportAck();
|
||||
|
||||
// Ensure bot now has a valid map. If this fails, there is a core/session bug?
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Instance strategies after teleport
|
||||
// apply instance-related strategies after map attach
|
||||
if (sPlayerbotAIConfig->applyInstanceStrategies)
|
||||
ApplyInstanceStrategies(bot->GetMapId(), true);
|
||||
|
||||
// healer DPS strategies if restrictions are enabled
|
||||
if (sPlayerbotAIConfig->restrictHealerDPS)
|
||||
EvaluateHealerDpsStrategy();
|
||||
|
||||
// Reset AI state to to before teleport conditions
|
||||
// reset AI state after teleport
|
||||
Reset(true);
|
||||
|
||||
// Slightly longer delay to simulate far teleport latency (used cmangos delays)
|
||||
// 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;
|
||||
}
|
||||
|
||||
SetNextCheckDelay(sPlayerbotAIConfig->globalCoolDown);
|
||||
/*
|
||||
* NEAR TELEPORT (same map / instance)
|
||||
* Player MUST be in world (and in grid).
|
||||
*/
|
||||
if (bot->IsBeingTeleportedNear())
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
// simulate near teleport latency
|
||||
SetNextCheckDelay(urand(1000, 2000));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerbotAI::Reset(bool full)
|
||||
@@ -930,7 +965,6 @@ 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;
|
||||
@@ -1286,7 +1320,12 @@ void PlayerbotAI::SpellInterrupted(uint32 spellid)
|
||||
Spell* spell = bot->GetCurrentSpell((CurrentSpellTypes)type);
|
||||
if (!spell)
|
||||
continue;
|
||||
if (spell->GetSpellInfo()->Id == spellid)
|
||||
|
||||
SpellInfo const* spellInfo = spell->GetSpellInfo();
|
||||
if (!spellInfo)
|
||||
continue;
|
||||
|
||||
if (spellInfo->Id == spellid)
|
||||
bot->InterruptSpell((CurrentSpellTypes)type);
|
||||
}
|
||||
// LastSpellCast& lastSpell = aiObjectContext->GetValue<LastSpellCast&>("last spell cast")->Get();
|
||||
@@ -1387,8 +1426,8 @@ void PlayerbotAI::DoNextAction(bool min)
|
||||
return;
|
||||
}
|
||||
|
||||
// Change engine if just ressed
|
||||
if (currentEngine == engines[BOT_STATE_DEAD] && isBotAlive)
|
||||
// Change engine if just ressed (no movement update when rooted)
|
||||
if (currentEngine == engines[BOT_STATE_DEAD] && isBotAlive && !bot->IsRooted())
|
||||
{
|
||||
bot->SendMovementFlagUpdate();
|
||||
|
||||
@@ -1504,9 +1543,6 @@ 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;
|
||||
@@ -1731,6 +1767,7 @@ bool PlayerbotAI::IsRanged(Player* player, bool bySpec)
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1824,10 +1861,9 @@ bool PlayerbotAI::IsRangedDpsAssistantOfIndex(Player* player, int index)
|
||||
|
||||
bool PlayerbotAI::HasAggro(Unit* unit)
|
||||
{
|
||||
if (!unit)
|
||||
{
|
||||
if (!IsValidUnit(unit))
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isMT = IsMainTank(bot);
|
||||
Unit* victim = unit->GetVictim();
|
||||
if (victim && (victim->GetGUID() == bot->GetGUID() || (!isMT && victim->ToPlayer() && IsTank(victim->ToPlayer()))))
|
||||
@@ -2035,7 +2071,7 @@ bool PlayerbotAI::IsTank(Player* player, bool bySpec)
|
||||
switch (player->getClass())
|
||||
{
|
||||
case CLASS_DEATH_KNIGHT:
|
||||
if (tab == DEATHKNIGHT_TAB_BLOOD)
|
||||
if (tab == DEATH_KNIGHT_TAB_BLOOD)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -2143,7 +2179,7 @@ bool PlayerbotAI::IsDps(Player* player, bool bySpec)
|
||||
}
|
||||
break;
|
||||
case CLASS_DEATH_KNIGHT:
|
||||
if (tab != DEATHKNIGHT_TAB_BLOOD)
|
||||
if (tab != DEATH_KNIGHT_TAB_BLOOD)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -2803,7 +2839,12 @@ bool PlayerbotAI::TellMaster(std::ostringstream& stream, PlayerbotSecurityLevel
|
||||
|
||||
bool PlayerbotAI::TellMaster(std::string const text, PlayerbotSecurityLevel securityLevel)
|
||||
{
|
||||
if (!master || !TellMasterNoFacing(text, securityLevel))
|
||||
if (!master)
|
||||
{
|
||||
if (sPlayerbotAIConfig->randomBotSayWithoutMaster)
|
||||
return TellMasterNoFacing(text, securityLevel);
|
||||
}
|
||||
if (!TellMasterNoFacing(text, securityLevel))
|
||||
return false;
|
||||
|
||||
if (!bot->isMoving() && !bot->IsInCombat() && bot->GetMapId() == master->GetMapId() &&
|
||||
@@ -2820,6 +2861,9 @@ 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;
|
||||
|
||||
@@ -2827,6 +2871,8 @@ 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)
|
||||
@@ -2842,7 +2888,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 (!unit)
|
||||
if (!IsValidUnit(unit))
|
||||
return false;
|
||||
|
||||
std::wstring wnamepart;
|
||||
@@ -2938,7 +2984,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 (!unit)
|
||||
if (!IsValidUnit(unit))
|
||||
return nullptr;
|
||||
|
||||
std::wstring wnamepart;
|
||||
@@ -2956,6 +3002,9 @@ 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)
|
||||
@@ -3036,6 +3085,9 @@ 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;
|
||||
@@ -3297,6 +3349,9 @@ 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)
|
||||
{
|
||||
@@ -3309,15 +3364,19 @@ 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;
|
||||
|
||||
Pet* pet = bot->GetPet();
|
||||
if (!IsValidUnit(target))
|
||||
return false;
|
||||
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||
if (!spellInfo)
|
||||
return false;
|
||||
|
||||
Pet* pet = bot->GetPet();
|
||||
if (pet && pet->HasSpell(spellId))
|
||||
{
|
||||
// List of spell IDs for which we do NOT want to toggle auto-cast or send message
|
||||
@@ -3720,6 +3779,9 @@ bool PlayerbotAI::CanCastVehicleSpell(uint32 spellId, Unit* target)
|
||||
if (!spellId)
|
||||
return false;
|
||||
|
||||
if (!IsValidUnit(target))
|
||||
return false;
|
||||
|
||||
Vehicle* vehicle = bot->GetVehicle();
|
||||
if (!vehicle)
|
||||
return false;
|
||||
@@ -3730,12 +3792,12 @@ bool PlayerbotAI::CanCastVehicleSpell(uint32 spellId, Unit* target)
|
||||
return false;
|
||||
|
||||
Unit* vehicleBase = vehicle->GetBase();
|
||||
|
||||
Unit* spellTarget = target;
|
||||
|
||||
if (!spellTarget)
|
||||
spellTarget = vehicleBase;
|
||||
|
||||
if (!spellTarget)
|
||||
if (!IsValidUnit(spellTarget))
|
||||
return false;
|
||||
|
||||
if (vehicleBase->HasSpellCooldown(spellId))
|
||||
@@ -3802,6 +3864,9 @@ bool PlayerbotAI::CastVehicleSpell(uint32 spellId, Unit* target)
|
||||
if (!spellId)
|
||||
return false;
|
||||
|
||||
if (!IsValidUnit(target))
|
||||
return false;
|
||||
|
||||
Vehicle* vehicle = bot->GetVehicle();
|
||||
if (!vehicle)
|
||||
return false;
|
||||
@@ -3812,12 +3877,12 @@ bool PlayerbotAI::CastVehicleSpell(uint32 spellId, Unit* target)
|
||||
return false;
|
||||
|
||||
Unit* vehicleBase = vehicle->GetBase();
|
||||
|
||||
Unit* spellTarget = target;
|
||||
|
||||
if (!spellTarget)
|
||||
spellTarget = vehicleBase;
|
||||
|
||||
if (!spellTarget)
|
||||
if (!IsValidUnit(spellTarget))
|
||||
return false;
|
||||
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||
@@ -3970,9 +4035,13 @@ 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->IsChanneled())
|
||||
|
||||
if (spellInfo && spellInfo->IsChanneled())
|
||||
{
|
||||
int32 duration = spellInfo->GetDuration();
|
||||
bot->ApplySpellMod(spellInfo->Id, SPELLMOD_DURATION, duration);
|
||||
@@ -4020,6 +4089,9 @@ 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;
|
||||
@@ -4048,17 +4120,25 @@ bool PlayerbotAI::IsInterruptableSpellCasting(Unit* target, std::string const sp
|
||||
|
||||
bool PlayerbotAI::HasAuraToDispel(Unit* target, uint32 dispelType)
|
||||
{
|
||||
if (!target->IsInWorld())
|
||||
{
|
||||
if (!IsValidUnit(target) || !target->IsAlive())
|
||||
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)
|
||||
{
|
||||
Aura* aura = itr->second->GetBase();
|
||||
if (!itr->second)
|
||||
continue;
|
||||
|
||||
if (aura->IsPassive())
|
||||
Aura* aura = itr->second->GetBase();
|
||||
if (!aura || aura->IsPassive() || aura->IsRemoved())
|
||||
continue;
|
||||
|
||||
if (sPlayerbotAIConfig->dispelAuraDuration && aura->GetDuration() &&
|
||||
@@ -4066,6 +4146,8 @@ bool PlayerbotAI::HasAuraToDispel(Unit* target, uint32 dispelType)
|
||||
continue;
|
||||
|
||||
SpellInfo const* spellInfo = aura->GetSpellInfo();
|
||||
if (!spellInfo)
|
||||
continue;
|
||||
|
||||
bool isPositiveSpell = spellInfo->IsPositive();
|
||||
if (isPositiveSpell && isFriend)
|
||||
@@ -4077,6 +4159,7 @@ bool PlayerbotAI::HasAuraToDispel(Unit* target, uint32 dispelType)
|
||||
if (canDispel(spellInfo, dispelType))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -5707,7 +5790,7 @@ void PlayerbotAI::ImbueItem(Item* item) { ImbueItem(item, TARGET_FLAG_NONE, Obje
|
||||
// item on unit
|
||||
void PlayerbotAI::ImbueItem(Item* item, Unit* target)
|
||||
{
|
||||
if (!target)
|
||||
if (!IsValidUnit(target))
|
||||
return;
|
||||
|
||||
ImbueItem(item, TARGET_FLAG_UNIT, target->GetGUID());
|
||||
@@ -5849,30 +5932,38 @@ int32 PlayerbotAI::GetNearGroupMemberCount(float dis)
|
||||
|
||||
bool PlayerbotAI::CanMove()
|
||||
{
|
||||
// do not allow if not vehicle driver
|
||||
if (IsInVehicle() && !IsInVehicle(true))
|
||||
// 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))
|
||||
return false;
|
||||
|
||||
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))
|
||||
// Death state (w/o spirit release) and Spirit of Redemption aura (priest)
|
||||
if ((bot->isDead() && !bot->HasPlayerFlag(PLAYER_FLAGS_GHOST)) || bot->HasSpiritOfRedemptionAura())
|
||||
return false;
|
||||
|
||||
return bot->GetMotionMaster()->GetCurrentMovementGeneratorType() != FLIGHT_MOTION_TYPE;
|
||||
}
|
||||
|
||||
bool PlayerbotAI::IsRealGuild(uint32 guildId)
|
||||
{
|
||||
Guild* guild = sGuildMgr->GetGuildById(guildId);
|
||||
if (!guild)
|
||||
{
|
||||
// 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 false;
|
||||
}
|
||||
uint32 leaderAccount = sCharacterCache->GetCharacterAccountIdByGuid(guild->GetLeaderGUID());
|
||||
if (!leaderAccount)
|
||||
if (bot->isFrozen() || bot->IsPolymorphed())
|
||||
return false;
|
||||
|
||||
return !(sPlayerbotAIConfig->IsInRandomAccountList(leaderAccount));
|
||||
// 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;
|
||||
}
|
||||
|
||||
bool PlayerbotAI::IsInRealGuild()
|
||||
@@ -5880,7 +5971,7 @@ bool PlayerbotAI::IsInRealGuild()
|
||||
if (!bot->GetGuildId())
|
||||
return false;
|
||||
|
||||
return IsRealGuild(bot->GetGuildId());
|
||||
return sPlayerbotGuildMgr->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_BEASTMASTERY,
|
||||
HUNTER_TAB_BEAST_MASTERY,
|
||||
HUNTER_TAB_MARKSMANSHIP,
|
||||
HUNTER_TAB_SURVIVAL,
|
||||
};
|
||||
@@ -295,11 +295,11 @@ enum PRIEST_TABS
|
||||
PRIEST_TAB_SHADOW,
|
||||
};
|
||||
|
||||
enum DEATHKNIGHT_TABS
|
||||
enum DEATH_KNIGHT_TABS
|
||||
{
|
||||
DEATHKNIGHT_TAB_BLOOD,
|
||||
DEATHKNIGHT_TAB_FROST,
|
||||
DEATHKNIGHT_TAB_UNHOLY,
|
||||
DEATH_KNIGHT_TAB_BLOOD,
|
||||
DEATH_KNIGHT_TAB_FROST,
|
||||
DEATH_KNIGHT_TAB_UNHOLY,
|
||||
};
|
||||
|
||||
enum DRUID_TABS
|
||||
@@ -579,7 +579,6 @@ 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);
|
||||
@@ -617,7 +616,15 @@ private:
|
||||
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;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "PlayerbotDungeonSuggestionMgr.h"
|
||||
#include "PlayerbotFactory.h"
|
||||
#include "Playerbots.h"
|
||||
#include "PlayerbotGuildMgr.h"
|
||||
#include "RandomItemMgr.h"
|
||||
#include "RandomPlayerbotFactory.h"
|
||||
#include "RandomPlayerbotMgr.h"
|
||||
@@ -222,6 +223,11 @@ 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);
|
||||
@@ -661,6 +667,7 @@ bool PlayerbotAIConfig::Initialize()
|
||||
sRandomPlayerbotMgr->Init();
|
||||
}
|
||||
|
||||
sPlayerbotGuildMgr->Init();
|
||||
sRandomItemMgr->Init();
|
||||
sRandomItemMgr->InitAfterAhBot();
|
||||
sPlayerbotTextMgr->LoadBotTexts();
|
||||
|
||||
@@ -145,6 +145,10 @@ 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;
|
||||
@@ -269,7 +273,6 @@ public:
|
||||
bool deleteRandomBotAccounts;
|
||||
uint32 randomBotGuildCount, randomBotGuildSizeMax;
|
||||
bool deleteRandomBotGuilds;
|
||||
std::vector<uint32> randomBotGuilds;
|
||||
std::vector<uint32> pvpProhibitedZoneIds;
|
||||
std::vector<uint32> pvpProhibitedAreaIds;
|
||||
bool fastReactInBG;
|
||||
|
||||
322
src/PlayerbotGuildMgr.cpp
Normal file
322
src/PlayerbotGuildMgr.cpp
Normal file
@@ -0,0 +1,322 @@
|
||||
#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();
|
||||
}
|
||||
52
src/PlayerbotGuildMgr.h
Normal file
52
src/PlayerbotGuildMgr.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#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
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "PlayerbotSecurity.h"
|
||||
#include "PlayerbotWorldThreadProcessor.h"
|
||||
#include "Playerbots.h"
|
||||
#include "PlayerbotGuildMgr.h"
|
||||
#include "RandomPlayerbotMgr.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "WorldSession.h"
|
||||
@@ -1193,7 +1194,7 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
|
||||
if (ObjectAccessor::FindConnectedPlayer(guid))
|
||||
continue;
|
||||
uint32 guildId = sCharacterCache->GetCharacterGuildIdByGuid(guid);
|
||||
if (guildId && PlayerbotAI::IsRealGuild(guildId))
|
||||
if (guildId && sPlayerbotGuildMgr->IsRealGuild(guildId))
|
||||
continue;
|
||||
AddPlayerBot(guid, master->GetSession()->GetAccountId());
|
||||
messages.push_back("Add class " + std::string(charname));
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "Metric.h"
|
||||
#include "PlayerScript.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "PlayerbotGuildMgr.h"
|
||||
#include "PlayerbotSpellCache.h"
|
||||
#include "PlayerbotWorldThreadProcessor.h"
|
||||
#include "RandomPlayerbotMgr.h"
|
||||
@@ -264,46 +265,33 @@ public:
|
||||
|
||||
void OnPlayerGiveXP(Player* player, uint32& amount, Unit* /*victim*/, uint8 /*xpSource*/) override
|
||||
{
|
||||
if (!player || !player->IsInWorld())
|
||||
// early return
|
||||
if (sPlayerbotAIConfig->randomBotXPRate == 1.0 || !player)
|
||||
return;
|
||||
|
||||
if (WorldSession* session = player->GetSession(); !session || !session->IsBot())
|
||||
// no XP multiplier, when player is no bot.
|
||||
if (!player->GetSession()->IsBot() || !sRandomPlayerbotMgr->IsRandomBot(player))
|
||||
return;
|
||||
|
||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(player);
|
||||
if (!botAI)
|
||||
return;
|
||||
|
||||
// No XP gain if master is a real player with XP gain disabled
|
||||
if (const Player* master = botAI->GetMaster())
|
||||
{
|
||||
if (WorldSession* masterSession = master->GetSession();
|
||||
masterSession && !masterSession->IsBot() &&
|
||||
master->HasPlayerFlag(PLAYER_FLAGS_NO_XP_GAIN))
|
||||
{
|
||||
amount = 0; // disable XP multiplier
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// From here on we only care about random bots
|
||||
if (sPlayerbotAIConfig->randomBotXPRate == 1.0f || !sRandomPlayerbotMgr->IsRandomBot(player))
|
||||
return;
|
||||
|
||||
// No XP multiplier if bot is in a group with at least one real player
|
||||
// no XP multiplier, when bot is in a group with a real player.
|
||||
if (Group* group = player->GetGroup())
|
||||
{
|
||||
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
||||
{
|
||||
if (Player* member = gref->GetSource())
|
||||
Player* member = gref->GetSource();
|
||||
if (!member)
|
||||
{
|
||||
if (WorldSession* session = member->GetSession();session && !session->IsBot())
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!member->GetSession()->IsBot())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise apply XP multiplier
|
||||
// otherwise apply bot XP multiplier.
|
||||
amount = static_cast<uint32>(std::round(static_cast<float>(amount) * sPlayerbotAIConfig->randomBotXPRate));
|
||||
}
|
||||
};
|
||||
@@ -515,6 +503,8 @@ public:
|
||||
void OnBattlegroundEnd(Battleground* bg, TeamId /*winnerTeam*/) override { bgStrategies.erase(bg->GetInstanceID()); }
|
||||
};
|
||||
|
||||
void AddPlayerbotsSecureLoginScripts();
|
||||
|
||||
void AddPlayerbotsScripts()
|
||||
{
|
||||
new PlayerbotsDatabaseScript();
|
||||
@@ -524,6 +514,7 @@ void AddPlayerbotsScripts()
|
||||
new PlayerbotsWorldScript();
|
||||
new PlayerbotsScript();
|
||||
new PlayerBotsBGScript();
|
||||
|
||||
AddPlayerbotsSecureLoginScripts();
|
||||
AddSC_playerbots_commandscript();
|
||||
PlayerBotsGuildValidationScript();
|
||||
}
|
||||
|
||||
82
src/PlayerbotsSecureLogin.cpp
Normal file
82
src/PlayerbotsSecureLogin.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
#include "ScriptMgr.h"
|
||||
#include "Opcodes.h"
|
||||
#include "Player.h"
|
||||
#include "ObjectAccessor.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
static Player* FindOnlineAltbotByGuid(ObjectGuid guid)
|
||||
{
|
||||
if (!guid)
|
||||
return nullptr;
|
||||
|
||||
Player* p = ObjectAccessor::FindPlayer(guid);
|
||||
if (!p)
|
||||
return nullptr;
|
||||
|
||||
PlayerbotAI* ai = GET_PLAYERBOT_AI(p);
|
||||
if (!ai || ai->IsRealPlayer())
|
||||
return nullptr;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static void ForceLogoutViaPlayerbotHolder(Player* target)
|
||||
{
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
PlayerbotAI* ai = GET_PLAYERBOT_AI(target);
|
||||
if (!ai)
|
||||
return;
|
||||
|
||||
if (Player* master = ai->GetMaster())
|
||||
{
|
||||
if (PlayerbotMgr* mgr = GET_PLAYERBOT_MGR(master))
|
||||
{
|
||||
mgr->LogoutPlayerBot(target->GetGUID());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (sRandomPlayerbotMgr)
|
||||
{
|
||||
sRandomPlayerbotMgr->LogoutPlayerBot(target->GetGUID());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PlayerbotsSecureLoginServerScript : public ServerScript
|
||||
{
|
||||
public:
|
||||
PlayerbotsSecureLoginServerScript()
|
||||
: ServerScript("PlayerbotsSecureLoginServerScript", { SERVERHOOK_CAN_PACKET_RECEIVE }) {}
|
||||
|
||||
bool CanPacketReceive(WorldSession* /*session*/, WorldPacket& packet) override
|
||||
{
|
||||
if (packet.GetOpcode() != CMSG_PLAYER_LOGIN)
|
||||
return true;
|
||||
|
||||
auto const oldPos = packet.rpos();
|
||||
ObjectGuid loginGuid;
|
||||
packet >> loginGuid;
|
||||
packet.rpos(oldPos);
|
||||
|
||||
if (!loginGuid)
|
||||
return true;
|
||||
|
||||
Player* existingAltbot = FindOnlineAltbotByGuid(loginGuid);
|
||||
if (existingAltbot)
|
||||
ForceLogoutViaPlayerbotHolder(existingAltbot);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void AddPlayerbotsSecureLoginScripts()
|
||||
{
|
||||
new PlayerbotsSecureLoginServerScript();
|
||||
}
|
||||
@@ -2834,22 +2834,20 @@ inline bool ContainsInternal(ItemTemplate const* proto, uint32 skillId)
|
||||
CreatureTemplateContainer const* creatures = sObjectMgr->GetCreatureTemplates();
|
||||
for (CreatureTemplateContainer::const_iterator itr = creatures->begin(); itr != creatures->end(); ++itr)
|
||||
{
|
||||
if (itr->second.trainer_type != TRAINER_TYPE_TRADESKILLS)
|
||||
Trainer::Trainer* trainer = sObjectMgr->GetTrainer(itr->first);
|
||||
|
||||
if (!trainer)
|
||||
continue;
|
||||
|
||||
uint32 trainerId = itr->second.Entry;
|
||||
TrainerSpellData const* trainer_spells = sObjectMgr->GetNpcTrainerSpells(trainerId);
|
||||
if (!trainer_spells)
|
||||
if (trainer->GetTrainerType() != Trainer::Type::Tradeskill)
|
||||
continue;
|
||||
|
||||
for (TrainerSpellMap::const_iterator iter = trainer_spells->spellList.begin();
|
||||
iter != trainer_spells->spellList.end(); ++iter)
|
||||
for (auto& spell : trainer->GetSpells())
|
||||
{
|
||||
TrainerSpell const* tSpell = &iter->second;
|
||||
if (!tSpell || tSpell->reqSkill != skillId)
|
||||
if (spell.ReqSkillLine != skillId)
|
||||
continue;
|
||||
|
||||
if (IsCraftedBy(proto, tSpell->spell))
|
||||
if (IsCraftedBy(proto, spell.SpellId))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "GuildMgr.h"
|
||||
#include "PlayerbotFactory.h"
|
||||
#include "Playerbots.h"
|
||||
#include "PlayerbotGuildMgr.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "SocialMgr.h"
|
||||
@@ -754,187 +755,6 @@ 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,7 +51,6 @@ 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();
|
||||
|
||||
@@ -1654,6 +1654,10 @@ 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;
|
||||
@@ -2593,14 +2597,17 @@ void RandomPlayerbotMgr::Refresh(Player* bot)
|
||||
|
||||
bool RandomPlayerbotMgr::IsRandomBot(Player* bot)
|
||||
{
|
||||
if (!bot)
|
||||
return false;
|
||||
if (bot && GET_PLAYERBOT_AI(bot))
|
||||
{
|
||||
if (GET_PLAYERBOT_AI(bot)->IsRealPlayer())
|
||||
return false;
|
||||
}
|
||||
if (bot)
|
||||
{
|
||||
return IsRandomBot(bot->GetGUID().GetCounter());
|
||||
}
|
||||
|
||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||
if (!botAI || botAI->IsRealPlayer())
|
||||
return false;
|
||||
|
||||
return IsRandomBot(bot->GetGUID().GetCounter());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RandomPlayerbotMgr::IsRandomBot(ObjectGuid::LowType bot)
|
||||
|
||||
@@ -41,13 +41,17 @@ bool ServerFacade::IsDistanceLessOrEqualThan(float dist1, float dist2) { return
|
||||
|
||||
void ServerFacade::SetFacingTo(Player* bot, WorldObject* wo, bool force)
|
||||
{
|
||||
if (!bot)
|
||||
return;
|
||||
|
||||
float angle = bot->GetAngle(wo);
|
||||
// if (!force && bot->isMoving())
|
||||
// bot->SetFacingTo(bot->GetAngle(wo));
|
||||
// else
|
||||
// {
|
||||
bot->SetOrientation(angle);
|
||||
bot->SendMovementFlagUpdate();
|
||||
if (!bot->IsRooted())
|
||||
bot->SendMovementFlagUpdate();
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
@@ -218,6 +218,11 @@ bool WorldPosition::isUnderWater()
|
||||
: false;
|
||||
};
|
||||
|
||||
bool WorldPosition::IsValid()
|
||||
{
|
||||
return !(GetMapId() == MAPID_INVALID && GetPositionX() == 0 && GetPositionY() == 0 && GetPositionZ() == 0);
|
||||
}
|
||||
|
||||
WorldPosition WorldPosition::relPoint(WorldPosition* center)
|
||||
{
|
||||
return WorldPosition(GetMapId(), GetPositionX() - center->GetPositionX(), GetPositionY() - center->GetPositionY(),
|
||||
@@ -3404,13 +3409,14 @@ void TravelMgr::LoadQuestTravelTable()
|
||||
{
|
||||
Strategy* strat = con->GetStrategy(stratName);
|
||||
|
||||
if (strat->getDefaultActions())
|
||||
for (uint32 i = 0; i < NextAction::size(strat->getDefaultActions()); i++)
|
||||
{
|
||||
NextAction* nextAction = strat->getDefaultActions()[i];
|
||||
const std::vector<NextAction> defaultActions = strat->getDefaultActions();
|
||||
|
||||
if (defaultActions.size() > 0)
|
||||
{
|
||||
for (NextAction nextAction : defaultActions)
|
||||
{
|
||||
std::ostringstream aout;
|
||||
aout << nextAction->getRelevance() << "," << nextAction->getName()
|
||||
aout << nextAction.getRelevance() << "," << nextAction.getName()
|
||||
<< ",,S:" << stratName;
|
||||
|
||||
if (actions.find(aout.str().c_str()) != actions.end())
|
||||
@@ -3422,27 +3428,24 @@ void TravelMgr::LoadQuestTravelTable()
|
||||
|
||||
actions.insert_or_assign(aout.str().c_str(), classSpecLevel);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<TriggerNode*> triggers;
|
||||
strat->InitTriggers(triggers);
|
||||
for (auto& triggerNode : triggers)
|
||||
{
|
||||
// out << " TN:" << triggerNode->getName();
|
||||
|
||||
for (TriggerNode*& triggerNode : triggers)
|
||||
{
|
||||
if (Trigger* trigger = con->GetTrigger(triggerNode->getName()))
|
||||
{
|
||||
triggerNode->setTrigger(trigger);
|
||||
|
||||
NextAction** nextActions = triggerNode->getHandlers();
|
||||
std::vector<NextAction> nextActions = triggerNode->getHandlers();
|
||||
|
||||
for (uint32 i = 0; i < NextAction::size(nextActions); i++)
|
||||
// for (uint32_t i = 0; i < nextActions.size(); ++i)
|
||||
for (NextAction nextAction : nextActions)
|
||||
{
|
||||
NextAction* nextAction = nextActions[i];
|
||||
// out << " A:" << nextAction->getName() << "(" <<
|
||||
// nextAction->getRelevance() << ")";
|
||||
|
||||
std::ostringstream aout;
|
||||
aout << nextAction->getRelevance() << "," << nextAction->getName()
|
||||
aout << nextAction.getRelevance() << "," << nextAction.getName()
|
||||
<< "," << triggerNode->getName() << "," << stratName;
|
||||
|
||||
if (actions.find(aout.str().c_str()) != actions.end())
|
||||
|
||||
@@ -141,6 +141,7 @@ public:
|
||||
bool isOverworld();
|
||||
bool isInWater();
|
||||
bool isUnderWater();
|
||||
bool IsValid();
|
||||
|
||||
WorldPosition relPoint(WorldPosition* center);
|
||||
WorldPosition offset(WorldPosition* center);
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "PlayerbotAI.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "PlayerbotDbStore.h"
|
||||
#include "PlayerbotGuildMgr.h"
|
||||
#include "Playerbots.h"
|
||||
#include "QuestDef.h"
|
||||
#include "RandomItemMgr.h"
|
||||
@@ -121,7 +122,11 @@ void PlayerbotFactory::Init()
|
||||
uint32 maxStoreSize = sSpellMgr->GetSpellInfoStoreSize();
|
||||
for (uint32 id = 1; id < maxStoreSize; ++id)
|
||||
{
|
||||
if (id == 47181 || id == 50358 || id == 47242 || id == 52639 || id == 47147 || id == 7218) // Test Enchant
|
||||
if (id == 7218 || id == 19927 || id == 44119 || id == 47147 || id == 47181 ||
|
||||
id == 47242 || id == 50358 || id == 52639) // Test Enchants
|
||||
continue;
|
||||
|
||||
if (id == 35791 || id == 39405) // Grandfathered TBC Enchants
|
||||
continue;
|
||||
|
||||
if (id == 15463 || id == 15490) // Legendary Arcane Amalgamation
|
||||
@@ -1102,8 +1107,6 @@ void PlayerbotFactory::ResetQuests()
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerbotFactory::InitSpells() { InitAvailableSpells(); }
|
||||
|
||||
void PlayerbotFactory::InitTalentsTree(bool increment /*false*/, bool use_template /*true*/, bool reset /*false*/)
|
||||
{
|
||||
uint32 specTab;
|
||||
@@ -2525,66 +2528,35 @@ void PlayerbotFactory::InitAvailableSpells()
|
||||
for (CreatureTemplateContainer::const_iterator i = creatureTemplateContainer->begin();
|
||||
i != creatureTemplateContainer->end(); ++i)
|
||||
{
|
||||
CreatureTemplate const& co = i->second;
|
||||
if (co.trainer_type != TRAINER_TYPE_TRADESKILLS && co.trainer_type != TRAINER_TYPE_CLASS)
|
||||
Trainer::Trainer* trainer = sObjectMgr->GetTrainer(i->first);
|
||||
|
||||
if (!trainer)
|
||||
continue;
|
||||
|
||||
if (co.trainer_type == TRAINER_TYPE_CLASS && co.trainer_class != bot->getClass())
|
||||
if (trainer->GetTrainerType() != Trainer::Type::Tradeskill &&
|
||||
trainer->GetTrainerType() != Trainer::Type::Class)
|
||||
continue;
|
||||
|
||||
uint32 trainerId = co.Entry;
|
||||
trainerIdCache[bot->getClass()].push_back(trainerId);
|
||||
if (trainer->GetTrainerType() == Trainer::Type::Class &&
|
||||
!trainer->IsTrainerValidForPlayer(bot))
|
||||
continue;
|
||||
|
||||
trainerIdCache[bot->getClass()].push_back(i->first);
|
||||
}
|
||||
}
|
||||
for (uint32 trainerId : trainerIdCache[bot->getClass()])
|
||||
{
|
||||
TrainerSpellData const* trainer_spells = sObjectMgr->GetNpcTrainerSpells(trainerId);
|
||||
if (!trainer_spells)
|
||||
trainer_spells = sObjectMgr->GetNpcTrainerSpells(trainerId);
|
||||
Trainer::Trainer* trainer = sObjectMgr->GetTrainer(trainerId);
|
||||
|
||||
if (!trainer_spells)
|
||||
continue;
|
||||
|
||||
for (TrainerSpellMap::const_iterator itr = trainer_spells->spellList.begin();
|
||||
itr != trainer_spells->spellList.end(); ++itr)
|
||||
for (auto& spell : trainer->GetSpells())
|
||||
{
|
||||
TrainerSpell const* tSpell = &itr->second;
|
||||
|
||||
if (!tSpell)
|
||||
if (!trainer->CanTeachSpell(bot, trainer->GetSpell(spell.SpellId)))
|
||||
continue;
|
||||
|
||||
if (tSpell->learnedSpell[0] && !bot->IsSpellFitByClassAndRace(tSpell->learnedSpell[0]))
|
||||
continue;
|
||||
|
||||
TrainerSpellState state = bot->GetTrainerSpellState(tSpell);
|
||||
if (state != TRAINER_SPELL_GREEN)
|
||||
continue;
|
||||
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(tSpell->spell);
|
||||
bool learn = true;
|
||||
for (uint8 j = 0; j < 3; ++j)
|
||||
{
|
||||
if (!tSpell->learnedSpell[j] && !bot->IsSpellFitByClassAndRace(tSpell->learnedSpell[j]))
|
||||
continue;
|
||||
|
||||
if (spellInfo->Effects[j].Effect == SPELL_EFFECT_PROFICIENCY ||
|
||||
(spellInfo->Effects[j].Effect == SPELL_EFFECT_SKILL_STEP &&
|
||||
spellInfo->Effects[j].MiscValue != SKILL_RIDING) ||
|
||||
spellInfo->Effects[j].Effect == SPELL_EFFECT_DUAL_WIELD)
|
||||
{
|
||||
learn = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!learn)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tSpell->IsCastable())
|
||||
bot->CastSpell(bot, tSpell->spell, true);
|
||||
if (spell.IsCastable())
|
||||
bot->CastSpell(bot, spell.SpellId, true);
|
||||
else
|
||||
bot->learnSpell(tSpell->learnedSpell[0], false);
|
||||
bot->learnSpell(spell.SpellId, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3965,45 +3937,33 @@ void PlayerbotFactory::InitInventoryEquip()
|
||||
void PlayerbotFactory::InitGuild()
|
||||
{
|
||||
if (bot->GetGuildId())
|
||||
return;
|
||||
|
||||
// bot->SaveToDB(false, false);
|
||||
|
||||
// add guild tabard
|
||||
if (bot->GetGuildId() && !bot->HasItemCount(5976, 1))
|
||||
StoreItem(5976, 1);
|
||||
|
||||
if (sPlayerbotAIConfig->randomBotGuilds.empty())
|
||||
RandomPlayerbotFactory::CreateRandomGuilds();
|
||||
|
||||
std::vector<uint32> guilds;
|
||||
for (std::vector<uint32>::iterator i = sPlayerbotAIConfig->randomBotGuilds.begin();
|
||||
i != sPlayerbotAIConfig->randomBotGuilds.end(); ++i)
|
||||
guilds.push_back(*i);
|
||||
|
||||
if (guilds.empty())
|
||||
{
|
||||
LOG_ERROR("playerbots", "No random guilds available");
|
||||
if (!bot->HasItemCount(5976, 1) && bot->GetLevel() > 9)
|
||||
StoreItem(5976, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
int index = urand(0, guilds.size() - 1);
|
||||
uint32 guildId = guilds[index];
|
||||
Guild* guild = sGuildMgr->GetGuildById(guildId);
|
||||
std::string guildName = sPlayerbotGuildMgr->AssignToGuild(bot);
|
||||
if (guildName.empty())
|
||||
return;
|
||||
|
||||
Guild* guild = sGuildMgr->GetGuildByName(guildName);
|
||||
if (!guild)
|
||||
{
|
||||
LOG_ERROR("playerbots", "Invalid guild {}", guildId);
|
||||
if (!sPlayerbotGuildMgr->CreateGuild(bot, guildName))
|
||||
LOG_ERROR("playerbots","Failed to create guild {} for bot {}", guildName, bot->GetName());
|
||||
return;
|
||||
}
|
||||
|
||||
if (guild->GetMemberSize() < urand(10, sPlayerbotAIConfig->randomBotGuildSizeMax))
|
||||
guild->AddMember(bot->GetGUID(), urand(GR_OFFICER, GR_INITIATE));
|
||||
|
||||
else
|
||||
{
|
||||
if (guild->AddMember(bot->GetGUID(),urand(GR_OFFICER, GR_INITIATE)))
|
||||
sPlayerbotGuildMgr->OnGuildUpdate(guild);
|
||||
else
|
||||
LOG_ERROR("playerbots","Bot {} failed to join guild {}.", bot->GetName(), guildName);
|
||||
}
|
||||
// add guild tabard
|
||||
if (bot->GetGuildId() && bot->GetLevel() > 9 && urand(0, 4) && !bot->HasItemCount(5976, 1))
|
||||
StoreItem(5976, 1);
|
||||
|
||||
// bot->SaveToDB(false, false);
|
||||
}
|
||||
|
||||
void PlayerbotFactory::InitImmersive()
|
||||
|
||||
@@ -95,7 +95,6 @@ private:
|
||||
void InitTradeSkills();
|
||||
void UpdateTradeSkills();
|
||||
void SetRandomSkill(uint16 id);
|
||||
void InitSpells();
|
||||
void ClearSpells();
|
||||
void ClearSkills();
|
||||
void InitTalents(uint32 specNo);
|
||||
|
||||
@@ -37,7 +37,7 @@ StatsWeightCalculator::StatsWeightCalculator(Player* player) : player_(player)
|
||||
tab = AiFactory::GetPlayerSpecTab(player);
|
||||
collector_ = std::make_unique<StatsCollector>(type_, cls);
|
||||
|
||||
if (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_UNHOLY)
|
||||
if (cls == CLASS_DEATH_KNIGHT && tab == DEATH_KNIGHT_TAB_UNHOLY)
|
||||
hitOverflowType_ = CollectorType::SPELL;
|
||||
else if (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT)
|
||||
hitOverflowType_ = CollectorType::SPELL;
|
||||
@@ -193,7 +193,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
|
||||
stats_weights_[STATS_TYPE_MELEE_DPS] += 0.01f;
|
||||
stats_weights_[STATS_TYPE_RANGED_DPS] += 0.01f;
|
||||
|
||||
if (cls == CLASS_HUNTER && (tab == HUNTER_TAB_BEASTMASTERY || tab == HUNTER_TAB_SURVIVAL))
|
||||
if (cls == CLASS_HUNTER && (tab == HUNTER_TAB_BEAST_MASTERY || tab == HUNTER_TAB_SURVIVAL))
|
||||
{
|
||||
stats_weights_[STATS_TYPE_AGILITY] += 2.5f;
|
||||
stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f;
|
||||
@@ -249,7 +249,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
|
||||
stats_weights_[STATS_TYPE_EXPERTISE] += 2.1f;
|
||||
stats_weights_[STATS_TYPE_MELEE_DPS] += 5.0f;
|
||||
}
|
||||
else if (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY) // fury
|
||||
else if (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY)
|
||||
{
|
||||
stats_weights_[STATS_TYPE_AGILITY] += 1.8f;
|
||||
stats_weights_[STATS_TYPE_STRENGTH] += 2.6f;
|
||||
@@ -261,7 +261,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
|
||||
stats_weights_[STATS_TYPE_EXPERTISE] += 2.5f;
|
||||
stats_weights_[STATS_TYPE_MELEE_DPS] += 7.0f;
|
||||
}
|
||||
else if (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_ARMS) // arm
|
||||
else if (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_ARMS)
|
||||
{
|
||||
stats_weights_[STATS_TYPE_AGILITY] += 1.6f;
|
||||
stats_weights_[STATS_TYPE_STRENGTH] += 2.3f;
|
||||
@@ -273,7 +273,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
|
||||
stats_weights_[STATS_TYPE_EXPERTISE] += 1.4f;
|
||||
stats_weights_[STATS_TYPE_MELEE_DPS] += 7.0f;
|
||||
}
|
||||
else if (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_FROST) // frost dk
|
||||
else if (cls == CLASS_DEATH_KNIGHT && tab == DEATH_KNIGHT_TAB_FROST)
|
||||
{
|
||||
stats_weights_[STATS_TYPE_AGILITY] += 1.7f;
|
||||
stats_weights_[STATS_TYPE_STRENGTH] += 2.8f;
|
||||
@@ -285,7 +285,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
|
||||
stats_weights_[STATS_TYPE_EXPERTISE] += 2.5f;
|
||||
stats_weights_[STATS_TYPE_MELEE_DPS] += 7.0f;
|
||||
}
|
||||
else if (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_UNHOLY)
|
||||
else if (cls == CLASS_DEATH_KNIGHT && tab == DEATH_KNIGHT_TAB_UNHOLY)
|
||||
{
|
||||
stats_weights_[STATS_TYPE_AGILITY] += 0.9f;
|
||||
stats_weights_[STATS_TYPE_STRENGTH] += 2.5f;
|
||||
@@ -297,7 +297,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
|
||||
stats_weights_[STATS_TYPE_EXPERTISE] += 1.5f;
|
||||
stats_weights_[STATS_TYPE_MELEE_DPS] += 5.0f;
|
||||
}
|
||||
else if (cls == CLASS_PALADIN && tab == PALADIN_TAB_RETRIBUTION) // retribution
|
||||
else if (cls == CLASS_PALADIN && tab == PALADIN_TAB_RETRIBUTION)
|
||||
{
|
||||
stats_weights_[STATS_TYPE_AGILITY] += 1.6f;
|
||||
stats_weights_[STATS_TYPE_STRENGTH] += 2.5f;
|
||||
@@ -311,7 +311,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
|
||||
stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f;
|
||||
stats_weights_[STATS_TYPE_MELEE_DPS] += 9.0f;
|
||||
}
|
||||
else if ((cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT)) // enhancement
|
||||
else if ((cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT))
|
||||
{
|
||||
stats_weights_[STATS_TYPE_AGILITY] += 1.4f;
|
||||
stats_weights_[STATS_TYPE_STRENGTH] += 1.1f;
|
||||
@@ -325,9 +325,10 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
|
||||
stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f;
|
||||
stats_weights_[STATS_TYPE_MELEE_DPS] += 8.5f;
|
||||
}
|
||||
else if (cls == CLASS_WARLOCK || (cls == CLASS_MAGE && tab != MAGE_TAB_FIRE) ||
|
||||
(cls == CLASS_PRIEST && tab == PRIEST_TAB_SHADOW) || // shadow
|
||||
(cls == CLASS_DRUID && tab == DRUID_TAB_BALANCE)) // balance
|
||||
else if (cls == CLASS_WARLOCK ||
|
||||
(cls == CLASS_MAGE && tab != MAGE_TAB_FIRE) ||
|
||||
(cls == CLASS_PRIEST && tab == PRIEST_TAB_SHADOW) ||
|
||||
(cls == CLASS_DRUID && tab == DRUID_TAB_BALANCE))
|
||||
{
|
||||
stats_weights_[STATS_TYPE_INTELLECT] += 0.3f;
|
||||
stats_weights_[STATS_TYPE_SPIRIT] += 0.6f;
|
||||
@@ -355,8 +356,8 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
|
||||
stats_weights_[STATS_TYPE_CRIT] += 0.8f;
|
||||
stats_weights_[STATS_TYPE_HASTE] += 1.0f;
|
||||
}
|
||||
else if ((cls == CLASS_PALADIN && tab == PALADIN_TAB_HOLY) || // holy
|
||||
(cls == CLASS_SHAMAN && tab == SHAMAN_TAB_RESTORATION)) // heal
|
||||
else if ((cls == CLASS_PALADIN && tab == PALADIN_TAB_HOLY) ||
|
||||
(cls == CLASS_SHAMAN && tab == SHAMAN_TAB_RESTORATION))
|
||||
{
|
||||
stats_weights_[STATS_TYPE_INTELLECT] += 0.9f;
|
||||
stats_weights_[STATS_TYPE_SPIRIT] += 0.15f;
|
||||
@@ -365,7 +366,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
|
||||
stats_weights_[STATS_TYPE_CRIT] += 0.6f;
|
||||
stats_weights_[STATS_TYPE_HASTE] += 0.8f;
|
||||
}
|
||||
else if ((cls == CLASS_PRIEST && tab != PRIEST_TAB_SHADOW) || // discipline / holy
|
||||
else if ((cls == CLASS_PRIEST && tab != PRIEST_TAB_SHADOW) ||
|
||||
(cls == CLASS_DRUID && tab == DRUID_TAB_RESTORATION))
|
||||
{
|
||||
stats_weights_[STATS_TYPE_INTELLECT] += 0.8f;
|
||||
@@ -396,7 +397,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
|
||||
stats_weights_[STATS_TYPE_EXPERTISE] += 3.0f;
|
||||
stats_weights_[STATS_TYPE_MELEE_DPS] += 2.0f;
|
||||
}
|
||||
else if (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_BLOOD)
|
||||
else if (cls == CLASS_DEATH_KNIGHT && tab == DEATH_KNIGHT_TAB_BLOOD)
|
||||
{
|
||||
stats_weights_[STATS_TYPE_AGILITY] += 2.0f;
|
||||
stats_weights_[STATS_TYPE_STRENGTH] += 1.0f;
|
||||
@@ -539,7 +540,7 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
|
||||
// spec without double hand
|
||||
// enhancement, rogue, ice dk, unholy dk, shield tank, fury warrior without titan's grip but with duel wield
|
||||
if (((cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && player_->CanDualWield()) ||
|
||||
(cls == CLASS_ROGUE) || (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_FROST) ||
|
||||
(cls == CLASS_ROGUE) || (cls == CLASS_DEATH_KNIGHT && tab == DEATH_KNIGHT_TAB_FROST) ||
|
||||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !player_->CanTitanGrip() &&
|
||||
player_->CanDualWield()) ||
|
||||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_PROTECTION) ||
|
||||
@@ -556,7 +557,7 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
|
||||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !player_->CanDualWield()) ||
|
||||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_ARMS) || (cls == CLASS_DRUID && tab == DRUID_TAB_FERAL) ||
|
||||
(cls == CLASS_PALADIN && tab == PALADIN_TAB_RETRIBUTION) ||
|
||||
(cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_BLOOD) ||
|
||||
(cls == CLASS_DEATH_KNIGHT && tab == DEATH_KNIGHT_TAB_BLOOD) ||
|
||||
(cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && !player_->CanDualWield()))
|
||||
{
|
||||
weight_ *= 0.1;
|
||||
|
||||
@@ -8,90 +8,6 @@
|
||||
#include "Playerbots.h"
|
||||
#include "Timer.h"
|
||||
|
||||
uint32 NextAction::size(NextAction** actions)
|
||||
{
|
||||
if (!actions)
|
||||
return 0;
|
||||
|
||||
uint32 size = 0;
|
||||
for (size = 0; actions[size];)
|
||||
++size;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
NextAction** NextAction::clone(NextAction** actions)
|
||||
{
|
||||
if (!actions)
|
||||
return nullptr;
|
||||
|
||||
uint32 size = NextAction::size(actions);
|
||||
|
||||
NextAction** res = new NextAction*[size + 1];
|
||||
for (uint32 i = 0; i < size; i++)
|
||||
res[i] = new NextAction(*actions[i]);
|
||||
|
||||
res[size] = nullptr;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
NextAction** NextAction::merge(NextAction** left, NextAction** right)
|
||||
{
|
||||
uint32 leftSize = NextAction::size(left);
|
||||
uint32 rightSize = NextAction::size(right);
|
||||
|
||||
NextAction** res = new NextAction*[leftSize + rightSize + 1];
|
||||
|
||||
for (uint32 i = 0; i < leftSize; i++)
|
||||
res[i] = new NextAction(*left[i]);
|
||||
|
||||
for (uint32 i = 0; i < rightSize; i++)
|
||||
res[leftSize + i] = new NextAction(*right[i]);
|
||||
|
||||
res[leftSize + rightSize] = nullptr;
|
||||
|
||||
NextAction::destroy(left);
|
||||
NextAction::destroy(right);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
NextAction** NextAction::array(uint32 nil, ...)
|
||||
{
|
||||
va_list vl;
|
||||
va_start(vl, nil);
|
||||
|
||||
uint32 size = 0;
|
||||
NextAction* cur = nullptr;
|
||||
do
|
||||
{
|
||||
cur = va_arg(vl, NextAction*);
|
||||
++size;
|
||||
} while (cur);
|
||||
|
||||
va_end(vl);
|
||||
|
||||
NextAction** res = new NextAction*[size];
|
||||
va_start(vl, nil);
|
||||
for (uint32 i = 0; i < size; i++)
|
||||
res[i] = va_arg(vl, NextAction*);
|
||||
va_end(vl);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void NextAction::destroy(NextAction** actions)
|
||||
{
|
||||
if (!actions)
|
||||
return;
|
||||
|
||||
for (uint32 i = 0; actions[i]; i++)
|
||||
delete actions[i];
|
||||
|
||||
delete[] actions;
|
||||
}
|
||||
|
||||
Value<Unit*>* Action::GetTargetValue() { return context->GetValue<Unit*>(GetTargetName()); }
|
||||
|
||||
Unit* Action::GetTarget() { return GetTargetValue()->Get(); }
|
||||
@@ -101,4 +17,4 @@ ActionBasket::ActionBasket(ActionNode* action, float relevance, bool skipPrerequ
|
||||
{
|
||||
}
|
||||
|
||||
bool ActionBasket::isExpired(uint32 msecs) { return getMSTime() - created >= msecs; }
|
||||
bool ActionBasket::isExpired(uint32_t msecs) { return getMSTime() - created >= msecs; }
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_ACTION_H
|
||||
#define _PLAYERBOT_ACTION_H
|
||||
#pragma once
|
||||
|
||||
#include "AiObject.h"
|
||||
#include "Common.h"
|
||||
@@ -24,15 +23,26 @@ public:
|
||||
std::string const getName() { return name; }
|
||||
float getRelevance() { return relevance; }
|
||||
|
||||
static uint32 size(NextAction** actions);
|
||||
static NextAction** clone(NextAction** actions);
|
||||
static NextAction** merge(NextAction** what, NextAction** with);
|
||||
static NextAction** array(uint32 nil, ...);
|
||||
static void destroy(NextAction** actions);
|
||||
static std::vector<NextAction> merge(std::vector<NextAction> const& what, std::vector<NextAction> const& with)
|
||||
{
|
||||
std::vector<NextAction> result = {};
|
||||
|
||||
for (NextAction const& action : what)
|
||||
{
|
||||
result.push_back(action);
|
||||
}
|
||||
|
||||
for (NextAction const& action : with)
|
||||
{
|
||||
result.push_back(action);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
private:
|
||||
float relevance;
|
||||
std::string const name;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
class Action : public AiNamedObject
|
||||
@@ -52,9 +62,9 @@ public:
|
||||
virtual bool Execute([[maybe_unused]] Event event) { return true; }
|
||||
virtual bool isPossible() { return true; }
|
||||
virtual bool isUseful() { return true; }
|
||||
virtual NextAction** getPrerequisites() { return nullptr; }
|
||||
virtual NextAction** getAlternatives() { return nullptr; }
|
||||
virtual NextAction** getContinuers() { return nullptr; }
|
||||
virtual std::vector<NextAction> getPrerequisites() { return {}; }
|
||||
virtual std::vector<NextAction> getAlternatives() { return {}; }
|
||||
virtual std::vector<NextAction> getContinuers() { return {}; }
|
||||
virtual ActionThreatType getThreatType() { return ActionThreatType::None; }
|
||||
void Update() {}
|
||||
void Reset() {}
|
||||
@@ -73,39 +83,44 @@ protected:
|
||||
class ActionNode
|
||||
{
|
||||
public:
|
||||
ActionNode(std::string const name, NextAction** prerequisites = nullptr, NextAction** alternatives = nullptr,
|
||||
NextAction** continuers = nullptr)
|
||||
: name(name), action(nullptr), continuers(continuers), alternatives(alternatives), prerequisites(prerequisites)
|
||||
{
|
||||
} // reorder arguments - whipowill
|
||||
ActionNode(
|
||||
std::string name,
|
||||
std::vector<NextAction> prerequisites = {},
|
||||
std::vector<NextAction> alternatives = {},
|
||||
std::vector<NextAction> continuers = {}
|
||||
) :
|
||||
name(std::move(name)),
|
||||
action(nullptr),
|
||||
continuers(continuers),
|
||||
alternatives(alternatives),
|
||||
prerequisites(prerequisites)
|
||||
{}
|
||||
|
||||
virtual ~ActionNode()
|
||||
{
|
||||
NextAction::destroy(prerequisites);
|
||||
NextAction::destroy(alternatives);
|
||||
NextAction::destroy(continuers);
|
||||
}
|
||||
virtual ~ActionNode() = default;
|
||||
|
||||
Action* getAction() { return action; }
|
||||
void setAction(Action* action) { this->action = action; }
|
||||
std::string const getName() { return name; }
|
||||
const std::string getName() { return name; }
|
||||
|
||||
NextAction** getContinuers() { return NextAction::merge(NextAction::clone(continuers), action->getContinuers()); }
|
||||
NextAction** getAlternatives()
|
||||
std::vector<NextAction> getContinuers()
|
||||
{
|
||||
return NextAction::merge(NextAction::clone(alternatives), action->getAlternatives());
|
||||
return NextAction::merge(this->continuers, action->getContinuers());
|
||||
}
|
||||
NextAction** getPrerequisites()
|
||||
std::vector<NextAction> getAlternatives()
|
||||
{
|
||||
return NextAction::merge(NextAction::clone(prerequisites), action->getPrerequisites());
|
||||
return NextAction::merge(this->alternatives, action->getAlternatives());
|
||||
}
|
||||
std::vector<NextAction> getPrerequisites()
|
||||
{
|
||||
return NextAction::merge(this->prerequisites, action->getPrerequisites());
|
||||
}
|
||||
|
||||
private:
|
||||
std::string const name;
|
||||
const std::string name;
|
||||
Action* action;
|
||||
NextAction** continuers;
|
||||
NextAction** alternatives;
|
||||
NextAction** prerequisites;
|
||||
std::vector<NextAction> continuers;
|
||||
std::vector<NextAction> alternatives;
|
||||
std::vector<NextAction> prerequisites;
|
||||
};
|
||||
|
||||
class ActionBasket
|
||||
@@ -121,14 +136,12 @@ public:
|
||||
bool isSkipPrerequisites() { return skipPrerequisites; }
|
||||
void AmendRelevance(float k) { relevance *= k; }
|
||||
void setRelevance(float relevance) { this->relevance = relevance; }
|
||||
bool isExpired(uint32 msecs);
|
||||
bool isExpired(uint32_t msecs);
|
||||
|
||||
private:
|
||||
ActionNode* action;
|
||||
float relevance;
|
||||
bool skipPrerequisites;
|
||||
Event event;
|
||||
uint32 created;
|
||||
uint32_t created;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -42,9 +42,6 @@ protected:
|
||||
// TRIGGERS
|
||||
//
|
||||
|
||||
#define NEXT_TRIGGERS(name, relevance) \
|
||||
virtual NextAction* getNextAction() { return new NextAction(name, relevance); }
|
||||
|
||||
#define BEGIN_TRIGGER(clazz, super) \
|
||||
class clazz : public super \
|
||||
{ \
|
||||
@@ -78,14 +75,6 @@ protected:
|
||||
clazz(PlayerbotAI* botAI) : BuffOnPartyTrigger(botAI, spell) {} \
|
||||
}
|
||||
|
||||
#define BUFF_PARTY_TRIGGER_A(clazz, spell) \
|
||||
class clazz : public BuffOnPartyTrigger \
|
||||
{ \
|
||||
public: \
|
||||
clazz(PlayerbotAI* botAI) : BuffOnPartyTrigger(botAI, spell) {} \
|
||||
bool IsActive() override; \
|
||||
}
|
||||
|
||||
#define DEBUFF_TRIGGER(clazz, spell) \
|
||||
class clazz : public DebuffTrigger \
|
||||
{ \
|
||||
@@ -296,14 +285,6 @@ protected:
|
||||
clazz(PlayerbotAI* botAI) : CastHealingSpellAction(botAI, spell) {} \
|
||||
}
|
||||
|
||||
#define HEAL_ACTION_U(clazz, spell, useful) \
|
||||
class clazz : public CastHealingSpellAction \
|
||||
{ \
|
||||
public: \
|
||||
clazz(PlayerbotAI* botAI) : CastHealingSpellAction(botAI, spell) {} \
|
||||
bool isUseful() override { return useful; } \
|
||||
}
|
||||
|
||||
#define HEAL_PARTY_ACTION(clazz, spell, estAmount, manaEfficiency) \
|
||||
class clazz : public HealPartyMemberAction \
|
||||
{ \
|
||||
@@ -404,14 +385,6 @@ protected:
|
||||
clazz(PlayerbotAI* botAI) : CastReachTargetSpellAction(botAI, spell, range) {} \
|
||||
}
|
||||
|
||||
#define REACH_ACTION_U(clazz, spell, range, useful) \
|
||||
class clazz : public CastReachTargetSpellAction \
|
||||
{ \
|
||||
public: \
|
||||
clazz(PlayerbotAI* botAI) : CastReachTargetSpellAction(botAI, spell, range) {} \
|
||||
bool isUseful() override { return useful; } \
|
||||
}
|
||||
|
||||
#define ENEMY_HEALER_ACTION(clazz, spell) \
|
||||
class clazz : public CastSpellOnEnemyHealerAction \
|
||||
{ \
|
||||
@@ -440,10 +413,6 @@ protected:
|
||||
clazz(PlayerbotAI* botAI) : CastProtectSpellAction(botAI, spell) {} \
|
||||
}
|
||||
|
||||
#define END_RANGED_SPELL_ACTION() \
|
||||
} \
|
||||
;
|
||||
|
||||
#define BEGIN_SPELL_ACTION(clazz, name) \
|
||||
class clazz : public CastSpellAction \
|
||||
{ \
|
||||
@@ -472,42 +441,4 @@ protected:
|
||||
public: \
|
||||
clazz(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, name) {}
|
||||
|
||||
#define END_RANGED_SPELL_ACTION() \
|
||||
} \
|
||||
;
|
||||
|
||||
#define BEGIN_BUFF_ON_PARTY_ACTION(clazz, name) \
|
||||
class clazz : public BuffOnPartyAction \
|
||||
{ \
|
||||
public: \
|
||||
clazz(PlayerbotAI* botAI) : BuffOnPartyAction(botAI, name) {}
|
||||
|
||||
//
|
||||
// Action node
|
||||
//
|
||||
|
||||
// node_name , action, prerequisite
|
||||
#define ACTION_NODE_P(name, spell, pre) \
|
||||
static ActionNode* name([[maybe_unused]] PlayerbotAI* botAI) \
|
||||
{ \
|
||||
return new ActionNode(spell, /*P*/ NextAction::array(0, new NextAction(pre), nullptr), /*A*/ nullptr, \
|
||||
/*C*/ nullptr); \
|
||||
}
|
||||
|
||||
// node_name , action, alternative
|
||||
#define ACTION_NODE_A(name, spell, alt) \
|
||||
static ActionNode* name([[maybe_unused]] PlayerbotAI* botAI) \
|
||||
{ \
|
||||
return new ActionNode(spell, /*P*/ nullptr, /*A*/ NextAction::array(0, new NextAction(alt), nullptr), \
|
||||
/*C*/ nullptr); \
|
||||
}
|
||||
|
||||
// node_name , action, continuer
|
||||
#define ACTION_NODE_C(name, spell, con) \
|
||||
static ActionNode* name([[maybe_unused]] PlayerbotAI* botAI) \
|
||||
{ \
|
||||
return new ActionNode(spell, /*P*/ nullptr, /*A*/ nullptr, \
|
||||
/*C*/ NextAction::array(0, new NextAction(con), nullptr)); \
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -43,8 +43,6 @@
|
||||
#include "raids/magtheridon/RaidMagtheridonTriggerContext.h"
|
||||
#include "raids/gruulslair/RaidGruulsLairActionContext.h"
|
||||
#include "raids/gruulslair/RaidGruulsLairTriggerContext.h"
|
||||
#include "raids/naxxramas/RaidNaxxActionContext.h"
|
||||
#include "raids/naxxramas/RaidNaxxTriggerContext.h"
|
||||
#include "raids/eyeofeternity/RaidEoEActionContext.h"
|
||||
#include "raids/eyeofeternity/RaidEoETriggerContext.h"
|
||||
#include "raids/vaultofarchavon/RaidVoAActionContext.h"
|
||||
@@ -117,7 +115,6 @@ void AiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList<Act
|
||||
actionContexts.Add(new RaidKarazhanActionContext());
|
||||
actionContexts.Add(new RaidMagtheridonActionContext());
|
||||
actionContexts.Add(new RaidGruulsLairActionContext());
|
||||
actionContexts.Add(new RaidNaxxActionContext());
|
||||
actionContexts.Add(new RaidOsActionContext());
|
||||
actionContexts.Add(new RaidEoEActionContext());
|
||||
actionContexts.Add(new RaidVoAActionContext());
|
||||
@@ -152,7 +149,6 @@ void AiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextList<Tr
|
||||
triggerContexts.Add(new RaidKarazhanTriggerContext());
|
||||
triggerContexts.Add(new RaidMagtheridonTriggerContext());
|
||||
triggerContexts.Add(new RaidGruulsLairTriggerContext());
|
||||
triggerContexts.Add(new RaidNaxxTriggerContext());
|
||||
triggerContexts.Add(new RaidOsTriggerContext());
|
||||
triggerContexts.Add(new RaidEoETriggerContext());
|
||||
triggerContexts.Add(new RaidVoATriggerContext());
|
||||
|
||||
@@ -6,36 +6,40 @@
|
||||
#include "CustomStrategy.h"
|
||||
|
||||
#include <regex>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
std::map<std::string, std::string> CustomStrategy::actionLinesCache;
|
||||
|
||||
NextAction* toNextAction(std::string const action)
|
||||
NextAction toNextAction(std::string const action)
|
||||
{
|
||||
std::vector<std::string> tokens = split(action, '!');
|
||||
if (tokens.size() == 2 && !tokens[0].empty())
|
||||
return new NextAction(tokens[0], atof(tokens[1].c_str()));
|
||||
else if (tokens.size() == 1 && !tokens[0].empty())
|
||||
return new NextAction(tokens[0], ACTION_NORMAL);
|
||||
|
||||
if (tokens[0].empty())
|
||||
throw std::invalid_argument("Invalid action");
|
||||
|
||||
if (tokens.size() == 2)
|
||||
return NextAction(tokens[0], atof(tokens[1].c_str()));
|
||||
|
||||
if (tokens.size() == 1)
|
||||
return NextAction(tokens[0], ACTION_NORMAL);
|
||||
|
||||
LOG_ERROR("playerbots", "Invalid action {}", action.c_str());
|
||||
return nullptr;
|
||||
|
||||
throw std::invalid_argument("Invalid action");
|
||||
}
|
||||
|
||||
NextAction** toNextActionArray(std::string const actions)
|
||||
std::vector<NextAction> toNextActionArray(const std::string actions)
|
||||
{
|
||||
std::vector<std::string> tokens = split(actions, ',');
|
||||
NextAction** res = new NextAction*[tokens.size() + 1];
|
||||
const std::vector<std::string> tokens = split(actions, ',');
|
||||
std::vector<NextAction> res = {};
|
||||
|
||||
uint32 index = 0;
|
||||
for (std::vector<std::string>::iterator i = tokens.begin(); i != tokens.end(); ++i)
|
||||
for (const std::string token : tokens)
|
||||
{
|
||||
if (NextAction* na = toNextAction(*i))
|
||||
res[index++] = na;
|
||||
res.push_back(toNextAction(token));
|
||||
}
|
||||
|
||||
res[index++] = nullptr;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@@ -258,48 +258,45 @@ ActionNode* Engine::CreateActionNode(std::string const name)
|
||||
return node;
|
||||
|
||||
return new ActionNode(name,
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
bool Engine::MultiplyAndPush(NextAction** actions, float forceRelevance, bool skipPrerequisites, Event event,
|
||||
char const* pushType)
|
||||
bool Engine::MultiplyAndPush(
|
||||
std::vector<NextAction> actions,
|
||||
float forceRelevance,
|
||||
bool skipPrerequisites,
|
||||
Event event,
|
||||
char const* pushType
|
||||
)
|
||||
{
|
||||
bool pushed = false;
|
||||
if (actions)
|
||||
|
||||
for (NextAction nextAction : actions)
|
||||
{
|
||||
for (uint32 j = 0; actions[j]; j++)
|
||||
ActionNode* action = this->CreateActionNode(nextAction.getName());
|
||||
|
||||
this->InitializeAction(action);
|
||||
|
||||
float k = nextAction.getRelevance();
|
||||
|
||||
if (forceRelevance > 0.0f)
|
||||
{
|
||||
if (NextAction* nextAction = actions[j])
|
||||
{
|
||||
ActionNode* action = CreateActionNode(nextAction->getName());
|
||||
InitializeAction(action);
|
||||
|
||||
float k = nextAction->getRelevance();
|
||||
if (forceRelevance > 0.0f)
|
||||
{
|
||||
k = forceRelevance;
|
||||
}
|
||||
|
||||
if (k > 0)
|
||||
{
|
||||
LogAction("PUSH:%s - %f (%s)", action->getName().c_str(), k, pushType);
|
||||
queue.Push(new ActionBasket(action, k, skipPrerequisites, event));
|
||||
pushed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
delete action;
|
||||
}
|
||||
|
||||
delete nextAction;
|
||||
}
|
||||
else
|
||||
break;
|
||||
k = forceRelevance;
|
||||
}
|
||||
|
||||
delete[] actions;
|
||||
if (k > 0)
|
||||
{
|
||||
this->LogAction("PUSH:%s - %f (%s)", action->getName().c_str(), k, pushType);
|
||||
queue.Push(new ActionBasket(action, k, skipPrerequisites, event));
|
||||
pushed = true;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
delete action;
|
||||
|
||||
}
|
||||
|
||||
return pushed;
|
||||
@@ -530,10 +527,10 @@ std::vector<std::string> Engine::GetStrategies()
|
||||
|
||||
void Engine::PushAgain(ActionNode* actionNode, float relevance, Event event)
|
||||
{
|
||||
NextAction** nextAction = new NextAction*[2];
|
||||
nextAction[0] = new NextAction(actionNode->getName(), relevance);
|
||||
nextAction[1] = nullptr;
|
||||
std::vector<NextAction> nextAction = { NextAction(actionNode->getName(), relevance) };
|
||||
|
||||
MultiplyAndPush(nextAction, relevance, true, event, "again");
|
||||
|
||||
delete actionNode;
|
||||
}
|
||||
|
||||
@@ -563,6 +560,13 @@ bool Engine::ListenAndExecute(Action* action, Event event)
|
||||
{
|
||||
bool actionExecuted = false;
|
||||
|
||||
if (action == nullptr)
|
||||
{
|
||||
LOG_ERROR("playerbots", "Action is nullptr");
|
||||
|
||||
return actionExecuted;
|
||||
}
|
||||
|
||||
if (actionExecutionListeners.Before(action, event))
|
||||
{
|
||||
actionExecuted = actionExecutionListeners.AllowExecution(action, event) ? action->Execute(event) : true;
|
||||
|
||||
@@ -90,7 +90,7 @@ public:
|
||||
bool testMode;
|
||||
|
||||
private:
|
||||
bool MultiplyAndPush(NextAction** actions, float forceRelevance, bool skipPrerequisites, Event event,
|
||||
bool MultiplyAndPush(std::vector<NextAction> actions, float forceRelevance, bool skipPrerequisites, Event event,
|
||||
const char* pushType);
|
||||
void Reset();
|
||||
void ProcessTriggers(bool minimal);
|
||||
|
||||
@@ -28,90 +28,112 @@ public:
|
||||
private:
|
||||
static ActionNode* melee([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("melee",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"melee",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* healthstone([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("healthstone",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("healing potion"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
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*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("follow"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"be near",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("follow") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* attack_anything([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("attack anything",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"attack anything",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* move_random([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("move random",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("stay line"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
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*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"move to loot",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* food([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("food",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"food",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* drink([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("drink",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"drink",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* mana_potion([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("mana potion",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"mana potion",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* healing_potion([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("healing potion",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("food"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"healing potion",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("food") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* flee([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("flee",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"flee",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ public:
|
||||
Strategy(PlayerbotAI* botAI);
|
||||
virtual ~Strategy() {}
|
||||
|
||||
virtual NextAction** getDefaultActions() { return nullptr; }
|
||||
virtual std::vector<NextAction> getDefaultActions() { return {}; }
|
||||
virtual void InitTriggers([[maybe_unused]] std::vector<TriggerNode*>& triggers) {}
|
||||
virtual void InitMultipliers([[maybe_unused]] std::vector<Multiplier*>& multipliers) {}
|
||||
virtual std::string const getName() = 0;
|
||||
|
||||
@@ -120,6 +120,8 @@ public:
|
||||
creators["formation"] = &StrategyContext::combat_formation;
|
||||
creators["move from group"] = &StrategyContext::move_from_group;
|
||||
creators["worldbuff"] = &StrategyContext::world_buff;
|
||||
creators["use bobber"] = &StrategyContext::bobber_strategy;
|
||||
creators["master fishing"] = &StrategyContext::master_fishing;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -188,6 +190,8 @@ private:
|
||||
static Strategy* combat_formation(PlayerbotAI* botAI) { return new CombatFormationStrategy(botAI); }
|
||||
static Strategy* move_from_group(PlayerbotAI* botAI) { return new MoveFromGroupStrategy(botAI); }
|
||||
static Strategy* world_buff(PlayerbotAI* botAI) { return new WorldBuffStrategy(botAI); }
|
||||
static Strategy* bobber_strategy(PlayerbotAI* botAI) { return new UseBobberStrategy(botAI); }
|
||||
static Strategy* master_fishing(PlayerbotAI* botAI) { return new MasterFishingStrategy(botAI); }
|
||||
};
|
||||
|
||||
class MovementStrategyContext : public NamedObjectContext<Strategy>
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_TRIGGER_H
|
||||
#define _PLAYERBOT_TRIGGER_H
|
||||
#pragma once
|
||||
|
||||
#include "Action.h"
|
||||
#include "Common.h"
|
||||
@@ -15,7 +14,11 @@ class Unit;
|
||||
class Trigger : public AiNamedObject
|
||||
{
|
||||
public:
|
||||
Trigger(PlayerbotAI* botAI, std::string const name = "trigger", int32 checkInterval = 1);
|
||||
Trigger(
|
||||
PlayerbotAI* botAI,
|
||||
const std::string name = "trigger",
|
||||
int32_t checkInterval = 1
|
||||
);
|
||||
|
||||
virtual ~Trigger() {}
|
||||
|
||||
@@ -23,7 +26,7 @@ public:
|
||||
virtual void ExternalEvent([[maybe_unused]] std::string const param, [[maybe_unused]] Player* owner = nullptr) {}
|
||||
virtual void ExternalEvent([[maybe_unused]] WorldPacket& packet, [[maybe_unused]] Player* owner = nullptr) {}
|
||||
virtual bool IsActive() { return false; }
|
||||
virtual NextAction** getHandlers() { return nullptr; }
|
||||
virtual std::vector<NextAction> getHandlers() { return {}; }
|
||||
void Update() {}
|
||||
virtual void Reset() {}
|
||||
virtual Unit* GetTarget();
|
||||
@@ -33,32 +36,49 @@ public:
|
||||
bool needCheck(uint32 now);
|
||||
|
||||
protected:
|
||||
int32 checkInterval;
|
||||
uint32 lastCheckTime;
|
||||
int32_t checkInterval;
|
||||
uint32_t lastCheckTime;
|
||||
};
|
||||
|
||||
class TriggerNode
|
||||
{
|
||||
public:
|
||||
TriggerNode(std::string const name, NextAction** handlers = nullptr)
|
||||
: trigger(nullptr), handlers(handlers), name(name)
|
||||
{
|
||||
} // reorder args - whipowill
|
||||
|
||||
virtual ~TriggerNode() { NextAction::destroy(handlers); }
|
||||
TriggerNode(
|
||||
const std::string& name,
|
||||
std::vector<NextAction> handlers = {}
|
||||
) :
|
||||
trigger(nullptr),
|
||||
handlers(std::move(handlers)),
|
||||
name(name)
|
||||
{}
|
||||
|
||||
Trigger* getTrigger() { return trigger; }
|
||||
void setTrigger(Trigger* trigger) { this->trigger = trigger; }
|
||||
std::string const getName() { return name; }
|
||||
const std::string getName() { return name; }
|
||||
|
||||
NextAction** getHandlers() { return NextAction::merge(NextAction::clone(handlers), trigger->getHandlers()); }
|
||||
std::vector<NextAction> getHandlers()
|
||||
{
|
||||
std::vector<NextAction> result = this->handlers;
|
||||
|
||||
float getFirstRelevance() { return handlers[0] ? handlers[0]->getRelevance() : -1; }
|
||||
if (trigger != nullptr)
|
||||
{
|
||||
std::vector<NextAction> extra = trigger->getHandlers();
|
||||
result.insert(result.end(), extra.begin(), extra.end());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
float getFirstRelevance()
|
||||
{
|
||||
if (this->handlers.size() > 0)
|
||||
return this->handlers[0].getRelevance();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private:
|
||||
Trigger* trigger;
|
||||
NextAction** handlers;
|
||||
std::string const name;
|
||||
std::vector<NextAction> handlers;
|
||||
const std::string name;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -59,7 +59,7 @@ bool AcceptInvitationAction::Execute(Event event)
|
||||
|
||||
if (sPlayerbotAIConfig->summonWhenGroup && bot->GetDistance(inviter) > sPlayerbotAIConfig->sightDistance)
|
||||
{
|
||||
Teleport(inviter, bot);
|
||||
Teleport(inviter, bot, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -46,7 +46,6 @@
|
||||
#include "OutfitAction.h"
|
||||
#include "PositionAction.h"
|
||||
#include "DropQuestAction.h"
|
||||
#include "RaidNaxxActions.h"
|
||||
#include "RandomBotUpdateAction.h"
|
||||
#include "ReachTargetActions.h"
|
||||
#include "ReleaseSpiritAction.h"
|
||||
@@ -64,6 +63,7 @@
|
||||
#include "WorldBuffAction.h"
|
||||
#include "XpGainAction.h"
|
||||
#include "NewRpgAction.h"
|
||||
#include "FishingAction.h"
|
||||
#include "CancelChannelAction.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
@@ -191,6 +191,11 @@ public:
|
||||
creators["buy tabard"] = &ActionContext::buy_tabard;
|
||||
creators["guild manage nearby"] = &ActionContext::guild_manage_nearby;
|
||||
creators["clean quest log"] = &ActionContext::clean_quest_log;
|
||||
creators["move near water"] = &ActionContext::move_near_water;
|
||||
creators["go fishing"] = &ActionContext::go_fishing;
|
||||
creators["use fishing bobber"] = &ActionContext::use_fishing_bobber;
|
||||
creators["end master fishing"] = &ActionContext::end_master_fishing;
|
||||
creators["remove bobber strategy"] = &ActionContext::remove_bobber_strategy;
|
||||
creators["roll"] = &ActionContext::roll_action;
|
||||
creators["cancel channel"] = &ActionContext::cancel_channel;
|
||||
|
||||
@@ -380,6 +385,11 @@ private:
|
||||
static Action* buy_tabard(PlayerbotAI* botAI) { return new BuyTabardAction(botAI); }
|
||||
static Action* guild_manage_nearby(PlayerbotAI* botAI) { return new GuildManageNearbyAction(botAI); }
|
||||
static Action* clean_quest_log(PlayerbotAI* botAI) { return new CleanQuestLogAction(botAI); }
|
||||
static Action* move_near_water(PlayerbotAI* botAI) { return new MoveNearWaterAction(botAI); }
|
||||
static Action* go_fishing(PlayerbotAI* botAI) { return new FishingAction(botAI);}
|
||||
static Action* use_fishing_bobber(PlayerbotAI* botAI) { return new UseBobberAction(botAI);}
|
||||
static Action* end_master_fishing(PlayerbotAI* botAI) { return new EndMasterFishingAction(botAI); }
|
||||
static Action* remove_bobber_strategy(PlayerbotAI* botAI) { return new RemoveBobberStrategyAction(botAI); }
|
||||
static Action* roll_action(PlayerbotAI* botAI) { return new RollAction(botAI); }
|
||||
|
||||
// BG Tactics
|
||||
|
||||
@@ -159,7 +159,7 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/)
|
||||
bot->StopMoving();
|
||||
}
|
||||
|
||||
if (IsMovingAllowed() && !bot->HasInArc(CAST_ANGLE_IN_FRONT, target))
|
||||
if (botAI->CanMove() && !bot->HasInArc(CAST_ANGLE_IN_FRONT, target))
|
||||
sServerFacade->SetFacingTo(bot, target);
|
||||
|
||||
botAI->ChangeEngine(BOT_STATE_COMBAT);
|
||||
|
||||
@@ -185,7 +185,6 @@ public:
|
||||
creators["guild remove"] = &ChatActionContext::guild_remove;
|
||||
creators["guild leave"] = &ChatActionContext::guild_leave;
|
||||
creators["rtsc"] = &ChatActionContext::rtsc;
|
||||
creators["naxx chat shortcut"] = &ChatActionContext::naxx_chat_shortcut;
|
||||
creators["bwl chat shortcut"] = &ChatActionContext::bwl_chat_shortcut;
|
||||
creators["tell estimated dps"] = &ChatActionContext::tell_estimated_dps;
|
||||
creators["join"] = &ChatActionContext::join;
|
||||
@@ -298,7 +297,6 @@ private:
|
||||
static Action* guild_remove(PlayerbotAI* botAI) { return new GuildRemoveAction(botAI); }
|
||||
static Action* guild_leave(PlayerbotAI* botAI) { return new GuildLeaveAction(botAI); }
|
||||
static Action* rtsc(PlayerbotAI* botAI) { return new RTSCAction(botAI); }
|
||||
static Action* naxx_chat_shortcut(PlayerbotAI* ai) { return new NaxxChatShortcutAction(ai); }
|
||||
static Action* bwl_chat_shortcut(PlayerbotAI* ai) { return new BwlChatShortcutAction(ai); }
|
||||
static Action* tell_estimated_dps(PlayerbotAI* ai) { return new TellEstimatedDpsAction(ai); }
|
||||
static Action* join(PlayerbotAI* ai) { return new JoinGroupAction(ai); }
|
||||
|
||||
@@ -241,20 +241,6 @@ bool MaxDpsChatShortcutAction::Execute(Event event)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NaxxChatShortcutAction::Execute(Event event)
|
||||
{
|
||||
Player* master = GetMaster();
|
||||
if (!master)
|
||||
return false;
|
||||
|
||||
botAI->Reset();
|
||||
botAI->ChangeStrategy("+naxx", BOT_STATE_NON_COMBAT);
|
||||
botAI->ChangeStrategy("+naxx", BOT_STATE_COMBAT);
|
||||
botAI->TellMasterNoFacing("Add Naxx Strategies!");
|
||||
// bot->Say("Add Naxx Strategies!", LANG_UNIVERSAL);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BwlChatShortcutAction::Execute(Event event)
|
||||
{
|
||||
Player* master = GetMaster();
|
||||
|
||||
@@ -85,13 +85,6 @@ public:
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class NaxxChatShortcutAction : public Action
|
||||
{
|
||||
public:
|
||||
NaxxChatShortcutAction(PlayerbotAI* ai) : Action(ai, "naxx chat shortcut") {}
|
||||
virtual bool Execute(Event event);
|
||||
};
|
||||
|
||||
class BwlChatShortcutAction : public Action
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -78,20 +78,17 @@ float ChooseRpgTargetAction::getMaxRelevance(GuidPosition guidP)
|
||||
if (!trigger->IsActive())
|
||||
continue;
|
||||
|
||||
NextAction** nextActions = triggerNode->getHandlers();
|
||||
std::vector<NextAction> nextActions = triggerNode->getHandlers();
|
||||
|
||||
bool isRpg = false;
|
||||
|
||||
for (int32 i = 0; i < NextAction::size(nextActions); i++)
|
||||
for (NextAction nextAction : nextActions)
|
||||
{
|
||||
NextAction* nextAction = nextActions[i];
|
||||
|
||||
Action* action = botAI->GetAiObjectContext()->GetAction(nextAction->getName());
|
||||
Action* action = botAI->GetAiObjectContext()->GetAction(nextAction.getName());
|
||||
|
||||
if (dynamic_cast<RpgEnabled*>(action))
|
||||
isRpg = true;
|
||||
}
|
||||
NextAction::destroy(nextActions);
|
||||
|
||||
if (isRpg)
|
||||
{
|
||||
|
||||
@@ -344,6 +344,27 @@ bool EquipUpgradesAction::Execute(Event event)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (event.GetSource() == "item push result")
|
||||
{
|
||||
WorldPacket p(event.getPacket());
|
||||
p.rpos(0);
|
||||
ObjectGuid playerGuid;
|
||||
uint32 received, created, sendChatMessage, itemSlot, itemId;
|
||||
uint8 bagSlot;
|
||||
|
||||
p >> playerGuid;
|
||||
p >> received;
|
||||
p >> created;
|
||||
p >> sendChatMessage;
|
||||
p >> bagSlot;
|
||||
p >> itemSlot;
|
||||
p >> itemId;
|
||||
|
||||
ItemTemplate const* item = sObjectMgr->GetItemTemplate(itemId);
|
||||
if (item->Class == ITEM_CLASS_TRADE_GOODS && item->SubClass == ITEM_SUBCLASS_MEAT)
|
||||
return false;
|
||||
}
|
||||
|
||||
CollectItemsVisitor visitor;
|
||||
IterateItems(&visitor, ITERATE_ITEMS_IN_BAGS);
|
||||
|
||||
|
||||
493
src/strategy/actions/FishingAction.cpp
Normal file
493
src/strategy/actions/FishingAction.cpp
Normal file
@@ -0,0 +1,493 @@
|
||||
/*
|
||||
* 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) || pos != bot->GetPosition();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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();
|
||||
|
||||
return pos.IsValid() && !fishingSpotValueObject->IsStale(FISHING_LOCATION_TIMEOUT) && pos == bot->GetPosition();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
71
src/strategy/actions/FishingAction.h
Normal file
71
src/strategy/actions/FishingAction.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -97,6 +97,8 @@ bool FollowAction::isUseful()
|
||||
|
||||
distance = bot->GetDistance(loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ());
|
||||
}
|
||||
if (botAI->HasStrategy("master fishing", BOT_STATE_NON_COMBAT))
|
||||
return sServerFacade->IsDistanceGreaterThan(distance, sPlayerbotAIConfig->fishingDistanceFromMaster);
|
||||
|
||||
return sServerFacade->IsDistanceGreaterThan(distance, formation->GetMaxDistance());
|
||||
}
|
||||
|
||||
@@ -265,11 +265,6 @@ CastShootAction::CastShootAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "s
|
||||
}
|
||||
}
|
||||
|
||||
NextAction** CastSpellAction::getPrerequisites()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Value<Unit*>* CastDebuffSpellOnAttackerAction::GetTargetValue()
|
||||
{
|
||||
return context->GetValue<Unit*>("attacker without aura", spell);
|
||||
|
||||
@@ -27,7 +27,11 @@ public:
|
||||
bool isUseful() override;
|
||||
ActionThreatType getThreatType() override { return ActionThreatType::Single; }
|
||||
|
||||
NextAction** getPrerequisites() override;
|
||||
std::vector<NextAction> getPrerequisites() override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string const getSpell() { return spell; }
|
||||
|
||||
protected:
|
||||
@@ -193,10 +197,12 @@ public:
|
||||
ResurrectPartyMemberAction(PlayerbotAI* botAI, std::string const spell) : CastSpellAction(botAI, spell) {}
|
||||
|
||||
std::string const GetTargetName() override { return "party member to resurrect"; }
|
||||
NextAction** getPrerequisites() override
|
||||
std::vector<NextAction> getPrerequisites() override
|
||||
{
|
||||
return NextAction::merge(NextAction::array(0, new NextAction("reach party member to resurrect"), NULL),
|
||||
Action::getPrerequisites());
|
||||
return NextAction::merge(
|
||||
{ NextAction("reach party member to resurrect") },
|
||||
Action::getPrerequisites()
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -946,36 +946,7 @@ bool MovementAction::IsWaitingForLastMove(MovementPriority priority)
|
||||
|
||||
bool MovementAction::IsMovingAllowed()
|
||||
{
|
||||
// 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))
|
||||
return false;
|
||||
|
||||
// Death state (w/o spirit release) and Spirit of Redemption aura (priest)
|
||||
if ((bot->isDead() && !bot->HasPlayerFlag(PLAYER_FLAGS_GHOST)) || bot->HasSpiritOfRedemptionAura())
|
||||
return false;
|
||||
|
||||
// Common CC effects, ordered by frequency: rooted > frozen > polymorphed
|
||||
if (bot->IsRooted() || bot->isFrozen() || bot->IsPolymorphed())
|
||||
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).
|
||||
// We need to check charmed state AFTER vehicle one, cuz that's how it works:
|
||||
// passengers are set to charmed by vehicle with CHARM_TYPE_VEHICLE.
|
||||
if ((bot->GetVehicle() && !botAI->IsInVehicle(true)) || bot->IsCharmed())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
return botAI->CanMove();
|
||||
}
|
||||
|
||||
bool MovementAction::Follow(Unit* target, float distance) { return Follow(target, distance, GetFollowAngle()); }
|
||||
|
||||
@@ -68,17 +68,15 @@ bool RpgAction::SetNextRpgAction()
|
||||
|
||||
triggerNode->setTrigger(trigger);
|
||||
|
||||
NextAction** nextActions = triggerNode->getHandlers();
|
||||
std::vector<NextAction> nextActions = triggerNode->getHandlers();
|
||||
|
||||
Trigger* trigger = triggerNode->getTrigger();
|
||||
|
||||
bool isChecked = false;
|
||||
|
||||
for (int32 i = 0; i < NextAction::size(nextActions); i++)
|
||||
for (NextAction nextAction : nextActions)
|
||||
{
|
||||
NextAction* nextAction = nextActions[i];
|
||||
|
||||
if (nextAction->getRelevance() > 5.0f)
|
||||
if (nextAction.getRelevance() > 5.0f)
|
||||
continue;
|
||||
|
||||
if (!isChecked && !trigger->IsActive())
|
||||
@@ -86,14 +84,13 @@ bool RpgAction::SetNextRpgAction()
|
||||
|
||||
isChecked = true;
|
||||
|
||||
Action* action = botAI->GetAiObjectContext()->GetAction(nextAction->getName());
|
||||
Action* action = botAI->GetAiObjectContext()->GetAction(nextAction.getName());
|
||||
if (!dynamic_cast<RpgEnabled*>(action) || !action->isPossible() || !action->isUseful())
|
||||
continue;
|
||||
|
||||
actions.push_back(action);
|
||||
relevances.push_back((nextAction->getRelevance() - 1) * 500);
|
||||
relevances.push_back((nextAction.getRelevance() - 1) * 500);
|
||||
}
|
||||
NextAction::destroy(nextActions);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include "PositionValue.h"
|
||||
#include "ByteBuffer.h"
|
||||
|
||||
std::set<uint32> const FISHING_SPELLS = {7620, 7731, 7732, 18248, 33095, 51294};
|
||||
|
||||
Creature* SeeSpellAction::CreateWps(Player* wpOwner, float x, float y, float z, float o, uint32 entry, Creature* lastWp,
|
||||
bool important)
|
||||
{
|
||||
@@ -57,6 +59,16 @@ bool SeeSpellAction::Execute(Event event)
|
||||
// if (!botAI->HasStrategy("RTSC", botAI->GetState()))
|
||||
// return false;
|
||||
|
||||
if (FISHING_SPELLS.find(spellId) != FISHING_SPELLS.end())
|
||||
{
|
||||
if (AI_VALUE(bool, "can fish") && sPlayerbotAIConfig->enableFishingWithMaster)
|
||||
{
|
||||
botAI->ChangeStrategy("+master fishing", BOT_STATE_NON_COMBAT);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (spellId != RTSC_MOVE_SPELL)
|
||||
return false;
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "PlayerbotFactory.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
void TrainerAction::Learn(uint32 cost, TrainerSpell const* tSpell, std::ostringstream& msg)
|
||||
void TrainerAction::Learn(uint32 cost, const Trainer::Spell tSpell, std::ostringstream& msg)
|
||||
{
|
||||
if (sPlayerbotAIConfig->autoTrainSpells != "free" && !botAI->HasCheat(BotCheatMask::gold))
|
||||
{
|
||||
@@ -23,7 +23,7 @@ void TrainerAction::Learn(uint32 cost, TrainerSpell const* tSpell, std::ostrings
|
||||
bot->ModifyMoney(-int32(cost));
|
||||
}
|
||||
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(tSpell->spell);
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(tSpell.SpellId);
|
||||
if (!spellInfo)
|
||||
return;
|
||||
|
||||
@@ -41,10 +41,8 @@ void TrainerAction::Learn(uint32 cost, TrainerSpell const* tSpell, std::ostrings
|
||||
}
|
||||
}
|
||||
|
||||
if (!learned && !bot->HasSpell(tSpell->spell))
|
||||
{
|
||||
bot->learnSpell(tSpell->spell);
|
||||
}
|
||||
if (!learned && !bot->HasSpell(tSpell.SpellId))
|
||||
bot->learnSpell(tSpell.SpellId);
|
||||
|
||||
msg << " - learned";
|
||||
}
|
||||
@@ -53,37 +51,35 @@ void TrainerAction::Iterate(Creature* creature, TrainerSpellAction action, Spell
|
||||
{
|
||||
TellHeader(creature);
|
||||
|
||||
TrainerSpellData const* trainer_spells = creature->GetTrainerSpells();
|
||||
Trainer::Trainer* trainer = sObjectMgr->GetTrainer(creature->GetEntry());
|
||||
|
||||
if (!trainer)
|
||||
return;
|
||||
|
||||
float fDiscountMod = bot->GetReputationPriceDiscount(creature);
|
||||
uint32 totalCost = 0;
|
||||
|
||||
for (TrainerSpellMap::const_iterator itr = trainer_spells->spellList.begin();
|
||||
itr != trainer_spells->spellList.end(); ++itr)
|
||||
for (auto& spell : trainer->GetSpells())
|
||||
{
|
||||
TrainerSpell const* tSpell = &itr->second;
|
||||
if (!tSpell)
|
||||
if (!trainer->CanTeachSpell(bot, trainer->GetSpell(spell.SpellId)))
|
||||
continue;
|
||||
|
||||
TrainerSpellState state = bot->GetTrainerSpellState(tSpell);
|
||||
if (state != TRAINER_SPELL_GREEN)
|
||||
if (!spells.empty() && spells.find(spell.SpellId) == spells.end())
|
||||
continue;
|
||||
|
||||
uint32 spellId = tSpell->spell;
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell.SpellId);
|
||||
|
||||
if (!spellInfo)
|
||||
continue;
|
||||
|
||||
if (!spells.empty() && spells.find(tSpell->spell) == spells.end())
|
||||
continue;
|
||||
|
||||
uint32 cost = uint32(floor(tSpell->spellCost * fDiscountMod));
|
||||
uint32 cost = uint32(floor(spell.MoneyCost * fDiscountMod));
|
||||
totalCost += cost;
|
||||
|
||||
std::ostringstream out;
|
||||
out << chat->FormatSpell(spellInfo) << chat->formatMoney(cost);
|
||||
|
||||
if (action)
|
||||
(this->*action)(cost, tSpell, out);
|
||||
(this->*action)(cost, spell, out);
|
||||
|
||||
botAI->TellMaster(out);
|
||||
}
|
||||
@@ -112,15 +108,14 @@ bool TrainerAction::Execute(Event event)
|
||||
if (!creature || !creature->IsTrainer())
|
||||
return false;
|
||||
|
||||
if (!creature->IsValidTrainerForPlayer(bot))
|
||||
{
|
||||
botAI->TellError("This trainer cannot teach me");
|
||||
return false;
|
||||
}
|
||||
Trainer::Trainer* trainer = sObjectMgr->GetTrainer(creature->GetEntry());
|
||||
|
||||
// check present spell in trainer spell list
|
||||
TrainerSpellData const* cSpells = creature->GetTrainerSpells();
|
||||
if (!cSpells)
|
||||
if (!trainer || !trainer->IsTrainerValidForPlayer(bot))
|
||||
return false;
|
||||
|
||||
std::vector<Trainer::Spell> trainer_spells = trainer->GetSpells();
|
||||
|
||||
if (trainer_spells.empty())
|
||||
{
|
||||
botAI->TellError("No spells can be learned from this trainer");
|
||||
return false;
|
||||
@@ -133,7 +128,7 @@ bool TrainerAction::Execute(Event event)
|
||||
|
||||
if (text.find("learn") != std::string::npos || sRandomPlayerbotMgr->IsRandomBot(bot) ||
|
||||
(sPlayerbotAIConfig->autoTrainSpells != "no" &&
|
||||
(creature->GetCreatureTemplate()->trainer_type != TRAINER_TYPE_TRADESKILLS ||
|
||||
(trainer->GetTrainerType() != Trainer::Type::Tradeskill ||
|
||||
!botAI->HasActivePlayerMaster()))) // Todo rewrite to only exclude start primary profession skills and make
|
||||
// config dependent.
|
||||
Iterate(creature, &TrainerAction::Learn, spells);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "Action.h"
|
||||
#include "ChatHelper.h"
|
||||
#include "Trainer.h"
|
||||
|
||||
class Creature;
|
||||
class PlayerbotAI;
|
||||
@@ -22,9 +23,9 @@ public:
|
||||
bool Execute(Event event) override;
|
||||
|
||||
private:
|
||||
typedef void (TrainerAction::*TrainerSpellAction)(uint32, TrainerSpell const*, std::ostringstream& msg);
|
||||
typedef void (TrainerAction::*TrainerSpellAction)(uint32, const Trainer::Spell, std::ostringstream& msg);
|
||||
void Iterate(Creature* creature, TrainerSpellAction action, SpellIds& spells);
|
||||
void Learn(uint32 cost, TrainerSpell const* tSpell, std::ostringstream& msg);
|
||||
void Learn(uint32 cost, const Trainer::Spell tSpell, std::ostringstream& msg);
|
||||
void TellHeader(Creature* creature);
|
||||
void TellFooter(uint32 totalCost);
|
||||
};
|
||||
|
||||
@@ -52,7 +52,7 @@ bool UseMeetingStoneAction::Execute(Event event)
|
||||
if (!goInfo || goInfo->entry != 179944)
|
||||
return false;
|
||||
|
||||
return Teleport(master, bot);
|
||||
return Teleport(master, bot, false);
|
||||
}
|
||||
|
||||
bool SummonAction::Execute(Event event)
|
||||
@@ -70,16 +70,16 @@ bool SummonAction::Execute(Event event)
|
||||
{
|
||||
// botAI->GetAiObjectContext()->GetValue<GuidVector>("prioritized targets")->Set({});
|
||||
AI_VALUE(std::list<FleeInfo>&, "recently flee info").clear();
|
||||
return Teleport(master, bot);
|
||||
return Teleport(master, bot, true);
|
||||
}
|
||||
|
||||
if (SummonUsingGos(master, bot) || SummonUsingNpcs(master, bot))
|
||||
if (SummonUsingGos(master, bot, true) || SummonUsingNpcs(master, bot, true))
|
||||
{
|
||||
botAI->TellMasterNoFacing("Hello!");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (SummonUsingGos(bot, master) || SummonUsingNpcs(bot, master))
|
||||
if (SummonUsingGos(bot, master, true) || SummonUsingNpcs(bot, master, true))
|
||||
{
|
||||
botAI->TellMasterNoFacing("Welcome!");
|
||||
return true;
|
||||
@@ -88,7 +88,7 @@ bool SummonAction::Execute(Event event)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SummonAction::SummonUsingGos(Player* summoner, Player* player)
|
||||
bool SummonAction::SummonUsingGos(Player* summoner, Player* player, bool preserveAuras)
|
||||
{
|
||||
std::list<GameObject*> targets;
|
||||
AnyGameObjectInObjectRangeCheck u_check(summoner, sPlayerbotAIConfig->sightDistance);
|
||||
@@ -98,14 +98,14 @@ bool SummonAction::SummonUsingGos(Player* summoner, Player* player)
|
||||
for (GameObject* go : targets)
|
||||
{
|
||||
if (go->isSpawned() && go->GetGoType() == GAMEOBJECT_TYPE_MEETINGSTONE)
|
||||
return Teleport(summoner, player);
|
||||
return Teleport(summoner, player, preserveAuras);
|
||||
}
|
||||
|
||||
botAI->TellError(summoner == bot ? "There is no meeting stone nearby" : "There is no meeting stone near you");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SummonAction::SummonUsingNpcs(Player* summoner, Player* player)
|
||||
bool SummonAction::SummonUsingNpcs(Player* summoner, Player* player, bool preserveAuras)
|
||||
{
|
||||
if (!sPlayerbotAIConfig->summonAtInnkeepersEnabled)
|
||||
return false;
|
||||
@@ -139,7 +139,7 @@ bool SummonAction::SummonUsingNpcs(Player* summoner, Player* player)
|
||||
Spell spell(player, spellInfo, TRIGGERED_NONE);
|
||||
spell.SendSpellCooldown();
|
||||
|
||||
return Teleport(summoner, player);
|
||||
return Teleport(summoner, player, preserveAuras);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ bool SummonAction::SummonUsingNpcs(Player* summoner, Player* player)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SummonAction::Teleport(Player* summoner, Player* player)
|
||||
bool SummonAction::Teleport(Player* summoner, Player* player, bool preserveAuras)
|
||||
{
|
||||
// Player* master = GetMaster();
|
||||
if (!summoner)
|
||||
@@ -208,7 +208,11 @@ bool SummonAction::Teleport(Player* summoner, Player* player)
|
||||
|
||||
player->GetMotionMaster()->Clear();
|
||||
AI_VALUE(LastMovement&, "last movement").clear();
|
||||
player->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
|
||||
|
||||
if (!preserveAuras)
|
||||
player->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED |
|
||||
AURA_INTERRUPT_FLAG_CHANGE_MAP);
|
||||
|
||||
player->TeleportTo(mapId, x, y, z, 0);
|
||||
|
||||
if (botAI->HasStrategy("stay", botAI->GetState()))
|
||||
|
||||
@@ -19,9 +19,9 @@ public:
|
||||
bool Execute(Event event) override;
|
||||
|
||||
protected:
|
||||
bool Teleport(Player* summoner, Player* player);
|
||||
bool SummonUsingGos(Player* summoner, Player* player);
|
||||
bool SummonUsingNpcs(Player* summoner, Player* player);
|
||||
bool Teleport(Player* summoner, Player* player, bool preserveAuras);
|
||||
bool SummonUsingGos(Player* summoner, Player* player, bool preserveAuras);
|
||||
bool SummonUsingNpcs(Player* summoner, Player* player, bool preserveAuras);
|
||||
};
|
||||
|
||||
class UseMeetingStoneAction : public SummonAction
|
||||
|
||||
@@ -12,25 +12,10 @@ class BloodDKStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
|
||||
public:
|
||||
BloodDKStrategyActionNodeFactory()
|
||||
{
|
||||
// creators["melee"] = &melee;
|
||||
// creators["blood strike"] = &blood_strike;
|
||||
creators["rune strike"] = &rune_strike;
|
||||
creators["heart strike"] = &heart_strike;
|
||||
creators["death strike"] = &death_strike;
|
||||
// creators["death grip"] = &death_grip;
|
||||
// creators["plague strike"] = &plague_strike;
|
||||
// creators["pestilence"] = &pestilence;
|
||||
creators["icy touch"] = &icy_touch;
|
||||
// creators["obliterate"] = &obliterate;
|
||||
// creators["blood boil"] = &blood_boil;
|
||||
// creators["mark of_blood"] = &mark_of_blood;
|
||||
// creators["blood presence"] = &blood_presence;
|
||||
// creators["rune tap"] = &rune_tap;
|
||||
// creators["vampiric blood"] = &vampiric_blood;
|
||||
// creators["death pact"] = &death_pact;
|
||||
// creators["death rune_mastery"] = &death_rune_mastery;
|
||||
// creators["hysteria"] = &hysteria;
|
||||
// creators["dancing weapon"] = &dancing_weapon;
|
||||
creators["dark command"] = &dark_command;
|
||||
creators["taunt spell"] = &dark_command;
|
||||
}
|
||||
@@ -38,39 +23,61 @@ public:
|
||||
private:
|
||||
static ActionNode* rune_strike([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("rune strike",
|
||||
/*P*/ NextAction::array(0, new NextAction("frost presence"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"rune strike",
|
||||
{
|
||||
NextAction("frost presence")
|
||||
},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
static ActionNode* icy_touch([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("icy touch",
|
||||
/*P*/ NextAction::array(0, new NextAction("frost presence"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"icy touch",
|
||||
{
|
||||
NextAction("frost presence")
|
||||
},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
static ActionNode* heart_strike([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("heart strike",
|
||||
/*P*/ NextAction::array(0, new NextAction("frost presence"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"heart strike",
|
||||
{
|
||||
NextAction("frost presence")
|
||||
},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* death_strike([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("death strike",
|
||||
/*P*/ NextAction::array(0, new NextAction("frost presence"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"death strike",
|
||||
{
|
||||
NextAction("frost presence")
|
||||
},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
static ActionNode* dark_command([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("dark command",
|
||||
/*P*/ NextAction::array(0, new NextAction("frost presence"), NULL),
|
||||
/*A*/ NextAction::array(0, new NextAction("death grip"), NULL),
|
||||
/*C*/ NULL);
|
||||
return new ActionNode(
|
||||
"dark command",
|
||||
{
|
||||
NextAction("frost presence")
|
||||
},
|
||||
/*A*/ {
|
||||
NextAction("death grip")
|
||||
},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -79,33 +86,80 @@ BloodDKStrategy::BloodDKStrategy(PlayerbotAI* botAI) : GenericDKStrategy(botAI)
|
||||
actionNodeFactories.Add(new BloodDKStrategyActionNodeFactory());
|
||||
}
|
||||
|
||||
NextAction** BloodDKStrategy::getDefaultActions()
|
||||
std::vector<NextAction> BloodDKStrategy::getDefaultActions()
|
||||
{
|
||||
return NextAction::array(
|
||||
0, new NextAction("rune strike", ACTION_DEFAULT + 0.8f), new NextAction("icy touch", ACTION_DEFAULT + 0.7f),
|
||||
new NextAction("heart strike", ACTION_DEFAULT + 0.6f), new NextAction("blood strike", ACTION_DEFAULT + 0.5f),
|
||||
new NextAction("dancing rune weapon", ACTION_DEFAULT + 0.4f),
|
||||
new NextAction("death coil", ACTION_DEFAULT + 0.3f), new NextAction("plague strike", ACTION_DEFAULT + 0.2f),
|
||||
new NextAction("horn of winter", ACTION_DEFAULT + 0.1f), new NextAction("melee", ACTION_DEFAULT), NULL);
|
||||
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::array(0, new NextAction("rune strike", ACTION_NORMAL + 3), nullptr)));
|
||||
triggers.push_back(
|
||||
new TriggerNode("blood tap", NextAction::array(0, new NextAction("blood tap", ACTION_HIGH + 5), nullptr)));
|
||||
new TriggerNode(
|
||||
"rune strike",
|
||||
{
|
||||
NextAction("rune strike", ACTION_NORMAL + 3)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode("lose aggro", NextAction::array(0, new NextAction("dark command", ACTION_HIGH + 3), nullptr)));
|
||||
new TriggerNode(
|
||||
"blood tap",
|
||||
{
|
||||
NextAction("blood tap", ACTION_HIGH + 5)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode("low health", NextAction::array(0, new NextAction("army of the dead", ACTION_HIGH + 4),
|
||||
new NextAction("death strike", ACTION_HIGH + 3), nullptr)));
|
||||
new TriggerNode(
|
||||
"lose aggro",
|
||||
{
|
||||
NextAction("dark command", ACTION_HIGH + 3)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode("critical health", NextAction::array(0, new NextAction("vampiric blood", ACTION_HIGH + 5), nullptr)));
|
||||
new TriggerNode(
|
||||
"low health",
|
||||
{
|
||||
NextAction("army of the dead", ACTION_HIGH + 4),
|
||||
NextAction("death strike", ACTION_HIGH + 3)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode("icy touch", NextAction::array(0, new NextAction("icy touch", ACTION_HIGH + 2), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"plague strike", NextAction::array(0, new NextAction("plague strike", ACTION_HIGH + 2), nullptr)));
|
||||
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)
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ public:
|
||||
|
||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
std::string const getName() override { return "blood"; }
|
||||
NextAction** getDefaultActions() override;
|
||||
std::vector<NextAction> getDefaultActions() override;
|
||||
uint32 GetType() const override { return STRATEGY_TYPE_TANK | STRATEGY_TYPE_MELEE; }
|
||||
};
|
||||
|
||||
|
||||
@@ -11,39 +11,40 @@
|
||||
#include "SpellInfo.h"
|
||||
#include "SpellMgr.h"
|
||||
|
||||
NextAction** CastDeathchillAction::getPrerequisites()
|
||||
std::vector<NextAction> CastDeathchillAction::getPrerequisites()
|
||||
{
|
||||
return NextAction::merge(NextAction::array(0, new NextAction("frost presence"), nullptr),
|
||||
return NextAction::merge({ NextAction("frost presence") },
|
||||
CastSpellAction::getPrerequisites());
|
||||
}
|
||||
|
||||
NextAction** CastUnholyMeleeSpellAction::getPrerequisites()
|
||||
std::vector<NextAction> CastUnholyMeleeSpellAction::getPrerequisites()
|
||||
{
|
||||
return NextAction::merge(NextAction::array(0, new NextAction("unholy presence"), nullptr),
|
||||
return NextAction::merge({ NextAction("unholy presence") },
|
||||
CastMeleeSpellAction::getPrerequisites());
|
||||
}
|
||||
|
||||
NextAction** CastFrostMeleeSpellAction::getPrerequisites()
|
||||
std::vector<NextAction> CastFrostMeleeSpellAction::getPrerequisites()
|
||||
{
|
||||
return NextAction::merge(NextAction::array(0, new NextAction("frost presence"), nullptr),
|
||||
return NextAction::merge({ NextAction("frost presence") },
|
||||
CastMeleeSpellAction::getPrerequisites());
|
||||
}
|
||||
|
||||
NextAction** CastBloodMeleeSpellAction::getPrerequisites()
|
||||
std::vector<NextAction> CastBloodMeleeSpellAction::getPrerequisites()
|
||||
{
|
||||
return NextAction::merge(NextAction::array(0, new NextAction("blood presence"), nullptr),
|
||||
return NextAction::merge({ NextAction("blood presence") },
|
||||
CastMeleeSpellAction::getPrerequisites());
|
||||
}
|
||||
|
||||
bool CastRaiseDeadAction::Execute(Event event)
|
||||
{
|
||||
bool result = CastBuffSpellAction::Execute(event);
|
||||
const bool result = CastBuffSpellAction::Execute(event);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
uint32 spellId = AI_VALUE2(uint32, "spell id", spell);
|
||||
// SpellInfo const *spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||
|
||||
const uint32_t spellId = AI_VALUE2(uint32_t, "spell id", spell);
|
||||
|
||||
bot->AddSpellCooldown(spellId, 0, 3 * 60 * 1000);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ class CastDeathchillAction : public CastBuffSpellAction
|
||||
public:
|
||||
CastDeathchillAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "deathchill") {}
|
||||
|
||||
NextAction** getPrerequisites() override;
|
||||
std::vector<NextAction> getPrerequisites() override;
|
||||
};
|
||||
|
||||
class CastDarkCommandAction : public CastSpellAction
|
||||
@@ -52,7 +52,7 @@ class CastUnholyMeleeSpellAction : public CastMeleeSpellAction
|
||||
public:
|
||||
CastUnholyMeleeSpellAction(PlayerbotAI* botAI, std::string const spell) : CastMeleeSpellAction(botAI, spell) {}
|
||||
|
||||
NextAction** getPrerequisites() override;
|
||||
std::vector<NextAction> getPrerequisites() override;
|
||||
};
|
||||
|
||||
// Frost presence
|
||||
@@ -61,7 +61,7 @@ class CastFrostMeleeSpellAction : public CastMeleeSpellAction
|
||||
public:
|
||||
CastFrostMeleeSpellAction(PlayerbotAI* botAI, std::string const spell) : CastMeleeSpellAction(botAI, spell) {}
|
||||
|
||||
NextAction** getPrerequisites() override;
|
||||
std::vector<NextAction> getPrerequisites() override;
|
||||
};
|
||||
|
||||
// Blood presence
|
||||
@@ -70,7 +70,7 @@ class CastBloodMeleeSpellAction : public CastMeleeSpellAction
|
||||
public:
|
||||
CastBloodMeleeSpellAction(PlayerbotAI* botAI, std::string const spell) : CastMeleeSpellAction(botAI, spell) {}
|
||||
|
||||
NextAction** getPrerequisites() override;
|
||||
std::vector<NextAction> getPrerequisites() override;
|
||||
};
|
||||
|
||||
class CastRuneStrikeAction : public CastMeleeSpellAction
|
||||
@@ -79,10 +79,6 @@ public:
|
||||
CastRuneStrikeAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "rune strike") {}
|
||||
};
|
||||
|
||||
// debuff
|
||||
// BEGIN_DEBUFF_ACTION(CastPestilenceAction, "pestilence")
|
||||
// END_SPELL_ACTION()
|
||||
|
||||
class CastPestilenceAction : public CastSpellAction
|
||||
{
|
||||
public:
|
||||
@@ -90,20 +86,12 @@ public:
|
||||
ActionThreatType getThreatType() override { return ActionThreatType::None; }
|
||||
};
|
||||
|
||||
// debuff
|
||||
// BEGIN_DEBUFF_ACTION(CastHowlingBlastAction, "howling blast")
|
||||
// END_SPELL_ACTION()
|
||||
|
||||
class CastHowlingBlastAction : public CastSpellAction
|
||||
{
|
||||
public:
|
||||
CastHowlingBlastAction(PlayerbotAI* ai) : CastSpellAction(ai, "howling blast") {}
|
||||
};
|
||||
|
||||
// debuff it
|
||||
// BEGIN_DEBUFF_ACTION(CastIcyTouchAction, "icy touch")
|
||||
// END_SPELL_ACTION()
|
||||
|
||||
class CastIcyTouchAction : public CastSpellAction
|
||||
{
|
||||
public:
|
||||
@@ -126,8 +114,6 @@ class CastPlagueStrikeAction : public CastSpellAction
|
||||
public:
|
||||
CastPlagueStrikeAction(PlayerbotAI* ai) : CastSpellAction(ai, "plague strike") {}
|
||||
};
|
||||
// BEGIN_DEBUFF_ACTION(CastPlagueStrikeAction, "plague strike")
|
||||
// END_SPELL_ACTION()
|
||||
|
||||
class CastPlagueStrikeOnAttackerAction : public CastDebuffSpellOnMeleeAttackerAction
|
||||
{
|
||||
|
||||
@@ -16,66 +16,68 @@ public:
|
||||
creators["obliterate"] = &obliterate;
|
||||
creators["howling blast"] = &howling_blast;
|
||||
creators["frost strike"] = &frost_strike;
|
||||
// creators["chains of ice"] = &chains_of_ice;
|
||||
creators["rune strike"] = &rune_strike;
|
||||
// creators["icy clutch"] = &icy_clutch;
|
||||
// creators["horn of winter"] = &horn_of_winter;
|
||||
// creators["killing machine"] = &killing_machine;
|
||||
// creators["frost presence"] = &frost_presence;
|
||||
// creators["deathchill"] = &deathchill;
|
||||
// creators["icebound fortitude"] = &icebound_fortitude;
|
||||
// creators["mind freeze"] = &mind_freeze;
|
||||
// creators["hungering cold"] = &hungering_cold;
|
||||
creators["unbreakable armor"] = &unbreakable_armor;
|
||||
// creators["improved icy talons"] = &improved_icy_talons;
|
||||
}
|
||||
|
||||
private:
|
||||
static ActionNode* icy_touch([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("icy touch",
|
||||
/*P*/ NextAction::array(0, new NextAction("blood presence"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"icy touch",
|
||||
/*P*/ { NextAction("blood presence") },
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* obliterate([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("obliterate",
|
||||
/*P*/ NextAction::array(0, new NextAction("blood presence"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
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::array(0, new NextAction("blood presence"), nullptr),
|
||||
/*A*/ NextAction::array(0, new NextAction("melee"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
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::array(0, new NextAction("blood presence"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
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::array(0, new NextAction("blood presence"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
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::array(0, new NextAction("blood tap"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"unbreakable armor",
|
||||
/*P*/ { NextAction("blood tap") },
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -84,41 +86,84 @@ FrostDKStrategy::FrostDKStrategy(PlayerbotAI* botAI) : GenericDKStrategy(botAI)
|
||||
actionNodeFactories.Add(new FrostDKStrategyActionNodeFactory());
|
||||
}
|
||||
|
||||
NextAction** FrostDKStrategy::getDefaultActions()
|
||||
std::vector<NextAction> FrostDKStrategy::getDefaultActions()
|
||||
{
|
||||
return NextAction::array(
|
||||
0, new NextAction("obliterate", ACTION_DEFAULT + 0.7f),
|
||||
new NextAction("frost strike", ACTION_DEFAULT + 0.4f),
|
||||
new NextAction("empower rune weapon", ACTION_DEFAULT + 0.3f),
|
||||
new NextAction("horn of winter", ACTION_DEFAULT + 0.1f), new NextAction("melee", ACTION_DEFAULT), NULL);
|
||||
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::array(0, new NextAction("unbreakable armor", ACTION_DEFAULT + 0.6f), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"freezing fog", NextAction::array(0, new NextAction("howling blast", ACTION_DEFAULT + 0.5f), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"high blood rune", NextAction::array(0, new NextAction("blood strike", ACTION_DEFAULT + 0.2f), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"army of the dead", NextAction::array(0, new NextAction("army of the dead", ACTION_HIGH + 6), nullptr)));
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"unbreakable armor",
|
||||
{
|
||||
NextAction("unbreakable armor", ACTION_DEFAULT + 0.6f)
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode("icy touch", NextAction::array(0, new NextAction("icy touch", ACTION_HIGH + 2), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"plague strike", NextAction::array(0, new NextAction("plague strike", ACTION_HIGH + 2), nullptr)));
|
||||
// triggers.push_back(new TriggerNode("empower rune weapon", NextAction::array(0, new NextAction("empower rune
|
||||
// weapon", ACTION_NORMAL + 4), nullptr)));
|
||||
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::array(0, new NextAction("howling blast", ACTION_HIGH + 4), nullptr)));
|
||||
new TriggerNode(
|
||||
"medium aoe",
|
||||
{
|
||||
NextAction("howling blast", ACTION_HIGH + 4)
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ public:
|
||||
|
||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
std::string const getName() override { return "frost"; }
|
||||
NextAction** getDefaultActions() override;
|
||||
std::vector<NextAction> getDefaultActions() override;
|
||||
uint32 GetType() const override { return STRATEGY_TYPE_COMBAT | STRATEGY_TYPE_DPS | STRATEGY_TYPE_MELEE; }
|
||||
};
|
||||
|
||||
|
||||
@@ -20,17 +20,17 @@ private:
|
||||
static ActionNode* bone_shield([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("bone shield",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* horn_of_winter([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("horn of winter",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -44,19 +44,18 @@ void GenericDKNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
|
||||
NonCombatStrategy::InitTriggers(triggers);
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode("no pet", NextAction::array(0, new NextAction("raise dead", ACTION_NORMAL + 1), nullptr)));
|
||||
new TriggerNode("no pet", { NextAction("raise dead", ACTION_NORMAL + 1) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("horn of winter", NextAction::array(0, new NextAction("horn of winter", 21.0f), nullptr)));
|
||||
new TriggerNode("horn of winter", { NextAction("horn of winter", 21.0f) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("bone shield", NextAction::array(0, new NextAction("bone shield", 21.0f), nullptr)));
|
||||
new TriggerNode("bone shield", { NextAction("bone shield", 21.0f) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("has pet", NextAction::array(0, new NextAction("toggle pet spell", 60.0f), NULL)));
|
||||
new TriggerNode("has pet", { NextAction("toggle pet spell", 60.0f) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("new pet", NextAction::array(0, new NextAction("set pet stance", 60.0f), NULL)));
|
||||
new TriggerNode("new pet", { NextAction("set pet stance", 60.0f) }));
|
||||
}
|
||||
|
||||
void DKBuffDpsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
// triggers.push_back(new TriggerNode("improved icy talons", NextAction::array(0, new NextAction("improved icy
|
||||
// talons", 19.0f), nullptr)));
|
||||
|
||||
}
|
||||
|
||||
@@ -54,105 +54,105 @@ private:
|
||||
static ActionNode* death_coil([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("death coil",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* death_grip([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("death grip",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("icy touch"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("icy touch") },
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* plague_strike([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("plague strike",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* icy_touch([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("icy touch",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* heart_strike([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("heart strike",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* pestilence([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("pestilence",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* horn_of_winter([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("horn of winter",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* bone_shield([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("bone shield",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* killing_machine([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("killing machine",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("improved icy talons"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("improved icy talons") },
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* corpse_explosion([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("corpse explosion",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* death_and_decay([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("death and decay",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* anti_magic_zone([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("anti magic zone",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("anti magic shell"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("anti magic shell") },
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* icebound_fortitude([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("icebound fortitude",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -165,36 +165,29 @@ void GenericDKStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
MeleeCombatStrategy::InitTriggers(triggers);
|
||||
|
||||
// triggers.push_back(new TriggerNode("high aoe", NextAction::array(0, new NextAction("anti magic shell",
|
||||
// ACTION_NORMAL + 3), nullptr))); triggers.push_back(new TriggerNode("death coil", NextAction::array(0, new
|
||||
// NextAction("death coil", ACTION_NORMAL + 3), nullptr))); triggers.push_back(new TriggerNode("critical aoe heal",
|
||||
// NextAction::array(0, new NextAction("anti magic zone", ACTION_EMERGENCY + 1), nullptr)));
|
||||
triggers.push_back(
|
||||
new TriggerNode("no pet", NextAction::array(0, new NextAction("raise dead", ACTION_NORMAL + 5), nullptr)));
|
||||
new TriggerNode("no pet", { NextAction("raise dead", ACTION_NORMAL + 5) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("has pet", NextAction::array(0, new NextAction("toggle pet spell", 60.0f), nullptr)));
|
||||
new TriggerNode("has pet", { NextAction("toggle pet spell", 60.0f) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("new pet", NextAction::array(0, new NextAction("set pet stance", 60.0f), nullptr)));
|
||||
new TriggerNode("new pet", { NextAction("set pet stance", 60.0f) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("mind freeze", NextAction::array(0, new NextAction("mind freeze", ACTION_HIGH + 1), nullptr)));
|
||||
new TriggerNode("mind freeze", { NextAction("mind freeze", ACTION_HIGH + 1) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("mind freeze on enemy healer",
|
||||
NextAction::array(0, new NextAction("mind freeze on enemy healer", ACTION_HIGH + 1), nullptr)));
|
||||
{ NextAction("mind freeze on enemy healer", ACTION_HIGH + 1) }));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"horn of winter", NextAction::array(0, new NextAction("horn of winter", ACTION_NORMAL + 1), nullptr)));
|
||||
"horn of winter", { NextAction("horn of winter", ACTION_NORMAL + 1) }));
|
||||
triggers.push_back(new TriggerNode("critical health",
|
||||
NextAction::array(0, new NextAction("death pact", ACTION_HIGH + 5), nullptr)));
|
||||
{ NextAction("death pact", ACTION_HIGH + 5) }));
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode("low health", NextAction::array(0, new NextAction("icebound fortitude", ACTION_HIGH + 5),
|
||||
new NextAction("rune tap", ACTION_HIGH + 4), nullptr)));
|
||||
new TriggerNode("low health", { NextAction("icebound fortitude", ACTION_HIGH + 5),
|
||||
NextAction("rune tap", ACTION_HIGH + 4) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("medium aoe", NextAction::array(0, new NextAction("death and decay", ACTION_HIGH + 9),
|
||||
new NextAction("pestilence", ACTION_NORMAL + 4),
|
||||
new NextAction("blood boil", ACTION_NORMAL + 3), nullptr)));
|
||||
// triggers.push_back(new TriggerNode("light aoe", NextAction::array(0,
|
||||
// new NextAction("pestilence", ACTION_NORMAL + 4),
|
||||
// nullptr)));
|
||||
new TriggerNode("medium aoe", { NextAction("death and decay", ACTION_HIGH + 9),
|
||||
NextAction("pestilence", ACTION_NORMAL + 4),
|
||||
NextAction("blood boil", ACTION_NORMAL + 3) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("pestilence glyph", NextAction::array(0, new NextAction("pestilence", ACTION_HIGH + 9), NULL)));
|
||||
new TriggerNode("pestilence glyph", { NextAction("pestilence", ACTION_HIGH + 9) }));
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#/*
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@@ -11,21 +11,8 @@ class UnholyDKStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
|
||||
public:
|
||||
UnholyDKStrategyActionNodeFactory()
|
||||
{
|
||||
// Unholy
|
||||
// creators["bone shield"] = &bone_shield;
|
||||
// creators["plague strike"] = &plague_strike;
|
||||
// creators["death grip"] = &death_grip;
|
||||
// creators["death coil"] = &death_coil;
|
||||
creators["death strike"] = &death_strike;
|
||||
// creators["unholy blight"] = &unholy_blight;
|
||||
creators["scourge strike"] = &scourge_strike;
|
||||
// creators["death and decay"] = &death_and_decay;
|
||||
// creators["unholy pressence"] = &unholy_pressence;
|
||||
// creators["raise dead"] = &raise_dead;
|
||||
// creators["army of the dead"] = &army of the dead;
|
||||
// creators["summon gargoyle"] = &army of the dead;
|
||||
// creators["anti magic shell"] = &anti_magic_shell;
|
||||
// creators["anti magic zone"] = &anti_magic_zone;
|
||||
creators["ghoul frenzy"] = &ghoul_frenzy;
|
||||
creators["corpse explosion"] = &corpse_explosion;
|
||||
creators["icy touch"] = &icy_touch;
|
||||
@@ -34,39 +21,49 @@ public:
|
||||
private:
|
||||
static ActionNode* death_strike([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("death strike",
|
||||
/*P*/ NextAction::array(0, new NextAction("blood presence"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
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::array(0, new NextAction("blood presence"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
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::array(0, new NextAction("blood presence"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
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::array(0, new NextAction("blood presence"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
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::array(0, new NextAction("blood presence"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"icy touch",
|
||||
/*P*/ { NextAction("blood presence") },
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -75,69 +72,121 @@ UnholyDKStrategy::UnholyDKStrategy(PlayerbotAI* botAI) : GenericDKStrategy(botAI
|
||||
actionNodeFactories.Add(new UnholyDKStrategyActionNodeFactory());
|
||||
}
|
||||
|
||||
NextAction** UnholyDKStrategy::getDefaultActions()
|
||||
std::vector<NextAction> UnholyDKStrategy::getDefaultActions()
|
||||
{
|
||||
return NextAction::array(
|
||||
0, new NextAction("death and decay", ACTION_HIGH + 5),
|
||||
new NextAction("summon gargoyle", ACTION_DEFAULT + 0.4f),
|
||||
// new NextAction("empower rune weapon", ACTION_DEFAULT + 0.3f),
|
||||
new NextAction("horn of winter", ACTION_DEFAULT + 0.2f),
|
||||
new NextAction("death coil", ACTION_DEFAULT + 0.1f),
|
||||
new NextAction("melee", ACTION_DEFAULT), nullptr);
|
||||
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::array(0,
|
||||
new NextAction("ghoul frenzy", ACTION_DEFAULT + 0.9f),
|
||||
new NextAction("scourge strike", ACTION_DEFAULT + 0.8f),
|
||||
new NextAction("icy touch", ACTION_DEFAULT + 0.7f),
|
||||
new NextAction("blood strike", ACTION_DEFAULT + 0.6f),
|
||||
new NextAction("plague strike", ACTION_DEFAULT + 0.5f),
|
||||
nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode("dd cd and no desolation",
|
||||
NextAction::array(0, new NextAction("blood strike", ACTION_DEFAULT + 0.75f), nullptr)));
|
||||
|
||||
// triggers.push_back(
|
||||
// new TriggerNode("icy touch", NextAction::array(0, new NextAction("icy touch", ACTION_HIGH + 2), nullptr)));
|
||||
// triggers.push_back(new TriggerNode(
|
||||
// "plague strike", NextAction::array(0, new NextAction("plague strike", ACTION_HIGH + 1), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"high frost rune", NextAction::array(0,
|
||||
new NextAction("icy touch", ACTION_NORMAL + 3), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"high blood rune", NextAction::array(0, new NextAction("blood strike", ACTION_NORMAL + 2), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"high unholy rune", NextAction::array(0,
|
||||
new NextAction("plague strike", ACTION_NORMAL + 1), nullptr)));
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode("dd cd and plague strike 3s", NextAction::array(0, new NextAction("plague strike", ACTION_HIGH + 1), nullptr)));
|
||||
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 icy touch 3s", NextAction::array(0, new NextAction("icy touch", ACTION_HIGH + 2), nullptr)));
|
||||
|
||||
new TriggerNode(
|
||||
"dd cd and no desolation",
|
||||
{
|
||||
NextAction("blood strike", ACTION_DEFAULT + 0.75f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode("no rune", NextAction::array(0, new NextAction("empower rune weapon", ACTION_HIGH + 1), nullptr)));
|
||||
|
||||
// triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction(, ACTION_NORMAL + 2), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"army of the dead", NextAction::array(0, new NextAction("army of the dead", ACTION_HIGH + 6), nullptr)));
|
||||
new TriggerNode(
|
||||
"high frost rune",
|
||||
{
|
||||
NextAction("icy touch", ACTION_NORMAL + 3)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode("bone shield", NextAction::array(0, new NextAction("bone shield", ACTION_HIGH + 3), nullptr)));
|
||||
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::array(0, new NextAction("corpse explosion", ACTION_NORMAL + 1), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"medium aoe", NextAction::array(0, new NextAction("death and decay", ACTION_NORMAL + 3),
|
||||
new NextAction("corpse explosion", ACTION_NORMAL + 3), nullptr)));
|
||||
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)
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ public:
|
||||
|
||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
std::string const getName() override { return "unholy"; }
|
||||
NextAction** getDefaultActions() override;
|
||||
std::vector<NextAction> getDefaultActions() override;
|
||||
uint32 GetType() const override { return STRATEGY_TYPE_COMBAT | STRATEGY_TYPE_DPS | STRATEGY_TYPE_MELEE; }
|
||||
};
|
||||
|
||||
|
||||
@@ -30,107 +30,132 @@ public:
|
||||
private:
|
||||
static ActionNode* melee([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("melee",
|
||||
/*P*/ NextAction::array(0, new NextAction("feral charge - bear"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
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*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("reach melee"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
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*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
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::array(0, new NextAction("feral charge - bear"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
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*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
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::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ NextAction::array(0, new NextAction("bear form"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
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*/ nullptr,
|
||||
// /*A*/ NextAction::array(0, new NextAction("lacerate"), nullptr),
|
||||
nullptr,
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"mangle (bear)",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* maul([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("maul",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("melee"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"maul",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("melee") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* bash([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("bash",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("melee"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"bash",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("melee") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* swipe([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("swipe",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("melee"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"swipe",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("melee") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* lacerate([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("lacerate",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("maul"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"lacerate",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("maul") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* growl([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("growl",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"growl",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* demoralizing_roar([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("demoralizing roar",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"demoralizing roar",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -139,38 +164,93 @@ BearTankDruidStrategy::BearTankDruidStrategy(PlayerbotAI* botAI) : FeralDruidStr
|
||||
actionNodeFactories.Add(new BearTankDruidStrategyActionNodeFactory());
|
||||
}
|
||||
|
||||
NextAction** BearTankDruidStrategy::getDefaultActions()
|
||||
std::vector<NextAction> BearTankDruidStrategy::getDefaultActions()
|
||||
{
|
||||
return NextAction::array(
|
||||
0, new NextAction("mangle (bear)", ACTION_DEFAULT + 0.5f),
|
||||
new NextAction("faerie fire (feral)", ACTION_DEFAULT + 0.4f), new NextAction("lacerate", ACTION_DEFAULT + 0.3f),
|
||||
new NextAction("maul", ACTION_DEFAULT + 0.2f), new NextAction("enrage", ACTION_DEFAULT + 0.1f),
|
||||
new NextAction("melee", ACTION_DEFAULT), nullptr);
|
||||
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::array(0, new NextAction("feral charge - bear", ACTION_NORMAL + 8), nullptr)));
|
||||
// triggers.push_back(new TriggerNode("thorns", NextAction::array(0, new NextAction("thorns", ACTION_HIGH + 9),
|
||||
// nullptr)));
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode("bear form", NextAction::array(0, new NextAction("dire bear form", ACTION_HIGH + 8), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"low health", NextAction::array(0, new NextAction("frenzied regeneration", ACTION_HIGH + 7), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"faerie fire (feral)", NextAction::array(0, new NextAction("faerie fire (feral)", ACTION_HIGH + 7), nullptr)));
|
||||
new TriggerNode(
|
||||
"enemy out of melee",
|
||||
{
|
||||
NextAction("feral charge - bear", ACTION_NORMAL + 8)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode("lose aggro", NextAction::array(0, new NextAction("growl", ACTION_HIGH + 8), nullptr)));
|
||||
new TriggerNode(
|
||||
"bear form",
|
||||
{
|
||||
NextAction("dire bear form", ACTION_HIGH + 8)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode("medium aoe", NextAction::array(0, new NextAction("demoralizing roar", ACTION_HIGH + 6),
|
||||
new NextAction("swipe (bear)", ACTION_HIGH + 6), nullptr)));
|
||||
new TriggerNode(
|
||||
"low health",
|
||||
{
|
||||
NextAction("frenzied regeneration", ACTION_HIGH + 7)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode("light aoe", NextAction::array(0, new NextAction("swipe (bear)", ACTION_HIGH + 5), nullptr)));
|
||||
new TriggerNode(
|
||||
"faerie fire (feral)",
|
||||
{
|
||||
NextAction("faerie fire (feral)", ACTION_HIGH + 7)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode("bash", NextAction::array(0, new NextAction("bash", ACTION_INTERRUPT + 2), nullptr)));
|
||||
new TriggerNode(
|
||||
"lose aggro",
|
||||
{
|
||||
NextAction("growl", ACTION_HIGH + 8)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode("bash on enemy healer",
|
||||
NextAction::array(0, new NextAction("bash on enemy healer", ACTION_INTERRUPT + 1), nullptr)));
|
||||
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)
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ public:
|
||||
|
||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
std::string const getName() override { return "bear"; }
|
||||
NextAction** getDefaultActions() override;
|
||||
std::vector<NextAction> getDefaultActions() override;
|
||||
uint32 GetType() const override { return STRATEGY_TYPE_TANK | STRATEGY_TYPE_MELEE; }
|
||||
};
|
||||
|
||||
|
||||
@@ -28,82 +28,102 @@ public:
|
||||
private:
|
||||
static ActionNode* faerie_fire([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("faerie fire",
|
||||
/*P*/ NextAction::array(0, new NextAction("moonkin form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"faerie fire",
|
||||
/*P*/ { NextAction("moonkin form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* hibernate([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("hibernate",
|
||||
/*P*/ NextAction::array(0, new NextAction("moonkin form"), nullptr),
|
||||
/*A*/ NextAction::array(0, new NextAction("entangling roots"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
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::array(0, new NextAction("moonkin form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
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::array(0, new NextAction("moonkin form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
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::array(0, new NextAction("moonkin form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"wrath",
|
||||
/*P*/ { NextAction("moonkin form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* starfall([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("starfall",
|
||||
/*P*/ NextAction::array(0, new NextAction("moonkin form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
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::array(0, new NextAction("moonkin form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"insect swarm",
|
||||
/*P*/ { NextAction("moonkin form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* moonfire([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("moonfire",
|
||||
/*P*/ NextAction::array(0, new NextAction("moonkin form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"moonfire",
|
||||
/*P*/ { NextAction("moonkin form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* starfire([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("starfire",
|
||||
/*P*/ NextAction::array(0, new NextAction("moonkin form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
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::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"moonkin form",
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -113,55 +133,122 @@ CasterDruidStrategy::CasterDruidStrategy(PlayerbotAI* botAI) : GenericDruidStrat
|
||||
actionNodeFactories.Add(new ShapeshiftDruidStrategyActionNodeFactory());
|
||||
}
|
||||
|
||||
NextAction** CasterDruidStrategy::getDefaultActions()
|
||||
std::vector<NextAction> CasterDruidStrategy::getDefaultActions()
|
||||
{
|
||||
return NextAction::array(0,
|
||||
new NextAction("starfall", ACTION_HIGH + 1.0f),
|
||||
new NextAction("force of nature", ACTION_DEFAULT + 1.0f),
|
||||
new NextAction("wrath", ACTION_DEFAULT + 0.1f),
|
||||
// new NextAction("starfire", ACTION_NORMAL),
|
||||
nullptr);
|
||||
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("enemy out of spell", NextAction::array(0, new NextAction("reach spell",
|
||||
// ACTION_MOVE), nullptr)));
|
||||
triggers.push_back(new TriggerNode("eclipse (lunar) cooldown",
|
||||
NextAction::array(0, new NextAction("starfire", ACTION_DEFAULT + 0.2f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("eclipse (solar) cooldown",
|
||||
NextAction::array(0, new NextAction("wrath", ACTION_DEFAULT + 0.2f), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"insect swarm", NextAction::array(0, new NextAction("insect swarm", ACTION_NORMAL + 5), nullptr)));
|
||||
triggers.push_back(
|
||||
new TriggerNode("moonfire", NextAction::array(0, new NextAction("moonfire", ACTION_NORMAL + 4), nullptr)));
|
||||
new TriggerNode(
|
||||
"eclipse (lunar) cooldown",
|
||||
{
|
||||
NextAction("starfire", ACTION_DEFAULT + 0.2f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode("eclipse (solar)", NextAction::array(0, new NextAction("wrath", ACTION_NORMAL + 6), nullptr)));
|
||||
triggers.push_back(new TriggerNode("eclipse (lunar)",
|
||||
NextAction::array(0, new NextAction("starfire", ACTION_NORMAL + 6), nullptr)));
|
||||
new TriggerNode(
|
||||
"eclipse (solar) cooldown",
|
||||
{
|
||||
NextAction("wrath", ACTION_DEFAULT + 0.2f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode("medium mana", NextAction::array(0, new NextAction("innervate", ACTION_HIGH + 9), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode("enemy too close for spell",
|
||||
NextAction::array(0, new NextAction("flee", ACTION_MOVE + 9), nullptr)));
|
||||
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::array(0, new NextAction("cancel channel", ACTION_HIGH + 2), nullptr)));
|
||||
new TriggerNode(
|
||||
"hurricane channel check",
|
||||
{
|
||||
NextAction("cancel channel", ACTION_HIGH + 2)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode("medium aoe", NextAction::array(0, new NextAction("hurricane", ACTION_HIGH + 1), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"light aoe", NextAction::array(0, new NextAction("insect swarm on attacker", ACTION_NORMAL + 3),
|
||||
new NextAction("moonfire on attacker", ACTION_NORMAL + 3), NULL)));
|
||||
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::array(0, new NextAction("faerie fire", ACTION_HIGH), nullptr)));
|
||||
new TriggerNode(
|
||||
"faerie fire",
|
||||
{
|
||||
NextAction("faerie fire", ACTION_HIGH)
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ public:
|
||||
public:
|
||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
std::string const getName() override { return "caster"; }
|
||||
NextAction** getDefaultActions() override;
|
||||
std::vector<NextAction> getDefaultActions() override;
|
||||
uint32 GetType() const override { return STRATEGY_TYPE_COMBAT | STRATEGY_TYPE_DPS | STRATEGY_TYPE_RANGED; }
|
||||
};
|
||||
|
||||
|
||||
@@ -28,90 +28,112 @@ public:
|
||||
private:
|
||||
static ActionNode* faerie_fire_feral([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("faerie fire (feral)",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"faerie fire (feral)",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* melee([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("melee",
|
||||
/*P*/ NextAction::array(0, new NextAction("feral charge - cat"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
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*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("reach melee"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
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::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"cat form",
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* claw([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("claw",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("melee"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"claw",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("melee") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* mangle_cat([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("mangle (cat)",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"mangle (cat)",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* rake([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("rake",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"rake",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* ferocious_bite([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("ferocious bite",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("rip"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"ferocious bite",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("rip") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* rip([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("rip",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"rip",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* pounce([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("pounce",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("ravage"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"pounce",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("ravage") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* ravage([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("ravage",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("shred"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"ravage",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("shred") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -120,9 +142,11 @@ CatDpsDruidStrategy::CatDpsDruidStrategy(PlayerbotAI* botAI) : FeralDruidStrateg
|
||||
actionNodeFactories.Add(new CatDpsDruidStrategyActionNodeFactory());
|
||||
}
|
||||
|
||||
NextAction** CatDpsDruidStrategy::getDefaultActions()
|
||||
std::vector<NextAction> CatDpsDruidStrategy::getDefaultActions()
|
||||
{
|
||||
return NextAction::array(0, new NextAction("tiger's fury", ACTION_DEFAULT + 0.1f), nullptr);
|
||||
return {
|
||||
NextAction("tiger's fury", ACTION_DEFAULT + 0.1f)
|
||||
};
|
||||
}
|
||||
|
||||
void CatDpsDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
@@ -130,50 +154,161 @@ void CatDpsDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
FeralDruidStrategy::InitTriggers(triggers);
|
||||
|
||||
// Default priority
|
||||
triggers.push_back(new TriggerNode("almost full energy available",
|
||||
NextAction::array(0, new NextAction("shred", ACTION_DEFAULT + 0.4f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("combo points not full",
|
||||
NextAction::array(0, new NextAction("shred", ACTION_DEFAULT + 0.4f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("almost full energy available",
|
||||
NextAction::array(0, new NextAction("mangle (cat)", ACTION_DEFAULT + 0.3f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("combo points not full and high energy",
|
||||
NextAction::array(0, new NextAction("mangle (cat)", ACTION_DEFAULT + 0.3f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("almost full energy available",
|
||||
NextAction::array(0, new NextAction("claw", ACTION_DEFAULT + 0.2f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("combo points not full and high energy",
|
||||
NextAction::array(0, new NextAction("claw", ACTION_DEFAULT + 0.2f), nullptr)));
|
||||
triggers.push_back(
|
||||
new TriggerNode("faerie fire (feral)",
|
||||
NextAction::array(0, new NextAction("faerie fire (feral)", ACTION_DEFAULT + 0.0f), nullptr)));
|
||||
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::array(0, new NextAction("cat form", ACTION_HIGH + 8), nullptr)));
|
||||
new TriggerNode(
|
||||
"cat form", {
|
||||
NextAction("cat form", ACTION_HIGH + 8)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode("savage roar", NextAction::array(0, new NextAction("savage roar", ACTION_HIGH + 7), nullptr)));
|
||||
triggers.push_back(new TriggerNode("combo points available",
|
||||
NextAction::array(0, new NextAction("rip", ACTION_HIGH + 6), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"ferocious bite time", NextAction::array(0, new NextAction("ferocious bite", ACTION_HIGH + 5), nullptr)));
|
||||
new TriggerNode(
|
||||
"savage roar", {
|
||||
NextAction("savage roar", ACTION_HIGH + 7)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode("target with combo points almost dead",
|
||||
NextAction::array(0, new NextAction("ferocious bite", ACTION_HIGH + 4), nullptr)));
|
||||
triggers.push_back(new TriggerNode("mangle (cat)",
|
||||
NextAction::array(0, new NextAction("mangle (cat)", ACTION_HIGH + 3), nullptr)));
|
||||
triggers.push_back(new TriggerNode("rake", NextAction::array(0, new NextAction("rake", ACTION_HIGH + 2), nullptr)));
|
||||
new TriggerNode(
|
||||
"combo points available",
|
||||
{
|
||||
NextAction("rip", ACTION_HIGH + 6)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode("medium threat", NextAction::array(0, new NextAction("cower", ACTION_HIGH + 1), nullptr)));
|
||||
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::array(0, new NextAction("swipe (cat)", ACTION_HIGH + 3), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"light aoe", NextAction::array(0, new NextAction("rake on attacker", ACTION_HIGH + 2), nullptr)));
|
||||
// Reach target
|
||||
triggers.push_back(new TriggerNode(
|
||||
"enemy out of melee", NextAction::array(0, new NextAction("feral charge - cat", ACTION_HIGH + 9), nullptr)));
|
||||
new TriggerNode(
|
||||
"medium aoe",
|
||||
{
|
||||
NextAction("swipe (cat)", ACTION_HIGH + 3)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode("enemy out of melee", NextAction::array(0, new NextAction("dash", ACTION_HIGH + 8), nullptr)));
|
||||
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) {}
|
||||
|
||||
@@ -18,7 +18,7 @@ public:
|
||||
public:
|
||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
std::string const getName() override { return "cat"; }
|
||||
NextAction** getDefaultActions() override;
|
||||
std::vector<NextAction> getDefaultActions() override;
|
||||
uint32 GetType() const override { return STRATEGY_TYPE_COMBAT | STRATEGY_TYPE_MELEE; }
|
||||
};
|
||||
|
||||
|
||||
@@ -11,15 +11,15 @@
|
||||
#include "AoeValues.h"
|
||||
#include "TargetValue.h"
|
||||
|
||||
NextAction** CastAbolishPoisonAction::getAlternatives()
|
||||
std::vector<NextAction> CastAbolishPoisonAction::getAlternatives()
|
||||
{
|
||||
return NextAction::merge(NextAction::array(0, new NextAction("cure poison"), nullptr),
|
||||
return NextAction::merge({ NextAction("cure poison") },
|
||||
CastSpellAction::getPrerequisites());
|
||||
}
|
||||
|
||||
NextAction** CastAbolishPoisonOnPartyAction::getAlternatives()
|
||||
std::vector<NextAction> CastAbolishPoisonOnPartyAction::getAlternatives()
|
||||
{
|
||||
return NextAction::merge(NextAction::array(0, new NextAction("cure poison on party"), nullptr),
|
||||
return NextAction::merge({ NextAction("cure poison on party") },
|
||||
CastSpellAction::getPrerequisites());
|
||||
}
|
||||
|
||||
@@ -60,15 +60,15 @@ bool CastStarfallAction::isUseful()
|
||||
return true;
|
||||
}
|
||||
|
||||
NextAction** CastReviveAction::getPrerequisites()
|
||||
std::vector<NextAction> CastReviveAction::getPrerequisites()
|
||||
{
|
||||
return NextAction::merge(NextAction::array(0, new NextAction("caster form"), nullptr),
|
||||
return NextAction::merge({ NextAction("caster form") },
|
||||
ResurrectPartyMemberAction::getPrerequisites());
|
||||
}
|
||||
|
||||
NextAction** CastRebirthAction::getPrerequisites()
|
||||
std::vector<NextAction> CastRebirthAction::getPrerequisites()
|
||||
{
|
||||
return NextAction::merge(NextAction::array(0, new NextAction("caster form"), nullptr),
|
||||
return NextAction::merge({ NextAction("caster form") },
|
||||
ResurrectPartyMemberAction::getPrerequisites());
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ class CastReviveAction : public ResurrectPartyMemberAction
|
||||
public:
|
||||
CastReviveAction(PlayerbotAI* botAI) : ResurrectPartyMemberAction(botAI, "revive") {}
|
||||
|
||||
NextAction** getPrerequisites() override;
|
||||
std::vector<NextAction> getPrerequisites() override;
|
||||
};
|
||||
|
||||
class CastRebirthAction : public ResurrectPartyMemberAction
|
||||
@@ -82,7 +82,7 @@ class CastRebirthAction : public ResurrectPartyMemberAction
|
||||
public:
|
||||
CastRebirthAction(PlayerbotAI* botAI) : ResurrectPartyMemberAction(botAI, "rebirth") {}
|
||||
|
||||
NextAction** getPrerequisites() override;
|
||||
std::vector<NextAction> getPrerequisites() override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
@@ -223,7 +223,7 @@ class CastAbolishPoisonAction : public CastCureSpellAction
|
||||
{
|
||||
public:
|
||||
CastAbolishPoisonAction(PlayerbotAI* botAI) : CastCureSpellAction(botAI, "abolish poison") {}
|
||||
NextAction** getAlternatives() override;
|
||||
std::vector<NextAction> getAlternatives() override;
|
||||
};
|
||||
|
||||
class CastAbolishPoisonOnPartyAction : public CurePartyMemberAction
|
||||
@@ -233,7 +233,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
NextAction** getAlternatives() override;
|
||||
std::vector<NextAction> getAlternatives() override;
|
||||
};
|
||||
|
||||
class CastBarkskinAction : public CastBuffSpellAction
|
||||
|
||||
@@ -17,9 +17,9 @@ bool CastBearFormAction::isUseful()
|
||||
return CastBuffSpellAction::isUseful() && !botAI->HasAura("dire bear form", GetTarget());
|
||||
}
|
||||
|
||||
NextAction** CastDireBearFormAction::getAlternatives()
|
||||
std::vector<NextAction> CastDireBearFormAction::getAlternatives()
|
||||
{
|
||||
return NextAction::merge(NextAction::array(0, new NextAction("bear form"), nullptr),
|
||||
return NextAction::merge({ NextAction("bear form") },
|
||||
CastSpellAction::getAlternatives());
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ class CastDireBearFormAction : public CastBuffSpellAction
|
||||
public:
|
||||
CastDireBearFormAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "dire bear form") {}
|
||||
|
||||
NextAction** getAlternatives() override;
|
||||
std::vector<NextAction> getAlternatives() override;
|
||||
};
|
||||
|
||||
class CastCatFormAction : public CastBuffSpellAction
|
||||
|
||||
@@ -26,65 +26,65 @@ private:
|
||||
static ActionNode* survival_instincts([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("survival instincts",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("barkskin"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("barkskin") },
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* thorns([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("thorns",
|
||||
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* omen_of_clarity([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("omen of clarity",
|
||||
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* cure_poison([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("cure poison",
|
||||
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* cure_poison_on_party([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("cure poison on party",
|
||||
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* abolish_poison([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("abolish poison",
|
||||
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*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::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* prowl([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("prowl",
|
||||
/*P*/ NextAction::array(0, new NextAction("cat form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*P*/ { NextAction("cat form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -98,20 +98,16 @@ void FeralDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
GenericDruidStrategy::InitTriggers(triggers);
|
||||
|
||||
// triggers.push_back(new TriggerNode("not facing target", NextAction::array(0, new NextAction("set facing",
|
||||
// ACTION_NORMAL + 7), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"enemy out of melee", NextAction::array(0, new NextAction("reach melee", ACTION_HIGH + 1), nullptr)));
|
||||
// triggers.push_back(new TriggerNode("enemy too close for melee", NextAction::array(0, new NextAction("move out of
|
||||
// enemy contact", ACTION_NORMAL + 8), nullptr)));
|
||||
"enemy out of melee", { NextAction("reach melee", ACTION_HIGH + 1) }));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"critical health", NextAction::array(0, new NextAction("survival instincts", ACTION_EMERGENCY + 1), nullptr)));
|
||||
"critical health", { NextAction("survival instincts", ACTION_EMERGENCY + 1) }));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"omen of clarity", NextAction::array(0, new NextAction("omen of clarity", ACTION_HIGH + 9), nullptr)));
|
||||
"omen of clarity", { NextAction("omen of clarity", ACTION_HIGH + 9) }));
|
||||
triggers.push_back(new TriggerNode("player has flag",
|
||||
NextAction::array(0, new NextAction("dash", ACTION_EMERGENCY + 2), nullptr)));
|
||||
{ NextAction("dash", ACTION_EMERGENCY + 2) }));
|
||||
triggers.push_back(new TriggerNode("enemy flagcarrier near",
|
||||
NextAction::array(0, new NextAction("dash", ACTION_EMERGENCY + 2), nullptr)));
|
||||
{ NextAction("dash", ACTION_EMERGENCY + 2) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("berserk", NextAction::array(0, new NextAction("berserk", ACTION_HIGH + 6), nullptr)));
|
||||
new TriggerNode("berserk", { NextAction("berserk", ACTION_HIGH + 6) }));
|
||||
}
|
||||
|
||||
@@ -27,49 +27,49 @@ private:
|
||||
static ActionNode* regrowth([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("regrowth",
|
||||
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ NextAction::array(0, new NextAction("healing touch"), nullptr),
|
||||
/*C*/ NextAction::array(0, new NextAction("melee", 10.0f), nullptr));
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ { NextAction("healing touch") },
|
||||
/*C*/ { NextAction("melee", 10.0f) });
|
||||
}
|
||||
|
||||
static ActionNode* rejuvenation([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("rejuvenation",
|
||||
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* healing_touch([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("healing touch",
|
||||
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* regrowth_on_party([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("regrowth on party",
|
||||
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ NextAction::array(0, new NextAction("healing touch on party"), nullptr),
|
||||
/*C*/ NextAction::array(0, new NextAction("melee", 10.0f), nullptr));
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ { NextAction("healing touch on party") },
|
||||
/*C*/ { NextAction("melee", 10.0f) });
|
||||
}
|
||||
|
||||
static ActionNode* rejuvenation_on_party([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("rejuvenation on party",
|
||||
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* healing_touch_on_party([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("healing touch on party",
|
||||
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -29,76 +29,69 @@ private:
|
||||
static ActionNode* thorns([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("thorns",
|
||||
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* thorns_on_party([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("thorns on party",
|
||||
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*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::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*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::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
static ActionNode* regrowth_on_party([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("regrowth on party",
|
||||
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ NULL,
|
||||
/*C*/ NULL);
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
static ActionNode* rejuvenation_on_party([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("rejuvenation on party",
|
||||
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ NULL,
|
||||
/*C*/ NULL);
|
||||
/*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::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ NULL,
|
||||
/*C*/ NULL);
|
||||
/*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::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ NULL,
|
||||
/*C*/ NULL);
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
static ActionNode* revive([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("revive",
|
||||
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ NULL,
|
||||
/*C*/ NULL);
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
// static ActionNode* innervate([[maybe_unused]] PlayerbotAI* botAI)
|
||||
// {
|
||||
// return new ActionNode ("innervate",
|
||||
// /*P*/ nullptr,
|
||||
// /*A*/ NextAction::array(0, new NextAction("drink"), nullptr),
|
||||
// /*C*/ nullptr);
|
||||
// }
|
||||
};
|
||||
|
||||
GenericDruidNonCombatStrategy::GenericDruidNonCombatStrategy(PlayerbotAI* botAI) : NonCombatStrategy(botAI)
|
||||
@@ -110,79 +103,73 @@ void GenericDruidNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& trig
|
||||
{
|
||||
NonCombatStrategy::InitTriggers(triggers);
|
||||
|
||||
triggers.push_back(new TriggerNode("mark of the wild", NextAction::array(0, new NextAction("mark of the wild", 14.0f), nullptr)));
|
||||
// triggers.push_back(new TriggerNode("thorns", NextAction::array(0, new NextAction("thorns", 12.0f), nullptr)));
|
||||
// triggers.push_back(new TriggerNode("cure poison", NextAction::array(0, new NextAction("abolish poison", 21.0f),
|
||||
// nullptr)));
|
||||
triggers.push_back(new TriggerNode("party member cure poison", NextAction::array(0, new NextAction("abolish poison on party", 20.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("party member dead", NextAction::array(0, new NextAction("revive", ACTION_CRITICAL_HEAL + 10), nullptr)));
|
||||
// triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("innervate", ACTION_EMERGENCY
|
||||
// + 5), nullptr))); triggers.push_back(new TriggerNode("swimming", NextAction::array(0, new NextAction("aquatic
|
||||
// form", 1.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("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::array(0, new NextAction("apply oil", 1.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("often", { NextAction("apply oil", 1.0f) }));
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode("party member critical health",
|
||||
NextAction::array(0,
|
||||
new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 7),
|
||||
new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 6),
|
||||
new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 5),
|
||||
nullptr)));
|
||||
{
|
||||
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::array(0,
|
||||
new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 5),
|
||||
new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 4),
|
||||
new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 3),
|
||||
nullptr)));
|
||||
{
|
||||
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::array(0, new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 3),
|
||||
new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 2),
|
||||
new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 1),
|
||||
nullptr)));
|
||||
{ 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::array(0, new NextAction("wild growth on party", ACTION_LIGHT_HEAL + 3), new NextAction("rejuvenation on party", ACTION_LIGHT_HEAL + 2), NULL)));
|
||||
{ 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::array(0, new NextAction("remove curse on party", ACTION_DISPEL + 7), nullptr)));
|
||||
{ NextAction("remove curse on party", ACTION_DISPEL + 7) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("new pet", NextAction::array(0, new NextAction("set pet stance", 60.0f), nullptr)));
|
||||
new TriggerNode("new pet", { NextAction("set pet stance", 60.0f) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("party member critical health", NextAction::array(0,
|
||||
new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 7),
|
||||
new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 6),
|
||||
new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 5),
|
||||
nullptr)));
|
||||
triggers.push_back(new TriggerNode("party member low health", NextAction::array(0,
|
||||
new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 5),
|
||||
new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 4),
|
||||
new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 3),
|
||||
nullptr)));
|
||||
triggers.push_back(new TriggerNode("party member medium health", NextAction::array(0,
|
||||
new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 3),
|
||||
new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 2),
|
||||
new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 1),
|
||||
nullptr)));
|
||||
triggers.push_back(new TriggerNode("party member almost full health", NextAction::array(0,
|
||||
new NextAction("wild growth on party", ACTION_LIGHT_HEAL + 3),
|
||||
new NextAction("rejuvenation on party", ACTION_LIGHT_HEAL + 2),
|
||||
nullptr)));
|
||||
triggers.push_back(new TriggerNode("party member remove curse", NextAction::array(0,
|
||||
new NextAction("remove curse on party", ACTION_DISPEL + 7),
|
||||
nullptr)));
|
||||
triggers.push_back(new TriggerNode("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::array(0, new NextAction("apply oil", 1.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("often", { NextAction("apply oil", 1.0f) }));
|
||||
if (specTab == 1) // Feral
|
||||
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("apply stone", 1.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("often", { NextAction("apply stone", 1.0f) }));
|
||||
|
||||
}
|
||||
|
||||
@@ -195,13 +182,13 @@ void GenericDruidBuffStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
NonCombatStrategy::InitTriggers(triggers);
|
||||
|
||||
triggers.push_back(new TriggerNode("mark of the wild on party", NextAction::array(0,
|
||||
new NextAction("mark of the wild on party", 13.0f),
|
||||
nullptr)));
|
||||
triggers.push_back(new TriggerNode("thorns on main tank", NextAction::array(0,
|
||||
new NextAction("thorns on main tank", 11.0f),
|
||||
nullptr)));
|
||||
triggers.push_back(new TriggerNode("thorns", NextAction::array(0,
|
||||
new NextAction("thorns", 10.0f),
|
||||
nullptr)));
|
||||
triggers.push_back(new TriggerNode("mark of the wild on party", {
|
||||
NextAction("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),
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -27,73 +27,73 @@ private:
|
||||
static ActionNode* melee([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("melee",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* caster_form([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("caster form",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* cure_poison([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("cure poison",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* cure_poison_on_party([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("cure poison on party",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* abolish_poison([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("abolish poison",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* abolish_poison_on_party([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("abolish poison on party",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* rebirth([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("rebirth",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* entangling_roots_on_cc([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("entangling roots on cc",
|
||||
/*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ {},
|
||||
/*C*/ {});
|
||||
}
|
||||
|
||||
static ActionNode* innervate([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("innervate",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("mana potion"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("mana potion") },
|
||||
/*C*/ {});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -107,70 +107,52 @@ void GenericDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
CombatStrategy::InitTriggers(triggers);
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode("low health", NextAction::array(0, new NextAction("barkskin", ACTION_HIGH + 7), nullptr)));
|
||||
new TriggerNode("low health", { NextAction("barkskin", ACTION_HIGH + 7) }));
|
||||
|
||||
// triggers.push_back(new TriggerNode("low health", NextAction::array(0, new NextAction("regrowth",
|
||||
// ACTION_MEDIUM_HEAL + 2), nullptr))); triggers.push_back(new TriggerNode("party member low health",
|
||||
// NextAction::array(0, new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 1), nullptr)));
|
||||
// triggers.push_back(new TriggerNode("critical health", NextAction::array(0, new NextAction("regrowth",
|
||||
// ACTION_CRITICAL_HEAL + 2), new NextAction("healing touch", ACTION_CRITICAL_HEAL + 2), nullptr)));
|
||||
// triggers.push_back(new TriggerNode("party member critical health", NextAction::array(0, new NextAction("regrowth
|
||||
// on party", ACTION_CRITICAL_HEAL + 1), new NextAction("healing touch on party", ACTION_CRITICAL_HEAL + 1),
|
||||
// nullptr))); triggers.push_back(new TriggerNode("party member dead", NextAction::array(0, new
|
||||
// NextAction("rebirth", ACTION_HIGH + 1), nullptr))); triggers.push_back(new TriggerNode("low mana",
|
||||
// NextAction::array(0, new NextAction("innervate", ACTION_EMERGENCY + 5), nullptr)));
|
||||
triggers.push_back(new TriggerNode("combat party member dead",
|
||||
NextAction::array(0, new NextAction("rebirth", ACTION_HIGH + 9), NULL)));
|
||||
{ NextAction("rebirth", ACTION_HIGH + 9) }));
|
||||
triggers.push_back(new TriggerNode("being attacked",
|
||||
NextAction::array(0, new NextAction("nature's grasp", ACTION_HIGH + 1), nullptr)));
|
||||
triggers.push_back(new TriggerNode("new pet", NextAction::array(0, new NextAction("set pet stance", 60.0f), nullptr)));
|
||||
{ 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("cure poison", NextAction::array(0, new NextAction("abolish poison",
|
||||
// ACTION_DISPEL + 2), nullptr)));
|
||||
triggers.push_back(
|
||||
new TriggerNode("party member cure poison",
|
||||
NextAction::array(0, new NextAction("abolish poison on party", ACTION_DISPEL + 1), nullptr)));
|
||||
{ NextAction("abolish poison on party", ACTION_DISPEL + 1) }));
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode("party member remove curse",
|
||||
NextAction::array(0, new NextAction("remove curse on party", ACTION_DISPEL + 7), NULL)));
|
||||
{ NextAction("remove curse on party", ACTION_DISPEL + 7) }));
|
||||
|
||||
}
|
||||
|
||||
void DruidBoostStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode(
|
||||
"nature's swiftness", NextAction::array(0, new NextAction("nature's swiftness", ACTION_HIGH + 9), nullptr)));
|
||||
"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::array(0, new NextAction("entangling roots on cc", ACTION_HIGH + 2), nullptr)));
|
||||
"entangling roots", { NextAction("entangling roots on cc", ACTION_HIGH + 2) }));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"entangling roots kite", NextAction::array(0, new NextAction("entangling roots", ACTION_HIGH + 2), nullptr)));
|
||||
"entangling roots kite", { NextAction("entangling roots", ACTION_HIGH + 2) }));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"hibernate", NextAction::array(0, new NextAction("hibernate on cc", ACTION_HIGH + 3), nullptr)));
|
||||
"hibernate", { NextAction("hibernate on cc", ACTION_HIGH + 3) }));
|
||||
}
|
||||
|
||||
void DruidHealerDpsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(
|
||||
new TriggerNode("healer should attack",
|
||||
NextAction::array(0,
|
||||
new NextAction("cancel tree form", ACTION_DEFAULT + 0.3f),
|
||||
new NextAction("moonfire", ACTION_DEFAULT + 0.2f),
|
||||
new NextAction("wrath", ACTION_DEFAULT + 0.1f),
|
||||
new NextAction("starfire", ACTION_DEFAULT),
|
||||
nullptr)));
|
||||
{
|
||||
NextAction("cancel tree form", ACTION_DEFAULT + 0.3f),
|
||||
NextAction("moonfire", ACTION_DEFAULT + 0.2f),
|
||||
NextAction("wrath", ACTION_DEFAULT + 0.1f),
|
||||
NextAction("starfire", ACTION_DEFAULT),
|
||||
}));
|
||||
|
||||
// long cast time
|
||||
// triggers.push_back(
|
||||
// new TriggerNode("medium aoe and healer should attack",
|
||||
// NextAction::array(0,
|
||||
// new NextAction("hurricane", ACTION_DEFAULT + 0.7f),
|
||||
// nullptr)));
|
||||
}
|
||||
|
||||
@@ -12,40 +12,16 @@ class HealDruidStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
|
||||
public:
|
||||
HealDruidStrategyActionNodeFactory() {
|
||||
creators["nourish on party"] = &nourtish_on_party;
|
||||
// creators["wild growth on party"] = &wild_growth_on_party;
|
||||
// creators["rejuvenation on party"] = &rejuvenation_on_party;
|
||||
// creators["regrowth on party"] = ®rowth_on_party;
|
||||
}
|
||||
|
||||
private:
|
||||
static ActionNode* nourtish_on_party([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("nourish on party",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("healing touch on party"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("healing touch on party") },
|
||||
/*C*/ {});
|
||||
}
|
||||
// static ActionNode* wild_growth_on_party([[maybe_unused]] PlayerbotAI* botAI)
|
||||
// {
|
||||
// return new ActionNode("wild growth on party",
|
||||
// /*P*/ NextAction::array(0, new NextAction("tree form"), nullptr),
|
||||
// /*A*/ nullptr,
|
||||
// /*C*/ nullptr);
|
||||
// }
|
||||
// static ActionNode* rejuvenation_on_party([[maybe_unused]] PlayerbotAI* botAI)
|
||||
// {
|
||||
// return new ActionNode("rejuvenation on party",
|
||||
// /*P*/ NextAction::array(0, new NextAction("tree form"), nullptr),
|
||||
// /*A*/ nullptr,
|
||||
// /*C*/ nullptr);
|
||||
// }
|
||||
// static ActionNode* regrowth_on_party([[maybe_unused]] PlayerbotAI* botAI)
|
||||
// {
|
||||
// return new ActionNode("regrowth on party",
|
||||
// /*P*/ NextAction::array(0, new NextAction("tree form"), nullptr),
|
||||
// /*A*/ nullptr,
|
||||
// /*C*/ nullptr);
|
||||
// }
|
||||
};
|
||||
|
||||
HealDruidStrategy::HealDruidStrategy(PlayerbotAI* botAI) : GenericDruidStrategy(botAI)
|
||||
@@ -57,73 +33,69 @@ void HealDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
GenericDruidStrategy::InitTriggers(triggers);
|
||||
|
||||
// triggers.push_back(
|
||||
// new TriggerNode("tree form", NextAction::array(0, new NextAction("tree form", ACTION_HIGH + 1), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"party member to heal out of spell range",
|
||||
NextAction::array(0, new NextAction("reach party member to heal", ACTION_CRITICAL_HEAL + 9), nullptr)));
|
||||
{ NextAction("reach party member to heal", ACTION_CRITICAL_HEAL + 9) }));
|
||||
|
||||
// CRITICAL
|
||||
triggers.push_back(
|
||||
new TriggerNode("party member critical health",
|
||||
NextAction::array(0,
|
||||
new NextAction("tree form", ACTION_CRITICAL_HEAL + 4.1f),
|
||||
new NextAction("swiftmend on party", ACTION_CRITICAL_HEAL + 4),
|
||||
new NextAction("regrowth on party", ACTION_CRITICAL_HEAL + 3),
|
||||
new NextAction("wild growth on party", ACTION_CRITICAL_HEAL + 2),
|
||||
new NextAction("nourish on party", ACTION_CRITICAL_HEAL + 1),
|
||||
// new NextAction("healing touch on party", ACTION_CRITICAL_HEAL + 0),
|
||||
nullptr)));
|
||||
{
|
||||
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::array(0, new NextAction("nature's swiftness", ACTION_CRITICAL_HEAL + 4), nullptr)));
|
||||
{ NextAction("nature's swiftness", ACTION_CRITICAL_HEAL + 4) }));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"group heal setting",
|
||||
NextAction::array(0,
|
||||
new NextAction("tree form", ACTION_MEDIUM_HEAL + 2.3f),
|
||||
new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 2.2f),
|
||||
new NextAction("rejuvenation on not full", ACTION_MEDIUM_HEAL + 2.1f),
|
||||
nullptr)));
|
||||
{
|
||||
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::array(0,
|
||||
new NextAction("tree form", ACTION_CRITICAL_HEAL + 0.6f),
|
||||
new NextAction("tranquility", ACTION_CRITICAL_HEAL + 0.5f), nullptr)));
|
||||
{
|
||||
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::array(0, new NextAction("tree form", ACTION_MEDIUM_HEAL + 1.5f),
|
||||
new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 1.4f),
|
||||
new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 1.3f),
|
||||
new NextAction("swiftmend on party", ACTION_MEDIUM_HEAL + 1.2),
|
||||
new NextAction("nourish on party", ACTION_MEDIUM_HEAL + 1.1f),
|
||||
nullptr)));
|
||||
{ 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::array(0,
|
||||
new NextAction("tree form", ACTION_MEDIUM_HEAL + 0.5f),
|
||||
new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 0.4f),
|
||||
new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 0.3f),
|
||||
new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 0.2f),
|
||||
new NextAction("nourish on party", ACTION_MEDIUM_HEAL + 0.1f), nullptr)));
|
||||
{
|
||||
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::array(0, new NextAction("wild growth on party", ACTION_LIGHT_HEAL + 0.3f),
|
||||
new NextAction("rejuvenation on party", ACTION_LIGHT_HEAL + 0.2f),
|
||||
new NextAction("regrowth on party", ACTION_LIGHT_HEAL + 0.1f), nullptr)));
|
||||
{ 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::array(0, new NextAction("innervate", ACTION_HIGH + 5), nullptr)));
|
||||
new TriggerNode("medium mana", { NextAction("innervate", ACTION_HIGH + 5) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("enemy too close for spell",
|
||||
NextAction::array(0, new NextAction("flee", ACTION_MOVE + 9), nullptr)));
|
||||
{ NextAction("flee", ACTION_MOVE + 9) }));
|
||||
}
|
||||
|
||||
@@ -9,16 +9,24 @@
|
||||
|
||||
MeleeDruidStrategy::MeleeDruidStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) {}
|
||||
|
||||
NextAction** MeleeDruidStrategy::getDefaultActions()
|
||||
std::vector<NextAction> MeleeDruidStrategy::getDefaultActions()
|
||||
{
|
||||
return NextAction::array(0, new NextAction("faerie fire", ACTION_DEFAULT + 0.1f),
|
||||
new NextAction("melee", ACTION_DEFAULT), nullptr);
|
||||
return {
|
||||
NextAction("faerie fire", ACTION_DEFAULT + 0.1f),
|
||||
NextAction("melee", ACTION_DEFAULT)
|
||||
};
|
||||
}
|
||||
|
||||
void MeleeDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode(
|
||||
"omen of clarity", NextAction::array(0, new NextAction("omen of clarity", ACTION_HIGH + 9), nullptr)));
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"omen of clarity",
|
||||
{
|
||||
NextAction("omen of clarity", ACTION_HIGH + 9)
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
CombatStrategy::InitTriggers(triggers);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ public:
|
||||
|
||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
std::string const getName() override { return "melee"; }
|
||||
NextAction** getDefaultActions() override;
|
||||
std::vector<NextAction> getDefaultActions() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -29,90 +29,112 @@ public:
|
||||
private:
|
||||
static ActionNode* cat_form([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("cat form",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"cat form",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* mangle_cat([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("mangle (cat)",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"mangle (cat)",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* shred([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("shred",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("claw"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"shred",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("claw") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* rake([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("rake",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"rake",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* rip([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("rip",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"rip",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* ferocious_bite([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("ferocious bite",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ NextAction::array(0, new NextAction("rip"), nullptr),
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"ferocious bite",
|
||||
/*P*/ {},
|
||||
/*A*/ { NextAction("rip") },
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* savage_roar([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("savage roar",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
return new ActionNode(
|
||||
"savage roar",
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ {}
|
||||
);
|
||||
}
|
||||
|
||||
static ActionNode* faerie_fire_feral([[maybe_unused]] PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("faerie fire (feral)",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ nullptr);
|
||||
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::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ NextAction::array(0, new NextAction("cat form"), nullptr));
|
||||
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::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ NextAction::array(0, new NextAction("cat form"), nullptr));
|
||||
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::array(0, new NextAction("caster form"), nullptr),
|
||||
/*A*/ nullptr,
|
||||
/*C*/ NextAction::array(0, new NextAction("cat form"), nullptr));
|
||||
return new ActionNode(
|
||||
"rejuvenation on party",
|
||||
/*P*/ { NextAction("caster form") },
|
||||
/*A*/ {},
|
||||
/*C*/ { NextAction("cat form") }
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -121,12 +143,15 @@ OffhealDruidCatStrategy::OffhealDruidCatStrategy(PlayerbotAI* botAI) : FeralDrui
|
||||
actionNodeFactories.Add(new OffhealDruidCatStrategyActionNodeFactory());
|
||||
}
|
||||
|
||||
NextAction** OffhealDruidCatStrategy::getDefaultActions()
|
||||
std::vector<NextAction> OffhealDruidCatStrategy::getDefaultActions()
|
||||
{
|
||||
return NextAction::array(0, new NextAction("mangle (cat)", ACTION_DEFAULT + 0.5f),
|
||||
new NextAction("shred", ACTION_DEFAULT + 0.4f),
|
||||
new NextAction("rake", ACTION_DEFAULT + 0.3f), new NextAction("melee", ACTION_DEFAULT),
|
||||
new NextAction("cat form", ACTION_DEFAULT - 0.1f), nullptr);
|
||||
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)
|
||||
@@ -134,46 +159,149 @@ void OffhealDruidCatStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
FeralDruidStrategy::InitTriggers(triggers);
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode("cat form", NextAction::array(0, new NextAction("cat form", ACTION_HIGH + 8), nullptr)));
|
||||
new TriggerNode(
|
||||
"cat form",
|
||||
{
|
||||
NextAction("cat form", ACTION_HIGH + 8)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode("savage roar", NextAction::array(0, new NextAction("savage roar", ACTION_HIGH + 7), nullptr)));
|
||||
triggers.push_back(new TriggerNode("combo points available",
|
||||
NextAction::array(0, new NextAction("rip", ACTION_HIGH + 6), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"ferocious bite time", NextAction::array(0, new NextAction("ferocious bite", ACTION_HIGH + 5), nullptr)));
|
||||
new TriggerNode(
|
||||
"savage roar",
|
||||
{
|
||||
NextAction("savage roar", ACTION_HIGH + 7)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode("target with combo points almost dead",
|
||||
NextAction::array(0, new NextAction("ferocious bite", ACTION_HIGH + 4), nullptr)));
|
||||
triggers.push_back(new TriggerNode("mangle (cat)",
|
||||
NextAction::array(0, new NextAction("mangle (cat)", ACTION_HIGH + 3), nullptr)));
|
||||
triggers.push_back(new TriggerNode("rake", NextAction::array(0, new NextAction("rake", ACTION_HIGH + 2), nullptr)));
|
||||
triggers.push_back(new TriggerNode("almost full energy available",
|
||||
NextAction::array(0, new NextAction("shred", ACTION_DEFAULT + 0.4f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("combo points not full",
|
||||
NextAction::array(0, new NextAction("shred", ACTION_DEFAULT + 0.4f), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"faerie fire (feral)", NextAction::array(0, new NextAction("faerie fire (feral)", ACTION_NORMAL), nullptr)));
|
||||
triggers.push_back(new TriggerNode("enemy out of melee",
|
||||
NextAction::array(0, new NextAction("feral charge - cat", ACTION_HIGH + 9),
|
||||
new NextAction("dash", ACTION_HIGH + 8), nullptr)));
|
||||
new TriggerNode(
|
||||
"combo points available",
|
||||
{
|
||||
NextAction("rip", ACTION_HIGH + 6)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode("medium aoe", NextAction::array(0, new NextAction("swipe (cat)", ACTION_HIGH + 3), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"low energy", NextAction::array(0, new NextAction("tiger's fury", ACTION_NORMAL + 1), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"party member critical health",
|
||||
NextAction::array(0, new NextAction("regrowth on party", ACTION_CRITICAL_HEAL + 6),
|
||||
new NextAction("healing touch on party", ACTION_CRITICAL_HEAL + 5), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"party member low health",
|
||||
NextAction::array(0, new NextAction("healing touch on party", ACTION_MEDIUM_HEAL + 5), nullptr)));
|
||||
new TriggerNode(
|
||||
"ferocious bite time",
|
||||
{
|
||||
NextAction("ferocious bite", ACTION_HIGH + 5)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode("party member medium health",
|
||||
NextAction::array(0, new NextAction("rejuvenation on party", ACTION_LIGHT_HEAL + 8), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"party member to heal out of spell range",
|
||||
NextAction::array(0, new NextAction("reach party member to heal", ACTION_EMERGENCY + 3), nullptr)));
|
||||
new TriggerNode(
|
||||
"target with combo points almost dead",
|
||||
{
|
||||
NextAction("ferocious bite", ACTION_HIGH + 4)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode("low mana", NextAction::array(0, new NextAction("innervate", ACTION_HIGH + 4), nullptr)));
|
||||
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)
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
std::string const getName() override { return "offheal"; }
|
||||
NextAction** getDefaultActions() override;
|
||||
std::vector<NextAction> getDefaultActions() override;
|
||||
uint32 GetType() const override
|
||||
{
|
||||
return STRATEGY_TYPE_COMBAT | STRATEGY_TYPE_DPS | STRATEGY_TYPE_HEAL | STRATEGY_TYPE_MELEE;
|
||||
|
||||
@@ -7,9 +7,9 @@ void WotlkDungeonANStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
|
||||
// TODO: Add CC trigger while web wraps are casting?
|
||||
// TODO: Bring healer closer than ranged dps to avoid fixates?
|
||||
triggers.push_back(new TriggerNode("krik'thir web wrap",
|
||||
NextAction::array(0, new NextAction("attack web wrap", ACTION_RAID + 5), nullptr)));
|
||||
{ NextAction("attack web wrap", ACTION_RAID + 5) }));
|
||||
triggers.push_back(new TriggerNode("krik'thir watchers",
|
||||
NextAction::array(0, new NextAction("krik'thir priority", ACTION_RAID + 4), nullptr)));
|
||||
{ NextAction("krik'thir priority", ACTION_RAID + 4) }));
|
||||
|
||||
// Hadronox
|
||||
// The core AC triggers are very buggy with this boss, but default strat seems to play correctly
|
||||
@@ -19,9 +19,9 @@ void WotlkDungeonANStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
|
||||
// and cast time is instant so no way to check currently casting location.
|
||||
// May need to hook boss AI.. might be able to just heal through it for now.
|
||||
// triggers.push_back(new TriggerNode("anub'arak impale",
|
||||
// NextAction::array(0, new NextAction("TODO", ACTION_MOVE + 5), nullptr)));
|
||||
// { NextAction("TODO", ACTION_MOVE + 5) }));
|
||||
triggers.push_back(new TriggerNode("anub'arak pound",
|
||||
NextAction::array(0, new NextAction("dodge pound", ACTION_MOVE + 5), nullptr)));
|
||||
{ NextAction("dodge pound", ACTION_MOVE + 5) }));
|
||||
}
|
||||
|
||||
void WotlkDungeonANStrategy::InitMultipliers(std::vector<Multiplier*> &multipliers)
|
||||
|
||||
@@ -8,12 +8,12 @@ void WotlkDungeonCoSStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
|
||||
|
||||
// Salramm the Fleshcrafter
|
||||
triggers.push_back(new TriggerNode("explode ghoul",
|
||||
NextAction::array(0, new NextAction("explode ghoul spread", ACTION_MOVE + 5), nullptr)));
|
||||
{ NextAction("explode ghoul spread", ACTION_MOVE + 5) }));
|
||||
|
||||
// Chrono-Lord Epoch
|
||||
// Not sure if this actually works, I think I've seen him charge melee characters..?
|
||||
triggers.push_back(new TriggerNode("epoch ranged",
|
||||
NextAction::array(0, new NextAction("epoch stack", ACTION_MOVE + 5), nullptr)));
|
||||
{ NextAction("epoch stack", ACTION_MOVE + 5) }));
|
||||
|
||||
// Mal'Ganis
|
||||
|
||||
|
||||
@@ -5,32 +5,32 @@ void WotlkDungeonDTKStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
|
||||
{
|
||||
// Trollgore
|
||||
triggers.push_back(new TriggerNode("corpse explode",
|
||||
NextAction::array(0, new NextAction("corpse explode spread", ACTION_MOVE + 5), nullptr)));
|
||||
{ NextAction("corpse explode spread", ACTION_MOVE + 5) }));
|
||||
|
||||
// Novos the Summoner
|
||||
// TODO: Can be improved - it's a pretty easy fight but complex to program, revisit if needed
|
||||
triggers.push_back(new TriggerNode("arcane field",
|
||||
NextAction::array(0, new NextAction("avoid arcane field", ACTION_MOVE + 5), nullptr)));
|
||||
{ NextAction("avoid arcane field", ACTION_MOVE + 5) }));
|
||||
triggers.push_back(new TriggerNode("arcane field",
|
||||
NextAction::array(0, new NextAction("novos positioning", ACTION_MOVE + 4), nullptr)));
|
||||
{ NextAction("novos positioning", ACTION_MOVE + 4) }));
|
||||
triggers.push_back(new TriggerNode("arcane field",
|
||||
NextAction::array(0, new NextAction("novos target priority", ACTION_NORMAL + 1), nullptr)));
|
||||
{ NextAction("novos target priority", ACTION_NORMAL + 1) }));
|
||||
|
||||
// King Dred
|
||||
// TODO: Fear ward / tremor totem, or general anti-fear strat development
|
||||
|
||||
//The Prophet Tharon'ja
|
||||
triggers.push_back(new TriggerNode("gift of tharon'ja",
|
||||
NextAction::array(0, new NextAction("touch of life", ACTION_NORMAL + 5), nullptr)));
|
||||
{ NextAction("touch of life", ACTION_NORMAL + 5) }));
|
||||
triggers.push_back(new TriggerNode("gift of tharon'ja",
|
||||
NextAction::array(0, new NextAction("bone armor", ACTION_NORMAL + 4), nullptr)));
|
||||
{ NextAction("bone armor", ACTION_NORMAL + 4) }));
|
||||
// Run ranged chars (who would normally stand at range) into melee, to dps in skeleton form
|
||||
triggers.push_back(new TriggerNode("tharon'ja out of melee",
|
||||
NextAction::array(0, new NextAction("reach melee", ACTION_NORMAL + 3), nullptr)));
|
||||
{ NextAction("reach melee", ACTION_NORMAL + 3) }));
|
||||
triggers.push_back(new TriggerNode("gift of tharon'ja",
|
||||
NextAction::array(0, new NextAction("taunt", ACTION_NORMAL + 2), nullptr)));
|
||||
{ NextAction("taunt", ACTION_NORMAL + 2) }));
|
||||
triggers.push_back(new TriggerNode("gift of tharon'ja",
|
||||
NextAction::array(0, new NextAction("slaying strike", ACTION_NORMAL + 2), nullptr)));
|
||||
{ NextAction("slaying strike", ACTION_NORMAL + 2) }));
|
||||
}
|
||||
|
||||
void WotlkDungeonDTKStrategy::InitMultipliers(std::vector<Multiplier*> &multipliers)
|
||||
|
||||
@@ -4,17 +4,16 @@
|
||||
void WotlkDungeonFoSStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("move from bronjahm",
|
||||
NextAction::array(0, new NextAction("move from bronjahm", ACTION_MOVE + 5), nullptr)));
|
||||
{ NextAction("move from bronjahm", ACTION_MOVE + 5) }));
|
||||
triggers.push_back(new TriggerNode("switch to soul fragment",
|
||||
NextAction::array(0, new NextAction("attack corrupted soul fragment", ACTION_RAID + 2), nullptr)));
|
||||
{ NextAction("attack corrupted soul fragment", ACTION_RAID + 2) }));
|
||||
triggers.push_back(new TriggerNode("bronjahm position",
|
||||
NextAction::array(0, new NextAction("bronjahm group position", ACTION_RAID + 1), nullptr)));
|
||||
{ NextAction("bronjahm group position", ACTION_RAID + 1) }));
|
||||
triggers.push_back(new TriggerNode("devourer of souls",
|
||||
NextAction::array(0, new NextAction("devourer of souls", ACTION_RAID + 1), nullptr)));
|
||||
{ NextAction("devourer of souls", ACTION_RAID + 1) }));
|
||||
}
|
||||
|
||||
void WotlkDungeonFoSStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||
{
|
||||
multipliers.push_back(new BronjahmMultiplier(botAI));
|
||||
//multipliers.push_back(new AttackFragmentMultiplier(botAI));
|
||||
}
|
||||
|
||||
@@ -11,13 +11,13 @@ void WotlkDungeonGDStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
|
||||
// TODO: Might need to add target priority for heroic on the snakes or to burn down boss.
|
||||
// Will re-test in heroic, decent dps groups should be able to blast him down with no funky strats.
|
||||
triggers.push_back(new TriggerNode("poison nova",
|
||||
NextAction::array(0, new NextAction("avoid poison nova", ACTION_RAID + 5), nullptr)));
|
||||
{ NextAction("avoid poison nova", ACTION_RAID + 5) }));
|
||||
triggers.push_back(new TriggerNode("snake wrap",
|
||||
NextAction::array(0, new NextAction("attack snake wrap", ACTION_RAID + 4), nullptr)));
|
||||
{ NextAction("attack snake wrap", ACTION_RAID + 4) }));
|
||||
|
||||
// Gal'darah
|
||||
triggers.push_back(new TriggerNode("whirling slash",
|
||||
NextAction::array(0, new NextAction("avoid whirling slash", ACTION_RAID + 5), nullptr)));
|
||||
{ NextAction("avoid whirling slash", ACTION_RAID + 5) }));
|
||||
|
||||
// Eck the Ferocious (Heroic only)
|
||||
}
|
||||
|
||||
@@ -5,30 +5,30 @@ void WotlkDungeonHoLStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
|
||||
{
|
||||
// General Bjarngrim
|
||||
triggers.push_back(new TriggerNode("stormforged lieutenant",
|
||||
NextAction::array(0, new NextAction("bjarngrim target", ACTION_RAID + 5), nullptr)));
|
||||
{ NextAction("bjarngrim target", ACTION_RAID + 5) }));
|
||||
triggers.push_back(new TriggerNode("whirlwind",
|
||||
NextAction::array(0, new NextAction("avoid whirlwind", ACTION_RAID + 4), nullptr)));
|
||||
{ NextAction("avoid whirlwind", ACTION_RAID + 4) }));
|
||||
|
||||
// Volkhan
|
||||
triggers.push_back(new TriggerNode("volkhan",
|
||||
NextAction::array(0, new NextAction("volkhan target", ACTION_RAID + 5), nullptr)));
|
||||
{ NextAction("volkhan target", ACTION_RAID + 5) }));
|
||||
|
||||
// Ionar
|
||||
triggers.push_back(new TriggerNode("ionar disperse",
|
||||
NextAction::array(0, new NextAction("disperse position", ACTION_MOVE + 5), nullptr)));
|
||||
{ NextAction("disperse position", ACTION_MOVE + 5) }));
|
||||
triggers.push_back(new TriggerNode("ionar tank aggro",
|
||||
NextAction::array(0, new NextAction("ionar tank position", ACTION_MOVE + 4), nullptr)));
|
||||
{ NextAction("ionar tank position", ACTION_MOVE + 4) }));
|
||||
triggers.push_back(new TriggerNode("static overload",
|
||||
NextAction::array(0, new NextAction("static overload spread", ACTION_MOVE + 3), nullptr)));
|
||||
{ NextAction("static overload spread", ACTION_MOVE + 3) }));
|
||||
// TODO: Targeted player can dodge the ball, but a single player soaking it isn't too bad to heal
|
||||
triggers.push_back(new TriggerNode("ball lightning",
|
||||
NextAction::array(0, new NextAction("ball lightning spread", ACTION_MOVE + 2), nullptr)));
|
||||
{ NextAction("ball lightning spread", ACTION_MOVE + 2) }));
|
||||
|
||||
// Loken
|
||||
triggers.push_back(new TriggerNode("lightning nova",
|
||||
NextAction::array(0, new NextAction("avoid lightning nova", ACTION_MOVE + 5), nullptr)));
|
||||
{ NextAction("avoid lightning nova", ACTION_MOVE + 5) }));
|
||||
triggers.push_back(new TriggerNode("loken ranged",
|
||||
NextAction::array(0, new NextAction("loken stack", ACTION_MOVE + 4), nullptr)));
|
||||
{ NextAction("loken stack", ACTION_MOVE + 4) }));
|
||||
}
|
||||
|
||||
void WotlkDungeonHoLStrategy::InitMultipliers(std::vector<Multiplier*> &multipliers)
|
||||
|
||||
@@ -9,7 +9,7 @@ void WotlkDungeonHoSStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
|
||||
// Krystallus
|
||||
// TODO: I think bots need to dismiss pets on this, or they nuke players they are standing close to
|
||||
triggers.push_back(new TriggerNode("ground slam",
|
||||
NextAction::array(0, new NextAction("shatter spread", ACTION_RAID + 5), nullptr)));
|
||||
{ NextAction("shatter spread", ACTION_RAID + 5) }));
|
||||
|
||||
// Tribunal of Ages
|
||||
// Seems fine, maybe add focus targeting strat if needed on heroic.
|
||||
@@ -19,7 +19,7 @@ void WotlkDungeonHoSStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
|
||||
// Sjonnir The Ironshaper
|
||||
// Possibly tank in place in the middle of the room, assign a dps to adds?
|
||||
triggers.push_back(new TriggerNode("lightning ring",
|
||||
NextAction::array(0, new NextAction("avoid lightning ring", ACTION_RAID + 5), nullptr)));
|
||||
{ NextAction("avoid lightning ring", ACTION_RAID + 5) }));
|
||||
}
|
||||
|
||||
void WotlkDungeonHoSStrategy::InitMultipliers(std::vector<Multiplier*> &multipliers)
|
||||
|
||||
@@ -7,39 +7,39 @@ void WotlkDungeonNexStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
|
||||
// or
|
||||
// Alliance Commander (Horde N)/Commander Stoutbeard (Horde H)
|
||||
triggers.push_back(new TriggerNode("faction commander whirlwind",
|
||||
NextAction::array(0, new NextAction("move from whirlwind", ACTION_MOVE + 5), nullptr)));
|
||||
{ NextAction("move from whirlwind", ACTION_MOVE + 5) }));
|
||||
// TODO: Handle fear? (tremor totems, fear ward etc.)
|
||||
|
||||
// Grand Magus Telestra
|
||||
triggers.push_back(new TriggerNode("telestra firebomb",
|
||||
NextAction::array(0, new NextAction("firebomb spread", ACTION_MOVE + 5), nullptr)));
|
||||
{ NextAction("firebomb spread", ACTION_MOVE + 5) }));
|
||||
triggers.push_back(new TriggerNode("telestra split phase",
|
||||
NextAction::array(0, new NextAction("telestra split target", ACTION_RAID + 1), nullptr)));
|
||||
{ NextAction("telestra split target", ACTION_RAID + 1) }));
|
||||
// TODO: Add priority interrupt on the frost split's Blizzard casts
|
||||
|
||||
// Anomalus
|
||||
triggers.push_back(new TriggerNode("chaotic rift",
|
||||
NextAction::array(0, new NextAction("chaotic rift target", ACTION_RAID + 1), nullptr)));
|
||||
{ NextAction("chaotic rift target", ACTION_RAID + 1) }));
|
||||
|
||||
// Ormorok the Tree-Shaper
|
||||
// Tank trigger to stack inside boss. Can also add return action to prevent boss repositioning
|
||||
// if it becomes too much of a problem. He usually dies before he's up against a wall though
|
||||
triggers.push_back(new TriggerNode("ormorok spikes",
|
||||
NextAction::array(0, new NextAction("dodge spikes", ACTION_MOVE + 5), nullptr)));
|
||||
{ NextAction("dodge spikes", ACTION_MOVE + 5) }));
|
||||
// Non-tank trigger to stack. Avoiding the spikes at range is.. harder than it seems.
|
||||
// TODO: This turns hunters into melee marshmallows, have not come up with a better solution yet
|
||||
triggers.push_back(new TriggerNode("ormorok stack",
|
||||
NextAction::array(0, new NextAction("dodge spikes", ACTION_MOVE + 5), nullptr)));
|
||||
{ NextAction("dodge spikes", ACTION_MOVE + 5) }));
|
||||
// TODO: Add handling for spell reflect... best to spam low level/weak spells but don't want
|
||||
// to hardcode spells per class, might be difficult to dynamically generate this.
|
||||
// Will revisit if I find my altbots killing themselves in heroic, just heal through it for now
|
||||
|
||||
// Keristrasza
|
||||
triggers.push_back(new TriggerNode("intense cold",
|
||||
NextAction::array(0, new NextAction("intense cold jump", ACTION_MOVE + 5), nullptr)));
|
||||
{ NextAction("intense cold jump", ACTION_MOVE + 5) }));
|
||||
// Flank dragon positioning
|
||||
triggers.push_back(new TriggerNode("keristrasza positioning",
|
||||
NextAction::array(0, new NextAction("rear flank", ACTION_MOVE + 4), nullptr)));
|
||||
{ NextAction("rear flank", ACTION_MOVE + 4) }));
|
||||
// TODO: Add frost resist aura for paladins?
|
||||
}
|
||||
|
||||
|
||||
@@ -6,28 +6,28 @@ void WotlkDungeonOccStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
|
||||
// Drakos the Interrogator
|
||||
// TODO: May need work, TBA.
|
||||
triggers.push_back(new TriggerNode("unstable sphere",
|
||||
NextAction::array(0, new NextAction("avoid unstable sphere", ACTION_MOVE + 5), nullptr)));
|
||||
{ NextAction("avoid unstable sphere", ACTION_MOVE + 5) }));
|
||||
|
||||
// DRAKES
|
||||
triggers.push_back(new TriggerNode("drake mount",
|
||||
NextAction::array(0, new NextAction("mount drake", ACTION_RAID + 5), nullptr)));
|
||||
{ NextAction("mount drake", ACTION_RAID + 5) }));
|
||||
triggers.push_back(new TriggerNode("drake dismount",
|
||||
NextAction::array(0, new NextAction("dismount drake", ACTION_RAID + 5), nullptr)));
|
||||
{ NextAction("dismount drake", ACTION_RAID + 5) }));
|
||||
triggers.push_back(new TriggerNode("group flying",
|
||||
NextAction::array(0, new NextAction("occ fly drake", ACTION_NORMAL + 1), nullptr)));
|
||||
{ NextAction("occ fly drake", ACTION_NORMAL + 1) }));
|
||||
triggers.push_back(new TriggerNode("drake combat",
|
||||
NextAction::array(0, new NextAction("occ drake attack", ACTION_NORMAL + 5), nullptr)));
|
||||
{ NextAction("occ drake attack", ACTION_NORMAL + 5) }));
|
||||
|
||||
// Varos Cloudstrider
|
||||
// Seems to be no way to identify the marked cores, may need to hook boss AI..
|
||||
// triggers.push_back(new TriggerNode("varos cloudstrider",
|
||||
// NextAction::array(0, new NextAction("avoid energize cores", ACTION_RAID + 5), nullptr)));
|
||||
// { NextAction("avoid energize cores", ACTION_RAID + 5) }));
|
||||
|
||||
// Mage-Lord Urom
|
||||
triggers.push_back(new TriggerNode("arcane explosion",
|
||||
NextAction::array(0, new NextAction("avoid arcane explosion", ACTION_MOVE + 5), nullptr)));
|
||||
{ NextAction("avoid arcane explosion", ACTION_MOVE + 5) }));
|
||||
triggers.push_back(new TriggerNode("time bomb",
|
||||
NextAction::array(0, new NextAction("time bomb spread", ACTION_MOVE + 4), nullptr)));
|
||||
{ NextAction("time bomb spread", ACTION_MOVE + 4) }));
|
||||
|
||||
// Ley-Guardian Eregos
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ void WotlkDungeonOKStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
|
||||
{
|
||||
// Elder Nadox
|
||||
triggers.push_back(new TriggerNode("nadox guardian",
|
||||
NextAction::array(0, new NextAction("attack nadox guardian", ACTION_RAID + 5), nullptr)));
|
||||
{ NextAction("attack nadox guardian", ACTION_RAID + 5) }));
|
||||
|
||||
// Prince Taldaram
|
||||
// Flame Orb spawns in melee, doesn't have a clear direction until it starts moving.
|
||||
@@ -14,13 +14,13 @@ void WotlkDungeonOKStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
|
||||
|
||||
// Jedoga Shadowseeker
|
||||
triggers.push_back(new TriggerNode("jedoga volunteer",
|
||||
NextAction::array(0, new NextAction("attack jedoga volunteer", ACTION_RAID + 5), nullptr)));
|
||||
{ NextAction("attack jedoga volunteer", ACTION_RAID + 5) }));
|
||||
|
||||
// Herald Volazj
|
||||
// Trash mobs before him have a big telegraphed shadow crash spell,
|
||||
// this can be avoided and is intended to be dodged
|
||||
triggers.push_back(new TriggerNode("shadow crash",
|
||||
NextAction::array(0, new NextAction("avoid shadow crash", ACTION_MOVE + 5), nullptr)));
|
||||
{ NextAction("avoid shadow crash", ACTION_MOVE + 5) }));
|
||||
// Volazj is not implemented properly in AC, insanity phase does nothing.
|
||||
|
||||
// Amanitar (Heroic Only)
|
||||
|
||||
@@ -4,14 +4,13 @@
|
||||
void WotlkDungeonPoSStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("ick and krick",
|
||||
NextAction::array(0, new NextAction("ick and krick", ACTION_RAID + 5), nullptr)));
|
||||
{ NextAction("ick and krick", ACTION_RAID + 5) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("tyrannus",
|
||||
NextAction::array(0, new NextAction("tyrannus", ACTION_RAID + 5), nullptr)));
|
||||
{ NextAction("tyrannus", ACTION_RAID + 5) }));
|
||||
}
|
||||
|
||||
void WotlkDungeonPoSStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||
{
|
||||
multipliers.push_back(new IckAndKrickMultiplier(botAI));
|
||||
//multipliers.push_back(new AttackFragmentMultiplier(botAI));
|
||||
}
|
||||
|
||||
@@ -4,19 +4,18 @@
|
||||
void WotlkDungeonToCStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("toc lance",
|
||||
NextAction::array(0, new NextAction("toc lance", ACTION_RAID + 5), nullptr)));
|
||||
{ NextAction("toc lance", ACTION_RAID + 5) }));
|
||||
triggers.push_back(new TriggerNode("toc ue lance",
|
||||
NextAction::array(0, new NextAction("toc ue lance", ACTION_RAID + 2), nullptr)));
|
||||
{ NextAction("toc ue lance", ACTION_RAID + 2) }));
|
||||
triggers.push_back(new TriggerNode("toc mount near",
|
||||
NextAction::array(0, new NextAction("toc mount", ACTION_RAID + 4), nullptr)));
|
||||
{ NextAction("toc mount", ACTION_RAID + 4) }));
|
||||
triggers.push_back(new TriggerNode("toc mounted",
|
||||
NextAction::array(0, new NextAction("toc mounted", ACTION_RAID + 6), nullptr)));
|
||||
{ NextAction("toc mounted", ACTION_RAID + 6) }));
|
||||
triggers.push_back(new TriggerNode("toc eadric",
|
||||
NextAction::array(0, new NextAction("toc eadric", ACTION_RAID + 3), nullptr)));
|
||||
{ NextAction("toc eadric", ACTION_RAID + 3) }));
|
||||
|
||||
}
|
||||
|
||||
void WotlkDungeonToCStrategy::InitMultipliers(std::vector<Multiplier*> &multipliers)
|
||||
{
|
||||
//multipliers.push_back(new toc...); if needed in the future
|
||||
}
|
||||
|
||||
@@ -5,32 +5,32 @@ void WotlkDungeonUKStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
|
||||
{
|
||||
// Prince Keleseth
|
||||
triggers.push_back(new TriggerNode("keleseth frost tomb",
|
||||
NextAction::array(0, new NextAction("attack frost tomb", ACTION_RAID + 1), nullptr)));
|
||||
{ NextAction("attack frost tomb", ACTION_RAID + 1) }));
|
||||
|
||||
// Skarvald the Constructor & Dalronn the Controller
|
||||
triggers.push_back(new TriggerNode("dalronn priority",
|
||||
NextAction::array(0, new NextAction("attack dalronn", ACTION_RAID + 1), nullptr)));
|
||||
{ NextAction("attack dalronn", ACTION_RAID + 1) }));
|
||||
|
||||
// Ingvar the Plunderer
|
||||
|
||||
// Doesn't work yet, this action doesn't get processed until the existing cast finishes
|
||||
// triggers.push_back(new TriggerNode("ingvar staggering roar",
|
||||
// NextAction::array(0, new NextAction("ingvar stop casting", ACTION_RAID + 1), nullptr)));
|
||||
// { NextAction("ingvar stop casting", ACTION_RAID + 1) }));
|
||||
|
||||
// No easy way to check LoS here, the pillars do not seem to count as gameobjects.
|
||||
// Not implemented for now, unsure if this is needed as a good group can probably burst through the boss
|
||||
// and just eat the debuff.
|
||||
// triggers.push_back(new TriggerNode("ingvar dreadful roar",
|
||||
// NextAction::array(0, new NextAction("ingvar hide los", ACTION_RAID + 1), nullptr)));
|
||||
// { NextAction("ingvar hide los", ACTION_RAID + 1) }));
|
||||
triggers.push_back(new TriggerNode("ingvar smash tank",
|
||||
NextAction::array(0, new NextAction("ingvar dodge smash", ACTION_MOVE + 5), nullptr)));
|
||||
{ NextAction("ingvar dodge smash", ACTION_MOVE + 5) }));
|
||||
triggers.push_back(new TriggerNode("ingvar smash tank return",
|
||||
NextAction::array(0, new NextAction("ingvar smash return", ACTION_MOVE + 5), nullptr)));
|
||||
{ NextAction("ingvar smash return", ACTION_MOVE + 5) }));
|
||||
// Buggy... if not behind target, ai can get stuck running towards and away from target.
|
||||
// I think for ranged chars, a custom action should be added that doesn't attempt to run into melee.
|
||||
// This is a bandaid for now, needs to be improved.
|
||||
triggers.push_back(new TriggerNode("not behind ingvar",
|
||||
NextAction::array(0, new NextAction("set behind", ACTION_MOVE + 1), nullptr)));
|
||||
{ NextAction("set behind", ACTION_MOVE + 1) }));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -10,14 +10,14 @@ void WotlkDungeonUPStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
|
||||
// Skadi the Ruthless
|
||||
// TODO: Harpoons launchable via GameObject. For now players should do them
|
||||
triggers.push_back(new TriggerNode("freezing cloud",
|
||||
NextAction::array(0, new NextAction("avoid freezing cloud", ACTION_RAID + 5), nullptr)));
|
||||
{ NextAction("avoid freezing cloud", ACTION_RAID + 5) }));
|
||||
triggers.push_back(new TriggerNode("skadi whirlwind",
|
||||
NextAction::array(0, new NextAction("avoid skadi whirlwind", ACTION_RAID + 4), nullptr)));
|
||||
{ NextAction("avoid skadi whirlwind", ACTION_RAID + 4) }));
|
||||
|
||||
// King Ymiron
|
||||
// May need to avoid orb.. unclear if the generic avoid AoE does this well
|
||||
triggers.push_back(new TriggerNode("ymiron bane",
|
||||
NextAction::array(0, new NextAction("stop attack", ACTION_RAID + 5), nullptr)));
|
||||
{ NextAction("stop attack", ACTION_RAID + 5) }));
|
||||
}
|
||||
|
||||
void WotlkDungeonUPStrategy::InitMultipliers(std::vector<Multiplier*> &multipliers)
|
||||
|
||||
@@ -6,14 +6,14 @@ void WotlkDungeonVHStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
|
||||
// Erekem
|
||||
// This boss has many purgable buffs, purging/dispels could be merged into generic strats though
|
||||
triggers.push_back(new TriggerNode("erekem target",
|
||||
NextAction::array(0, new NextAction("attack erekem", ACTION_RAID + 1), nullptr)));
|
||||
{ NextAction("attack erekem", ACTION_RAID + 1) }));
|
||||
|
||||
// Moragg
|
||||
// TODO: This guy has Optic Link which may require moving, add if needed
|
||||
|
||||
// Ichoron
|
||||
triggers.push_back(new TriggerNode("ichoron target",
|
||||
NextAction::array(0, new NextAction("attack ichor globule", ACTION_RAID + 1), nullptr)));
|
||||
{ NextAction("attack ichor globule", ACTION_RAID + 1) }));
|
||||
|
||||
// Xevozz
|
||||
// TODO: Revisit in heroics, waypoints back and forth on stairs. Need to test with double beacon spawn
|
||||
@@ -23,13 +23,13 @@ void WotlkDungeonVHStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
|
||||
|
||||
// Zuramat the Obliterator
|
||||
triggers.push_back(new TriggerNode("shroud of darkness",
|
||||
NextAction::array(0, new NextAction("stop attack", ACTION_HIGH + 5), nullptr)));
|
||||
{ NextAction("stop attack", ACTION_HIGH + 5) }));
|
||||
triggers.push_back(new TriggerNode("void shift",
|
||||
NextAction::array(0, new NextAction("attack void sentry", ACTION_RAID + 1), nullptr)));
|
||||
{ NextAction("attack void sentry", ACTION_RAID + 1) }));
|
||||
|
||||
// Cyanigosa
|
||||
triggers.push_back(new TriggerNode("cyanigosa positioning",
|
||||
NextAction::array(0, new NextAction("rear flank", ACTION_MOVE + 5), nullptr)));
|
||||
{ NextAction("rear flank", ACTION_MOVE + 5) }));
|
||||
}
|
||||
|
||||
void WotlkDungeonVHStrategy::InitMultipliers(std::vector<Multiplier*> &multipliers)
|
||||
|
||||
@@ -10,5 +10,5 @@
|
||||
void AttackEnemyPlayersStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("enemy player near",
|
||||
NextAction::array(0, new NextAction("attack enemy player", 55.0f), nullptr)));
|
||||
{ NextAction("attack enemy player", 55.0f) }));
|
||||
}
|
||||
|
||||
@@ -9,83 +9,79 @@
|
||||
|
||||
void BGStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("bg join", relevance), nullptr)));
|
||||
triggers.push_back(new TriggerNode("bg invite active", NextAction::array(0, new NextAction("bg status check", relevance), nullptr)));
|
||||
triggers.push_back(new TriggerNode("timer", NextAction::array(0, new NextAction("bg strategy check", relevance), nullptr)));
|
||||
triggers.push_back(new TriggerNode("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::array(0, new NextAction("bg move to start", ACTION_BG), nullptr)));
|
||||
triggers.push_back(new TriggerNode("bg active", NextAction::array(0, new NextAction("bg move to objective", ACTION_BG), nullptr)));
|
||||
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("bg check objective", ACTION_BG + 1), nullptr)));
|
||||
triggers.push_back(new TriggerNode("dead", NextAction::array(0, new NextAction("bg reset objective force", ACTION_EMERGENCY), nullptr)));
|
||||
// triggers.push_back(new TriggerNode("enemy flagcarrier near", NextAction::array(0, new NextAction("attack enemy
|
||||
// flag carrier", 80.0f), nullptr))); triggers.push_back(new TriggerNode("team flagcarrier near",
|
||||
// NextAction::array(0, new NextAction("bg protect fc", 40.0f), nullptr))); triggers.push_back(new
|
||||
// TriggerNode("player has flag", NextAction::array(0, new NextAction("bg move to objective", 90.0f), nullptr)));
|
||||
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::array(0, new NextAction("bg check flag", ACTION_EMERGENCY ), nullptr)));
|
||||
triggers.push_back(new TriggerNode("enemy flagcarrier near", NextAction::array(0, new NextAction("attack enemy flag carrier", ACTION_RAID + 1.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("team flagcarrier near", NextAction::array(0, new NextAction("bg protect fc", ACTION_RAID), nullptr)));
|
||||
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("bg use buff", ACTION_BG), nullptr)));
|
||||
triggers.push_back(new TriggerNode("low health", NextAction::array(0, new NextAction("bg use buff", ACTION_MOVE), nullptr)));
|
||||
triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("bg use buff", ACTION_MOVE), nullptr)));
|
||||
triggers.push_back(new TriggerNode("player has flag", NextAction::array(0, new NextAction("bg move to objective", ACTION_EMERGENCY), nullptr)));
|
||||
triggers.push_back(new TriggerNode("timer bg", NextAction::array(0, new NextAction("bg reset objective force", ACTION_EMERGENCY), nullptr)));
|
||||
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::array(0, new NextAction("bg move to objective", ACTION_EMERGENCY), nullptr)));
|
||||
triggers.push_back(new TriggerNode("timer bg", NextAction::array(0, new NextAction("bg reset objective force", ACTION_EMERGENCY), nullptr)));
|
||||
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::array(0, new NextAction("bg check flag", ACTION_EMERGENCY), nullptr)));
|
||||
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("bg use buff", ACTION_BG), nullptr)));
|
||||
triggers.push_back(new TriggerNode("low health", NextAction::array(0, new NextAction("bg use buff", ACTION_MOVE), nullptr)));
|
||||
triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("bg use buff", ACTION_MOVE), nullptr)));
|
||||
triggers.push_back(new TriggerNode("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::array(0, new NextAction("bg check flag", ACTION_EMERGENCY), nullptr)));
|
||||
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("bg use buff", ACTION_BG), nullptr)));
|
||||
triggers.push_back(new TriggerNode("low health", NextAction::array(0, new NextAction("bg use buff", ACTION_MOVE), nullptr)));
|
||||
triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("bg use buff", ACTION_MOVE), nullptr)));
|
||||
triggers.push_back(new TriggerNode("enemy flagcarrier near", NextAction::array(0, new NextAction("attack enemy flag carrier", ACTION_RAID), nullptr)));
|
||||
triggers.push_back(new TriggerNode("player has flag",NextAction::array(0, new NextAction("bg move to objective", ACTION_EMERGENCY), nullptr)));
|
||||
triggers.push_back(new TriggerNode("bg active", { NextAction("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::array(0, new NextAction("bg check flag", ACTION_MOVE), nullptr)));
|
||||
triggers.push_back(new TriggerNode("timer", NextAction::array(0, new NextAction("enter vehicle", ACTION_MOVE + 8.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("random", NextAction::array(0, new NextAction("leave vehicle", ACTION_MOVE + 7.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("in vehicle", NextAction::array(0, new NextAction("hurl boulder", ACTION_MOVE + 9.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("in vehicle", NextAction::array(0, new NextAction("fire cannon", ACTION_MOVE + 9.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("in vehicle", NextAction::array(0, new NextAction("napalm", ACTION_MOVE + 9.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("enemy is close", NextAction::array(0, new NextAction("steam blast", ACTION_MOVE + 9.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("in vehicle", NextAction::array(0, new NextAction("ram", ACTION_MOVE + 9.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("enemy is close", NextAction::array(0, new NextAction("ram", ACTION_MOVE + 9.1f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("enemy out of melee", NextAction::array(0, new NextAction("steam rush", ACTION_MOVE + 9.2f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("in vehicle", NextAction::array(0, new NextAction("incendiary rocket", ACTION_MOVE + 9.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("in vehicle", NextAction::array(0, new NextAction("rocket blast", ACTION_MOVE + 9.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("bg active", { NextAction("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::array(0, new NextAction("blade salvo", ACTION_MOVE + 9.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("in vehicle", NextAction::array(0, new NextAction("glaive throw", ACTION_MOVE + 9.0f), nullptr)));
|
||||
// triggers.push_back(new TriggerNode("in vehicle", { NextAction("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::array(0, new NextAction("arena tactics", ACTION_BG), nullptr)));
|
||||
new TriggerNode("no possible targets", { NextAction("arena tactics", ACTION_BG)}));
|
||||
}
|
||||
|
||||
@@ -16,9 +16,9 @@ private:
|
||||
static ActionNode* tank_attack_chat_shortcut(PlayerbotAI* botAI)
|
||||
{
|
||||
return new ActionNode("tank attack chat shortcut",
|
||||
/*P*/ nullptr,
|
||||
/*A*/ nullptr,
|
||||
/*C*/ NextAction::array(0, new NextAction("attack my target", 100.0f), nullptr));
|
||||
/*P*/ {},
|
||||
/*A*/ {},
|
||||
/*C*/ { NextAction("attack my target", 100.0f) });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -26,88 +26,86 @@ void ChatCommandHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
|
||||
{
|
||||
PassTroughStrategy::InitTriggers(triggers);
|
||||
|
||||
triggers.push_back(new TriggerNode("rep", NextAction::array(0, new NextAction("reputation", relevance), nullptr)));
|
||||
triggers.push_back(new TriggerNode("q", NextAction::array(0, new NextAction("query quest", relevance),
|
||||
new NextAction("query item usage", relevance), nullptr)));
|
||||
triggers.push_back(new TriggerNode("add all loot", NextAction::array(0, new NextAction("add all loot", relevance),
|
||||
new NextAction("loot", relevance), nullptr)));
|
||||
triggers.push_back(new TriggerNode("u", NextAction::array(0, new NextAction("use", relevance), nullptr)));
|
||||
triggers.push_back(new TriggerNode("c", NextAction::array(0, new NextAction("item count", relevance), nullptr)));
|
||||
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::array(0, new NextAction("item count", relevance), nullptr)));
|
||||
triggers.push_back(new TriggerNode("inv", NextAction::array(0, new NextAction("item count", relevance), nullptr)));
|
||||
triggers.push_back(new TriggerNode("e", NextAction::array(0, new NextAction("equip", relevance), nullptr)));
|
||||
triggers.push_back(new TriggerNode("ue", NextAction::array(0, new NextAction("unequip", relevance), nullptr)));
|
||||
triggers.push_back(new TriggerNode("t", NextAction::array(0, new NextAction("trade", relevance), nullptr)));
|
||||
triggers.push_back(new TriggerNode("nt", NextAction::array(0, new NextAction("trade", relevance), nullptr)));
|
||||
triggers.push_back(new TriggerNode("s", NextAction::array(0, new NextAction("sell", relevance), nullptr)));
|
||||
triggers.push_back(new TriggerNode("b", NextAction::array(0, new NextAction("buy", relevance), nullptr)));
|
||||
triggers.push_back(new TriggerNode("r", NextAction::array(0, new NextAction("reward", relevance), nullptr)));
|
||||
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::array(0, new NextAction("attack my target", relevance), nullptr)));
|
||||
new TriggerNode("attack", { NextAction("attack my target", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("accept", NextAction::array(0, new NextAction("accept quest", relevance), nullptr)));
|
||||
new TriggerNode("accept", { NextAction("accept quest", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("follow", NextAction::array(0, new NextAction("follow chat shortcut", relevance), nullptr)));
|
||||
new TriggerNode("follow", { NextAction("follow chat shortcut", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("stay", NextAction::array(0, new NextAction("stay chat shortcut", relevance), nullptr)));
|
||||
new TriggerNode("stay", { NextAction("stay chat shortcut", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("move from group", NextAction::array(0, new NextAction("move from group chat shortcut", relevance), nullptr)));
|
||||
new TriggerNode("move from group", { NextAction("move from group chat shortcut", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("flee", NextAction::array(0, new NextAction("flee chat shortcut", relevance), nullptr)));
|
||||
new TriggerNode("flee", { NextAction("flee chat shortcut", relevance) }));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"tank attack", NextAction::array(0, new NextAction("tank attack chat shortcut", relevance), nullptr)));
|
||||
"tank attack", { NextAction("tank attack chat shortcut", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("grind", NextAction::array(0, new NextAction("grind chat shortcut", relevance), nullptr)));
|
||||
new TriggerNode("grind", { NextAction("grind chat shortcut", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("talk", NextAction::array(0, new NextAction("gossip hello", relevance),
|
||||
new NextAction("talk to quest giver", relevance), nullptr)));
|
||||
new TriggerNode("talk", { NextAction("gossip hello", relevance),
|
||||
NextAction("talk to quest giver", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("enter vehicle", NextAction::array(0, new NextAction("enter vehicle", relevance), nullptr)));
|
||||
new TriggerNode("enter vehicle", { NextAction("enter vehicle", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("leave vehicle", NextAction::array(0, new NextAction("leave vehicle", relevance), nullptr)));
|
||||
new TriggerNode("leave vehicle", { NextAction("leave vehicle", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("cast", NextAction::array(0, new NextAction("cast custom spell", relevance), nullptr)));
|
||||
new TriggerNode("cast", { NextAction("cast custom spell", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("castnc", NextAction::array(0, new NextAction("cast custom nc spell", relevance), nullptr)));
|
||||
new TriggerNode("castnc", { NextAction("cast custom nc spell", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("revive", NextAction::array(0, new NextAction("spirit healer", relevance), nullptr)));
|
||||
new TriggerNode("revive", { NextAction("spirit healer", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("runaway", NextAction::array(0, new NextAction("runaway chat shortcut", relevance), nullptr)));
|
||||
new TriggerNode("runaway", { NextAction("runaway chat shortcut", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("warning", NextAction::array(0, new NextAction("runaway chat shortcut", relevance), nullptr)));
|
||||
new TriggerNode("warning", { NextAction("runaway chat shortcut", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("max dps", NextAction::array(0, new NextAction("max dps chat shortcut", relevance), nullptr)));
|
||||
new TriggerNode("max dps", { NextAction("max dps chat shortcut", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("attackers", NextAction::array(0, new NextAction("tell attackers", relevance), nullptr)));
|
||||
new TriggerNode("attackers", { NextAction("tell attackers", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("target", NextAction::array(0, new NextAction("tell target", relevance), nullptr)));
|
||||
new TriggerNode("target", { NextAction("tell target", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("ready", NextAction::array(0, new NextAction("ready check", relevance), nullptr)));
|
||||
new TriggerNode("ready", { NextAction("ready check", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("naxx", NextAction::array(0, new NextAction("naxx chat shortcut", relevance), NULL)));
|
||||
new TriggerNode("bwl", { NextAction("bwl chat shortcut", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("bwl", NextAction::array(0, new NextAction("bwl chat shortcut", relevance), NULL)));
|
||||
new TriggerNode("dps", { NextAction("tell estimated dps", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("dps", NextAction::array(0, new NextAction("tell estimated dps", relevance), NULL)));
|
||||
new TriggerNode("disperse", { NextAction("disperse set", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("disperse", NextAction::array(0, new NextAction("disperse set", relevance), NULL)));
|
||||
new TriggerNode("open items", { NextAction("open items", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("open items", NextAction::array(0, new NextAction("open items", relevance), nullptr)));
|
||||
new TriggerNode("qi", { NextAction("query item usage", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("qi", NextAction::array(0, new NextAction("query item usage", relevance), nullptr)));
|
||||
new TriggerNode("unlock items", { NextAction("unlock items", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("unlock items", NextAction::array(0, new NextAction("unlock items", relevance), nullptr)));
|
||||
new TriggerNode("unlock traded item", { NextAction("unlock traded item", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("unlock traded item", NextAction::array(0, new NextAction("unlock traded item", relevance), nullptr)));
|
||||
triggers.push_back(
|
||||
new TriggerNode("wipe", NextAction::array(0, new NextAction("wipe", relevance), nullptr)));
|
||||
triggers.push_back(new TriggerNode("tame", NextAction::array(0, new NextAction("tame", relevance), nullptr)));
|
||||
triggers.push_back(new TriggerNode("glyphs", NextAction::array(0, new NextAction("glyphs", relevance), nullptr))); // Added for custom Glyphs
|
||||
triggers.push_back(new TriggerNode("glyph equip", NextAction::array(0, new NextAction("glyph equip", relevance), nullptr))); // Added for custom Glyphs
|
||||
triggers.push_back(new TriggerNode("pet", NextAction::array(0, new NextAction("pet", relevance), nullptr)));
|
||||
triggers.push_back(new TriggerNode("pet attack", NextAction::array(0, new NextAction("pet attack", relevance), nullptr)));
|
||||
triggers.push_back(new TriggerNode("roll", NextAction::array(0, new NextAction("roll", relevance), nullptr)));
|
||||
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)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user