Merge pull request #1623 from brighton-chi/karazhan

Implement Karazhan strategy
This commit is contained in:
kadeshar
2025-10-03 06:30:39 +02:00
committed by GitHub
17 changed files with 2775 additions and 31 deletions

View File

@@ -443,7 +443,7 @@ AiPlayerbot.AutoAvoidAoe = 1
AiPlayerbot.MaxAoeAvoidRadius = 15.0
# A whitelist of aoe spell IDs that should not be avoided
AiPlayerbot.AoeAvoidSpellWhitelist = 50759,57491,13810
AiPlayerbot.AoeAvoidSpellWhitelist = 50759,57491,13810,29946
# Enable healer bot save mana strategy
# Default: 1 (enabled)

View File

@@ -0,0 +1,29 @@
DELETE FROM ai_playerbot_texts WHERE name IN (
'netherspite_beam_blocking_red',
'netherspite_beam_blocking_blue',
'netherspite_beam_blocking_green',
'netherspite_beam_leaving_blue',
'netherspite_beam_leaving_green'
);
DELETE FROM ai_playerbot_texts_chance WHERE name IN (
'netherspite_beam_blocking_red',
'netherspite_beam_blocking_blue',
'netherspite_beam_blocking_green',
'netherspite_beam_leaving_blue',
'netherspite_beam_leaving_green'
);
INSERT INTO ai_playerbot_texts (name, text, say_type, reply_type, text_loc1, text_loc2, text_loc3, text_loc4, text_loc5, text_loc6, text_loc7, text_loc8) VALUES
('netherspite_beam_blocking_red', '%player is moving to block the red beam!', 0, 0, '', '', '', '', '', '', '', ''),
('netherspite_beam_blocking_blue', '%player is moving to block the blue beam!', 0, 0, '', '', '', '', '', '', '', ''),
('netherspite_beam_blocking_green', '%player is moving to block the green beam!', 0, 0, '', '', '', '', '', '', '', ''),
('netherspite_beam_leaving_blue', '%player is leaving the blue beam--next blocker up!', 0, 0, '', '', '', '', '', '', '', ''),
('netherspite_beam_leaving_green', '%player is leaving the green beam--next blocker up!', 0, 0, '', '', '', '', '', '', '', '');
INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES
('netherspite_beam_blocking_red', 100),
('netherspite_beam_blocking_blue', 100),
('netherspite_beam_blocking_green', 100),
('netherspite_beam_leaving_blue', 100),
('netherspite_beam_leaving_green', 100);

View File

@@ -1513,19 +1513,22 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
switch (mapId)
{
case 249:
strategyName = "onyxia";
strategyName = "onyxia"; // Onyxia's Lair
break;
case 409:
strategyName = "mc";
strategyName = "mc"; // Molten Core
break;
case 469:
strategyName = "bwl";
strategyName = "bwl"; // Blackwing Lair
break;
case 509:
strategyName = "aq20";
strategyName = "aq20"; // Ruins of Ahn'Qiraj
break;
case 532:
strategyName = "karazhan"; // Karazhan
break;
case 533:
strategyName = "naxx";
strategyName = "naxx"; // Naxxramas
break;
case 574:
strategyName = "wotlk-uk"; // Utgarde Keep

View File

@@ -33,22 +33,24 @@
#include "raids/RaidStrategyContext.h"
#include "raids/aq20/RaidAq20ActionContext.h"
#include "raids/aq20/RaidAq20TriggerContext.h"
#include "raids/blackwinglair/RaidBwlActionContext.h"
#include "raids/blackwinglair/RaidBwlTriggerContext.h"
#include "raids/eyeofeternity/RaidEoEActionContext.h"
#include "raids/eyeofeternity/RaidEoETriggerContext.h"
#include "raids/icecrown/RaidIccActionContext.h"
#include "raids/icecrown/RaidIccTriggerContext.h"
#include "raids/moltencore/RaidMcActionContext.h"
#include "raids/moltencore/RaidMcTriggerContext.h"
#include "raids/blackwinglair/RaidBwlActionContext.h"
#include "raids/blackwinglair/RaidBwlTriggerContext.h"
#include "raids/karazhan/RaidKarazhanActionContext.h"
#include "raids/karazhan/RaidKarazhanTriggerContext.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"
#include "raids/vaultofarchavon/RaidVoATriggerContext.h"
#include "raids/obsidiansanctum/RaidOsActionContext.h"
#include "raids/obsidiansanctum/RaidOsTriggerContext.h"
#include "raids/onyxia/RaidOnyxiaActionContext.h"
#include "raids/onyxia/RaidOnyxiaTriggerContext.h"
#include "raids/vaultofarchavon/RaidVoAActionContext.h"
#include "raids/vaultofarchavon/RaidVoATriggerContext.h"
#include "raids/icecrown/RaidIccActionContext.h"
#include "raids/icecrown/RaidIccTriggerContext.h"
SharedNamedObjectContextList<Strategy> AiObjectContext::sharedStrategyContexts;
SharedNamedObjectContextList<Action> AiObjectContext::sharedActionContexts;
@@ -96,8 +98,8 @@ void AiObjectContext::BuildSharedStrategyContexts(SharedNamedObjectContextList<S
strategyContexts.Add(new MovementStrategyContext());
strategyContexts.Add(new AssistStrategyContext());
strategyContexts.Add(new QuestStrategyContext());
strategyContexts.Add(new RaidStrategyContext());
strategyContexts.Add(new DungeonStrategyContext());
strategyContexts.Add(new RaidStrategyContext());
}
void AiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList<Action>& actionContexts)
@@ -105,15 +107,16 @@ void AiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList<Act
actionContexts.Add(new ActionContext());
actionContexts.Add(new ChatActionContext());
actionContexts.Add(new WorldPacketActionContext());
actionContexts.Add(new RaidAq20ActionContext());
actionContexts.Add(new RaidMcActionContext());
actionContexts.Add(new RaidBwlActionContext());
actionContexts.Add(new RaidOnyxiaActionContext());
actionContexts.Add(new RaidAq20ActionContext());
actionContexts.Add(new RaidKarazhanActionContext());
actionContexts.Add(new RaidNaxxActionContext());
actionContexts.Add(new RaidOsActionContext());
actionContexts.Add(new RaidEoEActionContext());
actionContexts.Add(new RaidVoAActionContext());
actionContexts.Add(new RaidUlduarActionContext());
actionContexts.Add(new RaidOnyxiaActionContext());
actionContexts.Add(new RaidIccActionContext());
actionContexts.Add(new WotlkDungeonUKActionContext());
actionContexts.Add(new WotlkDungeonNexActionContext());
@@ -137,15 +140,16 @@ void AiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextList<Tr
triggerContexts.Add(new TriggerContext());
triggerContexts.Add(new ChatTriggerContext());
triggerContexts.Add(new WorldPacketTriggerContext());
triggerContexts.Add(new RaidAq20TriggerContext());
triggerContexts.Add(new RaidMcTriggerContext());
triggerContexts.Add(new RaidBwlTriggerContext());
triggerContexts.Add(new RaidOnyxiaTriggerContext());
triggerContexts.Add(new RaidAq20TriggerContext());
triggerContexts.Add(new RaidKarazhanTriggerContext());
triggerContexts.Add(new RaidNaxxTriggerContext());
triggerContexts.Add(new RaidOsTriggerContext());
triggerContexts.Add(new RaidEoETriggerContext());
triggerContexts.Add(new RaidVoATriggerContext());
triggerContexts.Add(new RaidUlduarTriggerContext());
triggerContexts.Add(new RaidOnyxiaTriggerContext());
triggerContexts.Add(new RaidIccTriggerContext());
triggerContexts.Add(new WotlkDungeonUKTriggerContext());
triggerContexts.Add(new WotlkDungeonNexTriggerContext());

View File

