mirror of
https://github.com/liyunfan1223/mod-playerbots.git
synced 2026-06-20 15:39:25 +02:00
Merge pull request #2191 from mod-playerbots/test-staging
Test staging to master
This commit is contained in:
commit
299e4398da
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:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
|
types: [opened, synchronize, reopened, edited]
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
- test-staging
|
- 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.
|
## Pull Request Description
|
||||||
Complex player-mimicking logic is intentionally limited due to its negative impact on scalability, maintainability, and
|
<!-- Describe what this change does and why it is needed -->
|
||||||
long-term robustness.
|
|
||||||
|
|
||||||
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
|
## 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
|
## 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?
|
## Impact Assessment
|
||||||
- - [ ] No
|
<!-- As a generic test, before and after measure of pmon (playerbot pmon tick) can help you here. -->
|
||||||
- - [ ] Yes (**explain below**)
|
- 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
|
- Does this change add new decision branches or increase maintenance complexity?
|
||||||
- - [ ] More complex behavior is optional and thereby configurable
|
- [ ] 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
|
## 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.
|
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
|
We expect contributors to be honest about what they do and do not understand.
|
||||||
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
|
## Final Checklist
|
||||||
|
|
||||||
- - [ ] Stability is not compromised
|
- [ ] Stability is not compromised.
|
||||||
- - [ ] Performance impact is understood, tested, and acceptable
|
- [ ] Performance impact is understood, tested, and acceptable.
|
||||||
- - [ ] Added logic complexity is justified and explained
|
- [ ] Added logic complexity is justified and explained.
|
||||||
- - [ ] Documentation updated if needed
|
- [ ] Documentation updated if needed (Conf comments, WiKi commands).
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Notes for Reviewers
|
## Notes for Reviewers
|
||||||
|
<!-- Anything else that's helpful to review or test your pull request. -->
|
||||||
Anything that significantly improves realism at the cost of stability or performance should be carefully discussed
|
|
||||||
before merging.
|
|
||||||
|
|||||||
@ -566,7 +566,10 @@ AiPlayerbot.AutoGearScoreLimit = 0
|
|||||||
# Default: food, taxi, and raid are enabled
|
# Default: food, taxi, and raid are enabled
|
||||||
AiPlayerbot.BotCheats = "food,taxi,raid"
|
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:
|
# Default:
|
||||||
# Caverns of Time - Part 1
|
# Caverns of Time - Part 1
|
||||||
# - 10279, To The Master's Lair
|
# - 10279, To The Master's Lair
|
||||||
@ -592,7 +595,17 @@ AiPlayerbot.BotCheats = "food,taxi,raid"
|
|||||||
#
|
#
|
||||||
# Serpentshrine Cavern
|
# Serpentshrine Cavern
|
||||||
# - 10901, The Cudgel of Kar'desh
|
# - 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%)
|
# Set between 0 (0%) and 1 (100%)
|
||||||
AiPlayerbot.RandomGearLoweringChance = 0
|
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
|
# Randombots check player's gearscore level and deny the group invitation if it's too low
|
||||||
# Default: 0 (disabled)
|
# Default: 0 (disabled)
|
||||||
AiPlayerbot.GearScoreCheck = 0
|
AiPlayerbot.GearScoreCheck = 0
|
||||||
|
|||||||
@ -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;
|
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*/)
|
bool BwlChatShortcutAction::Execute(Event /*event*/)
|
||||||
{
|
{
|
||||||
Player* master = GetMaster();
|
Player* master = GetMaster();
|
||||||
|
|||||||
@ -85,6 +85,13 @@ public:
|
|||||||
bool Execute(Event event) override;
|
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
|
class BwlChatShortcutAction : public Action
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|||||||
@ -854,6 +854,11 @@ float MovementAction::GetFollowAngle()
|
|||||||
if (!group)
|
if (!group)
|
||||||
return 0.0f;
|
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;
|
uint32 index = 1;
|
||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
{
|
{
|
||||||
@ -861,7 +866,7 @@ float MovementAction::GetFollowAngle()
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (ref->GetSource() == bot)
|
if (ref->GetSource() == bot)
|
||||||
return 2 * M_PI / (group->GetMembersCount() - 1) * index;
|
return 2 * M_PI / (memberCount - 1) * index;
|
||||||
|
|
||||||
++index;
|
++index;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -187,6 +187,7 @@ public:
|
|||||||
creators["guild leave"] = &ChatActionContext::guild_leave;
|
creators["guild leave"] = &ChatActionContext::guild_leave;
|
||||||
creators["rtsc"] = &ChatActionContext::rtsc;
|
creators["rtsc"] = &ChatActionContext::rtsc;
|
||||||
creators["bwl chat shortcut"] = &ChatActionContext::bwl_chat_shortcut;
|
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["tell estimated dps"] = &ChatActionContext::tell_estimated_dps;
|
||||||
creators["join"] = &ChatActionContext::join;
|
creators["join"] = &ChatActionContext::join;
|
||||||
creators["lfg"] = &ChatActionContext::lfg;
|
creators["lfg"] = &ChatActionContext::lfg;
|
||||||
@ -298,6 +299,7 @@ private:
|
|||||||
static Action* guild_remove(PlayerbotAI* botAI) { return new GuildRemoveAction(botAI); }
|
static Action* guild_remove(PlayerbotAI* botAI) { return new GuildRemoveAction(botAI); }
|
||||||
static Action* guild_leave(PlayerbotAI* botAI) { return new GuildLeaveAction(botAI); }
|
static Action* guild_leave(PlayerbotAI* botAI) { return new GuildLeaveAction(botAI); }
|
||||||
static Action* rtsc(PlayerbotAI* botAI) { return new RTSCAction(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* bwl_chat_shortcut(PlayerbotAI* ai) { return new BwlChatShortcutAction(ai); }
|
||||||
static Action* tell_estimated_dps(PlayerbotAI* ai) { return new TellEstimatedDpsAction(ai); }
|
static Action* tell_estimated_dps(PlayerbotAI* ai) { return new TellEstimatedDpsAction(ai); }
|
||||||
static Action* join(PlayerbotAI* ai) { return new JoinGroupAction(ai); }
|
static Action* join(PlayerbotAI* ai) { return new JoinGroupAction(ai); }
|
||||||
|
|||||||
@ -127,7 +127,6 @@ public:
|
|||||||
creators["guild leave"] = &ChatTriggerContext::guild_leave;
|
creators["guild leave"] = &ChatTriggerContext::guild_leave;
|
||||||
creators["rtsc"] = &ChatTriggerContext::rtsc;
|
creators["rtsc"] = &ChatTriggerContext::rtsc;
|
||||||
creators["drink"] = &ChatTriggerContext::drink;
|
creators["drink"] = &ChatTriggerContext::drink;
|
||||||
// creators["bwl"] = &ChatTriggerContext::bwl;
|
|
||||||
creators["dps"] = &ChatTriggerContext::dps;
|
creators["dps"] = &ChatTriggerContext::dps;
|
||||||
creators["disperse"] = &ChatTriggerContext::disperse;
|
creators["disperse"] = &ChatTriggerContext::disperse;
|
||||||
creators["calc"] = &ChatTriggerContext::calc;
|
creators["calc"] = &ChatTriggerContext::calc;
|
||||||
@ -245,7 +244,6 @@ private:
|
|||||||
static Trigger* guild_leave(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "guild leave"); }
|
static Trigger* guild_leave(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "guild leave"); }
|
||||||
static Trigger* rtsc(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "rtsc"); }
|
static Trigger* rtsc(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "rtsc"); }
|
||||||
static Trigger* drink(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "drink"); }
|
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* dps(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "dps"); }
|
||||||
static Trigger* disperse(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "disperse"); }
|
static Trigger* disperse(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "disperse"); }
|
||||||
static Trigger* calc(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "calc"); }
|
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) }));
|
new TriggerNode("target", { NextAction("tell target", relevance) }));
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("ready", { NextAction("ready check", relevance) }));
|
new TriggerNode("ready", { NextAction("ready check", relevance) }));
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("naxx", {NextAction("naxx chat shortcut", relevance)}));
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("bwl", { NextAction("bwl chat shortcut", relevance) }));
|
new TriggerNode("bwl", { NextAction("bwl chat shortcut", relevance) }));
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
|
|||||||
@ -116,9 +116,9 @@ bool AttumenTheHuntsmanStackBehindAction::Execute(Event /*event*/)
|
|||||||
float rearX = attumenMounted->GetPositionX() + std::cos(orientation) * distanceBehind;
|
float rearX = attumenMounted->GetPositionX() + std::cos(orientation) * distanceBehind;
|
||||||
float rearY = attumenMounted->GetPositionY() + std::sin(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);
|
MovementPriority::MOVEMENT_FORCED, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1178,7 +1178,7 @@ bool PrinceMalchezaarNonTankAvoidInfernalAction::Execute(Event /*event*/)
|
|||||||
bot->AttackStop();
|
bot->AttackStop();
|
||||||
bot->InterruptNonMeleeSpells(true);
|
bot->InterruptNonMeleeSpells(true);
|
||||||
return MoveTo(KARAZHAN_MAP_ID, bestDestX, bestDestY, bestDestZ, false, false, false, false,
|
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();
|
bot->AttackStop();
|
||||||
return MoveTo(KARAZHAN_MAP_ID, bestDestX, bestDestY, bestDestZ, false, false, false, false,
|
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;
|
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
|
// The assist tank should stay on the boss to be 2nd on aggro and tank Hateful Bolts
|
||||||
float TheCuratorDisableTankAssistMultiplier::GetValue(Action* action)
|
float TheCuratorDisableTankAssistMultiplier::GetValue(Action* action)
|
||||||
{
|
{
|
||||||
@ -93,6 +106,19 @@ float TheCuratorDisableTankAssistMultiplier::GetValue(Action* action)
|
|||||||
return 1.0f;
|
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)
|
// Save Bloodlust/Heroism for Evocation (100% increased damage)
|
||||||
float TheCuratorDelayBloodlustAndHeroismMultiplier::GetValue(Action* action)
|
float TheCuratorDelayBloodlustAndHeroismMultiplier::GetValue(Action* action)
|
||||||
{
|
{
|
||||||
@ -350,16 +376,10 @@ float NightbaneDisableMovementMultiplier::GetValue(Action* action)
|
|||||||
|
|
||||||
if (dynamic_cast<CastBlinkBackAction*>(action) ||
|
if (dynamic_cast<CastBlinkBackAction*>(action) ||
|
||||||
dynamic_cast<CastDisengageAction*>(action) ||
|
dynamic_cast<CastDisengageAction*>(action) ||
|
||||||
dynamic_cast<FleeAction*>(action))
|
dynamic_cast<FleeAction*>(action) ||
|
||||||
return 0.0f;
|
(dynamic_cast<CombatFormationMoveAction*>(action) &&
|
||||||
|
!dynamic_cast<SetBehindTargetAction*>(action)))
|
||||||
// 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))
|
|
||||||
{
|
{
|
||||||
if (dynamic_cast<CombatFormationMoveAction*>(action))
|
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -27,6 +27,14 @@ public:
|
|||||||
virtual float GetValue(Action* action);
|
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
|
class TheCuratorDisableTankAssistMultiplier : public Multiplier
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -35,6 +43,14 @@ public:
|
|||||||
virtual float GetValue(Action* action);
|
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
|
class TheCuratorDelayBloodlustAndHeroismMultiplier : public Multiplier
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|||||||
@ -146,7 +146,9 @@ void RaidKarazhanStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers
|
|||||||
multipliers.push_back(new AttumenTheHuntsmanDisableTankAssistMultiplier(botAI));
|
multipliers.push_back(new AttumenTheHuntsmanDisableTankAssistMultiplier(botAI));
|
||||||
multipliers.push_back(new AttumenTheHuntsmanStayStackedMultiplier(botAI));
|
multipliers.push_back(new AttumenTheHuntsmanStayStackedMultiplier(botAI));
|
||||||
multipliers.push_back(new AttumenTheHuntsmanWaitForDpsMultiplier(botAI));
|
multipliers.push_back(new AttumenTheHuntsmanWaitForDpsMultiplier(botAI));
|
||||||
|
multipliers.push_back(new MaidenOfVirtueDisableCombatFormationMoveMultiplier(botAI));
|
||||||
multipliers.push_back(new TheCuratorDisableTankAssistMultiplier(botAI));
|
multipliers.push_back(new TheCuratorDisableTankAssistMultiplier(botAI));
|
||||||
|
multipliers.push_back(new TheCuratorDisableCombatFormationMoveMultiplier(botAI));
|
||||||
multipliers.push_back(new TheCuratorDelayBloodlustAndHeroismMultiplier(botAI));
|
multipliers.push_back(new TheCuratorDelayBloodlustAndHeroismMultiplier(botAI));
|
||||||
multipliers.push_back(new ShadeOfAranArcaneExplosionDisableChargeMultiplier(botAI));
|
multipliers.push_back(new ShadeOfAranArcaneExplosionDisableChargeMultiplier(botAI));
|
||||||
multipliers.push_back(new ShadeOfAranFlameWreathDisableMovementMultiplier(botAI));
|
multipliers.push_back(new ShadeOfAranFlameWreathDisableMovementMultiplier(botAI));
|
||||||
|
|||||||
@ -62,7 +62,6 @@ namespace KarazhanHelpers
|
|||||||
NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED = 16152,
|
NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED = 16152,
|
||||||
|
|
||||||
// Terestian Illhoof
|
// Terestian Illhoof
|
||||||
NPC_TERESTIAN_ILLHOOF = 15688,
|
|
||||||
NPC_DEMON_CHAINS = 17248,
|
NPC_DEMON_CHAINS = 17248,
|
||||||
NPC_KILREK = 17229,
|
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 "RaidKarazhanStrategy.h"
|
||||||
#include "RaidGruulsLairStrategy.h"
|
#include "RaidGruulsLairStrategy.h"
|
||||||
#include "RaidMagtheridonStrategy.h"
|
#include "RaidMagtheridonStrategy.h"
|
||||||
|
#include "RaidNaxxStrategy.h"
|
||||||
#include "RaidSSCStrategy.h"
|
#include "RaidSSCStrategy.h"
|
||||||
#include "RaidTempestKeepStrategy.h"
|
#include "RaidTempestKeepStrategy.h"
|
||||||
#include "RaidOsStrategy.h"
|
#include "RaidOsStrategy.h"
|
||||||
@ -28,6 +29,7 @@ public:
|
|||||||
creators["karazhan"] = &RaidStrategyContext::karazhan;
|
creators["karazhan"] = &RaidStrategyContext::karazhan;
|
||||||
creators["gruulslair"] = &RaidStrategyContext::gruulslair;
|
creators["gruulslair"] = &RaidStrategyContext::gruulslair;
|
||||||
creators["magtheridon"] = &RaidStrategyContext::magtheridon;
|
creators["magtheridon"] = &RaidStrategyContext::magtheridon;
|
||||||
|
creators["naxx"] = &RaidStrategyContext::naxx;
|
||||||
creators["ssc"] = &RaidStrategyContext::ssc;
|
creators["ssc"] = &RaidStrategyContext::ssc;
|
||||||
creators["tempestkeep"] = &RaidStrategyContext::tempestkeep;
|
creators["tempestkeep"] = &RaidStrategyContext::tempestkeep;
|
||||||
creators["wotlk-os"] = &RaidStrategyContext::wotlk_os;
|
creators["wotlk-os"] = &RaidStrategyContext::wotlk_os;
|
||||||
@ -45,6 +47,7 @@ private:
|
|||||||
static Strategy* karazhan(PlayerbotAI* botAI) { return new RaidKarazhanStrategy(botAI); }
|
static Strategy* karazhan(PlayerbotAI* botAI) { return new RaidKarazhanStrategy(botAI); }
|
||||||
static Strategy* gruulslair(PlayerbotAI* botAI) { return new RaidGruulsLairStrategy(botAI); }
|
static Strategy* gruulslair(PlayerbotAI* botAI) { return new RaidGruulsLairStrategy(botAI); }
|
||||||
static Strategy* magtheridon(PlayerbotAI* botAI) { return new RaidMagtheridonStrategy(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* ssc(PlayerbotAI* botAI) { return new RaidSSCStrategy(botAI); }
|
||||||
static Strategy* tempestkeep(PlayerbotAI* botAI) { return new RaidTempestKeepStrategy(botAI); }
|
static Strategy* tempestkeep(PlayerbotAI* botAI) { return new RaidTempestKeepStrategy(botAI); }
|
||||||
static Strategy* wotlk_os(PlayerbotAI* botAI) { return new RaidOsStrategy(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;
|
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, consumableSubClasses) ||
|
||||||
substrContainsInMap<uint32>(text, tradeSubClasses) || substrContainsInMap<uint32>(text, itemQualities) ||
|
substrContainsInMap<uint32>(text, tradeSubClasses) || substrContainsInMap<uint32>(text, itemQualities) ||
|
||||||
substrContainsInMap<uint32>(text, slots) || substrContainsInMap<ChatMsg>(text, chats) ||
|
substrContainsInMap<uint32>(text, slots) || substrContainsInMap<ChatMsg>(text, chats) ||
|
||||||
|
|||||||
@ -66,7 +66,7 @@ public:
|
|||||||
static uint32 parseSlot(std::string const text);
|
static uint32 parseSlot(std::string const text);
|
||||||
uint32 parseSkill(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);
|
void eraseAllSubStr(std::string& mainStr, std::string const toErase);
|
||||||
|
|
||||||
|
|||||||
@ -4,59 +4,17 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "AiObjectContext.h"
|
#include "AiObjectContext.h"
|
||||||
|
#include "Helpers.h"
|
||||||
#include "ActionContext.h"
|
|
||||||
#include "ChatActionContext.h"
|
|
||||||
#include "ChatTriggerContext.h"
|
|
||||||
#include "DKAiObjectContext.h"
|
#include "DKAiObjectContext.h"
|
||||||
#include "DruidAiObjectContext.h"
|
#include "DruidAiObjectContext.h"
|
||||||
#include "HunterAiObjectContext.h"
|
#include "HunterAiObjectContext.h"
|
||||||
#include "MageAiObjectContext.h"
|
#include "MageAiObjectContext.h"
|
||||||
#include "PaladinAiObjectContext.h"
|
#include "PaladinAiObjectContext.h"
|
||||||
#include "Playerbots.h"
|
|
||||||
#include "PriestAiObjectContext.h"
|
#include "PriestAiObjectContext.h"
|
||||||
#include "RogueAiObjectContext.h"
|
#include "RogueAiObjectContext.h"
|
||||||
#include "ShamanAiObjectContext.h"
|
#include "ShamanAiObjectContext.h"
|
||||||
#include "SharedValueContext.h"
|
|
||||||
#include "StrategyContext.h"
|
|
||||||
#include "TriggerContext.h"
|
|
||||||
#include "ValueContext.h"
|
|
||||||
#include "WarlockAiObjectContext.h"
|
#include "WarlockAiObjectContext.h"
|
||||||
#include "WarriorAiObjectContext.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<Strategy> AiObjectContext::sharedStrategyContexts;
|
||||||
SharedNamedObjectContextList<Action> AiObjectContext::sharedActionContexts;
|
SharedNamedObjectContextList<Action> AiObjectContext::sharedActionContexts;
|
||||||
@ -98,93 +56,6 @@ void AiObjectContext::BuildSharedContexts()
|
|||||||
BuildSharedValueContexts(sharedValueContexts);
|
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> AiObjectContext::Save()
|
||||||
{
|
{
|
||||||
std::vector<std::string> result;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ChatHelper::parseable(command))
|
if (!ChatHelper::parseableItem(command))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
HandleCommand("c", command, owner);
|
HandleCommand("c", command, owner);
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
#include "ArenaTeamMgr.h"
|
#include "ArenaTeamMgr.h"
|
||||||
#include "DatabaseEnv.h"
|
#include "DatabaseEnv.h"
|
||||||
#include "PlayerbotAI.h"
|
#include "PlayerbotAI.h"
|
||||||
|
#include "RaceMgr.h"
|
||||||
#include "ScriptMgr.h"
|
#include "ScriptMgr.h"
|
||||||
#include "SharedDefines.h"
|
#include "SharedDefines.h"
|
||||||
#include "SocialMgr.h"
|
#include "SocialMgr.h"
|
||||||
@ -60,7 +61,7 @@ Player* RandomPlayerbotFactory::CreateRandomBot(WorldSession* session, uint8 cls
|
|||||||
const bool alliance = static_cast<bool>(urand(0, 1));
|
const bool alliance = static_cast<bool>(urand(0, 1));
|
||||||
|
|
||||||
std::vector<uint8> raceOptions;
|
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
|
// skip disabled with config races
|
||||||
if ((1 << (race - 1)) & sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_DISABLED_RACEMASK))
|
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)
|
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;
|
std::string strategyName;
|
||||||
switch (mapId)
|
switch (mapId)
|
||||||
{
|
{
|
||||||
@ -1550,6 +1565,9 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
|
|||||||
case 532:
|
case 532:
|
||||||
strategyName = "karazhan"; // Karazhan
|
strategyName = "karazhan"; // Karazhan
|
||||||
break;
|
break;
|
||||||
|
case 533:
|
||||||
|
strategyName = "naxx"; // Naxxramas
|
||||||
|
break;
|
||||||
case 544:
|
case 544:
|
||||||
strategyName = "magtheridon"; // Magtheridon's Lair
|
strategyName = "magtheridon"; // Magtheridon's Lair
|
||||||
break;
|
break;
|
||||||
@ -1628,10 +1646,13 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strategyName.empty())
|
if (strategyName.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
engines[BOT_STATE_COMBAT]->addStrategy(strategyName);
|
engines[BOT_STATE_COMBAT]->addStrategy(strategyName);
|
||||||
engines[BOT_STATE_NON_COMBAT]->addStrategy(strategyName);
|
engines[BOT_STATE_NON_COMBAT]->addStrategy(strategyName);
|
||||||
|
|
||||||
if (tellMaster && !strategyName.empty())
|
if (tellMaster && !strategyName.empty())
|
||||||
{
|
{
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
|
|||||||
@ -37,6 +37,7 @@
|
|||||||
#include "PlayerbotFactory.h"
|
#include "PlayerbotFactory.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "Position.h"
|
#include "Position.h"
|
||||||
|
#include "RaceMgr.h"
|
||||||
#include "Random.h"
|
#include "Random.h"
|
||||||
#include "RandomPlayerbotFactory.h"
|
#include "RandomPlayerbotFactory.h"
|
||||||
#include "ServerFacade.h"
|
#include "ServerFacade.h"
|
||||||
@ -1995,7 +1996,7 @@ void RandomPlayerbotMgr::PrepareTeleportCache()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add all initial position
|
// 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++)
|
for (uint32 j = 1; j < MAX_CLASSES; j++)
|
||||||
{
|
{
|
||||||
@ -2008,7 +2009,7 @@ void RandomPlayerbotMgr::PrepareTeleportCache()
|
|||||||
|
|
||||||
for (int32 l = 1; l <= 5; l++)
|
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);
|
allianceStarterPerLevelCache[(uint8)l].push_back(pos);
|
||||||
else
|
else
|
||||||
hordeStarterPerLevelCache[(uint8)l].push_back(pos);
|
hordeStarterPerLevelCache[(uint8)l].push_back(pos);
|
||||||
@ -3126,7 +3127,7 @@ void RandomPlayerbotMgr::PrintStats()
|
|||||||
|
|
||||||
std::map<uint8, uint32> lvlPerRace;
|
std::map<uint8, uint32> lvlPerRace;
|
||||||
std::map<uint8, uint32> lvlPerClass;
|
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;
|
perRace[race] = 0;
|
||||||
lvlPerRace[race] = 0;
|
lvlPerRace[race] = 0;
|
||||||
@ -3273,7 +3274,7 @@ void RandomPlayerbotMgr::PrintStats()
|
|||||||
}
|
}
|
||||||
|
|
||||||
LOG_INFO("playerbots", "Bots race:");
|
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])
|
if (perRace[race])
|
||||||
{
|
{
|
||||||
|
|||||||
@ -2255,10 +2255,7 @@ void RandomItemMgr::BuildEquipCacheNew()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unobtainable or unusable items
|
if (sPlayerbotAIConfig.unobtainableItems.find(itemId) != sPlayerbotAIConfig.unobtainableItems.end())
|
||||||
if (itemId == 12468 || // Chilton Wand
|
|
||||||
itemId == 22784 || // Sunwell Orb
|
|
||||||
itemId == 46978) // Totem of the Earthen Ring
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
equipCacheNew[proto->RequiredLevel][proto->InventoryType].push_back(itemId);
|
equipCacheNew[proto->RequiredLevel][proto->InventoryType].push_back(itemId);
|
||||||
|
|||||||
@ -14,6 +14,7 @@
|
|||||||
#include "MapMgr.h"
|
#include "MapMgr.h"
|
||||||
#include "PathGenerator.h"
|
#include "PathGenerator.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
#include "RaceMgr.h"
|
||||||
#include "TransportMgr.h"
|
#include "TransportMgr.h"
|
||||||
#include "VMapFactory.h"
|
#include "VMapFactory.h"
|
||||||
#include "VMapMgr2.h"
|
#include "VMapMgr2.h"
|
||||||
@ -3335,7 +3336,7 @@ void TravelMgr::LoadQuestTravelTable()
|
|||||||
|
|
||||||
std::ostringstream out;
|
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)
|
for (uint8 cls = CLASS_WARRIOR; cls < MAX_CLASSES; ++cls)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
#include "BudgetValues.h"
|
#include "BudgetValues.h"
|
||||||
#include "PathGenerator.h"
|
#include "PathGenerator.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
#include "RaceMgr.h"
|
||||||
#include "ServerFacade.h"
|
#include "ServerFacade.h"
|
||||||
#include "TransportMgr.h"
|
#include "TransportMgr.h"
|
||||||
|
|
||||||
@ -1660,7 +1661,7 @@ void TravelNodeMap::generateStartNodes()
|
|||||||
startNames[RACE_GNOME] = "Dwarf and Gnome";
|
startNames[RACE_GNOME] = "Dwarf and Gnome";
|
||||||
startNames[RACE_TROLL] = "Orc and Troll";
|
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++)
|
for (uint32 j = 0; j < MAX_CLASSES; j++)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -180,8 +180,13 @@ bool PlayerbotAIConfig::Initialize()
|
|||||||
"165739,165738,175245,175970,176325,176327,123329,2560"),
|
"165739,165738,175245,175970,176325,176327,123329,2560"),
|
||||||
disallowedGameObjects);
|
disallowedGameObjects);
|
||||||
LoadSet<std::set<uint32>>(
|
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);
|
attunementQuests);
|
||||||
|
|
||||||
|
LoadSet<std::set<uint32>>(
|
||||||
|
sConfigMgr->GetOption<std::string>("AiPlayerbot.UnobtainableItems", "12468,46978"),
|
||||||
|
unobtainableItems);
|
||||||
|
|
||||||
botAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.BotAutologin", false);
|
botAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.BotAutologin", false);
|
||||||
randomBotAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotAutologin", true);
|
randomBotAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotAutologin", true);
|
||||||
minRandomBots = sConfigMgr->GetOption<int32>("AiPlayerbot.MinRandomBots", 500);
|
minRandomBots = sConfigMgr->GetOption<int32>("AiPlayerbot.MinRandomBots", 500);
|
||||||
|
|||||||
@ -99,6 +99,7 @@ public:
|
|||||||
bool tellWhenAvoidAoe;
|
bool tellWhenAvoidAoe;
|
||||||
std::set<uint32> disallowedGameObjects;
|
std::set<uint32> disallowedGameObjects;
|
||||||
std::set<uint32> attunementQuests;
|
std::set<uint32> attunementQuests;
|
||||||
|
std::set<uint32> unobtainableItems;
|
||||||
|
|
||||||
uint32 openGoSpell;
|
uint32 openGoSpell;
|
||||||
bool randomBotAutologin;
|
bool randomBotAutologin;
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
|
||||||
|
#include "BattlefieldScript.h"
|
||||||
#include "Channel.h"
|
#include "Channel.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "DatabaseEnv.h"
|
#include "DatabaseEnv.h"
|
||||||
@ -518,12 +519,20 @@ public:
|
|||||||
void OnBattlegroundEnd(Battleground* bg, TeamId /*winnerTeam*/) override { bgStrategies.erase(bg->GetInstanceID()); }
|
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 AddPlayerbotsSecureLoginScripts();
|
||||||
|
|
||||||
void AddSC_TempestKeepBotScripts();
|
void AddSC_TempestKeepBotScripts();
|
||||||
|
|
||||||
void AddPlayerbotsScripts()
|
void AddPlayerbotsScripts()
|
||||||
{
|
{
|
||||||
|
new PlayerbotsBattlefieldScript();
|
||||||
new PlayerbotsDatabaseScript();
|
new PlayerbotsDatabaseScript();
|
||||||
new PlayerbotsPlayerScript();
|
new PlayerbotsPlayerScript();
|
||||||
new PlayerbotsMiscScript();
|
new PlayerbotsMiscScript();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user