Merge pull request #700 from Bobblybook/master

Dungeon strat improvements (GD & UP)
This commit is contained in:
Bobblybook
2024-11-10 04:05:01 +11:00
committed by GitHub
15 changed files with 148 additions and 7 deletions

View File

@@ -10,10 +10,12 @@ class WotlkDungeonGDActionContext : public NamedObjectContext<Action>
public:
WotlkDungeonGDActionContext() {
creators["avoid poison nova"] = &WotlkDungeonGDActionContext::avoid_poison_nova;
creators["attack snake wrap"] = &WotlkDungeonGDActionContext::attack_snake_wrap;
creators["avoid whirling slash"] = &WotlkDungeonGDActionContext::avoid_whirling_slash;
}
private:
static Action* avoid_poison_nova(PlayerbotAI* ai) { return new AvoidPoisonNovaAction(ai); }
static Action* attack_snake_wrap(PlayerbotAI* ai) { return new AttackSnakeWrapAction(ai); }
static Action* avoid_whirling_slash(PlayerbotAI* ai) { return new AvoidWhirlingSlashAction(ai); }
};

View File

@@ -19,6 +19,32 @@ bool AvoidPoisonNovaAction::Execute(Event event)
return false;
}
bool AttackSnakeWrapAction::Execute(Event event)
{
Unit* boss = AI_VALUE2(Unit*, "find target", "slad'ran");
if (!boss) { return false; }
Unit* snakeWrap = nullptr;
// Target is not findable from threat table using AI_VALUE2(),
// therefore need to search manually for the unit name
GuidVector targets = AI_VALUE(GuidVector, "possible targets no los");
for (auto& target : targets)
{
Unit* unit = botAI->GetUnit(target);
if (unit && unit->GetEntry() == NPC_SNAKE_WRAP)
{
Unit* currentTarget = AI_VALUE(Unit*, "current target");
if (!currentTarget || currentTarget->GetEntry() != NPC_SNAKE_WRAP)
{
return Attack(unit);
}
}
}
return false;
}
bool AvoidWhirlingSlashAction::Execute(Event event)
{
Unit* boss = AI_VALUE2(Unit*, "find target", "gal'darah");

View File

@@ -14,6 +14,13 @@ public:
bool Execute(Event event) override;
};
class AttackSnakeWrapAction : public AttackAction
{
public:
AttackSnakeWrapAction(PlayerbotAI* ai) : AttackAction(ai, "attack snake wrap") {}
bool Execute(Event event) override;
};
class AvoidWhirlingSlashAction : public MovementAction
{
public:

View File

@@ -11,13 +11,38 @@ float SladranMultiplier::GetValue(Action* action)
Unit* boss = AI_VALUE2(Unit*, "find target", "slad'ran");
if (!boss) { return 1.0f; }
if (boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_POISON_NOVA))
if (boss->FindCurrentSpellBySpellId(SPELL_POISON_NOVA))
{
if (dynamic_cast<MovementAction*>(action) && !dynamic_cast<AvoidPoisonNovaAction*>(action))
{
return 0.0f;
}
}
if (!botAI->IsDps(bot)) { return 1.0f; }
if (action->getThreatType() == Action::ActionThreatType::Aoe)
{
return 0.0f;
}
Unit* snakeWrap = nullptr;
GuidVector targets = AI_VALUE(GuidVector, "possible targets no los");
for (auto& target : targets)
{
Unit* unit = botAI->GetUnit(target);
if (unit && unit->GetEntry() == NPC_SNAKE_WRAP)
{
snakeWrap = unit;
break;
}
}
// Prevent auto-target acquisition during snake wraps
if (snakeWrap && dynamic_cast<DpsAssistAction*>(action))
{
return 0.0f;
}
return 1.0f;
}

View File