@@ -1,49 +1,49 @@
#ifndef _PLAYERBOT_RAIDSTRATEGYCONTEXT_H_
#define _PLAYERBOT_RAIDSTRATEGYCONTEXT_H_
#include "RaidOnyxiaStrategy.h"
#include "RaidUlduarStrategy.h"
#include "Strategy.h"
#include "RaidAq20Strategy.h"
#include "RaidMcStrategy.h"
#include "RaidBwlStrategy.h"
#include "RaidKarazhanStrategy.h"
#include "RaidNaxxStrategy.h"
#include "RaidOsStrategy.h"
#include "RaidEoEStrategy.h"
#include "RaidMcStrategy.h"
#include "RaidAq20Strategy.h"
#include "RaidIccStrategy.h"
#include "RaidVoAStrategy.h"
#include "RaidUlduarStrategy.h"
#include "RaidOnyxiaStrategy.h"
#include "RaidIccStrategy.h"
class RaidStrategyContext : public NamedObjectContext<Strategy>
{
public:
RaidStrategyContext() : NamedObjectContext<Strategy>(false, true)
{
// TODO should we give these prefixes (eg: "naxx" -> "raid naxx")? because if we don't it's going to end up
// very crowded (with possible conflicts) once we have strats for all raids and some dungeons
// (mc already very similiar to nc)
creators["aq20"] = &RaidStrategyContext::aq20;
creators["mc"] = &RaidStrategyContext::mc;
creators["bwl"] = &RaidStrategyContext::bwl;
creators["aq20"] = &RaidStrategyContext::aq20;
creators["karazhan"] = &RaidStrategyContext::karazhan;
creators["naxx"] = &RaidStrategyContext::naxx;
creators["wotlk-os"] = &RaidStrategyContext::wotlk_os;
creators["wotlk-eoe"] = &RaidStrategyContext::wotlk_eoe;
creators["voa"] = &RaidStrategyContext::voa;
creators["uld"] = &RaidStrategyContext::uld;
creators["icc"] = &RaidStrategyContext::icc;
creators["onyxia"] = &RaidStrategyContext::onyxia;
creators["icc"] = &RaidStrategyContext::icc;
}
private:
static Strategy* aq20(PlayerbotAI* botAI) { return new RaidAq20Strategy(botAI); }
static Strategy* mc(PlayerbotAI* botAI) { return new RaidMcStrategy(botAI); }
static Strategy* bwl(PlayerbotAI* botAI) { return new RaidBwlStrategy(botAI); }
static Strategy* aq20(PlayerbotAI* botAI) { return new RaidAq20Strategy(botAI); }
static Strategy* karazhan(PlayerbotAI* botAI) { return new RaidKarazhanStrategy(botAI); }
static Strategy* naxx(PlayerbotAI* botAI) { return new RaidNaxxStrategy(botAI); }
static Strategy* wotlk_os(PlayerbotAI* botAI) { return new RaidOsStrategy(botAI); }
static Strategy* wotlk_eoe(PlayerbotAI* botAI) { return new RaidEoEStrategy(botAI); }
static Strategy* voa(PlayerbotAI* botAI) { return new RaidVoAStrategy(botAI); }
static Strategy* onyxia(PlayerbotAI* botAI) { return new RaidOnyxiaStrategy(botAI); }
static Strategy* uld(PlayerbotAI* botAI) { return new RaidUlduarStrategy(botAI); }
static Strategy* icc(PlayerbotAI* botAI) { return new RaidIccStrategy(botAI); }
static Strategy* onyxia(PlayerbotAI* botAI) { return new RaidOnyxiaStrategy(botAI); }
};
#endif

View File

