Merge pull request #2489 from mod-playerbots/test-staging

Test staging
This commit is contained in:
Keleborn 2026-06-19 08:18:30 -07:00 committed by GitHub
commit 1b5c1788f3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
47 changed files with 272 additions and 921 deletions

View File

@ -89,11 +89,21 @@ AiPlayerbot.MinRandomBots = 500
AiPlayerbot.MaxRandomBots = 500 AiPlayerbot.MaxRandomBots = 500
# Randombot accounts # Randombot accounts
# If you are not using any expansion at all, you may have to set this manually, in which case please # Set to 0 to automatically calculate needed accounts based on MaxRandomBots, EnablePeriodicOnlineOffline
# ensure that RandomBotAccountCount is at least greater than (MaxRandomBots / 10 + AddClassAccountPoolSize) # and its ratio, and AddClassAccountPoolSize. Set manually if you want to create more accounts than needed.
# If the manual value is less than the required, the system will override it with the required value.
# Default: 0 (automatic) # Default: 0 (automatic)
AiPlayerbot.RandomBotAccountCount = 0 AiPlayerbot.RandomBotAccountCount = 0
# Addclass accounts
# Number of accounts created for bots reserved for the addclass command. As with randombots, each account has
# 10 bots (or 9 if WotLK is disabled), one bot for each class.
AiPlayerbot.AddClassAccountPoolSize = 50
# Prefix for created bot accounts (of any type).
# Do not change this prefix while there are existing bot accounts.
AiPlayerbot.RandomBotAccountPrefix = "rndbot"
# Delete all randombot accounts # Delete all randombot accounts
# To apply this, set the number to 1 and run the Worldserver. Once deletion is complete, if you would # 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. # like to recreate randombots, set the number back to 0 and rerun the Worldserver.
@ -128,9 +138,6 @@ AiPlayerbot.MaxAddedBots = 40
# Default: 1 (enabled) # Default: 1 (enabled)
AiPlayerbot.AddClassCommand = 1 AiPlayerbot.AddClassCommand = 1
# Set the addclass command account pool size
AiPlayerbot.AddClassAccountPoolSize = 50
# Bot group invitation permission level (0 = GM only, 1 = accept based on level, 2 = always accept) # Bot group invitation permission level (0 = GM only, 1 = accept based on level, 2 = always accept)
# Default: 1 (accept based on level) # Default: 1 (accept based on level)
AiPlayerbot.GroupInvitationPermission = 1 AiPlayerbot.GroupInvitationPermission = 1
@ -680,9 +687,6 @@ AiPlayerbot.EndFishingWithMaster = 30.0
# Enable/Disable randomly generated password for randombot accounts # Enable/Disable randomly generated password for randombot accounts
AiPlayerbot.RandomBotRandomPassword = 0 AiPlayerbot.RandomBotRandomPassword = 0
# Prefix for account names to create for randombots
AiPlayerbot.RandomBotAccountPrefix = "rndbot"
# Minimum and maximum levels for randombots # Minimum and maximum levels for randombots
AiPlayerbot.RandomBotMinLevel = 1 AiPlayerbot.RandomBotMinLevel = 1
AiPlayerbot.RandomBotMaxLevel = 80 AiPlayerbot.RandomBotMaxLevel = 80

View File

@ -175,6 +175,8 @@ public:
creators["berserking"] = &ActionContext::berserking; creators["berserking"] = &ActionContext::berserking;
creators["every man for himself"] = &ActionContext::every_man_for_himself; creators["every man for himself"] = &ActionContext::every_man_for_himself;
creators["will of the forsaken"] = &ActionContext::will_of_the_forsaken; creators["will of the forsaken"] = &ActionContext::will_of_the_forsaken;
creators["stoneform"] = &ActionContext::stoneform;
creators["escape artist"] = &ActionContext::escape_artist;
creators["use trinket"] = &ActionContext::use_trinket; creators["use trinket"] = &ActionContext::use_trinket;
creators["auto talents"] = &ActionContext::auto_talents; creators["auto talents"] = &ActionContext::auto_talents;
creators["auto share quest"] = &ActionContext::auto_share_quest; creators["auto share quest"] = &ActionContext::auto_share_quest;
@ -380,6 +382,8 @@ private:
static Action* berserking(PlayerbotAI* botAI) { return new CastBerserkingAction(botAI); } static Action* berserking(PlayerbotAI* botAI) { return new CastBerserkingAction(botAI); }
static Action* every_man_for_himself(PlayerbotAI* botAI) { return new CastEveryManForHimselfAction(botAI); } static Action* every_man_for_himself(PlayerbotAI* botAI) { return new CastEveryManForHimselfAction(botAI); }
static Action* will_of_the_forsaken(PlayerbotAI* botAI) { return new CastWillOfTheForsakenAction(botAI); } static Action* will_of_the_forsaken(PlayerbotAI* botAI) { return new CastWillOfTheForsakenAction(botAI); }
static Action* stoneform(PlayerbotAI* botAI) { return new CastStoneformAction(botAI); }
static Action* escape_artist(PlayerbotAI* botAI) { return new CastEscapeArtistAction(botAI); }
static Action* use_trinket(PlayerbotAI* botAI) { return new UseTrinketAction(botAI); } static Action* use_trinket(PlayerbotAI* botAI) { return new UseTrinketAction(botAI); }
static Action* auto_talents(PlayerbotAI* botAI) { return new AutoSetTalentsAction(botAI); } static Action* auto_talents(PlayerbotAI* botAI) { return new AutoSetTalentsAction(botAI); }
static Action* auto_share_quest(PlayerbotAI* ai) { return new AutoShareQuestAction(ai); } static Action* auto_share_quest(PlayerbotAI* ai) { return new AutoShareQuestAction(ai); }

View File

@ -259,6 +259,8 @@ bool FollowAction::isUseful()
Unit* fTarget = nullptr; Unit* fTarget = nullptr;
if (!target.empty()) if (!target.empty())
fTarget = AI_VALUE(Unit*, target); fTarget = AI_VALUE(Unit*, target);
else if (botAI->GetMaster())
fTarget = botAI->GetMaster();
else else
fTarget = AI_VALUE(Unit*, "group leader"); fTarget = AI_VALUE(Unit*, "group leader");

View File

@ -491,32 +491,13 @@ bool CastVehicleSpellAction::Execute(Event /*event*/)
bool CastEveryManForHimselfAction::isPossible() bool CastEveryManForHimselfAction::isPossible()
{ {
uint32 spellId = AI_VALUE2(uint32, "spell id", spell); uint32 spellId = AI_VALUE2(uint32, "spell id", spell);
return spellId && bot->HasSpell(spellId) && !HasSpellOrCategoryCooldown(bot, spellId); return spellId && !HasSpellOrCategoryCooldown(bot, spellId);
}
bool CastEveryManForHimselfAction::isUseful()
{
return (bot->HasAuraType(SPELL_AURA_MOD_STUN) ||
bot->HasAuraType(SPELL_AURA_MOD_FEAR) ||
bot->HasAuraType(SPELL_AURA_MOD_ROOT) ||
bot->HasAuraType(SPELL_AURA_MOD_CONFUSE) ||
bot->HasAuraType(SPELL_AURA_MOD_CHARM))
&& CastSpellAction::isUseful();
} }
bool CastWillOfTheForsakenAction::isPossible() bool CastWillOfTheForsakenAction::isPossible()
{ {
uint32 spellId = AI_VALUE2(uint32, "spell id", spell); uint32 spellId = AI_VALUE2(uint32, "spell id", spell);
return spellId && bot->HasSpell(spellId) && !HasSpellOrCategoryCooldown(bot, spellId); return spellId && !HasSpellOrCategoryCooldown(bot, spellId);
}
bool CastWillOfTheForsakenAction::isUseful()
{
return (bot->HasAuraType(SPELL_AURA_MOD_FEAR) ||
bot->HasAuraType(SPELL_AURA_MOD_CHARM) ||
bot->HasAuraType(SPELL_AURA_AOE_CHARM) ||
bot->HasAuraWithMechanic(1 << MECHANIC_SLEEP))
&& CastSpellAction::isUseful();
} }
bool UseTrinketAction::Execute(Event /*event*/) bool UseTrinketAction::Execute(Event /*event*/)

View File

@ -313,7 +313,6 @@ public:
std::string const GetTargetName() override { return "self target"; } std::string const GetTargetName() override { return "self target"; }
bool isPossible() override; bool isPossible() override;
bool isUseful() override;
}; };
class CastWillOfTheForsakenAction : public CastSpellAction class CastWillOfTheForsakenAction : public CastSpellAction
@ -323,7 +322,22 @@ public:
std::string const GetTargetName() override { return "self target"; } std::string const GetTargetName() override { return "self target"; }
bool isPossible() override; bool isPossible() override;
bool isUseful() override; };
class CastStoneformAction : public CastSpellAction
{
public:
CastStoneformAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "stoneform") {}
std::string const GetTargetName() override { return "self target"; }
};
class CastEscapeArtistAction : public CastSpellAction
{
public:
CastEscapeArtistAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "escape artist") {}
std::string const GetTargetName() override { return "self target"; }
}; };
class UseTrinketAction : public Action class UseTrinketAction : public Action

View File

