mirror of
https://github.com/ZhengPeiRu21/mod-individual-progression
synced 2025-11-29 23:44:51 +08:00
Feat. Playerbots naxx 40 Integration (#749)
Override default Naxx playerbots strategy to include Naxx40 bosses.
This commit is contained in:
@@ -6,6 +6,9 @@
|
||||
#include "IndividualProgressionOnyxiaActions.h"
|
||||
#include "RaidOnyxiaActionContext.h"
|
||||
|
||||
#include "IndividualProgressionNaxxActions.h"
|
||||
#include "RaidNaxxActionContext.h"
|
||||
|
||||
class IPOnyxiaActionContext : public RaidOnyxiaActionContext
|
||||
{
|
||||
public:
|
||||
@@ -18,5 +21,20 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class IPNaxxActionContext : public RaidNaxxActionContext
|
||||
{
|
||||
public:
|
||||
IPNaxxActionContext : public RaidNaxxActionContext()
|
||||
{
|
||||
creators["grobbulus go behind"] = &IPNaxxActionContext::go_behind_the_boss;
|
||||
creators["rotate grobbulus"] = &IPNaxxActionContext::rotate_grobbulus;
|
||||
}
|
||||
|
||||
private:
|
||||
static Action* go_behind_the_boss(PlayerbotAI* ai) { return new IPGrobbulusGoBehindAction(ai); }
|
||||
static Action* rotate_grobbulus(PlayerbotAI* ai) { return new IPGrobbulusRotateAction(ai); }
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,69 @@
|
||||
// /*
|
||||
// * Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
// and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
// */
|
||||
#ifdef MOD_PLAYERBOTS
|
||||
#ifndef MOD_INDIVIDUAL_PROGRESSION_TRIGGER_CONTEXT_H
|
||||
#define MOD_INDIVIDUAL_PROGRESSION_TRIGGER_CONTEXT_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidNaxxTriggers.h"
|
||||
#include "IndividualProgressionNaxxTriggers.h"
|
||||
|
||||
|
||||
class IPRaidNaxxTriggerContext : public RaidNaxxTriggerContext
|
||||
{
|
||||
public:
|
||||
IPRaidNaxxTriggerContext : RaidNaxxTriggerContext()
|
||||
{
|
||||
creators["grobbulus cloud"] = &IPRaidNaxxTriggerContext::grobbulus_cloud;
|
||||
creators["heigan melee"] = &IPRaidNaxxTriggerContext::heigan_melee;
|
||||
creators["heigan ranged"] = &IPRaidNaxxTriggerContext::heigan_ranged;
|
||||
|
||||
creators["thaddius phase pet"] = &IPRaidNaxxTriggerContext::thaddius_phase_pet;
|
||||
creators["thaddius phase transition"] = &IPRaidNaxxTriggerContext::thaddius_phase_transition;
|
||||
creators["thaddius phase thaddius"] = &IPRaidNaxxTriggerContext::thaddius_phase_thaddius;
|
||||
|
||||
creators["razuvious tank"] = &IPRaidNaxxTriggerContext::razuvious_tank;
|
||||
creators["razuvious nontank"] = &IPRaidNaxxTriggerContext::razuvious_nontank;
|
||||
|
||||
creators["horseman attractors"] = &IPRaidNaxxTriggerContext::horseman_attractors;
|
||||
creators["horseman except attractors"] = &IPRaidNaxxTriggerContext::horseman_except_attractors;
|
||||
|
||||
creators["sapphiron ground"] = &IPRaidNaxxTriggerContext::sapphiron_ground;
|
||||
creators["sapphiron flight"] = &IPRaidNaxxTriggerContext::sapphiron_flight;
|
||||
|
||||
creators["kel'thuzad"] = &IPRaidNaxxTriggerContext::kelthuzad;
|
||||
|
||||
creators["gluth"] = &IPRaidNaxxTriggerContext::gluth;
|
||||
creators["gluth main tank mortal wound"] = &IPRaidNaxxTriggerContext::gluth_main_tank_mortal_wound;
|
||||
|
||||
creators["loatheb"] = &IPRaidNaxxTriggerContext::loatheb;
|
||||
}
|
||||
|
||||
private:
|
||||
static Trigger* grobbulus_cloud(PlayerbotAI* ai) { return new IPGrobbulusCloudTrigger(ai); }
|
||||
static Trigger* heigan_melee(PlayerbotAI* ai) { return new IPHeiganMeleeTrigger(ai); }
|
||||
static Trigger* heigan_ranged(PlayerbotAI* ai) { return new IPHeiganRangedTrigger(ai); }
|
||||
|
||||
static Trigger* thaddius_phase_pet(PlayerbotAI* ai) { return new IPThaddiusPhasePetTrigger(ai); }
|
||||
static Trigger* thaddius_phase_transition(PlayerbotAI* ai) { return new IPThaddiusPhaseTransitionTrigger(ai); }
|
||||
static Trigger* thaddius_phase_thaddius(PlayerbotAI* ai) { return new IPThaddiusPhaseThaddiusTrigger(ai); }
|
||||
static Trigger* razuvious_tank(PlayerbotAI* ai) { return new IPRazuviousTankTrigger(ai); }
|
||||
static Trigger* razuvious_nontank(PlayerbotAI* ai) { return new IPRazuviousNontankTrigger(ai); }
|
||||
|
||||
static Trigger* horseman_attractors(PlayerbotAI* ai) { return new IPHorsemanAttractorsTrigger(ai); }
|
||||
static Trigger* horseman_except_attractors(PlayerbotAI* ai) { return new IPHorsemanExceptAttractorsTrigger(ai); }
|
||||
|
||||
static Trigger* sapphiron_ground(PlayerbotAI* ai) { return new IPSapphironGroundTrigger(ai); }
|
||||
static Trigger* sapphiron_flight(PlayerbotAI* ai) { return new IPSapphironFlightTrigger(ai); }
|
||||
static Trigger* kelthuzad(PlayerbotAI* ai) { return new IPKelthuzadTrigger(ai); }
|
||||
static Trigger* anubrekhan(PlayerbotAI* ai) { return new IPAnubrekhanTrigger(ai); }
|
||||
static Trigger* gluth(PlayerbotAI* ai) { return new IPGluthTrigger(ai); }
|
||||
static Trigger* gluth_main_tank_mortal_wound(PlayerbotAI* ai) { return new IPGluthMainTankMortalWoundTrigger(ai); }
|
||||
static Trigger* loatheb(PlayerbotAI* ai) { return new IPLoathebTrigger(ai); }
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
22
src/playerbots/IndividualProgressionStrategyOverride.cpp
Normal file
22
src/playerbots/IndividualProgressionStrategyOverride.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifdef MOD_PLAYERBOTS
|
||||
|
||||
#include "IndividualProgressionStrategyOverride.h"
|
||||
#include "IndividualProgressionNaxxMultipliers.h"
|
||||
#include "RaidNaxxMultipliers.h"
|
||||
|
||||
void IPRaidNaxxStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||
{
|
||||
multipliers.push_back(new GrobbulusMultiplier(botAI));
|
||||
multipliers.push_back(new IPHeiganDanceMultiplier(botAI));
|
||||
multipliers.push_back(new LoathebGenericMultiplier(botAI));
|
||||
multipliers.push_back(new IPThaddiusGenericMultiplier(botAI));
|
||||
multipliers.push_back(new IPSapphironGenericMultiplier(botAI));
|
||||
multipliers.push_back(new IPInstructorRazuviousGenericMultiplier(botAI));
|
||||
multipliers.push_back(new IPKelthuzadGenericMultiplier(botAI));
|
||||
multipliers.push_back(new AnubrekhanGenericMultiplier(botAI));
|
||||
multipliers.push_back(new FourhorsemanGenericMultiplier(botAI));
|
||||
// multipliers.push_back(new GothikGenericMultiplier(botAI));
|
||||
multipliers.push_back(new IPGluthGenericMultiplier(botAI));
|
||||
}
|
||||
|
||||
#endif
|
||||
17
src/playerbots/IndividualProgressionStrategyOverride.h
Normal file
17
src/playerbots/IndividualProgressionStrategyOverride.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifdef MOD_PLAYERBOTS
|
||||
#ifndef MOD_INDIVIDUAL_PROGRESSION_STRATEGY_OVERRIDE_H
|
||||
#define MOD_INDIVIDUAL_PROGRESSION_STRATEGY_OVERRIDE_H
|
||||
|
||||
#include "RaidNaxxStrategy.h"
|
||||
#include "Multiplier.h"
|
||||
|
||||
class IPRaidNaxxStrategy : public RaidNaxxStrategy
|
||||
{
|
||||
public:
|
||||
IPRaidNaxxStrategy(PlayerbotAI* ai) : RaidNaxxStrategy(ai) {}
|
||||
virtual void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
#endif
|
||||
143
src/playerbots/raids/Naxx40/IndividualProgressionNaxxActions.cpp
Normal file
143
src/playerbots/raids/Naxx40/IndividualProgressionNaxxActions.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
#ifdef MOD_PLAYERBOTS
|
||||
|
||||
#include "IndividualProgressionNaxxActions.h"
|
||||
#include "RaidNaxxActions.h"
|
||||
#include "IndividualProgressionNaxxAi40.h"
|
||||
|
||||
#include "LastMovementValue.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "Playerbots.h"
|
||||
#include "RaidNaxxBossHelper.h"
|
||||
#include "RaidNaxxStrategy.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "SharedDefines.h"
|
||||
|
||||
bool IPGrobbulusGoBehindAction::Execute(Event event)
|
||||
{
|
||||
Unit* boss = AI_VALUE(Unit*, "boss target");
|
||||
if (!boss
|
||||
|| (boss->GetEntry() != 15931 // Default Azerothcore Grobbulus
|
||||
&& boss->GetEntry() != 351003)) // mod-individual-progression Grobbulus
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
uint32 IPGrobbulusRotateAction::GetCurrWaypoint()
|
||||
{
|
||||
Unit* boss = AI_VALUE(Unit*, "boss target");
|
||||
if (!boss
|
||||
|| (boss->GetEntry() != 15931 // Default AzerothCore Grobbulus
|
||||
&& boss->GetEntry() != 351003)) // mod-individual-progression Grobbulus
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto* ai = boss->GetAI();
|
||||
if (!ai)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
EventMap* eventMap = nullptr;
|
||||
const char* typeName = typeid(*ai).name();
|
||||
if (std::string(typeName).find("boss_grobbulus_40") != std::string::npos)
|
||||
{
|
||||
auto* boss_ai = reinterpret_cast<BossAiGrobbulus40*>(ai);
|
||||
if (!boss_ai->events.Empty())
|
||||
eventMap = &boss_ai->events;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto* boss_ai = dynamic_cast<Grobbulus::boss_grobbulus::boss_grobbulusAI*>(ai);
|
||||
if (!boss_ai || boss_ai->events.Empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
eventMap = &boss_ai->events;
|
||||
}
|
||||
if (!eventMap || eventMap->Empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (eventMap->GetTimer() > 1000000)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const uint32 event_time = eventMap->GetNextEventTime(2); // EVENT_DECEPIT_FEVER
|
||||
return (event_time / 15000) % intervals;
|
||||
}
|
||||
|
||||
|
||||
bool IPHeiganDanceAction::CalculateSafe()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "heigan the unclean");
|
||||
if (!boss || boss->isDead())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto* ai = boss->GetAI();
|
||||
if (!ai)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
EventMap* eventMap = nullptr;
|
||||
uint32 curr_phase = 0;
|
||||
const char* typeName = typeid(*ai).name();
|
||||
if (std::string(typeName).find("boss_heigan_40") != std::string::npos)
|
||||
{
|
||||
auto* boss_ai = reinterpret_cast<BossAiHeigan40*>(ai);
|
||||
if (boss_ai)
|
||||
{
|
||||
if (!boss_ai->events.Empty())
|
||||
eventMap = &boss_ai->events;
|
||||
if (boss_ai->currentPhase)
|
||||
curr_phase = boss_ai->currentPhase;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto* boss_ai = dynamic_cast<Heigan::boss_heigan::boss_heiganAI*>(ai);
|
||||
if (!boss_ai || boss_ai->events.Empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
eventMap = &boss_ai->events;
|
||||
curr_phase = boss_ai->currentPhase;
|
||||
}
|
||||
if (!eventMap || eventMap->Empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
uint32 curr_timer = eventMap->GetTimer();
|
||||
if (curr_timer > 1000000)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
uint32 curr_erupt = eventMap->GetNextEventTime(3); // EVENT_ERUPT_SECTION
|
||||
uint32 curr_dance = eventMap->GetNextEventTime(4); // EVENT_SWITCH_PHASE
|
||||
if ((curr_phase == 0 && curr_dance - curr_timer >= 85000) || (curr_phase == 1 && curr_dance - curr_timer >= 40000))
|
||||
{
|
||||
ResetSafe();
|
||||
}
|
||||
else if (curr_erupt != prev_erupt)
|
||||
{
|
||||
NextSafe();
|
||||
}
|
||||
prev_phase = curr_phase;
|
||||
prev_erupt = curr_erupt;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,40 @@
|
||||
#ifdef MOD_PLAYERBOTS
|
||||
#ifndef MOD_INDIVIDUAL_PROGRESSION_NAXXACTIONS_H
|
||||
#define MOD_INDIVIDUAL_PROGRESSION_NAXXACTIONS_H
|
||||
|
||||
#include "RaidNaxxActions.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class IPGrobbulusGoBehindAction : public GrobbulusGoBehindAction
|
||||
{
|
||||
public:
|
||||
IPGrobbulusGoBehindAction(PlayerbotAI* ai, float distance = 24.0f, float delta_angle = M_PI / 8)
|
||||
: GrobbulusGoBehindAction(ai,distance, delta_angle) {}
|
||||
virtual bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
|
||||
class IPGrobbulusRotateAction : public GrobbulusRotateAction
|
||||
{
|
||||
public:
|
||||
IPGrobbulusRotateAction(PlayerbotAI* botAI)
|
||||
: GrobbulusRotateAction(botAI)
|
||||
{}
|
||||
|
||||
uint32 GetCurrWaypoint() override;
|
||||
};
|
||||
|
||||
class IPHeiganDanceAction : public HeiganDanceAction
|
||||
{
|
||||
public:
|
||||
IPHeiganDanceAction(PlayerbotAI* ai) : HeiganDanceAction(ai)
|
||||
{}
|
||||
|
||||
protected:
|
||||
bool CalculateSafe();
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
#endif
|
||||
81
src/playerbots/raids/Naxx40/IndividualProgressionNaxxAi40.h
Normal file
81
src/playerbots/raids/Naxx40/IndividualProgressionNaxxAi40.h
Normal file
@@ -0,0 +1,81 @@
|
||||
#ifdef MOD_PLAYERBOTS
|
||||
#ifndef MOD_INDIVIDUAL_PROGRESSION_NAXXAI_H
|
||||
#define MOD_INDIVIDUAL_PROGRESSION_NAXXAI_H
|
||||
|
||||
#include "EventMap.h"
|
||||
#include "CreatureAIImpl.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "SpellInfo.h"
|
||||
#include <list>
|
||||
|
||||
struct BossAiHeigan40 : BossAI
|
||||
{
|
||||
EventMap events;
|
||||
uint8 currentPhase{};
|
||||
uint8 currentSection{};
|
||||
bool moveRight{};
|
||||
GuidList portedPlayersThisPhase;
|
||||
};
|
||||
|
||||
struct BossAiGrobbulus40 : BossAI
|
||||
{
|
||||
EventMap events;
|
||||
SummonList summons;
|
||||
uint32 dropSludgeTimer{};
|
||||
};
|
||||
|
||||
struct BossAiGluth40 : BossAI
|
||||
{
|
||||
EventMap events;
|
||||
SummonList summons;
|
||||
};
|
||||
|
||||
struct BossAiThaddius40 : BossAI
|
||||
{
|
||||
EventMap events;
|
||||
SummonList summons;
|
||||
uint32 summonTimer{};
|
||||
uint32 reviveTimer{};
|
||||
uint32 resetTimer{};
|
||||
bool ballLightningEnabled;
|
||||
};
|
||||
|
||||
struct BossAiRazuvious40 : BossAI
|
||||
{
|
||||
EventMap events;
|
||||
SummonList summons;
|
||||
};
|
||||
|
||||
struct BossAiFourhorsemen40 : BossAI
|
||||
{
|
||||
EventMap events;
|
||||
uint8 horsemanId;
|
||||
bool doneFirstShieldWall;
|
||||
};
|
||||
|
||||
struct BossAiLoatheb40 : BossAI
|
||||
{
|
||||
uint8 doomCounter;
|
||||
EventMap events;
|
||||
SummonList summons;
|
||||
};
|
||||
|
||||
struct BossAiSapphiron40 : BossAI
|
||||
{
|
||||
EventMap events;
|
||||
uint8 iceboltCount{};
|
||||
uint32 spawnTimer{};
|
||||
GuidList blockList;
|
||||
ObjectGuid currentTarget;
|
||||
};
|
||||
|
||||
struct BossAiKelthuzad40 : BossAI
|
||||
{
|
||||
EventMap events;
|
||||
SummonList summons;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,470 @@
|
||||
#ifdef MOD_PLAYERBOTS
|
||||
|
||||
#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 "RaidNaxxScripts.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "SharedDefines.h"
|
||||
|
||||
#include "RaidNaxxBossHelper.h"
|
||||
#include "IndividualProgressionNaxxAi40.h"
|
||||
|
||||
|
||||
template <class BossAiType, class Boss40AiType>
|
||||
class IPGenericBossHelper : public AiObject
|
||||
{
|
||||
public:
|
||||
IPGenericBossHelper(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;
|
||||
}
|
||||
auto* ai = _target->GetAI();
|
||||
if (!ai)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const char* typeName = typeid(*ai).name();
|
||||
if (std::string(typeName).find("_40") != std::string::npos)
|
||||
{
|
||||
auto* boss_ai = reinterpret_cast<Boss40AiType*>(ai);
|
||||
if (boss_ai)
|
||||
{
|
||||
if (!boss_ai->events.Empty())
|
||||
_event_map = &boss_ai->events;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto* boss_ai = dynamic_cast<BossAiType*>(ai);
|
||||
if (!boss_ai || boss_ai->events.Empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_event_map = &boss_ai->events;
|
||||
}
|
||||
if (!_event_map)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!_event_map || _event_map->Empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_timer = _event_map->GetTimer();
|
||||
if (_timer > 1000000)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
virtual void Reset()
|
||||
{
|
||||
_unit = nullptr;
|
||||
_target = nullptr;
|
||||
_event_map = nullptr;
|
||||
_timer = 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string _name;
|
||||
Unit* _unit = nullptr;
|
||||
Creature* _target = nullptr;
|
||||
EventMap* _event_map = nullptr;
|
||||
uint32 _timer = 0;
|
||||
};
|
||||
|
||||
class IPKelthuzadBossHelper : public IPGenericBossHelper<Kelthuzad::boss_kelthuzad::boss_kelthuzadAI, BossAiKelthuzad40>
|
||||
{
|
||||
public:
|
||||
IPKelthuzadBossHelper(PlayerbotAI* botAI) : IPGenericBossHelper(botAI, "kel'thuzad") {}
|
||||
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 IsPhaseOne() { return _event_map->GetNextEventTime(4) != 0; } // EVENT_PHASE_2
|
||||
bool IsPhaseTwo() { return !IsPhaseOne(); }
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
class IPRazuviousBossHelper : public IPGenericBossHelper<Razuvious::boss_razuvious::boss_razuviousAI, BossAiRazuvious40>
|
||||
{
|
||||
public:
|
||||
IPRazuviousBossHelper(PlayerbotAI* botAI) : IPGenericBossHelper(botAI, "instructor razuvious") {}
|
||||
};
|
||||
|
||||
class IPSapphironBossHelper : public IPGenericBossHelper<Sapphiron::boss_sapphiron::boss_sapphironAI, BossAiSapphiron40>
|
||||
{
|
||||
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;
|
||||
IPSapphironBossHelper(PlayerbotAI* botAI) : IPGenericBossHelper(botAI, "sapphiron") {}
|
||||
bool UpdateBossAI() override
|
||||
{
|
||||
if (!IPGenericBossHelper::UpdateBossAI())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
uint32 nextEventGround = _event_map->GetNextEventTime(13); // EVENT_GROUND
|
||||
if (nextEventGround && nextEventGround != lastEventGround)
|
||||
lastEventGround = nextEventGround;
|
||||
return true;
|
||||
}
|
||||
bool IsPhaseGround() { return _target->GetReactState() == REACT_AGGRESSIVE; }
|
||||
bool IsPhaseFlight() { return !IsPhaseGround(); }
|
||||
bool JustLanded()
|
||||
{
|
||||
return (_event_map->GetNextEventTime(6) - _timer) >= EVENT_FLIGHT_INTERVAL - POSITION_TIME_AFTER_LANDED; // EVENT_FLIGHT_START
|
||||
}
|
||||
bool WaitForExplosion() { return _event_map->GetNextEventTime(10); } // EVENT_FLIGHT_SPELL_EXPLOSION
|
||||
bool FindPosToAvoidChill(std::vector<float>& dest)
|
||||
{
|
||||
Aura* 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:
|
||||
const uint32 POSITION_TIME_AFTER_LANDED = 5000;
|
||||
const uint32 EVENT_FLIGHT_INTERVAL = 45000;
|
||||
uint32 lastEventGround = 0;
|
||||
};
|
||||
|
||||
class IPGluthBossHelper : public IPGenericBossHelper<Gluth::boss_gluth::boss_gluthAI, BossAiGluth40>
|
||||
{
|
||||
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;
|
||||
IPGluthBossHelper(PlayerbotAI* botAI) : IPGenericBossHelper(botAI, "gluth") {}
|
||||
bool BeforeDecimate()
|
||||
{
|
||||
uint32 decimate = _event_map->GetNextEventTime(3); // EVENT_DECIMATE
|
||||
return decimate && decimate - _timer <= 3000;
|
||||
}
|
||||
bool JustStartCombat() { return _timer < 10000; }
|
||||
};
|
||||
|
||||
class IPLoathebBossHelper : public IPGenericBossHelper<Loatheb::boss_loatheb::boss_loathebAI, BossAiLoatheb40>
|
||||
{
|
||||
public:
|
||||
const std::pair<float, float> mainTankPos = {2877.57f, -3967.00f};
|
||||
const std::pair<float, float> rangePos = {2896.96f, -3980.61f};
|
||||
IPLoathebBossHelper(PlayerbotAI* botAI) : IPGenericBossHelper(botAI, "loatheb") {}
|
||||
};
|
||||
|
||||
class IPFourhorsemanBossHelper : public IPGenericBossHelper<FourHorsemen::boss_four_horsemen::boss_four_horsemenAI, BossAiFourhorsemen40>
|
||||
{
|
||||
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)
|
||||
IPFourhorsemanBossHelper(PlayerbotAI* botAI) : IPGenericBossHelper(botAI, "sir zeliek") {}
|
||||
bool UpdateBossAI() override
|
||||
{
|
||||
if (!IPGenericBossHelper::UpdateBossAI())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!bot->IsInCombat())
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
sir = _unit;
|
||||
lady = AI_VALUE2(Unit*, "find target", "lady blaumeux");
|
||||
if (!lady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
auto* ladyBossAi = lady->GetAI();
|
||||
if (!ladyBossAi)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
const char* typeName = typeid(*ladyBossAi).name();
|
||||
if (std::string(typeName).find("boss_four_horsemen_40") != std::string::npos)
|
||||
{
|
||||
auto* ladyRealAi = reinterpret_cast<BossAiFourhorsemen40*>(lady->GetAI());
|
||||
if (ladyRealAi)
|
||||
{
|
||||
if (!ladyRealAi->events.Empty())
|
||||
ladyEvent = &ladyRealAi->events;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto* ladyRealAi = dynamic_cast<FourHorsemen::boss_four_horsemen::boss_four_horsemenAI*>(lady->GetAI());
|
||||
if (!ladyRealAi || ladyRealAi->events.Empty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
ladyEvent = &ladyRealAi->events;
|
||||
}
|
||||
if (!ladyEvent || ladyEvent->Empty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (ladyEvent->GetTimer() > 1000000)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
const uint32 voidZone = ladyEvent->GetNextEventTime(3); // EVENT_SECONDARY_SPELL
|
||||
if (voidZone && lastEventVoidZone != voidZone)
|
||||
{
|
||||
voidZoneCounter++;
|
||||
voidZoneCounter %= 8;
|
||||
lastEventVoidZone = voidZone;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void Reset() override
|
||||
{
|
||||
IPGenericBossHelper::Reset();
|
||||
sir = nullptr;
|
||||
lady = nullptr;
|
||||
ladyEvent = nullptr;
|
||||
lastEventVoidZone = 0;
|
||||
voidZoneCounter = 0;
|
||||
posToGo = 0;
|
||||
}
|
||||
bool IsAttracter(Player* bot)
|
||||
{
|
||||
Difficulty diff = bot->GetRaidDifficulty();
|
||||
if (diff == RAID_DIFFICULTY_25MAN_NORMAL)
|
||||
{
|
||||
return botAI->IsRangedDpsAssistantOfIndex(bot, 0) || botAI->IsHealAssistantOfIndex(bot, 0) ||
|
||||
botAI->IsHealAssistantOfIndex(bot, 1) || botAI->IsHealAssistantOfIndex(bot, 2);
|
||||
}
|
||||
return botAI->IsRangedDpsAssistantOfIndex(bot, 0) || botAI->IsHealAssistantOfIndex(bot, 0);
|
||||
}
|
||||
void CalculatePosToGo(Player* bot)
|
||||
{
|
||||
bool raid25 = bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL;
|
||||
if (!lady)
|
||||
{
|
||||
posToGo = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Interval: 24s - 15s - 15s - ...
|
||||
posToGo = !(_timer <= 9000 || ((_timer - 9000) / 67500) % 2 == 0);
|
||||
if (botAI->IsRangedDpsAssistantOfIndex(bot, 0) || (raid25 && botAI->IsHealAssistantOfIndex(bot, 1)))
|
||||
{
|
||||
posToGo = 1 - posToGo;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::pair<float, float> CurrentAttractPos()
|
||||
{
|
||||
float posX = attractPos[posToGo].first, posY = attractPos[posToGo].second;
|
||||
if (posToGo == 1)
|
||||
{
|
||||
float offset_x;
|
||||
float offset_y;
|
||||
if (voidZoneCounter < 4)
|
||||
{
|
||||
offset_x = voidZoneCounter * (-4.5f);
|
||||
offset_y = voidZoneCounter * (4.5f);
|
||||
}
|
||||
if (voidZoneCounter >= 4)
|
||||
{
|
||||
offset_x = (7 - voidZoneCounter) * (-4.5f);
|
||||
offset_y = (7 - voidZoneCounter) * (4.5f);
|
||||
offset_x += 4.5f;
|
||||
offset_y += 4.5f;
|
||||
}
|
||||
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;
|
||||
EventMap* ladyEvent = nullptr;
|
||||
uint32 lastEventVoidZone = 0;
|
||||
uint32 voidZoneCounter = 0;
|
||||
int posToGo = 0;
|
||||
};
|
||||
|
||||
class IPThaddiusBossHelper : public IPGenericBossHelper<Thaddius::boss_thaddius::boss_thaddiusAI, BossAiThaddius40>
|
||||
{
|
||||
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;
|
||||
IPThaddiusBossHelper(PlayerbotAI* botAI) : IPGenericBossHelper(botAI, "thaddius") {}
|
||||
bool UpdateBossAI() override
|
||||
{
|
||||
if (!IPGenericBossHelper::UpdateBossAI())
|
||||
{
|
||||
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->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:
|
||||
Unit* feugen = nullptr;
|
||||
Unit* stalagg = nullptr;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,255 @@
|
||||
#ifdef MOD_PLAYERBOTS
|
||||
|
||||
#include "IndividualProgressionNaxxMultipliers.h"
|
||||
#include "IndividualProgressionNaxxAi40.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 "ReachTargetActions.h"
|
||||
#include "RogueActions.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "ShamanActions.h"
|
||||
#include "UseMeetingStoneAction.h"
|
||||
#include "WarriorActions.h"
|
||||
#include "PlayerbotAI.h"
|
||||
|
||||
|
||||
float IPHeiganDanceMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "heigan the unclean");
|
||||
if (!boss || boss->isDead())
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
auto* ai = boss->GetAI();
|
||||
if (!ai)
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
EventMap* eventMap = nullptr;
|
||||
uint32 curr_phase = 0;
|
||||
const char* typeName = typeid(*ai).name();
|
||||
if (std::string(typeName).find("boss_heigan_40") != std::string::npos)
|
||||
{
|
||||
auto* boss_ai = reinterpret_cast<BossAiHeigan40*>(ai);
|
||||
if (boss_ai)
|
||||
{
|
||||
if (!boss_ai->events.Empty())
|
||||
eventMap = &boss_ai->events;
|
||||
if (boss_ai->currentPhase)
|
||||
curr_phase = boss_ai->currentPhase;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto* boss_ai = dynamic_cast<Heigan::boss_heigan::boss_heiganAI*>(ai);
|
||||
if (!boss_ai || boss_ai->events.Empty())
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
eventMap = &boss_ai->events;
|
||||
curr_phase = boss_ai->currentPhase;
|
||||
}
|
||||
if (!eventMap || eventMap->Empty())
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
uint32 curr_timer = eventMap->GetTimer();
|
||||
if (curr_timer > 1000000)
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
uint32 curr_dance = eventMap->GetNextEventTime(4); // EVENT_SWITCH_PHASE
|
||||
uint32 curr_erupt = eventMap->GetNextEventTime(3); // EVENT_ERUPT_SECTION
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||
dynamic_cast<CastDisengageAction*>(action) ||
|
||||
dynamic_cast<CastBlinkBackAction*>(action) )
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
if (curr_phase != 1 && (int32)curr_dance - curr_timer >= 3000)
|
||||
{
|
||||
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 IPThaddiusGenericMultiplier::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 IPSapphironGenericMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
if (dynamic_cast<FollowAction*>(action) || dynamic_cast<CastDeathGripAction*>(action) ||
|
||||
dynamic_cast<CombatFormationMoveAction*>(action))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float IPInstructorRazuviousGenericMultiplier::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 IPKelthuzadGenericMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
if ((dynamic_cast<DpsAssistAction*>(action) || dynamic_cast<TankAssistAction*>(action) ||
|
||||
dynamic_cast<CastDebuffSpellOnAttackerAction*>(action) || dynamic_cast<FollowAction*>(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 IPGluthGenericMultiplier::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 = 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 (target && !target->isDead()
|
||||
&& (target->GetEntry() == 16360 // Default Azerothcore Zombie Chow
|
||||
|| target->GetEntry() == 351069)) // mod-individual-progression Zombie Chow
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,71 @@
|
||||
|
||||
#ifdef MOD_PLAYERBOTS
|
||||
#ifndef MOD_INDIVIDUAL_PROGRESSION_NAXXMULTIPLIERS_H
|
||||
#define MOD_INDIVIDUAL_PROGRESSION_NAXXMULTIPLIERS_H
|
||||
|
||||
#include "Multiplier.h"
|
||||
#include "IndividualProgressionNaxxBossHelper.h"
|
||||
|
||||
class IPHeiganDanceMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
IPHeiganDanceMultiplier(PlayerbotAI* ai) : Multiplier(ai, "helgan dance") {}
|
||||
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class IPThaddiusGenericMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
IPThaddiusGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "thaddius generic"), helper(ai) {}
|
||||
|
||||
virtual float GetValue(Action* action);
|
||||
|
||||
private:
|
||||
IPThaddiusBossHelper helper;
|
||||
};
|
||||
|
||||
class IPSapphironGenericMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
IPSapphironGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "sapphiron generic"), helper(ai) {}
|
||||
|
||||
virtual float GetValue(Action* action);
|
||||
|
||||
private:
|
||||
IPSapphironBossHelper helper;
|
||||
};
|
||||
|
||||
class IPInstructorRazuviousGenericMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
IPInstructorRazuviousGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "instructor razuvious generic"), helper(ai) {}
|
||||
|
||||
virtual float GetValue(Action* action);
|
||||
|
||||
private:
|
||||
IPRazuviousBossHelper helper;
|
||||
};
|
||||
|
||||
class IPKelthuzadGenericMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
IPKelthuzadGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "kelthuzad generic"), helper(ai) {}
|
||||
virtual float GetValue(Action* action);
|
||||
|
||||
private:
|
||||
IPKelthuzadBossHelper helper;
|
||||
};
|
||||
|
||||
class IPGluthGenericMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
IPGluthGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "gluth generic"), helper(ai) {}
|
||||
float GetValue(Action* action) override;
|
||||
|
||||
private:
|
||||
IPGluthBossHelper helper;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,156 @@
|
||||
#ifdef MOD_PLAYERBOTS
|
||||
#include "IndividualProgressionNaxxTriggers.h"
|
||||
|
||||
#include "EventMap.h"
|
||||
#include "Playerbots.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "Trigger.h"
|
||||
|
||||
|
||||
template <class T>
|
||||
bool BossEventTrigger<T>::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE(Unit*, "boss target");
|
||||
if (!boss
|
||||
|| (boss->GetEntry() != boss_entry // Default Azerothcore from BossEventTrigger instanciation
|
||||
&& (boss_entry_secondary == 0 || boss->GetEntry() != boss_entry_secondary))) // If an other boss version exists from an external module
|
||||
{
|
||||
return false;
|
||||
}
|
||||
T* ai = dynamic_cast<T*>(boss->GetAI());
|
||||
if(!ai)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
EventMap* eventMap = &ai->events;
|
||||
if (!eventMap)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const uint32 event_time = eventMap->GetNextEventTime(event_id);
|
||||
if (event_time != last_event_time)
|
||||
{
|
||||
last_event_time = event_time;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IPGrobbulusCloudTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE(Unit*, "boss target");
|
||||
if (!boss
|
||||
|| (boss->GetEntry() != boss_entry // Default Azerothcore Grobbulus (15931)
|
||||
&& boss->GetEntry() != boss_entry_secondary)) // mod-individual-progression Grobbulus (351003)
|
||||
{
|
||||
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);
|
||||
return AI_VALUE2(bool, "has aggro", "boss target");
|
||||
}
|
||||
|
||||
bool IPHeiganMeleeTrigger::IsActive()
|
||||
{
|
||||
Unit* heigan = AI_VALUE2(Unit*, "find target", "heigan the unclean");
|
||||
if (!heigan || heigan->isDead())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return !botAI->IsRanged(bot);
|
||||
}
|
||||
|
||||
bool IPHeiganRangedTrigger::IsActive()
|
||||
{
|
||||
Unit* heigan = AI_VALUE2(Unit*, "find target", "heigan the unclean");
|
||||
if (!heigan || heigan->isDead())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return botAI->IsRanged(bot);
|
||||
}
|
||||
|
||||
bool IPRazuviousTankTrigger::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 IPRazuviousNontankTrigger::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 IPGluthTrigger::IsActive() { return helper.UpdateBossAI(); }
|
||||
|
||||
bool IPGluthMainTankMortalWoundTrigger::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 = botAI->GetAura("mortal wound", mt, false, true);
|
||||
if (!aura || aura->GetStackAmount() < 5)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IPKelthuzadTrigger::IsActive() { return helper.UpdateBossAI(); }
|
||||
|
||||
bool IPLoathebTrigger::IsActive() { return helper.UpdateBossAI(); }
|
||||
|
||||
bool IPThaddiusPhasePetTrigger::IsActive()
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return helper.IsPhasePet();
|
||||
}
|
||||
|
||||
bool IPThaddiusPhaseTransitionTrigger::IsActive()
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return helper.IsPhaseTransition();
|
||||
}
|
||||
|
||||
bool IPThaddiusPhaseThaddiusTrigger::IsActive()
|
||||
{
|
||||
if (!helper.UpdateBossAI())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return helper.IsPhaseThaddius();
|
||||
}
|
||||
|
||||
template bool BossEventTrigger<Grobbulus::boss_grobbulus::boss_grobbulusAI>::IsActive();
|
||||
|
||||
#endif
|
||||
183
src/playerbots/raids/Naxx40/IndividualProgressionNaxxTriggers.h
Normal file
183
src/playerbots/raids/Naxx40/IndividualProgressionNaxxTriggers.h
Normal file
@@ -0,0 +1,183 @@
|
||||
#ifdef MOD_PLAYERBOTS
|
||||
#ifndef _PLAYERBOT_RAIDNAXXTRIGGERS_H
|
||||
#define _PLAYERBOT_RAIDNAXXTRIGGERS_H
|
||||
|
||||
#include "EventMap.h"
|
||||
#include "GenericTriggers.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "IndividualProgressionNaxxBossHelper.h"
|
||||
#include "RaidNaxxScripts.h"
|
||||
#include "Trigger.h"
|
||||
|
||||
template <class T>
|
||||
class BossEventTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
BossEventTrigger(PlayerbotAI* ai, uint32 event_id, uint32 boss_entry, uint32 boss_entry_secondary = 0, std::string name = "boss event")
|
||||
: Trigger(ai, name, 1)
|
||||
{
|
||||
this->event_id = event_id;
|
||||
this->boss_entry = boss_entry;
|
||||
this->boss_entry_secondary = boss_entry_secondary;
|
||||
this->last_event_time = -1;
|
||||
}
|
||||
virtual bool IsActive();
|
||||
|
||||
protected:
|
||||
uint32 event_id, boss_entry, boss_entry_secondary, last_event_time;
|
||||
};
|
||||
|
||||
class IPGrobbulusCloudTrigger : public BossEventTrigger<Grobbulus::boss_grobbulus::boss_grobbulusAI>
|
||||
{
|
||||
public:
|
||||
IPGrobbulusCloudTrigger(PlayerbotAI* ai) : BossEventTrigger(ai, 2, 15931, 351003, "grobbulus cloud event") {}
|
||||
virtual bool IsActive();
|
||||
};
|
||||
|
||||
class IPHeiganMeleeTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IPHeiganMeleeTrigger(PlayerbotAI* ai) : Trigger(ai, "heigan melee") {}
|
||||
virtual bool IsActive();
|
||||
};
|
||||
|
||||
class IPHeiganRangedTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IPHeiganRangedTrigger(PlayerbotAI* ai) : Trigger(ai, "heigan ranged") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IPRazuviousTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IPRazuviousTankTrigger(PlayerbotAI* ai) : Trigger(ai, "instructor razuvious tank"), helper(ai) {}
|
||||
bool IsActive() override;
|
||||
|
||||
private:
|
||||
IPRazuviousBossHelper helper;
|
||||
};
|
||||
|
||||
class IPRazuviousNontankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IPRazuviousNontankTrigger(PlayerbotAI* ai) : Trigger(ai, "instructor razuvious non-tank"), helper(ai) {}
|
||||
bool IsActive() override;
|
||||
|
||||
private:
|
||||
IPRazuviousBossHelper helper;
|
||||
};
|
||||
|
||||
class IPKelthuzadTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IPKelthuzadTrigger(PlayerbotAI* ai) : Trigger(ai, "kel'thuzad trigger"), helper(ai) {}
|
||||
bool IsActive() override;
|
||||
|
||||
private:
|
||||
IPKelthuzadBossHelper helper;
|
||||
};
|
||||
|
||||
class IPThaddiusPhasePetTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IPThaddiusPhasePetTrigger(PlayerbotAI* ai) : Trigger(ai, "thaddius phase pet"), helper(ai) {}
|
||||
bool IsActive() override;
|
||||
|
||||
private:
|
||||
IPThaddiusBossHelper helper;
|
||||
};
|
||||
|
||||
class IPThaddiusPhaseTransitionTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IPThaddiusPhaseTransitionTrigger(PlayerbotAI* ai) : Trigger(ai, "thaddius phase transition"), helper(ai) {}
|
||||
bool IsActive() override;
|
||||
|
||||
private:
|
||||
IPThaddiusBossHelper helper;
|
||||
};
|
||||
|
||||
class IPThaddiusPhaseThaddiusTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IPThaddiusPhaseThaddiusTrigger(PlayerbotAI* ai) : Trigger(ai, "thaddius phase thaddius"), helper(ai) {}
|
||||
bool IsActive() override;
|
||||
|
||||
private:
|
||||
IPThaddiusBossHelper helper;
|
||||
};
|
||||
|
||||
class IPHorsemanAttractorsTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IPHorsemanAttractorsTrigger(PlayerbotAI* ai) : Trigger(ai, "fourhorsemen attractors"), helper(ai) {}
|
||||
bool IsActive() override;
|
||||
|
||||
private:
|
||||
IPFourhorsemanBossHelper helper;
|
||||
};
|
||||
|
||||
class IPHorsemanExceptAttractorsTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IPHorsemanExceptAttractorsTrigger(PlayerbotAI* ai) : Trigger(ai, "fourhorsemen except attractors"), helper(ai) {}
|
||||
bool IsActive() override;
|
||||
|
||||
private:
|
||||
IPFourhorsemanBossHelper helper;
|
||||
};
|
||||
|
||||
class IPSapphironGroundTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IPSapphironGroundTrigger(PlayerbotAI* ai) : Trigger(ai, "sapphiron ground"), helper(ai) {}
|
||||
bool IsActive() override;
|
||||
|
||||
private:
|
||||
IPSapphironBossHelper helper;
|
||||
};
|
||||
|
||||
|
||||
class IPSapphironFlightTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IPSapphironFlightTrigger(PlayerbotAI* ai) : Trigger(ai, "sapphiron flight"), helper(ai) {}
|
||||
bool IsActive() override;
|
||||
|
||||
private:
|
||||
IPSapphironBossHelper helper;
|
||||
};
|
||||
|
||||
class IPGluthTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IPGluthTrigger(PlayerbotAI* ai) : Trigger(ai, "gluth trigger"), helper(ai) {}
|
||||
bool IsActive() override;
|
||||
|
||||
private:
|
||||
IPGluthBossHelper helper;
|
||||
};
|
||||
|
||||
class IPGluthMainTankMortalWoundTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IPGluthMainTankMortalWoundTrigger(PlayerbotAI* ai) : Trigger(ai, "gluth main tank mortal wound trigger"), helper(ai) {}
|
||||
bool IsActive() override;
|
||||
|
||||
private:
|
||||
IPGluthBossHelper helper;
|
||||
};
|
||||
|
||||
class IPLoathebTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IPLoathebTrigger(PlayerbotAI* ai) : Trigger(ai, "loatheb"), helper(ai) {}
|
||||
bool IsActive() override;
|
||||
|
||||
private:
|
||||
IPLoathebBossHelper helper;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
Reference in New Issue
Block a user