Implement Battle for Mount Hyjal Strategies (#2258)

<!--
Thank you for contributing to mod-playerbots, please make sure that
you...
1. Submit your PR to the test-staging branch, not master.
2. Read the guidelines below before submitting.
3. Don't delete parts of this template.

DESIGN PHILOSOPHY: We prioritize STABILITY, PERFORMANCE, AND
PREDICTABILITY over behavioral realism.

Every action and decision executes PER BOT AND PER TRIGGER. Small
increases in logic complexity scale
poorly across thousands of bots and negatively affect all. We prioritize
a stable system over a smarter
one. Bots don't need to behave perfectly; believable behavior is the
goal, not human simulation.
Default behavior must be cheap in processing; expensive behavior must be
opt-in.

Before submitting, make sure your changes aligns with these principles.
-->

### Contingent on
https://github.com/mod-playerbots/mod-playerbots/pull/2295/

## Pull Request Description
<!-- Describe what this change does and why it is needed -->
This PR implements raid strategies for all bosses in everybody's
favorite TBC instance, the Battle for Mount Hyjal. As before, I have
designed these all to work with IP with 50% damage and healing. I also
did not merge the 1.88x buff to Vanilla & TBC healing items that IP
recently implemented.

The next post will outline all implemented strategies.

Note: Set to draft for now as I may tweak Archimonde some more, but I
generally consider Hyjal complete, subject to comments.

## Feature Evaluation
<!--
If your PR is very minimal (comment typo, wrong ID reference, etc), and
it is very obvious it will not have
any impact on performance, you may skip these question. If necessary, a
maintainer may ask you for them later.
-->

<!-- Please answer the following: -->
- Describe the **minimum logic** required to achieve the intended
behavior.
- Describe the **processing cost** when this logic executes across many
bots.

As with previous strategies, I've worked within the existing context of
actions/multipliers/triggers. This strategy does implement new spell
hooks, but I don't think that is problematic for performance, and I'll
explain why they are necessary in the next post.


## How to Test the Changes
<!--
- Step-by-step instructions to test the change.
- Any required setup (e.g. multiple players, number of bots, specific
configuration).
- Expected behavior and how to verify it.
-->



## Impact Assessment
<!-- As a generic test, before and after measure of pmon (playerbot pmon
tick) can help you here. -->
- Does this change increase per-bot/per-tick processing or risk scaling
poorly with thousands of bots?
    - - [ ] No, not at all
    - - [x] Minimal impact (**explain below**)
    - - [ ] Moderate impact (**explain below**)

There are many new triggers, multipliers, and actions, but they will be
evaluated only if the "hyjal" strategy is added. Additionally, I've
attempted to order and implement checks in a manner to limit performance
impact and have tested with .pmon active. In general, I consider
performance to be highly important so am always working on ways to limit
the impact (e.g., trying to use the most targeted grid searches
available when needed).


- Does this change modify default bot behavior?
    - - [ ] No
    - - [x] Yes (**explain why**)

Only in the Hyjal instance, for obvious reasons.


- Does this change add new decision branches or increase maintenance
complexity?
    - - [ ] No
    - - [x] Yes (**explain below**)

New decision branches apply only in the new Hyjal instance, for obvious
reasons. Maintenance complexity should not be increased as code outside
of the new Hyjal files is not impacted, except to the extent needed to
register and implement the strategy in the same manner as all existing
strategies. Exception: As noted, I did add new spell hooks, but if they
become problematic, they can easily be removed.


## Messages to Translate
<!--
Bot messages have to be translatable, but you don't need to do the
translations here. You only need to make sure
the message is in a translatable format, and list in the table the
message_key and the default English message.
Search for GetBotTextOrDefault in the codebase for examples.
-->
- Does this change add bot messages to translate?
    - - [x] No
    - - [ ] Yes (**list messages in the table**)

| Message key  | Default message |
| --------------- | ------------------ |
|			 |			      |
|			 |			      |

## AI Assistance
<!--
AI assistance is allowed, but all submitted code must be fully
understood, reviewed, and owned by the contributor.
We expect contributors to be honest about what they do and do not
understand.
-->
- Was AI assistance used while working on this change?
    - - [ ] No
    - - [x] Yes (**explain below**)
<!--
If yes, please specify:
- Purpose of usage (e.g. brainstorming, refactoring, documentation, code
generation).
- Which parts of the change were influenced or generated, and whether it
was thoroughly reviewed.
-->

Claude Sonnet 4.6 was used for more complex implementation and
occasionally GPT-5 mini was used for simple questions. I do not use AI
for brainstorming or developing strategies, only for implementation and
review of code. Most of this was written by me directly, but most
notably I needed AI support to implement the spell hooks and
triggers/actions that relied on them. Everything was reviewed and tested
many times.


## Final Checklist

- - [x] Stability is not compromised.
- - [x] Performance impact is understood, tested, and acceptable.
Caveat: The full impact of implementing the spell hooks on a broad scale
is beyond my knowledge to evaluate.
- - [x] Added logic complexity is justified and explained.
- - [x] Documentation updated if needed (Conf comments, WiKi commands).

## Notes for Reviewers
<!-- Anything else that's helpful to review or test your pull request.
-->

---------

Co-authored-by: Keleborn <22352763+Celandriel@users.noreply.github.com>
Co-authored-by: bash <hermensb@gmail.com>
Co-authored-by: Revision <tkn963@gmail.com>
Co-authored-by: kadeshar <kadeshar@gmail.com>
This commit is contained in:
Crow 2026-05-09 00:39:32 -05:00 committed by GitHub
parent 38caa1daa7
commit 5d9761c9e8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 3760 additions and 5 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,277 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_RAIDHYJALSUMMITACTIONS_H
#define _PLAYERBOT_RAIDHYJALSUMMITACTIONS_H
#include "Action.h"
#include "AttackAction.h"
#include "MovementActions.h"
// General
class HyjalSummitEraseTrackersAction : public Action
{
public:
HyjalSummitEraseTrackersAction(
PlayerbotAI* botAI) : Action(botAI, "hyjal summit erase trackers") {}
bool Execute(Event event) override;
};
// Rage Winterchill
class RageWinterchillMisdirectBossToMainTankAction : public AttackAction
{
public:
RageWinterchillMisdirectBossToMainTankAction(
PlayerbotAI* botAI) : AttackAction(botAI, "rage winterchill misdirect boss to main tank") {}
bool Execute(Event event) override;
};
class RageWinterchillMainTankPositionBossAction : public AttackAction
{
public:
RageWinterchillMainTankPositionBossAction(
PlayerbotAI* botAI) : AttackAction(botAI, "rage winterchill main tank position boss") {}
bool Execute(Event event) override;
};
class RageWinterchillSpreadRangedInCircleAction : public MovementAction
{
public:
RageWinterchillSpreadRangedInCircleAction(
PlayerbotAI* botAI) : MovementAction(botAI, "rage winterchill spread ranged in circle") {}
bool Execute(Event event) override;
};
class RageWinterchillMeleeGetOutOfDeathAndDecayAction : public AttackAction
{
public:
RageWinterchillMeleeGetOutOfDeathAndDecayAction(
PlayerbotAI* botAI) : AttackAction(botAI, "rage winterchill melee get out of death and decay") {}
bool Execute(Event event) override;
};
// Anetheron
class AnetheronMisdirectBossAndInfernalsToTanksAction : public AttackAction
{
public:
AnetheronMisdirectBossAndInfernalsToTanksAction(
PlayerbotAI* botAI) : AttackAction(botAI, "anetheron misdirect boss and infernals to tanks") {}
bool Execute(Event event) override;
};
class AnetheronMainTankPositionBossAction : public AttackAction
{
public:
AnetheronMainTankPositionBossAction(
PlayerbotAI* botAI) : AttackAction(botAI, "anetheron main tank position boss") {}
bool Execute(Event event) override;
};
class AnetheronSpreadRangedInCircleAction : public MovementAction
{
public:
AnetheronSpreadRangedInCircleAction(
PlayerbotAI* botAI) : MovementAction(botAI, "anetheron spread ranged in circle") {}
bool Execute(Event event) override;
};
class AnetheronBringInfernalToInfernalTankAction : public MovementAction
{
public:
AnetheronBringInfernalToInfernalTankAction(
PlayerbotAI* botAI) : MovementAction(botAI, "anetheron bring infernal to infernal tank") {}
bool Execute(Event event) override;
};
class AnetheronFirstAssistTankPickUpInfernalsAction : public AttackAction
{
public:
AnetheronFirstAssistTankPickUpInfernalsAction(
PlayerbotAI* botAI) : AttackAction(botAI, "anetheron first assist tank pick up infernals") {}
bool Execute(Event event) override;
};
class AnetheronAssignDpsPriorityAction : public AttackAction
{
public:
AnetheronAssignDpsPriorityAction(
PlayerbotAI* botAI) : AttackAction(botAI, "anetheron assign dps priority") {}
bool Execute(Event event) override;
};
// Kaz'rogal
class KazrogalMisdirectBossToMainTankAction : public AttackAction
{
public:
KazrogalMisdirectBossToMainTankAction(
PlayerbotAI* botAI) : AttackAction(botAI, "kaz'rogal misdirect boss to main tank") {}
bool Execute(Event event) override;
};
class KazrogalMainTankPositionBossAction : public AttackAction
{
public:
KazrogalMainTankPositionBossAction(
PlayerbotAI* botAI) : AttackAction(botAI, "kaz'rogal main tank position boss") {}
bool Execute(Event event) override;
};
class KazrogalAssistTanksMoveInFrontOfBossAction : public AttackAction
{
public:
KazrogalAssistTanksMoveInFrontOfBossAction(
PlayerbotAI* botAI) : AttackAction(botAI, "kaz'rogal assist tanks move in front of boss") {}
bool Execute(Event event) override;
};
class KazrogalSpreadRangedInArcAction : public MovementAction
{
public:
KazrogalSpreadRangedInArcAction(
PlayerbotAI* botAI) : MovementAction(botAI, "kaz'rogal spread ranged in arc") {}
bool Execute(Event event) override;
};
class KazrogalLowManaBotTakeDefensiveMeasuresAction : public MovementAction
{
public:
KazrogalLowManaBotTakeDefensiveMeasuresAction(
PlayerbotAI* botAI) : MovementAction(botAI, "kaz'rogal low mana bot take defensive measures") {}
bool Execute(Event event) override;
};
class KazrogalCastShadowProtectionSpellAction : public Action
{
public:
KazrogalCastShadowProtectionSpellAction(
PlayerbotAI* botAI) : Action(botAI, "kaz'rogal cast shadow protection spell") {}
bool Execute(Event event) override;
};
// Azgalor
class AzgalorMisdirectBossToMainTankAction : public AttackAction
{
public:
AzgalorMisdirectBossToMainTankAction(
PlayerbotAI* botAI) : AttackAction(botAI, "azgalor misdirect boss to main tank") {}
bool Execute(Event event) override;
};
class AzgalorMainTankPositionBossAction : public AttackAction
{
public:
AzgalorMainTankPositionBossAction(
PlayerbotAI* botAI) : AttackAction(botAI, "azgalor main tank position boss") {}
bool Execute(Event event) override;
};
class AzgalorWaitAtSafePositionAction : public MovementAction
{
public:
AzgalorWaitAtSafePositionAction(
PlayerbotAI* botAI) : MovementAction(botAI, "azgalor wait at safe position") {}
bool Execute(Event event) override;
};
class AzgalorDisperseRangedAction : public MovementAction
{
public:
AzgalorDisperseRangedAction(
PlayerbotAI* botAI) : MovementAction(botAI, "azgalor disperse ranged") {}
bool Execute(Event event) override;
};
class AzgalorMeleeGetOutOfFireAndSwapTargetsAction : public AttackAction
{
public:
AzgalorMeleeGetOutOfFireAndSwapTargetsAction(
PlayerbotAI* botAI) : AttackAction(botAI, "azgalor melee get out of fire and swap targets") {}
bool Execute(Event event) override;
};
class AzgalorMoveToDoomguardTankAction : public MovementAction
{
public:
AzgalorMoveToDoomguardTankAction(
PlayerbotAI* botAI) : MovementAction(botAI, "azgalor move to doomguard tank") {}
bool Execute(Event event) override;
};
class AzgalorFirstAssistTankPositionDoomguardAction : public AttackAction
{
public:
AzgalorFirstAssistTankPositionDoomguardAction(
PlayerbotAI* botAI) : AttackAction(botAI, "azgalor first assist tank position doomguard") {}
bool Execute(Event event) override;
};
class AzgalorRangedDpsPrioritizeDoomguardsAction : public AttackAction
{
public:
AzgalorRangedDpsPrioritizeDoomguardsAction(
PlayerbotAI* botAI) : AttackAction(botAI, "azgalor ranged dps prioritize doomguards") {}
bool Execute(Event event) override;
};
// Archimonde
class ArchimondeMisdirectBossToMainTankAction : public AttackAction
{
public:
ArchimondeMisdirectBossToMainTankAction(
PlayerbotAI* botAI) : AttackAction(botAI, "archimonde misdirect boss to main tank") {}
bool Execute(Event event) override;
};
class ArchimondeMoveBossToInitialPositionAction : public AttackAction
{
public:
ArchimondeMoveBossToInitialPositionAction(
PlayerbotAI* botAI) : AttackAction(botAI, "archimonde move boss to initial position") {}
bool Execute(Event event) override;
};
class ArchimondeCastFearImmunitySpellAction : public Action
{
public:
ArchimondeCastFearImmunitySpellAction(
PlayerbotAI* botAI) : Action(botAI, "archimonde cast fear immunity spell") {}
bool Execute(Event event) override;
private:
bool CastFearWardOnMainTank();
bool UseTremorTotemStrategy();
};
class ArchimondeSpreadToAvoidAirBurstAction : public MovementAction
{
public:
ArchimondeSpreadToAvoidAirBurstAction(
PlayerbotAI* botAI) : MovementAction(botAI, "archimonde spread to avoid air burst") {}
bool Execute(Event event) override;
};
class ArchimondeAvoidDoomfireAction : public MovementAction
{
public:
ArchimondeAvoidDoomfireAction(
PlayerbotAI* botAI) : MovementAction(botAI, "archimonde avoid doomfire") {}
bool Execute(Event event) override;
};
class ArchimondeRemoveDoomfireDotAction : public Action
{
public:
ArchimondeRemoveDoomfireDotAction(
PlayerbotAI* botAI) : Action(botAI, "archimonde remove doomfire dot") {}
bool Execute(Event event) override;
};
#endif

View File

@ -0,0 +1,295 @@
/*
* 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 "RaidHyjalSummitMultipliers.h"
#include "RaidHyjalSummitActions.h"
#include "RaidHyjalSummitHelpers.h"
#include "AiFactory.h"
#include "ChooseTargetActions.h"
#include "DKActions.h"
#include "DruidBearActions.h"
#include "HunterActions.h"
#include "PaladinActions.h"
#include "RaidBossHelpers.h"
#include "ReachTargetActions.h"
#include "ShamanActions.h"
#include "WarriorActions.h"
using namespace HyjalSummitHelpers;
// Without this multiplier, Bloodlust/Heroism will not be available for
// bosses because it will be used on cooldown during trash waves
float HyjalSummitTimeBloodlustAndHeroismMultiplier::GetValue(Action* action)
{
if (bot->getClass() != CLASS_SHAMAN)
return 1.0f;
if (dynamic_cast<CastBloodlustAction*>(action) ||
dynamic_cast<CastHeroismAction*>(action))
{
Unit* archimonde = AI_VALUE2(Unit*, "find target", "archimonde");
if (archimonde && archimonde->GetHealthPct() < 90.0f)
return 1.0f;
Unit* azgalor = AI_VALUE2(Unit*, "find target", "azgalor");
if (azgalor && azgalor->GetHealthPct() < 90.0f)
return 1.0f;
Unit* kazrogal = AI_VALUE2(Unit*, "find target", "kaz'rogal");
if (kazrogal && kazrogal->GetHealthPct() < 90.0f)
return 1.0f;
Unit* anetheron = AI_VALUE2(Unit*, "find target", "anetheron");
if (anetheron && anetheron->GetHealthPct() < 85.0f)
return 1.0f;
Unit* winterchill = AI_VALUE2(Unit*, "find target", "rage winterchill");
if (winterchill && winterchill->GetHealthPct() < 90.0f)
return 1.0f;
return 0.0f;
}
return 1.0f;
}
// Rage Winterchill
float RageWinterchillDisableCombatFormationMoveMultiplier::GetValue(Action* action)
{
if (!AI_VALUE2(Unit*, "find target", "rage winterchill"))
return 1.0f;
if (dynamic_cast<CombatFormationMoveAction*>(action) &&
!dynamic_cast<SetBehindTargetAction*>(action))
return 0.0f;
return 1.0f;
}
float RageWinterchillMeleeControlAvoidanceMultiplier::GetValue(Action* action)
{
if (botAI->IsRanged(bot))
return 1.0f;
Unit* winterchill = AI_VALUE2(Unit*, "find target", "rage winterchill");
if (!winterchill)
return 1.0f;
if (IsInDeathAndDecay(bot, DEATH_AND_DECAY_SAFE_RADIUS + 2.0f))
{
if (dynamic_cast<AvoidAoeAction*>(action))
return 0.0f;
if (botAI->IsMainTank(bot) || winterchill->GetVictim() == bot)
return 1.0f;
if (dynamic_cast<MovementAction*>(action) &&
!dynamic_cast<RageWinterchillMeleeGetOutOfDeathAndDecayAction*>(action))
return 0.0f;
if (dynamic_cast<CastReachTargetSpellAction*>(action))
return 0.0f;
}
return 1.0f;
}
// Anetheron
float AnetheronDisableTankActionsMultiplier::GetValue(Action* action)
{
if (!botAI->IsTank(bot) || !AI_VALUE2(Unit*, "find target", "anetheron"))
return 1.0f;
if (dynamic_cast<AvoidAoeAction*>(action))
return 0.0f;
if (bot->GetVictim() != nullptr &&
dynamic_cast<TankAssistAction*>(action))
return 0.0f;
return 1.0f;
}
float AnetheronDisableCombatFormationMoveMultiplier::GetValue(Action* action)
{
if (!AI_VALUE2(Unit*, "find target", "anetheron"))
return 1.0f;
if (dynamic_cast<CombatFormationMoveAction*>(action) &&
!dynamic_cast<SetBehindTargetAction*>(action))
return 0.0f;
return 1.0f;
}
float AnetheronControlMisdirectionMultiplier::GetValue(Action* action)
{
if (bot->getClass() != CLASS_HUNTER ||
!AI_VALUE2(Unit*, "find target", "anetheron"))
return 1.0f;
if (dynamic_cast<CastMisdirectionOnMainTankAction*>(action))
return 0.0f;
return 1.0f;
}
// Kaz'rogal
float KazrogalLowManaBotStayAwayFromGroupMultiplier::GetValue(Action* action)
{
if (bot->getClass() == CLASS_WARRIOR || bot->getClass() == CLASS_ROGUE ||
bot->getClass() == CLASS_DEATH_KNIGHT || bot->getClass() == CLASS_HUNTER)
return 1.0f;
uint8 tab = AiFactory::GetPlayerSpecTab(bot);
if (bot->getClass() == CLASS_DRUID && tab == DRUID_TAB_FERAL)
return 1.0f;
if (!AI_VALUE2(Unit*, "find target", "kaz'rogal"))
return 1.0f;
if (!isBelowManaThreshold.count(bot->GetGUID()))
return 1.0f;
if (dynamic_cast<CastReachTargetSpellAction*>(action) ||
(dynamic_cast<MovementAction*>(action) &&
!dynamic_cast<AttackAction*>(action) &&
!dynamic_cast<KazrogalLowManaBotTakeDefensiveMeasuresAction*>(action)))
return 0.0f;
return 1.0f;
}
float KazrogalKeepAspectOfTheViperActiveMultiplier::GetValue(Action* action)
{
if (bot->getClass() != CLASS_HUNTER || bot->GetPower(POWER_MANA) > 4000 ||
!AI_VALUE2(Unit*, "find target", "kaz'rogal"))
return 1.0f;
if (dynamic_cast<CastAspectOfTheHawkAction*>(action) ||
dynamic_cast<CastAspectOfTheWildAction*>(action) ||
dynamic_cast<CastAspectOfTheDragonhawkAction*>(action) ||
dynamic_cast<CastAspectOfTheCheetahAction*>(action) ||
dynamic_cast<CastAspectOfThePackAction*>(action) ||
dynamic_cast<CastAspectOfTheMonkeyAction*>(action))
return 0.0f;
return 1.0f;
}
float KazrogalControlMovementMultiplier::GetValue(Action* action)
{
if (!AI_VALUE2(Unit*, "find target", "kaz'rogal"))
return 1.0f;
if (dynamic_cast<CombatFormationMoveAction*>(action) &&
!dynamic_cast<SetBehindTargetAction*>(action))
return 0.0f;
if (dynamic_cast<FleeAction*>(action))
return 0.0f;
if (botAI->IsRanged(bot) && dynamic_cast<ReachTargetAction*>(action))
return 0.0f;
return 1.0f;
}
// Azgalor
float AzgalorDisableTankActionsMultiplier::GetValue(Action* action)
{
if (bot->GetVictim() == nullptr)
return 1.0f;
if (!botAI->IsTank(bot) || !AI_VALUE2(Unit*, "find target", "azgalor"))
return 1.0f;
if (dynamic_cast<TankFaceAction*>(action))
return 0.0f;
if (dynamic_cast<TankAssistAction*>(action) || dynamic_cast<AvoidAoeAction*>(action))
{
if (botAI->IsMainTank(bot))
{
return 0.0f;
}
else if (botAI->IsAssistTank(bot) && (AnyGroupMemberHasDoom(bot) ||
AI_VALUE2(Unit*, "find target", "lesser doomguard")))
{
return 0.0f;
}
}
return 1.0f;
}
float AzgalorDoomedBotPrioritizePositioningMultiplier::GetValue(Action* action)
{
if (!bot->HasAura(static_cast<uint32>(HyjalSummitSpells::SPELL_DOOM)))
return 1.0f;
if (dynamic_cast<MovementAction*>(action) &&
!dynamic_cast<AttackAction*>(action) &&
!dynamic_cast<AvoidAoeAction*>(action) &&
!dynamic_cast<AzgalorMoveToDoomguardTankAction*>(action))
return 0.0f;
return 1.0f;
}
float AzgalorMeleeDpsControlAvoidanceMultiplier::GetValue(Action* action)
{
if (botAI->IsRanged(bot) || botAI->IsTank(bot))
return 1.0f;
Unit* azgalor = AI_VALUE2(Unit*, "find target", "azgalor");
if (!azgalor)
return 1.0f;
constexpr float singleTickMoveAwayDist = 6.0f;
if (IsInRainOfFire(bot, RAIN_OF_FIRE_RADIUS + singleTickMoveAwayDist))
{
if (dynamic_cast<AvoidAoeAction*>(action) ||
dynamic_cast<CastReachTargetSpellAction*>(action))
return 0.0f;
if (dynamic_cast<MovementAction*>(action) &&
!dynamic_cast<AzgalorMeleeGetOutOfFireAndSwapTargetsAction*>(action))
return 0.0f;
}
Player* mainTank = GetGroupMainTank(botAI, bot);
if (!mainTank || !GET_PLAYERBOT_AI(mainTank))
return 1.0f;
TankPositionState tankState = GetAzgalorTankPositionState(botAI, bot);
if ((tankState == TankPositionState::Unknown ||
tankState == TankPositionState::MovingToTransition) &&
dynamic_cast<MovementAction*>(action) &&
!dynamic_cast<AzgalorWaitAtSafePositionAction*>(action))
{
return 0.0f;
}
return 1.0f;
}
// Archimonde
float ArchimondeDisableCombatFormationMoveMultiplier::GetValue(Action* action)
{
if (!AI_VALUE2(Unit*, "find target", "archimonde"))
return 1.0f;
if (dynamic_cast<CombatFormationMoveAction*>(action) &&
!dynamic_cast<SetBehindTargetAction*>(action))
return 0.0f;
return 1.0f;
}

View File

@ -0,0 +1,125 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_RAIDHYJALSUMMITMULTIPLIERS_H
#define _PLAYERBOT_RAIDHYJALSUMMITMULTIPLIERS_H
#include "Multiplier.h"
class HyjalSummitTimeBloodlustAndHeroismMultiplier : public Multiplier
{
public:
HyjalSummitTimeBloodlustAndHeroismMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "hyjal summit time bloodlust and heroism multiplier") {}
virtual float GetValue(Action* action);
};
// Rage Winterchill
class RageWinterchillDisableCombatFormationMoveMultiplier : public Multiplier
{
public:
RageWinterchillDisableCombatFormationMoveMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "rage winterchill disable combat formation move multiplier") {}
virtual float GetValue(Action* action);
};
class RageWinterchillMeleeControlAvoidanceMultiplier : public Multiplier
{
public:
RageWinterchillMeleeControlAvoidanceMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "rage winterchill melee control avoidance multiplier") {}
virtual float GetValue(Action* action);
};
// Anetheron
class AnetheronDisableTankActionsMultiplier : public Multiplier
{
public:
AnetheronDisableTankActionsMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "anetheron disable tank actions multiplier") {}
virtual float GetValue(Action* action);
};
class AnetheronDisableCombatFormationMoveMultiplier : public Multiplier
{
public:
AnetheronDisableCombatFormationMoveMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "anetheron disable combat formation move multiplier") {}
virtual float GetValue(Action* action);
};
class AnetheronControlMisdirectionMultiplier : public Multiplier
{
public:
AnetheronControlMisdirectionMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "anetheron control misdirection multiplier") {}
virtual float GetValue(Action* action);
};
// Kaz'rogal
class KazrogalLowManaBotStayAwayFromGroupMultiplier : public Multiplier
{
public:
KazrogalLowManaBotStayAwayFromGroupMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "kaz'rogal low mana bot stay away from group multiplier") {}
virtual float GetValue(Action* action);
};
class KazrogalKeepAspectOfTheViperActiveMultiplier : public Multiplier
{
public:
KazrogalKeepAspectOfTheViperActiveMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "kaz'rogal keep aspect of the viper active multiplier") {}
virtual float GetValue(Action* action);
};
class KazrogalControlMovementMultiplier : public Multiplier
{
public:
KazrogalControlMovementMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "kaz'rogal control movement multiplier") {}
virtual float GetValue(Action* action);
};
// Azgalor
class AzgalorDisableTankActionsMultiplier : public Multiplier
{
public:
AzgalorDisableTankActionsMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "azgalor disable tank actions multiplier") {}
virtual float GetValue(Action* action);
};
class AzgalorDoomedBotPrioritizePositioningMultiplier : public Multiplier
{
public:
AzgalorDoomedBotPrioritizePositioningMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "azgalor doomed bot prioritize positioning multiplier") {}
virtual float GetValue(Action* action);
};
class AzgalorMeleeDpsControlAvoidanceMultiplier : public Multiplier
{
public:
AzgalorMeleeDpsControlAvoidanceMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "azgalor melee dps control avoidance multiplier") {}
virtual float GetValue(Action* action);
};
// Archimonde
class ArchimondeDisableCombatFormationMoveMultiplier : public Multiplier
{
public:
ArchimondeDisableCombatFormationMoveMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "archimonde disable combat formation move multiplier") {}
virtual float GetValue(Action* action);
};
#endif

View File

@ -0,0 +1,218 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_RAIDHYJALSUMMITACTIONCONTEXT_H
#define _PLAYERBOT_RAIDHYJALSUMMITACTIONCONTEXT_H
#include "RaidHyjalSummitActions.h"
#include "NamedObjectContext.h"
class RaidHyjalSummitActionContext : public NamedObjectContext<Action>
{
public:
RaidHyjalSummitActionContext()
{
// General
creators["hyjal summit erase trackers"] =
&RaidHyjalSummitActionContext::hyjal_summit_erase_trackers;
// Rage Winterchill
creators["rage winterchill misdirect boss to main tank"] =
&RaidHyjalSummitActionContext::rage_winterchill_misdirect_boss_to_main_tank;
creators["rage winterchill main tank position boss"] =
&RaidHyjalSummitActionContext::rage_winterchill_main_tank_position_boss;
creators["rage winterchill spread ranged in circle"] =
&RaidHyjalSummitActionContext::rage_winterchill_spread_ranged_in_circle;
creators["rage winterchill melee get out of death and decay"] =
&RaidHyjalSummitActionContext::rage_winterchill_melee_get_out_of_death_and_decay;
// Anetheron
creators["anetheron misdirect boss and infernals to tanks"] =
&RaidHyjalSummitActionContext::anetheron_misdirect_boss_and_infernals_to_tanks;
creators["anetheron main tank position boss"] =
&RaidHyjalSummitActionContext::anetheron_main_tank_position_boss;
creators["anetheron spread ranged in circle"] =
&RaidHyjalSummitActionContext::anetheron_spread_ranged_in_circle;
creators["anetheron bring infernal to infernal tank"] =
&RaidHyjalSummitActionContext::anetheron_bring_infernal_to_infernal_tank;
creators["anetheron first assist tank pick up infernals"] =
&RaidHyjalSummitActionContext::anetheron_first_assist_tank_pick_up_infernals;
creators["anetheron assign dps priority"] =
&RaidHyjalSummitActionContext::anetheron_assign_dps_priority;
// Kaz'rogal
creators["kaz'rogal misdirect boss to main tank"] =
&RaidHyjalSummitActionContext::kazrogal_misdirect_boss_to_main_tank;
creators["kaz'rogal main tank position boss"] =
&RaidHyjalSummitActionContext::kazrogal_main_tank_position_boss;
creators["kaz'rogal assist tanks move in front of boss"] =
&RaidHyjalSummitActionContext::kazrogal_assist_tanks_move_in_front_of_boss;
creators["kaz'rogal spread ranged in arc"] =
&RaidHyjalSummitActionContext::kazrogal_spread_ranged_in_arc;
creators["kaz'rogal low mana bot take defensive measures"] =
&RaidHyjalSummitActionContext::kazrogal_low_mana_bot_take_defensive_measures;
creators["kaz'rogal cast shadow protection spell"] =
&RaidHyjalSummitActionContext::kazrogal_cast_shadow_protection_spell;
// Azgalor
creators["azgalor misdirect boss to main tank"] =
&RaidHyjalSummitActionContext::azgalor_misdirect_boss_to_main_tank;
creators["azgalor main tank position boss"] =
&RaidHyjalSummitActionContext::azgalor_main_tank_position_boss;
creators["azgalor wait at safe position"] =
&RaidHyjalSummitActionContext::azgalor_wait_at_safe_position;
creators["azgalor disperse ranged"] =
&RaidHyjalSummitActionContext::azgalor_disperse_ranged;
creators["azgalor melee get out of fire and swap targets"] =
&RaidHyjalSummitActionContext::azgalor_melee_get_out_of_fire_and_swap_targets;
creators["azgalor move to doomguard tank"] =
&RaidHyjalSummitActionContext::azgalor_move_to_doomguard_tank;
creators["azgalor first assist tank position doomguard"] =
&RaidHyjalSummitActionContext::azgalor_first_assist_tank_position_doomguard;
creators["azgalor ranged dps prioritize doomguards"] =
&RaidHyjalSummitActionContext::azgalor_ranged_dps_prioritize_doomguards;
// Archimonde
creators["archimonde misdirect boss to main tank"] =
&RaidHyjalSummitActionContext::archimonde_misdirect_boss_to_main_tank;
creators["archimonde move boss to initial position"] =
&RaidHyjalSummitActionContext::archimonde_move_boss_to_initial_position;
creators["archimonde cast fear immunity spell"] =
&RaidHyjalSummitActionContext::archimonde_cast_fear_immunity_spell;
creators["archimonde spread to avoid air burst"] =
&RaidHyjalSummitActionContext::archimonde_spread_to_avoid_air_burst;
creators["archimonde avoid doomfire"] =
&RaidHyjalSummitActionContext::archimonde_avoid_doomfire;
creators["archimonde remove doomfire dot"] =
&RaidHyjalSummitActionContext::archimonde_remove_doomfire_dot;
}
private:
// General
static Action* hyjal_summit_erase_trackers(
PlayerbotAI* botAI) { return new HyjalSummitEraseTrackersAction(botAI); }
// Rage Winterchill
static Action* rage_winterchill_misdirect_boss_to_main_tank(
PlayerbotAI* botAI) { return new RageWinterchillMisdirectBossToMainTankAction(botAI); }
static Action* rage_winterchill_main_tank_position_boss(
PlayerbotAI* botAI) { return new RageWinterchillMainTankPositionBossAction(botAI); }
static Action* rage_winterchill_spread_ranged_in_circle(
PlayerbotAI* botAI) { return new RageWinterchillSpreadRangedInCircleAction(botAI); }
static Action* rage_winterchill_melee_get_out_of_death_and_decay(
PlayerbotAI* botAI) { return new RageWinterchillMeleeGetOutOfDeathAndDecayAction(botAI); }
// Anetheron
static Action* anetheron_misdirect_boss_and_infernals_to_tanks(
PlayerbotAI* botAI) { return new AnetheronMisdirectBossAndInfernalsToTanksAction(botAI); }
static Action* anetheron_main_tank_position_boss(
PlayerbotAI* botAI) { return new AnetheronMainTankPositionBossAction(botAI); }
static Action* anetheron_spread_ranged_in_circle(
PlayerbotAI* botAI) { return new AnetheronSpreadRangedInCircleAction(botAI); }
static Action* anetheron_bring_infernal_to_infernal_tank(
PlayerbotAI* botAI) { return new AnetheronBringInfernalToInfernalTankAction(botAI); }
static Action* anetheron_first_assist_tank_pick_up_infernals(
PlayerbotAI* botAI) { return new AnetheronFirstAssistTankPickUpInfernalsAction(botAI); }
static Action* anetheron_assign_dps_priority(
PlayerbotAI* botAI) { return new AnetheronAssignDpsPriorityAction(botAI); }
// Kaz'rogal
static Action* kazrogal_misdirect_boss_to_main_tank(
PlayerbotAI* botAI) { return new KazrogalMisdirectBossToMainTankAction(botAI); }
static Action* kazrogal_main_tank_position_boss(
PlayerbotAI* botAI) { return new KazrogalMainTankPositionBossAction(botAI); }
static Action* kazrogal_assist_tanks_move_in_front_of_boss(
PlayerbotAI* botAI) { return new KazrogalAssistTanksMoveInFrontOfBossAction(botAI); }
static Action* kazrogal_spread_ranged_in_arc(
PlayerbotAI* botAI) { return new KazrogalSpreadRangedInArcAction(botAI); }
static Action* kazrogal_low_mana_bot_take_defensive_measures(
PlayerbotAI* botAI) { return new KazrogalLowManaBotTakeDefensiveMeasuresAction(botAI); }
static Action* kazrogal_cast_shadow_protection_spell(
PlayerbotAI* botAI) { return new KazrogalCastShadowProtectionSpellAction(botAI); }
// Azgalor
static Action* azgalor_misdirect_boss_to_main_tank(
PlayerbotAI* botAI) { return new AzgalorMisdirectBossToMainTankAction(botAI); }
static Action* azgalor_main_tank_position_boss(
PlayerbotAI* botAI) { return new AzgalorMainTankPositionBossAction(botAI); }
static Action* azgalor_wait_at_safe_position(
PlayerbotAI* botAI) { return new AzgalorWaitAtSafePositionAction(botAI); }
static Action* azgalor_disperse_ranged(
PlayerbotAI* botAI) { return new AzgalorDisperseRangedAction(botAI); }
static Action* azgalor_melee_get_out_of_fire_and_swap_targets(
PlayerbotAI* botAI) { return new AzgalorMeleeGetOutOfFireAndSwapTargetsAction(botAI); }
static Action* azgalor_move_to_doomguard_tank(
PlayerbotAI* botAI) { return new AzgalorMoveToDoomguardTankAction(botAI); }
static Action* azgalor_first_assist_tank_position_doomguard(
PlayerbotAI* botAI) { return new AzgalorFirstAssistTankPositionDoomguardAction(botAI); }
static Action* azgalor_ranged_dps_prioritize_doomguards(
PlayerbotAI* botAI) { return new AzgalorRangedDpsPrioritizeDoomguardsAction(botAI); }
// Archimonde
static Action* archimonde_misdirect_boss_to_main_tank(
PlayerbotAI* botAI) { return new ArchimondeMisdirectBossToMainTankAction(botAI); }
static Action* archimonde_move_boss_to_initial_position(
PlayerbotAI* botAI) { return new ArchimondeMoveBossToInitialPositionAction(botAI); }
static Action* archimonde_cast_fear_immunity_spell(
PlayerbotAI* botAI) { return new ArchimondeCastFearImmunitySpellAction(botAI); }
static Action* archimonde_spread_to_avoid_air_burst(
PlayerbotAI* botAI) { return new ArchimondeSpreadToAvoidAirBurstAction(botAI); }
static Action* archimonde_avoid_doomfire(
PlayerbotAI* botAI) { return new ArchimondeAvoidDoomfireAction(botAI); }
static Action* archimonde_remove_doomfire_dot(
PlayerbotAI* botAI) { return new ArchimondeRemoveDoomfireDotAction(botAI); }
};
#endif

View File

@ -0,0 +1,218 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_RAIDHYJALSUMMITTRIGGERCONTEXT_H
#define _PLAYERBOT_RAIDHYJALSUMMITTRIGGERCONTEXT_H
#include "RaidHyjalSummitTriggers.h"
#include "NamedObjectContext.h"
class RaidHyjalSummitTriggerContext : public NamedObjectContext<Trigger>
{
public:
RaidHyjalSummitTriggerContext()
{
// General
creators["hyjal summit bot is not in combat"] =
&RaidHyjalSummitTriggerContext::hyjal_summit_bot_is_not_in_combat;
// Rage Winterchill
creators["rage winterchill pulling boss"] =
&RaidHyjalSummitTriggerContext::rage_winterchill_pulling_boss;
creators["rage winterchill boss engaged by main tank"] =
&RaidHyjalSummitTriggerContext::rage_winterchill_boss_engaged_by_main_tank;
creators["rage winterchill boss casts death and decay on ranged"] =
&RaidHyjalSummitTriggerContext::rage_winterchill_boss_casts_death_and_decay_on_ranged;
creators["rage winterchill melee is standing in death and decay"] =
&RaidHyjalSummitTriggerContext::rage_winterchill_melee_is_standing_in_death_and_decay;
// Anetheron
creators["anetheron pulling boss or infernal"] =
&RaidHyjalSummitTriggerContext::anetheron_pulling_boss_or_infernal;
creators["anetheron boss engaged by main tank"] =
&RaidHyjalSummitTriggerContext::anetheron_boss_engaged_by_main_tank;
creators["anetheron boss casts carrion swarm"] =
&RaidHyjalSummitTriggerContext::anetheron_boss_casts_carrion_swarm;
creators["anetheron bot is targeted by infernal"] =
&RaidHyjalSummitTriggerContext::anetheron_bot_is_targeted_by_infernal;
creators["anetheron infernals need to be kept away from raid"] =
&RaidHyjalSummitTriggerContext::anetheron_infernals_need_to_be_kept_away_from_raid;
creators["anetheron infernals continue to spawn"] =
&RaidHyjalSummitTriggerContext::anetheron_infernals_continue_to_spawn;
// Kaz'rogal
creators["kaz'rogal pulling boss"] =
&RaidHyjalSummitTriggerContext::kazrogal_pulling_boss;
creators["kaz'rogal boss engaged by main tank"] =
&RaidHyjalSummitTriggerContext::kazrogal_boss_engaged_by_main_tank;
creators["kaz'rogal boss engaged by assist tanks"] =
&RaidHyjalSummitTriggerContext::kazrogal_boss_engaged_by_assist_tanks;
creators["kaz'rogal bot is low on mana"] =
&RaidHyjalSummitTriggerContext::kazrogal_bot_is_low_on_mana;
creators["kaz'rogal low mana bots need escape path"] =
&RaidHyjalSummitTriggerContext::kazrogal_low_mana_bots_need_escape_path;
creators["kaz'rogal mark deals shadow damage"] =
&RaidHyjalSummitTriggerContext::kazrogal_mark_deals_shadow_damage;
// Azgalor
creators["azgalor pulling boss"] =
&RaidHyjalSummitTriggerContext::azgalor_pulling_boss;
creators["azgalor boss engaged by main tank"] =
&RaidHyjalSummitTriggerContext::azgalor_boss_engaged_by_main_tank;
creators["azgalor main tank is positioning boss"] =
&RaidHyjalSummitTriggerContext::azgalor_main_tank_is_positioning_boss;
creators["azgalor boss engaged by ranged"] =
&RaidHyjalSummitTriggerContext::azgalor_boss_engaged_by_ranged;
creators["azgalor boss casts rain of fire on melee"] =
&RaidHyjalSummitTriggerContext::azgalor_boss_casts_rain_of_fire_on_melee;
creators["azgalor bot is doomed"] =
&RaidHyjalSummitTriggerContext::azgalor_bot_is_doomed;
creators["azgalor doomguards must be controlled"] =
&RaidHyjalSummitTriggerContext::azgalor_doomguards_must_be_controlled;
creators["azgalor doomguards must die"] =
&RaidHyjalSummitTriggerContext::azgalor_doomguards_must_die;
// Archimonde
creators["archimonde pulling boss"] =
&RaidHyjalSummitTriggerContext::archimonde_pulling_boss;
creators["archimonde boss engaged by main tank"] =
&RaidHyjalSummitTriggerContext::archimonde_boss_engaged_by_main_tank;
creators["archimonde boss casts fear"] =
&RaidHyjalSummitTriggerContext::archimonde_boss_casts_fear;
creators["archimonde boss casts air burst"] =
&RaidHyjalSummitTriggerContext::archimonde_boss_casts_air_burst;
creators["archimonde boss summoned doomfire"] =
&RaidHyjalSummitTriggerContext::archimonde_boss_summoned_doomfire;
creators["archimonde bot stood in doomfire"] =
&RaidHyjalSummitTriggerContext::archimonde_bot_stood_in_doomfire;
}
private:
// General
static Trigger* hyjal_summit_bot_is_not_in_combat(
PlayerbotAI* botAI) { return new HyjalSummitBotIsNotInCombatTrigger(botAI); }
// Rage Winterchill
static Trigger* rage_winterchill_pulling_boss(
PlayerbotAI* botAI) { return new RageWinterchillPullingBossTrigger(botAI); }
static Trigger* rage_winterchill_boss_engaged_by_main_tank(
PlayerbotAI* botAI) { return new RageWinterchillBossEngagedByMainTankTrigger(botAI); }
static Trigger* rage_winterchill_boss_casts_death_and_decay_on_ranged(
PlayerbotAI* botAI) { return new RageWinterchillBossCastsDeathAndDecayOnRangedTrigger(botAI); }
static Trigger* rage_winterchill_melee_is_standing_in_death_and_decay(
PlayerbotAI* botAI) { return new RageWinterchillMeleeIsStandingInDeathAndDecayTrigger(botAI); }
// Anetheron
static Trigger* anetheron_pulling_boss_or_infernal(
PlayerbotAI* botAI) { return new AnetheronPullingBossOrInfernalTrigger(botAI); }
static Trigger* anetheron_boss_engaged_by_main_tank(
PlayerbotAI* botAI) { return new AnetheronBossEngagedByMainTankTrigger(botAI); }
static Trigger* anetheron_boss_casts_carrion_swarm(
PlayerbotAI* botAI) { return new AnetheronBossCastsCarrionSwarmTrigger(botAI); }
static Trigger* anetheron_bot_is_targeted_by_infernal(
PlayerbotAI* botAI) { return new AnetheronBotIsTargetedByInfernalTrigger(botAI); }
static Trigger* anetheron_infernals_need_to_be_kept_away_from_raid(
PlayerbotAI* botAI) { return new AnetheronInfernalsNeedToBeKeptAwayFromRaidTrigger(botAI); }
static Trigger* anetheron_infernals_continue_to_spawn(
PlayerbotAI* botAI) { return new AnetheronInfernalsContinueToSpawnTrigger(botAI); }
// Kaz'rogal
static Trigger* kazrogal_pulling_boss(
PlayerbotAI* botAI) { return new KazrogalPullingBossTrigger(botAI); }
static Trigger* kazrogal_boss_engaged_by_main_tank(
PlayerbotAI* botAI) { return new KazrogalBossEngagedByMainTankTrigger(botAI); }
static Trigger* kazrogal_boss_engaged_by_assist_tanks(
PlayerbotAI* botAI) { return new KazrogalBossEngagedByAssistTanksTrigger(botAI); }
static Trigger* kazrogal_low_mana_bots_need_escape_path(
PlayerbotAI* botAI) { return new KazrogalLowManaBotsNeedEscapePathTrigger(botAI); }
static Trigger* kazrogal_bot_is_low_on_mana(
PlayerbotAI* botAI) { return new KazrogalBotIsLowOnManaTrigger(botAI); }
static Trigger* kazrogal_mark_deals_shadow_damage(
PlayerbotAI* botAI) { return new KazrogalMarkDealsShadowDamageTrigger(botAI); }
// Azgalor
static Trigger* azgalor_pulling_boss(
PlayerbotAI* botAI) { return new AzgalorPullingBossTrigger(botAI); }
static Trigger* azgalor_boss_engaged_by_main_tank(
PlayerbotAI* botAI) { return new AzgalorBossEngagedByMainTankTrigger(botAI); }
static Trigger* azgalor_main_tank_is_positioning_boss(
PlayerbotAI* botAI) { return new AzgalorMainTankIsPositioningBossTrigger(botAI); }
static Trigger* azgalor_boss_engaged_by_ranged(
PlayerbotAI* botAI) { return new AzgalorBossEngagedByRangedTrigger(botAI); }
static Trigger* azgalor_boss_casts_rain_of_fire_on_melee(
PlayerbotAI* botAI) { return new AzgalorBossCastsRainOfFireOnMeleeTrigger(botAI); }
static Trigger* azgalor_bot_is_doomed(
PlayerbotAI* botAI) { return new AzgalorBotIsDoomedTrigger(botAI); }
static Trigger* azgalor_doomguards_must_be_controlled(
PlayerbotAI* botAI) { return new AzgalorDoomguardsMustBeControlledTrigger(botAI); }
static Trigger* azgalor_doomguards_must_die(
PlayerbotAI* botAI) { return new AzgalorDoomguardsMustDieTrigger(botAI); }
// Archimonde
static Trigger* archimonde_pulling_boss(
PlayerbotAI* botAI) { return new ArchimondePullingBossTrigger(botAI); }
static Trigger* archimonde_boss_engaged_by_main_tank(
PlayerbotAI* botAI) { return new ArchimondeBossEngagedByMainTankTrigger(botAI); }
static Trigger* archimonde_boss_casts_fear(
PlayerbotAI* botAI) { return new ArchimondeBossCastsFearTrigger(botAI); }
static Trigger* archimonde_boss_casts_air_burst(
PlayerbotAI* botAI) { return new ArchimondeBossCastsAirBurstTrigger(botAI); }
static Trigger* archimonde_boss_summoned_doomfire(
PlayerbotAI* botAI) { return new ArchimondeBossSummonedDoomfireTrigger(botAI); }
static Trigger* archimonde_bot_stood_in_doomfire(
PlayerbotAI* botAI) { return new ArchimondeBotStoodInDoomfireTrigger(botAI); }
};
#endif

View File

@ -0,0 +1,137 @@
/*
* 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 "RaidHyjalSummitStrategy.h"
#include "RaidHyjalSummitMultipliers.h"
void RaidHyjalSummitStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
// General
triggers.push_back(new TriggerNode("hyjal summit bot is not in combat", {
NextAction("hyjal summit erase trackers", ACTION_EMERGENCY + 11) }));
// Rage Winterchill
triggers.push_back(new TriggerNode("rage winterchill pulling boss", {
NextAction("rage winterchill misdirect boss to main tank", ACTION_RAID + 2) }));
triggers.push_back(new TriggerNode("rage winterchill boss engaged by main tank", {
NextAction("rage winterchill main tank position boss", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("rage winterchill boss casts death and decay on ranged", {
NextAction("rage winterchill spread ranged in circle", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("rage winterchill melee is standing in death and decay", {
NextAction("rage winterchill melee get out of death and decay", ACTION_EMERGENCY + 1) }));
// Anetheron
triggers.push_back(new TriggerNode("anetheron pulling boss or infernal", {
NextAction("anetheron misdirect boss and infernals to tanks", ACTION_RAID + 3) }));
triggers.push_back(new TriggerNode("anetheron boss engaged by main tank", {
NextAction("anetheron main tank position boss", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("anetheron boss casts carrion swarm", {
NextAction("anetheron spread ranged in circle", ACTION_RAID + 2) }));
triggers.push_back(new TriggerNode("anetheron bot is targeted by infernal", {
NextAction("anetheron bring infernal to infernal tank", ACTION_EMERGENCY + 2) }));
triggers.push_back(new TriggerNode("anetheron infernals need to be kept away from raid", {
NextAction("anetheron first assist tank pick up infernals", ACTION_EMERGENCY + 1) }));
triggers.push_back(new TriggerNode("anetheron infernals continue to spawn", {
NextAction("anetheron assign dps priority", ACTION_RAID + 1) }));
// Kaz'rogal
triggers.push_back(new TriggerNode("kaz'rogal pulling boss", {
NextAction("kaz'rogal misdirect boss to main tank", ACTION_RAID + 2) }));
triggers.push_back(new TriggerNode("kaz'rogal boss engaged by main tank", {
NextAction("kaz'rogal main tank position boss", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("kaz'rogal boss engaged by assist tanks", {
NextAction("kaz'rogal assist tanks move in front of boss", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("kaz'rogal low mana bots need escape path", {
NextAction("kaz'rogal spread ranged in arc", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("kaz'rogal bot is low on mana", {
NextAction("kaz'rogal low mana bot take defensive measures", ACTION_EMERGENCY + 1) }));
triggers.push_back(new TriggerNode("kaz'rogal mark deals shadow damage", {
NextAction("kaz'rogal cast shadow protection spell", ACTION_EMERGENCY + 6) }));
// Azgalor
triggers.push_back(new TriggerNode("azgalor pulling boss", {
NextAction("azgalor misdirect boss to main tank", ACTION_RAID + 3) }));
triggers.push_back(new TriggerNode("azgalor boss engaged by main tank", {
NextAction("azgalor main tank position boss", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("azgalor main tank is positioning boss", {
NextAction("azgalor wait at safe position", ACTION_EMERGENCY + 1) }));
triggers.push_back(new TriggerNode("azgalor boss engaged by ranged", {
NextAction("azgalor disperse ranged", ACTION_RAID + 2) }));
triggers.push_back(new TriggerNode("azgalor boss casts rain of fire on melee", {
NextAction("azgalor melee get out of fire and swap targets", ACTION_EMERGENCY + 2) }));
triggers.push_back(new TriggerNode("azgalor bot is doomed", {
NextAction("azgalor move to doomguard tank", ACTION_EMERGENCY + 3) }));
triggers.push_back(new TriggerNode("azgalor doomguards must be controlled", {
NextAction("azgalor first assist tank position doomguard", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("azgalor doomguards must die", {
NextAction("azgalor ranged dps prioritize doomguards", ACTION_RAID + 1) }));
// Archimonde
triggers.push_back(new TriggerNode("archimonde pulling boss", {
NextAction("archimonde misdirect boss to main tank", ACTION_RAID + 2) }));
triggers.push_back(new TriggerNode("archimonde boss engaged by main tank", {
NextAction("archimonde move boss to initial position", ACTION_RAID + 2) }));
triggers.push_back(new TriggerNode("archimonde boss casts fear", {
NextAction("archimonde cast fear immunity spell", ACTION_RAID + 2) }));
triggers.push_back(new TriggerNode("archimonde boss casts air burst", {
NextAction("archimonde spread to avoid air burst", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("archimonde boss summoned doomfire", {
NextAction("archimonde avoid doomfire", ACTION_EMERGENCY + 6) }));
triggers.push_back(new TriggerNode("archimonde bot stood in doomfire", {
NextAction("archimonde remove doomfire dot", ACTION_EMERGENCY + 7) }));
}
void RaidHyjalSummitStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
{
// Trash
multipliers.push_back(new HyjalSummitTimeBloodlustAndHeroismMultiplier(botAI));
// Rage Winterchill
multipliers.push_back(new RageWinterchillDisableCombatFormationMoveMultiplier(botAI));
multipliers.push_back(new RageWinterchillMeleeControlAvoidanceMultiplier(botAI));
// Anetheron
multipliers.push_back(new AnetheronDisableTankActionsMultiplier(botAI));
multipliers.push_back(new AnetheronDisableCombatFormationMoveMultiplier(botAI));
multipliers.push_back(new AnetheronControlMisdirectionMultiplier(botAI));
// Kaz'rogal
multipliers.push_back(new KazrogalLowManaBotStayAwayFromGroupMultiplier(botAI));
multipliers.push_back(new KazrogalKeepAspectOfTheViperActiveMultiplier(botAI));
multipliers.push_back(new KazrogalControlMovementMultiplier(botAI));
// Azgalor
multipliers.push_back(new AzgalorDisableTankActionsMultiplier(botAI));
multipliers.push_back(new AzgalorDoomedBotPrioritizePositioningMultiplier(botAI));
multipliers.push_back(new AzgalorMeleeDpsControlAvoidanceMultiplier(botAI));
// Archimonde
multipliers.push_back(new ArchimondeDisableCombatFormationMoveMultiplier(botAI));
}

View File

@ -0,0 +1,22 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_RAIDHYJALSUMMITSTRATEGY_H_
#define _PLAYERBOT_RAIDHYJALSUMMITSTRATEGY_H_
#include "Strategy.h"
class RaidHyjalSummitStrategy : public Strategy
{
public:
RaidHyjalSummitStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
std::string const getName() override { return "hyjal"; }
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
};
#endif

View File

@ -0,0 +1,357 @@
/*
* 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 "RaidHyjalSummitTriggers.h"
#include "RaidHyjalSummitHelpers.h"
#include "RaidHyjalSummitActions.h"
#include "AiFactory.h"
#include "Playerbots.h"
#include "RaidBossHelpers.h"
using namespace HyjalSummitHelpers;
// General
bool HyjalSummitBotIsNotInCombatTrigger::IsActive()
{
return !bot->IsInCombat() && bot->GetMapId() == HYJAL_SUMMIT_MAP_ID;
}
// Rage Winterchill
bool RageWinterchillPullingBossTrigger::IsActive()
{
if (bot->getClass() != CLASS_HUNTER)
return false;
Unit* winterchill = AI_VALUE2(Unit*, "find target", "rage winterchill");
return winterchill && winterchill->GetHealthPct() > 95.0f;
}
bool RageWinterchillBossEngagedByMainTankTrigger::IsActive()
{
return botAI->IsMainTank(bot) &&
AI_VALUE2(Unit*, "find target", "rage winterchill");
}
bool RageWinterchillBossCastsDeathAndDecayOnRangedTrigger::IsActive()
{
return botAI->IsRanged(bot) &&
AI_VALUE2(Unit*, "find target", "rage winterchill");
}
bool RageWinterchillMeleeIsStandingInDeathAndDecayTrigger::IsActive()
{
if (botAI->IsRanged(bot))
return false;
Unit* winterchill = AI_VALUE2(Unit*, "find target", "rage winterchill");
if (!winterchill || winterchill->GetVictim() == bot)
return false;
if (botAI->IsMainTank(bot))
return false;
return IsInDeathAndDecay(bot, DEATH_AND_DECAY_SAFE_RADIUS);
}
// Anetheron
bool AnetheronPullingBossOrInfernalTrigger::IsActive()
{
return bot->getClass() == CLASS_HUNTER &&
AI_VALUE2(Unit*, "find target", "anetheron");
}
bool AnetheronBossEngagedByMainTankTrigger::IsActive()
{
return botAI->IsMainTank(bot) && AI_VALUE2(Unit*, "find target", "anetheron");
}
bool AnetheronBossCastsCarrionSwarmTrigger::IsActive()
{
if (botAI->IsMelee(bot))
return false;
Unit* anetheron = AI_VALUE2(Unit*, "find target", "anetheron");
if (!anetheron)
return false;
return GetInfernoTarget(anetheron) != bot;
}
bool AnetheronBotIsTargetedByInfernalTrigger::IsActive()
{
Unit* anetheron = AI_VALUE2(Unit*, "find target", "anetheron");
if (!anetheron || botAI->IsMainTank(bot))
return false;
return GetInfernoTarget(anetheron) == bot;
}
bool AnetheronInfernalsNeedToBeKeptAwayFromRaidTrigger::IsActive()
{
return botAI->IsAssistTankOfIndex(bot, 0, true) &&
AI_VALUE2(Unit*, "find target", "towering infernal");
}
bool AnetheronInfernalsContinueToSpawnTrigger::IsActive()
{
return !botAI->IsTank(bot) && AI_VALUE2(Unit*, "find target", "anetheron");
}
// Kaz'rogal
bool KazrogalPullingBossTrigger::IsActive()
{
if (bot->getClass() != CLASS_HUNTER)
return false;
Unit* kazrogal = AI_VALUE2(Unit*, "find target", "kaz'rogal");
return kazrogal && kazrogal->GetHealthPct() > 95.0f;
}
bool KazrogalBossEngagedByMainTankTrigger::IsActive()
{
return botAI->IsMainTank(bot) && AI_VALUE2(Unit*, "find target", "kaz'rogal");
}
bool KazrogalBossEngagedByAssistTanksTrigger::IsActive()
{
if (!botAI->IsAssistTank(bot))
return false;
if (!AI_VALUE2(Unit*, "find target", "kaz'rogal"))
return false;
return bot->GetPower(POWER_MANA) > 3000;
}
bool KazrogalLowManaBotsNeedEscapePathTrigger::IsActive()
{
if (bot->getClass() == CLASS_WARRIOR || bot->getClass() == CLASS_ROGUE ||
bot->getClass() == CLASS_DEATH_KNIGHT)
return false;
uint8 tab = AiFactory::GetPlayerSpecTab(bot);
if (bot->getClass() == CLASS_DRUID && tab == DRUID_TAB_FERAL)
return false;
if (!AI_VALUE2(Unit*, "find target", "kaz'rogal"))
return false;
if (bot->getClass() == CLASS_HUNTER)
{
return true;
}
else if (bot->GetPower(POWER_MANA) > 4000)
{
isBelowManaThreshold.erase(bot->GetGUID());
if (botAI->IsMelee(bot))
return false;
else
return true;
}
return false;
}
bool KazrogalBotIsLowOnManaTrigger::IsActive()
{
if (bot->getClass() == CLASS_WARRIOR || bot->getClass() == CLASS_ROGUE ||
bot->getClass() == CLASS_DEATH_KNIGHT)
return false;
uint8 tab = AiFactory::GetPlayerSpecTab(bot);
if (bot->getClass() == CLASS_DRUID && tab == DRUID_TAB_FERAL)
return false;
if (!AI_VALUE2(Unit*, "find target", "kaz'rogal"))
return false;
if (botAI->HasAnyAuraOf(bot, "ice block", "divine shield", nullptr))
return false;
if (isBelowManaThreshold.count(bot->GetGUID()) ||
bot->GetPower(POWER_MANA) <= 3200)
return true;
return false;
}
bool KazrogalMarkDealsShadowDamageTrigger::IsActive()
{
if (bot->getClass() != CLASS_PALADIN && bot->getClass() != CLASS_WARLOCK)
return false;
if (!AI_VALUE2(Unit*, "find target", "kaz'rogal"))
return false;
if (bot->getClass() == CLASS_PALADIN &&
(botAI->HasAura("shadow resistance aura", bot) ||
botAI->HasAura("prayer of shadow protection", bot) ||
botAI->HasAura("shadow protection", bot)))
return false;
return bot->HasAura(
static_cast<uint32>(HyjalSummitSpells::SPELL_MARK_OF_KAZROGAL));
}
// Azgalor
bool AzgalorPullingBossTrigger::IsActive()
{
if (bot->getClass() != CLASS_HUNTER)
return false;
Unit* azgalor = AI_VALUE2(Unit*, "find target", "azgalor");
return azgalor && azgalor->GetHealthPct() > 95.0f;
}
bool AzgalorBossEngagedByMainTankTrigger::IsActive()
{
return botAI->IsMainTank(bot) && AI_VALUE2(Unit*, "find target", "azgalor");
}
bool AzgalorMainTankIsPositioningBossTrigger::IsActive()
{
if (botAI->IsRanged(bot))
return false;
Unit* azgalor = AI_VALUE2(Unit*, "find target", "azgalor");
if (!azgalor || azgalor->GetVictim() == bot)
return false;
Player* mainTank = GetGroupMainTank(botAI, bot);
if (!mainTank || !GET_PLAYERBOT_AI(mainTank) || botAI->IsMainTank(bot))
return false;
TankPositionState tankState = GetAzgalorTankPositionState(botAI, bot);
return tankState == TankPositionState::Unknown ||
tankState == TankPositionState::MovingToTransition;
}
bool AzgalorBossEngagedByRangedTrigger::IsActive()
{
if (botAI->IsMelee(bot))
return false;
Unit* azgalor = AI_VALUE2(Unit*, "find target", "azgalor");
return azgalor && azgalor->GetVictim() != bot &&
!bot->HasAura(static_cast<uint32>(HyjalSummitSpells::SPELL_DOOM));
}
bool AzgalorBossCastsRainOfFireOnMeleeTrigger::IsActive()
{
if (botAI->IsRanged(bot) || botAI->IsTank(bot))
return false;
Unit* azgalor = AI_VALUE2(Unit*, "find target", "azgalor");
if (!azgalor || azgalor->GetVictim() == bot ||
bot->HasAura(static_cast<uint32>(HyjalSummitSpells::SPELL_DOOM)))
return false;
return IsInRainOfFire(bot, RAIN_OF_FIRE_RADIUS);
}
bool AzgalorBotIsDoomedTrigger::IsActive()
{
return bot->HasAura(static_cast<uint32>(HyjalSummitSpells::SPELL_DOOM));
}
bool AzgalorDoomguardsMustBeControlledTrigger::IsActive()
{
if (!botAI->IsAssistTank(bot) ||
!AI_VALUE2(Unit*, "find target", "azgalor"))
return false;
if (botAI->IsAssistTankOfIndex(bot, 0, true))
{
return AI_VALUE2(Unit*, "find target", "lesser doomguard") ||
AnyGroupMemberHasDoom(bot);
}
if (botAI->IsAssistTankOfIndex(bot, 1, true))
{
// Trigger for second assist tank only if first assist tank has Doom
Player* firstAssistTank = GetGroupAssistTank(botAI, bot, 0);
if (firstAssistTank &&
!firstAssistTank->HasAura(static_cast<uint32>(HyjalSummitSpells::SPELL_DOOM)))
return false;
return AI_VALUE2(Unit*, "find target", "lesser doomguard") ||
AnyGroupMemberHasDoom(bot);
}
return false;
}
bool AzgalorDoomguardsMustDieTrigger::IsActive()
{
return botAI->IsRangedDps(bot) && AI_VALUE2(Unit*, "find target", "azgalor");
}
// Archimonde
bool ArchimondePullingBossTrigger::IsActive()
{
if (bot->getClass() != CLASS_HUNTER)
return false;
Unit* archimonde = AI_VALUE2(Unit*, "find target", "archimonde");
return archimonde && archimonde->GetHealthPct() > 95.0f;
}
bool ArchimondeBossEngagedByMainTankTrigger::IsActive()
{
if (!botAI->IsMainTank(bot))
return false;
Unit* archimonde = AI_VALUE2(Unit*, "find target", "archimonde");
return archimonde && archimonde->GetHealthPct() > 95.0f;
}
bool ArchimondeBossCastsFearTrigger::IsActive()
{
if (bot->getClass() != CLASS_PRIEST &&
bot->getClass() != CLASS_SHAMAN)
return false;
Unit* archimonde = AI_VALUE2(Unit*, "find target", "archimonde");
return archimonde && archimonde->GetHealthPct() > 10.0f;
}
bool ArchimondeBossCastsAirBurstTrigger::IsActive()
{
Unit* archimonde = AI_VALUE2(Unit*, "find target", "archimonde");
if (!archimonde || archimonde->GetHealthPct() <= 10.0f ||
archimonde->GetVictim() == bot)
return false;
return !botAI->IsMainTank(bot);
}
bool ArchimondeBossSummonedDoomfireTrigger::IsActive()
{
Unit* archimonde = AI_VALUE2(Unit*, "find target", "archimonde");
if (!archimonde || archimonde->GetHealthPct() <= 10.0f)
return false;
// If I don't make an exception, bots actually refuse to enter the
// Doomfire even when feared
return !bot->HasAura(
static_cast<uint32>(HyjalSummitSpells::SPELL_ARCHIMONDE_FEAR));
}
bool ArchimondeBotStoodInDoomfireTrigger::IsActive()
{
if (bot->getClass() != CLASS_MAGE && bot->getClass() != CLASS_ROGUE &&
bot->getClass() != CLASS_PALADIN)
return false;
return bot->GetHealthPct() < 40.0f &&
(bot->HasAura(static_cast<uint32>(HyjalSummitSpells::SPELL_DOOMFIRE)) ||
bot->HasAura(static_cast<uint32>(HyjalSummitSpells::SPELL_DOOMFIRE_DOT)));
}

View File

@ -0,0 +1,271 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_RAIDHYJALSUMMITTRIGGERS_H
#define _PLAYERBOT_RAIDHYJALSUMMITTRIGGERS_H
#include "Trigger.h"
// General
class HyjalSummitBotIsNotInCombatTrigger : public Trigger
{
public:
HyjalSummitBotIsNotInCombatTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "hyjal summit bot is not in combat") {}
bool IsActive() override;
};
// Rage Winterchill
class RageWinterchillPullingBossTrigger : public Trigger
{
public:
RageWinterchillPullingBossTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "rage winterchill pulling boss") {}
bool IsActive() override;
};
class RageWinterchillBossEngagedByMainTankTrigger : public Trigger
{
public:
RageWinterchillBossEngagedByMainTankTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "rage winterchill boss engaged by main tank") {}
bool IsActive() override;
};
class RageWinterchillBossCastsDeathAndDecayOnRangedTrigger : public Trigger
{
public:
RageWinterchillBossCastsDeathAndDecayOnRangedTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "rage winterchill boss casts death and decay on ranged") {}
bool IsActive() override;
};
class RageWinterchillMeleeIsStandingInDeathAndDecayTrigger : public Trigger
{
public:
RageWinterchillMeleeIsStandingInDeathAndDecayTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "rage winterchill melee is standing in death and decay") {}
bool IsActive() override;
};
// Anetheron
class AnetheronPullingBossOrInfernalTrigger : public Trigger
{
public:
AnetheronPullingBossOrInfernalTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "anetheron pulling boss or infernal") {}
bool IsActive() override;
};
class AnetheronBossEngagedByMainTankTrigger : public Trigger
{
public:
AnetheronBossEngagedByMainTankTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "anetheron boss engaged by main tank") {}
bool IsActive() override;
};
class AnetheronBossCastsCarrionSwarmTrigger : public Trigger
{
public:
AnetheronBossCastsCarrionSwarmTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "anetheron boss casts carrion swarm") {}
bool IsActive() override;
};
class AnetheronBotIsTargetedByInfernalTrigger : public Trigger
{
public:
AnetheronBotIsTargetedByInfernalTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "anetheron bot is targeted by infernal") {}
bool IsActive() override;
};
class AnetheronInfernalsNeedToBeKeptAwayFromRaidTrigger : public Trigger
{
public:
AnetheronInfernalsNeedToBeKeptAwayFromRaidTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "anetheron infernals need to be kept away from raid") {}
bool IsActive() override;
};
class AnetheronInfernalsContinueToSpawnTrigger : public Trigger
{
public:
AnetheronInfernalsContinueToSpawnTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "anetheron infernals continue to spawn") {}
bool IsActive() override;
};
// Kaz'rogal
class KazrogalPullingBossTrigger : public Trigger
{
public:
KazrogalPullingBossTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "kaz'rogal pulling boss") {}
bool IsActive() override;
};
class KazrogalBossEngagedByMainTankTrigger : public Trigger
{
public:
KazrogalBossEngagedByMainTankTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "kaz'rogal boss engaged by main tank") {}
bool IsActive() override;
};
class KazrogalBossEngagedByAssistTanksTrigger : public Trigger
{
public:
KazrogalBossEngagedByAssistTanksTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "kaz'rogal boss engaged by assist tanks") {}
bool IsActive() override;
};
class KazrogalLowManaBotsNeedEscapePathTrigger : public Trigger
{
public:
KazrogalLowManaBotsNeedEscapePathTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "kaz'rogal low mana bots need escape path") {}
bool IsActive() override;
};
class KazrogalBotIsLowOnManaTrigger : public Trigger
{
public:
KazrogalBotIsLowOnManaTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "kaz'rogal bot is low on mana") {}
bool IsActive() override;
};
class KazrogalMarkDealsShadowDamageTrigger : public Trigger
{
public:
KazrogalMarkDealsShadowDamageTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "kaz'rogal mark deals shadow damage") {}
bool IsActive() override;
};
// Azgalor
class AzgalorPullingBossTrigger : public Trigger
{
public:
AzgalorPullingBossTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "azgalor pulling boss") {}
bool IsActive() override;
};
class AzgalorBossEngagedByMainTankTrigger : public Trigger
{
public:
AzgalorBossEngagedByMainTankTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "azgalor boss engaged by main tank") {}
bool IsActive() override;
};
class AzgalorMainTankIsPositioningBossTrigger : public Trigger
{
public:
AzgalorMainTankIsPositioningBossTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "azgalor main tank is positioning boss") {}
bool IsActive() override;
};
class AzgalorBossEngagedByRangedTrigger : public Trigger
{
public:
AzgalorBossEngagedByRangedTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "azgalor boss engaged by ranged") {}
bool IsActive() override;
};
class AzgalorBossCastsRainOfFireOnMeleeTrigger : public Trigger
{
public:
AzgalorBossCastsRainOfFireOnMeleeTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "azgalor boss casts rain of fire on melee") {}
bool IsActive() override;
};
class AzgalorBotIsDoomedTrigger : public Trigger
{
public:
AzgalorBotIsDoomedTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "azgalor bot is doomed") {}
bool IsActive() override;
};
class AzgalorDoomguardsMustBeControlledTrigger : public Trigger
{
public:
AzgalorDoomguardsMustBeControlledTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "azgalor doomguards must be controlled") {}
bool IsActive() override;
};
class AzgalorDoomguardsMustDieTrigger : public Trigger
{
public:
AzgalorDoomguardsMustDieTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "azgalor doomguards must die") {}
bool IsActive() override;
};
// Archimonde
class ArchimondePullingBossTrigger : public Trigger
{
public:
ArchimondePullingBossTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "archimonde pulling boss") {}
bool IsActive() override;
};
class ArchimondeBossEngagedByMainTankTrigger : public Trigger
{
public:
ArchimondeBossEngagedByMainTankTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "archimonde boss engaged by main tank") {}
bool IsActive() override;
};
class ArchimondeBossCastsFearTrigger : public Trigger
{
public:
ArchimondeBossCastsFearTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "archimonde boss casts fear") {}
bool IsActive() override;
};
class ArchimondeBossCastsAirBurstTrigger : public Trigger
{
public:
ArchimondeBossCastsAirBurstTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "archimonde boss casts air burst") {}
bool IsActive() override;
};
class ArchimondeBossSummonedDoomfireTrigger : public Trigger
{
public:
ArchimondeBossSummonedDoomfireTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "archimonde boss summoned doomfire") {}
bool IsActive() override;
};
class ArchimondeBotStoodInDoomfireTrigger : public Trigger
{
public:
ArchimondeBotStoodInDoomfireTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "archimonde bot stood in doomfire") {}
bool IsActive() override;
};
#endif

View File

@ -0,0 +1,268 @@
/*
* 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 "RaidHyjalSummitHelpers.h"
#include <algorithm>
#include "Playerbots.h"
#include "RaidBossHelpers.h"
#include "Timer.h"
namespace HyjalSummitHelpers
{
// General
bool GetGroundedStepPosition(
Player* bot, float destinationX, float destinationY, float moveDist,
float& stepX, float& stepY, float& stepZ)
{
const float distance = bot->GetExactDist2d(destinationX, destinationY);
if (distance <= 0.0f)
return false;
const float stepDistance = std::min(moveDist, distance);
const float deltaX = destinationX - bot->GetPositionX();
const float deltaY = destinationY - bot->GetPositionY();
stepX = bot->GetPositionX() + (deltaX / distance) * stepDistance;
stepY = bot->GetPositionY() + (deltaY / distance) * stepDistance;
stepZ = bot->GetMapWaterOrGroundLevel(stepX, stepY, bot->GetPositionZ());
if (stepZ <= INVALID_HEIGHT)
stepZ = bot->GetPositionZ();
bot->GetMap()->CheckCollisionAndGetValidCoords(
bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(),
stepX, stepY, stepZ, false);
return true;
}
RangedGroups GetRangedGroups(PlayerbotAI* botAI, Player* bot)
{
RangedGroups result;
Group* group = bot->GetGroup();
if (!group)
return result;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!member || !botAI->IsRanged(member))
continue;
if (botAI->IsHeal(member))
result.healers.push_back(member);
else
result.rangedDps.push_back(member);
}
return result;
}
std::pair<size_t, size_t> GetBotCircleIndexAndCount(PlayerbotAI* botAI, Player* bot,
const RangedGroups& groups)
{
const std::vector<Player*>& vec = botAI->IsHeal(bot) ? groups.healers : groups.rangedDps;
auto it = std::find(vec.begin(), vec.end(), bot);
size_t index = (it != vec.end()) ? std::distance(vec.begin(), it) : 0;
return {index, vec.size()};
}
// Rage Winterchill
const Position WINTERCHILL_TANK_POSITION = { 5031.061f, -1784.521f, 1321.626f };
std::unordered_map<ObjectGuid, bool> hasReachedWinterchillPosition;
std::unordered_map<uint32, DeathAndDecayData> deathAndDecayPosition;
DeathAndDecayData* GetActiveWinterchillDeathAndDecay(uint32 instanceId)
{
auto instanceIt = deathAndDecayPosition.find(instanceId);
if (instanceIt == deathAndDecayPosition.end())
return nullptr;
const uint32 now = getMSTime();
const uint32 elapsed = getMSTimeDiff(instanceIt->second.spawnTime, now);
if (elapsed >= DEATH_AND_DECAY_REACQUIRE_DELAY)
{
deathAndDecayPosition.erase(instanceIt);
return nullptr;
}
if (elapsed >= DEATH_AND_DECAY_DURATION)
return nullptr;
return &instanceIt->second;
}
bool IsInDeathAndDecay(Player* bot, float radius)
{
const uint32 instanceId = bot->GetMap()->GetInstanceId();
Aura* aura = bot->GetAura(static_cast<uint32>(HyjalSummitSpells::SPELL_DEATH_AND_DECAY));
if (aura)
{
DynamicObject* dynObj = aura->GetDynobjOwner();
if (dynObj && dynObj->IsInWorld())
{
const uint32 now = getMSTime();
auto instanceIt = deathAndDecayPosition.find(instanceId);
if (instanceIt == deathAndDecayPosition.end() ||
getMSTimeDiff(instanceIt->second.spawnTime, now) >= DEATH_AND_DECAY_REACQUIRE_DELAY)
{
deathAndDecayPosition[instanceId] =
DeathAndDecayData{ dynObj->GetPosition(), now };
}
}
}
DeathAndDecayData* data = GetActiveWinterchillDeathAndDecay(instanceId);
if (!data)
return false;
return bot->GetExactDist2d(data->position) < radius;
}
// Anetheron
const Position ANETHERON_TANK_POSITION = { 5033.177f, -1765.996f, 1324.195f };
const Position ANETHERON_E_INFERNAL_POSITION = { 5016.578f, -1800.233f, 1323.070f };
const Position ANETHERON_W_INFERNAL_POSITION = { 5048.911f, -1722.164f, 1321.408f };
std::unordered_map<ObjectGuid, bool> hasReachedAnetheronPosition;
Player* GetInfernoTarget(Unit* anetheron)
{
if (!anetheron)
return nullptr;
Spell* spell = anetheron->GetCurrentSpell(CURRENT_GENERIC_SPELL);
if (spell && spell->m_spellInfo->Id ==
static_cast<uint32>(HyjalSummitSpells::SPELL_INFERNO))
{
Unit* spellTarget = spell->m_targets.GetUnitTarget();
if (spellTarget && spellTarget->IsPlayer())
return spellTarget->ToPlayer();
}
return nullptr;
}
const Position& GetClosestInfernalTankPosition(Player* bot)
{
const Position& east = ANETHERON_E_INFERNAL_POSITION;
const Position& west = ANETHERON_W_INFERNAL_POSITION;
return (bot->GetExactDist2d(east.GetPositionX(), east.GetPositionY()) <=
bot->GetExactDist2d(west.GetPositionX(), west.GetPositionY())) ? east : west;
}
// Kaz'rogal
const Position KAZROGAL_TANK_TRANSITION_POSITION = { 5528.792f, -2636.486f, 1481.293f };
const Position KAZROGAL_TANK_FINAL_POSITION = { 5511.514f, -2662.466f, 1480.288f };
std::unordered_map<ObjectGuid, TankPositionState> kazrogalTankStep;
std::unordered_map<ObjectGuid, bool> isBelowManaThreshold;
TankPositionState GetKazrogalTankPositionState(PlayerbotAI* botAI, Player* bot)
{
Player* mainTank = GetGroupMainTank(botAI, bot);
if (!mainTank)
return TankPositionState::Unknown;
auto it = kazrogalTankStep.find(mainTank->GetGUID());
if (it != kazrogalTankStep.end())
return it->second;
return TankPositionState::Unknown;
}
// Azgalor
const Position AZGALOR_TANK_TRANSITION_POSITION = { 5486.787f, -2696.215f, 1482.007f };
const Position AZGALOR_TANK_FINAL_POSITION = { 5496.379f, -2675.265f, 1481.053f };
const Position AZGALOR_DOOMGUARD_POSITION = { 5485.555f, -2731.659f, 1485.555f };
std::unordered_map<ObjectGuid, TankPositionState> azgalorTankStep;
std::unordered_map<uint32, RainOfFireData> rainOfFirePosition;
RainOfFireData* GetActiveAzgalorRainOfFire(uint32 instanceId)
{
auto instanceIt = rainOfFirePosition.find(instanceId);
if (instanceIt == rainOfFirePosition.end())
return nullptr;
const uint32 now = getMSTime();
const uint32 elapsed = getMSTimeDiff(instanceIt->second.spawnTime, now);
if (elapsed >= RAIN_OF_FIRE_REACQUIRE_DELAY)
{
rainOfFirePosition.erase(instanceIt);
return nullptr;
}
if (elapsed >= RAIN_OF_FIRE_DURATION)
return nullptr;
return &instanceIt->second;
}
TankPositionState GetAzgalorTankPositionState(PlayerbotAI* botAI, Player* bot)
{
Player* mainTank = GetGroupMainTank(botAI, bot);
if (!mainTank)
return TankPositionState::Unknown;
auto it = azgalorTankStep.find(mainTank->GetGUID());
if (it != azgalorTankStep.end())
return it->second;
return TankPositionState::Unknown;
}
bool IsInRainOfFire(Player* bot, float radius)
{
RainOfFireData* data = GetActiveAzgalorRainOfFire(bot->GetMap()->GetInstanceId());
if (!data)
return false;
return bot->GetExactDist2d(data->position) < radius;
}
bool AnyGroupMemberHasDoom(Player* bot)
{
if (Group* group = bot->GetGroup())
{
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (member &&
member->HasAura(static_cast<uint32>(HyjalSummitSpells::SPELL_DOOM)))
return true;
}
}
return false;
}
// Archimonde
const Position ARCHIMONDE_INITIAL_POSITION = { 5640.502f, -3421.238f, 1587.453f };
std::unordered_map<uint32, AirBurstData> archimondeAirBurstTargets;
std::unordered_map<uint32, std::vector<DoomfireTrailData>> doomfireTrails;
std::unordered_map<ObjectGuid, uint32> doomfireLastSampleTime;
AirBurstData* GetRecentArchimondeAirBurst(uint32 instanceId)
{
auto instanceIt = archimondeAirBurstTargets.find(instanceId);
if (instanceIt == archimondeAirBurstTargets.end())
return nullptr;
constexpr uint32 airBurstReactionWindow = 2000;
const uint32 now = getMSTime();
if (getMSTimeDiff(instanceIt->second.castTime, now) >= airBurstReactionWindow)
{
archimondeAirBurstTargets.erase(instanceIt);
return nullptr;
}
return &instanceIt->second;
}
}

View File

@ -0,0 +1,143 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_RAIDHYJALSUMMITHELPERS_H_
#define _PLAYERBOT_RAIDHYJALSUMMITHELPERS_H_
#include <unordered_map>
#include <utility>
#include <vector>
#include "AiObject.h"
#include "Position.h"
#include "Unit.h"
namespace HyjalSummitHelpers
{
enum class HyjalSummitSpells : uint32
{
// Rage Winterchill
SPELL_DEATH_AND_DECAY = 31258,
// Anetheron
SPELL_INFERNO = 31299,
// Kaz'rogal
SPELL_MARK_OF_KAZROGAL = 31447,
// Azgalor
SPELL_RAIN_OF_FIRE = 31340,
SPELL_DOOM = 31347,
// Archimonde
SPELL_DOOMFIRE = 31944, // Damaging part of trail
SPELL_DOOMFIRE_DOT = 31969, // DoT after exiting trail
SPELL_ARCHIMONDE_FEAR = 31970,
SPELL_AIR_BURST = 32014,
// Hunter
SPELL_MISDIRECTION = 35079,
// Priest
SPELL_FEAR_WARD = 6346,
};
enum class HyjalSummitNpcs : uint32
{
// Archimonde
NPC_DOOMFIRE = 18095,
};
enum class TankPositionState : uint8
{
MovingToTransition = 0,
MovingToFinal = 1,
Positioned = 2,
Unknown = 255,
};
// General
constexpr uint32 HYJAL_SUMMIT_MAP_ID = 534;
struct RangedGroups
{
std::vector<Player*> healers;
std::vector<Player*> rangedDps;
};
bool GetGroundedStepPosition(
Player* bot, float destinationX, float destinationY, float moveDist,
float& stepX, float& stepY, float& stepZ);
RangedGroups GetRangedGroups(PlayerbotAI* botAI, Player* bot);
std::pair<size_t, size_t> GetBotCircleIndexAndCount(PlayerbotAI* botAI, Player* bot,
const RangedGroups& groups);
// Rage Winterchill
extern const Position WINTERCHILL_TANK_POSITION;
extern std::unordered_map<ObjectGuid, bool> hasReachedWinterchillPosition;
constexpr uint32 DEATH_AND_DECAY_DURATION = 15000;
constexpr uint32 DEATH_AND_DECAY_REACQUIRE_DELAY = 20000;
constexpr float DEATH_AND_DECAY_SAFE_RADIUS = 22.0f; // 20y radius + 1.5y player hitbox + 0.5y buffer
struct DeathAndDecayData
{
Position position;
uint32 spawnTime;
};
extern std::unordered_map<uint32, DeathAndDecayData> deathAndDecayPosition;
DeathAndDecayData* GetActiveWinterchillDeathAndDecay(uint32 instanceId);
bool IsInDeathAndDecay(Player* bot, float radius);
// Anetheron
extern const Position ANETHERON_TANK_POSITION;
extern const Position ANETHERON_E_INFERNAL_POSITION;
extern const Position ANETHERON_W_INFERNAL_POSITION;
extern std::unordered_map<ObjectGuid, bool> hasReachedAnetheronPosition;
Player* GetInfernoTarget(Unit* anetheron);
const Position& GetClosestInfernalTankPosition(Player* bot);
// Kaz'rogal
extern const Position KAZROGAL_TANK_TRANSITION_POSITION;
extern const Position KAZROGAL_TANK_FINAL_POSITION;
extern std::unordered_map<ObjectGuid, TankPositionState> kazrogalTankStep;
extern std::unordered_map<ObjectGuid, bool> isBelowManaThreshold;
TankPositionState GetKazrogalTankPositionState(PlayerbotAI* botAI, Player* bot);
// Azgalor
extern const Position AZGALOR_TANK_TRANSITION_POSITION;
extern const Position AZGALOR_TANK_FINAL_POSITION;
extern const Position AZGALOR_DOOMGUARD_POSITION;
extern std::unordered_map<ObjectGuid, TankPositionState> azgalorTankStep;
constexpr uint32 RAIN_OF_FIRE_DURATION = 10000;
constexpr uint32 RAIN_OF_FIRE_REACQUIRE_DELAY = 15000;
constexpr float RAIN_OF_FIRE_RADIUS = 17.0f; // 15y radius + 1.5y player hitbox + 0.5y buffer
struct RainOfFireData
{
Position position;
uint32 spawnTime;
};
extern std::unordered_map<uint32, RainOfFireData> rainOfFirePosition;
TankPositionState GetAzgalorTankPositionState(PlayerbotAI* botAI, Player* bot);
RainOfFireData* GetActiveAzgalorRainOfFire(uint32 instanceId);
bool IsInRainOfFire(Player* bot, float radius);
bool AnyGroupMemberHasDoom(Player* bot);
// Archimonde
constexpr float AIR_BURST_SAFE_DISTANCE = 15.0f;
struct AirBurstData
{
ObjectGuid targetGuid;
uint32 castTime;
};
struct DoomfireTrailData
{
Position position;
uint32 recordTime;
};
extern const Position ARCHIMONDE_INITIAL_POSITION;
extern std::unordered_map<uint32, AirBurstData> archimondeAirBurstTargets;
extern std::unordered_map<uint32, std::vector<DoomfireTrailData>> doomfireTrails;
extern std::unordered_map<ObjectGuid, uint32> doomfireLastSampleTime;
AirBurstData* GetRecentArchimondeAirBurst(uint32 instanceId);
}
#endif

View File

@ -0,0 +1,211 @@
/*
* 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 "RaidHyjalSummitHelpers.h"
#include "AllCreatureScript.h"
#include "ObjectAccessor.h"
#include "Player.h"
#include "RaidBossHelpers.h"
#include "DynamicObjectScript.h"
#include "Playerbots.h"
#include "ScriptMgr.h"
#include "Spell.h"
#include "Timer.h"
using namespace HyjalSummitHelpers;
static Player* GetFirstPlayerSpellTarget(Spell* spell, Unit* caster)
{
if (!spell || !caster)
return nullptr;
if (Unit* unitTarget = spell->m_targets.GetUnitTarget())
return unitTarget->ToPlayer();
std::list<TargetInfo> const& targets = *spell->GetUniqueTargetInfo();
for (TargetInfo const& targetInfo : targets)
{
if (Player* target = ObjectAccessor::GetPlayer(*caster, targetInfo.targetGUID))
return target;
}
return nullptr;
}
static bool ShouldInterruptForArchimondeAirBurst(PlayerbotAI* botAI, Player* bot, Player* target)
{
if (!target)
return false;
Player* mainTank = GetGroupMainTank(botAI, bot);
if (!mainTank || bot == mainTank)
return false;
float distanceToMainTank = bot->GetExactDist2d(mainTank);
return (target == mainTank || target == bot) &&
distanceToMainTank < AIR_BURST_SAFE_DISTANCE;
}
// Records the active Rain of Fire dynamic object so that melee bots can avoid it by running
// away from Azgalor or swapping to a Doomguard; the standard FleePosition() logic to avoid aoe
// can take melee in front of Azgalor, resulting in them getting cleaved
class AzgalorRainOfFireScript : public DynamicObjectScript
{
public:
AzgalorRainOfFireScript() : DynamicObjectScript("AzgalorRainOfFireScript") {}
void OnUpdate(DynamicObject* dynobj, uint32 /*diff*/) override
{
if (dynobj->GetSpellId() != static_cast<uint32>(HyjalSummitSpells::SPELL_RAIN_OF_FIRE))
return;
uint32 instanceId = dynobj->GetMap()->GetInstanceId();
if (GetActiveAzgalorRainOfFire(instanceId))
return;
uint32 now = getMSTime();
auto instanceIt = rainOfFirePosition.find(instanceId);
if (instanceIt != rainOfFirePosition.end() &&
getMSTimeDiff(instanceIt->second.spawnTime, now) < RAIN_OF_FIRE_REACQUIRE_DELAY)
{
return;
}
bool shouldTrackRainOfFire = false;
Map::PlayerList const& players = dynobj->GetMap()->GetPlayers();
for (Map::PlayerList::const_iterator it = players.begin(); it != players.end(); ++it)
{
Player* player = it->GetSource();
if (!player || !player->IsAlive())
continue;
PlayerbotAI* botAI = GET_PLAYERBOT_AI(player);
if (!botAI || !botAI->HasStrategy("hyjal", BOT_STATE_COMBAT))
continue;
shouldTrackRainOfFire = true;
break;
}
if (!shouldTrackRainOfFire)
return;
rainOfFirePosition[instanceId] = RainOfFireData{ dynobj->GetPosition(), now };
}
};
// Records the position of each Doomfire NPC at regular intervals so that bots can avoid
// the persistent fire trail it leaves behind. Each sample is tagged with a timestamp and
// expires after TRAIL_DURATION ms, matching the lifetime of a Doomfire DynamicObject (18s)
class ArchimondeDoomfireTrailScript : public AllCreatureScript
{
public:
ArchimondeDoomfireTrailScript() : AllCreatureScript("ArchimondeDoomfireTrailScript") {}
void OnAllCreatureUpdate(Creature* creature, uint32 /*diff*/) override
{
if (creature->GetEntry() != static_cast<uint32>(HyjalSummitNpcs::NPC_DOOMFIRE))
return;
uint32 now = getMSTime();
ObjectGuid guid = creature->GetGUID();
auto& lastSample = doomfireLastSampleTime[guid];
if (getMSTimeDiff(lastSample, now) < 500)
return;
lastSample = now;
uint32 instanceId = creature->GetMap()->GetInstanceId();
auto& trail = doomfireTrails[instanceId];
DoomfireTrailData data;
data.position = creature->GetPosition();
data.recordTime = now;
trail.push_back(data);
constexpr uint32 TRAIL_DURATION = 18000;
trail.erase(std::remove_if(trail.begin(), trail.end(),
[now](const DoomfireTrailData& d)
{
return getMSTimeDiff(d.recordTime, now) > TRAIL_DURATION;
}), trail.end());
constexpr float DOOMFIRE_DANGER_RANGE = 10.0f;
Map::PlayerList const& players = creature->GetMap()->GetPlayers();
for (Map::PlayerList::const_iterator it = players.begin(); it != players.end(); ++it)
{
Player* player = it->GetSource();
if (!player || !player->IsAlive())
continue;
PlayerbotAI* botAI = GET_PLAYERBOT_AI(player);
if (!botAI || !botAI->HasStrategy("hyjal", BOT_STATE_COMBAT) ||
creature->GetDistance(player) > DOOMFIRE_DANGER_RANGE)
{
continue;
}
botAI->RequestSpellInterrupt();
}
}
void OnCreatureRemoveWorld(Creature* creature) override
{
if (creature->GetEntry() != static_cast<uint32>(HyjalSummitNpcs::NPC_DOOMFIRE))
return;
doomfireLastSampleTime.erase(creature->GetGUID());
}
};
class ArchimondeAirBurstSpellListenerScript : public AllSpellScript
{
public:
ArchimondeAirBurstSpellListenerScript() :
AllSpellScript("ArchimondeAirBurstSpellListenerScript") {}
void OnSpellCast(
Spell* spell, Unit* caster, SpellInfo const* spellInfo, bool /*skipCheck*/) override
{
if (!spell || !caster || !spellInfo)
return;
if (spellInfo->Id != static_cast<uint32>(HyjalSummitSpells::SPELL_AIR_BURST))
return;
Player* target = GetFirstPlayerSpellTarget(spell, caster);
if (!target)
return;
archimondeAirBurstTargets[caster->GetMap()->GetInstanceId()] =
AirBurstData{ target->GetGUID(), getMSTime() };
Map::PlayerList const& players = caster->GetMap()->GetPlayers();
for (Map::PlayerList::const_iterator it = players.begin(); it != players.end(); ++it)
{
Player* player = it->GetSource();
if (!player || !player->IsAlive())
continue;
PlayerbotAI* botAI = GET_PLAYERBOT_AI(player);
if (!botAI || !botAI->HasStrategy("hyjal", BOT_STATE_COMBAT) ||
!ShouldInterruptForArchimondeAirBurst(botAI, player, target))
{
continue;
}
botAI->RequestSpellInterrupt();
}
}
};
void AddSC_HyjalSummitBotScripts()
{
new AzgalorRainOfFireScript();
new ArchimondeDoomfireTrailScript();
new ArchimondeAirBurstSpellListenerScript();
}