@ -4,45 +4,104 @@
*/ */
#include "RacialsStrategy.h" #include "RacialsStrategy.h"
#include "Playerbots.h"
class RacialsStrategyActionNodeFactory : public NamedObjectFactory<ActionNode> namespace
{ {
public: constexpr uint32 SPELL_ARCANE_TORRENT_ENERGY = 25046;
RacialsStrategyActionNodeFactory() { creators["lifeblood"] = &lifeblood; } constexpr uint32 SPELL_ARCANE_TORRENT_MANA = 28730;
constexpr uint32 SPELL_ARCANE_TORRENT_RUNIC_POWER = 50613;
private: constexpr uint32 SPELL_WAR_STOMP = 20549;
static ActionNode* lifeblood(PlayerbotAI* /*botAI*/) constexpr uint32 SPELL_BERSERKING = 26297;
{ constexpr uint32 SPELL_EVERY_MAN_FOR_HIMSELF = 59752;
return new ActionNode("lifeblood", constexpr uint32 SPELL_WILL_OF_THE_FORSAKEN = 7744;
/*P*/ {}, constexpr uint32 SPELL_STONEFORM = 20594;
/*A*/ { NextAction("gift of the naaru") }, constexpr uint32 SPELL_ESCAPE_ARTIST = 20589;
/*C*/ {});
}
};
void RacialsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("low health", { NextAction("lifeblood", ACTION_NORMAL + 5) }));
triggers.push_back(
new TriggerNode("medium aoe", { NextAction("war stomp", ACTION_NORMAL + 5) }));
triggers.push_back(new TriggerNode(
"low mana", { NextAction("arcane torrent", ACTION_NORMAL + 5) }));
triggers.push_back(new TriggerNode(
"generic boost", { NextAction("blood fury", ACTION_NORMAL + 5),
NextAction("berserking", ACTION_NORMAL + 5),
NextAction("use trinket", ACTION_NORMAL + 4) }));
triggers.push_back(new TriggerNode(
"loss of control", { NextAction("every man for himself", ACTION_EMERGENCY + 1) }));
triggers.push_back(new TriggerNode(
"fear charm sleep", { NextAction("will of the forsaken", ACTION_EMERGENCY + 1) }));
} }
RacialsStrategy::RacialsStrategy(PlayerbotAI* botAI) : Strategy(botAI) RacialsStrategy::RacialsStrategy(PlayerbotAI* botAI) : Strategy(botAI)
{ {
actionNodeFactories.Add(new RacialsStrategyActionNodeFactory()); // No custom ActionNodeFactory needed
}
void RacialsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
Player* bot = botAI->GetBot();
if (bot->HasSpell(SPELL_ARCANE_TORRENT_MANA))
{
triggers.push_back(new TriggerNode(
"low mana", { NextAction("arcane torrent", ACTION_NORMAL + 5) }));
}
if (bot->HasSpell(SPELL_ARCANE_TORRENT_ENERGY))
{
triggers.push_back(new TriggerNode(
"low energy", { NextAction("arcane torrent", ACTION_NORMAL + 5) }));
}
if (bot->HasSpell(SPELL_ARCANE_TORRENT_RUNIC_POWER))
{
// No low runic power trigger exists; this trigger should be modified if one is added
triggers.push_back(new TriggerNode(
"generic boost", { NextAction("arcane torrent", ACTION_NORMAL + 5) }));
}
if (bot->HasSpell(SPELL_WAR_STOMP))
{
triggers.push_back(new TriggerNode(
"medium aoe", { NextAction("war stomp", ACTION_NORMAL + 5) }));
}
if (bot->HasSpell(SPELL_BERSERKING))
{
triggers.push_back(new TriggerNode(
"generic boost", { NextAction("berserking", ACTION_NORMAL + 5) }));
}
if (bot->HasSpell(SPELL_EVERY_MAN_FOR_HIMSELF))
{
triggers.push_back(new TriggerNode(
"loss of control", { NextAction("every man for himself", ACTION_EMERGENCY + 1) }));
}
if (bot->HasSpell(SPELL_WILL_OF_THE_FORSAKEN))
{
triggers.push_back(new TriggerNode(
"fear charm sleep", { NextAction("will of the forsaken", ACTION_EMERGENCY + 1) }));
}
if (bot->HasSpell(SPELL_STONEFORM))
{
triggers.push_back(new TriggerNode(
"poison disease bleed", { NextAction("stoneform", ACTION_DISPEL) }));
}
if (bot->HasSpell(SPELL_ESCAPE_ARTIST))
{
triggers.push_back(new TriggerNode(
"movement impaired", { NextAction("escape artist", ACTION_EMERGENCY + 1) }));
}
if (botAI->HasSpell("blood fury"))
{
triggers.push_back(new TriggerNode(
"generic boost", { NextAction("blood fury", ACTION_NORMAL + 5) }));
}
if (botAI->HasSpell("gift of the naaru"))
{
// Currently targets self only
triggers.push_back(new TriggerNode(
"medium health", { NextAction("gift of the naaru", ACTION_LIGHT_HEAL + 5) }));
}
if (botAI->HasSpell("lifeblood"))
{
triggers.push_back(new TriggerNode(
"medium health", { NextAction("lifeblood", ACTION_LIGHT_HEAL + 5) }));
}
triggers.push_back(new TriggerNode(
"generic boost", { NextAction("use trinket", ACTION_NORMAL + 4) }));
} }

View File

@ -32,5 +32,5 @@ void UsePotionsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
triggers.push_back(new TriggerNode( triggers.push_back(new TriggerNode(
"critical health", { NextAction("healthstone", ACTION_MEDIUM_HEAL + 1) })); "critical health", { NextAction("healthstone", ACTION_MEDIUM_HEAL + 1) }));
triggers.push_back( triggers.push_back(
new TriggerNode("low mana", { NextAction("mana potion", ACTION_EMERGENCY) })); new TriggerNode("medium mana", { NextAction("mana potion", ACTION_EMERGENCY) }));
} }

View File

@ -491,6 +491,19 @@ bool FearSleepSapTrigger::IsActive()
bot->HasAuraWithMechanic(1 << MECHANIC_SAPPED); bot->HasAuraWithMechanic(1 << MECHANIC_SAPPED);
} }
bool PoisonDiseaseBleedTrigger::IsActive()
{
return botAI->HasAuraToDispel(bot, DISPEL_POISON) ||
botAI->HasAuraToDispel(bot, DISPEL_DISEASE) ||
bot->HasAuraWithMechanic(1 << MECHANIC_BLEED);
}
bool MovementImpairedTrigger::IsActive()
{
return botAI->IsMovementImpaired(bot) &&
!botAI->HasAnyAuraOf(bot, "stealth", "prowl", nullptr);
}
bool HasAuraStackTrigger::IsActive() bool HasAuraStackTrigger::IsActive()
{ {
return botAI->GetAura(getName(), GetTarget(), false, true, stack); return botAI->GetAura(getName(), GetTarget(), false, true, stack);

View File

@ -752,6 +752,22 @@ public:
bool IsActive() override; bool IsActive() override;
}; };
class PoisonDiseaseBleedTrigger : public Trigger
{
public:
PoisonDiseaseBleedTrigger(PlayerbotAI* botAI) : Trigger(botAI, "poison disease bleed", 1) {}
bool IsActive() override;
};
class MovementImpairedTrigger : public Trigger
{
public:
MovementImpairedTrigger(PlayerbotAI* botAI) : Trigger(botAI, "movement impaired", 1) {}
bool IsActive() override;
};
class IsSwimmingTrigger : public Trigger class IsSwimmingTrigger : public Trigger
{ {
public: public:

View File

@ -66,6 +66,8 @@ public:
creators["loss of control"] = &TriggerContext::loss_of_control; creators["loss of control"] = &TriggerContext::loss_of_control;
creators["fear charm sleep"] = &TriggerContext::fear_charm_sleep; creators["fear charm sleep"] = &TriggerContext::fear_charm_sleep;
creators["fear sleep sap"] = &TriggerContext::fear_sleep_sap; creators["fear sleep sap"] = &TriggerContext::fear_sleep_sap;
creators["poison disease bleed"] = &TriggerContext::poison_disease_bleed;
creators["movement impaired"] = &TriggerContext::movement_impaired;
creators["protect party member"] = &TriggerContext::protect_party_member; creators["protect party member"] = &TriggerContext::protect_party_member;
@ -382,6 +384,8 @@ private:
static Trigger* loss_of_control(PlayerbotAI* botAI) { return new LossOfControlTrigger(botAI); } static Trigger* loss_of_control(PlayerbotAI* botAI) { return new LossOfControlTrigger(botAI); }
static Trigger* fear_charm_sleep(PlayerbotAI* botAI) { return new FearCharmSleepTrigger(botAI); } static Trigger* fear_charm_sleep(PlayerbotAI* botAI) { return new FearCharmSleepTrigger(botAI); }
static Trigger* fear_sleep_sap(PlayerbotAI* botAI) { return new FearSleepSapTrigger(botAI); } static Trigger* fear_sleep_sap(PlayerbotAI* botAI) { return new FearSleepSapTrigger(botAI); }
static Trigger* poison_disease_bleed(PlayerbotAI* botAI) { return new PoisonDiseaseBleedTrigger(botAI); }
static Trigger* movement_impaired(PlayerbotAI* botAI) { return new MovementImpairedTrigger(botAI); }
static Trigger* PartyMemberCriticalHealth(PlayerbotAI* botAI) static Trigger* PartyMemberCriticalHealth(PlayerbotAI* botAI)
{ {
return new PartyMemberCriticalHealthTrigger(botAI); return new PartyMemberCriticalHealthTrigger(botAI);

View File

@ -51,18 +51,6 @@ private:
static Strategy* blood(PlayerbotAI* botAI) { return new BloodDKStrategy(botAI); } static Strategy* blood(PlayerbotAI* botAI) { return new BloodDKStrategy(botAI); }
}; };
class DeathKnightDKBuffStrategyFactoryInternal : public NamedObjectContext<Strategy>
{
public:
DeathKnightDKBuffStrategyFactoryInternal() : NamedObjectContext<Strategy>(false, true)
{
creators["bdps"] = &DeathKnightDKBuffStrategyFactoryInternal::bdps;
}
private:
static Strategy* bdps(PlayerbotAI* botAI) { return new DKBuffDpsStrategy(botAI); }
};
class DeathKnightTriggerFactoryInternal : public NamedObjectContext<Trigger> class DeathKnightTriggerFactoryInternal : public NamedObjectContext<Trigger>
{ {
public: public:
@ -299,7 +287,6 @@ void DKAiObjectContext::BuildSharedStrategyContexts(SharedNamedObjectContextList
AiObjectContext::BuildSharedStrategyContexts(strategyContexts); AiObjectContext::BuildSharedStrategyContexts(strategyContexts);
strategyContexts.Add(new DeathKnightStrategyFactoryInternal()); strategyContexts.Add(new DeathKnightStrategyFactoryInternal());
strategyContexts.Add(new DeathKnightCombatStrategyFactoryInternal()); strategyContexts.Add(new DeathKnightCombatStrategyFactoryInternal());
strategyContexts.Add(new DeathKnightDKBuffStrategyFactoryInternal());
} }
void DKAiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList<Action>& actionContexts) void DKAiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList<Action>& actionContexts)

View File

@ -5,36 +5,9 @@
#include "GenericDKNonCombatStrategy.h" #include "GenericDKNonCombatStrategy.h"
class GenericDKNonCombatStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
GenericDKNonCombatStrategyActionNodeFactory()
{
creators["bone shield"] = &bone_shield;
creators["horn of winter"] = &horn_of_winter;
}
private:
static ActionNode* bone_shield([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("bone shield",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* horn_of_winter([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("horn of winter",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
};
GenericDKNonCombatStrategy::GenericDKNonCombatStrategy(PlayerbotAI* botAI) : NonCombatStrategy(botAI) GenericDKNonCombatStrategy::GenericDKNonCombatStrategy(PlayerbotAI* botAI) : NonCombatStrategy(botAI)
{ {
actionNodeFactories.Add(new GenericDKNonCombatStrategyActionNodeFactory()); // No custom ActionNodeFactory needed
} }
void GenericDKNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& triggers) void GenericDKNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
@ -47,8 +20,3 @@ void GenericDKNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
triggers.push_back( triggers.push_back(
new TriggerNode("bone shield", { NextAction("bone shield", 21.0f) })); new TriggerNode("bone shield", { NextAction("bone shield", 21.0f) }));
} }
void DKBuffDpsStrategy::InitTriggers(std::vector<TriggerNode*>& /*triggers*/)
{
}

View File

@ -20,13 +20,4 @@ public:
void InitTriggers(std::vector<TriggerNode*>& triggers) override; void InitTriggers(std::vector<TriggerNode*>& triggers) override;
}; };
class DKBuffDpsStrategy : public Strategy
{
public:
DKBuffDpsStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "bdps"; }
};
#endif #endif

