mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-11-29 15:58:20 +08:00
@@ -75,6 +75,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
|
||||
actionContexts.Add(new WotlkDungeonUPActionContext());
|
||||
actionContexts.Add(new WotlkDungeonCoSActionContext());
|
||||
actionContexts.Add(new WotlkDungeonFoSActionContext());
|
||||
actionContexts.Add(new WotlkDungeonPoSActionContext());
|
||||
actionContexts.Add(new WotlkDungeonToCActionContext());
|
||||
|
||||
triggerContexts.Add(new TriggerContext());
|
||||
@@ -102,7 +103,8 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
|
||||
triggerContexts.Add(new WotlkDungeonOccTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonUPTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonCoSTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonFosTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonFoSTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonPoSTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonToCTriggerContext());
|
||||
|
||||
valueContexts.Add(new ValueContext());
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "wotlk/utgardepinnacle/UtgardePinnacleStrategy.h"
|
||||
#include "wotlk/cullingofstratholme/CullingOfStratholmeStrategy.h"
|
||||
#include "wotlk/forgeofsouls/ForgeOfSoulsStrategy.h"
|
||||
#include "wotlk/pitofsaron/PitOfSaronStrategy.h"
|
||||
#include "wotlk/trialofthechampion/TrialOfTheChampionStrategy.h"
|
||||
|
||||
/*
|
||||
@@ -79,10 +80,11 @@ class DungeonStrategyContext : public NamedObjectContext<Strategy>
|
||||
static Strategy* wotlk_up(PlayerbotAI* botAI) { return new WotlkDungeonUPStrategy(botAI); }
|
||||
static Strategy* wotlk_cos(PlayerbotAI* botAI) { return new WotlkDungeonCoSStrategy(botAI); }
|
||||
static Strategy* wotlk_fos(PlayerbotAI* botAI) { return new WotlkDungeonFoSStrategy(botAI); }
|
||||
static Strategy* wotlk_pos(PlayerbotAI* botAI) { return new WotlkDungeonPoSStrategy(botAI); }
|
||||
static Strategy* wotlk_toc(PlayerbotAI* botAI) { return new WotlkDungeonToCStrategy(botAI); }
|
||||
// NYI from here down
|
||||
static Strategy* wotlk_hor(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); }
|
||||
static Strategy* wotlk_pos(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); }
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
#include "utgardepinnacle/UtgardePinnacleActionContext.h"
|
||||
#include "cullingofstratholme/CullingOfStratholmeActionContext.h"
|
||||
#include "forgeofsouls/ForgeOfSoulsActionContext.h"
|
||||
#include "pitofsaron/PitOfSaronActionContext.h"
|
||||
#include "trialofthechampion/TrialOfTheChampionActionContext.h"
|
||||
// #include "hallsofreflection/HallsOfReflectionActionContext.h"
|
||||
// #include "pitofsaron/PitOfSaronActionContext.h"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -14,8 +14,9 @@
|
||||
#include "utgardepinnacle/UtgardePinnacleTriggerContext.h"
|
||||
#include "cullingofstratholme/CullingOfStratholmeTriggerContext.h"
|
||||
#include "forgeofsouls/ForgeOfSoulsTriggerContext.h"
|
||||
#include "pitofsaron/PitOfSaronTriggerContext.h"
|
||||
#include "trialofthechampion/TrialOfTheChampionTriggerContext.h"
|
||||
// #include "hallsofreflection/HallsOfReflectionTriggerContext.h"
|
||||
// #include "pitofsaron/PitOfSaronTriggerContext.h"
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -5,15 +5,15 @@
|
||||
#include "AiObjectContext.h"
|
||||
#include "ForgeOfSoulsTriggers.h"
|
||||
|
||||
class WotlkDungeonFosTriggerContext : public NamedObjectContext<Trigger>
|
||||
class WotlkDungeonFoSTriggerContext : public NamedObjectContext<Trigger>
|
||||
{
|
||||
public:
|
||||
WotlkDungeonFosTriggerContext()
|
||||
WotlkDungeonFoSTriggerContext()
|
||||
{
|
||||
creators["bronjahm position"] = &WotlkDungeonFosTriggerContext::bronjahm_position;
|
||||
creators["move from bronjahm"] = &WotlkDungeonFosTriggerContext::move_from_bronjahm;
|
||||
creators["switch to soul fragment"] = &WotlkDungeonFosTriggerContext::switch_to_soul_fragment;
|
||||
creators["devourer of souls"] = &WotlkDungeonFosTriggerContext::devourer_of_souls;
|
||||
creators["bronjahm position"] = &WotlkDungeonFoSTriggerContext::bronjahm_position;
|
||||
creators["move from bronjahm"] = &WotlkDungeonFoSTriggerContext::move_from_bronjahm;
|
||||
creators["switch to soul fragment"] = &WotlkDungeonFoSTriggerContext::switch_to_soul_fragment;
|
||||
creators["devourer of souls"] = &WotlkDungeonFoSTriggerContext::devourer_of_souls;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONPOSACTIONCONTEXT_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONPOSACTIONCONTEXT_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "PitOfSaronActions.h"
|
||||
|
||||
class WotlkDungeonPoSActionContext : public NamedObjectContext<Action>
|
||||
{
|
||||
public:
|
||||
WotlkDungeonPoSActionContext()
|
||||
{
|
||||
creators["ick and krick"] = &WotlkDungeonPoSActionContext::ick_and_krick;
|
||||
creators["tyrannus"] = &WotlkDungeonPoSActionContext::tyrannus;
|
||||
}
|
||||
private:
|
||||
static Action* ick_and_krick(PlayerbotAI* ai) { return new IckAndKrickAction(ai); }
|
||||
static Action* tyrannus(PlayerbotAI* ai) { return new TyrannusAction(ai); }
|
||||
};
|
||||
|
||||
#endif
|
||||
315
src/strategy/dungeons/wotlk/pitofsaron/PitOfSaronActions.cpp
Normal file
315
src/strategy/dungeons/wotlk/pitofsaron/PitOfSaronActions.cpp
Normal file
@@ -0,0 +1,315 @@
|
||||
#include "Playerbots.h"
|
||||
#include "PitOfSaronActions.h"
|
||||
#include "PitOfSaronStrategy.h"
|
||||
#include "SharedDefines.h"
|
||||
|
||||
bool IckAndKrickAction::Execute(Event event)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "Ick");
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
std::vector<Unit*> orbs;
|
||||
bool orb = false;
|
||||
|
||||
// First gather all orbs
|
||||
GuidVector npcs1 = AI_VALUE(GuidVector, "nearest hostile npcs");
|
||||
for (auto& npc : npcs1)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(npc);
|
||||
if (unit && unit->IsAlive() && unit->HasAura(SPELL_EXPLODING_ORB_VISUAL))
|
||||
{
|
||||
orb = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool pursuit = bot->HasAura(SPELL_PURSUIT) || (!botAI->IsTank(bot) && boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_PURSUIT));
|
||||
bool poisonNova = boss->HasUnitState(UNIT_STATE_CASTING) && (boss->FindCurrentSpellBySpellId(SPELL_POISON_NOVA_POS) || boss->FindCurrentSpellBySpellId(SPELL_POISON_NOVA_POS_HC));
|
||||
bool explosiveBarrage = orb || boss->HasUnitState(UNIT_STATE_CASTING) && (boss->FindCurrentSpellBySpellId(SPELL_EXPLOSIVE_BARRAGE_ICK) || boss->FindCurrentSpellBySpellId(SPELL_EXPLOSIVE_BARRAGE_KRICK));
|
||||
bool isTank = botAI->IsTank(bot);
|
||||
|
||||
if (pursuit && Pursuit(pursuit, boss))
|
||||
return true;
|
||||
|
||||
if (poisonNova && PoisonNova(poisonNova, boss))
|
||||
return true;
|
||||
|
||||
if (explosiveBarrage && ExplosiveBarrage(explosiveBarrage, boss))
|
||||
return true;
|
||||
|
||||
if (isTank && !pursuit && !poisonNova && !explosiveBarrage)
|
||||
{
|
||||
if (TankPosition(boss))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IckAndKrickAction::TankPosition(Unit* boss)
|
||||
{
|
||||
if (botAI->HasAggro(boss))
|
||||
{
|
||||
float distance = bot->GetExactDist2d(ICKANDKRICK_TANK_POSITION.GetPositionX(), ICKANDKRICK_TANK_POSITION.GetPositionY());
|
||||
if (distance > 7.0f)
|
||||
{
|
||||
// Calculate direction vector
|
||||
float dirX = ICKANDKRICK_TANK_POSITION.GetPositionX() - bot->GetPositionX();
|
||||
float dirY = ICKANDKRICK_TANK_POSITION.GetPositionY() - bot->GetPositionY();
|
||||
float length = sqrt(dirX * dirX + dirY * dirY);
|
||||
dirX /= length;
|
||||
dirY /= length;
|
||||
|
||||
// Move in increments of 3.0f
|
||||
float moveX = bot->GetPositionX() + dirX * 3.0f;
|
||||
float moveY = bot->GetPositionY() + dirY * 3.0f;
|
||||
|
||||
return MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IckAndKrickAction::Pursuit(bool pursuit, Unit* boss)
|
||||
{
|
||||
// Only execute this action when pursuit is active and for non-tank players
|
||||
if (!pursuit || botAI->IsTank(bot))
|
||||
return false;
|
||||
|
||||
// Get the tank position as a reference point
|
||||
Position tankPosition = ICKANDKRICK_TANK_POSITION;
|
||||
|
||||
// Calculate distance from boss
|
||||
float distanceToBoss = bot->GetExactDist2d(boss);
|
||||
|
||||
// If boss is close, move in a circular pattern
|
||||
if (distanceToBoss < 20.0f)
|
||||
{
|
||||
// Current bot position
|
||||
float x = bot->GetPositionX();
|
||||
float y = bot->GetPositionY();
|
||||
|
||||
// Calculate vector from tank to bot
|
||||
float dx = x - tankPosition.GetPositionX();
|
||||
float dy = y - tankPosition.GetPositionY();
|
||||
|
||||
// Normalize the vector
|
||||
float distance = std::sqrt(dx * dx + dy * dy);
|
||||
if (distance > 0)
|
||||
{
|
||||
dx /= distance;
|
||||
dy /= distance;
|
||||
}
|
||||
|
||||
// Rotate the vector by 90 degrees to create circular movement
|
||||
// (x,y) rotated 90 degrees becomes (-y,x)
|
||||
float rotatedX = -dy;
|
||||
float rotatedY = dx;
|
||||
|
||||
// Create a target position along the circular path
|
||||
// We want to maintain a specific radius from the tank
|
||||
float targetRadius = 20.0f; // 20 feet radius circle around tank
|
||||
|
||||
Position targetPos;
|
||||
targetPos.Relocate(tankPosition.GetPositionX() + rotatedX * targetRadius,
|
||||
tankPosition.GetPositionY() + rotatedY * targetRadius, bot->GetPositionZ());
|
||||
|
||||
// Move to the calculated circular position
|
||||
botAI->Reset();
|
||||
return MoveTo(bot->GetMapId(), targetPos.GetPositionX(), targetPos.GetPositionY(), bot->GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IckAndKrickAction::PoisonNova(bool poisonNova, Unit* boss)
|
||||
{
|
||||
if (bot->GetExactDist2d(boss) < 20.0f && poisonNova)
|
||||
return MoveAway(boss, 11.0f);
|
||||
else if (poisonNova)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool IckAndKrickAction::ExplosiveBarrage(bool explosiveBarrage, Unit* boss)
|
||||
{
|
||||
std::vector<Unit*> orbs;
|
||||
Unit* closestOrb = nullptr;
|
||||
float closestDistance = std::numeric_limits<float>::max();
|
||||
|
||||
// First gather all orbs
|
||||
GuidVector npcs1 = AI_VALUE(GuidVector, "nearest hostile npcs");
|
||||
for (auto& npc : npcs1)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(npc);
|
||||
if (unit && unit->IsAlive() && unit->HasAura(SPELL_EXPLODING_ORB_VISUAL))
|
||||
{
|
||||
orbs.push_back(unit);
|
||||
float dist = bot->GetDistance(unit);
|
||||
if (dist < closestDistance)
|
||||
{
|
||||
closestDistance = dist;
|
||||
closestOrb = unit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!orbs.empty() && closestOrb && bot->GetExactDist2d(closestOrb) < 7.0f)
|
||||
{
|
||||
// Get player positions to avoid moving toward them
|
||||
std::vector<Position> playerPositions;
|
||||
Group* group = bot->GetGroup();
|
||||
if (group)
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
if (Player* member = ref->GetSource())
|
||||
{
|
||||
if (member != bot && member->IsAlive() && bot->GetMap() == member->GetMap() &&
|
||||
bot->GetExactDist2d(member) < 20.0f)
|
||||
{
|
||||
playerPositions.push_back(member->GetPosition());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate several potential escape positions
|
||||
const int NUM_ANGLES = 16; // Check 16 different directions
|
||||
const float SAFE_DISTANCE = 7.0f;
|
||||
const float MAX_BOSS_DISTANCE = 30.0f; // Maximum allowed distance from boss
|
||||
const float ANGLE_INCREMENT = 2 * M_PI / NUM_ANGLES;
|
||||
|
||||
Position bestPos;
|
||||
bool foundValidPos = false;
|
||||
float bestScore = -1.0f;
|
||||
|
||||
for (int i = 0; i < NUM_ANGLES; i++)
|
||||
{
|
||||
float angle = i * ANGLE_INCREMENT;
|
||||
Position potentialPos;
|
||||
potentialPos.m_positionX = bot->GetPositionX() + SAFE_DISTANCE * cos(angle);
|
||||
potentialPos.m_positionY = bot->GetPositionY() + SAFE_DISTANCE * sin(angle);
|
||||
potentialPos.m_positionZ = bot->GetPositionZ();
|
||||
|
||||
// Check if position is valid (not in a wall)
|
||||
if (!bot->IsWithinLOS(potentialPos.m_positionX, potentialPos.m_positionY, potentialPos.m_positionZ))
|
||||
continue;
|
||||
|
||||
// Check if position is within maximum allowed distance from boss
|
||||
if (boss && sqrt(pow(potentialPos.m_positionX - boss->GetPositionX(), 2) +
|
||||
pow(potentialPos.m_positionY - boss->GetPositionY(), 2)) > MAX_BOSS_DISTANCE)
|
||||
continue;
|
||||
|
||||
// Score this position based on:
|
||||
// 1. Distance from all orbs (farther is better)
|
||||
// 2. Distance from other players (farther is better)
|
||||
// 3. Distance from boss (closer is better, but still safe)
|
||||
float score = 0.0f;
|
||||
|
||||
// Check distance from all orbs
|
||||
float minOrbDist = std::numeric_limits<float>::max();
|
||||
for (Unit* orb : orbs)
|
||||
{
|
||||
float orbDist = sqrt(pow(potentialPos.m_positionX - orb->GetPositionX(), 2) +
|
||||
pow(potentialPos.m_positionY - orb->GetPositionY(), 2));
|
||||
minOrbDist = std::min(minOrbDist, orbDist);
|
||||
}
|
||||
score += minOrbDist * 2.0f; // Weight orb distance more heavily
|
||||
|
||||
// Check distance from other players
|
||||
for (const Position& playerPos : playerPositions)
|
||||
{
|
||||
float playerDist = sqrt(pow(potentialPos.m_positionX - playerPos.m_positionX, 2) +
|
||||
pow(potentialPos.m_positionY - playerPos.m_positionY, 2));
|
||||
score += std::min(playerDist, 10.0f); // Cap player distance contribution
|
||||
}
|
||||
|
||||
// Factor in proximity to boss (closer is better, as long as we're safe from orbs)
|
||||
if (boss)
|
||||
{
|
||||
float bossDist = sqrt(pow(potentialPos.m_positionX - boss->GetPositionX(), 2) +
|
||||
pow(potentialPos.m_positionY - boss->GetPositionY(), 2));
|
||||
// Add points for being closer to boss (inverse relationship)
|
||||
// but only if we're safely away from orbs
|
||||
if (minOrbDist > SAFE_DISTANCE)
|
||||
{
|
||||
score += (MAX_BOSS_DISTANCE - bossDist) * 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
// If this is the best position so far, remember it
|
||||
if (score > bestScore)
|
||||
{
|
||||
bestScore = score;
|
||||
bestPos = potentialPos;
|
||||
foundValidPos = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If we found a valid position, move there
|
||||
if (foundValidPos)
|
||||
{
|
||||
botAI->Reset();
|
||||
// Use MoveTo instead of FleePosition since we already calculated the exact position
|
||||
return MoveTo(bot->GetMapId(), bestPos.m_positionX, bestPos.m_positionY, bot->GetPositionZ(), false, false,
|
||||
false, true, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
else
|
||||
{
|
||||
return FleePosition(closestOrb->GetPosition(), 7.0f, 250U);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TyrannusAction::Execute(Event event)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "scourgelord tyrannus");
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
bool rangedSpread = false;
|
||||
|
||||
if (botAI->IsRanged(bot) && boss->HealthBelowPct(99))
|
||||
rangedSpread = true;
|
||||
|
||||
if (rangedSpread && RangedSpread(rangedSpread))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TyrannusAction::RangedSpread(bool rangedSpread)
|
||||
{
|
||||
float radius = 10.0f;
|
||||
float moveIncrement = 3.0f;
|
||||
|
||||
GuidVector members = AI_VALUE(GuidVector, "group members");
|
||||
if (botAI->IsRanged(bot) && rangedSpread)
|
||||
{
|
||||
// Ranged: spread from other members
|
||||
for (auto& member : members)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(member);
|
||||
if (!unit || !unit->IsAlive() || unit == bot)
|
||||
continue;
|
||||
|
||||
float dist = bot->GetExactDist2d(unit);
|
||||
if (dist < radius)
|
||||
{
|
||||
float moveDistance = std::min(moveIncrement, radius - dist + 1.0f);
|
||||
return FleePosition(unit->GetPosition(), moveDistance, 250U);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
32
src/strategy/dungeons/wotlk/pitofsaron/PitOfSaronActions.h
Normal file
32
src/strategy/dungeons/wotlk/pitofsaron/PitOfSaronActions.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONPOSACTIONS_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONPOSACTIONS_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "AttackAction.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
#include "PitOfSaronTriggers.h"
|
||||
|
||||
const Position ICKANDKRICK_TANK_POSITION = Position(816.8508f, 102.331505f, 509.1586f);
|
||||
|
||||
class IckAndKrickAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
IckAndKrickAction(PlayerbotAI* ai) : AttackAction(ai, "ick and krick") {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
bool TankPosition(Unit* boss);
|
||||
bool Pursuit(bool pursuit, Unit* boss);
|
||||
bool PoisonNova(bool poisonNova, Unit* boss);
|
||||
bool ExplosiveBarrage(bool explosiveBarrage, Unit* boss);
|
||||
};
|
||||
|
||||
class TyrannusAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
TyrannusAction(PlayerbotAI* ai) : AttackAction(ai, "tyrannus") {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
bool RangedSpread(bool rangedSpread);
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,39 @@
|
||||
#include "PitOfSaronMultipliers.h"
|
||||
#include "PitOfSaronActions.h"
|
||||
#include "GenericSpellActions.h"
|
||||
#include "ChooseTargetActions.h"
|
||||
#include "MovementActions.h"
|
||||
#include "PitOfSaronTriggers.h"
|
||||
|
||||
|
||||
|
||||
float IckAndKrickMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "ick");
|
||||
if (!boss)
|
||||
return 1.0f;
|
||||
|
||||
// Allow the IckAndKrickAction to run
|
||||
if (dynamic_cast<IckAndKrickAction*>(action))
|
||||
return 1.0f;
|
||||
|
||||
if (boss->HasUnitState(UNIT_STATE_CASTING) && (boss->FindCurrentSpellBySpellId(SPELL_POISON_NOVA_POS) || boss->FindCurrentSpellBySpellId(SPELL_POISON_NOVA_POS_HC)) && bot->GetExactDist2d(boss) < 20.0f)
|
||||
return 0.0f; // Cancel all other actions when we need to handle Poison Nova
|
||||
|
||||
if (bot->GetExactDist2d(boss) < 15.0f && bot->HasAura(SPELL_PURSUIT) && !botAI->IsTank(bot))
|
||||
return 0.0f; // Cancel all other actions when we need to handle Pursuit
|
||||
|
||||
if (!botAI->IsHeal(bot) && boss->HasUnitState(UNIT_STATE_CASTING) && (boss->FindCurrentSpellBySpellId(SPELL_EXPLOSIVE_BARRAGE_ICK) || boss->FindCurrentSpellBySpellId(SPELL_EXPLOSIVE_BARRAGE_KRICK)))
|
||||
return 0.0f; // Cancel all other actions when we need to handle Explosive Barrage
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float GarfrostMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "garfrost");
|
||||
if (!boss)
|
||||
return 1.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONPOSMULTIPLIERS_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONPOSMULTIPLIERS_H
|
||||
|
||||
#include "Multiplier.h"
|
||||
|
||||
class IckAndKrickMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
IckAndKrickMultiplier(PlayerbotAI* ai) : Multiplier(ai, "ick and krick") {}
|
||||
|
||||
public:
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class GarfrostMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
GarfrostMultiplier(PlayerbotAI* ai) : Multiplier(ai, "garfrost") { }
|
||||
|
||||
float GetValue(Action* action) override;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,17 @@
|
||||
#include "PitOfSaronStrategy.h"
|
||||
#include "PitOfSaronMultipliers.h"
|
||||
|
||||
void WotlkDungeonPoSStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(new TriggerNode("ick and krick",
|
||||
NextAction::array(0, new NextAction("ick and krick", ACTION_RAID + 5), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode("tyrannus",
|
||||
NextAction::array(0, new NextAction("tyrannus", ACTION_RAID + 5), nullptr)));
|
||||
}
|
||||
|
||||
void WotlkDungeonPoSStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||
{
|
||||
multipliers.push_back(new IckAndKrickMultiplier(botAI));
|
||||
//multipliers.push_back(new AttackFragmentMultiplier(botAI));
|
||||
}
|
||||
16
src/strategy/dungeons/wotlk/pitofsaron/PitOfSaronStrategy.h
Normal file
16
src/strategy/dungeons/wotlk/pitofsaron/PitOfSaronStrategy.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONPOSSTRATEGY_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONPOSSTRATEGY_H
|
||||
#include "Multiplier.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
class WotlkDungeonPoSStrategy : public Strategy
|
||||
{
|
||||
public:
|
||||
WotlkDungeonPoSStrategy(PlayerbotAI* ai) : Strategy(ai) {}
|
||||
std::string const getName() override { return "pit of saron"; }
|
||||
void InitTriggers(std::vector<TriggerNode*> &triggers) override;
|
||||
void InitMultipliers(std::vector<Multiplier*> &multipliers) override;
|
||||
|
||||
};
|
||||
|
||||
#endif // !_PLAYERBOT_WOTLKDUNGEONFOSSTRATEGY_H
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONPOSTRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONPOSTRIGGERCONTEXT_H
|
||||
|
||||
#include "NamedObjectContext.h"
|
||||
#include "AiObjectContext.h"
|
||||
#include "PitOfSaronTriggers.h"
|
||||
|
||||
class WotlkDungeonPoSTriggerContext : public NamedObjectContext<Trigger>
|
||||
{
|
||||
public:
|
||||
WotlkDungeonPoSTriggerContext()
|
||||
{
|
||||
creators["ick and krick"] = &WotlkDungeonPoSTriggerContext::ick_and_krick;
|
||||
creators["tyrannus"] = &WotlkDungeonPoSTriggerContext::tyrannus;
|
||||
}
|
||||
|
||||
private:
|
||||
static Trigger* ick_and_krick(PlayerbotAI* ai) { return new IckAndKrickTrigger(ai); }
|
||||
static Trigger* tyrannus(PlayerbotAI* ai) { return new TyrannusTrigger(ai); }
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
#include "Playerbots.h"
|
||||
#include "PitOfSaronTriggers.h"
|
||||
#include "AiObject.h"
|
||||
#include "AiObjectContext.h"
|
||||
|
||||
bool IckAndKrickTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "Ick");
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TyrannusTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "scourgelord tyrannus");
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
48
src/strategy/dungeons/wotlk/pitofsaron/PitOfSaronTriggers.h
Normal file
48
src/strategy/dungeons/wotlk/pitofsaron/PitOfSaronTriggers.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#ifndef _PLAYERBOT_WOTLKDUNGEONPOSTRIGGERS_H
|
||||
#define _PLAYERBOT_WOTLKDUNGEONPOSTRIGGERS_H
|
||||
|
||||
#include "Trigger.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "GenericTriggers.h"
|
||||
#include "DungeonStrategyUtils.h"
|
||||
|
||||
enum PitOfSaronIDs
|
||||
{
|
||||
//NPCs
|
||||
NPC_GARFROST = 36494,
|
||||
NPC_KRICK = 36477,
|
||||
NPC_ICK = 36476,
|
||||
NPC_TYRANNUS = 36658,
|
||||
NPC_RIMEFANG = 36661,
|
||||
|
||||
//Spells
|
||||
SPELL_PURSUIT = 68987,
|
||||
SPELL_POISON_NOVA_POS = 68989,
|
||||
SPELL_POISON_NOVA_POS_HC = 70434,
|
||||
SPELL_EXPLOSIVE_BARRAGE_KRICK = 69012,
|
||||
SPELL_EXPLOSIVE_BARRAGE_ICK = 69263,
|
||||
SPELL_EXPLOSIVE_BARRAGE_SUMMON = 69015,
|
||||
SPELL_EXPLODING_ORB_VISUAL = 69017,
|
||||
SPELL_MARK_OF_RIMEFANG = 69275,
|
||||
RIMEFANG_SPELL_HOARFROST = 69246,
|
||||
RIMEFANG_SPELL_HOARFROST_HC = 69245,
|
||||
RIMEFANG_SPELL_HOARFROST_HC2 = 69645,
|
||||
};
|
||||
|
||||
class IckAndKrickTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IckAndKrickTrigger(PlayerbotAI* ai) : Trigger(ai, "ick and krick") {}
|
||||
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class TyrannusTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
TyrannusTrigger(PlayerbotAI* ai) : Trigger(ai, "tyrannus") {}
|
||||
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user