View File

@ -11,6 +11,7 @@
#include "RaidNaxxStrategy.h" #include "RaidNaxxStrategy.h"
#include "RaidSSCStrategy.h" #include "RaidSSCStrategy.h"
#include "RaidTempestKeepStrategy.h" #include "RaidTempestKeepStrategy.h"
#include "RaidHyjalSummitStrategy.h"
#include "RaidZulAmanStrategy.h" #include "RaidZulAmanStrategy.h"
#include "RaidOsStrategy.h" #include "RaidOsStrategy.h"
#include "RaidEoEStrategy.h" #include "RaidEoEStrategy.h"
@ -33,6 +34,7 @@ public:
creators["naxx"] = &RaidStrategyContext::naxx; creators["naxx"] = &RaidStrategyContext::naxx;
creators["ssc"] = &RaidStrategyContext::ssc; creators["ssc"] = &RaidStrategyContext::ssc;
creators["tempestkeep"] = &RaidStrategyContext::tempestkeep; creators["tempestkeep"] = &RaidStrategyContext::tempestkeep;
creators["hyjal"] = &RaidStrategyContext::hyjal;
creators["zulaman"] = &RaidStrategyContext::zulaman; creators["zulaman"] = &RaidStrategyContext::zulaman;
creators["wotlk-os"] = &RaidStrategyContext::wotlk_os; creators["wotlk-os"] = &RaidStrategyContext::wotlk_os;
creators["wotlk-eoe"] = &RaidStrategyContext::wotlk_eoe; creators["wotlk-eoe"] = &RaidStrategyContext::wotlk_eoe;
@ -52,6 +54,7 @@ private:
static Strategy* naxx(PlayerbotAI* botAI) { return new RaidNaxxStrategy(botAI); } static Strategy* naxx(PlayerbotAI* botAI) { return new RaidNaxxStrategy(botAI); }
static Strategy* ssc(PlayerbotAI* botAI) { return new RaidSSCStrategy(botAI); } static Strategy* ssc(PlayerbotAI* botAI) { return new RaidSSCStrategy(botAI); }
static Strategy* tempestkeep(PlayerbotAI* botAI) { return new RaidTempestKeepStrategy(botAI); } static Strategy* tempestkeep(PlayerbotAI* botAI) { return new RaidTempestKeepStrategy(botAI); }
static Strategy* hyjal(PlayerbotAI* botAI) { return new RaidHyjalSummitStrategy(botAI); }
static Strategy* zulaman(PlayerbotAI* botAI) { return new RaidZulAmanStrategy(botAI); } static Strategy* zulaman(PlayerbotAI* botAI) { return new RaidZulAmanStrategy(botAI); }
static Strategy* wotlk_os(PlayerbotAI* botAI) { return new RaidOsStrategy(botAI); } static Strategy* wotlk_os(PlayerbotAI* botAI) { return new RaidOsStrategy(botAI); }
static Strategy* wotlk_eoe(PlayerbotAI* botAI) { return new RaidEoEStrategy(botAI); } static Strategy* wotlk_eoe(PlayerbotAI* botAI) { return new RaidEoEStrategy(botAI); }