@@ -0,0 +1,85 @@
#ifndef _PLAYERBOT_RAIDKARAZHANACTIONS_CONTEXT_H
#define _PLAYERBOT_RAIDKARAZHANACTIONS_CONTEXT_H
#include "RaidKarazhanActions.h"
#include "NamedObjectContext.h"
class RaidKarazhanActionContext : public NamedObjectContext<Action>
{
public:
RaidKarazhanActionContext()
{
creators["karazhan attumen the huntsman stack behind"] = &RaidKarazhanActionContext::karazhan_attumen_the_huntsman_stack_behind;
creators["karazhan moroes mark target"] = &RaidKarazhanActionContext::karazhan_moroes_mark_target;
creators["karazhan maiden of virtue position boss"] = &RaidKarazhanActionContext::karazhan_maiden_of_virtue_position_boss;
creators["karazhan maiden of virtue position ranged"] = &RaidKarazhanActionContext::karazhan_maiden_of_virtue_position_ranged;
creators["karazhan big bad wolf position boss"] = &RaidKarazhanActionContext::karazhan_big_bad_wolf_position_boss;
creators["karazhan big bad wolf run away"] = &RaidKarazhanActionContext::karazhan_big_bad_wolf_run_away;
creators["karazhan romulo and julianne mark target"] = &RaidKarazhanActionContext::karazhan_romulo_and_julianne_mark_target;
creators["karazhan wizard of oz mark target"] = &RaidKarazhanActionContext::karazhan_wizard_of_oz_mark_target;
creators["karazhan wizard of oz scorch strawman"] = &RaidKarazhanActionContext::karazhan_wizard_of_oz_scorch_strawman;
creators["karazhan the curator mark target"] = &RaidKarazhanActionContext::karazhan_the_curator_mark_target;
creators["karazhan the curator position boss"] = &RaidKarazhanActionContext::karazhan_the_curator_position_boss;
creators["karazhan the curator spread ranged"] = &RaidKarazhanActionContext::karazhan_the_curator_spread_ranged;
creators["karazhan terestian illhoof mark target"] = &RaidKarazhanActionContext::karazhan_terestian_illhoof_mark_target;
creators["karazhan shade of aran arcane explosion run away"] = &RaidKarazhanActionContext::karazhan_shade_of_aran_arcane_explosion_run_away;
creators["karazhan shade of aran flame wreath stop movement"] = &RaidKarazhanActionContext::karazhan_shade_of_aran_flame_wreath_stop_movement;
creators["karazhan shade of aran mark conjured elemental"] = &RaidKarazhanActionContext::karazhan_shade_of_aran_mark_conjured_elemental;
creators["karazhan shade of aran spread ranged"] = &RaidKarazhanActionContext::karazhan_shade_of_aran_spread_ranged;
creators["karazhan netherspite block red beam"] = &RaidKarazhanActionContext::karazhan_netherspite_block_red_beam;
creators["karazhan netherspite block blue beam"] = &RaidKarazhanActionContext::karazhan_netherspite_block_blue_beam;
creators["karazhan netherspite block green beam"] = &RaidKarazhanActionContext::karazhan_netherspite_block_green_beam;
creators["karazhan netherspite avoid beam and void zone"] = &RaidKarazhanActionContext::karazhan_netherspite_avoid_beam_and_void_zone;
creators["karazhan netherspite banish phase avoid void zone"] = &RaidKarazhanActionContext::karazhan_netherspite_banish_phase_avoid_void_zone;
creators["karazhan prince malchezaar non tank avoid hazard"] = &RaidKarazhanActionContext::karazhan_prince_malchezaar_non_tank_avoid_hazard;
creators["karazhan prince malchezaar tank avoid hazard"] = &RaidKarazhanActionContext::karazhan_prince_malchezaar_tank_avoid_hazard;
}
private:
static Action* karazhan_attumen_the_huntsman_stack_behind(PlayerbotAI* botAI) { return new KarazhanAttumenTheHuntsmanStackBehindAction(botAI); }
static Action* karazhan_moroes_mark_target(PlayerbotAI* botAI) { return new KarazhanMoroesMarkTargetAction(botAI); }
static Action* karazhan_maiden_of_virtue_position_boss(PlayerbotAI* botAI) { return new KarazhanMaidenOfVirtuePositionBossAction(botAI); }
static Action* karazhan_maiden_of_virtue_position_ranged(PlayerbotAI* botAI) { return new KarazhanMaidenOfVirtuePositionRangedAction(botAI); }
static Action* karazhan_big_bad_wolf_position_boss(PlayerbotAI* botAI) { return new KarazhanBigBadWolfPositionBossAction(botAI); }
static Action* karazhan_big_bad_wolf_run_away(PlayerbotAI* botAI) { return new KarazhanBigBadWolfRunAwayAction(botAI); }
static Action* karazhan_romulo_and_julianne_mark_target(PlayerbotAI* botAI) { return new KarazhanRomuloAndJulianneMarkTargetAction(botAI); }
static Action* karazhan_wizard_of_oz_mark_target(PlayerbotAI* botAI) { return new KarazhanWizardOfOzMarkTargetAction(botAI); }
static Action* karazhan_wizard_of_oz_scorch_strawman(PlayerbotAI* botAI) { return new KarazhanWizardOfOzScorchStrawmanAction(botAI); }
static Action* karazhan_the_curator_mark_target(PlayerbotAI* botAI) { return new KarazhanTheCuratorMarkTargetAction(botAI); }
static Action* karazhan_the_curator_position_boss(PlayerbotAI* botAI) { return new KarazhanTheCuratorPositionBossAction(botAI); }
static Action* karazhan_the_curator_spread_ranged(PlayerbotAI* botAI) { return new KarazhanTheCuratorSpreadRangedAction(botAI); }
static Action* karazhan_terestian_illhoof_mark_target(PlayerbotAI* botAI) { return new KarazhanTerestianIllhoofMarkTargetAction(botAI); }
static Action* karazhan_shade_of_aran_arcane_explosion_run_away(PlayerbotAI* botAI) { return new KarazhanShadeOfAranArcaneExplosionRunAwayAction(botAI); }
static Action* karazhan_shade_of_aran_flame_wreath_stop_movement(PlayerbotAI* botAI) { return new KarazhanShadeOfAranFlameWreathStopMovementAction(botAI); }
static Action* karazhan_shade_of_aran_mark_conjured_elemental(PlayerbotAI* botAI) { return new KarazhanShadeOfAranMarkConjuredElementalAction(botAI); }
static Action* karazhan_shade_of_aran_spread_ranged(PlayerbotAI* botAI) { return new KarazhanShadeOfAranSpreadRangedAction(botAI); }
static Action* karazhan_netherspite_block_red_beam(PlayerbotAI* botAI) { return new KarazhanNetherspiteBlockRedBeamAction(botAI); }
static Action* karazhan_netherspite_block_blue_beam(PlayerbotAI* botAI) { return new KarazhanNetherspiteBlockBlueBeamAction(botAI); }
static Action* karazhan_netherspite_block_green_beam(PlayerbotAI* botAI) { return new KarazhanNetherspiteBlockGreenBeamAction(botAI); }
static Action* karazhan_netherspite_avoid_beam_and_void_zone(PlayerbotAI* botAI) { return new KarazhanNetherspiteAvoidBeamAndVoidZoneAction(botAI); }
static Action* karazhan_netherspite_banish_phase_avoid_void_zone(PlayerbotAI* botAI) { return new KarazhanNetherspiteBanishPhaseAvoidVoidZoneAction(botAI); }
static Action* karazhan_prince_malchezaar_non_tank_avoid_hazard(PlayerbotAI* botAI) { return new KarazhanPrinceMalchezaarNonTankAvoidHazardAction(botAI); }
static Action* karazhan_prince_malchezaar_tank_avoid_hazard(PlayerbotAI* botAI) { return new KarazhanPrinceMalchezaarTankAvoidHazardAction(botAI); }
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,218 @@
#ifndef _PLAYERBOT_RAIDKARAZHANACTIONS_H
#define _PLAYERBOT_RAIDKARAZHANACTIONS_H
#include "Action.h"
#include "MovementActions.h"
class KarazhanAttumenTheHuntsmanStackBehindAction : public MovementAction
{
public:
KarazhanAttumenTheHuntsmanStackBehindAction(PlayerbotAI* botAI, std::string const name = "karazhan attumen the huntsman stack behind") : MovementAction(botAI, name) {}
bool Execute(Event event) override;
bool isUseful() override;
};
class KarazhanMoroesMarkTargetAction : public Action
{
public:
KarazhanMoroesMarkTargetAction(PlayerbotAI* botAI, std::string const name = "karazhan moroes mark target") : Action(botAI, name) {}
bool Execute(Event event) override;
};
class KarazhanMaidenOfVirtuePositionBossAction : public MovementAction
{
public:
KarazhanMaidenOfVirtuePositionBossAction(PlayerbotAI* botAI, std::string const name = "karazhan maiden of virtue position boss") : MovementAction(botAI, name) {}
bool Execute(Event event) override;
bool isUseful() override;
};
class KarazhanMaidenOfVirtuePositionRangedAction : public MovementAction
{
public:
KarazhanMaidenOfVirtuePositionRangedAction(PlayerbotAI* botAI, std::string const name = "karazhan maiden of virtue position ranged") : MovementAction(botAI, name) {}
bool Execute(Event event) override;
bool isUseful() override;
};
class KarazhanBigBadWolfPositionBossAction : public MovementAction
{
public:
KarazhanBigBadWolfPositionBossAction(PlayerbotAI* botAI, std::string const name = "karazhan big bad wolf position boss") : MovementAction(botAI, name) {}
bool Execute(Event event) override;
bool isUseful() override;
};
class KarazhanBigBadWolfRunAwayAction : public MovementAction
{
public:
KarazhanBigBadWolfRunAwayAction(PlayerbotAI* botAI, std::string const name = "karazhan big bad wolf run away") : MovementAction(botAI, name) {}
bool Execute(Event event) override;
bool isUseful() override;
private:
size_t currentIndex = 0;
};
class KarazhanRomuloAndJulianneMarkTargetAction : public Action
{
public:
KarazhanRomuloAndJulianneMarkTargetAction(PlayerbotAI* botAI, std::string const name = "karazhan romulo and julianne mark target") : Action(botAI, name) {}
bool Execute(Event event) override;
};
class KarazhanWizardOfOzMarkTargetAction : public Action
{
public:
KarazhanWizardOfOzMarkTargetAction(PlayerbotAI* botAI, std::string const name = "karazhan wizard of oz mark target") : Action(botAI, name) {}
bool Execute(Event event) override;
};
class KarazhanWizardOfOzScorchStrawmanAction : public Action
{
public:
KarazhanWizardOfOzScorchStrawmanAction(PlayerbotAI* botAI, std::string const name = "karazhan wizard of oz scorch strawman") : Action(botAI, name) {}
bool Execute(Event event) override;
};
class KarazhanTheCuratorMarkTargetAction : public Action
{
public:
KarazhanTheCuratorMarkTargetAction(PlayerbotAI* botAI, std::string const name = "karazhan the curator mark target") : Action(botAI, name) {}
bool Execute(Event event) override;
};
class KarazhanTheCuratorPositionBossAction : public MovementAction
{
public:
KarazhanTheCuratorPositionBossAction(PlayerbotAI* botAI, std::string const name = "karazhan the curator position boss") : MovementAction(botAI, name) {}
bool Execute(Event event) override;
bool isUseful() override;
};
class KarazhanTheCuratorSpreadRangedAction : public MovementAction
{
public:
KarazhanTheCuratorSpreadRangedAction(PlayerbotAI* botAI, std::string const name = "karazhan the curator spread ranged") : MovementAction(botAI, name) {}
bool Execute(Event event) override;
bool isUseful() override;
};
class KarazhanTerestianIllhoofMarkTargetAction : public Action
{
public:
KarazhanTerestianIllhoofMarkTargetAction(PlayerbotAI* botAI, std::string const name = "karazhan terestian illhoof mark target") : Action(botAI, name) {}
bool Execute(Event event) override;
};
class KarazhanShadeOfAranArcaneExplosionRunAwayAction : public MovementAction
{
public:
KarazhanShadeOfAranArcaneExplosionRunAwayAction(PlayerbotAI* botAI, std::string const name = "karazhan shade of aran arcane explosion run away") : MovementAction(botAI, name) {}
bool Execute(Event event) override;
bool isUseful() override;
};
class KarazhanShadeOfAranFlameWreathStopMovementAction : public MovementAction
{
public:
KarazhanShadeOfAranFlameWreathStopMovementAction(PlayerbotAI* botAI, std::string const name = "karazhan shade of aran flame wreath stop bot") : MovementAction(botAI, name) {}
bool Execute(Event event) override;
};
class KarazhanShadeOfAranMarkConjuredElementalAction : public Action
{
public:
KarazhanShadeOfAranMarkConjuredElementalAction(PlayerbotAI* botAI, std::string const name = "karazhan shade of aran mark conjured elemental") : Action(botAI, name) {}
bool Execute(Event event) override;
};
class KarazhanShadeOfAranSpreadRangedAction : public MovementAction
{
public:
KarazhanShadeOfAranSpreadRangedAction(PlayerbotAI* botAI, std::string const name = "karazhan shade of aran spread ranged") : MovementAction(botAI, name) {}
bool Execute(Event event) override;
bool isUseful() override;
};
class KarazhanNetherspiteBlockRedBeamAction : public MovementAction
{
public:
KarazhanNetherspiteBlockRedBeamAction(PlayerbotAI* botAI, std::string const name = "karazhan netherspite block red beam") : MovementAction(botAI, name) {}
bool Execute(Event event) override;
bool isUseful() override;
};
class KarazhanNetherspiteBlockBlueBeamAction : public MovementAction
{
public:
KarazhanNetherspiteBlockBlueBeamAction(PlayerbotAI* botAI, std::string const name = "karazhan netherspite block blue beam") : MovementAction(botAI, name) {}
bool Execute(Event event) override;
bool isUseful() override;
};
class KarazhanNetherspiteBlockGreenBeamAction : public MovementAction
{
public:
KarazhanNetherspiteBlockGreenBeamAction(PlayerbotAI* botAI, std::string const name = "karazhan netherspite block green beam") : MovementAction(botAI, name) {}
bool Execute(Event event) override;
bool isUseful() override;
};
class KarazhanNetherspiteAvoidBeamAndVoidZoneAction : public MovementAction
{
public:
KarazhanNetherspiteAvoidBeamAndVoidZoneAction(PlayerbotAI* botAI, std::string const name = "karazhan netherspite avoid beam and void zone") : MovementAction(botAI, name) {}
bool Execute(Event event) override;
bool isUseful() override;
};
class KarazhanNetherspiteBanishPhaseAvoidVoidZoneAction : public MovementAction
{
public:
KarazhanNetherspiteBanishPhaseAvoidVoidZoneAction(PlayerbotAI* botAI, std::string const name = "karazhan netherspite banish phase avoid void zone") : MovementAction(botAI, name) {}
bool Execute(Event event) override;
bool isUseful() override;
};
class KarazhanPrinceMalchezaarNonTankAvoidHazardAction : public MovementAction
{
public:
KarazhanPrinceMalchezaarNonTankAvoidHazardAction(PlayerbotAI* botAI, std::string const name = "karazhan prince malchezaar non-tank avoid hazard") : MovementAction(botAI, name) {}
bool Execute(Event event) override;
bool isUseful() override;
};
class KarazhanPrinceMalchezaarTankAvoidHazardAction : public MovementAction
{
public:
KarazhanPrinceMalchezaarTankAvoidHazardAction(PlayerbotAI* botAI, std::string const name = "karazhan prince malchezaar tank avoid hazard") : MovementAction(botAI, name) {}
bool Execute(Event event) override;
bool isUseful() override;
};
#endif

View File

@@ -0,0 +1,409 @@
#include <algorithm>
#include <map>
#include "RaidKarazhanHelpers.h"
#include "RaidKarazhanActions.h"
#include "AiObjectContext.h"
#include "PlayerbotMgr.h"
#include "Position.h"
#include "Spell.h"
const Position KARAZHAN_MAIDEN_OF_VIRTUE_BOSS_POSITION = Position(-10945.881f, -2103.782f, 92.712f);
const Position KARAZHAN_MAIDEN_OF_VIRTUE_RANGED_POSITION[8] =
{
{ -10931.178f, -2116.580f, 92.179f },
{ -10925.828f, -2102.425f, 92.180f },
{ -10933.089f, -2088.5017f, 92.180f },
{ -10947.59f, -2082.8147f, 92.180f },
{ -10960.912f, -2090.4368f, 92.179f },
{ -10966.017f, -2105.288f, 92.175f },
{ -10959.242f, -2119.6172f, 92.180f },
{ -10944.495f, -2123.857f, 92.180f },
};
const Position KARAZHAN_BIG_BAD_WOLF_BOSS_POSITION = Position(-10913.391f, -1773.508f, 90.477f);
const Position KARAZHAN_BIG_BAD_WOLF_RUN_POSITION[4] =
{
{ -10875.456f, -1779.036f, 90.477f },
{ -10872.281f, -1751.638f, 90.477f },
{ -10910.492f, -1747.401f, 90.477f },
{ -10913.391f, -1773.508f, 90.477f },
};
const Position KARAZHAN_THE_CURATOR_BOSS_POSITION = Position(-11139.463f, -1884.645f, 165.765f);
void RaidKarazhanHelpers::MarkTargetWithSkull(Unit* target)
{
if (!target)
{
return;
}
if (Group* group = bot->GetGroup())
{
constexpr uint8_t skullIconId = 7;
ObjectGuid skullGuid = group->GetTargetIcon(skullIconId);
if (skullGuid != target->GetGUID())
{
group->SetTargetIcon(skullIconId, bot->GetGUID(), target->GetGUID());
}
}
}
Unit* RaidKarazhanHelpers::GetFirstAliveUnit(const std::vector<Unit*>& units)
{
for (Unit* unit : units)
{
if (unit && unit->IsAlive())
{
return unit;
}
}
return nullptr;
}
Unit* RaidKarazhanHelpers::GetFirstAliveUnitByEntry(uint32 entry)
{
const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
for (const auto& npcGuid : npcs)
{
Unit* unit = botAI->GetUnit(npcGuid);
if (unit && unit->IsAlive() && unit->GetEntry() == entry)
{
return unit;
}
}
return nullptr;
}
Unit* RaidKarazhanHelpers::GetNearestPlayerInRadius(float radius)
{
if (Group* group = bot->GetGroup())
{
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
{
Player* member = itr->GetSource();
if (!member || !member->IsAlive() || member == bot)
{
continue;
}
if (bot->GetExactDist2d(member) < radius)
{
return member;
}
}
}
return nullptr;
}
bool RaidKarazhanHelpers::IsFlameWreathActive()
{
Unit* boss = AI_VALUE2(Unit*, "find target", "shade of aran");
Spell* currentSpell = boss ? boss->GetCurrentSpell(CURRENT_GENERIC_SPELL) : nullptr;
if (currentSpell && currentSpell->m_spellInfo && currentSpell->m_spellInfo->Id == SPELL_FLAME_WREATH)
{
return true;
}
if (Group* group = bot->GetGroup())
{
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
{
Player* member = itr->GetSource();
if (!member || !member->IsAlive())
{
continue;
}
if (member->HasAura(SPELL_AURA_FLAME_WREATH))
{
return true;
}
}
}
return false;
}
// Red beam blockers: tank bots, no Nether Exhaustion Red
std::vector<Player*> RaidKarazhanHelpers::GetRedBlockers()
{
std::vector<Player*> redBlockers;
if (Group* group = bot->GetGroup())
{
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
{
Player* member = itr->GetSource();
if (!member || !member->IsAlive() || !botAI->IsTank(member) || !GET_PLAYERBOT_AI(member) ||
member->HasAura(SPELL_NETHER_EXHAUSTION_RED))
{
continue;
}
redBlockers.push_back(member);
}
}
return redBlockers;
}
// Blue beam blockers: non-Rogue/Warrior DPS bots, no Nether Exhaustion Blue and ≤25 stacks of Blue Beam debuff
std::vector<Player*> RaidKarazhanHelpers::GetBlueBlockers()
{
std::vector<Player*> blueBlockers;
if (Group* group = bot->GetGroup())
{
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
{
Player* member = itr->GetSource();
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member))
{
continue;
}
bool isDps = botAI->IsDps(member);
bool isWarrior = member->getClass() == CLASS_WARRIOR;
bool isRogue = member->getClass() == CLASS_ROGUE;
bool hasExhaustion = member->HasAura(SPELL_NETHER_EXHAUSTION_BLUE);
Aura* blueBuff = member->GetAura(SPELL_BLUE_BEAM_DEBUFF);
bool overStack = blueBuff && blueBuff->GetStackAmount() >= 26;
if (isDps && !isWarrior && !isRogue && !hasExhaustion && !overStack)
{
blueBlockers.push_back(member);
}
}
}
return blueBlockers;
}
// Green beam blockers:
// (1) Rogue and non-tank Warrior bots, no Nether Exhaustion Green
// (2) Healer bots, no Nether Exhaustion Green and ≤25 stacks of Green Beam debuff
std::vector<Player*> RaidKarazhanHelpers::GetGreenBlockers()
{
std::vector<Player*> greenBlockers;
if (Group* group = bot->GetGroup())
{
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
{
Player* member = itr->GetSource();
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member))
{
continue;
}
bool hasExhaustion = member->HasAura(SPELL_NETHER_EXHAUSTION_GREEN);
Aura* greenBuff = member->GetAura(SPELL_GREEN_BEAM_DEBUFF);
bool overStack = greenBuff && greenBuff->GetStackAmount() >= 26;
bool isRogue = member->getClass() == CLASS_ROGUE;
bool isDpsWarrior = member->getClass() == CLASS_WARRIOR && botAI->IsDps(member);
bool eligibleRogueWarrior = (isRogue || isDpsWarrior) && !hasExhaustion;
bool isHealer = botAI->IsHeal(member);
bool eligibleHealer = isHealer && !hasExhaustion && !overStack;
if (eligibleRogueWarrior || eligibleHealer)
{
greenBlockers.push_back(member);
}
}
}
return greenBlockers;
}
Position RaidKarazhanHelpers::GetPositionOnBeam(Unit* boss, Unit* portal, float distanceFromBoss)
{
float bx = boss->GetPositionX();
float by = boss->GetPositionY();
float bz = boss->GetPositionZ();
float px = portal->GetPositionX();
float py = portal->GetPositionY();
float dx = px - bx;
float dy = py - by;
float length = sqrt(dx*dx + dy*dy);
if (length == 0.0f)
{
return Position(bx, by, bz);
}
dx /= length;
dy /= length;
float targetX = bx + dx * distanceFromBoss;
float targetY = by + dy * distanceFromBoss;
float targetZ = bz;
return Position(targetX, targetY, targetZ);
}
std::tuple<Player*, Player*, Player*> RaidKarazhanHelpers::GetCurrentBeamBlockers()
{
static ObjectGuid currentRedBlocker;
static ObjectGuid currentGreenBlocker;
static ObjectGuid currentBlueBlocker;
Player* redBlocker = nullptr;
Player* greenBlocker = nullptr;
Player* blueBlocker = nullptr;
std::vector<Player*> redBlockers = GetRedBlockers();
if (!redBlockers.empty())
{
auto it = std::find_if(redBlockers.begin(), redBlockers.end(), [](Player* p)
{
return p && p->GetGUID() == currentRedBlocker;
});
if (it != redBlockers.end())
{
redBlocker = *it;
}
else
{
redBlocker = redBlockers.front();
}
currentRedBlocker = redBlocker ? redBlocker->GetGUID() : ObjectGuid::Empty;
}
else
{
currentRedBlocker = ObjectGuid::Empty;
redBlocker = nullptr;
}
std::vector<Player*> greenBlockers = GetGreenBlockers();
if (!greenBlockers.empty())
{
auto it = std::find_if(greenBlockers.begin(), greenBlockers.end(), [](Player* p)
{
return p && p->GetGUID() == currentGreenBlocker;
});
if (it != greenBlockers.end())
{
greenBlocker = *it;
}
else
{
greenBlocker = greenBlockers.front();
}
currentGreenBlocker = greenBlocker ? greenBlocker->GetGUID() : ObjectGuid::Empty;
}
else
{
currentGreenBlocker = ObjectGuid::Empty;
greenBlocker = nullptr;
}
std::vector<Player*> blueBlockers = GetBlueBlockers();
if (!blueBlockers.empty())
{
auto it = std::find_if(blueBlockers.begin(), blueBlockers.end(), [](Player* p)
{
return p && p->GetGUID() == currentBlueBlocker;
});
if (it != blueBlockers.end())
{
blueBlocker = *it;
}
else
{
blueBlocker = blueBlockers.front();
}
currentBlueBlocker = blueBlocker ? blueBlocker->GetGUID() : ObjectGuid::Empty;
}
else
{
currentBlueBlocker = ObjectGuid::Empty;
blueBlocker = nullptr;
}
return std::make_tuple(redBlocker, greenBlocker, blueBlocker);
}
std::vector<Unit*> RaidKarazhanHelpers::GetAllVoidZones()
{
std::vector<Unit*> voidZones;
const float radius = 30.0f;
const GuidVector npcs = botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest npcs")->Get();
for (const auto& npcGuid : npcs)
{
Unit* unit = botAI->GetUnit(npcGuid);
if (!unit || unit->GetEntry() != NPC_VOID_ZONE)
{
continue;
}
float dist = bot->GetExactDist2d(unit);
if (dist < radius)
{
voidZones.push_back(unit);
}
}
return voidZones;
}
bool RaidKarazhanHelpers::IsSafePosition(float x, float y, float z,
const std::vector<Unit*>& hazards, float hazardRadius)
{
for (Unit* hazard : hazards)
{
float dist = std::sqrt(std::pow(x - hazard->GetPositionX(), 2) + std::pow(y - hazard->GetPositionY(), 2));
if (dist < hazardRadius)
{
return false;
}
}
return true;
}
std::vector<Unit*> RaidKarazhanHelpers::GetSpawnedInfernals() const
{
std::vector<Unit*> infernals;
const GuidVector npcs = botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest npcs")->Get();
for (const auto& npcGuid : npcs)
{
Unit* unit = botAI->GetUnit(npcGuid);
if (unit && unit->GetEntry() == NPC_NETHERSPITE_INFERNAL)
{
infernals.push_back(unit);
}
}
return infernals;
}
bool RaidKarazhanHelpers::IsStraightPathSafe(const Position& start, const Position& target, const std::vector<Unit*>& hazards, float hazardRadius, float stepSize)
{
float sx = start.GetPositionX();
float sy = start.GetPositionY();
float sz = start.GetPositionZ();
float tx = target.GetPositionX();
float ty = target.GetPositionY();
float tz = target.GetPositionZ();
float totalDist = std::sqrt(std::pow(tx - sx, 2) + std::pow(ty - sy, 2));
if (totalDist == 0.0f)
{
return true;
}
for (float checkDist = 0.0f; checkDist <= totalDist; checkDist += stepSize)
{
float t = checkDist / totalDist;
float checkX = sx + (tx - sx) * t;
float checkY = sy + (ty - sy) * t;
float checkZ = sz + (tz - sz) * t;
for (Unit* hazard : hazards)
{
float hazardDist = std::sqrt(std::pow(checkX - hazard->GetPositionX(), 2) + std::pow(checkY - hazard->GetPositionY(), 2));
if (hazardDist < hazardRadius)
{
return false;
}
}
}
return true;
}