@@ -13,13 +13,14 @@ void WotlkDungeonGDStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
// Will re-test in heroic, decent dps groups should be able to blast him down with no funky strats.
triggers.push_back(new TriggerNode("poison nova",
NextAction::array(0, new NextAction("avoid poison nova", ACTION_RAID + 5), nullptr)));
triggers.push_back(new TriggerNode("snake wrap",
NextAction::array(0, new NextAction("attack snake wrap", ACTION_RAID + 4), nullptr)));
// Gal'darah
triggers.push_back(new TriggerNode("whirling slash",
NextAction::array(0, new NextAction("avoid whirling slash", ACTION_RAID + 5), nullptr)));
// Eck the Ferocious (Heroic only)
// TODO
}
void WotlkDungeonGDStrategy::InitMultipliers(std::vector<Multiplier*> &multipliers)

View File

@@ -11,10 +11,12 @@ class WotlkDungeonGDTriggerContext : public NamedObjectContext<Trigger>
WotlkDungeonGDTriggerContext()
{
creators["poison nova"] = &WotlkDungeonGDTriggerContext::poison_nova;
creators["snake wrap"] = &WotlkDungeonGDTriggerContext::snake_wrap;
creators["whirling slash"] = &WotlkDungeonGDTriggerContext::whirling_slash;
}
private:
static Trigger* poison_nova(PlayerbotAI* ai) { return new SladranPoisonNovaTrigger(ai); }
static Trigger* snake_wrap(PlayerbotAI* ai) { return new SladranSnakeWrapTrigger(ai); }
static Trigger* whirling_slash(PlayerbotAI* ai) { return new GaldarahWhirlingSlashTrigger(ai); }
};

View File

@@ -8,7 +8,26 @@ bool SladranPoisonNovaTrigger::IsActive()
Unit* boss = AI_VALUE2(Unit*, "find target", "slad'ran");
if (!boss) { return false; }
return boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_POISON_NOVA);
return bool(boss->FindCurrentSpellBySpellId(SPELL_POISON_NOVA));
}
bool SladranSnakeWrapTrigger::IsActive()
{
if (!botAI->IsDps(bot)) { return false; }
// Target is not findable from threat table using AI_VALUE2(),
// therefore need to search manually for the unit name
GuidVector targets = AI_VALUE(GuidVector, "possible targets");
for (auto& target : targets)
{
Unit* unit = botAI->GetUnit(target);
if (unit && unit->GetEntry() == NPC_SNAKE_WRAP)
{
return true;
}
}
return false;
}
bool GaldarahWhirlingSlashTrigger::IsActive()

View File

@@ -11,6 +11,7 @@ enum GundrakIDs
// Slad'ran
SPELL_POISON_NOVA_N = 55081,
SPELL_POISON_NOVA_H = 59842,
NPC_SNAKE_WRAP = 29742,
// Gal'darah
SPELL_WHIRLING_SLASH_N = 55250,
@@ -27,6 +28,13 @@ public:
bool IsActive() override;
};
class SladranSnakeWrapTrigger : public Trigger
{
public:
SladranSnakeWrapTrigger(PlayerbotAI* ai) : Trigger(ai, "slad'ran snake wrap") {}
bool IsActive() override;
};
class GaldarahWhirlingSlashTrigger : public Trigger
{
public:

View File

@@ -11,10 +11,12 @@ class WotlkDungeonUPActionContext : public NamedObjectContext<Action>
WotlkDungeonUPActionContext() {
creators["avoid freezing cloud"] = &WotlkDungeonUPActionContext::avoid_freezing_cloud;
creators["avoid skadi whirlwind"] = &WotlkDungeonUPActionContext::avoid_whirlwind;
creators["stop attack"] = &WotlkDungeonUPActionContext::stop_attack;
}
private:
static Action* avoid_freezing_cloud(PlayerbotAI* ai) { return new AvoidFreezingCloudAction(ai); }
static Action* avoid_whirlwind(PlayerbotAI* ai) { return new AvoidSkadiWhirlwindAction(ai); }
static Action* stop_attack(PlayerbotAI* ai) { return new DropTargetAction(ai); }
};
#endif

View File