View File

@ -11,6 +11,7 @@
#include "Ai/Raid/Magtheridon/RaidMagtheridonActionContext.h" #include "Ai/Raid/Magtheridon/RaidMagtheridonActionContext.h"
#include "Ai/Raid/SerpentshrineCavern/RaidSSCActionContext.h" #include "Ai/Raid/SerpentshrineCavern/RaidSSCActionContext.h"
#include "Ai/Raid/TempestKeep/RaidTempestKeepActionContext.h" #include "Ai/Raid/TempestKeep/RaidTempestKeepActionContext.h"
#include "Ai/Raid/HyjalSummit/RaidHyjalSummitActionContext.h"
#include "Ai/Raid/ZulAman/RaidZulAmanActionContext.h" #include "Ai/Raid/ZulAman/RaidZulAmanActionContext.h"
#include "Ai/Raid/ObsidianSanctum/RaidOsActionContext.h" #include "Ai/Raid/ObsidianSanctum/RaidOsActionContext.h"
#include "Ai/Raid/EyeOfEternity/RaidEoEActionContext.h" #include "Ai/Raid/EyeOfEternity/RaidEoEActionContext.h"
@ -34,6 +35,7 @@ void AiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList<Act
actionContexts.Add(new RaidMagtheridonActionContext()); actionContexts.Add(new RaidMagtheridonActionContext());
actionContexts.Add(new RaidSSCActionContext()); actionContexts.Add(new RaidSSCActionContext());
actionContexts.Add(new RaidTempestKeepActionContext()); actionContexts.Add(new RaidTempestKeepActionContext());
actionContexts.Add(new RaidHyjalSummitActionContext());
actionContexts.Add(new RaidZulAmanActionContext()); actionContexts.Add(new RaidZulAmanActionContext());
actionContexts.Add(new RaidNaxxActionContext()); actionContexts.Add(new RaidNaxxActionContext());
actionContexts.Add(new RaidOsActionContext()); actionContexts.Add(new RaidOsActionContext());