View File

@@ -0,0 +1,85 @@
#ifndef _PLAYERBOT_RAIDKARAZHANHELPERS_H_
#define _PLAYERBOT_RAIDKARAZHANHELPERS_H_
#include "AiObject.h"
#include "Playerbots.h"
#include "Position.h"
enum KarazhanSpells
{
// Maiden of Virtue
SPELL_REPENTANCE = 29511,
// Opera Event
SPELL_LITTLE_RED_RIDING_HOOD = 30756,
// Shade of Aran
SPELL_FLAME_WREATH = 30004,
SPELL_AURA_FLAME_WREATH = 29946,
SPELL_ARCANE_EXPLOSION = 29973,
SPELL_WARLOCK_BANISH = 18647, // Rank 2
// Netherspite
SPELL_GREEN_BEAM_DEBUFF = 30422,
SPELL_BLUE_BEAM_DEBUFF = 30423,
SPELL_NETHER_EXHAUSTION_RED = 38637,
SPELL_NETHER_EXHAUSTION_GREEN = 38638,
SPELL_NETHER_EXHAUSTION_BLUE = 38639,
SPELL_NETHERSPITE_BANISHED = 39833,
// Prince Malchezaar
SPELL_ENFEEBLE = 30843,
};
enum KarazhanNpcs
{
// Attumen the Huntsman
NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED = 16152,
// Terestian Illhoof
NPC_KILREK = 17229,
NPC_DEMON_CHAINS = 17248,
// Shade of Aran
NPC_CONJURED_ELEMENTAL = 17167,
// Netherspite
NPC_VOID_ZONE = 16697,
NPC_RED_PORTAL = 17369,
NPC_BLUE_PORTAL = 17368,
NPC_GREEN_PORTAL = 17367,
// Prince Malchezaar
NPC_NETHERSPITE_INFERNAL = 17646,
};
extern const Position KARAZHAN_MAIDEN_OF_VIRTUE_BOSS_POSITION;
extern const Position KARAZHAN_MAIDEN_OF_VIRTUE_RANGED_POSITION[8];
extern const Position KARAZHAN_BIG_BAD_WOLF_BOSS_POSITION;
extern const Position KARAZHAN_BIG_BAD_WOLF_RUN_POSITION[4];
extern const Position KARAZHAN_THE_CURATOR_BOSS_POSITION;
class RaidKarazhanHelpers : public AiObject
{
public:
explicit RaidKarazhanHelpers(PlayerbotAI* botAI) : AiObject(botAI) {}
void MarkTargetWithSkull(Unit* /*target*/);
Unit* GetFirstAliveUnit(const std::vector<Unit*>& /*units*/);
Unit* GetFirstAliveUnitByEntry(uint32 /*entry*/);
Unit* GetNearestPlayerInRadius(float /*radius*/ = 5.0f);
bool IsFlameWreathActive();
Position GetPositionOnBeam(Unit* boss, Unit* portal, float distanceFromBoss);
std::vector<Player*> GetRedBlockers();
std::vector<Player*> GetBlueBlockers();
std::vector<Player*> GetGreenBlockers();
std::tuple<Player*, Player*, Player*> GetCurrentBeamBlockers();
std::vector<Unit*> GetAllVoidZones();
bool IsSafePosition (float x, float y, float z,
const std::vector<Unit*>& hazards, float hazardRadius);
std::vector<Unit*> GetSpawnedInfernals() const;
bool IsStraightPathSafe(const Position& start, const Position& target,
const std::vector<Unit*>& hazards, float hazardRadius, float stepSize);
};
#endif

