mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-11-29 15:58:20 +08:00
@@ -54,6 +54,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
|
||||
actionContexts.Add(new WotlkDungeonNexActionContext());
|
||||
actionContexts.Add(new WotlkDungeonANActionContext());
|
||||
actionContexts.Add(new WotlkDungeonOKActionContext());
|
||||
actionContexts.Add(new WotlkDungeonDTKActionContext());
|
||||
|
||||
triggerContexts.Add(new TriggerContext());
|
||||
triggerContexts.Add(new ChatTriggerContext());
|
||||
@@ -68,6 +69,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
|
||||
triggerContexts.Add(new WotlkDungeonNexTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonANTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonOKTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonDTKTriggerContext());
|
||||
|
||||
valueContexts.Add(new ValueContext());
|
||||
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
#include "wotlk/nexus/NexusStrategy.h"
|
||||
#include "wotlk/azjolnerub/AzjolNerubStrategy.h"
|
||||
#include "wotlk/oldkingdom/OldKingdomStrategy.h"
|
||||
#include "wotlk/draktharonkeep/DrakTharonKeepStrategy.h"
|
||||
|
||||
/*
|
||||
Full list/TODO:
|
||||
Drak'Tharon Keep - DTK
|
||||
Trollgore, Novos the Summoner, King Dred, The Prophet Tharon'ja
|
||||
|
||||
The Violet Hold - VH
|
||||
Erekem, Moragg, Ichoron, Xevozz, Lavanthor, Zuramat the Obliterator, Cyanigosa
|
||||
Gundrak - GD
|
||||
@@ -75,8 +75,8 @@ class DungeonStrategyContext : public NamedObjectContext<Strategy>
|
||||
static Strategy* wotlk_nex(PlayerbotAI* botAI) { return new WotlkDungeonNexStrategy(botAI); }
|
||||
static Strategy* wotlk_an(PlayerbotAI* botAI) { return new WotlkDungeonANStrategy(botAI); }
|
||||
static Strategy* wotlk_ok(PlayerbotAI* botAI) { return new WotlkDungeonOKStrategy(botAI); }
|
||||
|
||||
static Strategy* wotlk_dtk(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); }
|
||||
static Strategy* wotlk_dtk(PlayerbotAI* botAI) { return new WotlkDungeonDTKStrategy(botAI); }
|
||||
|
||||
static Strategy* wotlk_vh(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); }
|
||||
static Strategy* wotlk_gd(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); }
|
||||
static Strategy* wotlk_hos(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); }
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "nexus/NexusActionContext.h"
|
||||
#include "azjolnerub/AzjolNerubActionContext.h"
|
||||
#include "oldkingdom/OldKingdomActionContext.h"
|
||||
// #include "draktharonkeep/DraktharonKeepActionContext.h"
|
||||
#include "draktharonkeep/DrakTharonKeepActionContext.h"
|
||||
// #include "violethold/VioletHoldActionContext.h"
|
||||
// #include "gundrak/GundrakActionContext.h"
|
||||
// #include "hallsofstone/HallsOfStoneActionContext.h"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "nexus/NexusTriggerContext.h"
|
||||
#include "azjolnerub/AzjolNerubTriggerContext.h"
|
||||
#include "oldkingdom/OldKingdomTriggerContext.h"
|
||||
// #include "draktharonkeep/DraktharonKeepTriggerContext.h"
|
||||
#include "draktharonkeep/DrakTharonKeepTriggerContext.h"
|
||||
// #include "violethold/VioletHoldTriggerContext.h"
|
||||
// #include "gundrak/GundrakTriggerContext.h"
|
||||
// #include "hallsofstone/HallsOfStoneTriggerContext.h"
|
||||
|
||||
@@ -10,7 +10,7 @@ class WotlkDungeonANStrategy : public Strategy
|
||||
{
|
||||
public:
|
||||
WotlkDungeonANStrategy(PlayerbotAI* ai) : Strategy(ai) {}
|
||||
virtual std::string const getName() override { return "azjol-nerub"; }
|
||||
virtual std::string const getName() override { return "azjol'nerub"; }
|
||||
virtual void InitTriggers(std::vector<TriggerNode*> &triggers) override;
|
||||
virtual void InitMultipliers(std::vector<Multiplier*> &multipliers) override;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONDTKACTIONCONTEXT_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONDTKACTIONCONTEXT_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "DrakTharonKeepActions.h"
|
||||
|
||||
class WotlkDungeonDTKActionContext : public NamedObjectContext<Action>
|
||||
{
|
||||
public:
|
||||
WotlkDungeonDTKActionContext() {
|
||||
creators["corpse explode spread"] = &WotlkDungeonDTKActionContext::corpse_explode_spread;
|
||||
creators["avoid arcane field"] = &WotlkDungeonDTKActionContext::avoid_arcane_field;
|
||||
creators["novos positioning"] = &WotlkDungeonDTKActionContext::novos_positioning;
|
||||
creators["novos target priority"] = &WotlkDungeonDTKActionContext::novos_target_priority;
|
||||
creators["slaying strike"] = &WotlkDungeonDTKActionContext::slaying_strike;
|
||||
creators["tharonja taunt"] = &WotlkDungeonDTKActionContext::taunt;
|
||||
creators["bone armor"] = &WotlkDungeonDTKActionContext::bone_armor;
|
||||
creators["touch of life"] = &WotlkDungeonDTKActionContext::touch_of_life;
|
||||
}
|
||||
private:
|
||||
static Action* corpse_explode_spread(PlayerbotAI* ai) { return new CorpseExplodeSpreadAction(ai); }
|
||||
static Action* avoid_arcane_field(PlayerbotAI* ai) { return new AvoidArcaneFieldAction(ai); }
|
||||
static Action* novos_positioning(PlayerbotAI* ai) { return new NovosDefaultPositionAction(ai); }
|
||||
static Action* novos_target_priority(PlayerbotAI* ai) { return new NovosTargetPriorityAction(ai); }
|
||||
static Action* slaying_strike(PlayerbotAI* ai) { return new CastSlayingStrikeAction(ai); }
|
||||
static Action* taunt(PlayerbotAI* ai) { return new CastTauntAction(ai); }
|
||||
static Action* bone_armor(PlayerbotAI* ai) { return new CastBoneArmorAction(ai); }
|
||||
static Action* touch_of_life(PlayerbotAI* ai) { return new CastTouchOfLifeAction(ai); }
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,172 @@
|
||||
#include "Playerbots.h"
|
||||
#include "DrakTharonKeepActions.h"
|
||||
#include "DrakTharonKeepStrategy.h"
|
||||
|
||||
|
||||
bool CorpseExplodeSpreadAction::Execute(Event event)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "trollgore");
|
||||
if (!boss) { return false; }
|
||||
|
||||
float distance = 6.0f; // 5 unit radius, 1 unit added as buffer
|
||||
GuidVector corpses = AI_VALUE(GuidVector, "nearest corpses");
|
||||
for (auto i = corpses.begin(); i != corpses.end(); ++i)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(*i);
|
||||
if (unit && unit->GetEntry() == NPC_DRAKKARI_INVADER)
|
||||
{
|
||||
if (bot->GetExactDist2d(unit) < distance)
|
||||
{
|
||||
return MoveAway(unit, distance - bot->GetExactDist2d(unit));
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AvoidArcaneFieldAction::Execute(Event event)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "novos the summoner");
|
||||
if (!boss) { return false; }
|
||||
|
||||
float distance = 12.0f; // 11 unit radius, 1 unit added as buffer
|
||||
if (bot->GetExactDist2d(boss) < distance)
|
||||
{
|
||||
return MoveAway(boss, distance - bot->GetExactDist2d(boss));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NovosDefaultPositionAction::isUseful()
|
||||
{
|
||||
// Distance to tether to centre of room
|
||||
float threshold = 15.0f;
|
||||
return bot->GetDistance(NOVOS_PARTY_POSITION) > threshold;
|
||||
}
|
||||
bool NovosDefaultPositionAction::Execute(Event event)
|
||||
{
|
||||
float clusterDistance = 4.0f;
|
||||
// Only reposition if we're not killing anything
|
||||
if (!bot->GetTarget())
|
||||
{
|
||||
return MoveNear(bot->GetMap()->GetId(),
|
||||
NOVOS_PARTY_POSITION.GetPositionX(),
|
||||
NOVOS_PARTY_POSITION.GetPositionY(),
|
||||
NOVOS_PARTY_POSITION.GetPositionZ(),
|
||||
clusterDistance, MovementPriority::MOVEMENT_NORMAL);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NovosTargetPriorityAction::Execute(Event event)
|
||||
{
|
||||
// TODO: This can be improved, some parts are still buggy.
|
||||
// But it works for now and this fight is very easy
|
||||
|
||||
// Designate a dps char to handle the stairs adds.
|
||||
// This is probably better as a melee, so just pick the first
|
||||
// melee dps in the party. If none exist, pick the first ranged.
|
||||
Player* stairsDps = nullptr;
|
||||
GuidVector members = AI_VALUE(GuidVector, "group members");
|
||||
for (auto& member : members)
|
||||
{
|
||||
Player* groupMember = botAI->GetPlayer(member);
|
||||
if (!groupMember) { continue; }
|
||||
|
||||
if (botAI->IsDps(groupMember))
|
||||
{
|
||||
if (botAI->IsMelee(groupMember))
|
||||
{
|
||||
// Found our first melee dps, grab handle and break
|
||||
stairsDps = groupMember;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ranged dps, only set if none already assigned.
|
||||
// Don't break, we want to keep searching for a melee instead.
|
||||
if (!stairsDps)
|
||||
{
|
||||
stairsDps = groupMember;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Unit* selectedTargets[2] = {nullptr, 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 i = targets.begin(); i != targets.end(); ++i)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(*i);
|
||||
if (!unit) { continue; }
|
||||
uint32 creatureId = unit->GetEntry();
|
||||
|
||||
// Tank priority:
|
||||
// Hulking Corpse -> Crystal Handler
|
||||
if (botAI->IsTank(bot))
|
||||
{
|
||||
if (creatureId == NPC_HULKING_CORPSE)
|
||||
{
|
||||
selectedTargets[0] = unit;
|
||||
}
|
||||
else if (creatureId == NPC_CRYSTAL_HANDLER)
|
||||
{
|
||||
selectedTargets[1] = unit;
|
||||
}
|
||||
}
|
||||
// Dedicated stairs dps is assigned.
|
||||
// Priority: Risen Shadowcaster -> Fetid Troll Corpse
|
||||
else if (stairsDps && bot == stairsDps)
|
||||
{
|
||||
if (creatureId == NPC_RISEN_SHADOWCASTER)
|
||||
{
|
||||
if (!selectedTargets[0] || bot->GetDistance(unit) < bot->GetDistance(selectedTargets[0]) - 5.0f)
|
||||
{
|
||||
selectedTargets[0] = unit;
|
||||
}
|
||||
|
||||
}
|
||||
else if (creatureId == NPC_FETID_TROLL_CORPSE)
|
||||
{
|
||||
if (!selectedTargets[1] || bot->GetDistance(unit) < bot->GetDistance(selectedTargets[1]) - 5.0f)
|
||||
{
|
||||
selectedTargets[1] = unit;
|
||||
}
|
||||
}
|
||||
}
|
||||
// All other dps priority:
|
||||
// Crystal Handler -> Hulking Corpse
|
||||
else if (botAI->IsDps(bot))
|
||||
{
|
||||
if (creatureId == NPC_CRYSTAL_HANDLER)
|
||||
{
|
||||
selectedTargets[0] = unit;
|
||||
}
|
||||
else if (creatureId == NPC_HULKING_CORPSE)
|
||||
{
|
||||
selectedTargets[1] = unit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Unit* primaryTarget : selectedTargets)
|
||||
{
|
||||
// Attack the first valid target in the priority list
|
||||
if (primaryTarget)
|
||||
{
|
||||
if (AI_VALUE(Unit*, "current target") != primaryTarget)
|
||||
{
|
||||
// bot->Yell(primaryTarget->GetName(), LANG_UNIVERSAL);
|
||||
return Attack(primaryTarget);
|
||||
}
|
||||
// Don't continue loop here, the target exists so we don't
|
||||
// want to move down the prio list. We just don't need to send attack
|
||||
// command again, just return false and exit the loop that way
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONDTKACTIONS_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONDTKACTIONS_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "AttackAction.h"
|
||||
#include "GenericSpellActions.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
#include "DrakTharonKeepTriggers.h"
|
||||
|
||||
const Position NOVOS_PARTY_POSITION = Position(-378.852f, -760.349f, 28.587f);
|
||||
|
||||
class CorpseExplodeSpreadAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
CorpseExplodeSpreadAction(PlayerbotAI* ai) : MovementAction(ai, "corpse explode spread") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class AvoidArcaneFieldAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
AvoidArcaneFieldAction(PlayerbotAI* ai) : MovementAction(ai, "avoid arcane field") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class NovosDefaultPositionAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
NovosDefaultPositionAction(PlayerbotAI* ai) : MovementAction(ai, "novos default position") {}
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class NovosTargetPriorityAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
NovosTargetPriorityAction(PlayerbotAI* ai) : AttackAction(ai, "novos target priority") {}
|
||||
bool Execute(Event event) override;
|
||||
// bool isUseful() override;
|
||||
};
|
||||
|
||||
class CastSlayingStrikeAction : public CastMeleeSpellAction
|
||||
{
|
||||
public:
|
||||
CastSlayingStrikeAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "slaying strike") {}
|
||||
};
|
||||
|
||||
class CastTauntAction : public CastSpellAction
|
||||
{
|
||||
public:
|
||||
CastTauntAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "taunt") {}
|
||||
};
|
||||
|
||||
class CastBoneArmorAction : public CastSpellAction
|
||||
{
|
||||
public:
|
||||
CastBoneArmorAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "bone armor") {}
|
||||
};
|
||||
|
||||
class CastTouchOfLifeAction : public CastSpellAction
|
||||
{
|
||||
public:
|
||||
CastTouchOfLifeAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "touch of life") {}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,62 @@
|
||||
#include "DrakTharonKeepMultipliers.h"
|
||||
#include "DrakTharonKeepActions.h"
|
||||
#include "GenericSpellActions.h"
|
||||
#include "ChooseTargetActions.h"
|
||||
#include "MovementActions.h"
|
||||
#include "DrakTharonKeepTriggers.h"
|
||||
#include "Action.h"
|
||||
|
||||
float NovosMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "novos the summoner");
|
||||
if (!boss) { return 1.0f; }
|
||||
|
||||
if (boss->FindCurrentSpellBySpellId(SPELL_ARCANE_FIELD) && bot->GetTarget())
|
||||
{
|
||||
if (dynamic_cast<DpsAssistAction*>(action)
|
||||
|| dynamic_cast<TankAssistAction*>(action))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float TharonjaMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!bot->HasAura(SPELL_GIFT_OF_THARONJA)) { return 1.0f; }
|
||||
|
||||
// Suppress all skills that are not enabled in skeleton form.
|
||||
// Still allow non-ability actions such as movement
|
||||
if (dynamic_cast<CastSpellAction*>(action)
|
||||
&& !dynamic_cast<CastSlayingStrikeAction*>(action)
|
||||
&& !dynamic_cast<CastTauntAction*>(action)
|
||||
&& !dynamic_cast<CastBoneArmorAction*>(action)
|
||||
&& !dynamic_cast<CastTouchOfLifeAction*>(action))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
// Also suppress FleeAction to prevent ranged characters from avoiding melee range
|
||||
if (dynamic_cast<FleeAction*>(action))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
// Tanks should only taunt, no slaying strike
|
||||
if (botAI->IsTank(bot))
|
||||
{
|
||||
if (dynamic_cast<CastSlayingStrikeAction*>(action))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
// Dps & healer should not taunt
|
||||
else
|
||||
{
|
||||
if (dynamic_cast<CastTauntAction*>(action))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONDTKMULTIPLIERS_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONDTKMULTIPLIERS_H
|
||||
|
||||
#include "Multiplier.h"
|
||||
|
||||
class NovosMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
NovosMultiplier(PlayerbotAI* ai) : Multiplier(ai, "novos the summoner") {}
|
||||
|
||||
public:
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class TharonjaMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
TharonjaMultiplier(PlayerbotAI* ai) : Multiplier(ai, "the prophet tharon'ja") {}
|
||||
|
||||
public:
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,41 @@
|
||||
#include "DrakTharonKeepStrategy.h"
|
||||
#include "DrakTharonKeepMultipliers.h"
|
||||
|
||||
|
||||
void WotlkDungeonDTKStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
|
||||
{
|
||||
// Trollgore
|
||||
triggers.push_back(new TriggerNode("corpse explode",
|
||||
NextAction::array(0, new NextAction("corpse explode spread", ACTION_MOVE + 5), nullptr)));
|
||||
|
||||
// Novos the Summoner
|
||||
// TODO: Can be improved - it's a pretty easy fight but complex to program, revisit if needed
|
||||
triggers.push_back(new TriggerNode("arcane field",
|
||||
NextAction::array(0, new NextAction("avoid arcane field", ACTION_MOVE + 5), nullptr)));
|
||||
triggers.push_back(new TriggerNode("arcane field",
|
||||
NextAction::array(0, new NextAction("novos positioning", ACTION_MOVE + 4), nullptr)));
|
||||
triggers.push_back(new TriggerNode("arcane field",
|
||||
NextAction::array(0, new NextAction("novos target priority", ACTION_NORMAL + 1), nullptr)));
|
||||
|
||||
// King Dred
|
||||
// TODO: Fear ward / tremor totem, or general anti-fear strat development
|
||||
|
||||
//The Prophet Tharon'ja
|
||||
triggers.push_back(new TriggerNode("gift of tharon'ja",
|
||||
NextAction::array(0, new NextAction("touch of life", ACTION_NORMAL + 5), nullptr)));
|
||||
triggers.push_back(new TriggerNode("gift of tharon'ja",
|
||||
NextAction::array(0, new NextAction("bone armor", ACTION_NORMAL + 4), nullptr)));
|
||||
// Run ranged chars (who would normally stand at range) into melee, to dps in skeleton form
|
||||
triggers.push_back(new TriggerNode("tharon'ja out of melee",
|
||||
NextAction::array(0, new NextAction("reach melee", ACTION_NORMAL + 3), nullptr)));
|
||||
triggers.push_back(new TriggerNode("gift of tharon'ja",
|
||||
NextAction::array(0, new NextAction("taunt", ACTION_NORMAL + 2), nullptr)));
|
||||
triggers.push_back(new TriggerNode("gift of tharon'ja",
|
||||
NextAction::array(0, new NextAction("slaying strike", ACTION_NORMAL + 2), nullptr)));
|
||||
}
|
||||
|
||||
void WotlkDungeonDTKStrategy::InitMultipliers(std::vector<Multiplier*> &multipliers)
|
||||
{
|
||||
multipliers.push_back(new NovosMultiplier(botAI));
|
||||
multipliers.push_back(new TharonjaMultiplier(botAI));
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONDTKSTRATEGY_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONDTKSTRATEGY_H
|
||||
|
||||
#include "Multiplier.h"
|
||||
#include "AiObjectContext.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
|
||||
class WotlkDungeonDTKStrategy : public Strategy
|
||||
{
|
||||
public:
|
||||
WotlkDungeonDTKStrategy(PlayerbotAI* ai) : Strategy(ai) {}
|
||||
virtual std::string const getName() override { return "drak'tharon keep"; }
|
||||
virtual void InitTriggers(std::vector<TriggerNode*> &triggers) override;
|
||||
virtual void InitMultipliers(std::vector<Multiplier*> &multipliers) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONDTKTRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONDTKTRIGGERCONTEXT_H
|
||||
|
||||
#include "NamedObjectContext.h"
|
||||
#include "AiObjectContext.h"
|
||||
#include "DrakTharonKeepTriggers.h"
|
||||
|
||||
class WotlkDungeonDTKTriggerContext : public NamedObjectContext<Trigger>
|
||||
{
|
||||
public:
|
||||
WotlkDungeonDTKTriggerContext()
|
||||
{
|
||||
creators["corpse explode"] = &WotlkDungeonDTKTriggerContext::corpse_explode;
|
||||
creators["arcane field"] = &WotlkDungeonDTKTriggerContext::arcane_field;
|
||||
// creators["crystal handler"] = &WotlkDungeonDTKTriggerContext::crystal_handler;
|
||||
creators["gift of tharon'ja"] = &WotlkDungeonDTKTriggerContext::gift_of_tharonja;
|
||||
creators["tharon'ja out of melee"] = &WotlkDungeonDTKTriggerContext::tharonja_out_of_melee;
|
||||
|
||||
}
|
||||
private:
|
||||
static Trigger* corpse_explode(PlayerbotAI* ai) { return new CorpseExplodeTrigger(ai); }
|
||||
static Trigger* arcane_field(PlayerbotAI* ai) { return new ArcaneFieldTrigger(ai); }
|
||||
// static Trigger* crystal_handler(PlayerbotAI* ai) { return new CrystalHandlerTrigger(ai); }
|
||||
static Trigger* gift_of_tharonja(PlayerbotAI* ai) { return new GiftOfTharonjaTrigger(ai); }
|
||||
static Trigger* tharonja_out_of_melee(PlayerbotAI* ai) { return new TwoTriggers(ai, "gift of tharon'ja", "enemy out of melee"); }
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,61 @@
|
||||
#include "Playerbots.h"
|
||||
#include "DrakTharonKeepTriggers.h"
|
||||
#include "AiObject.h"
|
||||
#include "AiObjectContext.h"
|
||||
|
||||
|
||||
bool CorpseExplodeTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "trollgore");
|
||||
if (!boss) { return false; }
|
||||
|
||||
float distance = 6.0f; // 5 unit radius, 1 unit added as buffer
|
||||
GuidVector corpses = AI_VALUE(GuidVector, "nearest corpses");
|
||||
for (auto i = corpses.begin(); i != corpses.end(); ++i)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(*i);
|
||||
if (unit && unit->GetEntry() == NPC_DRAKKARI_INVADER)
|
||||
{
|
||||
if (bot->GetExactDist2d(unit) < distance)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ArcaneFieldTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "novos the summoner");
|
||||
if (boss)
|
||||
{
|
||||
return boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_ARCANE_FIELD);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// bool CrystalHandlerTrigger::IsActive()
|
||||
// {
|
||||
// Unit* boss = AI_VALUE2(Unit*, "find target", "novos the summoner");
|
||||
// if (!boss) { 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 no los");
|
||||
|
||||
// for (auto i = targets.begin(); i != targets.end(); ++i)
|
||||
// {
|
||||
// Unit* unit = botAI->GetUnit(*i);
|
||||
// if (unit && unit->GetEntry() == NPC_CRYSTAL_HANDLER)
|
||||
// {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
|
||||
bool GiftOfTharonjaTrigger::IsActive()
|
||||
{
|
||||
return bot->HasAura(SPELL_GIFT_OF_THARONJA);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONDTKTRIGGERS_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONDTKTRIGGERS_H
|
||||
|
||||
#include "Trigger.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "GenericTriggers.h"
|
||||
#include "DungeonStrategyUtils.h"
|
||||
|
||||
enum DrakTharonIDs
|
||||
{
|
||||
// Trollgore
|
||||
NPC_DRAKKARI_INVADER = 27709,
|
||||
|
||||
// Novos the Summoner
|
||||
NPC_NOVOS = 26631,
|
||||
SPELL_ARCANE_FIELD = 47346,
|
||||
NPC_CRYSTAL_HANDLER = 26627,
|
||||
NPC_HULKING_CORPSE = 27597,
|
||||
NPC_RISEN_SHADOWCASTER = 27600,
|
||||
NPC_FETID_TROLL_CORPSE = 27598,
|
||||
|
||||
// The Prophet Tharon'ja
|
||||
SPELL_GIFT_OF_THARONJA = 52509,
|
||||
};
|
||||
|
||||
class CorpseExplodeTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
CorpseExplodeTrigger(PlayerbotAI* ai) : Trigger(ai, "corpse explode") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class ArcaneFieldTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
ArcaneFieldTrigger(PlayerbotAI* ai) : Trigger(ai, "arcane field") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
// class CrystalHandlerTrigger : public Trigger
|
||||
// {
|
||||
// public:
|
||||
// CrystalHandlerTrigger(PlayerbotAI* ai) : Trigger(ai, "crystal handler") {}
|
||||
// bool IsActive() override;
|
||||
// };
|
||||
|
||||
class GiftOfTharonjaTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
GiftOfTharonjaTrigger(PlayerbotAI* ai) : Trigger(ai, "gift of tharon'ja") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -182,11 +182,11 @@ bool RearFlankPositionAction::Execute(Event event)
|
||||
// Need to reduce this value very slightly, or the bots get the jitters -
|
||||
// may be due to rounding errors. Need to bring them just inside their attack range.
|
||||
// This boss has a big hitbox so we can reduce by 50% and it's still fine and looks better.
|
||||
// TODO: Investigate using bot->GetObjectSize() for sizing
|
||||
float distance = bot->GetMeleeRange(boss) * 0.5f;
|
||||
// Alternatively, summing both unit's melee ranges seems to give a fairly natural range.
|
||||
// Use whichever gives the best results..
|
||||
// float distanceOffset = bot->GetMeleeReach() + boss->GetMeleeReach();
|
||||
|
||||
Position leftFlank = boss->GetPosition();
|
||||
Position rightFlank = boss->GetPosition();
|
||||
Position* destination = nullptr;
|
||||
|
||||
@@ -17,6 +17,7 @@ float PrinceKelesethMultiplier::GetValue(Action* action)
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float SkarvaldAndDalronnMultiplier::GetValue(Action* action)
|
||||
{
|
||||
// Unit* skarvald = AI_VALUE2(Unit*, "find target", "skarvald the constructor");
|
||||
|
||||
Reference in New Issue
Block a user