View File

@ -11,6 +11,7 @@
#include "Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h" #include "Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h"
#include "Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h" #include "Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h"
#include "Ai/Raid/TempestKeep/RaidTempestKeepTriggerContext.h" #include "Ai/Raid/TempestKeep/RaidTempestKeepTriggerContext.h"
#include "Ai/Raid/HyjalSummit/RaidHyjalSummitTriggerContext.h"
#include "Ai/Raid/ZulAman/RaidZulAmanTriggerContext.h" #include "Ai/Raid/ZulAman/RaidZulAmanTriggerContext.h"
#include "Ai/Raid/ObsidianSanctum/RaidOsTriggerContext.h" #include "Ai/Raid/ObsidianSanctum/RaidOsTriggerContext.h"
#include "Ai/Raid/EyeOfEternity/RaidEoETriggerContext.h" #include "Ai/Raid/EyeOfEternity/RaidEoETriggerContext.h"
@ -35,6 +36,7 @@ void AiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextList<Tr
triggerContexts.Add(new RaidNaxxTriggerContext()); triggerContexts.Add(new RaidNaxxTriggerContext());
triggerContexts.Add(new RaidSSCTriggerContext()); triggerContexts.Add(new RaidSSCTriggerContext());
triggerContexts.Add(new RaidTempestKeepTriggerContext()); triggerContexts.Add(new RaidTempestKeepTriggerContext());
triggerContexts.Add(new RaidHyjalSummitTriggerContext());
triggerContexts.Add(new RaidZulAmanTriggerContext()); triggerContexts.Add(new RaidZulAmanTriggerContext());
triggerContexts.Add(new RaidOsTriggerContext()); triggerContexts.Add(new RaidOsTriggerContext());
triggerContexts.Add(new RaidEoETriggerContext()); triggerContexts.Add(new RaidEoETriggerContext());