View File

@@ -0,0 +1,266 @@
#include "RaidKarazhanMultipliers.h"
#include "RaidKarazhanActions.h"
#include "RaidKarazhanHelpers.h"
#include "AiObjectContext.h"
#include "AttackAction.h"
#include "DruidBearActions.h"
#include "DruidCatActions.h"
#include "RogueActions.h"
#include "WarriorActions.h"
static bool IsChargeAction(Action* action)
{
return dynamic_cast<CastChargeAction*>(action) ||
dynamic_cast<CastInterceptAction*>(action) ||
dynamic_cast<CastFeralChargeBearAction*>(action) ||
dynamic_cast<CastFeralChargeCatAction*>(action);
}
float KarazhanAttumenTheHuntsmanMultiplier::GetValue(Action* action)
{
RaidKarazhanHelpers karazhanHelper(botAI);
Unit* boss = karazhanHelper.GetFirstAliveUnitByEntry(NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED);
if (boss && !(botAI->IsTank(bot) && botAI->HasAggro(boss) && boss->GetVictim() == bot) &&
(dynamic_cast<MovementAction*>(action) &&
!dynamic_cast<KarazhanAttumenTheHuntsmanStackBehindAction*>(action)))
{
return 0.0f;
}
return 1.0f;
}
float KarazhanBigBadWolfMultiplier::GetValue(Action* action)
{
Unit* boss = AI_VALUE2(Unit*, "find target", "the big bad wolf");
if (!boss)
{
return 1.0f;
}
if (bot->HasAura(SPELL_LITTLE_RED_RIDING_HOOD))
{
if ((dynamic_cast<MovementAction*>(action) && !dynamic_cast<KarazhanBigBadWolfRunAwayAction*>(action)) ||
(dynamic_cast<AttackAction*>(action)))
{
return 0.0f;
}
}
return 1.0f;
}
float KarazhanShadeOfAranMultiplier::GetValue(Action* action)
{
Unit* boss = AI_VALUE2(Unit*, "find target", "shade of aran");
if (!boss)
{
return 1.0f;
}
if (boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_ARCANE_EXPLOSION))
{
if (IsChargeAction(action))
{
return 0.0f;
}
if (dynamic_cast<MovementAction*>(action))
{
const float safeDistance = 20.0f;
if (bot->GetDistance2d(boss) >= safeDistance)
{
return 0.0f;
}
}
}
bool flameWreathActive = boss->HasAura(SPELL_FLAME_WREATH);
if (!flameWreathActive && bot->GetGroup())
{
for (GroupReference* itr = bot->GetGroup()->GetFirstMember(); itr != nullptr; itr = itr->next())
{
Player* member = itr->GetSource();
if (member && member->HasAura(SPELL_AURA_FLAME_WREATH))
{
flameWreathActive = true;
break;
}
}
}
if (flameWreathActive)
{
if (dynamic_cast<MovementAction*>(action) || IsChargeAction(action))
{
return 0.0f;
}
}
return 1.0f;
}
float KarazhanNetherspiteBlueAndGreenBeamMultiplier::GetValue(Action* action)
{
Unit* boss = AI_VALUE2(Unit*, "find target", "netherspite");
if (!boss || !boss->IsAlive())
{
return 1.0f;
}
if (dynamic_cast<AvoidAoeAction*>(action) || dynamic_cast<CastKillingSpreeAction*>(action))
{
return 0.0f;
}
RaidKarazhanHelpers karazhanHelper(botAI);
auto [redBlocker /*unused*/, greenBlocker, blueBlocker] = karazhanHelper.GetCurrentBeamBlockers();
bool isBlocker = (bot == greenBlocker || bot == blueBlocker);
if (isBlocker)
{
Unit* bluePortal = bot->FindNearestCreature(NPC_BLUE_PORTAL, 150.0f);
Unit* greenPortal = bot->FindNearestCreature(NPC_GREEN_PORTAL, 150.0f);
bool inBeam = false;
for (Unit* portal : {bluePortal, greenPortal})
{
if (!portal)
{
continue;
}
float bx = boss->GetPositionX(), by = boss->GetPositionY();
float px = portal->GetPositionX(), py = portal->GetPositionY();
float dx = px - bx, dy = py - by;
float length = sqrt(dx*dx + dy*dy);
if (length == 0.0f)
{
continue;
}
dx /= length; dy /= length;
float botdx = bot->GetPositionX() - bx, botdy = bot->GetPositionY() - by;
float t = (botdx * dx + botdy * dy);
float beamX = bx + dx * t, beamY = by + dy * t;
float distToBeam = sqrt(pow(bot->GetPositionX() - beamX, 2) + pow(bot->GetPositionY() - beamY, 2));
if (distToBeam < 0.3f && t > 0.0f && t < length)
{
inBeam = true;
break;
}
}
if (inBeam)
{
std::vector<Unit*> voidZones = karazhanHelper.GetAllVoidZones();
bool inVoidZone = false;
for (Unit* vz : voidZones)
{
if (bot->GetExactDist2d(vz) < 4.0f)
{
inVoidZone = true;
break;
}
}
if (!inVoidZone)
{
if (dynamic_cast<MovementAction*>(action) || IsChargeAction(action))
{
return 0.0f;
}
}
}
}
return 1.0f;
}
float KarazhanNetherspiteRedBeamMultiplier::GetValue(Action* action)
{
Unit* boss = AI_VALUE2(Unit*, "find target", "netherspite");
if (!boss || !boss->IsAlive())
{
return 1.0f;
}
if (dynamic_cast<AvoidAoeAction*>(action))
{
return 0.0f;
}
RaidKarazhanHelpers karazhanHelper(botAI);
auto [redBlocker, greenBlocker /*unused*/, blueBlocker /*unused*/] = karazhanHelper.GetCurrentBeamBlockers();
static std::map<ObjectGuid, uint32> beamMoveTimes;
static std::map<ObjectGuid, bool> lastBeamMoveSideways;
ObjectGuid botGuid = bot->GetGUID();
Unit* redPortal = bot->FindNearestCreature(NPC_RED_PORTAL, 150.0f);
if (bot == redBlocker && boss && redPortal)
{
Position blockingPos = karazhanHelper.GetPositionOnBeam(boss, redPortal, 18.0f);
float bx = boss->GetPositionX();
float by = boss->GetPositionY();
float px = redPortal->GetPositionX();
float py = redPortal->GetPositionY();
float dx = px - bx;
float dy = py - by;
float length = sqrt(dx*dx + dy*dy);
if (length != 0.0f)
{
dx /= length;
dy /= length;
float perpDx = -dy;
float perpDy = dx;
Position sidewaysPos(blockingPos.GetPositionX() + perpDx * 3.0f,
blockingPos.GetPositionY() + perpDy * 3.0f,
blockingPos.GetPositionZ());
uint32 intervalSecs = 5;
if (beamMoveTimes[botGuid] == 0)
{
beamMoveTimes[botGuid] = time(nullptr);
lastBeamMoveSideways[botGuid] = false;
}
if (time(nullptr) - beamMoveTimes[botGuid] >= intervalSecs)
{
lastBeamMoveSideways[botGuid] = !lastBeamMoveSideways[botGuid];
beamMoveTimes[botGuid] = time(nullptr);
}
Position targetPos = lastBeamMoveSideways[botGuid] ? sidewaysPos : blockingPos;
float distToTarget = bot->GetExactDist2d(targetPos.GetPositionX(), targetPos.GetPositionY());
const float positionTolerance = 1.5f;
if (distToTarget < positionTolerance)
{
if (dynamic_cast<MovementAction*>(action) || IsChargeAction(action))
{
return 0.0f;
}
}
}
}
return 1.0f;
}
float KarazhanPrinceMalchezaarMultiplier::GetValue(Action* action)
{
Unit* boss = AI_VALUE2(Unit*, "find target", "prince malchezaar");
if (!boss || !boss->IsAlive())
{
return 1.0f;
}
if (dynamic_cast<AvoidAoeAction*>(action))
{
return 0.0f;
}
if (botAI->IsMelee(bot) && bot->HasAura(SPELL_ENFEEBLE) &&
!dynamic_cast<KarazhanPrinceMalchezaarNonTankAvoidHazardAction*>(action))
{
return 0.0f;
}
if (botAI->IsRanged(bot) && bot->HasAura(SPELL_ENFEEBLE) &&
(dynamic_cast<MovementAction*>(action) &&
!dynamic_cast<KarazhanPrinceMalchezaarNonTankAvoidHazardAction*>(action)))
{
return 0.0f;
}
return 1.0f;
}