View File

@ -1,5 +1,6 @@
#/* /*
* 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. * 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 "GenericDKStrategy.h" #include "GenericDKStrategy.h"
@ -12,53 +13,12 @@ class GenericDKStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
public: public:
GenericDKStrategyActionNodeFactory() GenericDKStrategyActionNodeFactory()
{ {
// blood creators["killing machine"] = &killing_machine;
// creators["rune tap"] = &rune_tap; cd
// creators["vampiric blood"] = &vampiric_blood;
// creators["death pact"] = &death_pact;
// creators["hysteria"] = &hysteria; boost party
// creators["dancing rune weapon"] = &dancing_rune_weapon; //cd
// creators["dark command"] = &dark_command; taunt
// frost
// creators["chains of ice"] = &chains_of_ice;
// creators["icy clutch"] = &icy_clutch;
creators["horn of winter"] = &horn_of_winter;
creators["killing machine"] = &killing_machine; // buff
// creators["deathchill"] = &deathchill; //boost
creators["icebound fortitude"] = &icebound_fortitude;
// creators["mind freeze"] = &mind_freeze; interrupt
// creators["empower rune weapon"] = &empower_rune_weapon; boost
// creators["hungering cold"] = &hungering_cold; snare
// creators["unbreakable armor"] = &unbreakable_armor; boost +cd
// creators["improved icy talons"] = &improved_icy_talons; boost party
// unholy
creators["death and decay"] = &death_and_decay;
// 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; cd
creators["anti magic zone"] = &anti_magic_zone; creators["anti magic zone"] = &anti_magic_zone;
// creators["ghoul frenzy"] = &ghoul_frenzy;
creators["corpse explosion"] = &corpse_explosion;
creators["bone shield"] = &bone_shield;
creators["heart strike"] = &heart_strike;
creators["death grip"] = &death_grip; creators["death grip"] = &death_grip;
creators["plague strike"] = &plague_strike;
creators["pestilence"] = &pestilence;
creators["icy touch"] = &icy_touch;
} }
private: private:
static ActionNode* death_coil([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("death coil",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* death_grip([[maybe_unused]] PlayerbotAI* botAI) static ActionNode* death_grip([[maybe_unused]] PlayerbotAI* botAI)
{ {
return new ActionNode("death grip", return new ActionNode("death grip",
@ -67,54 +27,6 @@ private:
/*C*/ {}); /*C*/ {});
} }
static ActionNode* plague_strike([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("plague strike",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* icy_touch([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("icy touch",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* heart_strike([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("heart strike",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* pestilence([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("pestilence",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* horn_of_winter([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("horn of winter",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* bone_shield([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("bone shield",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* killing_machine([[maybe_unused]] PlayerbotAI* botAI) static ActionNode* killing_machine([[maybe_unused]] PlayerbotAI* botAI)
{ {
return new ActionNode("killing machine", return new ActionNode("killing machine",
@ -123,22 +35,6 @@ private:
/*C*/ {}); /*C*/ {});
} }
static ActionNode* corpse_explosion([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("corpse explosion",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* death_and_decay([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("death and decay",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* anti_magic_zone([[maybe_unused]] PlayerbotAI* botAI) static ActionNode* anti_magic_zone([[maybe_unused]] PlayerbotAI* botAI)
{ {
return new ActionNode("anti magic zone", return new ActionNode("anti magic zone",
@ -146,14 +42,6 @@ private:
/*A*/ { NextAction("anti magic shell") }, /*A*/ { NextAction("anti magic shell") },
/*C*/ {}); /*C*/ {});
} }
static ActionNode* icebound_fortitude([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("icebound fortitude",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
}; };
GenericDKStrategy::GenericDKStrategy(PlayerbotAI* botAI) : MeleeCombatStrategy(botAI) GenericDKStrategy::GenericDKStrategy(PlayerbotAI* botAI) : MeleeCombatStrategy(botAI)

View File

@ -1,5 +1,6 @@
/* /*
* 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. * Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/ */
#include "UnholyDKStrategy.h" #include "UnholyDKStrategy.h"

View File

@ -13,16 +13,11 @@ public:
BearDruidStrategyActionNodeFactory() BearDruidStrategyActionNodeFactory()
{ {
creators["feral charge - bear"] = &feral_charge_bear; creators["feral charge - bear"] = &feral_charge_bear;
creators["swipe (bear)"] = &swipe_bear;
creators["bear form"] = &bear_form;
creators["dire bear form"] = &dire_bear_form; creators["dire bear form"] = &dire_bear_form;
creators["mangle (bear)"] = &mangle_bear;
creators["maul"] = &maul; creators["maul"] = &maul;
creators["bash"] = &bash; creators["bash"] = &bash;
creators["swipe"] = &swipe; creators["swipe"] = &swipe;
creators["lacerate"] = &lacerate; creators["lacerate"] = &lacerate;
creators["demoralizing roar"] = &demoralizing_roar;
creators["taunt spell"] = &growl;
} }
private: private:
@ -36,26 +31,6 @@ private:
); );
} }
static ActionNode* swipe_bear([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"swipe (bear)",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* bear_form([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"bear form",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* dire_bear_form([[maybe_unused]] PlayerbotAI* botAI) static ActionNode* dire_bear_form([[maybe_unused]] PlayerbotAI* botAI)
{ {
return new ActionNode( return new ActionNode(
@ -66,16 +41,6 @@ private:
); );
} }
static ActionNode* mangle_bear([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"mangle (bear)",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* maul([[maybe_unused]] PlayerbotAI* botAI) static ActionNode* maul([[maybe_unused]] PlayerbotAI* botAI)
{ {
return new ActionNode( return new ActionNode(
@ -115,26 +80,6 @@ private:
/*C*/ {} /*C*/ {}
); );
} }
static ActionNode* growl([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"growl",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* demoralizing_roar([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"demoralizing roar",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
}; };
BearDruidStrategy::BearDruidStrategy(PlayerbotAI* botAI) : FeralDruidStrategy(botAI) BearDruidStrategy::BearDruidStrategy(PlayerbotAI* botAI) : FeralDruidStrategy(botAI)

View File

@ -12,41 +12,15 @@ class CatDruidStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
public: public:
CatDruidStrategyActionNodeFactory() CatDruidStrategyActionNodeFactory()
{ {
creators["faerie fire (feral)"] = &faerie_fire_feral;
creators["melee"] = &melee;
creators["feral charge - cat"] = &feral_charge_cat; creators["feral charge - cat"] = &feral_charge_cat;
creators["cat form"] = &cat_form; creators["cat form"] = &cat_form;
creators["claw"] = &claw; creators["claw"] = &claw;
creators["mangle (cat)"] = &mangle_cat;
creators["rake"] = &rake;
creators["ferocious bite"] = &ferocious_bite;
creators["rip"] = &rip;
creators["pounce"] = &pounce; creators["pounce"] = &pounce;
creators["ravage"] = &ravage; creators["ravage"] = &ravage;
creators["prowl"] = &prowl; creators["prowl"] = &prowl;
} }
private: private:
static ActionNode* faerie_fire_feral([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"faerie fire (feral)",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* melee([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"melee",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* feral_charge_cat([[maybe_unused]] PlayerbotAI* botAI) static ActionNode* feral_charge_cat([[maybe_unused]] PlayerbotAI* botAI)
{ {
return new ActionNode( return new ActionNode(
@ -77,46 +51,6 @@ private:
); );
} }
static ActionNode* mangle_cat([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"mangle (cat)",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* rake([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"rake",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* ferocious_bite([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"ferocious bite",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* rip([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"rip",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* ravage([[maybe_unused]] PlayerbotAI* botAI) static ActionNode* ravage([[maybe_unused]] PlayerbotAI* botAI)
{ {
return new ActionNode( return new ActionNode(
@ -146,7 +80,6 @@ private:
/*C*/ {} /*C*/ {}
); );
} }
}; };
CatDruidStrategy::CatDruidStrategy(PlayerbotAI* botAI) : FeralDruidStrategy(botAI) CatDruidStrategy::CatDruidStrategy(PlayerbotAI* botAI) : FeralDruidStrategy(botAI)

View File

@ -109,7 +109,6 @@ private:
/*A*/ {}, /*A*/ {},
/*C*/ {}); /*C*/ {});
} }
}; };
GenericDruidNonCombatStrategy::GenericDruidNonCombatStrategy(PlayerbotAI* botAI) : NonCombatStrategy(botAI) GenericDruidNonCombatStrategy::GenericDruidNonCombatStrategy(PlayerbotAI* botAI) : NonCombatStrategy(botAI)

View File

@ -14,76 +14,12 @@ class GenericDruidStrategyActionNodeFactory : public NamedObjectFactory<ActionNo
public: public:
GenericDruidStrategyActionNodeFactory() GenericDruidStrategyActionNodeFactory()
{ {
creators["melee"] = &melee;
creators["caster form"] = &caster_form;
creators["cure poison"] = &cure_poison;
creators["cure poison on party"] = &cure_poison_on_party;
creators["abolish poison"] = &abolish_poison;
creators["abolish poison on party"] = &abolish_poison_on_party;
creators["rebirth"] = &rebirth;
creators["entangling roots on cc"] = &entangling_roots_on_cc; creators["entangling roots on cc"] = &entangling_roots_on_cc;
creators["cyclone on cc"] = &cyclone_on_cc; creators["cyclone on cc"] = &cyclone_on_cc;
creators["hibernate on cc"] = &hibernate_on_cc; creators["hibernate on cc"] = &hibernate_on_cc;
creators["innervate"] = &innervate;
} }
private: private:
static ActionNode* melee([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("melee",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* caster_form([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("caster form",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* cure_poison([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("cure poison",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* cure_poison_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("cure poison on party",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* abolish_poison([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("abolish poison",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* abolish_poison_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("abolish poison on party",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* rebirth([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("rebirth",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* entangling_roots_on_cc([[maybe_unused]] PlayerbotAI* botAI) static ActionNode* entangling_roots_on_cc([[maybe_unused]] PlayerbotAI* botAI)
{ {
return new ActionNode("entangling roots on cc", return new ActionNode("entangling roots on cc",
@ -107,14 +43,6 @@ private:
/*A*/ {}, /*A*/ {},
/*C*/ {}); /*C*/ {});
} }
static ActionNode* innervate([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("innervate",
/*P*/ {},
/*A*/ { NextAction("mana potion") },
/*C*/ {});
}
}; };
GenericDruidStrategy::GenericDruidStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) GenericDruidStrategy::GenericDruidStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI)

View File

@ -7,26 +7,9 @@
#include "Playerbots.h" #include "Playerbots.h"
class RestoDruidStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
RestoDruidStrategyActionNodeFactory() {
creators["nourish on party"] = &nourish_on_party;
}
private:
static ActionNode* nourish_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("nourish on party",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
};
RestoDruidStrategy::RestoDruidStrategy(PlayerbotAI* botAI) : GenericDruidStrategy(botAI) RestoDruidStrategy::RestoDruidStrategy(PlayerbotAI* botAI) : GenericDruidStrategy(botAI)
{ {
actionNodeFactories.Add(new RestoDruidStrategyActionNodeFactory()); // No custom ActionNodeFactory needed
} }
void RestoDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers) void RestoDruidStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)

View File

@ -28,7 +28,6 @@ public:
creators["frostbolt"] = &frostbolt; creators["frostbolt"] = &frostbolt;
creators["frostfire bolt"] = &frostfire_bolt; creators["frostfire bolt"] = &frostfire_bolt;
creators["scorch"] = &scorch; creators["scorch"] = &scorch;
creators["evocation"] = &evocation;
creators["remove curse"] = &remove_curse; creators["remove curse"] = &remove_curse;
creators["remove curse on party"] = &remove_curse_on_party; creators["remove curse on party"] = &remove_curse_on_party;
creators["fireball"] = &fireball; creators["fireball"] = &fireball;
@ -59,14 +58,6 @@ private:
/*C*/ {}); /*C*/ {});
} }
static ActionNode* evocation([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("evocation",
/*P*/ {},
/*A*/ { NextAction("mana potion") },
/*C*/ {});
}
static ActionNode* remove_curse([[maybe_unused]] PlayerbotAI* botAI) static ActionNode* remove_curse([[maybe_unused]] PlayerbotAI* botAI)
{ {
return new ActionNode("remove curse", return new ActionNode("remove curse",
@ -127,7 +118,6 @@ void GenericMageStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
else if (bot->HasSpell(SPELL_CONJURE_MANA_AGATE)) else if (bot->HasSpell(SPELL_CONJURE_MANA_AGATE))
triggers.push_back(new TriggerNode("high mana", { NextAction("use mana agate", 90.0f) })); triggers.push_back(new TriggerNode("high mana", { NextAction("use mana agate", 90.0f) }));
triggers.push_back(new TriggerNode("medium mana", { NextAction("mana potion", 90.0f) }));
triggers.push_back(new TriggerNode("low mana", { NextAction("evocation", 90.0f) })); triggers.push_back(new TriggerNode("low mana", { NextAction("evocation", 90.0f) }));
// Counterspell / Spellsteal Triggers // Counterspell / Spellsteal Triggers

View File

@ -16,7 +16,6 @@ public:
creators["sanctity aura"] = &sanctity_aura; creators["sanctity aura"] = &sanctity_aura;
creators["retribution aura"] = &retribution_aura; creators["retribution aura"] = &retribution_aura;
creators["blessing of might"] = &blessing_of_might; creators["blessing of might"] = &blessing_of_might;
creators["crusader strike"] = &crusader_strike;
creators["repentance"] = &repentance; creators["repentance"] = &repentance;
creators["repentance on enemy healer"] = &repentance_on_enemy_healer; creators["repentance on enemy healer"] = &repentance_on_enemy_healer;
creators["repentance on snare target"] = &repentance_on_snare_target; creators["repentance on snare target"] = &repentance_on_snare_target;
@ -34,16 +33,6 @@ private:
); );
} }
static ActionNode* crusader_strike([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"crusader strike",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* repentance([[maybe_unused]] PlayerbotAI* botAI) static ActionNode* repentance([[maybe_unused]] PlayerbotAI* botAI)
{ {
return new ActionNode( return new ActionNode(

View File

@ -56,7 +56,6 @@ void PaladinCureStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
void PaladinBoostStrategy::InitTriggers(std::vector<TriggerNode*>& /*triggers*/) void PaladinBoostStrategy::InitTriggers(std::vector<TriggerNode*>& /*triggers*/)
{ {
// triggers.push_back(new TriggerNode("divine favor", { NextAction("divine favor", // triggers.push_back(new TriggerNode("divine favor", { NextAction("divine favor",
// ACTION_HIGH + 1) })); // ACTION_HIGH + 1) }));
} }

View File

@ -16,10 +16,8 @@ class GenericPaladinStrategyActionNodeFactory : public NamedObjectFactory<Action
public: public:
GenericPaladinStrategyActionNodeFactory() GenericPaladinStrategyActionNodeFactory()
{ {
// creators["seal of light"] = &seal_of_light;
creators["cleanse poison"] = &cleanse_poison; creators["cleanse poison"] = &cleanse_poison;
creators["cleanse disease"] = &cleanse_disease; creators["cleanse disease"] = &cleanse_disease;
creators["cleanse magic"] = &cleanse_magic;
creators["cleanse poison on party"] = &cleanse_poison_on_party; creators["cleanse poison on party"] = &cleanse_poison_on_party;
creators["cleanse disease on party"] = &cleanse_disease_on_party; creators["cleanse disease on party"] = &cleanse_disease_on_party;
creators["seal of corruption"] = &seal_of_corruption; creators["seal of corruption"] = &seal_of_corruption;
@ -28,22 +26,11 @@ public:
creators["seal of wisdom"] = &seal_of_wisdom; creators["seal of wisdom"] = &seal_of_wisdom;
creators["seal of justice"] = &seal_of_justice; creators["seal of justice"] = &seal_of_justice;
creators["hand of reckoning"] = &hand_of_reckoning; creators["hand of reckoning"] = &hand_of_reckoning;
creators["judgement"] = &judgement;
creators["judgement of wisdom"] = &judgement_of_wisdom; creators["judgement of wisdom"] = &judgement_of_wisdom;
creators["divine shield"] = &divine_shield; creators["divine shield"] = &divine_shield;
creators["flash of light"] = &flash_of_light; creators["flash of light"] = &flash_of_light;
creators["flash of light on party"] = &flash_of_light_on_party; creators["flash of light on party"] = &flash_of_light_on_party;
creators["holy wrath"] = &holy_wrath;
creators["lay on hands"] = &lay_on_hands;
creators["lay on hands on party"] = &lay_on_hands_on_party;
creators["hammer of wrath"] = &hammer_of_wrath;
creators["retribution aura"] = &retribution_aura; creators["retribution aura"] = &retribution_aura;
creators["blessing of kings"] = &blessing_of_kings;
creators["blessing of wisdom"] = &blessing_of_wisdom;
creators["blessing of kings on party"] = &blessing_of_kings_on_party;
creators["blessing of wisdom on party"] = &blessing_of_wisdom_on_party;
creators["blessing of sanctuary on party"] = &blessing_of_sanctuary_on_party;
creators["blessing of sanctuary"] = &blessing_of_sanctuary;
creators["taunt spell"] = &hand_of_reckoning; creators["taunt spell"] = &hand_of_reckoning;
creators["righteous defense"] = &righteous_defense; creators["righteous defense"] = &righteous_defense;
creators["avenger's shield"] = &avengers_shield; creators["avenger's shield"] = &avengers_shield;
@ -51,48 +38,6 @@ public:
} }
private: private:
static ActionNode* blessing_of_sanctuary(PlayerbotAI* /* ai */)
{
return new ActionNode("blessing of sanctuary",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* blessing_of_kings(PlayerbotAI* /* ai */)
{
return new ActionNode("blessing of kings",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* blessing_of_wisdom(PlayerbotAI* /* ai */)
{
return new ActionNode("blessing of wisdom",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* blessing_of_kings_on_party(PlayerbotAI* /* ai */)
{
return new ActionNode("blessing of kings on party",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* blessing_of_wisdom_on_party(PlayerbotAI* /* ai */)
{
return new ActionNode("blessing of wisdom on party",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* blessing_of_sanctuary_on_party(PlayerbotAI* /* ai */)
{
return new ActionNode("blessing of sanctuary on party",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* retribution_aura(PlayerbotAI* /* ai */) static ActionNode* retribution_aura(PlayerbotAI* /* ai */)
{ {
return new ActionNode("retribution aura", return new ActionNode("retribution aura",
@ -100,28 +45,6 @@ private:
/*A*/ { NextAction("devotion aura") }, /*A*/ { NextAction("devotion aura") },
/*C*/ {}); /*C*/ {});
} }
static ActionNode* lay_on_hands(PlayerbotAI* /* ai */)
{
return new ActionNode("lay on hands",
/*P*/ {},
/*A*/ {}, // { NextAction("divine shield"), new
// NextAction("flash of light"), NULL),
/*C*/ {});
}
static ActionNode* lay_on_hands_on_party(PlayerbotAI* /* ai */)
{
return new ActionNode("lay on hands on party",
/*P*/ {},
/*A*/ {}, // { NextAction("flash of light"), NULL),
/*C*/ {});
}
// static ActionNode* seal_of_light(PlayerbotAI* /* ai */)
// {
// return new ActionNode ("seal of light",
// /*P*/ NULL,
// /*A*/ { NextAction("seal of justice"), NULL),
// /*C*/ NULL);
// }
static ActionNode* cleanse_poison(PlayerbotAI* /* ai */) static ActionNode* cleanse_poison(PlayerbotAI* /* ai */)
{ {
return new ActionNode("cleanse poison", return new ActionNode("cleanse poison",
@ -129,13 +52,6 @@ private:
/*A*/ { NextAction("purify poison") }, /*A*/ { NextAction("purify poison") },
/*C*/ {}); /*C*/ {});
} }
static ActionNode* cleanse_magic(PlayerbotAI* /* ai */)
{
return new ActionNode("cleanse magic",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* cleanse_disease(PlayerbotAI* /* ai */) static ActionNode* cleanse_disease(PlayerbotAI* /* ai */)
{ {
return new ActionNode("cleanse disease", return new ActionNode("cleanse disease",
@ -227,13 +143,6 @@ private:
/*A*/ { NextAction("judgement of light") }, /*A*/ { NextAction("judgement of light") },
/*C*/ {}); /*C*/ {});
} }
static ActionNode* judgement(PlayerbotAI* /* ai */)
{
return new ActionNode("judgement",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* divine_shield(PlayerbotAI* /* ai */) static ActionNode* divine_shield(PlayerbotAI* /* ai */)
{ {
return new ActionNode("divine shield", return new ActionNode("divine shield",
@ -255,20 +164,6 @@ private:
/*A*/ { NextAction("holy light on party") }, /*A*/ { NextAction("holy light on party") },
/*C*/ {}); /*C*/ {});
} }
static ActionNode* holy_wrath(PlayerbotAI* /* ai */)
{
return new ActionNode("holy wrath",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
static ActionNode* hammer_of_wrath(PlayerbotAI* /* ai */)
{
return new ActionNode("hammer of wrath",
/*P*/ {},
/*A*/ {},
/*C*/ {});
}
}; };
#endif #endif

View File

@ -8,13 +8,9 @@
#include "Playerbots.h" #include "Playerbots.h"
#include "Strategy.h" #include "Strategy.h"
class HealPaladinStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
};
HealPaladinStrategy::HealPaladinStrategy(PlayerbotAI* botAI) : GenericPaladinStrategy(botAI) HealPaladinStrategy::HealPaladinStrategy(PlayerbotAI* botAI) : GenericPaladinStrategy(botAI)
{ {
actionNodeFactories.Add(new HealPaladinStrategyActionNodeFactory()); // No custom ActionNodeFactory needed
} }
std::vector<NextAction> HealPaladinStrategy::getDefaultActions() std::vector<NextAction> HealPaladinStrategy::getDefaultActions()

View File

@ -18,8 +18,6 @@ public:
creators["seal of vengeance"] = &seal_of_vengeance; creators["seal of vengeance"] = &seal_of_vengeance;
creators["seal of command"] = &seal_of_command; creators["seal of command"] = &seal_of_command;
creators["blessing of might"] = &blessing_of_might; creators["blessing of might"] = &blessing_of_might;
creators["crusader strike"] = &crusader_strike;
creators["divine plea"] = &divine_plea;
} }
private: private:
@ -72,26 +70,6 @@ private:
/*C*/ {} /*C*/ {}
); );
} }
static ActionNode* crusader_strike([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"crusader strike",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* divine_plea([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"divine plea",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
}; };
OffhealRetPaladinStrategy::OffhealRetPaladinStrategy(PlayerbotAI* botAI) : GenericPaladinStrategy(botAI) OffhealRetPaladinStrategy::OffhealRetPaladinStrategy(PlayerbotAI* botAI) : GenericPaladinStrategy(botAI)

View File

@ -21,7 +21,6 @@ public:
creators["divine spirit"] = &divine_spirit; creators["divine spirit"] = &divine_spirit;
creators["divine spirit on party"] = &divine_spirit_on_party; creators["divine spirit on party"] = &divine_spirit_on_party;
creators["power word: shield"] = &power_word_shield; creators["power word: shield"] = &power_word_shield;
// creators["power word: shield on party"] = &power_word_shield_on_party;
creators["renew"] = &renew; creators["renew"] = &renew;
creators["renew on party"] = &renew_on_party; creators["renew on party"] = &renew_on_party;
creators["greater heal"] = &greater_heal; creators["greater heal"] = &greater_heal;
@ -33,8 +32,6 @@ public:
creators["flash heal"] = &flash_heal; creators["flash heal"] = &flash_heal;
creators["flash heal on party"] = &flash_heal_on_party; creators["flash heal on party"] = &flash_heal_on_party;
creators["psychic scream"] = &psychic_scream; creators["psychic scream"] = &psychic_scream;
// creators["fade"] = &fade;
creators["shadowfiend"] = &shadowfiend;
} }
private: private:
@ -210,16 +207,6 @@ private:
/*C*/ {} /*C*/ {}
); );
} }
static ActionNode* shadowfiend([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"shadowfiend",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
}; };
class CurePriestStrategyActionNodeFactory : public NamedObjectFactory<ActionNode> class CurePriestStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>

View File

@ -17,7 +17,6 @@ public:
ShadowPriestStrategyActionNodeFactory() ShadowPriestStrategyActionNodeFactory()
{ {
creators["mind blast"] = &mind_blast; creators["mind blast"] = &mind_blast;
creators["dispersion"] = &dispersion;
creators["mind flay"] = &mind_flay; creators["mind flay"] = &mind_flay;
creators["smite"] = &smite; creators["smite"] = &smite;
} }
@ -47,13 +46,6 @@ private:
/*C*/ {}); /*C*/ {});
} }
static ActionNode* dispersion([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode("dispersion",
/*P*/ {},
/*A*/ { NextAction("mana potion") },
/*C*/ {});
}
}; };
#endif #endif

View File

@ -14,7 +14,6 @@ public:
{ {
creators["sinister strike"] = &sinister_strike; creators["sinister strike"] = &sinister_strike;
creators["kick"] = &kick; creators["kick"] = &kick;
creators["kidney shot"] = &kidney_shot;
creators["backstab"] = &backstab; creators["backstab"] = &backstab;
creators["rupture"] = &rupture; creators["rupture"] = &rupture;
} }
@ -40,15 +39,6 @@ private:
/*C*/ {} /*C*/ {}
); );
} }
static ActionNode* kidney_shot([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"kidney shot",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* backstab([[maybe_unused]] PlayerbotAI* botAI) static ActionNode* backstab([[maybe_unused]] PlayerbotAI* botAI)
{ {
return new ActionNode( return new ActionNode(
@ -235,9 +225,6 @@ public:
StealthedRogueStrategyActionNodeFactory() StealthedRogueStrategyActionNodeFactory()
{ {
creators["ambush"] = &ambush; creators["ambush"] = &ambush;
creators["cheap shot"] = &cheap_shot;
creators["garrote"] = &garrote;
creators["sap"] = &sap;
creators["sinister strike"] = &sinister_strike; creators["sinister strike"] = &sinister_strike;
} }
@ -252,36 +239,6 @@ private:
); );
} }
static ActionNode* cheap_shot([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"cheap shot",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* garrote([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"garrote",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* sap([[maybe_unused]] PlayerbotAI* botAI)
{
return new ActionNode(
"sap",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* sinister_strike([[maybe_unused]] PlayerbotAI* botAI) static ActionNode* sinister_strike([[maybe_unused]] PlayerbotAI* botAI)
{ {
return new ActionNode( return new ActionNode(

View File

@ -6,45 +6,9 @@
#include "AfflictionWarlockStrategy.h" #include "AfflictionWarlockStrategy.h"
#include "Playerbots.h" #include "Playerbots.h"
// ===== Action Node Factory =====
class AfflictionWarlockStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
AfflictionWarlockStrategyActionNodeFactory()
{
creators["corruption"] = &corruption;
creators["corruption on attacker"] = &corruption;
creators["unstable affliction"] = &unstable_affliction;
creators["unstable affliction on attacker"] = &unstable_affliction;
creators["haunt"] = &haunt;
creators["shadow bolt"] = &shadow_bolt;
creators["drain soul"] = &drain_soul;
creators["life tap"] = &life_tap;
creators["shadowflame"] = &shadowflame;
creators["seed of corruption on attacker"] = &seed_of_corruption;
creators["seed of corruption"] = &seed_of_corruption;
creators["rain of fire"] = &rain_of_fire;
}
private:
static ActionNode* corruption(PlayerbotAI*) { return new ActionNode("corruption", {}, {}, {}); }
static ActionNode* corruption_on_attacker(PlayerbotAI*) { return new ActionNode("corruption on attacker", {}, {}, {}); }
static ActionNode* unstable_affliction(PlayerbotAI*) { return new ActionNode("unstable affliction", {}, {}, {}); }
static ActionNode* unstable_affliction_on_attacker(PlayerbotAI*) { return new ActionNode("unstable affliction on attacker", {}, {}, {}); }
static ActionNode* haunt(PlayerbotAI*) { return new ActionNode("haunt", {}, {}, {}); }
static ActionNode* shadow_bolt(PlayerbotAI*) { return new ActionNode("shadow bolt", {}, {}, {}); }
static ActionNode* drain_soul(PlayerbotAI*) { return new ActionNode("drain soul", {}, {}, {}); }
static ActionNode* life_tap(PlayerbotAI*) { return new ActionNode("life tap", {}, {}, {}); }
static ActionNode* shadowflame(PlayerbotAI*) { return new ActionNode("shadowflame", {}, {}, {}); }
static ActionNode* seed_of_corruption_on_attacker(PlayerbotAI*) { return new ActionNode("seed of corruption on attacker", {}, {}, {}); }
static ActionNode* seed_of_corruption(PlayerbotAI*) { return new ActionNode("seed of corruption", {}, {}, {}); }
static ActionNode* rain_of_fire(PlayerbotAI*) { return new ActionNode("rain of fire", {}, {}, {}); }
};
// ===== Single Target Strategy =====
AfflictionWarlockStrategy::AfflictionWarlockStrategy(PlayerbotAI* botAI) : GenericWarlockStrategy(botAI) AfflictionWarlockStrategy::AfflictionWarlockStrategy(PlayerbotAI* botAI) : GenericWarlockStrategy(botAI)
{ {
actionNodeFactories.Add(new AfflictionWarlockStrategyActionNodeFactory()); // No custom ActionNodeFactory needed
} }
// ===== Default Actions ===== // ===== Default Actions =====

View File

@ -6,53 +6,9 @@
#include "DemonologyWarlockStrategy.h" #include "DemonologyWarlockStrategy.h"
#include "Playerbots.h" #include "Playerbots.h"
// ===== Action Node Factory =====
class DemonologyWarlockStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
DemonologyWarlockStrategyActionNodeFactory()
{
creators["metamorphosis"] = &metamorphosis;
creators["demonic empowerment"] = &demonic_empowerment;
creators["corruption"] = &corruption;
creators["corruption on attacker"] = &corruption_on_attacker;
creators["immolate"] = &immolate;
creators["immolate on attacker"] = &immolate_on_attacker;
creators["incinerate"] = &incinerate;
creators["soul fire"] = &soul_fire;
creators["shadow bolt"] = &shadow_bolt;
creators["life tap"] = &life_tap;
creators["immolation aura"] = &immolation_aura;
creators["shadowflame"] = &shadowflame;
creators["seed of corruption on attacker"] = &seed_of_corruption_on_attacker;
creators["seed of corruption"] = &seed_of_corruption;
creators["rain of fire"] = &rain_of_fire;
creators["demon charge"] = &demon_charge;
}
private:
static ActionNode* metamorphosis(PlayerbotAI*) { return new ActionNode("metamorphosis", {}, {}, {}); }
static ActionNode* demonic_empowerment(PlayerbotAI*) { return new ActionNode("demonic empowerment", {}, {}, {}); }
static ActionNode* corruption(PlayerbotAI*) { return new ActionNode("corruption", {}, {}, {}); }
static ActionNode* corruption_on_attacker(PlayerbotAI*) { return new ActionNode("corruption on attacker", {}, {}, {}); }
static ActionNode* immolate(PlayerbotAI*) { return new ActionNode("immolate", {}, {}, {}); }
static ActionNode* immolate_on_attacker(PlayerbotAI*) { return new ActionNode("immolate on attacker", {}, {}, {}); }
static ActionNode* incinerate(PlayerbotAI*) { return new ActionNode("incinerate", {}, {}, {}); }
static ActionNode* soul_fire(PlayerbotAI*) { return new ActionNode("soul fire", {}, {}, {}); }
static ActionNode* shadow_bolt(PlayerbotAI*) { return new ActionNode("shadow bolt", {}, {}, {}); }
static ActionNode* life_tap(PlayerbotAI*) { return new ActionNode("life tap", {}, {}, {}); }
static ActionNode* immolation_aura(PlayerbotAI*) { return new ActionNode("immolation aura", {}, {}, {}); }
static ActionNode* shadowflame(PlayerbotAI*) { return new ActionNode("shadowflame", {}, {}, {}); }
static ActionNode* seed_of_corruption_on_attacker(PlayerbotAI*) { return new ActionNode("seed of corruption on attacker", {}, {}, {}); }
static ActionNode* seed_of_corruption(PlayerbotAI*) { return new ActionNode("seed of corruption", {}, {}, {}); }
static ActionNode* rain_of_fire(PlayerbotAI*) { return new ActionNode("rain of fire", {}, {}, {}); }
static ActionNode* demon_charge(PlayerbotAI*) { return new ActionNode("demon charge", {}, {}, {}); }
};
// ===== Single Target Strategy =====
DemonologyWarlockStrategy::DemonologyWarlockStrategy(PlayerbotAI* botAI) : GenericWarlockStrategy(botAI) DemonologyWarlockStrategy::DemonologyWarlockStrategy(PlayerbotAI* botAI) : GenericWarlockStrategy(botAI)
{ {
actionNodeFactories.Add(new DemonologyWarlockStrategyActionNodeFactory()); // No custom ActionNodeFactory needed
} }
// ===== Default Actions ===== // ===== Default Actions =====

View File

@ -6,49 +6,9 @@
#include "DestructionWarlockStrategy.h" #include "DestructionWarlockStrategy.h"
#include "Playerbots.h" #include "Playerbots.h"
// ===== Action Node Factory =====
class DestructionWarlockStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
DestructionWarlockStrategyActionNodeFactory()
{
creators["immolate"] = &immolate;
creators["conflagrate"] = &conflagrate;
creators["chaos bolt"] = &chaos_bolt;
creators["incinerate"] = &incinerate;
creators["corruption"] = &corruption;
creators["corruption on attacker"] = &corruption_on_attacker;
creators["shadow bolt"] = &shadow_bolt;
creators["shadowburn"] = &shadowburn;
creators["life tap"] = &life_tap;
creators["shadowfury"] = &shadowfury;
creators["shadowflame"] = &shadowflame;
creators["seed of corruption"] = &seed_of_corruption;
creators["seed of corruption on attacker"] = &seed_of_corruption;
creators["rain of fire"] = &rain_of_fire;
}
private:
static ActionNode* immolate(PlayerbotAI*) { return new ActionNode("immolate", {}, {}, {}); }
static ActionNode* conflagrate(PlayerbotAI*) { return new ActionNode("conflagrate", {}, {}, {}); }
static ActionNode* chaos_bolt(PlayerbotAI*) { return new ActionNode("chaos bolt", {}, {}, {}); }
static ActionNode* incinerate(PlayerbotAI*) { return new ActionNode("incinerate", {}, {}, {}); }
static ActionNode* corruption(PlayerbotAI*) { return new ActionNode("corruption", {}, {}, {}); }
static ActionNode* corruption_on_attacker(PlayerbotAI*) { return new ActionNode("corruption on attacker", {}, {}, {}); }
static ActionNode* shadow_bolt(PlayerbotAI*) { return new ActionNode("shadow bolt", {}, {}, {}); }
static ActionNode* shadowburn(PlayerbotAI*) { return new ActionNode("shadowburn", {}, {}, {}); }
static ActionNode* life_tap(PlayerbotAI*) { return new ActionNode("life tap", {}, {}, {}); }
static ActionNode* shadowfury(PlayerbotAI*) { return new ActionNode("shadowfury", {}, {}, {}); }
static ActionNode* shadowflame(PlayerbotAI*) { return new ActionNode("shadowflame", {}, {}, {}); }
static ActionNode* seed_of_corruption(PlayerbotAI*) { return new ActionNode("seed of corruption", {}, {}, {}); }
static ActionNode* seed_of_corruption_on_attacker(PlayerbotAI*) { return new ActionNode("seed of corruption on attacker", {}, {}, {}); }
static ActionNode* rain_of_fire(PlayerbotAI*) { return new ActionNode("rain of fire", {}, {}, {}); }
};
// ===== Single Target Strategy =====
DestructionWarlockStrategy::DestructionWarlockStrategy(PlayerbotAI* botAI) : GenericWarlockStrategy(botAI) DestructionWarlockStrategy::DestructionWarlockStrategy(PlayerbotAI* botAI) : GenericWarlockStrategy(botAI)
{ {
actionNodeFactories.Add(new DestructionWarlockStrategyActionNodeFactory()); // No custom ActionNodeFactory needed
} }
// ===== Default Actions ===== // ===== Default Actions =====

View File

@ -5,29 +5,9 @@
#include "GenericWarlockStrategy.h" #include "GenericWarlockStrategy.h"
class GenericWarlockStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
GenericWarlockStrategyActionNodeFactory()
{
creators["banish on cc"] = &banish_on_cc;
creators["fear on cc"] = &fear_on_cc;
creators["spell lock"] = &spell_lock;
creators["devour magic purge"] = &devour_magic_purge;
creators["devour magic cleanse"] = &devour_magic_cleanse;
}
private:
static ActionNode* banish_on_cc(PlayerbotAI*) { return new ActionNode("banish on cc", {}, {}, {}); }
static ActionNode* fear_on_cc(PlayerbotAI*) { return new ActionNode("fear on cc", {}, {}, {}); }
static ActionNode* spell_lock(PlayerbotAI*) { return new ActionNode("spell lock", {}, {}, {}); }
static ActionNode* devour_magic_purge(PlayerbotAI*) { return new ActionNode("devour magic purge", {}, {}, {}); }
static ActionNode* devour_magic_cleanse(PlayerbotAI*) { return new ActionNode("devour magic cleanse", {}, {}, {}); }
};
GenericWarlockStrategy::GenericWarlockStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) GenericWarlockStrategy::GenericWarlockStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI)
{ {
actionNodeFactories.Add(new GenericWarlockStrategyActionNodeFactory()); // No custom ActionNodeFactory needed
} }
std::vector<NextAction> GenericWarlockStrategy::getDefaultActions() std::vector<NextAction> GenericWarlockStrategy::getDefaultActions()

View File

@ -5,36 +5,13 @@
#include "TankWarlockStrategy.h" #include "TankWarlockStrategy.h"
// Combat strategy for a Warlock Tank, for certain bosses like Twin Emperors
// Priority is set to spam Searing Pain and use Shadow Ward on CD
// Disabled by default
// To enable, type "co +tank"
// To disable, type "co -tank"
// ===== Action Node Factory =====
class TankWarlockStrategyActionNodeFactory : public NamedObjectFactory<ActionNode>
{
public:
TankWarlockStrategyActionNodeFactory()
{
creators["shadow ward"] = &shadow_ward;
creators["searing pain"] = &searing_pain;
}
private:
static ActionNode* shadow_ward(PlayerbotAI*) { return new ActionNode("shadow ward", {}, {}, {}); }
static ActionNode* searing_pain(PlayerbotAI*) { return new ActionNode("searing pain", {}, {}, {}); }
};
// ===== Warlock Tank Combat Strategy =====
TankWarlockStrategy::TankWarlockStrategy(PlayerbotAI* botAI) : GenericWarlockStrategy(botAI) TankWarlockStrategy::TankWarlockStrategy(PlayerbotAI* botAI) : GenericWarlockStrategy(botAI)
{ {
actionNodeFactories.Add(new TankWarlockStrategyActionNodeFactory()); // No custom ActionNodeFactory needed
} }
std::vector<NextAction> TankWarlockStrategy::getDefaultActions() std::vector<NextAction> TankWarlockStrategy::getDefaultActions()
{ {
// Shadow Ward is the highest priority, Searing Pain next.
return { return {
NextAction("shadow ward", 27.5f), NextAction("shadow ward", 27.5f),
NextAction("searing pain", 27.0f) NextAction("searing pain", 27.0f)

View File

@ -15,9 +15,6 @@ public:
creators["piercing howl"] = &piercing_howl; creators["piercing howl"] = &piercing_howl;
creators["mocking blow"] = &mocking_blow; creators["mocking blow"] = &mocking_blow;
creators["heroic strike"] = &heroic_strike; creators["heroic strike"] = &heroic_strike;
creators["enraged regeneration"] = &enraged_regeneration;
creators["retaliation"] = &retaliation;
creators["shattering throw"] = &shattering_throw;
} }
private: private:
@ -70,36 +67,6 @@ private:
/*C*/ {} /*C*/ {}
); );
} }
static ActionNode* enraged_regeneration(PlayerbotAI* /*botAI*/)
{
return new ActionNode(
"enraged regeneration",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* retaliation(PlayerbotAI* /*botAI*/)
{
return new ActionNode(
"retaliation",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* shattering_throw(PlayerbotAI* /*botAI*/)
{
return new ActionNode(
"shattering throw",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
}; };
ArmsWarriorStrategy::ArmsWarriorStrategy(PlayerbotAI* botAI) : GenericWarriorStrategy(botAI) ArmsWarriorStrategy::ArmsWarriorStrategy(PlayerbotAI* botAI) : GenericWarriorStrategy(botAI)

View File

@ -14,7 +14,6 @@ public:
creators["intercept"] = &intercept; creators["intercept"] = &intercept;
creators["piercing howl"] = &piercing_howl; creators["piercing howl"] = &piercing_howl;
creators["pummel"] = &pummel; creators["pummel"] = &pummel;
creators["enraged regeneration"] = &enraged_regeneration;
} }
private: private:
@ -57,16 +56,6 @@ private:
/*C*/ {} /*C*/ {}
); );
} }
static ActionNode* enraged_regeneration(PlayerbotAI* /*botAI*/)
{
return new ActionNode(
"enraged regeneration",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
}; };
FuryWarriorStrategy::FuryWarriorStrategy(PlayerbotAI* botAI) : GenericWarriorStrategy(botAI) FuryWarriorStrategy::FuryWarriorStrategy(PlayerbotAI* botAI) : GenericWarriorStrategy(botAI)

View File

@ -19,8 +19,6 @@ public:
creators["heroic throw taunt"] = &heroic_throw_taunt; creators["heroic throw taunt"] = &heroic_throw_taunt;
creators["taunt"] = &taunt; creators["taunt"] = &taunt;
creators["taunt spell"] = &taunt; creators["taunt spell"] = &taunt;
creators["vigilance"] = &vigilance;
creators["enraged regeneration"] = &enraged_regeneration;
} }
private: private:
@ -103,26 +101,6 @@ private:
/*C*/ {} /*C*/ {}
); );
} }
static ActionNode* vigilance(PlayerbotAI* /*botAI*/)
{
return new ActionNode(
"vigilance",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
static ActionNode* enraged_regeneration(PlayerbotAI* /*botAI*/)
{
return new ActionNode(
"enraged regeneration",
/*P*/ {},
/*A*/ {},
/*C*/ {}
);
}
}; };
TankWarriorStrategy::TankWarriorStrategy(PlayerbotAI* botAI) : GenericWarriorStrategy(botAI) TankWarriorStrategy::TankWarriorStrategy(PlayerbotAI* botAI) : GenericWarriorStrategy(botAI)

View File

@ -26,6 +26,7 @@ public:
creators["mc golemagg assist tank attack core rager"] = &RaidMcActionContext::golemagg_assist_tank_attack_core_rager; creators["mc golemagg assist tank attack core rager"] = &RaidMcActionContext::golemagg_assist_tank_attack_core_rager;
creators["mc majordomo shadow resistance"] = &RaidMcActionContext::majordomo_shadow_resistance; creators["mc majordomo shadow resistance"] = &RaidMcActionContext::majordomo_shadow_resistance;
creators["mc ragnaros fire resistance"] = &RaidMcActionContext::ragnaros_fire_resistance; creators["mc ragnaros fire resistance"] = &RaidMcActionContext::ragnaros_fire_resistance;
creators["mc core hound mark"] = &RaidMcActionContext::core_hound_mark;
} }
private: private:
@ -44,6 +45,7 @@ private:
static Action* golemagg_assist_tank_attack_core_rager(PlayerbotAI* botAI) { return new McGolemaggAssistTankAttackCoreRagerAction(botAI); } static Action* golemagg_assist_tank_attack_core_rager(PlayerbotAI* botAI) { return new McGolemaggAssistTankAttackCoreRagerAction(botAI); }
static Action* majordomo_shadow_resistance(PlayerbotAI* botAI) { return new BossShadowResistanceAction(botAI, "majordomo executus"); } static Action* majordomo_shadow_resistance(PlayerbotAI* botAI) { return new BossShadowResistanceAction(botAI, "majordomo executus"); }
static Action* ragnaros_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceAction(botAI, "ragnaros"); } static Action* ragnaros_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceAction(botAI, "ragnaros"); }
static Action* core_hound_mark(PlayerbotAI* botAI) { return new McCoreHoundMarkAction(botAI); }
}; };
#endif #endif

View File

@ -212,3 +212,45 @@ bool McGolemaggAssistTankAttackCoreRagerAction::Execute(Event event)
return false; return false;
} }
Unit* McCoreHoundMarkAction::GetTarget()
{
Unit* highestHealthHound = nullptr;
for (auto const& [guid, ref] : bot->GetThreatMgr().GetThreatenedByMeList())
{
Unit* unit = ref->GetOwner();
if (unit && unit->IsAlive() && unit->GetEntry() == NPC_CORE_HOUND)
{
if (!highestHealthHound || unit->GetHealth() > highestHealthHound->GetHealth())
highestHealthHound = unit;
}
}
if (!highestHealthHound)
return nullptr;
Group* group = bot->GetGroup();
ObjectGuid currentSkullGuid = group ? group->GetTargetIcon(RtiTargetValue::skullIndex) : ObjectGuid::Empty;
if (!currentSkullGuid.IsEmpty() && currentSkullGuid != highestHealthHound->GetGUID())
{
// Only switch skull if the new target has meaningfully more health (10% buffer) to prevent rapid re-marking
if (Unit* currentSkullUnit = botAI->GetUnit(currentSkullGuid))
if (currentSkullUnit->IsAlive() && highestHealthHound->GetHealth() <= currentSkullUnit->GetHealth() * 1.10f)
return nullptr;
}
if (currentSkullGuid.IsEmpty() || currentSkullGuid != highestHealthHound->GetGUID())
return highestHealthHound;
return nullptr;
}
bool McCoreHoundMarkAction::Execute(Event /*event*/)
{
Unit* target = GetTarget();
if (!target)
return false;
bot->GetGroup()->SetTargetIcon(RtiTargetValue::skullIndex, bot->GetGUID(), target->GetGUID());
return true;
}

View File

@ -64,4 +64,13 @@ public:
bool Execute(Event event) override; bool Execute(Event event) override;
}; };
class McCoreHoundMarkAction : public Action
{
public:
McCoreHoundMarkAction(PlayerbotAI* botAI, std::string const name = "mc core hound mark")
: Action(botAI, name) {};
Unit* GetTarget() override;
bool Execute(Event event) override;
};
#endif #endif

View File

@ -7,6 +7,9 @@ enum MoltenCoreNPCs
{ {
// Golemagg // Golemagg
NPC_CORE_RAGER = 11672, NPC_CORE_RAGER = 11672,
// Core Hound (trash)
NPC_CORE_HOUND = 11671,
}; };
enum MoltenCoreSpells enum MoltenCoreSpells
{ {

View File

@ -71,6 +71,11 @@ void RaidMcStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
triggers.push_back( triggers.push_back(
new TriggerNode("mc ragnaros fire resistance", new TriggerNode("mc ragnaros fire resistance",
{ NextAction("mc ragnaros fire resistance", ACTION_RAID) })); { NextAction("mc ragnaros fire resistance", ACTION_RAID) }));
// Trash
triggers.push_back(
new TriggerNode("mc core hound mark",
{ NextAction("mc core hound mark", ACTION_RAID) }));
} }
void RaidMcStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers) void RaidMcStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)

View File

@ -25,6 +25,7 @@ public:
creators["mc golemagg is assist tank"] = &RaidMcTriggerContext::golemagg_is_assist_tank; creators["mc golemagg is assist tank"] = &RaidMcTriggerContext::golemagg_is_assist_tank;
creators["mc majordomo shadow resistance"] = &RaidMcTriggerContext::majordomo_shadow_resistance; creators["mc majordomo shadow resistance"] = &RaidMcTriggerContext::majordomo_shadow_resistance;
creators["mc ragnaros fire resistance"] = &RaidMcTriggerContext::ragnaros_fire_resistance; creators["mc ragnaros fire resistance"] = &RaidMcTriggerContext::ragnaros_fire_resistance;
creators["mc core hound mark"] = &RaidMcTriggerContext::core_hound_mark;
} }
private: private:
@ -43,6 +44,7 @@ private:
static Trigger* golemagg_is_assist_tank(PlayerbotAI* botAI) { return new McGolemaggIsAssistTankTrigger(botAI); } static Trigger* golemagg_is_assist_tank(PlayerbotAI* botAI) { return new McGolemaggIsAssistTankTrigger(botAI); }
static Trigger* majordomo_shadow_resistance(PlayerbotAI* botAI) { return new BossShadowResistanceTrigger(botAI, "majordomo executus"); } static Trigger* majordomo_shadow_resistance(PlayerbotAI* botAI) { return new BossShadowResistanceTrigger(botAI, "majordomo executus"); }
static Trigger* ragnaros_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceTrigger(botAI, "ragnaros"); } static Trigger* ragnaros_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceTrigger(botAI, "ragnaros"); }
static Trigger* core_hound_mark(PlayerbotAI* botAI) { return new McCoreHoundMarkTrigger(botAI); }
}; };
#endif #endif

View File

@ -38,3 +38,8 @@ bool McGolemaggIsAssistTankTrigger::IsActive()
{ {
return AI_VALUE2(Unit*, "find target", "golemagg the incinerator") && PlayerbotAI::IsAssistTank(bot); return AI_VALUE2(Unit*, "find target", "golemagg the incinerator") && PlayerbotAI::IsAssistTank(bot);
} }
bool McCoreHoundMarkTrigger::IsActive()
{
return PlayerbotAI::IsMainTank(bot) && AI_VALUE2(Unit*, "find target", "core hound");
}

View File

@ -47,4 +47,11 @@ public:
bool IsActive() override; bool IsActive() override;
}; };
class McCoreHoundMarkTrigger : public Trigger
{
public:
McCoreHoundMarkTrigger(PlayerbotAI* botAI) : Trigger(botAI, "mc core hound mark") {}
bool IsActive() override;
};
#endif #endif

View File

@ -334,18 +334,12 @@ uint32 RandomPlayerbotFactory::CalculateTotalAccountCount()
sPlayerbotAIConfig.addClassAccountPoolSize == 0 ? 2 : -1); sPlayerbotAIConfig.addClassAccountPoolSize == 0 ? 2 : -1);
if (!res || res->Fetch()[0].Get<uint64>() == 0) if (!res || res->Fetch()[0].Get<uint64>() == 0)
{
break; break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(50)); // Extra 50ms fixed delay for safety. std::this_thread::sleep_for(std::chrono::milliseconds(50)); // Extra 50ms fixed delay for safety.
} }
} }
// Checks if randomBotAccountCount is set, otherwise calculate it dynamically.
if (sPlayerbotAIConfig.randomBotAccountCount > 0)
return sPlayerbotAIConfig.randomBotAccountCount;
// Check existing account types // Check existing account types
uint32 existingRndBotAccounts = 0; uint32 existingRndBotAccounts = 0;
uint32 existingAddClassAccounts = 0; uint32 existingAddClassAccounts = 0;
@ -366,16 +360,14 @@ uint32 RandomPlayerbotFactory::CalculateTotalAccountCount()
} while (typeCheck->NextRow()); } while (typeCheck->NextRow());
} }
// Determine divisor based on Death Knight login eligibility and requested A&H faction ratio // Determine divisor based on Death Knight availability and requested A&H faction ratio
int divisor = CalculateAvailableCharsPerAccount(); int divisor = CalculateAvailableCharsPerAccount();
// Calculate max bots // Calculate max bots
int maxBots = sPlayerbotAIConfig.maxRandomBots; int maxBots = sPlayerbotAIConfig.maxRandomBots;
// Take periodic online - offline into account // Take periodic online/offline into account
if (sPlayerbotAIConfig.enablePeriodicOnlineOffline) if (sPlayerbotAIConfig.enablePeriodicOnlineOffline)
{
maxBots *= sPlayerbotAIConfig.periodicOnlineOfflineRatio; maxBots *= sPlayerbotAIConfig.periodicOnlineOfflineRatio;
}
// Calculate number of accounts needed for RNDbots // Calculate number of accounts needed for RNDbots
// Result is rounded up for maxBots not cleanly divisible by the divisor // Result is rounded up for maxBots not cleanly divisible by the divisor
@ -416,11 +408,22 @@ uint32 RandomPlayerbotFactory::CalculateTotalAccountCount()
} }
// Return existing total plus any additional accounts needed // Return existing total plus any additional accounts needed
return existingTotal + additionalAccountsNeeded; uint32 calculatedTotal = existingTotal + additionalAccountsNeeded;
// Manually set randomBotAccountCount meets the requirements
if (sPlayerbotAIConfig.randomBotAccountCount >= calculatedTotal)
return sPlayerbotAIConfig.randomBotAccountCount;
// Manually set randomBotAccountCount doesn't meet the requirements. Using calculated value
if (sPlayerbotAIConfig.randomBotAccountCount > 0)
LOG_WARN("playerbots", "RandomBotAccountCount ({}) is lower than the required calculated value ({}). Using the calculated value instead.",
sPlayerbotAIConfig.randomBotAccountCount, calculatedTotal);
return calculatedTotal;
} }
uint32 RandomPlayerbotFactory::CalculateAvailableCharsPerAccount() uint32 RandomPlayerbotFactory::CalculateAvailableCharsPerAccount()
{ {
// Death Knight availability according to their login eligibility, and if WotLK is enabled at all.
bool noDK = sPlayerbotAIConfig.disableDeathKnightLogin || sWorld->getIntConfig(CONFIG_EXPANSION) != EXPANSION_WRATH_OF_THE_LICH_KING; bool noDK = sPlayerbotAIConfig.disableDeathKnightLogin || sWorld->getIntConfig(CONFIG_EXPANSION) != EXPANSION_WRATH_OF_THE_LICH_KING;
uint32 availableChars = noDK ? 9 : 10; uint32 availableChars = noDK ? 9 : 10;
@ -434,11 +437,9 @@ uint32 RandomPlayerbotFactory::CalculateAvailableCharsPerAccount()
float unavailableRatio = static_cast<float>((std::max(hordeRatio, allianceRatio) - std::min(hordeRatio, allianceRatio))) / float unavailableRatio = static_cast<float>((std::max(hordeRatio, allianceRatio) - std::min(hordeRatio, allianceRatio))) /
(std::max(hordeRatio, allianceRatio) * 2); (std::max(hordeRatio, allianceRatio) * 2);
// Conservative floor to ensure enough characters (may result in more accounts than needed).
if (unavailableRatio != 0) if (unavailableRatio != 0)
{
// conservative floor to ensure enough chars (may result in more accounts than needed)
availableChars = availableChars - availableChars * unavailableRatio; availableChars = availableChars - availableChars * unavailableRatio;
}
return availableChars; return availableChars;
} }

View File

@ -1068,6 +1068,7 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
messages.push_back("Enable player botAI"); messages.push_back("Enable player botAI");
PlayerbotsMgr::instance().AddPlayerbotData(master, true); PlayerbotsMgr::instance().AddPlayerbotData(master, true);
GET_PLAYERBOT_AI(master)->SetMaster(master); GET_PLAYERBOT_AI(master)->SetMaster(master);
PlayerbotRepository::instance().Load(GET_PLAYERBOT_AI(master));
} }
return messages; return messages;