mirror of
https://github.com/mod-playerbots/mod-playerbots.git
synced 2026-03-15 13:25:09 +00:00
Merge pull request #2191 from mod-playerbots/test-staging
Test staging to master
This commit is contained in:
1
.github/workflows/check_pr_source.yml
vendored
1
.github/workflows/check_pr_source.yml
vendored
@@ -2,6 +2,7 @@ name: Enforce test-staging → master
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, edited]
|
||||
branches:
|
||||
- master
|
||||
- test-staging
|
||||
|
||||
@@ -1,124 +1,103 @@
|
||||
# Pull Request
|
||||
<!--
|
||||
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.
|
||||
|
||||
Describe what this change does and why it is needed...
|
||||
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.
|
||||
|
||||
## Design Philosophy
|
||||
Before submitting, make sure your changes aligns with these principles.
|
||||
-->
|
||||
|
||||
We prioritize **stability, performance, and predictability** over behavioral realism.
|
||||
Complex player-mimicking logic is intentionally limited due to its negative impact on scalability, maintainability, and
|
||||
long-term robustness.
|
||||
## Pull Request Description
|
||||
<!-- Describe what this change does and why it is needed -->
|
||||
|
||||
Excessive processing overhead can lead to server hiccups, increased CPU usage, and degraded performance for all
|
||||
participants. Because every action and
|
||||
decision tree is executed **per bot and per trigger**, even small increases in logic complexity can scale poorly and
|
||||
negatively affect both players and
|
||||
world (random) bots. Bots are not expected to behave perfectly, and perfect simulation of human decision-making is not a
|
||||
project goal. Increased behavioral
|
||||
realism often introduces disproportionate cost, reduced predictability, and significantly higher maintenance overhead.
|
||||
|
||||
Every additional branch of logic increases long-term responsibility. All decision paths must be tested, validated, and
|
||||
maintained continuously as the system evolves.
|
||||
If advanced or AI-intensive behavior is introduced, the **default configuration must remain the lightweight decision
|
||||
model**. More complex behavior should only be
|
||||
available as an **explicit opt-in option**, clearly documented as having a measurable performance cost.
|
||||
|
||||
Principles:
|
||||
|
||||
- **Stability before intelligence**
|
||||
A stable system is always preferred over a smarter one.
|
||||
|
||||
- **Performance is a shared resource**
|
||||
Any increase in bot cost affects all players and all bots.
|
||||
|
||||
- **Simple logic scales better than smart logic**
|
||||
Predictable behavior under load is more valuable than perfect decisions.
|
||||
|
||||
- **Complexity must justify itself**
|
||||
If a feature cannot clearly explain its cost, it should not exist.
|
||||
|
||||
- **Defaults must be cheap**
|
||||
Expensive behavior must always be optional and clearly communicated.
|
||||
|
||||
- **Bots should look reasonable, not perfect**
|
||||
The goal is believable behavior, not human simulation.
|
||||
|
||||
Before submitting, confirm that this change aligns with those principles.
|
||||
|
||||
---
|
||||
|
||||
## 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:
|
||||
<!-- 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.
|
||||
|
||||
- Describe the **minimum logic** required to achieve the intended behavior?
|
||||
- Describe the **cheapest implementation** that produces an acceptable result?
|
||||
- Describe the **runtime cost** when this logic executes across many bots?
|
||||
|
||||
---
|
||||
|
||||
## 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.
|
||||
-->
|
||||
|
||||
- Step-by-step instructions to test the change
|
||||
- Any required setup (e.g. multiple players, bots, specific configuration)
|
||||
- Expected behavior and how to verify it
|
||||
|
||||
## Complexity & Impact
|
||||
|
||||
Does this change add new decision branches?
|
||||
- - [ ] No
|
||||
- - [ ] Yes (**explain below**)
|
||||
## 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
|
||||
- [ ] Minimal impact (**explain below**)
|
||||
- [ ] Moderate impact (**explain below**)
|
||||
|
||||
Does this change increase per-bot or per-tick processing?
|
||||
- - [ ] No
|
||||
- - [ ] Yes (**describe and justify impact**)
|
||||
|
||||
Could this logic scale poorly under load?
|
||||
- - [ ] No
|
||||
- - [ ] Yes (**explain why**)
|
||||
---
|
||||
|
||||
## Defaults & Configuration
|
||||
- Does this change modify default bot behavior?
|
||||
- [ ] No
|
||||
- [ ] Yes (**explain why**)
|
||||
|
||||
Does this change modify default bot behavior?
|
||||
- - [ ] No
|
||||
- - [ ] Yes (**explain why**)
|
||||
|
||||
If this introduces more advanced or AI-heavy logic:
|
||||
- - [ ] Lightweight mode remains the default
|
||||
- - [ ] More complex behavior is optional and thereby configurable
|
||||
---
|
||||
|
||||
- Does this change add new decision branches or increase maintenance complexity?
|
||||
- [ ] No
|
||||
- [ ] Yes (**explain below**)
|
||||
|
||||
|
||||
|
||||
## 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?
|
||||
- [ ] No
|
||||
- [ ] Yes (**list messages in the table**)
|
||||
|
||||
| Message key | Default message |
|
||||
| --------------- | ------------------ |
|
||||
| | |
|
||||
| | |
|
||||
|
||||
## AI Assistance
|
||||
|
||||
Was AI assistance (e.g. ChatGPT or similar tools) used while working on this change?
|
||||
- - [ ] No
|
||||
- - [ ] Yes (**explain below**)
|
||||
|
||||
If yes, please specify:
|
||||
|
||||
- AI tool or model used (e.g. ChatGPT, GPT-4, Claude, etc.)
|
||||
- Purpose of usage (e.g. brainstorming, refactoring, documentation, code generation)
|
||||
- Which parts of the change were influenced or generated
|
||||
- Whether the result was manually reviewed and adapted
|
||||
|
||||
<!--
|
||||
AI assistance is allowed, but all submitted code must be fully understood, reviewed, and owned by the contributor.
|
||||
Any AI-influenced changes must be verified against existing CORE and PB logic. We expect contributors to be honest
|
||||
about what they do and do not understand.
|
||||
We expect contributors to be honest about what they do and do not understand.
|
||||
-->
|
||||
Was AI assistance used while working on this change?
|
||||
- [ ] No
|
||||
- [ ] 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.
|
||||
-->
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Final Checklist
|
||||
|
||||
- - [ ] Stability is not compromised
|
||||
- - [ ] Performance impact is understood, tested, and acceptable
|
||||
- - [ ] Added logic complexity is justified and explained
|
||||
- - [ ] Documentation updated if needed
|
||||
|
||||
---
|
||||
- [ ] Stability is not compromised.
|
||||
- [ ] Performance impact is understood, tested, and acceptable.
|
||||
- [ ] Added logic complexity is justified and explained.
|
||||
- [ ] Documentation updated if needed (Conf comments, WiKi commands).
|
||||
|
||||
## Notes for Reviewers
|
||||
|
||||
Anything that significantly improves realism at the cost of stability or performance should be carefully discussed
|
||||
before merging.
|
||||
<!-- Anything else that's helpful to review or test your pull request. -->
|
||||
|
||||
@@ -566,7 +566,10 @@ AiPlayerbot.AutoGearScoreLimit = 0
|
||||
# Default: food, taxi, and raid are enabled
|
||||
AiPlayerbot.BotCheats = "food,taxi,raid"
|
||||
|
||||
# Attunement quests (comma-separated list of quest IDs)
|
||||
# List of attunement quests (comma-separated list of quest IDs) that are automatically completed for all bots.
|
||||
# While mod-playerbots does not restore removed attunement requirements, although other mods, such as mod-individual-progression, may do so.
|
||||
# This is meant to exclude bots from such requirements.
|
||||
#
|
||||
# Default:
|
||||
# Caverns of Time - Part 1
|
||||
# - 10279, To The Master's Lair
|
||||
@@ -592,7 +595,17 @@ AiPlayerbot.BotCheats = "food,taxi,raid"
|
||||
#
|
||||
# Serpentshrine Cavern
|
||||
# - 10901, The Cudgel of Kar'desh
|
||||
AiPlayerbot.AttunementQuests = 10279,10277,10282,10283,10284,10285,10296,10297,10298,11481,11482,11488,11490,11492,10901
|
||||
#
|
||||
# The Eye
|
||||
# - 10888, Trial of the Naaru: Magtheridon
|
||||
#
|
||||
# Mount Hyjal
|
||||
# - 10445, The Vials of Eternity
|
||||
#
|
||||
# Black Temple
|
||||
# - 10985, A Distraction for Akama
|
||||
#
|
||||
AiPlayerbot.AttunementQuests = 10279,10277,10282,10283,10284,10285,10296,10297,10298,11481,11482,11488,11490,11492,10901,10888,10445,10985
|
||||
|
||||
#
|
||||
#
|
||||
@@ -796,6 +809,10 @@ AiPlayerbot.LimitGearExpansion = 1
|
||||
# Set between 0 (0%) and 1 (100%)
|
||||
AiPlayerbot.RandomGearLoweringChance = 0
|
||||
|
||||
# Unobtainable or unusable items (comma-separated list of item IDs)
|
||||
# Default: Chilton Wand (12468), Totem of the Earthen Ring (46978)
|
||||
AiPlayerbot.UnobtainableItems = 12468,46978
|
||||
|
||||
# Randombots check player's gearscore level and deny the group invitation if it's too low
|
||||
# Default: 0 (disabled)
|
||||
AiPlayerbot.GearScoreCheck = 0
|
||||
@@ -2213,4 +2230,4 @@ AiPlayerbot.SummonAtInnkeepersEnabled = 1
|
||||
# 30% more damage, 40% damage reduction (tank bots), increased all resistances, reduced threat for non tank bots, increased threat for tank bots.
|
||||
# Buffs will be applied on PP, Sindragosa and Lich King
|
||||
|
||||
AiPlayerbot.EnableICCBuffs = 1
|
||||
AiPlayerbot.EnableICCBuffs = 1
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
UPDATE `ai_playerbot_texts` SET `text_loc3`='%s, du hörst den triefenden Sarkasmus in meinem text nicht' WHERE `id`=1353;
|
||||
@@ -237,6 +237,20 @@ bool MaxDpsChatShortcutAction::Execute(Event /*event*/)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NaxxChatShortcutAction::Execute(Event /*event*/)
|
||||
{
|
||||
Player* master = GetMaster();
|
||||
if (!master)
|
||||
return false;
|
||||
|
||||
botAI->Reset();
|
||||
botAI->ChangeStrategy("+naxx", BOT_STATE_NON_COMBAT);
|
||||
botAI->ChangeStrategy("+naxx", BOT_STATE_COMBAT);
|
||||
botAI->TellMasterNoFacing("Add Naxx Strategies!");
|
||||
// bot->Say("Add Naxx Strategies!", LANG_UNIVERSAL);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BwlChatShortcutAction::Execute(Event /*event*/)
|
||||
{
|
||||
Player* master = GetMaster();
|
||||
|
||||
@@ -85,6 +85,13 @@ public:
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class NaxxChatShortcutAction : public Action
|
||||
{
|
||||
public:
|
||||
NaxxChatShortcutAction(PlayerbotAI* ai) : Action(ai, "naxx chat shortcut") {}
|
||||
virtual bool Execute(Event event);
|
||||
};
|
||||
|
||||
class BwlChatShortcutAction : public Action
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -854,6 +854,11 @@ float MovementAction::GetFollowAngle()
|
||||
if (!group)
|
||||
return 0.0f;
|
||||
|
||||
// Prevent bots with orphaned raid groups from dividing by 0, which freezes the server.
|
||||
uint32 memberCount = group->GetMembersCount();
|
||||
if (memberCount <= 1)
|
||||
return 0.0f;
|
||||
|
||||
uint32 index = 1;
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
@@ -861,7 +866,7 @@ float MovementAction::GetFollowAngle()
|
||||
continue;
|
||||
|
||||
if (ref->GetSource() == bot)
|
||||
return 2 * M_PI / (group->GetMembersCount() - 1) * index;
|
||||
return 2 * M_PI / (memberCount - 1) * index;
|
||||
|
||||
++index;
|
||||
}
|
||||
|
||||
@@ -187,6 +187,7 @@ public:
|
||||
creators["guild leave"] = &ChatActionContext::guild_leave;
|
||||
creators["rtsc"] = &ChatActionContext::rtsc;
|
||||
creators["bwl chat shortcut"] = &ChatActionContext::bwl_chat_shortcut;
|
||||
creators["naxx chat shortcut"] = &ChatActionContext::naxx_chat_shortcut;
|
||||
creators["tell estimated dps"] = &ChatActionContext::tell_estimated_dps;
|
||||
creators["join"] = &ChatActionContext::join;
|
||||
creators["lfg"] = &ChatActionContext::lfg;
|
||||
@@ -298,6 +299,7 @@ private:
|
||||
static Action* guild_remove(PlayerbotAI* botAI) { return new GuildRemoveAction(botAI); }
|
||||
static Action* guild_leave(PlayerbotAI* botAI) { return new GuildLeaveAction(botAI); }
|
||||
static Action* rtsc(PlayerbotAI* botAI) { return new RTSCAction(botAI); }
|
||||
static Action* naxx_chat_shortcut(PlayerbotAI* ai) { return new NaxxChatShortcutAction(ai); }
|
||||
static Action* bwl_chat_shortcut(PlayerbotAI* ai) { return new BwlChatShortcutAction(ai); }
|
||||
static Action* tell_estimated_dps(PlayerbotAI* ai) { return new TellEstimatedDpsAction(ai); }
|
||||
static Action* join(PlayerbotAI* ai) { return new JoinGroupAction(ai); }
|
||||
|
||||
@@ -127,7 +127,6 @@ public:
|
||||
creators["guild leave"] = &ChatTriggerContext::guild_leave;
|
||||
creators["rtsc"] = &ChatTriggerContext::rtsc;
|
||||
creators["drink"] = &ChatTriggerContext::drink;
|
||||
// creators["bwl"] = &ChatTriggerContext::bwl;
|
||||
creators["dps"] = &ChatTriggerContext::dps;
|
||||
creators["disperse"] = &ChatTriggerContext::disperse;
|
||||
creators["calc"] = &ChatTriggerContext::calc;
|
||||
@@ -245,7 +244,6 @@ private:
|
||||
static Trigger* guild_leave(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "guild leave"); }
|
||||
static Trigger* rtsc(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "rtsc"); }
|
||||
static Trigger* drink(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "drink"); }
|
||||
// static Trigger* bwl(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "bwl"); }
|
||||
static Trigger* dps(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "dps"); }
|
||||
static Trigger* disperse(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "disperse"); }
|
||||
static Trigger* calc(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "calc"); }
|
||||
|
||||
@@ -83,6 +83,8 @@ void ChatCommandHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
|
||||
new TriggerNode("target", { NextAction("tell target", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("ready", { NextAction("ready check", relevance) }));
|
||||
triggers.push_back(
|
||||
new TriggerNode("naxx", {NextAction("naxx chat shortcut", relevance)}));
|
||||
triggers.push_back(
|
||||
new TriggerNode("bwl", { NextAction("bwl chat shortcut", relevance) }));
|
||||
triggers.push_back(
|
||||
|
||||
@@ -116,9 +116,9 @@ bool AttumenTheHuntsmanStackBehindAction::Execute(Event /*event*/)
|
||||
float rearX = attumenMounted->GetPositionX() + std::cos(orientation) * distanceBehind;
|
||||
float rearY = attumenMounted->GetPositionY() + std::sin(orientation) * distanceBehind;
|
||||
|
||||
if (bot->GetExactDist2d(rearX, rearY) > 1.0f)
|
||||
if (bot->GetDistance2d(rearX, rearY) > 1.0f)
|
||||
{
|
||||
return MoveTo(KARAZHAN_MAP_ID, rearX, rearY, attumenMounted->GetPositionZ(), false, false, false, false,
|
||||
return MoveTo(KARAZHAN_MAP_ID, rearX, rearY, bot->GetPositionZ(), false, false, false, false,
|
||||
MovementPriority::MOVEMENT_FORCED, true, false);
|
||||
}
|
||||
|
||||
@@ -1178,7 +1178,7 @@ bool PrinceMalchezaarNonTankAvoidInfernalAction::Execute(Event /*event*/)
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(true);
|
||||
return MoveTo(KARAZHAN_MAP_ID, bestDestX, bestDestY, bestDestZ, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_FORCED, true, false);
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1244,7 +1244,7 @@ bool PrinceMalchezaarMainTankMovementAction::Execute(Event /*event*/)
|
||||
{
|
||||
bot->AttackStop();
|
||||
return MoveTo(KARAZHAN_MAP_ID, bestDestX, bestDestY, bestDestZ, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_FORCED, true, false);
|
||||
MovementPriority::MOVEMENT_COMBAT, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -80,6 +80,19 @@ float AttumenTheHuntsmanWaitForDpsMultiplier::GetValue(Action* action)
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Disables co +disperse and co +tank face
|
||||
float MaidenOfVirtueDisableCombatFormationMoveMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "maiden of virtue"))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action) &&
|
||||
!dynamic_cast<SetBehindTargetAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// The assist tank should stay on the boss to be 2nd on aggro and tank Hateful Bolts
|
||||
float TheCuratorDisableTankAssistMultiplier::GetValue(Action* action)
|
||||
{
|
||||
@@ -93,6 +106,19 @@ float TheCuratorDisableTankAssistMultiplier::GetValue(Action* action)
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Disables co +disperse and co +tank face
|
||||
float TheCuratorDisableCombatFormationMoveMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "the curator"))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action) &&
|
||||
!dynamic_cast<SetBehindTargetAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Save Bloodlust/Heroism for Evocation (100% increased damage)
|
||||
float TheCuratorDelayBloodlustAndHeroismMultiplier::GetValue(Action* action)
|
||||
{
|
||||
@@ -350,17 +376,11 @@ float NightbaneDisableMovementMultiplier::GetValue(Action* action)
|
||||
|
||||
if (dynamic_cast<CastBlinkBackAction*>(action) ||
|
||||
dynamic_cast<CastDisengageAction*>(action) ||
|
||||
dynamic_cast<FleeAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
// Disable CombatFormationMoveAction for all bots except:
|
||||
// (1) main tank and (2) only during the ground phase, other melee
|
||||
if (botAI->IsRanged(bot) ||
|
||||
(botAI->IsMelee(bot) && !botAI->IsMainTank(bot) &&
|
||||
nightbane->GetPositionZ() > NIGHTBANE_FLIGHT_Z))
|
||||
dynamic_cast<FleeAction*>(action) ||
|
||||
(dynamic_cast<CombatFormationMoveAction*>(action) &&
|
||||
!dynamic_cast<SetBehindTargetAction*>(action)))
|
||||
{
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action))
|
||||
return 0.0f;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
|
||||
@@ -27,6 +27,14 @@ public:
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class MaidenOfVirtueDisableCombatFormationMoveMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
MaidenOfVirtueDisableCombatFormationMoveMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "maiden of virtue disable combat formation move multiplier") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class TheCuratorDisableTankAssistMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
@@ -35,6 +43,14 @@ public:
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class TheCuratorDisableCombatFormationMoveMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
TheCuratorDisableCombatFormationMoveMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "the curator disable combat formation move multiplier") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class TheCuratorDelayBloodlustAndHeroismMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -146,7 +146,9 @@ void RaidKarazhanStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers
|
||||
multipliers.push_back(new AttumenTheHuntsmanDisableTankAssistMultiplier(botAI));
|
||||
multipliers.push_back(new AttumenTheHuntsmanStayStackedMultiplier(botAI));
|
||||
multipliers.push_back(new AttumenTheHuntsmanWaitForDpsMultiplier(botAI));
|
||||
multipliers.push_back(new MaidenOfVirtueDisableCombatFormationMoveMultiplier(botAI));
|
||||
multipliers.push_back(new TheCuratorDisableTankAssistMultiplier(botAI));
|
||||
multipliers.push_back(new TheCuratorDisableCombatFormationMoveMultiplier(botAI));
|
||||
multipliers.push_back(new TheCuratorDelayBloodlustAndHeroismMultiplier(botAI));
|
||||
multipliers.push_back(new ShadeOfAranArcaneExplosionDisableChargeMultiplier(botAI));
|
||||
multipliers.push_back(new ShadeOfAranFlameWreathDisableMovementMultiplier(botAI));
|
||||
|
||||
@@ -62,7 +62,6 @@ namespace KarazhanHelpers
|
||||
NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED = 16152,
|
||||
|
||||
// Terestian Illhoof
|
||||
NPC_TERESTIAN_ILLHOOF = 15688,
|
||||
NPC_DEMON_CHAINS = 17248,
|
||||
NPC_KILREK = 17229,
|
||||
|
||||
|
||||
326
src/Ai/Raid/Naxxramas/Action/RaidNaxxActions.h
Normal file
326
src/Ai/Raid/Naxxramas/Action/RaidNaxxActions.h
Normal file
@@ -0,0 +1,326 @@
|
||||
#ifndef _PLAYERBOT_RAIDNAXXACTIONS_H
|
||||
#define _PLAYERBOT_RAIDNAXXACTIONS_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "AttackAction.h"
|
||||
#include "GenericActions.h"
|
||||
#include "MovementActions.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
#include "RaidNaxxBossHelper.h"
|
||||
|
||||
class GrobbulusGoBehindAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
GrobbulusGoBehindAction(PlayerbotAI* ai, float distance = 24.0f, float delta_angle = M_PI / 8)
|
||||
: MovementAction(ai, "grobbulus go behind")
|
||||
{
|
||||
this->distance = distance;
|
||||
this->delta_angle = delta_angle;
|
||||
}
|
||||
virtual bool Execute(Event event);
|
||||
|
||||
protected:
|
||||
float distance, delta_angle;
|
||||
};
|
||||
|
||||
class GrobbulusRotateAction : public RotateAroundTheCenterPointAction
|
||||
{
|
||||
public:
|
||||
GrobbulusRotateAction(PlayerbotAI* botAI)
|
||||
: RotateAroundTheCenterPointAction(botAI, "rotate grobbulus", 3281.23f, -3310.38f, 35.0f, 8, true, M_PI) {}
|
||||
virtual bool isUseful() override
|
||||
{
|
||||
return RotateAroundTheCenterPointAction::isUseful() && botAI->IsMainTank(bot) &&
|
||||
AI_VALUE2(bool, "has aggro", "boss target");
|
||||
}
|
||||
uint32 GetCurrWaypoint() override;
|
||||
};
|
||||
|
||||
class GrobblulusMoveCenterAction : public MoveInsideAction
|
||||
{
|
||||
public:
|
||||
GrobblulusMoveCenterAction(PlayerbotAI* ai) : MoveInsideAction(ai, 3281.23f, -3310.38f, 5.0f) {}
|
||||
};
|
||||
|
||||
class GrobbulusMoveAwayAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
GrobbulusMoveAwayAction(PlayerbotAI* ai, float distance = 18.0f)
|
||||
: MovementAction(ai, "grobbulus move away"), distance(distance)
|
||||
{
|
||||
}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
private:
|
||||
float distance;
|
||||
};
|
||||
|
||||
//class HeiganDanceAction : public MovementAction
|
||||
//{
|
||||
//public:
|
||||
// HeiganDanceAction(PlayerbotAI* ai) : MovementAction(ai, "heigan dance")
|
||||
// {
|
||||
// this->last_eruption_ms = 0;
|
||||
// this->platform_phase = false;
|
||||
// ResetSafe();
|
||||
// waypoints.push_back(std::make_pair(2794.88f, -3668.12f));
|
||||
// waypoints.push_back(std::make_pair(2775.49f, -3674.43f));
|
||||
// waypoints.push_back(std::make_pair(2762.30f, -3684.59f));
|
||||
// waypoints.push_back(std::make_pair(2755.99f, -3703.96f));
|
||||
// platform = std::make_pair(2794.26f, -3706.67f);
|
||||
// }
|
||||
//
|
||||
//protected:
|
||||
// bool CalculateSafe();
|
||||
// void ResetSafe()
|
||||
// {
|
||||
// curr_safe = 0;
|
||||
// curr_dir = 1;
|
||||
// }
|
||||
// void NextSafe()
|
||||
// {
|
||||
// curr_safe += curr_dir;
|
||||
// if (curr_safe == 3 || curr_safe == 0)
|
||||
// {
|
||||
// curr_dir = -curr_dir;
|
||||
// }
|
||||
// }
|
||||
// uint32 last_eruption_ms;
|
||||
// bool platform_phase;
|
||||
// uint32 curr_safe, curr_dir;
|
||||
// std::vector<std::pair<float, float>> waypoints;
|
||||
// std::pair<float, float> platform;
|
||||
//};
|
||||
//
|
||||
//class HeiganDanceMeleeAction : public HeiganDanceAction
|
||||
//{
|
||||
//public:
|
||||
// HeiganDanceMeleeAction(PlayerbotAI* ai) : HeiganDanceAction(ai) {}
|
||||
// virtual bool Execute(Event event);
|
||||
//};
|
||||
//
|
||||
//class HeiganDanceRangedAction : public HeiganDanceAction
|
||||
//{
|
||||
//public:
|
||||
// HeiganDanceRangedAction(PlayerbotAI* ai) : HeiganDanceAction(ai) {}
|
||||
// virtual bool Execute(Event event);
|
||||
//};
|
||||
|
||||
class ThaddiusAttackNearestPetAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
ThaddiusAttackNearestPetAction(PlayerbotAI* ai) : AttackAction(ai, "thaddius attack nearest pet"), helper(ai) {}
|
||||
virtual bool Execute(Event event);
|
||||
virtual bool isUseful();
|
||||
|
||||
private:
|
||||
ThaddiusBossHelper helper;
|
||||
};
|
||||
|
||||
// class ThaddiusMeleeToPlaceAction : public MovementAction
|
||||
// {
|
||||
// public:
|
||||
// ThaddiusMeleeToPlaceAction(PlayerbotAI* ai) : MovementAction(ai, "thaddius melee to place") {}
|
||||
// virtual bool Execute(Event event);
|
||||
// virtual bool isUseful();
|
||||
// };
|
||||
|
||||
// class ThaddiusRangedToPlaceAction : public MovementAction
|
||||
// {
|
||||
// public:
|
||||
// ThaddiusRangedToPlaceAction(PlayerbotAI* ai) : MovementAction(ai, "thaddius ranged to place") {}
|
||||
// virtual bool Execute(Event event);
|
||||
// virtual bool isUseful();
|
||||
// };
|
||||
|
||||
class ThaddiusMoveToPlatformAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
ThaddiusMoveToPlatformAction(PlayerbotAI* ai) : MovementAction(ai, "thaddius move to platform") {}
|
||||
virtual bool Execute(Event event);
|
||||
virtual bool isUseful();
|
||||
};
|
||||
|
||||
class ThaddiusMovePolarityAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
ThaddiusMovePolarityAction(PlayerbotAI* ai) : MovementAction(ai, "thaddius move polarity") {}
|
||||
virtual bool Execute(Event event);
|
||||
virtual bool isUseful();
|
||||
};
|
||||
|
||||
class RazuviousUseObedienceCrystalAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
RazuviousUseObedienceCrystalAction(PlayerbotAI* ai)
|
||||
: MovementAction(ai, "razuvious use obedience crystal"), helper(ai)
|
||||
{
|
||||
}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
private:
|
||||
RazuviousBossHelper helper;
|
||||
};
|
||||
|
||||
class RazuviousTargetAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
RazuviousTargetAction(PlayerbotAI* ai) : AttackAction(ai, "razuvious target"), helper(ai) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
private:
|
||||
RazuviousBossHelper helper;
|
||||
};
|
||||
|
||||
class HorsemanAttractAlternativelyAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
HorsemanAttractAlternativelyAction(PlayerbotAI* ai) : AttackAction(ai, "horseman attract alternatively"), helper(ai)
|
||||
{
|
||||
}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
protected:
|
||||
FourhorsemanBossHelper helper;
|
||||
};
|
||||
|
||||
class HorsemanAttactInOrderAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
HorsemanAttactInOrderAction(PlayerbotAI* ai) : AttackAction(ai, "horseman attact in order"), helper(ai) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
protected:
|
||||
FourhorsemanBossHelper helper;
|
||||
};
|
||||
|
||||
// class SapphironGroundMainTankPositionAction : public MovementAction
|
||||
// {
|
||||
// public:
|
||||
// SapphironGroundMainTankPositionAction(PlayerbotAI* ai) : MovementAction(ai, "sapphiron ground main tank
|
||||
// position") {} virtual bool Execute(Event event);
|
||||
// };
|
||||
|
||||
class SapphironGroundPositionAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
SapphironGroundPositionAction(PlayerbotAI* ai) : MovementAction(ai, "sapphiron ground position"), helper(ai) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
protected:
|
||||
SapphironBossHelper helper;
|
||||
};
|
||||
|
||||
class SapphironFlightPositionAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
SapphironFlightPositionAction(PlayerbotAI* ai) : MovementAction(ai, "sapphiron flight position"), helper(ai) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
protected:
|
||||
SapphironBossHelper helper;
|
||||
bool MoveToNearestIcebolt();
|
||||
};
|
||||
|
||||
// class SapphironAvoidChillAction : public MovementAction
|
||||
// {
|
||||
// public:
|
||||
// SapphironAvoidChillAction(PlayerbotAI* ai) : MovementAction(ai, "sapphiron avoid chill") {}
|
||||
// virtual bool Execute(Event event);
|
||||
// };
|
||||
|
||||
class KelthuzadChooseTargetAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
KelthuzadChooseTargetAction(PlayerbotAI* ai) : AttackAction(ai, "kel'thuzad choose target"), helper(ai) {}
|
||||
virtual bool Execute(Event event);
|
||||
|
||||
private:
|
||||
KelthuzadBossHelper helper;
|
||||
};
|
||||
|
||||
class KelthuzadPositionAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
KelthuzadPositionAction(PlayerbotAI* ai) : MovementAction(ai, "kel'thuzad position"), helper(ai) {}
|
||||
virtual bool Execute(Event event);
|
||||
|
||||
private:
|
||||
KelthuzadBossHelper helper;
|
||||
};
|
||||
|
||||
class AnubrekhanChooseTargetAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
AnubrekhanChooseTargetAction(PlayerbotAI* ai) : AttackAction(ai, "anub'rekhan choose target") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class AnubrekhanPositionAction : public RotateAroundTheCenterPointAction
|
||||
{
|
||||
public:
|
||||
AnubrekhanPositionAction(PlayerbotAI* ai)
|
||||
: RotateAroundTheCenterPointAction(ai, "anub'rekhan position", 3272.49f, -3476.27f, 45.0f, 16) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class GluthChooseTargetAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
GluthChooseTargetAction(PlayerbotAI* ai) : AttackAction(ai, "gluth choose target"), helper(ai) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
private:
|
||||
GluthBossHelper helper;
|
||||
};
|
||||
|
||||
class GluthPositionAction : public RotateAroundTheCenterPointAction
|
||||
{
|
||||
public:
|
||||
GluthPositionAction(PlayerbotAI* ai)
|
||||
: RotateAroundTheCenterPointAction(ai, "gluth position", 3293.61f, -3149.01f, 12.0f, 12), helper(ai) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
private:
|
||||
GluthBossHelper helper;
|
||||
};
|
||||
|
||||
class GluthSlowdownAction : public Action
|
||||
{
|
||||
public:
|
||||
GluthSlowdownAction(PlayerbotAI* ai) : Action(ai, "gluth slowdown"), helper(ai) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
private:
|
||||
GluthBossHelper helper;
|
||||
};
|
||||
|
||||
class LoathebPositionAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
LoathebPositionAction(PlayerbotAI* ai) : MovementAction(ai, "loatheb position"), helper(ai) {}
|
||||
virtual bool Execute(Event event);
|
||||
|
||||
private:
|
||||
LoathebBossHelper helper;
|
||||
};
|
||||
|
||||
class LoathebChooseTargetAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
LoathebChooseTargetAction(PlayerbotAI* ai) : AttackAction(ai, "loatheb choose target"), helper(ai) {}
|
||||
virtual bool Execute(Event event);
|
||||
|
||||
private:
|
||||
LoathebBossHelper helper;
|
||||
};
|
||||
|
||||
//class PatchwerkRangedPositionAction : public MovementAction
|
||||
//{
|
||||
//public:
|
||||
// PatchwerkRangedPositionAction(PlayerbotAI* ai) : MovementAction(ai, "patchwerk ranged position") {}
|
||||
// bool Execute(Event event) override;
|
||||
//};
|
||||
|
||||
#endif
|
||||
81
src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Anubrekhan.cpp
Normal file
81
src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Anubrekhan.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
#include "RaidNaxxActions.h"
|
||||
|
||||
#include "ObjectGuid.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
bool AnubrekhanChooseTargetAction::Execute(Event /*event*/)
|
||||
{
|
||||
GuidVector attackers = context->GetValue<GuidVector>("attackers")->Get();
|
||||
Unit* target = nullptr;
|
||||
Unit* target_boss = nullptr;
|
||||
std::vector<Unit*> target_guards;
|
||||
for (ObjectGuid const guid : attackers)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(guid);
|
||||
if (!unit)
|
||||
continue;
|
||||
if (botAI->EqualLowercaseName(unit->GetName(), "crypt guard"))
|
||||
target_guards.push_back(unit);
|
||||
|
||||
if (botAI->EqualLowercaseName(unit->GetName(), "anub'rekhan"))
|
||||
target_boss = unit;
|
||||
}
|
||||
if (botAI->IsMainTank(bot))
|
||||
target = target_boss;
|
||||
else
|
||||
{
|
||||
if (target_guards.size() == 0)
|
||||
target = target_boss;
|
||||
else
|
||||
{
|
||||
if (botAI->IsAssistTank(bot))
|
||||
{
|
||||
for (Unit* t : target_guards)
|
||||
{
|
||||
if (target == nullptr || (target->GetVictim() && target->GetVictim()->ToPlayer() &&
|
||||
botAI->IsTank(target->GetVictim()->ToPlayer())))
|
||||
target = t;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (Unit* t : target_guards)
|
||||
{
|
||||
if (target == nullptr || target->GetHealthPct() > t->GetHealthPct())
|
||||
target = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (context->GetValue<Unit*>("current target")->Get() == target)
|
||||
return false;
|
||||
|
||||
return Attack(target);
|
||||
}
|
||||
|
||||
bool AnubrekhanPositionAction::Execute(Event /*event*/)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "anub'rekhan");
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
bool inPhase = botAI->HasAura("locust swarm", boss) || boss->GetCurrentSpell(CURRENT_GENERIC_SPELL);
|
||||
if (inPhase)
|
||||
{
|
||||
if (botAI->IsMainTank(bot))
|
||||
{
|
||||
uint32 nearest = FindNearestWaypoint();
|
||||
uint32 next_point;
|
||||
if (inPhase)
|
||||
next_point = (nearest + 1) % intervals;
|
||||
else
|
||||
next_point = nearest;
|
||||
|
||||
return MoveTo(bot->GetMapId(), waypoints[next_point].first, waypoints[next_point].second, bot->GetPositionZ(), false, false,
|
||||
false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
else
|
||||
return MoveInside(533, 3272.49f, -3476.27f, bot->GetPositionZ(), 3.0f, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "RaidNaxxActions.h"
|
||||
|
||||
// Reserved for Faerlina-specific actions.
|
||||
@@ -0,0 +1,59 @@
|
||||
#include "RaidNaxxActions.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
bool HorsemanAttractAlternativelyAction::Execute(Event /*event*/)
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
return false;
|
||||
|
||||
helper.CalculatePosToGo(bot);
|
||||
auto [posX, posY] = helper.CurrentAttractPos();
|
||||
if (MoveTo(bot->GetMapId(), posX, posY, helper.posZ, false, false, false, false, MovementPriority::MOVEMENT_COMBAT))
|
||||
return true;
|
||||
|
||||
Unit* attackTarget = helper.CurrentAttackTarget();
|
||||
if (context->GetValue<Unit*>("current target")->Get() != attackTarget)
|
||||
return Attack(attackTarget);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HorsemanAttactInOrderAction::Execute(Event /*event*/)
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
return false;
|
||||
|
||||
Unit* target = nullptr;
|
||||
Unit* thane = AI_VALUE2(Unit*, "find target", "thane korth'azz");
|
||||
Unit* lady = AI_VALUE2(Unit*, "find target", "lady blaumeux");
|
||||
Unit* sir = AI_VALUE2(Unit*, "find target", "sir zeliek");
|
||||
Unit* fourth = AI_VALUE2(Unit*, "find target", "baron rivendare");
|
||||
if (!fourth)
|
||||
fourth = AI_VALUE2(Unit*, "find target", "highlord mograine");
|
||||
|
||||
std::vector<Unit*> attack_order;
|
||||
if (botAI->IsAssistTank(bot))
|
||||
attack_order = {fourth, thane, lady, sir};
|
||||
else
|
||||
attack_order = {thane, fourth, lady, sir};
|
||||
for (Unit* t : attack_order)
|
||||
{
|
||||
if (t && t->IsAlive())
|
||||
{
|
||||
target = t;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (target)
|
||||
{
|
||||
if (context->GetValue<Unit*>("current target")->Get() == target && botAI->GetState() == BOT_STATE_COMBAT)
|
||||
return false;
|
||||
|
||||
if (!bot->IsWithinLOSInMap(target))
|
||||
return MoveNear(target, 22.0f, MovementPriority::MOVEMENT_COMBAT);
|
||||
|
||||
return Attack(target);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
178
src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Gluth.cpp
Normal file
178
src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Gluth.cpp
Normal file
@@ -0,0 +1,178 @@
|
||||
#include "RaidNaxxActions.h"
|
||||
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "Playerbots.h"
|
||||
#include "SharedDefines.h"
|
||||
|
||||
bool GluthChooseTargetAction::Execute(Event /*event*/)
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
return false;
|
||||
|
||||
GuidVector attackers = context->GetValue<GuidVector>("possible targets")->Get();
|
||||
Unit* target = nullptr;
|
||||
Unit* target_boss = nullptr;
|
||||
std::vector<Unit*> target_zombies;
|
||||
for (GuidVector::iterator i = attackers.begin(); i != attackers.end(); ++i)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(*i);
|
||||
if (!unit)
|
||||
continue;
|
||||
|
||||
if (!unit->IsAlive())
|
||||
continue;
|
||||
|
||||
if (botAI->EqualLowercaseName(unit->GetName(), "zombie chow"))
|
||||
target_zombies.push_back(unit);
|
||||
|
||||
if (botAI->EqualLowercaseName(unit->GetName(), "gluth"))
|
||||
target_boss = unit;
|
||||
}
|
||||
if (botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0))
|
||||
target = target_boss;
|
||||
else if (botAI->IsAssistTankOfIndex(bot, 1))
|
||||
{
|
||||
for (Unit* t : target_zombies)
|
||||
{
|
||||
if (t->GetHealthPct() > helper.decimatedZombiePct && t->GetVictim() != bot && t->GetDistance2d(bot) <= 10.0f)
|
||||
{
|
||||
if (!target || t->GetDistance2d(bot) < target->GetDistance2d(bot))
|
||||
target = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (botAI->GetClassIndex(bot, CLASS_HUNTER) == 0 || botAI->GetClassIndex(bot, CLASS_HUNTER) == 1)
|
||||
{
|
||||
// prevent zombie go straight to gluth
|
||||
for (Unit* t : target_zombies)
|
||||
{
|
||||
if (t->GetHealthPct() > helper.decimatedZombiePct && t->GetVictim() == target_boss &&
|
||||
t->GetDistance2d(bot) <= sPlayerbotAIConfig.spellDistance)
|
||||
{
|
||||
if (!target || t->GetDistance2d(bot) < target->GetDistance2d(bot))
|
||||
target = t;
|
||||
}
|
||||
}
|
||||
if (!target)
|
||||
target = target_boss;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (Unit* t : target_zombies)
|
||||
{
|
||||
if (t->GetHealthPct() <= helper.decimatedZombiePct)
|
||||
{
|
||||
if (target == nullptr ||
|
||||
target->GetDistance2d(helper.mainTankPos25.first, helper.mainTankPos25.second) >
|
||||
t->GetDistance2d(helper.mainTankPos25.first, helper.mainTankPos25.second))
|
||||
target = t;
|
||||
}
|
||||
}
|
||||
if (target == nullptr)
|
||||
target = target_boss;
|
||||
}
|
||||
if (!target || context->GetValue<Unit*>("current target")->Get() == target)
|
||||
return false;
|
||||
|
||||
if (target_boss && target == target_boss)
|
||||
return Attack(target, true);
|
||||
|
||||
return Attack(target, false);
|
||||
// return Attack(target);
|
||||
}
|
||||
|
||||
bool GluthPositionAction::Execute(Event /*event*/)
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
return false;
|
||||
|
||||
bool raid25 = bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL;
|
||||
if (botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0))
|
||||
{
|
||||
if (AI_VALUE2(bool, "has aggro", "boss target"))
|
||||
{
|
||||
if (raid25)
|
||||
{
|
||||
if (MoveTo(NAXX_MAP_ID, helper.mainTankPos25.first, helper.mainTankPos25.second, bot->GetPositionZ(), false, false, false,
|
||||
false, MovementPriority::MOVEMENT_COMBAT))
|
||||
return true;
|
||||
|
||||
return MoveInside(NAXX_MAP_ID, helper.mainTankPos25.first, helper.mainTankPos25.second, bot->GetPositionZ(), 2.0f,
|
||||
MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (MoveTo(NAXX_MAP_ID, helper.mainTankPos10.first, helper.mainTankPos10.second, bot->GetPositionZ(), false, false, false,
|
||||
false, MovementPriority::MOVEMENT_COMBAT))
|
||||
return true;
|
||||
|
||||
return MoveInside(NAXX_MAP_ID, helper.mainTankPos10.first, helper.mainTankPos10.second, bot->GetPositionZ(), 2.0f,
|
||||
MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (botAI->IsAssistTankOfIndex(bot, 1))
|
||||
{
|
||||
if (helper.BeforeDecimate())
|
||||
{
|
||||
if (MoveTo(bot->GetMapId(), helper.beforeDecimatePos.first, helper.beforeDecimatePos.second, bot->GetPositionZ(), false, false,
|
||||
false, false, MovementPriority::MOVEMENT_COMBAT))
|
||||
return true;
|
||||
|
||||
return MoveInside(bot->GetMapId(), helper.beforeDecimatePos.first, helper.beforeDecimatePos.second, bot->GetPositionZ(), 2.0f,
|
||||
MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AI_VALUE2(bool, "has aggro", "current target"))
|
||||
{
|
||||
uint32 nearest = FindNearestWaypoint();
|
||||
uint32 next_point = (nearest + 1) % intervals;
|
||||
return MoveTo(bot->GetMapId(), waypoints[next_point].first, waypoints[next_point].second, bot->GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (botAI->IsRangedDps(bot))
|
||||
{
|
||||
if (raid25)
|
||||
{
|
||||
if (botAI->GetClassIndex(bot, CLASS_HUNTER) == 0)
|
||||
return MoveInside(NAXX_MAP_ID, helper.leftSlowDownPos.first, helper.leftSlowDownPos.second, bot->GetPositionZ(), 0.0f,
|
||||
MovementPriority::MOVEMENT_COMBAT);
|
||||
|
||||
if (botAI->GetClassIndex(bot, CLASS_HUNTER) == 1)
|
||||
return MoveInside(NAXX_MAP_ID, helper.rightSlowDownPos.first, helper.rightSlowDownPos.second, bot->GetPositionZ(), 0.0f,
|
||||
MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
return MoveInside(NAXX_MAP_ID, helper.rangedPos.first, helper.rangedPos.second, bot->GetPositionZ(), 3.0f,
|
||||
MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
else if (botAI->IsHeal(bot))
|
||||
return MoveInside(NAXX_MAP_ID, helper.healPos.first, helper.healPos.second, bot->GetPositionZ(), 0.0f,
|
||||
MovementPriority::MOVEMENT_COMBAT);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GluthSlowdownAction::Execute(Event /*event*/)
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
return false;
|
||||
|
||||
bool raid25 = bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL;
|
||||
if (!raid25)
|
||||
return false;
|
||||
|
||||
if (helper.JustStartCombat())
|
||||
return false;
|
||||
|
||||
switch (bot->getClass())
|
||||
{
|
||||
case CLASS_HUNTER:
|
||||
return botAI->CastSpell("frost trap", bot);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
3
src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Gothik.cpp
Normal file
3
src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Gothik.cpp
Normal file
@@ -0,0 +1,3 @@
|
||||
#include "RaidNaxxActions.h"
|
||||
|
||||
// Reserved for Gothik-specific actions.
|
||||
46
src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Grobbulus.cpp
Normal file
46
src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Grobbulus.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#include "RaidNaxxActions.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
bool GrobbulusGoBehindAction::Execute(Event /*event*/)
|
||||
{
|
||||
Unit* boss = AI_VALUE(Unit*, "boss target");
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
// Position* pos = boss->GetPosition();
|
||||
float orientation = boss->GetOrientation() + M_PI + delta_angle;
|
||||
float x = boss->GetPositionX();
|
||||
float y = boss->GetPositionY();
|
||||
float z = boss->GetPositionZ();
|
||||
float rx = x + cos(orientation) * distance;
|
||||
float ry = y + sin(orientation) * distance;
|
||||
return MoveTo(bot->GetMapId(), rx, ry, z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
|
||||
bool GrobbulusMoveAwayAction::Execute(Event /*event*/)
|
||||
{
|
||||
Unit* boss = AI_VALUE(Unit*, "boss target");
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
const float currentDistance = bot->GetExactDist2d(boss);
|
||||
if (currentDistance >= distance)
|
||||
return false;
|
||||
|
||||
const float angle = boss->GetAngle(bot);
|
||||
const float x = boss->GetPositionX() + cos(angle) * distance;
|
||||
const float y = boss->GetPositionY() + sin(angle) * distance;
|
||||
const float z = bot->GetPositionZ();
|
||||
|
||||
return MoveTo(bot->GetMapId(), x, y, z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
|
||||
uint32 GrobbulusRotateAction::GetCurrWaypoint()
|
||||
{
|
||||
uint32 current = FindNearestWaypoint();
|
||||
if (clockwise)
|
||||
return (current + 1) % intervals;
|
||||
|
||||
return (current + intervals - 1) % intervals;
|
||||
}
|
||||
77
src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Heigan.cpp
Normal file
77
src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Heigan.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
#include "Playerbots.h"
|
||||
#include "RaidNaxxActions.h"
|
||||
#include "RaidNaxxSpellIds.h"
|
||||
#include "Spell.h"
|
||||
#include "Timer.h"
|
||||
|
||||
//bool HeiganDanceAction::CalculateSafe()
|
||||
//{
|
||||
// Unit* boss = AI_VALUE2(Unit*, "find target", "heigan the unclean");
|
||||
// if (!boss)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// uint32 now = getMSTime();
|
||||
// platform_phase = boss->IsWithinDist2d(platform.first, platform.second, 10.0f);
|
||||
// if (last_eruption_ms != 0 && now - last_eruption_ms > 15000)
|
||||
// {
|
||||
// ResetSafe();
|
||||
// }
|
||||
// if (boss->HasUnitState(UNIT_STATE_CASTING))
|
||||
// {
|
||||
// Spell* spell = boss->GetCurrentSpell(CURRENT_GENERIC_SPELL);
|
||||
// if (!spell)
|
||||
// {
|
||||
// spell = boss->GetCurrentSpell(CURRENT_CHANNELED_SPELL);
|
||||
// }
|
||||
// if (spell)
|
||||
// {
|
||||
// SpellInfo const* info = spell->GetSpellInfo();
|
||||
// bool isEruption = NaxxSpellIds::MatchesAnySpellId(info, {NaxxSpellIds::Eruption10});
|
||||
// if (!isEruption && info && info->SpellName[LOCALE_enUS])
|
||||
// {
|
||||
// // Fallback to name for custom spell data.
|
||||
// isEruption = botAI->EqualLowercaseName(info->SpellName[LOCALE_enUS], "eruption");
|
||||
// }
|
||||
// if (isEruption)
|
||||
// {
|
||||
// if (last_eruption_ms == 0 || now - last_eruption_ms > 500)
|
||||
// {
|
||||
// NextSafe();
|
||||
// }
|
||||
// last_eruption_ms = now;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return true;
|
||||
//}
|
||||
//
|
||||
//bool HeiganDanceMeleeAction::Execute(Event event)
|
||||
//{
|
||||
// CalculateSafe();
|
||||
// if (!platform_phase && botAI->IsMainTank(bot) && !AI_VALUE2(bool, "has aggro", "boss target"))
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// assert(curr_safe >= 0 && curr_safe <= 3);
|
||||
// return MoveInside(bot->GetMapId(), waypoints[curr_safe].first, waypoints[curr_safe].second, bot->GetPositionZ(),
|
||||
// botAI->IsMainTank(bot) ? 0 : 0, MovementPriority::MOVEMENT_COMBAT);
|
||||
//}
|
||||
//
|
||||
//bool HeiganDanceRangedAction::Execute(Event event)
|
||||
//{
|
||||
// CalculateSafe();
|
||||
// if (!platform_phase)
|
||||
// {
|
||||
// if (MoveTo(bot->GetMapId(), platform.first, platform.second, 276.54f, false, false, false, false,
|
||||
// MovementPriority::MOVEMENT_COMBAT))
|
||||
// {
|
||||
// return true;
|
||||
// }
|
||||
// return MoveInside(bot->GetMapId(), platform.first, platform.second, 276.54f, 2.0f,
|
||||
// MovementPriority::MOVEMENT_COMBAT);
|
||||
// }
|
||||
// botAI->InterruptSpell();
|
||||
// return MoveInside(bot->GetMapId(), waypoints[curr_safe].first, waypoints[curr_safe].second, bot->GetPositionZ(), 0,
|
||||
// MovementPriority::MOVEMENT_COMBAT);
|
||||
//}
|
||||
177
src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Kelthuzad.cpp
Normal file
177
src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Kelthuzad.cpp
Normal file
@@ -0,0 +1,177 @@
|
||||
#include "RaidNaxxActions.h"
|
||||
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
bool KelthuzadChooseTargetAction::Execute(Event /*event*/)
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
return false;
|
||||
|
||||
GuidVector attackers = context->GetValue<GuidVector>("attackers")->Get();
|
||||
Unit* target = nullptr;
|
||||
Unit *target_soldier = nullptr, *target_weaver = nullptr, *target_abomination = nullptr, *target_kelthuzad = nullptr,
|
||||
*target_guardian = nullptr;
|
||||
for (auto i = attackers.begin(); i != attackers.end(); ++i)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(*i);
|
||||
if (!unit)
|
||||
continue;
|
||||
|
||||
if (botAI->EqualLowercaseName(unit->GetName(), "guardian of icecrown"))
|
||||
{
|
||||
if (!target_guardian)
|
||||
target_guardian = unit;
|
||||
else if (unit->GetVictim() && target_guardian->GetVictim() && unit->GetVictim()->ToPlayer() &&
|
||||
target_guardian->GetVictim()->ToPlayer() && !botAI->IsAssistTank(unit->GetVictim()->ToPlayer()) &&
|
||||
botAI->IsAssistTank(target_guardian->GetVictim()->ToPlayer()))
|
||||
{
|
||||
target_guardian = unit;
|
||||
}
|
||||
else if (unit->GetVictim() && target_guardian->GetVictim() && unit->GetVictim()->ToPlayer() &&
|
||||
target_guardian->GetVictim()->ToPlayer() && !botAI->IsAssistTank(unit->GetVictim()->ToPlayer()) &&
|
||||
!botAI->IsAssistTank(target_guardian->GetVictim()->ToPlayer()) &&
|
||||
target_guardian->GetDistance2d(helper.center.first, helper.center.second) >
|
||||
bot->GetDistance2d(unit))
|
||||
{
|
||||
target_guardian = unit;
|
||||
}
|
||||
}
|
||||
|
||||
if (unit->GetDistance2d(helper.center.first, helper.center.second) > 30.0f)
|
||||
continue;
|
||||
|
||||
if (bot->GetDistance2d(unit) > sPlayerbotAIConfig.spellDistance)
|
||||
continue;
|
||||
|
||||
if (botAI->EqualLowercaseName(unit->GetName(), "unstoppable abomination"))
|
||||
{
|
||||
if (target_abomination == nullptr ||
|
||||
target_abomination->GetDistance2d(helper.center.first, helper.center.second) >
|
||||
unit->GetDistance2d(helper.center.first, helper.center.second))
|
||||
{
|
||||
target_abomination = unit;
|
||||
}
|
||||
}
|
||||
if (botAI->EqualLowercaseName(unit->GetName(), "soldier of the frozen wastes"))
|
||||
{
|
||||
if (target_soldier == nullptr ||
|
||||
target_soldier->GetDistance2d(helper.center.first, helper.center.second) >
|
||||
unit->GetDistance2d(helper.center.first, helper.center.second))
|
||||
{
|
||||
target_soldier = unit;
|
||||
}
|
||||
}
|
||||
if (botAI->EqualLowercaseName(unit->GetName(), "soul weaver"))
|
||||
{
|
||||
if (target_weaver == nullptr || target_weaver->GetDistance2d(helper.center.first, helper.center.second) >
|
||||
unit->GetDistance2d(helper.center.first, helper.center.second))
|
||||
target_weaver = unit;
|
||||
}
|
||||
|
||||
if (botAI->EqualLowercaseName(unit->GetName(), "kel'thuzad"))
|
||||
target_kelthuzad = unit;
|
||||
}
|
||||
std::vector<Unit*> targets;
|
||||
if (botAI->IsRanged(bot))
|
||||
{
|
||||
if (botAI->GetRangedDpsIndex(bot) <= 1)
|
||||
targets = {target_soldier, target_weaver, target_abomination, target_kelthuzad};
|
||||
else
|
||||
targets = {target_weaver, target_soldier, target_abomination, target_kelthuzad};
|
||||
}
|
||||
else if (botAI->IsAssistTank(bot))
|
||||
targets = {target_abomination, target_guardian, target_kelthuzad};
|
||||
else
|
||||
targets = {target_abomination, target_kelthuzad};
|
||||
|
||||
for (Unit* t : targets)
|
||||
{
|
||||
if (t)
|
||||
{
|
||||
target = t;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (context->GetValue<Unit*>("current target")->Get() == target)
|
||||
return false;
|
||||
|
||||
if (target_kelthuzad && target == target_kelthuzad)
|
||||
return Attack(target, true);
|
||||
|
||||
return Attack(target, false);
|
||||
}
|
||||
|
||||
bool KelthuzadPositionAction::Execute(Event /*event*/)
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
return false;
|
||||
|
||||
if (helper.IsPhaseOne())
|
||||
{
|
||||
if (AI_VALUE(Unit*, "current target") == nullptr)
|
||||
return MoveInside(NAXX_MAP_ID, helper.center.first, helper.center.second, bot->GetPositionZ(), 3.0f,
|
||||
MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
else if (helper.IsPhaseTwo())
|
||||
{
|
||||
Unit* shadow_fissure = helper.GetAnyShadowFissure();
|
||||
if (!shadow_fissure || !bot->IsWithinDistInMap(shadow_fissure, 10.0f))
|
||||
{
|
||||
float distance, angle;
|
||||
if (botAI->IsMainTank(bot))
|
||||
{
|
||||
if (AI_VALUE2(bool, "has aggro", "current target"))
|
||||
return MoveTo(NAXX_MAP_ID, helper.tank_pos.first, helper.tank_pos.second, bot->GetPositionZ(), false, false, false,
|
||||
false, MovementPriority::MOVEMENT_COMBAT);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else if (botAI->IsRanged(bot))
|
||||
{
|
||||
uint32 index = botAI->GetRangedIndex(bot);
|
||||
if (index < 8)
|
||||
{
|
||||
distance = 20.0f;
|
||||
angle = index * M_PI / 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
distance = 32.0f;
|
||||
angle = (index - 8) * M_PI / 4;
|
||||
}
|
||||
float dx, dy;
|
||||
dx = helper.center.first + cos(angle) * distance;
|
||||
dy = helper.center.second + sin(angle) * distance;
|
||||
return MoveTo(NAXX_MAP_ID, dx, dy, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
else if (botAI->IsTank(bot))
|
||||
{
|
||||
Unit* cur_tar = AI_VALUE(Unit*, "current target");
|
||||
if (cur_tar && cur_tar->GetVictim() && cur_tar->GetVictim()->ToPlayer() &&
|
||||
botAI->EqualLowercaseName(cur_tar->GetName(), "guardian of icecrown") &&
|
||||
botAI->IsAssistTank(cur_tar->GetVictim()->ToPlayer()))
|
||||
{
|
||||
return MoveTo(NAXX_MAP_ID, helper.assist_tank_pos.first, helper.assist_tank_pos.second, bot->GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float dx, dy;
|
||||
float angle;
|
||||
if (!botAI->IsRanged(bot))
|
||||
angle = shadow_fissure->GetAngle(helper.center.first, helper.center.second);
|
||||
else
|
||||
angle = bot->GetAngle(shadow_fissure) + M_PI;
|
||||
|
||||
dx = shadow_fissure->GetPositionX() + cos(angle) * 10.0f;
|
||||
dy = shadow_fissure->GetPositionY() + sin(angle) * 10.0f;
|
||||
return MoveTo(NAXX_MAP_ID, dx, dy, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
55
src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Loatheb.cpp
Normal file
55
src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Loatheb.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#include "RaidNaxxActions.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
bool LoathebPositionAction::Execute(Event /*event*/)
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
return false;
|
||||
|
||||
if (botAI->IsTank(bot))
|
||||
{
|
||||
if (AI_VALUE2(bool, "has aggro", "boss target"))
|
||||
return MoveTo(533, helper.mainTankPos.first, helper.mainTankPos.second, bot->GetPositionZ(), false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
else if (botAI->IsRanged(bot))
|
||||
return MoveInside(533, helper.rangePos.first, helper.rangePos.second, bot->GetPositionZ(), 1.0f,
|
||||
MovementPriority::MOVEMENT_COMBAT);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LoathebChooseTargetAction::Execute(Event /*event*/)
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
return false;
|
||||
|
||||
GuidVector attackers = context->GetValue<GuidVector>("attackers")->Get();
|
||||
Unit* target = nullptr;
|
||||
Unit* target_boss = nullptr;
|
||||
Unit* target_spore = nullptr;
|
||||
for (auto i = attackers.begin(); i != attackers.end(); ++i)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(*i);
|
||||
if (!unit)
|
||||
continue;
|
||||
|
||||
if (!unit->IsAlive())
|
||||
continue;
|
||||
|
||||
if (botAI->EqualLowercaseName(unit->GetName(), "spore"))
|
||||
target_spore = unit;
|
||||
|
||||
if (botAI->EqualLowercaseName(unit->GetName(), "loatheb"))
|
||||
target_boss = unit;
|
||||
}
|
||||
if (target_spore && bot->GetDistance2d(target_spore) <= 1.0f)
|
||||
target = target_spore;
|
||||
else
|
||||
target = target_boss;
|
||||
|
||||
if (!target || context->GetValue<Unit*>("current target")->Get() == target)
|
||||
return false;
|
||||
|
||||
return Attack(target);
|
||||
}
|
||||
3
src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Maexxna.cpp
Normal file
3
src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Maexxna.cpp
Normal file
@@ -0,0 +1,3 @@
|
||||
#include "RaidNaxxActions.h"
|
||||
|
||||
// Reserved for Maexxna-specific actions.
|
||||
3
src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Noth.cpp
Normal file
3
src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Noth.cpp
Normal file
@@ -0,0 +1,3 @@
|
||||
#include "RaidNaxxActions.h"
|
||||
|
||||
// Reserved for Noth-specific actions.
|
||||
31
src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Patchwerk.cpp
Normal file
31
src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Patchwerk.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#include "RaidNaxxActions.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
//bool PatchwerkRangedPositionAction::Execute(Event event)
|
||||
//{
|
||||
// Unit* boss = AI_VALUE2(Unit*, "find target", "patchwerk");
|
||||
// if (!boss)
|
||||
// return false;
|
||||
//
|
||||
// constexpr float minDistance = 12.0f;
|
||||
// constexpr float maxDistance = 15.0f;
|
||||
// const float distance = bot->GetExactDist2d(boss);
|
||||
//
|
||||
// if (distance >= minDistance && distance <= maxDistance)
|
||||
// return false;
|
||||
//
|
||||
// const float desiredDistance = std::clamp(distance, minDistance, maxDistance);
|
||||
// float angle = boss->GetAngle(bot);
|
||||
//
|
||||
// if (distance < 0.1f)
|
||||
// angle = boss->GetOrientation();
|
||||
//
|
||||
// const float x = boss->GetPositionX() + std::cos(angle) * desiredDistance;
|
||||
// const float y = boss->GetPositionY() + std::sin(angle) * desiredDistance;
|
||||
// const float z = bot->GetPositionZ();
|
||||
//
|
||||
// return MoveTo(boss->GetMapId(), x, y, z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true,
|
||||
// false);
|
||||
//}
|
||||
147
src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Razuvious.cpp
Normal file
147
src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Razuvious.cpp
Normal file
@@ -0,0 +1,147 @@
|
||||
#include "RaidNaxxActions.h"
|
||||
|
||||
#include "ObjectGuid.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "Playerbots.h"
|
||||
#include "SharedDefines.h"
|
||||
|
||||
bool RazuviousUseObedienceCrystalAction::Execute(Event /*event*/)
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
return false;
|
||||
|
||||
// bot->GetCharm
|
||||
if (Unit* charm = bot->GetCharm())
|
||||
{
|
||||
Unit* target = AI_VALUE2(Unit*, "find target", "instructor razuvious");
|
||||
if (!target)
|
||||
return false;
|
||||
|
||||
if (charm->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE) == NULL_MOTION_TYPE)
|
||||
{
|
||||
charm->GetMotionMaster()->Clear();
|
||||
charm->GetMotionMaster()->MoveChase(target);
|
||||
charm->GetAI()->AttackStart(target);
|
||||
}
|
||||
Aura* forceObedience = botAI->GetAura("force obedience", charm);
|
||||
uint32 duration_time;
|
||||
if (!forceObedience)
|
||||
{
|
||||
forceObedience = botAI->GetAura("mind control", charm);
|
||||
duration_time = 60000;
|
||||
}
|
||||
else
|
||||
duration_time = 90000;
|
||||
|
||||
if (!forceObedience)
|
||||
return false;
|
||||
|
||||
if (charm->GetDistance(target) <= 0.51f)
|
||||
{
|
||||
// taunt
|
||||
bool tauntUseful = true;
|
||||
if (forceObedience->GetDuration() <= (duration_time - 5000))
|
||||
{
|
||||
if (target->GetVictim() && botAI->HasAura(29061, target->GetVictim()))
|
||||
tauntUseful = false;
|
||||
|
||||
if (forceObedience->GetDuration() <= 3000)
|
||||
tauntUseful = false;
|
||||
|
||||
}
|
||||
if (forceObedience->GetDuration() >= (duration_time - 500))
|
||||
tauntUseful = false;
|
||||
|
||||
if (tauntUseful && !charm->HasSpellCooldown(29060))
|
||||
{
|
||||
// shield
|
||||
if (!charm->HasSpellCooldown(29061))
|
||||
{
|
||||
charm->CastSpell(charm, 29061, true);
|
||||
charm->AddSpellCooldown(29061, 0, 30 * 1000);
|
||||
}
|
||||
charm->CastSpell(target, 29060, true);
|
||||
charm->AddSpellCooldown(29060, 0, 20 * 1000);
|
||||
}
|
||||
// strike
|
||||
if (!charm->HasSpellCooldown(61696))
|
||||
{
|
||||
charm->CastSpell(target, 61696, true);
|
||||
charm->AddSpellCooldown(61696, 0, 4 * 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Difficulty diff = bot->GetRaidDifficulty();
|
||||
if (diff == RAID_DIFFICULTY_10MAN_NORMAL)
|
||||
{
|
||||
GuidVector npcs = AI_VALUE(GuidVector, "nearest npcs");
|
||||
for (auto i = npcs.begin(); i != npcs.end(); i++)
|
||||
{
|
||||
Creature* unit = botAI->GetCreature(*i);
|
||||
if (!unit)
|
||||
continue;
|
||||
|
||||
if (botAI->IsMainTank(bot) && unit->GetSpawnId() != 128352)
|
||||
continue;
|
||||
|
||||
if (!botAI->IsMainTank(bot) && unit->GetSpawnId() != 128353)
|
||||
continue;
|
||||
|
||||
if (MoveTo(unit, 0.0f, MovementPriority::MOVEMENT_COMBAT))
|
||||
return true;
|
||||
|
||||
Creature* creature = bot->GetNPCIfCanInteractWith(*i, UNIT_NPC_FLAG_SPELLCLICK);
|
||||
if (!creature)
|
||||
continue;
|
||||
|
||||
creature->HandleSpellClick(bot);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GuidVector attackers = context->GetValue<GuidVector>("attackers")->Get();
|
||||
Unit* target = nullptr;
|
||||
for (auto i = attackers.begin(); i != attackers.end(); ++i)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(*i);
|
||||
if (!unit)
|
||||
continue;
|
||||
if (botAI->EqualLowercaseName(unit->GetName(), "death knight understudy"))
|
||||
{
|
||||
target = unit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (target)
|
||||
{
|
||||
if (bot->GetDistance2d(target) > sPlayerbotAIConfig.spellDistance)
|
||||
return MoveNear(target, sPlayerbotAIConfig.spellDistance, MovementPriority::MOVEMENT_COMBAT);
|
||||
else
|
||||
return botAI->CastSpell("mind control", target);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RazuviousTargetAction::Execute(Event /*event*/)
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
return false;
|
||||
|
||||
Unit* razuvious = AI_VALUE2(Unit*, "find target", "instructor razuvious");
|
||||
Unit* understudy = AI_VALUE2(Unit*, "find target", "death knight understudy");
|
||||
Unit* target = nullptr;
|
||||
if (botAI->IsTank(bot))
|
||||
target = understudy;
|
||||
else
|
||||
target = razuvious;
|
||||
|
||||
if (AI_VALUE(Unit*, "current target") == target)
|
||||
return false;
|
||||
|
||||
return Attack(target);
|
||||
}
|
||||
104
src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Sapphiron.cpp
Normal file
104
src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Sapphiron.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
#include "RaidNaxxActions.h"
|
||||
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "Playerbots.h"
|
||||
#include "RaidNaxxBossHelper.h"
|
||||
#include "RaidNaxxSpellIds.h"
|
||||
|
||||
bool SapphironGroundPositionAction::Execute(Event /*event*/)
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
return false;
|
||||
|
||||
if (botAI->IsMainTank(bot))
|
||||
{
|
||||
if (AI_VALUE2(bool, "has aggro", "current target"))
|
||||
return MoveTo(NAXX_MAP_ID, helper.mainTankPos.first, helper.mainTankPos.second, helper.GENERIC_HEIGHT, false, false, false,
|
||||
false, MovementPriority::MOVEMENT_COMBAT);
|
||||
|
||||
return false;
|
||||
}
|
||||
if (helper.JustLanded())
|
||||
{
|
||||
uint32 index = botAI->GetGroupSlotIndex(bot);
|
||||
float start_angle = 0.85 * M_PI;
|
||||
float offset_angle = M_PI * 0.02 * index;
|
||||
float angle = start_angle + offset_angle;
|
||||
float distance;
|
||||
if (botAI->IsRanged(bot))
|
||||
distance = 35.0f;
|
||||
else if (botAI->IsHeal(bot))
|
||||
distance = 30.0f;
|
||||
else
|
||||
distance = 5.0f;
|
||||
|
||||
float posX = helper.center.first + cos(angle) * distance;
|
||||
float posY = helper.center.second + sin(angle) * distance;
|
||||
if (MoveTo(NAXX_MAP_ID, posX, posY, helper.GENERIC_HEIGHT, false, false, false, false, MovementPriority::MOVEMENT_COMBAT))
|
||||
return true;
|
||||
|
||||
return MoveInside(NAXX_MAP_ID, posX, posY, helper.GENERIC_HEIGHT, 2.0f, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<float> dest;
|
||||
if (helper.FindPosToAvoidChill(dest))
|
||||
return MoveTo(NAXX_MAP_ID, dest[0], dest[1], dest[2], false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SapphironFlightPositionAction::Execute(Event /*event*/)
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
return false;
|
||||
|
||||
if (helper.WaitForExplosion())
|
||||
return MoveToNearestIcebolt();
|
||||
else
|
||||
{
|
||||
std::vector<float> dest;
|
||||
if (helper.FindPosToAvoidChill(dest))
|
||||
return MoveTo(NAXX_MAP_ID, dest[0], dest[1], dest[2], false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SapphironFlightPositionAction::MoveToNearestIcebolt()
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
Group::MemberSlotList const& slots = group->GetMemberSlots();
|
||||
Player* playerWithIcebolt = nullptr;
|
||||
float minDistance;
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (NaxxSpellIds::HasAnyAura(botAI, member, {NaxxSpellIds::Icebolt10, NaxxSpellIds::Icebolt25}) ||
|
||||
botAI->HasAura("icebolt", member, false, false, -1, true))
|
||||
{
|
||||
if (!playerWithIcebolt || minDistance > bot->GetDistance(member))
|
||||
{
|
||||
playerWithIcebolt = member;
|
||||
minDistance = bot->GetDistance(member);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (playerWithIcebolt)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "sapphiron");
|
||||
if (boss)
|
||||
{
|
||||
float angle = boss->GetAngle(playerWithIcebolt);
|
||||
float posX = playerWithIcebolt->GetPositionX() + cos(angle) * 3.0f;
|
||||
float posY = playerWithIcebolt->GetPositionY() + sin(angle) * 3.0f;
|
||||
if (MoveTo(NAXX_MAP_ID, posX, posY, helper.GENERIC_HEIGHT, false, false, false, false, MovementPriority::MOVEMENT_COMBAT))
|
||||
return true;
|
||||
|
||||
return MoveNear(playerWithIcebolt, 3.0f, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
18
src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Shared.cpp
Normal file
18
src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Shared.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#include "RaidNaxxActions.h"
|
||||
|
||||
uint32 RotateAroundTheCenterPointAction::FindNearestWaypoint()
|
||||
{
|
||||
float minDistance = 0;
|
||||
int ret = -1;
|
||||
for (int i = 0; i < intervals; i++)
|
||||
{
|
||||
float w_x = waypoints[i].first, w_y = waypoints[i].second;
|
||||
float dis = bot->GetDistance2d(w_x, w_y);
|
||||
if (ret == -1 || dis < minDistance)
|
||||
{
|
||||
ret = i;
|
||||
minDistance = dis;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
134
src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Thaddius.cpp
Normal file
134
src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Thaddius.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
#include "RaidNaxxActions.h"
|
||||
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "Playerbots.h"
|
||||
#include "RaidNaxxSpellIds.h"
|
||||
|
||||
bool ThaddiusAttackNearestPetAction::isUseful()
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
return false;
|
||||
|
||||
if (!helper.IsPhasePet())
|
||||
return false;
|
||||
|
||||
Unit* target = helper.GetNearestPet();
|
||||
if (!bot->IsWithinDistInMap(target, 50.0f))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ThaddiusAttackNearestPetAction::Execute(Event /*event*/)
|
||||
{
|
||||
Unit* target = helper.GetNearestPet();
|
||||
if (!bot->IsWithinLOSInMap(target))
|
||||
return MoveTo(target, 0, MovementPriority::MOVEMENT_COMBAT);
|
||||
|
||||
if (AI_VALUE(Unit*, "current target") != target)
|
||||
return Attack(target);
|
||||
|
||||
if (botAI->IsTank(bot) && AI_VALUE2(bool, "has aggro", "current target"))
|
||||
{
|
||||
std::pair<float, float> posForTank = helper.PetPhaseGetPosForTank();
|
||||
return MoveTo(533, posForTank.first, posForTank.second, helper.tankPosZ, false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
if (botAI->IsRanged(bot))
|
||||
{
|
||||
std::pair<float, float> posForRanged = helper.PetPhaseGetPosForRanged();
|
||||
return MoveTo(533, posForRanged.first, posForRanged.second, helper.tankPosZ, false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ThaddiusMoveToPlatformAction::isUseful() { return true; }
|
||||
|
||||
bool ThaddiusMoveToPlatformAction::Execute(Event /*event*/)
|
||||
{
|
||||
std::vector<std::pair<float, float>> position = {
|
||||
// high left
|
||||
{3462.99f, -2918.90f},
|
||||
// high right
|
||||
{3520.65f, -2976.51f},
|
||||
// low left
|
||||
{3471.36f, -2910.65f},
|
||||
// low right
|
||||
{3528.80f, -2967.04f},
|
||||
// center
|
||||
{3512.19f, -2928.58f},
|
||||
};
|
||||
float high_z = 312.00f, low_z = 304.02f;
|
||||
bool is_left = bot->GetDistance2d(position[0].first, position[0].second) <
|
||||
bot->GetDistance2d(position[1].first, position[1].second);
|
||||
if (bot->GetPositionZ() >= (high_z - 3.0f))
|
||||
{
|
||||
if (is_left)
|
||||
{
|
||||
if (!MoveTo(bot->GetMapId(), position[0].first, position[0].second, high_z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT))
|
||||
{
|
||||
float distance = bot->GetExactDist2d(position[0].first, position[0].second);
|
||||
if (distance < sPlayerbotAIConfig.contactDistance)
|
||||
JumpTo(bot->GetMapId(), position[2].first, position[2].second, low_z, MovementPriority::MOVEMENT_COMBAT);
|
||||
// bot->TeleportTo(bot->GetMapId(), position[2].first, position[2].second, low_z, bot->GetOrientation());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!MoveTo(bot->GetMapId(), position[1].first, position[1].second, high_z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT))
|
||||
{
|
||||
float distance = bot->GetExactDist2d(position[1].first, position[1].second);
|
||||
if (distance < sPlayerbotAIConfig.contactDistance)
|
||||
JumpTo(bot->GetMapId(), position[3].first, position[3].second, low_z, MovementPriority::MOVEMENT_COMBAT);
|
||||
// bot->TeleportTo(bot->GetMapId(), position[3].first, position[3].second, low_z, bot->GetOrientation());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
return MoveTo(bot->GetMapId(), position[4].first, position[4].second, low_z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ThaddiusMovePolarityAction::isUseful()
|
||||
{
|
||||
return !botAI->IsMainTank(bot) || AI_VALUE2(bool, "has aggro", "current target");
|
||||
}
|
||||
|
||||
bool ThaddiusMovePolarityAction::Execute(Event /*event*/)
|
||||
{
|
||||
std::vector<std::pair<float, float>> position = {
|
||||
// left melee
|
||||
{3508.29f, -2920.12f},
|
||||
// left ranged
|
||||
{3501.72f, -2913.36f},
|
||||
// right melee
|
||||
{3519.74f, -2931.69f},
|
||||
// right ranged
|
||||
{3524.32f, -2936.26f},
|
||||
// center melee
|
||||
{3512.19f, -2928.58f},
|
||||
// center ranged
|
||||
{3504.68f, -2936.68f},
|
||||
};
|
||||
uint32 idx;
|
||||
if (NaxxSpellIds::HasAnyAura(
|
||||
botAI, bot,
|
||||
{NaxxSpellIds::NegativeCharge10, NaxxSpellIds::NegativeCharge25, NaxxSpellIds::NegativeChargeStack}) ||
|
||||
botAI->HasAura("negative charge", bot, false, false, -1, true))
|
||||
{
|
||||
idx = 0;
|
||||
}
|
||||
else if (NaxxSpellIds::HasAnyAura(
|
||||
botAI, bot,
|
||||
{NaxxSpellIds::PositiveCharge10, NaxxSpellIds::PositiveCharge25, NaxxSpellIds::PositiveChargeStack}) ||
|
||||
botAI->HasAura("positive charge", bot, false, false, -1, true))
|
||||
{
|
||||
idx = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
idx = 2;
|
||||
}
|
||||
idx = idx * 2 + botAI->IsRanged(bot);
|
||||
return MoveTo(bot->GetMapId(), position[idx].first, position[idx].second, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
322
src/Ai/Raid/Naxxramas/Multiplier/RaidNaxxMultipliers.cpp
Normal file
322
src/Ai/Raid/Naxxramas/Multiplier/RaidNaxxMultipliers.cpp
Normal file
@@ -0,0 +1,322 @@
|
||||
#include "RaidNaxxMultipliers.h"
|
||||
|
||||
#include "ChooseTargetActions.h"
|
||||
#include "DKActions.h"
|
||||
#include "DruidActions.h"
|
||||
#include "DruidBearActions.h"
|
||||
#include "FollowActions.h"
|
||||
#include "GenericActions.h"
|
||||
#include "GenericSpellActions.h"
|
||||
#include "HunterActions.h"
|
||||
#include "MageActions.h"
|
||||
#include "MovementActions.h"
|
||||
#include "PaladinActions.h"
|
||||
#include "PriestActions.h"
|
||||
#include "RaidNaxxActions.h"
|
||||
#include "RaidNaxxSpellIds.h"
|
||||
#include "ReachTargetActions.h"
|
||||
#include "RogueActions.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "ShamanActions.h"
|
||||
#include "Spell.h"
|
||||
#include "UseMeetingStoneAction.h"
|
||||
#include "WarriorActions.h"
|
||||
|
||||
float GrobbulusMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "grobbulus");
|
||||
if (!boss)
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<AvoidAoeAction*>(action))
|
||||
return botAI->IsMainTank(bot) ? 0.0f : 1.0f;
|
||||
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
//float HeiganDanceMultiplier::GetValue(Action* action)
|
||||
//{
|
||||
// Unit* boss = AI_VALUE2(Unit*, "find target", "heigan the unclean");
|
||||
// if (!boss)
|
||||
// {
|
||||
// return 1.0f;
|
||||
// }
|
||||
// bool platform_phase = boss->IsWithinDist2d(2794.26f, -3706.67f, 10.0f);
|
||||
// bool eruption_casting = false;
|
||||
// if (boss->HasUnitState(UNIT_STATE_CASTING))
|
||||
// {
|
||||
// Spell* spell = boss->GetCurrentSpell(CURRENT_GENERIC_SPELL);
|
||||
// if (!spell)
|
||||
// {
|
||||
// spell = boss->GetCurrentSpell(CURRENT_CHANNELED_SPELL);
|
||||
// }
|
||||
// if (spell)
|
||||
// {
|
||||
// SpellInfo const* info = spell->GetSpellInfo();
|
||||
// bool isEruption = NaxxSpellIds::MatchesAnySpellId(info, {NaxxSpellIds::Eruption10});
|
||||
// if (!isEruption && info && info->SpellName[LOCALE_enUS])
|
||||
// {
|
||||
// // Fallback to name for custom spell data.
|
||||
// isEruption = botAI->EqualLowercaseName(info->SpellName[LOCALE_enUS], "eruption");
|
||||
// }
|
||||
// if (isEruption)
|
||||
// {
|
||||
// eruption_casting = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||
// dynamic_cast<CastDisengageAction*>(action) ||
|
||||
// dynamic_cast<CastBlinkBackAction*>(action) )
|
||||
// {
|
||||
// return 0.0f;
|
||||
// }
|
||||
// if (!platform_phase && !eruption_casting)
|
||||
// {
|
||||
// return 1.0f;
|
||||
// }
|
||||
// if (dynamic_cast<HeiganDanceAction*>(action) || dynamic_cast<CurePartyMemberAction*>(action))
|
||||
// {
|
||||
// return 1.0f;
|
||||
// }
|
||||
// if (dynamic_cast<CastSpellAction*>(action) && !dynamic_cast<CastMeleeSpellAction*>(action))
|
||||
// {
|
||||
// CastSpellAction* spellAction = dynamic_cast<CastSpellAction*>(action);
|
||||
// uint32 spellId = AI_VALUE2(uint32, "spell id", spellAction->getSpell());
|
||||
// SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||
// if (!spellInfo)
|
||||
// {
|
||||
// return 0.0f;
|
||||
// }
|
||||
// uint32 castTime = spellInfo->CalcCastTime();
|
||||
// if (castTime == 0 && !spellInfo->IsChanneled())
|
||||
// {
|
||||
// return 1.0f;
|
||||
// }
|
||||
// }
|
||||
// return 0.0f;
|
||||
//}
|
||||
|
||||
float LoathebGenericMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "loatheb");
|
||||
if (!boss)
|
||||
return 1.0f;
|
||||
|
||||
context->GetValue<bool>("neglect threat")->Set(true);
|
||||
if (botAI->GetState() == BOT_STATE_COMBAT &&
|
||||
(dynamic_cast<DpsAssistAction*>(action) || dynamic_cast<TankAssistAction*>(action) ||
|
||||
dynamic_cast<CastDebuffSpellOnAttackerAction*>(action) || dynamic_cast<FleeAction*>(action) ||
|
||||
dynamic_cast<CombatFormationMoveAction*>(action)))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
if (!dynamic_cast<CastHealingSpellAction*>(action))
|
||||
return 1.0f;
|
||||
|
||||
Aura* aura = NaxxSpellIds::GetAnyAura(bot, {NaxxSpellIds::NecroticAura10});
|
||||
if (!aura)
|
||||
{
|
||||
// Fallback to name for custom spell data.
|
||||
aura = botAI->GetAura("necrotic aura", bot);
|
||||
}
|
||||
if (!aura || aura->GetDuration() <= 1500)
|
||||
return 1.0f;
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float ThaddiusGenericMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action))
|
||||
return 0.0f;
|
||||
// pet phase
|
||||
if (helper.IsPhasePet() &&
|
||||
(dynamic_cast<DpsAssistAction*>(action) || dynamic_cast<TankAssistAction*>(action) ||
|
||||
dynamic_cast<CastDebuffSpellOnAttackerAction*>(action) ||
|
||||
dynamic_cast<ReachPartyMemberToHealAction*>(action) || dynamic_cast<BuffOnMainTankAction*>(action)))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
// die at the same time
|
||||
Unit* target = AI_VALUE(Unit*, "current target");
|
||||
Unit* feugen = AI_VALUE2(Unit*, "find target", "feugen");
|
||||
Unit* stalagg = AI_VALUE2(Unit*, "find target", "stalagg");
|
||||
if (helper.IsPhasePet() && target && feugen && stalagg && target->GetHealthPct() <= 40 &&
|
||||
(feugen->GetHealthPct() >= target->GetHealthPct() + 3 || stalagg->GetHealthPct() >= target->GetHealthPct() + 3))
|
||||
{
|
||||
if (dynamic_cast<CastSpellAction*>(action) && !dynamic_cast<CastHealingSpellAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
// magnetic pull
|
||||
// uint32 curr_timer = eventMap->GetTimer();
|
||||
// // if (curr_phase == 2 && bot->GetPositionZ() > 312.5f && dynamic_cast<MovementAction*>(action))
|
||||
// {
|
||||
// if (curr_phase == 2 && (curr_timer % 20000 >= 18000 || curr_timer % 20000 <= 2000) &&
|
||||
// dynamic_cast<MovementAction*>(action))
|
||||
// {
|
||||
// // MotionMaster *mm = bot->GetMotionMaster();
|
||||
// // mm->Clear();
|
||||
// return 0.0f;
|
||||
// }
|
||||
// thaddius phase
|
||||
// if (curr_phase == 8 && dynamic_cast<FleeAction*>(action))
|
||||
// {
|
||||
// return 0.0f;
|
||||
// }
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float SapphironGenericMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<CastDeathGripAction*>(action) || dynamic_cast<CombatFormationMoveAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float InstructorRazuviousGenericMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
return 1.0f;
|
||||
|
||||
context->GetValue<bool>("neglect threat")->Set(true);
|
||||
if (botAI->GetState() == BOT_STATE_COMBAT &&
|
||||
(dynamic_cast<DpsAssistAction*>(action) || dynamic_cast<TankAssistAction*>(action) ||
|
||||
dynamic_cast<CastTauntAction*>(action) || dynamic_cast<CastDarkCommandAction*>(action) ||
|
||||
dynamic_cast<CastHandOfReckoningAction*>(action) || dynamic_cast<CastGrowlAction*>(action)))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float KelthuzadGenericMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
return 1.0f;
|
||||
|
||||
if ((dynamic_cast<DpsAssistAction*>(action) || dynamic_cast<TankAssistAction*>(action) ||
|
||||
dynamic_cast<CastDebuffSpellOnAttackerAction*>(action) || dynamic_cast<FleeAction*>(action)))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
if (helper.IsPhaseOne())
|
||||
{
|
||||
if (dynamic_cast<CastTotemAction*>(action) || dynamic_cast<CastShadowfiendAction*>(action) ||
|
||||
dynamic_cast<CastRaiseDeadAction*>(action) || dynamic_cast<CastFeignDeathAction*>(action) ||
|
||||
dynamic_cast<CastInvisibilityAction*>(action) || dynamic_cast<CastVanishAction*>(action) ||
|
||||
dynamic_cast<PetAttackAction*>(action))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
if (helper.IsPhaseTwo())
|
||||
{
|
||||
if (dynamic_cast<CastBlizzardAction*>(action) || dynamic_cast<CastFrostNovaAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float AnubrekhanGenericMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "anub'rekhan");
|
||||
if (!boss)
|
||||
return 1.0f;
|
||||
|
||||
if (NaxxSpellIds::HasAnyAura(
|
||||
botAI, boss, {NaxxSpellIds::LocustSwarm10, NaxxSpellIds::LocustSwarm10Alt, NaxxSpellIds::LocustSwarm25}) ||
|
||||
botAI->HasAura("locust swarm", boss))
|
||||
{
|
||||
if (dynamic_cast<FleeAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float FourhorsemanGenericMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "sir zeliek");
|
||||
if (!boss)
|
||||
return 1.0f;
|
||||
|
||||
context->GetValue<bool>("neglect threat")->Set(true);
|
||||
if ((dynamic_cast<DpsAssistAction*>(action) || dynamic_cast<TankAssistAction*>(action)))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// float GothikGenericMultiplier::GetValue(Action* action)
|
||||
// {
|
||||
// Unit* boss = AI_VALUE2(Unit*, "find target", "gothik the harvester");
|
||||
// if (!boss)
|
||||
// {
|
||||
// return 1.0f;
|
||||
// }
|
||||
// BossAI* boss_ai = dynamic_cast<BossAI*>(boss->GetAI());
|
||||
// EventMap* eventMap = boss_botAI->GetEvents();
|
||||
// uint32 curr_phase = eventMap->GetPhaseMask();
|
||||
// if (curr_phase == 1 && (dynamic_cast<FollowAction*>(action)))
|
||||
// {
|
||||
// return 0.0f;
|
||||
// }
|
||||
// if (curr_phase == 1 && (dynamic_cast<AttackAction*>(action)))
|
||||
// {
|
||||
// Unit* target = action->GetTarget();
|
||||
// if (target == boss)
|
||||
// {
|
||||
// return 0.0f;
|
||||
// }
|
||||
// }
|
||||
// return 1.0f;
|
||||
// }
|
||||
|
||||
float GluthGenericMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
return 1.0f;
|
||||
|
||||
if ((dynamic_cast<DpsAssistAction*>(action) || dynamic_cast<TankAssistAction*>(action) ||
|
||||
dynamic_cast<FleeAction*>(action) || dynamic_cast<CastDebuffSpellOnAttackerAction*>(action) ||
|
||||
dynamic_cast<CastStarfallAction*>(action)))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (botAI->IsMainTank(bot))
|
||||
{
|
||||
Aura* aura = NaxxSpellIds::GetAnyAura(bot, {NaxxSpellIds::MortalWound10, NaxxSpellIds::MortalWound25});
|
||||
if (!aura)
|
||||
{
|
||||
// Fallback to name for custom spell data.
|
||||
aura = botAI->GetAura("mortal wound", bot, false, true);
|
||||
}
|
||||
if (aura && aura->GetStackAmount() >= 5)
|
||||
{
|
||||
if (dynamic_cast<CastTauntAction*>(action) || dynamic_cast<CastDarkCommandAction*>(action) ||
|
||||
dynamic_cast<CastHandOfReckoningAction*>(action) || dynamic_cast<CastGrowlAction*>(action))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dynamic_cast<PetAttackAction*>(action))
|
||||
{
|
||||
Unit* target = AI_VALUE(Unit*, "current target");
|
||||
if (helper.IsZombieChow(target))
|
||||
return 0.0f;
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
115
src/Ai/Raid/Naxxramas/Multiplier/RaidNaxxMultipliers.h
Normal file
115
src/Ai/Raid/Naxxramas/Multiplier/RaidNaxxMultipliers.h
Normal file
@@ -0,0 +1,115 @@
|
||||
|
||||
#ifndef _PLAYERRBOT_RAIDNAXXMULTIPLIERS_H
|
||||
#define _PLAYERRBOT_RAIDNAXXMULTIPLIERS_H
|
||||
|
||||
#include "Multiplier.h"
|
||||
#include "RaidNaxxBossHelper.h"
|
||||
|
||||
class GrobbulusMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
GrobbulusMultiplier(PlayerbotAI* ai) : Multiplier(ai, "grobbulus") {}
|
||||
|
||||
public:
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
//class HeiganDanceMultiplier : public Multiplier
|
||||
//{
|
||||
//public:
|
||||
// HeiganDanceMultiplier(PlayerbotAI* ai) : Multiplier(ai, "helgan dance") {}
|
||||
//
|
||||
//public:
|
||||
// virtual float GetValue(Action* action);
|
||||
//};
|
||||
|
||||
class LoathebGenericMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
LoathebGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "loatheb generic") {}
|
||||
|
||||
public:
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class ThaddiusGenericMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
ThaddiusGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "thaddius generic"), helper(ai) {}
|
||||
|
||||
public:
|
||||
virtual float GetValue(Action* action);
|
||||
|
||||
private:
|
||||
ThaddiusBossHelper helper;
|
||||
};
|
||||
|
||||
class SapphironGenericMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
SapphironGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "sapphiron generic"), helper(ai) {}
|
||||
|
||||
virtual float GetValue(Action* action);
|
||||
|
||||
private:
|
||||
SapphironBossHelper helper;
|
||||
};
|
||||
|
||||
class InstructorRazuviousGenericMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
InstructorRazuviousGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "instructor razuvious generic"), helper(ai) {}
|
||||
virtual float GetValue(Action* action);
|
||||
|
||||
private:
|
||||
RazuviousBossHelper helper;
|
||||
};
|
||||
|
||||
class KelthuzadGenericMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
KelthuzadGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "kelthuzad generic"), helper(ai) {}
|
||||
virtual float GetValue(Action* action);
|
||||
|
||||
private:
|
||||
KelthuzadBossHelper helper;
|
||||
};
|
||||
|
||||
class AnubrekhanGenericMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
AnubrekhanGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "anubrekhan generic") {}
|
||||
|
||||
public:
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class FourhorsemanGenericMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
FourhorsemanGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "fourhorseman generic") {}
|
||||
|
||||
public:
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
// class GothikGenericMultiplier : public Multiplier
|
||||
// {
|
||||
// public:
|
||||
// GothikGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "gothik generic") {}
|
||||
|
||||
// public:
|
||||
// virtual float GetValue(Action* action);
|
||||
// };
|
||||
|
||||
class GluthGenericMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
GluthGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "gluth generic"), helper(ai) {}
|
||||
float GetValue(Action* action) override;
|
||||
|
||||
private:
|
||||
GluthBossHelper helper;
|
||||
};
|
||||
|
||||
#endif
|
||||
95
src/Ai/Raid/Naxxramas/RaidNaxxActionContext.h
Normal file
95
src/Ai/Raid/Naxxramas/RaidNaxxActionContext.h
Normal file
@@ -0,0 +1,95 @@
|
||||
// /*
|
||||
// * 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_RAIDNAXXACTIONCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDNAXXACTIONCONTEXT_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidNaxxActions.h"
|
||||
|
||||
class RaidNaxxActionContext : public NamedObjectContext<Action>
|
||||
{
|
||||
public:
|
||||
RaidNaxxActionContext()
|
||||
{
|
||||
creators["grobbulus go behind the boss"] = &RaidNaxxActionContext::go_behind_the_boss;
|
||||
creators["rotate grobbulus"] = &RaidNaxxActionContext::rotate_grobbulus;
|
||||
creators["grobbulus move center"] = &RaidNaxxActionContext::grobbulus_move_center;
|
||||
creators["grobbulus move away"] = &RaidNaxxActionContext::grobbulus_move_away;
|
||||
|
||||
//creators["heigan dance melee"] = &RaidNaxxActionContext::heigan_dance_melee;
|
||||
//creators["heigan dance ranged"] = &RaidNaxxActionContext::heigan_dance_ranged;
|
||||
creators["thaddius attack nearest pet"] = &RaidNaxxActionContext::thaddius_attack_nearest_pet;
|
||||
// creators["thaddius melee to place"] = &RaidNaxxActionContext::thaddius_tank_to_place;
|
||||
// creators["thaddius ranged to place"] = &RaidNaxxActionContext::thaddius_ranged_to_place;
|
||||
creators["thaddius move to platform"] = &RaidNaxxActionContext::thaddius_move_to_platform;
|
||||
creators["thaddius move polarity"] = &RaidNaxxActionContext::thaddius_move_polarity;
|
||||
|
||||
creators["razuvious use obedience crystal"] = &RaidNaxxActionContext::razuvious_use_obedience_crystal;
|
||||
creators["razuvious target"] = &RaidNaxxActionContext::razuvious_target;
|
||||
|
||||
creators["horseman attract alternatively"] = &RaidNaxxActionContext::horseman_attract_alternatively;
|
||||
creators["horseman attack in order"] = &RaidNaxxActionContext::horseman_attack_in_order;
|
||||
|
||||
creators["sapphiron ground position"] = &RaidNaxxActionContext::sapphiron_ground_position;
|
||||
creators["sapphiron flight position"] = &RaidNaxxActionContext::sapphiron_flight_position;
|
||||
|
||||
creators["kel'thuzad choose target"] = &RaidNaxxActionContext::kelthuzad_choose_target;
|
||||
creators["kel'thuzad position"] = &RaidNaxxActionContext::kelthuzad_position;
|
||||
|
||||
creators["anub'rekhan choose target"] = &RaidNaxxActionContext::anubrekhan_choose_target;
|
||||
creators["anub'rekhan position"] = &RaidNaxxActionContext::anubrekhan_position;
|
||||
|
||||
creators["gluth choose target"] = &RaidNaxxActionContext::gluth_choose_target;
|
||||
creators["gluth position"] = &RaidNaxxActionContext::gluth_position;
|
||||
creators["gluth slowdown"] = &RaidNaxxActionContext::gluth_slowdown;
|
||||
|
||||
//creators["patchwerk ranged position"] = &RaidNaxxActionContext::patchwerk_ranged_position;
|
||||
|
||||
creators["loatheb position"] = &RaidNaxxActionContext::loatheb_position;
|
||||
creators["loatheb choose target"] = &RaidNaxxActionContext::loatheb_choose_target;
|
||||
}
|
||||
|
||||
private:
|
||||
static Action* go_behind_the_boss(PlayerbotAI* ai) { return new GrobbulusGoBehindAction(ai); }
|
||||
static Action* rotate_grobbulus(PlayerbotAI* ai) { return new GrobbulusRotateAction(ai); }
|
||||
static Action* grobbulus_move_center(PlayerbotAI* ai) { return new GrobblulusMoveCenterAction(ai); }
|
||||
static Action* grobbulus_move_away(PlayerbotAI* ai) { return new GrobbulusMoveAwayAction(ai); }
|
||||
//static Action* heigan_dance_melee(PlayerbotAI* ai) { return new HeiganDanceMeleeAction(ai); }
|
||||
//static Action* heigan_dance_ranged(PlayerbotAI* ai) { return new HeiganDanceRangedAction(ai); }
|
||||
static Action* thaddius_attack_nearest_pet(PlayerbotAI* ai) { return new ThaddiusAttackNearestPetAction(ai); }
|
||||
// static Action* thaddius_tank_to_place(PlayerbotAI* ai) { return new ThaddiusMeleeToPlaceAction(ai); }
|
||||
// static Action* thaddius_ranged_to_place(PlayerbotAI* ai) { return new ThaddiusRangedToPlaceAction(ai); }
|
||||
static Action* thaddius_move_to_platform(PlayerbotAI* ai) { return new ThaddiusMoveToPlatformAction(ai); }
|
||||
static Action* thaddius_move_polarity(PlayerbotAI* ai) { return new ThaddiusMovePolarityAction(ai); }
|
||||
static Action* razuvious_target(PlayerbotAI* ai) { return new RazuviousTargetAction(ai); }
|
||||
static Action* razuvious_use_obedience_crystal(PlayerbotAI* ai)
|
||||
{
|
||||
return new RazuviousUseObedienceCrystalAction(ai);
|
||||
}
|
||||
static Action* horseman_attract_alternatively(PlayerbotAI* ai)
|
||||
{
|
||||
return new HorsemanAttractAlternativelyAction(ai);
|
||||
}
|
||||
static Action* horseman_attack_in_order(PlayerbotAI* ai) { return new HorsemanAttactInOrderAction(ai); }
|
||||
// static Action* sapphiron_ground_main_tank_position(PlayerbotAI* ai) { return new
|
||||
// SapphironGroundMainTankPositionAction(ai); }
|
||||
static Action* sapphiron_ground_position(PlayerbotAI* ai) { return new SapphironGroundPositionAction(ai); }
|
||||
static Action* sapphiron_flight_position(PlayerbotAI* ai) { return new SapphironFlightPositionAction(ai); }
|
||||
// static Action* sapphiron_avoid_chill(PlayerbotAI* ai) { return new SapphironAvoidChillAction(ai); }
|
||||
static Action* kelthuzad_choose_target(PlayerbotAI* ai) { return new KelthuzadChooseTargetAction(ai); }
|
||||
static Action* kelthuzad_position(PlayerbotAI* ai) { return new KelthuzadPositionAction(ai); }
|
||||
static Action* anubrekhan_choose_target(PlayerbotAI* ai) { return new AnubrekhanChooseTargetAction(ai); }
|
||||
static Action* anubrekhan_position(PlayerbotAI* ai) { return new AnubrekhanPositionAction(ai); }
|
||||
static Action* gluth_choose_target(PlayerbotAI* ai) { return new GluthChooseTargetAction(ai); }
|
||||
static Action* gluth_position(PlayerbotAI* ai) { return new GluthPositionAction(ai); }
|
||||
static Action* gluth_slowdown(PlayerbotAI* ai) { return new GluthSlowdownAction(ai); }
|
||||
//static Action* patchwerk_ranged_position(PlayerbotAI* ai) { return new PatchwerkRangedPositionAction(ai); }
|
||||
static Action* loatheb_position(PlayerbotAI* ai) { return new LoathebPositionAction(ai); }
|
||||
static Action* loatheb_choose_target(PlayerbotAI* ai) { return new LoathebChooseTargetAction(ai); }
|
||||
};
|
||||
|
||||
#endif
|
||||
86
src/Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h
Normal file
86
src/Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h
Normal file
@@ -0,0 +1,86 @@
|
||||
// /*
|
||||
// * 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_RAIDNAXXTRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDNAXXTRIGGERCONTEXT_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidNaxxTriggers.h"
|
||||
|
||||
class RaidNaxxTriggerContext : public NamedObjectContext<Trigger>
|
||||
{
|
||||
public:
|
||||
RaidNaxxTriggerContext()
|
||||
{
|
||||
creators["mutating injection melee"] = &RaidNaxxTriggerContext::mutating_injection_melee;
|
||||
creators["mutating injection ranged"] = &RaidNaxxTriggerContext::mutating_injection_ranged;
|
||||
creators["mutating injection removed"] = &RaidNaxxTriggerContext::mutating_injection_removed;
|
||||
creators["grobbulus cloud"] = &RaidNaxxTriggerContext::grobbulus_cloud;
|
||||
//creators["heigan melee"] = &RaidNaxxTriggerContext::heigan_melee;
|
||||
//creators["heigan ranged"] = &RaidNaxxTriggerContext::heigan_ranged;
|
||||
|
||||
creators["thaddius phase pet"] = &RaidNaxxTriggerContext::thaddius_phase_pet;
|
||||
creators["thaddius phase pet lose aggro"] = &RaidNaxxTriggerContext::thaddius_phase_pet_lose_aggro;
|
||||
creators["thaddius phase transition"] = &RaidNaxxTriggerContext::thaddius_phase_transition;
|
||||
creators["thaddius phase thaddius"] = &RaidNaxxTriggerContext::thaddius_phase_thaddius;
|
||||
|
||||
creators["razuvious tank"] = &RaidNaxxTriggerContext::razuvious_tank;
|
||||
creators["razuvious nontank"] = &RaidNaxxTriggerContext::razuvious_nontank;
|
||||
|
||||
creators["horseman attractors"] = &RaidNaxxTriggerContext::horseman_attractors;
|
||||
creators["horseman except attractors"] = &RaidNaxxTriggerContext::horseman_except_attractors;
|
||||
|
||||
creators["sapphiron ground"] = &RaidNaxxTriggerContext::sapphiron_ground;
|
||||
creators["sapphiron flight"] = &RaidNaxxTriggerContext::sapphiron_flight;
|
||||
|
||||
creators["kel'thuzad"] = &RaidNaxxTriggerContext::kelthuzad;
|
||||
|
||||
creators["anub'rekhan"] = &RaidNaxxTriggerContext::anubrekhan;
|
||||
creators["faerlina"] = &RaidNaxxTriggerContext::faerlina;
|
||||
creators["maexxna"] = &RaidNaxxTriggerContext::maexxna;
|
||||
//creators["patchwerk tank"] = &RaidNaxxTriggerContext::patchwerk_tank;
|
||||
//creators["patchwerk non-tank"] = &RaidNaxxTriggerContext::patchwerk_non_tank;
|
||||
//creators["patchwerk ranged"] = &RaidNaxxTriggerContext::patchwerk_ranged;
|
||||
|
||||
creators["gluth"] = &RaidNaxxTriggerContext::gluth;
|
||||
creators["gluth main tank mortal wound"] = &RaidNaxxTriggerContext::gluth_main_tank_mortal_wound;
|
||||
|
||||
creators["loatheb"] = &RaidNaxxTriggerContext::loatheb;
|
||||
}
|
||||
|
||||
private:
|
||||
static Trigger* mutating_injection_melee(PlayerbotAI* ai) { return new MutatingInjectionMeleeTrigger(ai); }
|
||||
static Trigger* mutating_injection_ranged(PlayerbotAI* ai) { return new MutatingInjectionRangedTrigger(ai); }
|
||||
static Trigger* mutating_injection_removed(PlayerbotAI* ai) { return new MutatingInjectionRemovedTrigger(ai); }
|
||||
static Trigger* grobbulus_cloud(PlayerbotAI* ai) { return new GrobbulusCloudTrigger(ai); }
|
||||
//static Trigger* heigan_melee(PlayerbotAI* ai) { return new HeiganMeleeTrigger(ai); }
|
||||
//static Trigger* heigan_ranged(PlayerbotAI* ai) { return new HeiganRangedTrigger(ai); }
|
||||
|
||||
static Trigger* thaddius_phase_pet(PlayerbotAI* ai) { return new ThaddiusPhasePetTrigger(ai); }
|
||||
static Trigger* thaddius_phase_pet_lose_aggro(PlayerbotAI* ai) { return new ThaddiusPhasePetLoseAggroTrigger(ai); }
|
||||
static Trigger* thaddius_phase_transition(PlayerbotAI* ai) { return new ThaddiusPhaseTransitionTrigger(ai); }
|
||||
static Trigger* thaddius_phase_thaddius(PlayerbotAI* ai) { return new ThaddiusPhaseThaddiusTrigger(ai); }
|
||||
static Trigger* razuvious_tank(PlayerbotAI* ai) { return new RazuviousTankTrigger(ai); }
|
||||
static Trigger* razuvious_nontank(PlayerbotAI* ai) { return new RazuviousNontankTrigger(ai); }
|
||||
|
||||
static Trigger* horseman_attractors(PlayerbotAI* ai) { return new HorsemanAttractorsTrigger(ai); }
|
||||
static Trigger* horseman_except_attractors(PlayerbotAI* ai) { return new HorsemanExceptAttractorsTrigger(ai); }
|
||||
|
||||
static Trigger* sapphiron_ground(PlayerbotAI* ai) { return new SapphironGroundTrigger(ai); }
|
||||
static Trigger* sapphiron_flight(PlayerbotAI* ai) { return new SapphironFlightTrigger(ai); }
|
||||
static Trigger* kelthuzad(PlayerbotAI* ai) { return new KelthuzadTrigger(ai); }
|
||||
static Trigger* anubrekhan(PlayerbotAI* ai) { return new AnubrekhanTrigger(ai); }
|
||||
static Trigger* faerlina(PlayerbotAI* ai) { return new FaerlinaTrigger(ai); }
|
||||
static Trigger* maexxna(PlayerbotAI* ai) { return new MaexxnaTrigger(ai); }
|
||||
//static Trigger* patchwerk_tank(PlayerbotAI* ai) { return new PatchwerkTankTrigger(ai); }
|
||||
//static Trigger* patchwerk_non_tank(PlayerbotAI* ai) { return new PatchwerkNonTankTrigger(ai); }
|
||||
//static Trigger* patchwerk_ranged(PlayerbotAI* ai) { return new PatchwerkRangedTrigger(ai); }
|
||||
static Trigger* gluth(PlayerbotAI* ai) { return new GluthTrigger(ai); }
|
||||
static Trigger* gluth_main_tank_mortal_wound(PlayerbotAI* ai) { return new GluthMainTankMortalWoundTrigger(ai); }
|
||||
static Trigger* loatheb(PlayerbotAI* ai) { return new LoathebTrigger(ai); }
|
||||
};
|
||||
|
||||
#endif
|
||||
156
src/Ai/Raid/Naxxramas/Strategy/RaidNaxxStrategy.cpp
Normal file
156
src/Ai/Raid/Naxxramas/Strategy/RaidNaxxStrategy.cpp
Normal file
@@ -0,0 +1,156 @@
|
||||
#include "RaidNaxxStrategy.h"
|
||||
|
||||
#include "RaidNaxxMultipliers.h"
|
||||
|
||||
void RaidNaxxStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
// Grobbulus
|
||||
triggers.push_back(new TriggerNode("mutating injection melee",
|
||||
{ NextAction("grobbulus move away", ACTION_RAID + 2) }
|
||||
));
|
||||
|
||||
triggers.push_back(new TriggerNode("mutating injection ranged",
|
||||
{ NextAction("grobbulus go behind the boss", ACTION_RAID + 2) }
|
||||
));
|
||||
|
||||
triggers.push_back(new TriggerNode("mutating injection removed",
|
||||
{ NextAction("grobbulus move center", ACTION_RAID + 1) }
|
||||
));
|
||||
|
||||
triggers.push_back(new TriggerNode("grobbulus cloud",
|
||||
{ NextAction("rotate grobbulus", ACTION_RAID + 1) }
|
||||
));
|
||||
|
||||
// Heigan the Unclean
|
||||
//triggers.push_back(new TriggerNode("heigan melee",
|
||||
// { NextAction("heigan dance melee", ACTION_RAID + 1) }
|
||||
//));
|
||||
|
||||
//triggers.push_back(new TriggerNode("heigan ranged",
|
||||
// { NextAction("heigan dance ranged", ACTION_RAID + 1) }
|
||||
//));
|
||||
|
||||
// Kel'Thuzad
|
||||
triggers.push_back(
|
||||
new TriggerNode("kel'thuzad",
|
||||
{
|
||||
NextAction("kel'thuzad position", ACTION_RAID + 2),
|
||||
NextAction("kel'thuzad choose target", ACTION_RAID + 1)
|
||||
})
|
||||
);
|
||||
|
||||
// Anub'Rekhan
|
||||
triggers.push_back(new TriggerNode("anub'rekhan",
|
||||
{ NextAction("anub'rekhan position", ACTION_RAID + 1) }
|
||||
));
|
||||
|
||||
// Grand Widow Faerlina
|
||||
triggers.push_back(new TriggerNode("faerlina",
|
||||
{ NextAction("avoid aoe", ACTION_RAID + 1) }
|
||||
));
|
||||
|
||||
// Maexxna
|
||||
triggers.push_back(
|
||||
new TriggerNode("maexxna",
|
||||
{
|
||||
NextAction("rear flank", ACTION_RAID + 1),
|
||||
NextAction("avoid aoe", ACTION_RAID + 1)
|
||||
})
|
||||
);
|
||||
|
||||
// Patchwerk
|
||||
//triggers.push_back(new TriggerNode("patchwerk tank",
|
||||
// { NextAction("tank face", ACTION_RAID + 2) }
|
||||
//));
|
||||
|
||||
//triggers.push_back(new TriggerNode("patchwerk ranged",
|
||||
// { NextAction("patchwerk ranged position", ACTION_RAID + 2) }
|
||||
//));
|
||||
|
||||
//triggers.push_back(new TriggerNode("patchwerk non-tank",
|
||||
// { NextAction("rear flank", ACTION_RAID + 1) }
|
||||
//));
|
||||
|
||||
// Thaddius
|
||||
triggers.push_back(new TriggerNode("thaddius phase pet",
|
||||
{ NextAction("thaddius attack nearest pet", ACTION_RAID + 1) }
|
||||
));
|
||||
|
||||
triggers.push_back(new TriggerNode("thaddius phase pet lose aggro",
|
||||
{ NextAction("taunt spell", ACTION_RAID + 2) }
|
||||
));
|
||||
|
||||
triggers.push_back(new TriggerNode("thaddius phase transition",
|
||||
{ NextAction("thaddius move to platform", ACTION_RAID + 1) }
|
||||
));
|
||||
|
||||
triggers.push_back(new TriggerNode("thaddius phase thaddius",
|
||||
{ NextAction("thaddius move polarity", ACTION_RAID + 1) }
|
||||
));
|
||||
|
||||
// Instructor Razuvious
|
||||
triggers.push_back(new TriggerNode("razuvious tank",
|
||||
{ NextAction("razuvious use obedience crystal", ACTION_RAID + 1) }
|
||||
));
|
||||
|
||||
triggers.push_back(new TriggerNode("razuvious nontank",
|
||||
{ NextAction("razuvious target", ACTION_RAID + 1) }
|
||||
));
|
||||
|
||||
// four horseman
|
||||
triggers.push_back(new TriggerNode("horseman attractors",
|
||||
{ NextAction("horseman attract alternatively", ACTION_RAID + 1) }
|
||||
));
|
||||
|
||||
triggers.push_back(new TriggerNode("horseman except attractors",
|
||||
{ NextAction("horseman attack in order", ACTION_RAID + 1) }
|
||||
));
|
||||
|
||||
// sapphiron
|
||||
triggers.push_back(new TriggerNode("sapphiron ground",
|
||||
{ NextAction("sapphiron ground position", ACTION_RAID + 1) }
|
||||
));
|
||||
|
||||
triggers.push_back(new TriggerNode("sapphiron flight",
|
||||
{ NextAction("sapphiron flight position", ACTION_RAID + 1) }
|
||||
));
|
||||
|
||||
// Gluth
|
||||
triggers.push_back(
|
||||
new TriggerNode("gluth",
|
||||
{
|
||||
NextAction("gluth choose target", ACTION_RAID + 1),
|
||||
NextAction("gluth position", ACTION_RAID + 1),
|
||||
NextAction("gluth slowdown", ACTION_RAID)
|
||||
})
|
||||
);
|
||||
|
||||
triggers.push_back(new TriggerNode("gluth main tank mortal wound",
|
||||
{ NextAction("taunt spell", ACTION_RAID + 1) }
|
||||
));
|
||||
|
||||
// Loatheb
|
||||
triggers.push_back(
|
||||
new TriggerNode("loatheb",
|
||||
{
|
||||
NextAction("loatheb position", ACTION_RAID + 1),
|
||||
NextAction("loatheb choose target", ACTION_RAID + 1)
|
||||
})
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
void RaidNaxxStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||
{
|
||||
multipliers.push_back(new GrobbulusMultiplier(botAI));
|
||||
//multipliers.push_back(new HeiganDanceMultiplier(botAI));
|
||||
multipliers.push_back(new LoathebGenericMultiplier(botAI));
|
||||
multipliers.push_back(new ThaddiusGenericMultiplier(botAI));
|
||||
multipliers.push_back(new SapphironGenericMultiplier(botAI));
|
||||
multipliers.push_back(new InstructorRazuviousGenericMultiplier(botAI));
|
||||
multipliers.push_back(new KelthuzadGenericMultiplier(botAI));
|
||||
multipliers.push_back(new AnubrekhanGenericMultiplier(botAI));
|
||||
multipliers.push_back(new FourhorsemanGenericMultiplier(botAI));
|
||||
// multipliers.push_back(new GothikGenericMultiplier(botAI));
|
||||
multipliers.push_back(new GluthGenericMultiplier(botAI));
|
||||
}
|
||||
18
src/Ai/Raid/Naxxramas/Strategy/RaidNaxxStrategy.h
Normal file
18
src/Ai/Raid/Naxxramas/Strategy/RaidNaxxStrategy.h
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
#ifndef _PLAYERBOT_RAIDNAXXSTRATEGY_H
|
||||
#define _PLAYERBOT_RAIDNAXXSTRATEGY_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "Multiplier.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
class RaidNaxxStrategy : public Strategy
|
||||
{
|
||||
public:
|
||||
RaidNaxxStrategy(PlayerbotAI* ai) : Strategy(ai) {}
|
||||
virtual std::string const getName() override { return "naxx"; }
|
||||
virtual void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
virtual void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
257
src/Ai/Raid/Naxxramas/Trigger/RaidNaxxTriggers.cpp
Normal file
257
src/Ai/Raid/Naxxramas/Trigger/RaidNaxxTriggers.cpp
Normal file
@@ -0,0 +1,257 @@
|
||||
#include "RaidNaxxTriggers.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
#include "RaidNaxxSpellIds.h"
|
||||
#include "Timer.h"
|
||||
#include "Trigger.h"
|
||||
|
||||
bool MutatingInjectionMeleeTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "grobbulus");
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
return MutatingInjectionTrigger::IsActive() && !botAI->IsRanged(bot);
|
||||
}
|
||||
|
||||
bool MutatingInjectionRangedTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "grobbulus");
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
return MutatingInjectionTrigger::IsActive() && botAI->IsRanged(bot);
|
||||
}
|
||||
|
||||
bool AuraRemovedTrigger::IsActive()
|
||||
{
|
||||
bool check = botAI->HasAura(name, bot, false, false, -1, true);
|
||||
bool ret = false;
|
||||
if (prev_check && !check)
|
||||
ret = true;
|
||||
|
||||
prev_check = check;
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool MutatingInjectionRemovedTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "grobbulus");
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
return HasNoAuraTrigger::IsActive() && botAI->GetState() == BOT_STATE_COMBAT && botAI->IsRanged(bot);
|
||||
}
|
||||
|
||||
bool GrobbulusCloudTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "grobbulus");
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
if (!botAI->IsMainTank(bot))
|
||||
return false;
|
||||
|
||||
// bot->Yell("has aggro on " + boss->GetName() + " : " + to_string(AI_VALUE2(bool, "has aggro", "boss target")),
|
||||
// LANG_UNIVERSAL);
|
||||
if (!AI_VALUE2(bool, "has aggro", "boss target"))
|
||||
return false;
|
||||
|
||||
uint32 now = getMSTime();
|
||||
bool poison_cloud_casting = false;
|
||||
if (boss->HasUnitState(UNIT_STATE_CASTING))
|
||||
{
|
||||
Spell* spell = boss->GetCurrentSpell(CURRENT_GENERIC_SPELL);
|
||||
if (!spell)
|
||||
spell = boss->GetCurrentSpell(CURRENT_CHANNELED_SPELL);
|
||||
|
||||
if (spell)
|
||||
poison_cloud_casting = NaxxSpellIds::MatchesAnySpellId(spell->GetSpellInfo(), {NaxxSpellIds::PoisonCloud});
|
||||
|
||||
}
|
||||
if (!poison_cloud_casting && last_cloud_ms != 0 && now - last_cloud_ms < CloudRotationDelayMs)
|
||||
return false;
|
||||
|
||||
last_cloud_ms = now;
|
||||
return true;
|
||||
}
|
||||
|
||||
//bool HeiganMeleeTrigger::IsActive()
|
||||
//{
|
||||
// Unit* heigan = AI_VALUE2(Unit*, "find target", "heigan the unclean");
|
||||
// if (!heigan)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// return !botAI->IsRanged(bot);
|
||||
//}
|
||||
//
|
||||
//bool HeiganRangedTrigger::IsActive()
|
||||
//{
|
||||
// Unit* heigan = AI_VALUE2(Unit*, "find target", "heigan the unclean");
|
||||
// if (!heigan)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// return botAI->IsRanged(bot);
|
||||
//}
|
||||
|
||||
bool RazuviousTankTrigger::IsActive()
|
||||
{
|
||||
Difficulty diff = bot->GetRaidDifficulty();
|
||||
if (diff == RAID_DIFFICULTY_10MAN_NORMAL)
|
||||
return helper.UpdateBossAI() && botAI->IsTank(bot);
|
||||
|
||||
return helper.UpdateBossAI() && bot->getClass() == CLASS_PRIEST;
|
||||
}
|
||||
|
||||
bool RazuviousNontankTrigger::IsActive()
|
||||
{
|
||||
Difficulty diff = bot->GetRaidDifficulty();
|
||||
if (diff == RAID_DIFFICULTY_10MAN_NORMAL)
|
||||
return helper.UpdateBossAI() && !(botAI->IsTank(bot));
|
||||
|
||||
return helper.UpdateBossAI() && !(bot->getClass() == CLASS_PRIEST);
|
||||
}
|
||||
|
||||
bool HorsemanAttractorsTrigger::IsActive()
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
return false;
|
||||
|
||||
return helper.IsAttracter(bot);
|
||||
}
|
||||
|
||||
bool HorsemanExceptAttractorsTrigger::IsActive()
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
return false;
|
||||
|
||||
return !helper.IsAttracter(bot);
|
||||
}
|
||||
|
||||
bool SapphironGroundTrigger::IsActive()
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
return false;
|
||||
|
||||
return helper.IsPhaseGround();
|
||||
}
|
||||
|
||||
bool SapphironFlightTrigger::IsActive()
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
return false;
|
||||
|
||||
return helper.IsPhaseFlight();
|
||||
}
|
||||
|
||||
bool GluthTrigger::IsActive() { return helper.UpdateBossAI(); }
|
||||
|
||||
bool GluthMainTankMortalWoundTrigger::IsActive()
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
return false;
|
||||
|
||||
if (!botAI->IsAssistTankOfIndex(bot, 0))
|
||||
return false;
|
||||
|
||||
Unit* mt = AI_VALUE(Unit*, "main tank");
|
||||
if (!mt)
|
||||
return false;
|
||||
|
||||
Aura* aura = NaxxSpellIds::GetAnyAura(mt, {NaxxSpellIds::MortalWound10, NaxxSpellIds::MortalWound25});
|
||||
if (!aura)
|
||||
{
|
||||
// Fallback to name for custom spell data.
|
||||
aura = botAI->GetAura("mortal wound", mt, false, true);
|
||||
}
|
||||
if (!aura || aura->GetStackAmount() < 5)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KelthuzadTrigger::IsActive() { return helper.UpdateBossAI(); }
|
||||
|
||||
bool AnubrekhanTrigger::IsActive() {
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "anub'rekhan");
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FaerlinaTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "grand widow faerlina");
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MaexxnaTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "maexxna");
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
return !botAI->IsTank(bot);
|
||||
}
|
||||
|
||||
//bool PatchwerkTankTrigger::IsActive()
|
||||
//{
|
||||
// Unit* boss = AI_VALUE2(Unit*, "find target", "patchwerk");
|
||||
// if (!boss)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// return !botAI->IsTank(bot) && !botAI->IsRanged(bot);
|
||||
//}
|
||||
//
|
||||
//bool PatchwerkRangedTrigger::IsActive()
|
||||
//{
|
||||
// Unit* boss = AI_VALUE2(Unit*, "find target", "patchwerk");
|
||||
// if (!boss)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// return !botAI->IsTank(bot) && botAI->IsRanged(bot);
|
||||
//}
|
||||
//
|
||||
//bool PatchwerkNonTankTrigger::IsActive()
|
||||
//{
|
||||
// Unit* boss = AI_VALUE2(Unit*, "find target", "patchwerk");
|
||||
// if (!boss)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// return !botAI->IsTank(bot);
|
||||
//}
|
||||
|
||||
bool LoathebTrigger::IsActive() { return helper.UpdateBossAI(); }
|
||||
|
||||
bool ThaddiusPhasePetTrigger::IsActive()
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
return false;
|
||||
|
||||
return helper.IsPhasePet();
|
||||
}
|
||||
|
||||
bool ThaddiusPhaseTransitionTrigger::IsActive()
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
return false;
|
||||
|
||||
return helper.IsPhaseTransition();
|
||||
}
|
||||
|
||||
bool ThaddiusPhaseThaddiusTrigger::IsActive()
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
return false;
|
||||
|
||||
return helper.IsPhaseThaddius();
|
||||
}
|
||||
259
src/Ai/Raid/Naxxramas/Trigger/RaidNaxxTriggers.h
Normal file
259
src/Ai/Raid/Naxxramas/Trigger/RaidNaxxTriggers.h
Normal file
@@ -0,0 +1,259 @@
|
||||
|
||||
#ifndef _PLAYERBOT_RAIDNAXXTRIGGERS_H
|
||||
#define _PLAYERBOT_RAIDNAXXTRIGGERS_H
|
||||
|
||||
#include "EventMap.h"
|
||||
#include "GenericTriggers.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "RaidNaxxBossHelper.h"
|
||||
#include "Trigger.h"
|
||||
|
||||
class MutatingInjectionTrigger : public HasAuraTrigger
|
||||
{
|
||||
public:
|
||||
MutatingInjectionTrigger(PlayerbotAI* ai) : HasAuraTrigger(ai, "mutating injection", 1) {}
|
||||
};
|
||||
|
||||
class MutatingInjectionMeleeTrigger : public MutatingInjectionTrigger
|
||||
{
|
||||
public:
|
||||
MutatingInjectionMeleeTrigger(PlayerbotAI* ai) : MutatingInjectionTrigger(ai) {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class MutatingInjectionRangedTrigger : public MutatingInjectionTrigger
|
||||
{
|
||||
public:
|
||||
MutatingInjectionRangedTrigger(PlayerbotAI* ai) : MutatingInjectionTrigger(ai) {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class AuraRemovedTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
AuraRemovedTrigger(PlayerbotAI* botAI, std::string name) : Trigger(botAI, name, 1)
|
||||
{
|
||||
this->prev_check = false;
|
||||
}
|
||||
virtual bool IsActive() override;
|
||||
|
||||
protected:
|
||||
bool prev_check;
|
||||
};
|
||||
|
||||
class MutatingInjectionRemovedTrigger : public HasNoAuraTrigger
|
||||
{
|
||||
public:
|
||||
MutatingInjectionRemovedTrigger(PlayerbotAI* ai) : HasNoAuraTrigger(ai, "mutating injection") {}
|
||||
virtual bool IsActive();
|
||||
};
|
||||
|
||||
class GrobbulusCloudTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
GrobbulusCloudTrigger(PlayerbotAI* ai) : Trigger(ai, "grobbulus cloud event"), last_cloud_ms(0) {}
|
||||
bool IsActive() override;
|
||||
|
||||
private:
|
||||
uint32 last_cloud_ms;
|
||||
static constexpr uint32 CloudRotationDelayMs = 15000;
|
||||
};
|
||||
|
||||
//class HeiganMeleeTrigger : public Trigger
|
||||
//{
|
||||
//public:
|
||||
// HeiganMeleeTrigger(PlayerbotAI* ai) : Trigger(ai, "heigan melee") {}
|
||||
// virtual bool IsActive();
|
||||
//};
|
||||
//
|
||||
//class HeiganRangedTrigger : public Trigger
|
||||
//{
|
||||
//public:
|
||||
// HeiganRangedTrigger(PlayerbotAI* ai) : Trigger(ai, "heigan ranged") {}
|
||||
// bool IsActive() override;
|
||||
//};
|
||||
|
||||
class RazuviousTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
RazuviousTankTrigger(PlayerbotAI* ai) : Trigger(ai, "instructor razuvious tank"), helper(ai) {}
|
||||
bool IsActive() override;
|
||||
|
||||
private:
|
||||
RazuviousBossHelper helper;
|
||||
};
|
||||
|
||||
class RazuviousNontankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
RazuviousNontankTrigger(PlayerbotAI* ai) : Trigger(ai, "instructor razuvious non-tank"), helper(ai) {}
|
||||
bool IsActive() override;
|
||||
|
||||
private:
|
||||
RazuviousBossHelper helper;
|
||||
};
|
||||
|
||||
class KelthuzadTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
KelthuzadTrigger(PlayerbotAI* ai) : Trigger(ai, "kel'thuzad trigger"), helper(ai) {}
|
||||
bool IsActive() override;
|
||||
|
||||
private:
|
||||
KelthuzadBossHelper helper;
|
||||
};
|
||||
|
||||
class AnubrekhanTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
AnubrekhanTrigger(PlayerbotAI* ai) : Trigger(ai, "anub'rekhan") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class FaerlinaTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
FaerlinaTrigger(PlayerbotAI* ai) : Trigger(ai, "faerlina") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class MaexxnaTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
MaexxnaTrigger(PlayerbotAI* ai) : Trigger(ai, "maexxna") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
//class PatchwerkTankTrigger : public Trigger
|
||||
//{
|
||||
//public:
|
||||
// PatchwerkTankTrigger(PlayerbotAI* ai) : Trigger(ai, "patchwerk tank") {}
|
||||
// bool IsActive() override;
|
||||
//};
|
||||
//
|
||||
//class PatchwerkNonTankTrigger : public Trigger
|
||||
//{
|
||||
//public:
|
||||
// PatchwerkNonTankTrigger(PlayerbotAI* ai) : Trigger(ai, "patchwerk non-tank") {}
|
||||
// bool IsActive() override;
|
||||
//};
|
||||
//
|
||||
//class PatchwerkRangedTrigger : public Trigger
|
||||
//{
|
||||
//public:
|
||||
// PatchwerkRangedTrigger(PlayerbotAI* ai) : Trigger(ai, "patchwerk ranged") {}
|
||||
// bool IsActive() override;
|
||||
//};
|
||||
|
||||
class ThaddiusPhasePetTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
ThaddiusPhasePetTrigger(PlayerbotAI* ai) : Trigger(ai, "thaddius phase pet"), helper(ai) {}
|
||||
bool IsActive() override;
|
||||
|
||||
private:
|
||||
ThaddiusBossHelper helper;
|
||||
};
|
||||
|
||||
class ThaddiusPhasePetLoseAggroTrigger : public ThaddiusPhasePetTrigger
|
||||
{
|
||||
public:
|
||||
ThaddiusPhasePetLoseAggroTrigger(PlayerbotAI* ai) : ThaddiusPhasePetTrigger(ai) {}
|
||||
virtual bool IsActive()
|
||||
{
|
||||
Unit* target = AI_VALUE(Unit*, "current target");
|
||||
return ThaddiusPhasePetTrigger::IsActive() && botAI->IsTank(bot) && target && target->GetVictim() != bot;
|
||||
}
|
||||
};
|
||||
|
||||
class ThaddiusPhaseTransitionTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
ThaddiusPhaseTransitionTrigger(PlayerbotAI* ai) : Trigger(ai, "thaddius phase transition"), helper(ai) {}
|
||||
bool IsActive() override;
|
||||
|
||||
private:
|
||||
ThaddiusBossHelper helper;
|
||||
};
|
||||
|
||||
class ThaddiusPhaseThaddiusTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
ThaddiusPhaseThaddiusTrigger(PlayerbotAI* ai) : Trigger(ai, "thaddius phase thaddius"), helper(ai) {}
|
||||
bool IsActive() override;
|
||||
|
||||
private:
|
||||
ThaddiusBossHelper helper;
|
||||
};
|
||||
|
||||
class HorsemanAttractorsTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
HorsemanAttractorsTrigger(PlayerbotAI* ai) : Trigger(ai, "fourhorsemen attractors"), helper(ai) {}
|
||||
bool IsActive() override;
|
||||
|
||||
private:
|
||||
FourhorsemanBossHelper helper;
|
||||
};
|
||||
|
||||
class HorsemanExceptAttractorsTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
HorsemanExceptAttractorsTrigger(PlayerbotAI* ai) : Trigger(ai, "fourhorsemen except attractors"), helper(ai) {}
|
||||
bool IsActive() override;
|
||||
|
||||
private:
|
||||
FourhorsemanBossHelper helper;
|
||||
};
|
||||
|
||||
class SapphironGroundTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
SapphironGroundTrigger(PlayerbotAI* ai) : Trigger(ai, "sapphiron ground"), helper(ai) {}
|
||||
bool IsActive() override;
|
||||
|
||||
private:
|
||||
SapphironBossHelper helper;
|
||||
};
|
||||
|
||||
class SapphironFlightTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
SapphironFlightTrigger(PlayerbotAI* ai) : Trigger(ai, "sapphiron flight"), helper(ai) {}
|
||||
bool IsActive() override;
|
||||
|
||||
private:
|
||||
SapphironBossHelper helper;
|
||||
};
|
||||
|
||||
class GluthTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
GluthTrigger(PlayerbotAI* ai) : Trigger(ai, "gluth trigger"), helper(ai) {}
|
||||
bool IsActive() override;
|
||||
|
||||
private:
|
||||
GluthBossHelper helper;
|
||||
};
|
||||
|
||||
class GluthMainTankMortalWoundTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
GluthMainTankMortalWoundTrigger(PlayerbotAI* ai) : Trigger(ai, "gluth main tank mortal wound trigger"), helper(ai) {}
|
||||
bool IsActive() override;
|
||||
|
||||
private:
|
||||
GluthBossHelper helper;
|
||||
};
|
||||
|
||||
class LoathebTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LoathebTrigger(PlayerbotAI* ai) : Trigger(ai, "loatheb"), helper(ai) {}
|
||||
bool IsActive() override;
|
||||
|
||||
private:
|
||||
LoathebBossHelper helper;
|
||||
};
|
||||
|
||||
#endif
|
||||
533
src/Ai/Raid/Naxxramas/Util/RaidNaxxBossHelper.h
Normal file
533
src/Ai/Raid/Naxxramas/Util/RaidNaxxBossHelper.h
Normal file
@@ -0,0 +1,533 @@
|
||||
#ifndef _PLAYERBOT_RAIDNAXXBOSSHELPER_H
|
||||
#define _PLAYERBOT_RAIDNAXXBOSSHELPER_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "AiObject.h"
|
||||
#include "AiObjectContext.h"
|
||||
#include "EventMap.h"
|
||||
#include "Log.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "Player.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "Spell.h"
|
||||
#include "Timer.h"
|
||||
#include "RaidNaxxSpellIds.h"
|
||||
|
||||
const uint32 NAXX_MAP_ID = 533;
|
||||
|
||||
template <class BossAiType>
|
||||
class GenericBossHelper : public AiObject
|
||||
{
|
||||
public:
|
||||
GenericBossHelper(PlayerbotAI* botAI, std::string name) : AiObject(botAI), _name(name) {}
|
||||
virtual bool UpdateBossAI()
|
||||
{
|
||||
if (!bot->IsInCombat())
|
||||
_unit = nullptr;
|
||||
|
||||
if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive()))
|
||||
_unit = nullptr;
|
||||
|
||||
if (!_unit)
|
||||
{
|
||||
_unit = AI_VALUE2(Unit*, "find target", _name);
|
||||
if (!_unit)
|
||||
return false;
|
||||
|
||||
_target = _unit->ToCreature();
|
||||
if (!_target)
|
||||
return false;
|
||||
|
||||
_ai = dynamic_cast<BossAiType*>(_target->GetAI());
|
||||
if (!_ai)
|
||||
return false;
|
||||
|
||||
_event_map = &_ai->events;
|
||||
if (!_event_map)
|
||||
return false;
|
||||
}
|
||||
if (!_event_map)
|
||||
return false;
|
||||
|
||||
_timer = getMSTime();
|
||||
return true;
|
||||
}
|
||||
virtual void Reset()
|
||||
{
|
||||
_unit = nullptr;
|
||||
_target = nullptr;
|
||||
_ai = nullptr;
|
||||
_event_map = nullptr;
|
||||
_timer = 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string _name;
|
||||
Unit* _unit = nullptr;
|
||||
Creature* _target = nullptr;
|
||||
BossAiType* _ai = nullptr;
|
||||
EventMap* _event_map = nullptr;
|
||||
uint32 _timer = 0;
|
||||
};
|
||||
|
||||
class KelthuzadBossHelper : public AiObject
|
||||
{
|
||||
public:
|
||||
KelthuzadBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {}
|
||||
const std::pair<float, float> center = {3716.19f, -5106.58f};
|
||||
const std::pair<float, float> tank_pos = {3709.19f, -5104.86f};
|
||||
const std::pair<float, float> assist_tank_pos = {3746.05f, -5112.74f};
|
||||
bool UpdateBossAI()
|
||||
{
|
||||
if (!bot->IsInCombat())
|
||||
Reset();
|
||||
|
||||
if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive()))
|
||||
Reset();
|
||||
|
||||
if (!_unit)
|
||||
_unit = AI_VALUE2(Unit*, "find target", "kel'thuzad");
|
||||
|
||||
return _unit != nullptr;
|
||||
}
|
||||
bool IsPhaseOne() { return _unit && _unit->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE); }
|
||||
bool IsPhaseTwo() { return _unit && !_unit->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE); }
|
||||
Unit* GetAnyShadowFissure()
|
||||
{
|
||||
Unit* shadow_fissure = nullptr;
|
||||
GuidVector units = *context->GetValue<GuidVector>("nearest triggers");
|
||||
for (auto i = units.begin(); i != units.end(); i++)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(*i);
|
||||
if (!unit)
|
||||
continue;
|
||||
if (botAI->EqualLowercaseName(unit->GetName(), "shadow fissure"))
|
||||
shadow_fissure = unit;
|
||||
}
|
||||
return shadow_fissure;
|
||||
}
|
||||
|
||||
private:
|
||||
void Reset() { _unit = nullptr; }
|
||||
|
||||
Unit* _unit = nullptr;
|
||||
};
|
||||
|
||||
class RazuviousBossHelper : public AiObject
|
||||
{
|
||||
public:
|
||||
RazuviousBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {}
|
||||
bool UpdateBossAI()
|
||||
{
|
||||
if (!bot->IsInCombat())
|
||||
Reset();
|
||||
|
||||
if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive()))
|
||||
Reset();
|
||||
|
||||
if (!_unit)
|
||||
_unit = AI_VALUE2(Unit*, "find target", "instructor razuvious");
|
||||
|
||||
return _unit != nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
void Reset() { _unit = nullptr; }
|
||||
|
||||
Unit* _unit = nullptr;
|
||||
};
|
||||
|
||||
class SapphironBossHelper : public AiObject
|
||||
{
|
||||
public:
|
||||
const std::pair<float, float> mainTankPos = {3512.07f, -5274.06f};
|
||||
const std::pair<float, float> center = {3517.31f, -5253.74f};
|
||||
const float GENERIC_HEIGHT = 137.29f;
|
||||
SapphironBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {}
|
||||
bool UpdateBossAI()
|
||||
{
|
||||
if (!bot->IsInCombat())
|
||||
Reset();
|
||||
|
||||
if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive()))
|
||||
Reset();
|
||||
|
||||
if (!_unit)
|
||||
{
|
||||
_unit = AI_VALUE2(Unit*, "find target", "sapphiron");
|
||||
if (!_unit)
|
||||
return false;
|
||||
}
|
||||
bool now_flying = _unit->IsFlying();
|
||||
if (_was_flying && !now_flying)
|
||||
_last_land_ms = getMSTime();
|
||||
|
||||
_was_flying = now_flying;
|
||||
return true;
|
||||
}
|
||||
bool IsPhaseGround() { return _unit && !_unit->IsFlying(); }
|
||||
bool IsPhaseFlight() { return _unit && _unit->IsFlying(); }
|
||||
bool JustLanded()
|
||||
{
|
||||
if (!_last_land_ms)
|
||||
return false;
|
||||
|
||||
return getMSTime() - _last_land_ms <= POSITION_TIME_AFTER_LANDED;
|
||||
}
|
||||
bool WaitForExplosion()
|
||||
{
|
||||
if (!IsPhaseFlight())
|
||||
return false;
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (member &&
|
||||
(NaxxSpellIds::HasAnyAura(botAI, member, {NaxxSpellIds::Icebolt10, NaxxSpellIds::Icebolt25}) ||
|
||||
botAI->HasAura("icebolt", member, false, false, -1, true)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool FindPosToAvoidChill(std::vector<float>& dest)
|
||||
{
|
||||
Aura* aura = NaxxSpellIds::GetAnyAura(bot, {NaxxSpellIds::Chill25});
|
||||
if (!aura)
|
||||
{
|
||||
// Fallback to name for custom spell data.
|
||||
aura = botAI->GetAura("chill", bot);
|
||||
}
|
||||
if (!aura)
|
||||
return false;
|
||||
|
||||
DynamicObject* dyn_obj = aura->GetDynobjOwner();
|
||||
if (!dyn_obj)
|
||||
return false;
|
||||
|
||||
Unit* currentTarget = AI_VALUE(Unit*, "current target");
|
||||
float angle = 0;
|
||||
uint32 index = botAI->GetGroupSlotIndex(bot);
|
||||
if (currentTarget)
|
||||
{
|
||||
if (botAI->IsRanged(bot))
|
||||
{
|
||||
if (bot->GetExactDist2d(currentTarget) <= 45.0f)
|
||||
angle = bot->GetAngle(dyn_obj) - M_PI + (rand_norm() - 0.5) * M_PI / 2;
|
||||
else
|
||||
{
|
||||
if (index % 2 == 0)
|
||||
angle = bot->GetAngle(currentTarget) + M_PI / 2;
|
||||
else
|
||||
angle = bot->GetAngle(currentTarget) - M_PI / 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (index % 3 == 0)
|
||||
angle = bot->GetAngle(currentTarget);
|
||||
else if (index % 3 == 1)
|
||||
angle = bot->GetAngle(currentTarget) + M_PI / 2;
|
||||
else
|
||||
angle = bot->GetAngle(currentTarget) - M_PI / 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
angle = bot->GetAngle(dyn_obj) - M_PI + (rand_norm() - 0.5) * M_PI / 2;
|
||||
|
||||
dest = {bot->GetPositionX() + cos(angle) * 5.0f, bot->GetPositionY() + sin(angle) * 5.0f, bot->GetPositionZ()};
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
void Reset()
|
||||
{
|
||||
_unit = nullptr;
|
||||
_was_flying = false;
|
||||
_last_land_ms = 0;
|
||||
}
|
||||
|
||||
const uint32 POSITION_TIME_AFTER_LANDED = 5000;
|
||||
Unit* _unit = nullptr;
|
||||
bool _was_flying = false;
|
||||
uint32 _last_land_ms = 0;
|
||||
};
|
||||
|
||||
class GluthBossHelper : public AiObject
|
||||
{
|
||||
public:
|
||||
const std::pair<float, float> mainTankPos25 = {3331.48f, -3109.06f};
|
||||
const std::pair<float, float> mainTankPos10 = {3278.29f, -3162.06f};
|
||||
const std::pair<float, float> beforeDecimatePos = {3267.34f, -3175.68f};
|
||||
const std::pair<float, float> leftSlowDownPos = {3290.68f, -3141.65f};
|
||||
const std::pair<float, float> rightSlowDownPos = {3300.78f, -3151.98f};
|
||||
const std::pair<float, float> rangedPos = {3301.45f, -3139.29f};
|
||||
const std::pair<float, float> healPos = {3303.09f, -3135.24f};
|
||||
|
||||
const float decimatedZombiePct = 10.0f;
|
||||
GluthBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {}
|
||||
bool UpdateBossAI()
|
||||
{
|
||||
if (!bot->IsInCombat())
|
||||
Reset();
|
||||
|
||||
if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive()))
|
||||
Reset();
|
||||
|
||||
if (!_unit)
|
||||
{
|
||||
_unit = AI_VALUE2(Unit*, "find target", "gluth");
|
||||
if (!_unit)
|
||||
return false;
|
||||
}
|
||||
if (_unit->IsInCombat())
|
||||
{
|
||||
if (_combat_start_ms == 0)
|
||||
_combat_start_ms = getMSTime();
|
||||
}
|
||||
else
|
||||
_combat_start_ms = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
bool BeforeDecimate()
|
||||
{
|
||||
if (!_unit || !_unit->HasUnitState(UNIT_STATE_CASTING))
|
||||
return false;
|
||||
|
||||
Spell* spell = _unit->GetCurrentSpell(CURRENT_GENERIC_SPELL);
|
||||
if (!spell)
|
||||
spell = _unit->GetCurrentSpell(CURRENT_CHANNELED_SPELL);
|
||||
|
||||
if (!spell)
|
||||
return false;
|
||||
|
||||
SpellInfo const* info = spell->GetSpellInfo();
|
||||
if (!info)
|
||||
return false;
|
||||
|
||||
if (NaxxSpellIds::MatchesAnySpellId(
|
||||
info, {NaxxSpellIds::Decimate10, NaxxSpellIds::Decimate25, NaxxSpellIds::Decimate25Alt}))
|
||||
return true;
|
||||
|
||||
// Fallback to name for custom spell data.
|
||||
return info->SpellName[LOCALE_enUS] && botAI->EqualLowercaseName(info->SpellName[LOCALE_enUS], "decimate");
|
||||
}
|
||||
bool JustStartCombat() const { return _combat_start_ms != 0 && getMSTime() - _combat_start_ms < 10000; }
|
||||
bool IsZombieChow(Unit* unit) const { return unit && botAI->EqualLowercaseName(unit->GetName(), "zombie chow"); }
|
||||
|
||||
private:
|
||||
void Reset()
|
||||
{
|
||||
_unit = nullptr;
|
||||
_combat_start_ms = 0;
|
||||
}
|
||||
|
||||
Unit* _unit = nullptr;
|
||||
uint32 _combat_start_ms = 0;
|
||||
};
|
||||
|
||||
class LoathebBossHelper : public AiObject
|
||||
{
|
||||
public:
|
||||
const std::pair<float, float> mainTankPos = {2877.57f, -3967.00f};
|
||||
const std::pair<float, float> rangePos = {2896.96f, -3980.61f};
|
||||
LoathebBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {}
|
||||
bool UpdateBossAI()
|
||||
{
|
||||
if (!bot->IsInCombat())
|
||||
Reset();
|
||||
|
||||
if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive()))
|
||||
Reset();
|
||||
|
||||
if (!_unit)
|
||||
_unit = AI_VALUE2(Unit*, "find target", "loatheb");
|
||||
|
||||
return _unit != nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
void Reset() { _unit = nullptr; }
|
||||
|
||||
Unit* _unit = nullptr;
|
||||
};
|
||||
|
||||
class FourhorsemanBossHelper : public AiObject
|
||||
{
|
||||
public:
|
||||
const float posZ = 241.27f;
|
||||
const std::pair<float, float> attractPos[2] = {{2502.03f, -2910.90f},
|
||||
{2484.61f, -2947.07f}}; // left (sir zeliek), right (lady blaumeux)
|
||||
FourhorsemanBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {}
|
||||
bool UpdateBossAI()
|
||||
{
|
||||
if (!bot->IsInCombat())
|
||||
Reset();
|
||||
|
||||
else if (_combat_start_ms == 0)
|
||||
_combat_start_ms = getMSTime();
|
||||
|
||||
if (_sir && (!_sir->IsInWorld() || !_sir->IsAlive()))
|
||||
Reset();
|
||||
|
||||
if (!_sir)
|
||||
{
|
||||
_sir = AI_VALUE2(Unit*, "find target", "sir zeliek");
|
||||
if (!_sir)
|
||||
return false;
|
||||
}
|
||||
_lady = AI_VALUE2(Unit*, "find target", "lady blaumeux");
|
||||
return true;
|
||||
}
|
||||
void Reset()
|
||||
{
|
||||
_sir = nullptr;
|
||||
_lady = nullptr;
|
||||
_combat_start_ms = 0;
|
||||
posToGo = 0;
|
||||
}
|
||||
bool IsAttracter(Player* bot)
|
||||
{
|
||||
Difficulty diff = bot->GetRaidDifficulty();
|
||||
if (diff == RAID_DIFFICULTY_25MAN_NORMAL)
|
||||
{
|
||||
return botAI->IsAssistRangedDpsOfIndex(bot, 0) || botAI->IsAssistHealOfIndex(bot, 0) ||
|
||||
botAI->IsAssistHealOfIndex(bot, 1) || botAI->IsAssistHealOfIndex(bot, 2);
|
||||
}
|
||||
return botAI->IsAssistRangedDpsOfIndex(bot, 0) || botAI->IsAssistHealOfIndex(bot, 0);
|
||||
}
|
||||
void CalculatePosToGo(Player* bot)
|
||||
{
|
||||
bool raid25 = bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL;
|
||||
Unit* lady = _lady;
|
||||
if (!lady)
|
||||
posToGo = 0;
|
||||
else
|
||||
{
|
||||
uint32 elapsed_ms = _combat_start_ms ? getMSTime() - _combat_start_ms : 0;
|
||||
// Interval: 24s - 15s - 15s - ...
|
||||
posToGo = !(elapsed_ms <= 9000 || ((elapsed_ms - 9000) / 67500) % 2 == 0);
|
||||
if (botAI->IsAssistRangedDpsOfIndex(bot, 0) || (raid25 && botAI->IsAssistHealOfIndex(bot, 1)))
|
||||
posToGo = 1 - posToGo;
|
||||
}
|
||||
}
|
||||
std::pair<float, float> CurrentAttractPos()
|
||||
{
|
||||
bool raid25 = bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL;
|
||||
float posX = attractPos[posToGo].first, posY = attractPos[posToGo].second;
|
||||
if (posToGo == 1)
|
||||
{
|
||||
float offset_x = 0.0f;
|
||||
float offset_y = 0.0f;
|
||||
float bias = 4.5f;
|
||||
if (raid25)
|
||||
{
|
||||
offset_x = -bias;
|
||||
offset_y = bias;
|
||||
}
|
||||
posX += offset_x;
|
||||
posY += offset_y;
|
||||
}
|
||||
return {posX, posY};
|
||||
}
|
||||
Unit* CurrentAttackTarget()
|
||||
{
|
||||
if (posToGo == 0)
|
||||
return _sir;
|
||||
|
||||
return _lady;
|
||||
}
|
||||
|
||||
protected:
|
||||
Unit* _sir = nullptr;
|
||||
Unit* _lady = nullptr;
|
||||
uint32 _combat_start_ms = 0;
|
||||
int posToGo = 0;
|
||||
};
|
||||
class ThaddiusBossHelper : public AiObject
|
||||
{
|
||||
public:
|
||||
const std::pair<float, float> tankPosFeugen = {3522.94f, -3002.60f};
|
||||
const std::pair<float, float> tankPosStalagg = {3436.14f, -2919.98f};
|
||||
const std::pair<float, float> rangedPosFeugen = {3500.45f, -2997.92f};
|
||||
const std::pair<float, float> rangedPosStalagg = {3441.01f, -2942.04f};
|
||||
const float tankPosZ = 312.61f;
|
||||
ThaddiusBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {}
|
||||
bool UpdateBossAI()
|
||||
{
|
||||
if (!bot->IsInCombat())
|
||||
Reset();
|
||||
|
||||
if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive()))
|
||||
Reset();
|
||||
|
||||
if (!_unit)
|
||||
{
|
||||
_unit = AI_VALUE2(Unit*, "find target", "thaddius");
|
||||
if (!_unit)
|
||||
return false;
|
||||
}
|
||||
feugen = AI_VALUE2(Unit*, "find target", "feugen");
|
||||
stalagg = AI_VALUE2(Unit*, "find target", "stalagg");
|
||||
return true;
|
||||
}
|
||||
bool IsPhasePet() { return (feugen && feugen->IsAlive()) || (stalagg && stalagg->IsAlive()); }
|
||||
bool IsPhaseTransition()
|
||||
{
|
||||
if (IsPhasePet())
|
||||
return false;
|
||||
|
||||
return _unit && _unit->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
|
||||
}
|
||||
bool IsPhaseThaddius() { return !IsPhasePet() && !IsPhaseTransition(); }
|
||||
Unit* GetNearestPet()
|
||||
{
|
||||
Unit* unit = nullptr;
|
||||
if (feugen && feugen->IsAlive())
|
||||
unit = feugen;
|
||||
|
||||
if (stalagg && stalagg->IsAlive() && (!feugen || bot->GetDistance(stalagg) < bot->GetDistance(feugen)))
|
||||
unit = stalagg;
|
||||
|
||||
return unit;
|
||||
}
|
||||
std::pair<float, float> PetPhaseGetPosForTank()
|
||||
{
|
||||
if (GetNearestPet() == feugen)
|
||||
return tankPosFeugen;
|
||||
|
||||
return tankPosStalagg;
|
||||
}
|
||||
std::pair<float, float> PetPhaseGetPosForRanged()
|
||||
{
|
||||
if (GetNearestPet() == feugen)
|
||||
return rangedPosFeugen;
|
||||
|
||||
return rangedPosStalagg;
|
||||
}
|
||||
|
||||
protected:
|
||||
void Reset()
|
||||
{
|
||||
_unit = nullptr;
|
||||
feugen = nullptr;
|
||||
stalagg = nullptr;
|
||||
}
|
||||
|
||||
Unit* _unit = nullptr;
|
||||
Unit* feugen = nullptr;
|
||||
Unit* stalagg = nullptr;
|
||||
};
|
||||
|
||||
#endif
|
||||
165
src/Ai/Raid/Naxxramas/Util/RaidNaxxSpellIds.h
Normal file
165
src/Ai/Raid/Naxxramas/Util/RaidNaxxSpellIds.h
Normal file
@@ -0,0 +1,165 @@
|
||||
#ifndef _PLAYERBOT_RAIDNAXXSPELLIDS_H
|
||||
#define _PLAYERBOT_RAIDNAXXSPELLIDS_H
|
||||
|
||||
#include <initializer_list>
|
||||
|
||||
#include "PlayerbotAI.h"
|
||||
|
||||
// use src/server/scripts/Northrend/Naxxramas/naxxramas.h for CreatureId, NaxxramasSay, NaxxramasEvent, NaxxramasMisc
|
||||
namespace NaxxSpellIds
|
||||
{
|
||||
// Heigan
|
||||
static constexpr uint32 Eruption10 = 29371;
|
||||
/*
|
||||
SPELL_SPELL_DISRUPTION = 29310,
|
||||
SPELL_DECREPIT_FEVER = 29998,
|
||||
SPELL_PLAGUE_CLOUD = 29350,
|
||||
SPELL_TELEPORT_SELF = 30211
|
||||
*/
|
||||
|
||||
// Grobbulus
|
||||
static constexpr uint32 PoisonCloud = 28240;
|
||||
|
||||
// Thaddius polarity
|
||||
static constexpr uint32 PositiveCharge10 = 28059;
|
||||
static constexpr uint32 PositiveCharge25 = 28062;
|
||||
static constexpr uint32 PositiveChargeStack = 29659;
|
||||
static constexpr uint32 NegativeCharge10 = 28084;
|
||||
static constexpr uint32 NegativeCharge25 = 28085;
|
||||
static constexpr uint32 NegativeChargeStack = 29660;
|
||||
/*
|
||||
SPELL_MAGNETIC_PULL = 28337,
|
||||
SPELL_TESLA_SHOCK = 28099,
|
||||
SPELL_SHOCK_VISUAL = 28159,
|
||||
|
||||
// Stalagg
|
||||
SPELL_POWER_SURGE = 54529,
|
||||
SPELL_STALAGG_CHAIN = 28096,
|
||||
|
||||
// Feugen
|
||||
SPELL_STATIC_FIELD = 28135,
|
||||
SPELL_FEUGEN_CHAIN = 28111,
|
||||
|
||||
// Thaddius
|
||||
SPELL_POLARITY_SHIFT = 28089,
|
||||
SPELL_BALL_LIGHTNING = 28299,
|
||||
SPELL_CHAIN_LIGHTNING = 28167,
|
||||
SPELL_BERSERK = 27680,
|
||||
SPELL_THADDIUS_VISUAL_LIGHTNING = 28136,
|
||||
SPELL_THADDIUS_SPAWN_STUN = 28160,
|
||||
|
||||
SPELL_POSITIVE_CHARGE = 28062,
|
||||
SPELL_POSITIVE_CHARGE_STACK = 29659,
|
||||
SPELL_NEGATIVE_CHARGE = 28085,
|
||||
SPELL_NEGATIVE_CHARGE_STACK = 29660,
|
||||
SPELL_POSITIVE_POLARITY = 28059,
|
||||
SPELL_NEGATIVE_POLARITY = 28084
|
||||
*/
|
||||
// Sapphiron
|
||||
static constexpr uint32 Icebolt10 = 28522;
|
||||
static constexpr uint32 Icebolt25 = 28526;
|
||||
static constexpr uint32 Chill25 = 55699;
|
||||
/*
|
||||
// Fight
|
||||
SPELL_FROST_AURA = 28531,
|
||||
SPELL_CLEAVE = 19983,
|
||||
SPELL_TAIL_SWEEP = 55697,
|
||||
SPELL_SUMMON_BLIZZARD = 28560,
|
||||
SPELL_LIFE_DRAIN = 28542,
|
||||
SPELL_BERSERK = 26662,
|
||||
|
||||
// Ice block
|
||||
SPELL_ICEBOLT_CAST = 28526,
|
||||
SPELL_ICEBOLT_TRIGGER = 28522,
|
||||
SPELL_FROST_MISSILE = 30101,
|
||||
SPELL_FROST_EXPLOSION = 28524,
|
||||
|
||||
// Visuals
|
||||
SPELL_SAPPHIRON_DIES = 29357
|
||||
*/
|
||||
// Gluth
|
||||
static constexpr uint32 Decimate10 = 28374;
|
||||
static constexpr uint32 Decimate25 = 54426;
|
||||
static constexpr uint32 Decimate25Alt = 28375;
|
||||
static constexpr uint32 MortalWound10 = 25646;
|
||||
static constexpr uint32 MortalWound25 = 54378;
|
||||
/*
|
||||
SPELL_MORTAL_WOUND = 25646,
|
||||
SPELL_ENRAGE = 28371,
|
||||
SPELL_DECIMATE = 28374,
|
||||
SPELL_DECIMATE_DAMAGE = 28375,
|
||||
SPELL_BERSERK = 26662,
|
||||
SPELL_INFECTED_WOUND = 29306,
|
||||
SPELL_CHOW_SEARCHER = 28404
|
||||
*/
|
||||
// Anub'Rekhan
|
||||
static constexpr uint32 LocustSwarm10 = 28785;
|
||||
static constexpr uint32 LocustSwarm10Alt = 28786;
|
||||
static constexpr uint32 LocustSwarm25 = 54021; // 25-man Locust Swarm
|
||||
/*
|
||||
SPELL_IMPALE = 28783,
|
||||
SPELL_LOCUST_SWARM = 28785,
|
||||
SPELL_SUMMON_CORPSE_SCARABS_5 = 29105,
|
||||
SPELL_SUMMON_CORPSE_SCARABS_10 = 28864,
|
||||
SPELL_BERSERK = 26662
|
||||
ACHIEV_TIMED_START_EVENT = 9891,
|
||||
EVENT_SPAWN_CRYPT_GUARDS_1 = 0,
|
||||
EVENT_BERSERK = 1,
|
||||
////
|
||||
Position const cryptguardPositions[] = {
|
||||
{ 3299.732f, -3502.489f, 287.077f, 2.378f },
|
||||
{ 3299.086f, -3450.929f, 287.077f, 3.999f },
|
||||
{ 3331.217f, -3476.607f, 287.074f, 3.269f }
|
||||
};
|
||||
|
||||
*/
|
||||
// Loatheb
|
||||
static constexpr uint32 NecroticAura10 = 55593;
|
||||
/*
|
||||
SPELL_NECROTIC_AURA = 55593,
|
||||
SPELL_SUMMON_SPORE = 29234,
|
||||
SPELL_DEATHBLOOM = 29865,
|
||||
SPELL_INEVITABLE_DOOM = 29204,
|
||||
SPELL_BERSERK = 26662
|
||||
*/
|
||||
inline bool HasAnyAura(PlayerbotAI* botAI, Unit* unit, std::initializer_list<uint32> spellIds)
|
||||
{
|
||||
if (!botAI || !unit)
|
||||
return false;
|
||||
|
||||
for (uint32 spellId : spellIds)
|
||||
{
|
||||
if (botAI->HasAura(spellId, unit))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline Aura* GetAnyAura(Unit* unit, std::initializer_list<uint32> spellIds)
|
||||
{
|
||||
if (!unit)
|
||||
return nullptr;
|
||||
|
||||
for (uint32 spellId : spellIds)
|
||||
{
|
||||
if (Aura* aura = unit->GetAura(spellId))
|
||||
return aura;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline bool MatchesAnySpellId(SpellInfo const* info, std::initializer_list<uint32> spellIds)
|
||||
{
|
||||
if (!info)
|
||||
return false;
|
||||
|
||||
for (uint32 spellId : spellIds)
|
||||
{
|
||||
if (info->Id == spellId)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} // namespace NaxxSpellIds
|
||||
|
||||
#endif
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "RaidKarazhanStrategy.h"
|
||||
#include "RaidGruulsLairStrategy.h"
|
||||
#include "RaidMagtheridonStrategy.h"
|
||||
#include "RaidNaxxStrategy.h"
|
||||
#include "RaidSSCStrategy.h"
|
||||
#include "RaidTempestKeepStrategy.h"
|
||||
#include "RaidOsStrategy.h"
|
||||
@@ -28,6 +29,7 @@ public:
|
||||
creators["karazhan"] = &RaidStrategyContext::karazhan;
|
||||
creators["gruulslair"] = &RaidStrategyContext::gruulslair;
|
||||
creators["magtheridon"] = &RaidStrategyContext::magtheridon;
|
||||
creators["naxx"] = &RaidStrategyContext::naxx;
|
||||
creators["ssc"] = &RaidStrategyContext::ssc;
|
||||
creators["tempestkeep"] = &RaidStrategyContext::tempestkeep;
|
||||
creators["wotlk-os"] = &RaidStrategyContext::wotlk_os;
|
||||
@@ -45,6 +47,7 @@ private:
|
||||
static Strategy* karazhan(PlayerbotAI* botAI) { return new RaidKarazhanStrategy(botAI); }
|
||||
static Strategy* gruulslair(PlayerbotAI* botAI) { return new RaidGruulsLairStrategy(botAI); }
|
||||
static Strategy* magtheridon(PlayerbotAI* botAI) { return new RaidMagtheridonStrategy(botAI); }
|
||||
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* wotlk_os(PlayerbotAI* botAI) { return new RaidOsStrategy(botAI); }
|
||||
|
||||
@@ -598,9 +598,9 @@ uint32 ChatHelper::parseSlot(std::string const text)
|
||||
return EQUIPMENT_SLOT_END;
|
||||
}
|
||||
|
||||
bool ChatHelper::parseable(std::string const text)
|
||||
bool ChatHelper::parseableItem(std::string const text)
|
||||
{
|
||||
return text.find("|H") != std::string::npos || text == "questitem" || text == "ammo" ||
|
||||
return text.find("|Hitem:") != std::string::npos || text == "questitem" || text == "ammo" ||
|
||||
substrContainsInMap<uint32>(text, consumableSubClasses) ||
|
||||
substrContainsInMap<uint32>(text, tradeSubClasses) || substrContainsInMap<uint32>(text, itemQualities) ||
|
||||
substrContainsInMap<uint32>(text, slots) || substrContainsInMap<ChatMsg>(text, chats) ||
|
||||
|
||||
@@ -66,7 +66,7 @@ public:
|
||||
static uint32 parseSlot(std::string const text);
|
||||
uint32 parseSkill(std::string const text);
|
||||
|
||||
static bool parseable(std::string const text);
|
||||
static bool parseableItem(std::string const text);
|
||||
|
||||
void eraseAllSubStr(std::string& mainStr, std::string const toErase);
|
||||
|
||||
|
||||
@@ -4,59 +4,17 @@
|
||||
*/
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
|
||||
#include "ActionContext.h"
|
||||
#include "ChatActionContext.h"
|
||||
#include "ChatTriggerContext.h"
|
||||
#include "Helpers.h"
|
||||
#include "DKAiObjectContext.h"
|
||||
#include "DruidAiObjectContext.h"
|
||||
#include "HunterAiObjectContext.h"
|
||||
#include "MageAiObjectContext.h"
|
||||
#include "PaladinAiObjectContext.h"
|
||||
#include "Playerbots.h"
|
||||
#include "PriestAiObjectContext.h"
|
||||
#include "RogueAiObjectContext.h"
|
||||
#include "ShamanAiObjectContext.h"
|
||||
#include "SharedValueContext.h"
|
||||
#include "StrategyContext.h"
|
||||
#include "TriggerContext.h"
|
||||
#include "ValueContext.h"
|
||||
#include "WarlockAiObjectContext.h"
|
||||
#include "WarriorAiObjectContext.h"
|
||||
#include "WorldPacketActionContext.h"
|
||||
#include "WorldPacketTriggerContext.h"
|
||||
#include "Ai/Dungeon/DungeonStrategyContext.h"
|
||||
#include "Ai/Dungeon/WotlkDungeonActionContext.h"
|
||||
#include "Ai/Dungeon/WotlkDungeonTriggerContext.h"
|
||||
#include "Ai/Raid/RaidStrategyContext.h"
|
||||
#include "Ai/Raid/Aq20/RaidAq20ActionContext.h"
|
||||
#include "Ai/Raid/Aq20/RaidAq20TriggerContext.h"
|
||||
#include "Ai/Raid/MoltenCore/RaidMcActionContext.h"
|
||||
#include "Ai/Raid/MoltenCore/RaidMcTriggerContext.h"
|
||||
#include "Ai/Raid/BlackwingLair/RaidBwlActionContext.h"
|
||||
#include "Ai/Raid/BlackwingLair/RaidBwlTriggerContext.h"
|
||||
#include "Ai/Raid/Karazhan/RaidKarazhanActionContext.h"
|
||||
#include "Ai/Raid/Karazhan/RaidKarazhanTriggerContext.h"
|
||||
#include "Ai/Raid/GruulsLair/RaidGruulsLairActionContext.h"
|
||||
#include "Ai/Raid/GruulsLair/RaidGruulsLairTriggerContext.h"
|
||||
#include "Ai/Raid/Magtheridon/RaidMagtheridonActionContext.h"
|
||||
#include "Ai/Raid/Magtheridon/RaidMagtheridonTriggerContext.h"
|
||||
#include "Ai/Raid/SerpentshrineCavern/RaidSSCActionContext.h"
|
||||
#include "Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h"
|
||||
#include "Ai/Raid/TempestKeep/RaidTempestKeepActionContext.h"
|
||||
#include "Ai/Raid/TempestKeep/RaidTempestKeepTriggerContext.h"
|
||||
#include "Ai/Raid/EyeOfEternity/RaidEoEActionContext.h"
|
||||
#include "Ai/Raid/EyeOfEternity/RaidEoETriggerContext.h"
|
||||
#include "Ai/Raid/VaultOfArchavon/RaidVoAActionContext.h"
|
||||
#include "Ai/Raid/VaultOfArchavon/RaidVoATriggerContext.h"
|
||||
#include "Ai/Raid/ObsidianSanctum/RaidOsActionContext.h"
|
||||
#include "Ai/Raid/ObsidianSanctum/RaidOsTriggerContext.h"
|
||||
#include "Ai/Raid/Ulduar/RaidUlduarActionContext.h"
|
||||
#include "Ai/Raid/Ulduar/RaidUlduarTriggerContext.h"
|
||||
#include "Ai/Raid/Onyxia/RaidOnyxiaActionContext.h"
|
||||
#include "Ai/Raid/Onyxia/RaidOnyxiaTriggerContext.h"
|
||||
#include "Ai/Raid/Icecrown/RaidIccActionContext.h"
|
||||
#include "Ai/Raid/Icecrown/RaidIccTriggerContext.h"
|
||||
|
||||
SharedNamedObjectContextList<Strategy> AiObjectContext::sharedStrategyContexts;
|
||||
SharedNamedObjectContextList<Action> AiObjectContext::sharedActionContexts;
|
||||
@@ -98,93 +56,6 @@ void AiObjectContext::BuildSharedContexts()
|
||||
BuildSharedValueContexts(sharedValueContexts);
|
||||
}
|
||||
|
||||
void AiObjectContext::BuildSharedStrategyContexts(SharedNamedObjectContextList<Strategy>& strategyContexts)
|
||||
{
|
||||
strategyContexts.Add(new StrategyContext());
|
||||
strategyContexts.Add(new MovementStrategyContext());
|
||||
strategyContexts.Add(new AssistStrategyContext());
|
||||
strategyContexts.Add(new QuestStrategyContext());
|
||||
strategyContexts.Add(new DungeonStrategyContext());
|
||||
strategyContexts.Add(new RaidStrategyContext());
|
||||
}
|
||||
|
||||
void AiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList<Action>& actionContexts)
|
||||
{
|
||||
actionContexts.Add(new ActionContext());
|
||||
actionContexts.Add(new ChatActionContext());
|
||||
actionContexts.Add(new WorldPacketActionContext());
|
||||
actionContexts.Add(new RaidAq20ActionContext());
|
||||
actionContexts.Add(new RaidMcActionContext());
|
||||
actionContexts.Add(new RaidBwlActionContext());
|
||||
actionContexts.Add(new RaidKarazhanActionContext());
|
||||
actionContexts.Add(new RaidGruulsLairActionContext());
|
||||
actionContexts.Add(new RaidMagtheridonActionContext());
|
||||
actionContexts.Add(new RaidSSCActionContext());
|
||||
actionContexts.Add(new RaidTempestKeepActionContext());
|
||||
actionContexts.Add(new RaidOsActionContext());
|
||||
actionContexts.Add(new RaidEoEActionContext());
|
||||
actionContexts.Add(new RaidVoAActionContext());
|
||||
actionContexts.Add(new RaidUlduarActionContext());
|
||||
actionContexts.Add(new RaidOnyxiaActionContext());
|
||||
actionContexts.Add(new RaidIccActionContext());
|
||||
actionContexts.Add(new WotlkDungeonUKActionContext());
|
||||
actionContexts.Add(new WotlkDungeonNexActionContext());
|
||||
actionContexts.Add(new WotlkDungeonANActionContext());
|
||||
actionContexts.Add(new WotlkDungeonOKActionContext());
|
||||
actionContexts.Add(new WotlkDungeonDTKActionContext());
|
||||
actionContexts.Add(new WotlkDungeonVHActionContext());
|
||||
actionContexts.Add(new WotlkDungeonGDActionContext());
|
||||
actionContexts.Add(new WotlkDungeonHoSActionContext());
|
||||
actionContexts.Add(new WotlkDungeonHoLActionContext());
|
||||
actionContexts.Add(new WotlkDungeonOccActionContext());
|
||||
actionContexts.Add(new WotlkDungeonUPActionContext());
|
||||
actionContexts.Add(new WotlkDungeonCoSActionContext());
|
||||
actionContexts.Add(new WotlkDungeonFoSActionContext());
|
||||
actionContexts.Add(new WotlkDungeonPoSActionContext());
|
||||
actionContexts.Add(new WotlkDungeonToCActionContext());
|
||||
}
|
||||
|
||||
void AiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextList<Trigger>& triggerContexts)
|
||||
{
|
||||
triggerContexts.Add(new TriggerContext());
|
||||
triggerContexts.Add(new ChatTriggerContext());
|
||||
triggerContexts.Add(new WorldPacketTriggerContext());
|
||||
triggerContexts.Add(new RaidAq20TriggerContext());
|
||||
triggerContexts.Add(new RaidMcTriggerContext());
|
||||
triggerContexts.Add(new RaidBwlTriggerContext());
|
||||
triggerContexts.Add(new RaidKarazhanTriggerContext());
|
||||
triggerContexts.Add(new RaidGruulsLairTriggerContext());
|
||||
triggerContexts.Add(new RaidMagtheridonTriggerContext());
|
||||
triggerContexts.Add(new RaidSSCTriggerContext());
|
||||
triggerContexts.Add(new RaidTempestKeepTriggerContext());
|
||||
triggerContexts.Add(new RaidOsTriggerContext());
|
||||
triggerContexts.Add(new RaidEoETriggerContext());
|
||||
triggerContexts.Add(new RaidVoATriggerContext());
|
||||
triggerContexts.Add(new RaidUlduarTriggerContext());
|
||||
triggerContexts.Add(new RaidOnyxiaTriggerContext());
|
||||
triggerContexts.Add(new RaidIccTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonUKTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonNexTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonANTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonOKTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonDTKTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonVHTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonGDTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonHoSTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonHoLTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonOccTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonUPTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonCoSTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonFoSTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonPoSTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonToCTriggerContext());
|
||||
}
|
||||
|
||||
void AiObjectContext::BuildSharedValueContexts(SharedNamedObjectContextList<UntypedValue>& valueContexts)
|
||||
{
|
||||
valueContexts.Add(new ValueContext());
|
||||
}
|
||||
|
||||
std::vector<std::string> AiObjectContext::Save()
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
|
||||
57
src/Bot/Engine/BuildSharedActionContexts.cpp
Normal file
57
src/Bot/Engine/BuildSharedActionContexts.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#include "AiObjectContext.h"
|
||||
#include "ActionContext.h"
|
||||
#include "ChatActionContext.h"
|
||||
#include "WorldPacketActionContext.h"
|
||||
#include "Ai/Raid/Aq20/RaidAq20ActionContext.h"
|
||||
#include "Ai/Raid/MoltenCore/RaidMcActionContext.h"
|
||||
#include "Ai/Raid/BlackwingLair/RaidBwlActionContext.h"
|
||||
#include "Ai/Raid/Karazhan/RaidKarazhanActionContext.h"
|
||||
#include "Ai/Raid/GruulsLair/RaidGruulsLairActionContext.h"
|
||||
#include "Ai/Raid/Naxxramas/RaidNaxxActionContext.h"
|
||||
#include "Ai/Raid/Magtheridon/RaidMagtheridonActionContext.h"
|
||||
#include "Ai/Raid/SerpentshrineCavern/RaidSSCActionContext.h"
|
||||
#include "Ai/Raid/TempestKeep/RaidTempestKeepActionContext.h"
|
||||
#include "Ai/Raid/ObsidianSanctum/RaidOsActionContext.h"
|
||||
#include "Ai/Raid/EyeOfEternity/RaidEoEActionContext.h"
|
||||
#include "Ai/Raid/VaultOfArchavon/RaidVoAActionContext.h"
|
||||
#include "Ai/Raid/Ulduar/RaidUlduarActionContext.h"
|
||||
#include "Ai/Raid/Onyxia/RaidOnyxiaActionContext.h"
|
||||
#include "Ai/Raid/Icecrown/RaidIccActionContext.h"
|
||||
#include "Ai/Dungeon/WotlkDungeonActionContext.h"
|
||||
|
||||
void AiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList<Action>& actionContexts)
|
||||
{
|
||||
actionContexts.Add(new ActionContext());
|
||||
actionContexts.Add(new ChatActionContext());
|
||||
actionContexts.Add(new WorldPacketActionContext());
|
||||
actionContexts.Add(new RaidAq20ActionContext());
|
||||
actionContexts.Add(new RaidMcActionContext());
|
||||
actionContexts.Add(new RaidBwlActionContext());
|
||||
actionContexts.Add(new RaidKarazhanActionContext());
|
||||
actionContexts.Add(new RaidGruulsLairActionContext());
|
||||
actionContexts.Add(new RaidMagtheridonActionContext());
|
||||
actionContexts.Add(new RaidSSCActionContext());
|
||||
actionContexts.Add(new RaidTempestKeepActionContext());
|
||||
actionContexts.Add(new RaidNaxxActionContext());
|
||||
actionContexts.Add(new RaidOsActionContext());
|
||||
actionContexts.Add(new RaidEoEActionContext());
|
||||
actionContexts.Add(new RaidVoAActionContext());
|
||||
actionContexts.Add(new RaidUlduarActionContext());
|
||||
actionContexts.Add(new RaidOnyxiaActionContext());
|
||||
actionContexts.Add(new RaidIccActionContext());
|
||||
actionContexts.Add(new WotlkDungeonUKActionContext());
|
||||
actionContexts.Add(new WotlkDungeonNexActionContext());
|
||||
actionContexts.Add(new WotlkDungeonANActionContext());
|
||||
actionContexts.Add(new WotlkDungeonOKActionContext());
|
||||
actionContexts.Add(new WotlkDungeonDTKActionContext());
|
||||
actionContexts.Add(new WotlkDungeonVHActionContext());
|
||||
actionContexts.Add(new WotlkDungeonGDActionContext());
|
||||
actionContexts.Add(new WotlkDungeonHoSActionContext());
|
||||
actionContexts.Add(new WotlkDungeonHoLActionContext());
|
||||
actionContexts.Add(new WotlkDungeonOccActionContext());
|
||||
actionContexts.Add(new WotlkDungeonUPActionContext());
|
||||
actionContexts.Add(new WotlkDungeonCoSActionContext());
|
||||
actionContexts.Add(new WotlkDungeonFoSActionContext());
|
||||
actionContexts.Add(new WotlkDungeonPoSActionContext());
|
||||
actionContexts.Add(new WotlkDungeonToCActionContext());
|
||||
}
|
||||
14
src/Bot/Engine/BuildSharedStrategyContexts.cpp
Normal file
14
src/Bot/Engine/BuildSharedStrategyContexts.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#include "AiObjectContext.h"
|
||||
#include "StrategyContext.h"
|
||||
#include "Ai/Dungeon/DungeonStrategyContext.h"
|
||||
#include "Ai/Raid/RaidStrategyContext.h"
|
||||
|
||||
void AiObjectContext::BuildSharedStrategyContexts(SharedNamedObjectContextList<Strategy>& strategyContexts)
|
||||
{
|
||||
strategyContexts.Add(new StrategyContext());
|
||||
strategyContexts.Add(new MovementStrategyContext());
|
||||
strategyContexts.Add(new AssistStrategyContext());
|
||||
strategyContexts.Add(new QuestStrategyContext());
|
||||
strategyContexts.Add(new DungeonStrategyContext());
|
||||
strategyContexts.Add(new RaidStrategyContext());
|
||||
}
|
||||
57
src/Bot/Engine/BuildSharedTriggerContexts.cpp
Normal file
57
src/Bot/Engine/BuildSharedTriggerContexts.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#include "AiObjectContext.h"
|
||||
#include "TriggerContext.h"
|
||||
#include "ChatTriggerContext.h"
|
||||
#include "WorldPacketTriggerContext.h"
|
||||
#include "Ai/Raid/Aq20/RaidAq20TriggerContext.h"
|
||||
#include "Ai/Raid/MoltenCore/RaidMcTriggerContext.h"
|
||||
#include "Ai/Raid/BlackwingLair/RaidBwlTriggerContext.h"
|
||||
#include "Ai/Raid/Karazhan/RaidKarazhanTriggerContext.h"
|
||||
#include "Ai/Raid/GruulsLair/RaidGruulsLairTriggerContext.h"
|
||||
#include "Ai/Raid/Magtheridon/RaidMagtheridonTriggerContext.h"
|
||||
#include "Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h"
|
||||
#include "Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h"
|
||||
#include "Ai/Raid/TempestKeep/RaidTempestKeepTriggerContext.h"
|
||||
#include "Ai/Raid/ObsidianSanctum/RaidOsTriggerContext.h"
|
||||
#include "Ai/Raid/EyeOfEternity/RaidEoETriggerContext.h"
|
||||
#include "Ai/Raid/VaultOfArchavon/RaidVoATriggerContext.h"
|
||||
#include "Ai/Raid/Ulduar/RaidUlduarTriggerContext.h"
|
||||
#include "Ai/Raid/Onyxia/RaidOnyxiaTriggerContext.h"
|
||||
#include "Ai/Raid/Icecrown/RaidIccTriggerContext.h"
|
||||
#include "Ai/Dungeon/WotlkDungeonTriggerContext.h"
|
||||
|
||||
void AiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextList<Trigger>& triggerContexts)
|
||||
{
|
||||
triggerContexts.Add(new TriggerContext());
|
||||
triggerContexts.Add(new ChatTriggerContext());
|
||||
triggerContexts.Add(new WorldPacketTriggerContext());
|
||||
triggerContexts.Add(new RaidAq20TriggerContext());
|
||||
triggerContexts.Add(new RaidMcTriggerContext());
|
||||
triggerContexts.Add(new RaidBwlTriggerContext());
|
||||
triggerContexts.Add(new RaidKarazhanTriggerContext());
|
||||
triggerContexts.Add(new RaidGruulsLairTriggerContext());
|
||||
triggerContexts.Add(new RaidMagtheridonTriggerContext());
|
||||
triggerContexts.Add(new RaidNaxxTriggerContext());
|
||||
triggerContexts.Add(new RaidSSCTriggerContext());
|
||||
triggerContexts.Add(new RaidTempestKeepTriggerContext());
|
||||
triggerContexts.Add(new RaidOsTriggerContext());
|
||||
triggerContexts.Add(new RaidEoETriggerContext());
|
||||
triggerContexts.Add(new RaidVoATriggerContext());
|
||||
triggerContexts.Add(new RaidUlduarTriggerContext());
|
||||
triggerContexts.Add(new RaidOnyxiaTriggerContext());
|
||||
triggerContexts.Add(new RaidIccTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonUKTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonNexTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonANTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonOKTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonDTKTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonVHTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonGDTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonHoSTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonHoLTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonOccTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonUPTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonCoSTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonFoSTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonPoSTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonToCTriggerContext());
|
||||
}
|
||||
7
src/Bot/Engine/BuildSharedValueContexts.cpp
Normal file
7
src/Bot/Engine/BuildSharedValueContexts.cpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#include "AiObjectContext.h"
|
||||
#include "ValueContext.h"
|
||||
|
||||
void AiObjectContext::BuildSharedValueContexts(SharedNamedObjectContextList<UntypedValue>& valueContexts)
|
||||
{
|
||||
valueContexts.Add(new ValueContext());
|
||||
}
|
||||
@@ -30,7 +30,7 @@ bool ExternalEventHelper::ParseChatCommand(std::string const command, Player* ow
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!ChatHelper::parseable(command))
|
||||
if (!ChatHelper::parseableItem(command))
|
||||
return false;
|
||||
|
||||
HandleCommand("c", command, owner);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "ArenaTeamMgr.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "RaceMgr.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "SocialMgr.h"
|
||||
@@ -60,7 +61,7 @@ Player* RandomPlayerbotFactory::CreateRandomBot(WorldSession* session, uint8 cls
|
||||
const bool alliance = static_cast<bool>(urand(0, 1));
|
||||
|
||||
std::vector<uint8> raceOptions;
|
||||
for (uint8 race = RACE_HUMAN; race < MAX_RACES; ++race)
|
||||
for (uint8 race = RACE_HUMAN; race < sRaceMgr->GetMaxRaces(); ++race)
|
||||
{
|
||||
// skip disabled with config races
|
||||
if ((1 << (race - 1)) & sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_DISABLED_RACEMASK))
|
||||
|
||||
@@ -1532,6 +1532,21 @@ std::vector<std::string> PlayerbotAI::GetStrategies(BotState type)
|
||||
|
||||
void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
|
||||
{
|
||||
static const std::vector<std::string> allInstanceStrategies =
|
||||
{
|
||||
"aq20", "bwl", "karazhan", "gruulslair", "icc", "magtheridon", "moltencore",
|
||||
"naxx", "onyxia", "ssc", "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"
|
||||
};
|
||||
|
||||
for (const std::string& strat : allInstanceStrategies)
|
||||
{
|
||||
engines[BOT_STATE_COMBAT]->removeStrategy(strat);
|
||||
engines[BOT_STATE_NON_COMBAT]->removeStrategy(strat);
|
||||
}
|
||||
|
||||
std::string strategyName;
|
||||
switch (mapId)
|
||||
{
|
||||
@@ -1550,6 +1565,9 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
|
||||
case 532:
|
||||
strategyName = "karazhan"; // Karazhan
|
||||
break;
|
||||
case 533:
|
||||
strategyName = "naxx"; // Naxxramas
|
||||
break;
|
||||
case 544:
|
||||
strategyName = "magtheridon"; // Magtheridon's Lair
|
||||
break;
|
||||
@@ -1628,10 +1646,13 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (strategyName.empty())
|
||||
return;
|
||||
|
||||
engines[BOT_STATE_COMBAT]->addStrategy(strategyName);
|
||||
engines[BOT_STATE_NON_COMBAT]->addStrategy(strategyName);
|
||||
|
||||
if (tellMaster && !strategyName.empty())
|
||||
{
|
||||
std::ostringstream out;
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "PlayerbotFactory.h"
|
||||
#include "Playerbots.h"
|
||||
#include "Position.h"
|
||||
#include "RaceMgr.h"
|
||||
#include "Random.h"
|
||||
#include "RandomPlayerbotFactory.h"
|
||||
#include "ServerFacade.h"
|
||||
@@ -1995,7 +1996,7 @@ void RandomPlayerbotMgr::PrepareTeleportCache()
|
||||
}
|
||||
|
||||
// add all initial position
|
||||
for (uint32 i = 1; i < MAX_RACES; i++)
|
||||
for (uint32 i = 1; i < sRaceMgr->GetMaxRaces(); i++)
|
||||
{
|
||||
for (uint32 j = 1; j < MAX_CLASSES; j++)
|
||||
{
|
||||
@@ -2008,7 +2009,7 @@ void RandomPlayerbotMgr::PrepareTeleportCache()
|
||||
|
||||
for (int32 l = 1; l <= 5; l++)
|
||||
{
|
||||
if ((1 << (i - 1)) & RACEMASK_ALLIANCE)
|
||||
if ((1 << (i - 1)) & sRaceMgr->GetAllianceRaceMask())
|
||||
allianceStarterPerLevelCache[(uint8)l].push_back(pos);
|
||||
else
|
||||
hordeStarterPerLevelCache[(uint8)l].push_back(pos);
|
||||
@@ -3126,7 +3127,7 @@ void RandomPlayerbotMgr::PrintStats()
|
||||
|
||||
std::map<uint8, uint32> lvlPerRace;
|
||||
std::map<uint8, uint32> lvlPerClass;
|
||||
for (uint8 race = RACE_HUMAN; race < MAX_RACES; ++race)
|
||||
for (uint8 race = RACE_HUMAN; race < sRaceMgr->GetMaxRaces(); ++race)
|
||||
{
|
||||
perRace[race] = 0;
|
||||
lvlPerRace[race] = 0;
|
||||
@@ -3273,7 +3274,7 @@ void RandomPlayerbotMgr::PrintStats()
|
||||
}
|
||||
|
||||
LOG_INFO("playerbots", "Bots race:");
|
||||
for (uint8 race = RACE_HUMAN; race < MAX_RACES; ++race)
|
||||
for (uint8 race = RACE_HUMAN; race < sRaceMgr->GetMaxRaces(); ++race)
|
||||
{
|
||||
if (perRace[race])
|
||||
{
|
||||
|
||||
@@ -2255,10 +2255,7 @@ void RandomItemMgr::BuildEquipCacheNew()
|
||||
continue;
|
||||
}
|
||||
|
||||
// Unobtainable or unusable items
|
||||
if (itemId == 12468 || // Chilton Wand
|
||||
itemId == 22784 || // Sunwell Orb
|
||||
itemId == 46978) // Totem of the Earthen Ring
|
||||
if (sPlayerbotAIConfig.unobtainableItems.find(itemId) != sPlayerbotAIConfig.unobtainableItems.end())
|
||||
continue;
|
||||
|
||||
equipCacheNew[proto->RequiredLevel][proto->InventoryType].push_back(itemId);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "MapMgr.h"
|
||||
#include "PathGenerator.h"
|
||||
#include "Playerbots.h"
|
||||
#include "RaceMgr.h"
|
||||
#include "TransportMgr.h"
|
||||
#include "VMapFactory.h"
|
||||
#include "VMapMgr2.h"
|
||||
@@ -3335,7 +3336,7 @@ void TravelMgr::LoadQuestTravelTable()
|
||||
|
||||
std::ostringstream out;
|
||||
|
||||
for (uint8 race = RACE_HUMAN; race < MAX_RACES; race++)
|
||||
for (uint8 race = RACE_HUMAN; race < sRaceMgr->GetMaxRaces(); race++)
|
||||
{
|
||||
for (uint8 cls = CLASS_WARRIOR; cls < MAX_CLASSES; ++cls)
|
||||
{
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "BudgetValues.h"
|
||||
#include "PathGenerator.h"
|
||||
#include "Playerbots.h"
|
||||
#include "RaceMgr.h"
|
||||
#include "ServerFacade.h"
|
||||
#include "TransportMgr.h"
|
||||
|
||||
@@ -1660,7 +1661,7 @@ void TravelNodeMap::generateStartNodes()
|
||||
startNames[RACE_GNOME] = "Dwarf and Gnome";
|
||||
startNames[RACE_TROLL] = "Orc and Troll";
|
||||
|
||||
for (uint32 i = 0; i < MAX_RACES; i++)
|
||||
for (uint32 i = 0; i < sRaceMgr->GetMaxRaces(); i++)
|
||||
{
|
||||
for (uint32 j = 0; j < MAX_CLASSES; j++)
|
||||
{
|
||||
|
||||
@@ -180,8 +180,13 @@ bool PlayerbotAIConfig::Initialize()
|
||||
"165739,165738,175245,175970,176325,176327,123329,2560"),
|
||||
disallowedGameObjects);
|
||||
LoadSet<std::set<uint32>>(
|
||||
sConfigMgr->GetOption<std::string>("AiPlayerbot.AttunementQuests", "10279,10277,10282,10283,10284,10285,10296,10297,10298,11481,11482,11488,11490,11492,10901"),
|
||||
sConfigMgr->GetOption<std::string>("AiPlayerbot.AttunementQuests", "10279,10277,10282,10283,10284,10285,10296,10297,10298,11481,11482,11488,11490,11492,10901,10888,10445,10985"),
|
||||
attunementQuests);
|
||||
|
||||
LoadSet<std::set<uint32>>(
|
||||
sConfigMgr->GetOption<std::string>("AiPlayerbot.UnobtainableItems", "12468,46978"),
|
||||
unobtainableItems);
|
||||
|
||||
botAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.BotAutologin", false);
|
||||
randomBotAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotAutologin", true);
|
||||
minRandomBots = sConfigMgr->GetOption<int32>("AiPlayerbot.MinRandomBots", 500);
|
||||
|
||||
@@ -99,6 +99,7 @@ public:
|
||||
bool tellWhenAvoidAoe;
|
||||
std::set<uint32> disallowedGameObjects;
|
||||
std::set<uint32> attunementQuests;
|
||||
std::set<uint32> unobtainableItems;
|
||||
|
||||
uint32 openGoSpell;
|
||||
bool randomBotAutologin;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
#include "BattlefieldScript.h"
|
||||
#include "Channel.h"
|
||||
#include "Config.h"
|
||||
#include "DatabaseEnv.h"
|
||||
@@ -518,12 +519,20 @@ public:
|
||||
void OnBattlegroundEnd(Battleground* bg, TeamId /*winnerTeam*/) override { bgStrategies.erase(bg->GetInstanceID()); }
|
||||
};
|
||||
|
||||
// Workaround for missing InitEnabledHooksIfNeeded for new BattlefieldScript in ScriptMgr
|
||||
class PlayerbotsBattlefieldScript : public BattlefieldScript
|
||||
{
|
||||
public:
|
||||
PlayerbotsBattlefieldScript() : BattlefieldScript("PlayerbotsBattlefieldScript") { }
|
||||
};
|
||||
|
||||
void AddPlayerbotsSecureLoginScripts();
|
||||
|
||||
void AddSC_TempestKeepBotScripts();
|
||||
|
||||
void AddPlayerbotsScripts()
|
||||
{
|
||||
new PlayerbotsBattlefieldScript();
|
||||
new PlayerbotsDatabaseScript();
|
||||
new PlayerbotsPlayerScript();
|
||||
new PlayerbotsMiscScript();
|
||||
|
||||
Reference in New Issue
Block a user