View File

@@ -0,0 +1,48 @@
#ifndef _PLAYERBOT_RAIDKARAZHANMULTIPLIERS_H
#define _PLAYERBOT_RAIDKARAZHANMULTIPLIERS_H
#include "Multiplier.h"
class KarazhanAttumenTheHuntsmanMultiplier : public Multiplier
{
public:
KarazhanAttumenTheHuntsmanMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "karazhan attumen the huntsman multiplier") {}
virtual float GetValue(Action* action);
};
class KarazhanBigBadWolfMultiplier : public Multiplier
{
public:
KarazhanBigBadWolfMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "karazhan big bad wolf multiplier") {}
virtual float GetValue(Action* action);
};
class KarazhanShadeOfAranMultiplier : public Multiplier
{
public:
KarazhanShadeOfAranMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "karazhan shade of aran multiplier") {}
virtual float GetValue(Action* action);
};
class KarazhanNetherspiteBlueAndGreenBeamMultiplier : public Multiplier
{
public:
KarazhanNetherspiteBlueAndGreenBeamMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "karazhan netherspite blue and green beam multiplier") {}
virtual float GetValue(Action* action);
};
class KarazhanNetherspiteRedBeamMultiplier : public Multiplier
{
public:
KarazhanNetherspiteRedBeamMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "karazhan netherspite red beam multiplier") {}
virtual float GetValue(Action* action);
};
class KarazhanPrinceMalchezaarMultiplier : public Multiplier
{
public:
KarazhanPrinceMalchezaarMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "karazhan prince malchezaar multiplier") {}
virtual float GetValue(Action* action);
};
#endif