@@ -82,3 +82,18 @@ float SkadiMultiplier::GetValue(Action* action)
return 1.0f;
}
float YmironMultiplier::GetValue(Action* action)
{
Unit* boss = AI_VALUE2(Unit*, "find target", "king ymiron");
if (!boss) { return 1.0f; }
if (boss->FindCurrentSpellBySpellId(SPELL_BANE) || boss->HasAura(SPELL_BANE))
{
if (dynamic_cast<AttackAction*>(action))
{
return 0.0f;
}
}
return 1.0f;
}

View File

@@ -12,4 +12,13 @@ class SkadiMultiplier : public Multiplier
virtual float GetValue(Action* action);
};
class YmironMultiplier : public Multiplier
{
public:
YmironMultiplier(PlayerbotAI* ai) : Multiplier(ai, "king ymiron") {}
public:
virtual float GetValue(Action* action);
};
#endif

View File

@@ -17,9 +17,12 @@ void WotlkDungeonUPStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
// King Ymiron
// May need to avoid orb.. unclear if the generic avoid AoE does this well
triggers.push_back(new TriggerNode("ymiron bane",
NextAction::array(0, new NextAction("stop attack", ACTION_RAID + 5), nullptr)));
}
void WotlkDungeonUPStrategy::InitMultipliers(std::vector<Multiplier*> &multipliers)
{
multipliers.push_back(new SkadiMultiplier(botAI));
multipliers.push_back(new YmironMultiplier(botAI));
}

View File

@@ -12,10 +12,12 @@ class WotlkDungeonUPTriggerContext : public NamedObjectContext<Trigger>
{
creators["freezing cloud"] = &WotlkDungeonUPTriggerContext::freezing_cloud;
creators["skadi whirlwind"] = &WotlkDungeonUPTriggerContext::whirlwind;
creators["ymiron bane"] = &WotlkDungeonUPTriggerContext::bane;
}
private:
static Trigger* freezing_cloud(PlayerbotAI* ai) { return new SkadiFreezingCloudTrigger(ai); }
static Trigger* whirlwind(PlayerbotAI* ai) { return new SkadiWhirlwindTrigger(ai); }
static Trigger* bane(PlayerbotAI* ai) { return new YmironBaneTrigger(ai); }
};
#endif

View File

@@ -46,3 +46,11 @@ bool SkadiWhirlwindTrigger::IsActive()
Unit* boss = AI_VALUE2(Unit*, "find target", "skadi the ruthless");
return boss && boss->HasAura(SPELL_SKADI_WHIRLWIND);
}
bool YmironBaneTrigger::IsActive()
{
Unit* boss = AI_VALUE2(Unit*, "find target", "king ymiron");
if (!boss) { return false; }
return boss->FindCurrentSpellBySpellId(SPELL_BANE) || boss->HasAura(SPELL_BANE);
}

View File

@@ -15,10 +15,15 @@ enum UtgardePinnacleIDs
NPC_BREATH_TRIGGER = 28351,
SPELL_SKADI_WHIRLWIND_N = 50228,
SPELL_SKADI_WHIRLWIND_H = 59322,
// King Ymiron
SPELL_BANE_N = 48294,
SPELL_BANE_H = 59301,
};
#define SPELL_FREEZING_CLOUD DUNGEON_MODE(bot, SPELL_FREEZING_CLOUD_N, SPELL_FREEZING_CLOUD_H)
#define SPELL_SKADI_WHIRLWIND DUNGEON_MODE(bot, SPELL_SKADI_WHIRLWIND_N, SPELL_SKADI_WHIRLWIND_H)
#define SPELL_BANE DUNGEON_MODE(bot, SPELL_BANE_N, SPELL_BANE_H)
// const float SKADI_BREATH_CENTRELINE = -512.46875f;
@@ -36,4 +41,11 @@ public:
bool IsActive() override;
};
class YmironBaneTrigger : public Trigger
{
public:
YmironBaneTrigger(PlayerbotAI* ai) : Trigger(ai, "ymiron bane") {}
bool IsActive() override;
};
#endif