View File

@ -1586,11 +1586,12 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
{ {
static const std::vector<std::string> allInstanceStrategies = static const std::vector<std::string> allInstanceStrategies =
{ {
"aq20", "bwl", "karazhan", "gruulslair", "icc", "magtheridon", "moltencore", "aq20", "bwl", "karazhan", "gruulslair", "hyjal", "icc", "magtheridon",
"naxx", "onyxia", "ssc", "tbc-ac", "tempestkeep", "ulduar", "voa", "wotlk-an", "wotlk-cos", "moltencore", "naxx", "onyxia", "ssc", "tbc-ac", "tempestkeep", "ulduar",
"wotlk-dtk", "wotlk-eoe", "wotlk-fos", "wotlk-gd", "wotlk-hol", "wotlk-hor", "voa", "wotlk-an", "wotlk-cos", "wotlk-dtk", "wotlk-eoe", "wotlk-fos",
"wotlk-hos", "wotlk-nex", "wotlk-occ", "wotlk-ok", "wotlk-os", "wotlk-pos", "wotlk-gd", "wotlk-hol", "wotlk-hor", "wotlk-hos", "wotlk-nex", "wotlk-occ",
"wotlk-toc", "wotlk-uk", "wotlk-up", "wotlk-vh", "zulaman" "wotlk-ok", "wotlk-os", "wotlk-pos", "wotlk-toc", "wotlk-uk", "wotlk-up",
"wotlk-vh", "zulaman"
}; };
for (const std::string& strat : allInstanceStrategies) for (const std::string& strat : allInstanceStrategies)
@ -1620,6 +1621,9 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
case 533: case 533:
strategyName = "naxx"; // Naxxramas strategyName = "naxx"; // Naxxramas
break; break;
case 534:
strategyName = "hyjal"; // The Battle for Mount Hyjal (Hyjal Summit)
break;
case 544: case 544:
strategyName = "magtheridon"; // Magtheridon's Lair strategyName = "magtheridon"; // Magtheridon's Lair
break; break;

View File

@ -526,6 +526,7 @@ public:
void AddPlayerbotsSecureLoginScripts(); void AddPlayerbotsSecureLoginScripts();
void AddSC_TempestKeepBotScripts(); void AddSC_TempestKeepBotScripts();
void AddSC_HyjalSummitBotScripts();
void AddPlayerbotsScripts() void AddPlayerbotsScripts()
{ {
@ -541,4 +542,5 @@ void AddPlayerbotsScripts()
AddPlayerbotsCommandscripts(); AddPlayerbotsCommandscripts();
PlayerBotsGuildValidationScript(); PlayerBotsGuildValidationScript();
AddSC_TempestKeepBotScripts(); AddSC_TempestKeepBotScripts();
AddSC_HyjalSummitBotScripts();
} }