View File

@@ -0,0 +1,81 @@
#include "RaidKarazhanStrategy.h"
#include "RaidKarazhanMultipliers.h"
void RaidKarazhanStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode(
"karazhan attumen the huntsman", NextAction::array(0,
new NextAction("karazhan attumen the huntsman stack behind", ACTION_RAID + 1),
nullptr)));
triggers.push_back(new TriggerNode(
"karazhan moroes", NextAction::array(0,
new NextAction("karazhan moroes mark target", ACTION_RAID + 1),
nullptr)));
triggers.push_back(new TriggerNode(
"karazhan maiden of virtue", NextAction::array(0,
new NextAction("karazhan maiden of virtue position ranged", ACTION_RAID + 1),
new NextAction("karazhan maiden of virtue position boss", ACTION_RAID + 1),
nullptr)));
triggers.push_back(new TriggerNode(
"karazhan big bad wolf", NextAction::array(0,
new NextAction("karazhan big bad wolf run away", ACTION_EMERGENCY + 6),
new NextAction("karazhan big bad wolf position boss", ACTION_RAID + 1),
nullptr)));
triggers.push_back(new TriggerNode(
"karazhan romulo and julianne", NextAction::array(0,
new NextAction("karazhan romulo and julianne mark target", ACTION_RAID + 1),
nullptr)));
triggers.push_back(new TriggerNode(
"karazhan wizard of oz", NextAction::array(0,
new NextAction("karazhan wizard of oz scorch strawman", ACTION_RAID + 2),
new NextAction("karazhan wizard of oz mark target", ACTION_RAID + 1),
nullptr)));
triggers.push_back(new TriggerNode(
"karazhan the curator", NextAction::array(0,
new NextAction("karazhan the curator spread ranged", ACTION_RAID + 2),
new NextAction("karazhan the curator position boss", ACTION_RAID + 2),
new NextAction("karazhan the curator mark target", ACTION_RAID + 1),
nullptr)));
triggers.push_back(new TriggerNode(
"karazhan terestian illhoof", NextAction::array(0,
new NextAction("karazhan terestian illhoof mark target", ACTION_RAID + 1),
nullptr)));
triggers.push_back(new TriggerNode(
"karazhan shade of aran", NextAction::array(0,
new NextAction("karazhan shade of aran flame wreath stop movement", ACTION_EMERGENCY + 7),
new NextAction("karazhan shade of aran arcane explosion run away", ACTION_EMERGENCY + 6),
new NextAction("karazhan shade of aran spread ranged", ACTION_RAID + 2),
new NextAction("karazhan shade of aran mark conjured elemental", ACTION_RAID + 1),
nullptr)));
triggers.push_back(new TriggerNode(
"karazhan netherspite", NextAction::array(0,
new NextAction("karazhan netherspite block red beam", ACTION_EMERGENCY + 8),
new NextAction("karazhan netherspite block blue beam", ACTION_EMERGENCY + 8),
new NextAction("karazhan netherspite block green beam", ACTION_EMERGENCY + 8),
new NextAction("karazhan netherspite avoid beam and void zone", ACTION_EMERGENCY + 7),
new NextAction("karazhan netherspite banish phase avoid void zone", ACTION_RAID + 1),
nullptr)));
triggers.push_back(new TriggerNode(
"karazhan prince malchezaar", NextAction::array(0,
new NextAction("karazhan prince malchezaar non tank avoid hazard", ACTION_EMERGENCY + 6),
new NextAction("karazhan prince malchezaar tank avoid hazard", ACTION_EMERGENCY + 6),
nullptr)));
}
void RaidKarazhanStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
{
multipliers.push_back(new KarazhanShadeOfAranMultiplier(botAI));
multipliers.push_back(new KarazhanNetherspiteBlueAndGreenBeamMultiplier(botAI));
multipliers.push_back(new KarazhanNetherspiteRedBeamMultiplier(botAI));
multipliers.push_back(new KarazhanPrinceMalchezaarMultiplier(botAI));
}

View File

@@ -0,0 +1,18 @@
#ifndef _PLAYERBOT_RAIDKARAZHANSTRATEGY_H_
#define _PLAYERBOT_RAIDKARAZHANSTRATEGY_H_
#include "Strategy.h"
#include "Multiplier.h"
class RaidKarazhanStrategy : public Strategy
{
public:
RaidKarazhanStrategy(PlayerbotAI* ai) : Strategy(ai) {}
std::string const getName() override { return "karazhan"; }
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
};
#endif

View File

@@ -0,0 +1,39 @@
#ifndef _PLAYERBOT_RAIDKARAZHANTRIGGERCONTEXT_H
#define _PLAYERBOT_RAIDKARAZHANTRIGGERCONTEXT_H
#include "RaidKarazhanTriggers.h"
#include "AiObjectContext.h"
class RaidKarazhanTriggerContext : public NamedObjectContext<Trigger>
{
public:
RaidKarazhanTriggerContext()
{
creators["karazhan attumen the huntsman"] = &RaidKarazhanTriggerContext::karazhan_attumen_the_huntsman;
creators["karazhan moroes"] = &RaidKarazhanTriggerContext::karazhan_moroes;
creators["karazhan maiden of virtue"] = &RaidKarazhanTriggerContext::karazhan_maiden_of_virtue;
creators["karazhan big bad wolf"] = &RaidKarazhanTriggerContext::karazhan_big_bad_wolf;
creators["karazhan romulo and julianne"] = &RaidKarazhanTriggerContext::karazhan_romulo_and_julianne;
creators["karazhan wizard of oz"] = &RaidKarazhanTriggerContext::karazhan_wizard_of_oz;
creators["karazhan the curator"] = &RaidKarazhanTriggerContext::karazhan_the_curator;
creators["karazhan terestian illhoof"] = &RaidKarazhanTriggerContext::karazhan_terestian_illhoof;
creators["karazhan shade of aran"] = &RaidKarazhanTriggerContext::karazhan_shade_of_aran;
creators["karazhan netherspite"] = &RaidKarazhanTriggerContext::karazhan_netherspite;
creators["karazhan prince malchezaar"] = &RaidKarazhanTriggerContext::karazhan_prince_malchezaar;
}
private:
static Trigger* karazhan_attumen_the_huntsman(PlayerbotAI* botAI) { return new KarazhanAttumenTheHuntsmanTrigger(botAI); }
static Trigger* karazhan_moroes(PlayerbotAI* botAI) { return new KarazhanMoroesTrigger(botAI); }
static Trigger* karazhan_maiden_of_virtue(PlayerbotAI* botAI) { return new KarazhanMaidenOfVirtueTrigger(botAI); }
static Trigger* karazhan_big_bad_wolf(PlayerbotAI* botAI) { return new KarazhanBigBadWolfTrigger(botAI); }
static Trigger* karazhan_romulo_and_julianne(PlayerbotAI* botAI) { return new KarazhanRomuloAndJulianneTrigger(botAI); }
static Trigger* karazhan_wizard_of_oz(PlayerbotAI* botAI) { return new KarazhanWizardOfOzTrigger(botAI); }
static Trigger* karazhan_the_curator(PlayerbotAI* botAI) { return new KarazhanTheCuratorTrigger(botAI); }
static Trigger* karazhan_terestian_illhoof(PlayerbotAI* botAI) { return new KarazhanTerestianIllhoofTrigger(botAI); }
static Trigger* karazhan_shade_of_aran(PlayerbotAI* botAI) { return new KarazhanShadeOfAranTrigger(botAI); }
static Trigger* karazhan_netherspite(PlayerbotAI* botAI) { return new KarazhanNetherspiteTrigger(botAI); }
static Trigger* karazhan_prince_malchezaar(PlayerbotAI* botAI) { return new KarazhanPrinceMalchezaarTrigger(botAI); }
};
#endif

