mirror of
https://github.com/liyunfan1223/mod-playerbots.git
synced 2026-06-20 15:39:25 +02:00
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:
parent
38caa1daa7
commit
5d9761c9e8
1200
src/Ai/Raid/HyjalSummit/Action/RaidHyjalSummitActions.cpp
Normal file
1200
src/Ai/Raid/HyjalSummit/Action/RaidHyjalSummitActions.cpp
Normal file
File diff suppressed because it is too large
Load Diff
277
src/Ai/Raid/HyjalSummit/Action/RaidHyjalSummitActions.h
Normal file
277
src/Ai/Raid/HyjalSummit/Action/RaidHyjalSummitActions.h
Normal 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
|
||||
@ -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;
|
||||
}
|
||||
125
src/Ai/Raid/HyjalSummit/Multiplier/RaidHyjalSummitMultipliers.h
Normal file
125
src/Ai/Raid/HyjalSummit/Multiplier/RaidHyjalSummitMultipliers.h
Normal 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
|
||||
218
src/Ai/Raid/HyjalSummit/RaidHyjalSummitActionContext.h
Normal file
218
src/Ai/Raid/HyjalSummit/RaidHyjalSummitActionContext.h
Normal 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
|
||||
218
src/Ai/Raid/HyjalSummit/RaidHyjalSummitTriggerContext.h
Normal file
218
src/Ai/Raid/HyjalSummit/RaidHyjalSummitTriggerContext.h
Normal 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
|
||||
137
src/Ai/Raid/HyjalSummit/Strategy/RaidHyjalSummitStrategy.cpp
Normal file
137
src/Ai/Raid/HyjalSummit/Strategy/RaidHyjalSummitStrategy.cpp
Normal 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));
|
||||
}
|
||||
22
src/Ai/Raid/HyjalSummit/Strategy/RaidHyjalSummitStrategy.h
Normal file
22
src/Ai/Raid/HyjalSummit/Strategy/RaidHyjalSummitStrategy.h
Normal 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
|
||||
357
src/Ai/Raid/HyjalSummit/Trigger/RaidHyjalSummitTriggers.cpp
Normal file
357
src/Ai/Raid/HyjalSummit/Trigger/RaidHyjalSummitTriggers.cpp
Normal 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)));
|
||||
}
|
||||
271
src/Ai/Raid/HyjalSummit/Trigger/RaidHyjalSummitTriggers.h
Normal file
271
src/Ai/Raid/HyjalSummit/Trigger/RaidHyjalSummitTriggers.h
Normal 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
|
||||
268
src/Ai/Raid/HyjalSummit/Util/RaidHyjalSummitHelpers.cpp
Normal file
268
src/Ai/Raid/HyjalSummit/Util/RaidHyjalSummitHelpers.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
143
src/Ai/Raid/HyjalSummit/Util/RaidHyjalSummitHelpers.h
Normal file
143
src/Ai/Raid/HyjalSummit/Util/RaidHyjalSummitHelpers.h
Normal 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
|
||||
211
src/Ai/Raid/HyjalSummit/Util/RaidHyjalSummitScripts.cpp
Normal file
211
src/Ai/Raid/HyjalSummit/Util/RaidHyjalSummitScripts.cpp
Normal 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();
|
||||
}
|
||||
@ -11,6 +11,7 @@
|
||||
#include "RaidNaxxStrategy.h"
|
||||
#include "RaidSSCStrategy.h"
|
||||
#include "RaidTempestKeepStrategy.h"
|
||||
#include "RaidHyjalSummitStrategy.h"
|
||||
#include "RaidZulAmanStrategy.h"
|
||||
#include "RaidOsStrategy.h"
|
||||
#include "RaidEoEStrategy.h"
|
||||
@ -33,6 +34,7 @@ public:
|
||||
creators["naxx"] = &RaidStrategyContext::naxx;
|
||||
creators["ssc"] = &RaidStrategyContext::ssc;
|
||||
creators["tempestkeep"] = &RaidStrategyContext::tempestkeep;
|
||||
creators["hyjal"] = &RaidStrategyContext::hyjal;
|
||||
creators["zulaman"] = &RaidStrategyContext::zulaman;
|
||||
creators["wotlk-os"] = &RaidStrategyContext::wotlk_os;
|
||||
creators["wotlk-eoe"] = &RaidStrategyContext::wotlk_eoe;
|
||||
@ -52,6 +54,7 @@ private:
|
||||
static Strategy* naxx(PlayerbotAI* botAI) { return new RaidNaxxStrategy(botAI); }
|
||||
static Strategy* ssc(PlayerbotAI* botAI) { return new RaidSSCStrategy(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* wotlk_os(PlayerbotAI* botAI) { return new RaidOsStrategy(botAI); }
|
||||
static Strategy* wotlk_eoe(PlayerbotAI* botAI) { return new RaidEoEStrategy(botAI); }
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#include "Ai/Raid/Magtheridon/RaidMagtheridonActionContext.h"
|
||||
#include "Ai/Raid/SerpentshrineCavern/RaidSSCActionContext.h"
|
||||
#include "Ai/Raid/TempestKeep/RaidTempestKeepActionContext.h"
|
||||
#include "Ai/Raid/HyjalSummit/RaidHyjalSummitActionContext.h"
|
||||
#include "Ai/Raid/ZulAman/RaidZulAmanActionContext.h"
|
||||
#include "Ai/Raid/ObsidianSanctum/RaidOsActionContext.h"
|
||||
#include "Ai/Raid/EyeOfEternity/RaidEoEActionContext.h"
|
||||
@ -34,6 +35,7 @@ void AiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList<Act
|
||||
actionContexts.Add(new RaidMagtheridonActionContext());
|
||||
actionContexts.Add(new RaidSSCActionContext());
|
||||
actionContexts.Add(new RaidTempestKeepActionContext());
|
||||
actionContexts.Add(new RaidHyjalSummitActionContext());
|
||||
actionContexts.Add(new RaidZulAmanActionContext());
|
||||
actionContexts.Add(new RaidNaxxActionContext());
|
||||
actionContexts.Add(new RaidOsActionContext());
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#include "Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h"
|
||||
#include "Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h"
|
||||
#include "Ai/Raid/TempestKeep/RaidTempestKeepTriggerContext.h"
|
||||
#include "Ai/Raid/HyjalSummit/RaidHyjalSummitTriggerContext.h"
|
||||
#include "Ai/Raid/ZulAman/RaidZulAmanTriggerContext.h"
|
||||
#include "Ai/Raid/ObsidianSanctum/RaidOsTriggerContext.h"
|
||||
#include "Ai/Raid/EyeOfEternity/RaidEoETriggerContext.h"
|
||||
@ -35,6 +36,7 @@ void AiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextList<Tr
|
||||
triggerContexts.Add(new RaidNaxxTriggerContext());
|
||||
triggerContexts.Add(new RaidSSCTriggerContext());
|
||||
triggerContexts.Add(new RaidTempestKeepTriggerContext());
|
||||
triggerContexts.Add(new RaidHyjalSummitTriggerContext());
|
||||
triggerContexts.Add(new RaidZulAmanTriggerContext());
|
||||
triggerContexts.Add(new RaidOsTriggerContext());
|
||||
triggerContexts.Add(new RaidEoETriggerContext());
|
||||
|
||||
@ -1586,11 +1586,12 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
|
||||
{
|
||||
static const std::vector<std::string> allInstanceStrategies =
|
||||
{
|
||||
"aq20", "bwl", "karazhan", "gruulslair", "icc", "magtheridon", "moltencore",
|
||||
"naxx", "onyxia", "ssc", "tbc-ac", "tempestkeep", "ulduar", "voa", "wotlk-an", "wotlk-cos",
|
||||
"wotlk-dtk", "wotlk-eoe", "wotlk-fos", "wotlk-gd", "wotlk-hol", "wotlk-hor",
|
||||
"wotlk-hos", "wotlk-nex", "wotlk-occ", "wotlk-ok", "wotlk-os", "wotlk-pos",
|
||||
"wotlk-toc", "wotlk-uk", "wotlk-up", "wotlk-vh", "zulaman"
|
||||
"aq20", "bwl", "karazhan", "gruulslair", "hyjal", "icc", "magtheridon",
|
||||
"moltencore", "naxx", "onyxia", "ssc", "tbc-ac", "tempestkeep", "ulduar",
|
||||
"voa", "wotlk-an", "wotlk-cos", "wotlk-dtk", "wotlk-eoe", "wotlk-fos",
|
||||
"wotlk-gd", "wotlk-hol", "wotlk-hor", "wotlk-hos", "wotlk-nex", "wotlk-occ",
|
||||
"wotlk-ok", "wotlk-os", "wotlk-pos", "wotlk-toc", "wotlk-uk", "wotlk-up",
|
||||
"wotlk-vh", "zulaman"
|
||||
};
|
||||
|
||||
for (const std::string& strat : allInstanceStrategies)
|
||||
@ -1620,6 +1621,9 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
|
||||
case 533:
|
||||
strategyName = "naxx"; // Naxxramas
|
||||
break;
|
||||
case 534:
|
||||
strategyName = "hyjal"; // The Battle for Mount Hyjal (Hyjal Summit)
|
||||
break;
|
||||
case 544:
|
||||
strategyName = "magtheridon"; // Magtheridon's Lair
|
||||
break;
|
||||
|
||||
@ -526,6 +526,7 @@ public:
|
||||
void AddPlayerbotsSecureLoginScripts();
|
||||
|
||||
void AddSC_TempestKeepBotScripts();
|
||||
void AddSC_HyjalSummitBotScripts();
|
||||
|
||||
void AddPlayerbotsScripts()
|
||||
{
|
||||
@ -541,4 +542,5 @@ void AddPlayerbotsScripts()
|
||||
AddPlayerbotsCommandscripts();
|
||||
PlayerBotsGuildValidationScript();
|
||||
AddSC_TempestKeepBotScripts();
|
||||
AddSC_HyjalSummitBotScripts();
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user