View File

@@ -0,0 +1,105 @@
#include "RaidKarazhanTriggers.h"
#include "RaidKarazhanHelpers.h"
#include "RaidKarazhanActions.h"
#include "Playerbots.h"
bool KarazhanAttumenTheHuntsmanTrigger::IsActive()
{
RaidKarazhanHelpers helpers(botAI);
Unit* boss = helpers.GetFirstAliveUnitByEntry(NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED);
return boss && boss->IsAlive();
}
bool KarazhanMoroesTrigger::IsActive()
{
Unit* moroes = AI_VALUE2(Unit*, "find target", "moroes");
Unit* dorothea = AI_VALUE2(Unit*, "find target", "baroness dorothea millstipe");
Unit* catriona = AI_VALUE2(Unit*, "find target", "lady catriona von'indi");
Unit* keira = AI_VALUE2(Unit*, "find target", "lady keira berrybuck");
Unit* rafe = AI_VALUE2(Unit*, "find target", "baron rafe dreuger");
Unit* robin = AI_VALUE2(Unit*, "find target", "lord robin daris");
Unit* crispin = AI_VALUE2(Unit*, "find target", "lord crispin ference");
return ((moroes && moroes->IsAlive()) ||
(dorothea && dorothea->IsAlive()) ||
(catriona && catriona->IsAlive()) ||
(keira && keira->IsAlive()) ||
(rafe && rafe->IsAlive()) ||
(robin && robin->IsAlive()) ||
(crispin && crispin->IsAlive()));
}
bool KarazhanMaidenOfVirtueTrigger::IsActive()
{
Unit* boss = AI_VALUE2(Unit*, "find target", "maiden of virtue");
return boss && boss->IsAlive();
}
bool KarazhanBigBadWolfTrigger::IsActive()
{
Unit* boss = AI_VALUE2(Unit*, "find target", "the big bad wolf");
return boss && boss->IsAlive();
}
bool KarazhanRomuloAndJulianneTrigger::IsActive()
{
Unit* julianne = AI_VALUE2(Unit*, "find target", "julianne");
Unit* romulo = AI_VALUE2(Unit*, "find target", "romulo");
return julianne && julianne->IsAlive() && romulo && romulo->IsAlive();
}
bool KarazhanWizardOfOzTrigger::IsActive()
{
Unit* dorothee = AI_VALUE2(Unit*, "find target", "dorothee");
Unit* tito = AI_VALUE2(Unit*, "find target", "tito");
Unit* roar = AI_VALUE2(Unit*, "find target", "roar");
Unit* strawman = AI_VALUE2(Unit*, "find target", "strawman");
Unit* tinhead = AI_VALUE2(Unit*, "find target", "tinhead");
Unit* crone = AI_VALUE2(Unit*, "find target", "the crone");
return ((dorothee && dorothee->IsAlive()) ||
(tito && tito->IsAlive()) ||
(roar && roar->IsAlive()) ||
(strawman && strawman->IsAlive()) ||
(tinhead && tinhead->IsAlive()) ||
(crone && crone->IsAlive()));
}
bool KarazhanTheCuratorTrigger::IsActive()
{
Unit* boss = AI_VALUE2(Unit*, "find target", "the curator");
return boss && boss->IsAlive();
}
bool KarazhanTerestianIllhoofTrigger::IsActive()
{
Unit* boss = AI_VALUE2(Unit*, "find target", "terestian illhoof");
return boss && boss->IsAlive();
}
bool KarazhanShadeOfAranTrigger::IsActive()
{
Unit* boss = AI_VALUE2(Unit*, "find target", "shade of aran");
return boss && boss->IsAlive();
}
bool KarazhanNetherspiteTrigger::IsActive()
{
Unit* boss = AI_VALUE2(Unit*, "find target", "netherspite");
return boss && boss->IsAlive();
}
bool KarazhanPrinceMalchezaarTrigger::IsActive()
{
Unit* boss = AI_VALUE2(Unit*, "find target", "prince malchezaar");
return boss && boss->IsAlive();
}

View File

@@ -0,0 +1,83 @@
#ifndef _PLAYERBOT_RAIDKARAZHANTRIGGERS_H
#define _PLAYERBOT_RAIDKARAZHANTRIGGERS_H
#include "Trigger.h"
class KarazhanAttumenTheHuntsmanTrigger : public Trigger
{
public:
KarazhanAttumenTheHuntsmanTrigger(PlayerbotAI* botAI) : Trigger(botAI, "karazhan attumen the huntsman") {}
bool IsActive() override;
};
class KarazhanMoroesTrigger : public Trigger
{
public:
KarazhanMoroesTrigger(PlayerbotAI* botAI) : Trigger(botAI, "karazhan moroes") {}
bool IsActive() override;
};
class KarazhanMaidenOfVirtueTrigger : public Trigger
{
public:
KarazhanMaidenOfVirtueTrigger(PlayerbotAI* botAI) : Trigger(botAI, "karazhan maiden of virtue") {}
bool IsActive() override;
};
class KarazhanBigBadWolfTrigger : public Trigger
{
public:
KarazhanBigBadWolfTrigger(PlayerbotAI* botAI) : Trigger(botAI, "karazhan big bad wolf") {}
bool IsActive() override;
};
class KarazhanRomuloAndJulianneTrigger : public Trigger
{
public:
KarazhanRomuloAndJulianneTrigger(PlayerbotAI* botAI) : Trigger(botAI, "karazhan romulo and julianne") {}
bool IsActive() override;
};
class KarazhanWizardOfOzTrigger : public Trigger
{
public:
KarazhanWizardOfOzTrigger(PlayerbotAI* botAI) : Trigger(botAI, "karazhan wizard of oz") {}
bool IsActive() override;
};
class KarazhanTheCuratorTrigger : public Trigger
{
public:
KarazhanTheCuratorTrigger(PlayerbotAI* botAI) : Trigger(botAI, "karazhan the curator") {}
bool IsActive() override;
};
class KarazhanTerestianIllhoofTrigger : public Trigger
{
public:
KarazhanTerestianIllhoofTrigger(PlayerbotAI* botAI) : Trigger(botAI, "karazhan terestian illhoof") {}
bool IsActive() override;
};
class KarazhanShadeOfAranTrigger : public Trigger
{
public:
KarazhanShadeOfAranTrigger(PlayerbotAI* botAI) : Trigger(botAI, "karazhan shade of aran") {}
bool IsActive() override;
};
class KarazhanNetherspiteTrigger : public Trigger
{
public:
KarazhanNetherspiteTrigger(PlayerbotAI* botAI) : Trigger(botAI, "karazhan netherspite") {}
bool IsActive() override;
};
class KarazhanPrinceMalchezaarTrigger : public Trigger
{
public:
KarazhanPrinceMalchezaarTrigger(PlayerbotAI* botAI) : Trigger(botAI, "karazhan prince malchezaar") {}
bool IsActive() override;
};
#endif