mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-11-29 15:58:20 +08:00
Merge branch 'master' into new_rpg_strats
This commit is contained in:
@@ -368,6 +368,10 @@ AiPlayerbot.SyncQuestWithPlayer = 1
|
||||
# Default: 0 (disabled)
|
||||
AiPlayerbot.SyncQuestForPlayer = 0
|
||||
|
||||
# Bots will drop obsolete quests
|
||||
# Default: 1 (enabled)
|
||||
AiPlayerbot.DropObsoleteQuests = 1
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
@@ -1027,6 +1031,56 @@ AiPlayerbot.PremadeSpecLink.11.3.80 = -553202032322010053100030310511-205503012
|
||||
#
|
||||
###################################################################################################
|
||||
|
||||
###################################
|
||||
# #
|
||||
# WORLD BUFFS #
|
||||
# #
|
||||
###################################
|
||||
|
||||
####################################################################################################
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
# Applies a permanent buff to all bots when not in combat simulating flasks, food, rune etc.
|
||||
# WorldBuff.Faction.Class.Spec.MinLevel.MaxLevel
|
||||
|
||||
AiPlayerbot.WorldBuff.0.1.0.80.80 = 53760,57358 #WARRIOR ARMS
|
||||
AiPlayerbot.WorldBuff.0.1.1.80.80 = 53760,57358 #WARRIOR FURY
|
||||
AiPlayerbot.WorldBuff.0.1.2.80.80 = 53758,57356 #WARRIOR PROTECTION
|
||||
AiPlayerbot.WorldBuff.0.2.0.80.80 = 60347,53749,57332 #PALADIN HOLY
|
||||
AiPlayerbot.WorldBuff.0.2.1.80.80 = 53758,57356 #PALADIN PROTECTION
|
||||
AiPlayerbot.WorldBuff.0.2.2.80.80 = 53760,57371 #PALADIN RETRIBUTION
|
||||
AiPlayerbot.WorldBuff.0.3.0.80.80 = 53760,57325 #HUNTER BEAST
|
||||
AiPlayerbot.WorldBuff.0.3.1.80.80 = 53760,57358 #HUNTER MARKSMANSHIP
|
||||
AiPlayerbot.WorldBuff.0.3.2.80.80 = 53760,57367 #HUNTER SURVIVAL
|
||||
AiPlayerbot.WorldBuff.0.4.0.80.80 = 53760,57325 #ROGUE ASSASINATION
|
||||
AiPlayerbot.WorldBuff.0.4.1.80.80 = 53760,57358 #ROGUE COMBAT
|
||||
AiPlayerbot.WorldBuff.0.4.2.80.80 = 53760,57367 #ROGUE SUBTLETY
|
||||
AiPlayerbot.WorldBuff.0.5.0.80.80 = 53755,57327 #PRIEST DISCIPLINE
|
||||
AiPlayerbot.WorldBuff.0.5.1.80.80 = 53755,57327 #PRIEST HOLY
|
||||
AiPlayerbot.WorldBuff.0.5.2.80.80 = 53755,57327 #PRIEST SHADOW
|
||||
AiPlayerbot.WorldBuff.0.6.0.80.80 = 53758,57356 #DEATH KNIGHT BLOOD
|
||||
AiPlayerbot.WorldBuff.0.6.1.80.80 = 53760,57358 #DEATH KNIGHT FROST
|
||||
AiPlayerbot.WorldBuff.0.6.2.80.80 = 53760,57358 #DEATH KNIGHT UNHOLY
|
||||
AiPlayerbot.WorldBuff.0.7.0.80.80 = 53755,57327 #SHAMAN ELEMENTAL
|
||||
AiPlayerbot.WorldBuff.0.7.1.80.80 = 53760,57325 #SHAMAN ENHANCEMENT
|
||||
AiPlayerbot.WorldBuff.0.7.2.80.80 = 53755,57327 #SHAMAN RESTORATION
|
||||
AiPlayerbot.WorldBuff.0.8.0.80.80 = 53755,57327 #MAGE ARCANE
|
||||
AiPlayerbot.WorldBuff.0.8.1.80.80 = 53755,57327 #MAGE FIRE
|
||||
AiPlayerbot.WorldBuff.0.8.2.80.80 = 53755,57327 #MAGE FROST
|
||||
AiPlayerbot.WorldBuff.0.9.0.80.80 = 53755,57327 #WARLOCK AFFLICTION
|
||||
AiPlayerbot.WorldBuff.0.9.1.80.80 = 53755,57327 #WARLOCK DEMONOLOGY
|
||||
AiPlayerbot.WorldBuff.0.9.2.80.80 = 53755,57327 #WARLOCK DESTRUCTION
|
||||
AiPlayerbot.WorldBuff.0.11.0.80.80 = 53755,57327 #DRUID BALANCE
|
||||
AiPlayerbot.WorldBuff.0.11.1.80.80 = 53749,53763,57367 #DRUID FERAL
|
||||
AiPlayerbot.WorldBuff.0.11.2.80.80 = 54212,57334 #DRUID RESTORATION
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
###################################################################################################
|
||||
|
||||
###################################
|
||||
# #
|
||||
# RANDOM BOT DEFAULT TALENT SPEC #
|
||||
@@ -1416,9 +1470,6 @@ AiPlayerbot.BroadcastChanceGuildManagement = 30000
|
||||
# Example: AiPlayerbot.AllowedLogFiles = travelNodes.csv,travelPaths.csv,TravelNodeStore.h,bot_movement.csv,bot_location.csv
|
||||
AiPlayerbot.AllowedLogFiles = ""
|
||||
|
||||
Appender.Playerbots=2,5,0,Playerbots.log,w
|
||||
Logger.playerbots=5,Console Playerbots
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
@@ -1606,6 +1606,9 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
|
||||
case 608:
|
||||
strategyName = "wotlk-vh"; // Violet Hold
|
||||
break;
|
||||
case 615:
|
||||
strategyName = "wotlk-os"; // Obsidian Sanctum
|
||||
break;
|
||||
case 619:
|
||||
strategyName = "wotlk-ok"; // Ahn'kahet: The Old Kingdom
|
||||
break;
|
||||
@@ -5466,10 +5469,10 @@ bool PlayerbotAI::CanMove()
|
||||
if (IsInVehicle() && !IsInVehicle(true))
|
||||
return false;
|
||||
|
||||
if (bot->isFrozen() || bot->IsPolymorphed() || (bot->isDead() && !bot->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) ||
|
||||
bot->IsBeingTeleported() || bot->isInRoots() || bot->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION) ||
|
||||
bot->HasAuraType(SPELL_AURA_MOD_CONFUSE) || bot->IsCharmed() || bot->HasAuraType(SPELL_AURA_MOD_STUN) ||
|
||||
bot->HasUnitState(UNIT_STATE_IN_FLIGHT) || bot->HasUnitState(UNIT_STATE_LOST_CONTROL))
|
||||
if (bot->isFrozen() || bot->IsPolymorphed() || (bot->isDead() && !bot->HasPlayerFlag(PLAYER_FLAGS_GHOST)) ||
|
||||
bot->IsBeingTeleported() || bot->HasRootAura() || bot->HasSpiritOfRedemptionAura() ||
|
||||
bot->HasConfuseAura() || bot->IsCharmed() || bot->HasStunAura() ||
|
||||
bot->IsInFlight() || bot->HasUnitState(UNIT_STATE_LOST_CONTROL))
|
||||
return false;
|
||||
|
||||
return bot->GetMotionMaster()->GetCurrentMovementGeneratorType() != FLIGHT_MOTION_TYPE;
|
||||
|
||||
@@ -402,12 +402,15 @@ bool PlayerbotAIConfig::Initialize()
|
||||
for (uint32 factionId = 0; factionId < 3; factionId++)
|
||||
{
|
||||
for (uint32 classId = 0; classId < MAX_CLASSES; classId++)
|
||||
{
|
||||
for (uint32 specId = 0; specId < MAX_SPECNO; specId++)
|
||||
{
|
||||
for (uint32 minLevel = 0; minLevel < MAX_LEVEL; minLevel++)
|
||||
{
|
||||
for (uint32 maxLevel = 0; maxLevel < MAX_LEVEL; maxLevel++)
|
||||
{
|
||||
loadWorldBuf(factionId, classId, minLevel, maxLevel);
|
||||
loadWorldBuf(factionId, classId, specId, minLevel, maxLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -491,6 +494,7 @@ bool PlayerbotAIConfig::Initialize()
|
||||
twoRoundsGearInit = sConfigMgr->GetOption<bool>("AiPlayerbot.TwoRoundsGearInit", false);
|
||||
syncQuestWithPlayer = sConfigMgr->GetOption<bool>("AiPlayerbot.SyncQuestWithPlayer", true);
|
||||
syncQuestForPlayer = sConfigMgr->GetOption<bool>("AiPlayerbot.SyncQuestForPlayer", false);
|
||||
dropObsoleteQuests = sConfigMgr->GetOption<bool>("AiPlayerbot.DropObsoleteQuests", true);
|
||||
autoTrainSpells = sConfigMgr->GetOption<std::string>("AiPlayerbot.AutoTrainSpells", "yes");
|
||||
autoPickTalents = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoPickTalents", true);
|
||||
autoUpgradeEquip = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoUpgradeEquip", false);
|
||||
@@ -645,36 +649,50 @@ void PlayerbotAIConfig::log(std::string const fileName, char const* str, ...)
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void PlayerbotAIConfig::loadWorldBuf(uint32 factionId1, uint32 classId1, uint32 minLevel1, uint32 maxLevel1)
|
||||
void PlayerbotAIConfig::loadWorldBuf(uint32 factionId1, uint32 classId1, uint32 specId1, uint32 minLevel1, uint32 maxLevel1)
|
||||
{
|
||||
std::vector<uint32> buffs;
|
||||
|
||||
std::ostringstream os;
|
||||
os << "AiPlayerbot.WorldBuff." << factionId1 << "." << classId1 << "." << minLevel1 << "." << maxLevel1;
|
||||
os << "AiPlayerbot.WorldBuff." << factionId1 << "." << classId1 << "." << specId1 << "." << minLevel1 << "." << maxLevel1;
|
||||
|
||||
LoadList<std::vector<uint32>>(sConfigMgr->GetOption<std::string>(os.str().c_str(), "", false), buffs);
|
||||
|
||||
for (auto buff : buffs)
|
||||
{
|
||||
worldBuff wb = {buff, factionId1, classId1, minLevel1, maxLevel1};
|
||||
worldBuff wb = {buff, factionId1, classId1, specId1, minLevel1, maxLevel1};
|
||||
worldBuffs.push_back(wb);
|
||||
}
|
||||
|
||||
if (maxLevel1 == 0)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "AiPlayerbot.WorldBuff." << factionId1 << "." << classId1 << "." << minLevel1;
|
||||
os << "AiPlayerbot.WorldBuff." << factionId1 << "." << classId1 << "." << specId1 << "." << minLevel1;
|
||||
|
||||
LoadList<std::vector<uint32>>(sConfigMgr->GetOption<std::string>(os.str().c_str(), "", false), buffs);
|
||||
|
||||
for (auto buff : buffs)
|
||||
{
|
||||
worldBuff wb = {buff, factionId1, classId1, minLevel1, maxLevel1};
|
||||
worldBuff wb = {buff, factionId1, classId1, specId1, minLevel1, maxLevel1};
|
||||
worldBuffs.push_back(wb);
|
||||
}
|
||||
}
|
||||
|
||||
if (maxLevel1 == 0 && minLevel1 == 0)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "AiPlayerbot.WorldBuff." << factionId1 << "." << factionId1 << "." << classId1 << "." << specId1;
|
||||
|
||||
LoadList<std::vector<uint32>>(sConfigMgr->GetOption<std::string>(os.str().c_str(), "", false), buffs);
|
||||
|
||||
for (auto buff : buffs)
|
||||
{
|
||||
worldBuff wb = {buff, factionId1, classId1, specId1, minLevel1, maxLevel1};
|
||||
worldBuffs.push_back(wb);
|
||||
}
|
||||
}
|
||||
|
||||
if (maxLevel1 == 0 && minLevel1 == 0 && specId1 == 0)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "AiPlayerbot.WorldBuff." << factionId1 << "." << factionId1 << "." << classId1;
|
||||
@@ -683,12 +701,12 @@ void PlayerbotAIConfig::loadWorldBuf(uint32 factionId1, uint32 classId1, uint32
|
||||
|
||||
for (auto buff : buffs)
|
||||
{
|
||||
worldBuff wb = {buff, factionId1, classId1, minLevel1, maxLevel1};
|
||||
worldBuff wb = {buff, factionId1, classId1, specId1, minLevel1, maxLevel1};
|
||||
worldBuffs.push_back(wb);
|
||||
}
|
||||
}
|
||||
|
||||
if (classId1 == 0 && maxLevel1 == 0 && minLevel1 == 0)
|
||||
if (classId1 == 0 && maxLevel1 == 0 && minLevel1 == 0 && specId1 == 0)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "AiPlayerbot.WorldBuff." << factionId1;
|
||||
@@ -697,12 +715,12 @@ void PlayerbotAIConfig::loadWorldBuf(uint32 factionId1, uint32 classId1, uint32
|
||||
|
||||
for (auto buff : buffs)
|
||||
{
|
||||
worldBuff wb = {buff, factionId1, classId1, minLevel1, maxLevel1};
|
||||
worldBuff wb = {buff, factionId1, classId1, specId1, minLevel1, maxLevel1};
|
||||
worldBuffs.push_back(wb);
|
||||
}
|
||||
}
|
||||
|
||||
if (factionId1 == 0 && classId1 == 0 && maxLevel1 == 0 && minLevel1 == 0)
|
||||
if (factionId1 == 0 && classId1 == 0 && maxLevel1 == 0 && minLevel1 == 0 && specId1 == 0)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "AiPlayerbot.WorldBuff";
|
||||
@@ -711,7 +729,7 @@ void PlayerbotAIConfig::loadWorldBuf(uint32 factionId1, uint32 classId1, uint32
|
||||
|
||||
for (auto buff : buffs)
|
||||
{
|
||||
worldBuff wb = {buff, factionId1, classId1, minLevel1, maxLevel1};
|
||||
worldBuff wb = {buff, factionId1, classId1, specId1, minLevel1, maxLevel1};
|
||||
worldBuffs.push_back(wb);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,6 +247,7 @@ public:
|
||||
uint32 spellId;
|
||||
uint32 factionId = 0;
|
||||
uint32 classId = 0;
|
||||
uint32 specId = 0;
|
||||
uint32 minLevel = 0;
|
||||
uint32 maxLevel = 0;
|
||||
};
|
||||
@@ -275,6 +276,7 @@ public:
|
||||
bool twoRoundsGearInit;
|
||||
bool syncQuestWithPlayer;
|
||||
bool syncQuestForPlayer;
|
||||
bool dropObsoleteQuests;
|
||||
std::string autoTrainSpells;
|
||||
bool autoPickTalents;
|
||||
bool autoUpgradeEquip;
|
||||
@@ -328,7 +330,7 @@ public:
|
||||
}
|
||||
void log(std::string const fileName, const char* str, ...);
|
||||
|
||||
void loadWorldBuf(uint32 factionId, uint32 classId, uint32 minLevel, uint32 maxLevel);
|
||||
void loadWorldBuf(uint32 factionId, uint32 classId, uint32 specId, uint32 minLevel, uint32 maxLevel);
|
||||
static std::vector<std::vector<uint32>> ParseTempTalentsOrder(uint32 cls, std::string temp_talents_order);
|
||||
static std::vector<std::vector<uint32>> ParseTempPetTalentsOrder(uint32 spec, std::string temp_talents_order);
|
||||
};
|
||||
|
||||
@@ -52,7 +52,7 @@ public:
|
||||
|
||||
void OnDatabaseSelectIndexLogout(Player* player, uint32& statementIndex, uint32& statementParam) override
|
||||
{
|
||||
statementIndex = CHAR_UPD_CHAR_ONLINE;
|
||||
statementIndex = CHAR_UPD_CHAR_OFFLINE;
|
||||
statementParam = player->GetGUID().GetCounter();
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
#include "raids/blackwinglair/RaidBwlTriggerContext.h"
|
||||
#include "raids/naxxramas/RaidNaxxActionContext.h"
|
||||
#include "raids/naxxramas/RaidNaxxTriggerContext.h"
|
||||
#include "raids/obsidiansanctum/RaidOsActionContext.h"
|
||||
#include "raids/obsidiansanctum/RaidOsTriggerContext.h"
|
||||
#include "raids/moltencore/RaidMcActionContext.h"
|
||||
#include "raids/moltencore/RaidMcTriggerContext.h"
|
||||
#include "raids/aq20/RaidAq20ActionContext.h"
|
||||
@@ -48,6 +50,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
|
||||
actionContexts.Add(new RaidBwlActionContext());
|
||||
actionContexts.Add(new RaidAq20ActionContext());
|
||||
actionContexts.Add(new RaidNaxxActionContext());
|
||||
actionContexts.Add(new RaidOsActionContext());
|
||||
actionContexts.Add(new RaidUlduarActionContext());
|
||||
actionContexts.Add(new RaidIccActionContext());
|
||||
actionContexts.Add(new WotlkDungeonUKActionContext());
|
||||
@@ -71,6 +74,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
|
||||
triggerContexts.Add(new RaidBwlTriggerContext());
|
||||
triggerContexts.Add(new RaidAq20TriggerContext());
|
||||
triggerContexts.Add(new RaidNaxxTriggerContext());
|
||||
triggerContexts.Add(new RaidOsTriggerContext());
|
||||
triggerContexts.Add(new RaidUlduarTriggerContext());
|
||||
triggerContexts.Add(new RaidIccTriggerContext());
|
||||
triggerContexts.Add(new WotlkDungeonUKTriggerContext());
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
#include "LootObjectStack.h"
|
||||
#include "Playerbots.h"
|
||||
#include "PossibleRpgTargetsValue.h"
|
||||
#include "ServerFacade.h"
|
||||
#include "PvpTriggers.h"
|
||||
#include "ServerFacade.h"
|
||||
|
||||
bool AttackEnemyPlayerAction::isUseful()
|
||||
{
|
||||
@@ -129,3 +129,33 @@ bool DpsAssistAction::isUseful()
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AttackRtiTargetAction::Execute(Event event)
|
||||
{
|
||||
Unit* rtiTarget = AI_VALUE(Unit*, "rti target");
|
||||
|
||||
if (rtiTarget && rtiTarget->IsInWorld() && rtiTarget->GetMapId() == bot->GetMapId())
|
||||
{
|
||||
botAI->GetAiObjectContext()->GetValue<GuidVector>("prioritized targets")->Set({rtiTarget->GetGUID()});
|
||||
bool result = Attack(botAI->GetUnit(rtiTarget->GetGUID()));
|
||||
if (result)
|
||||
{
|
||||
context->GetValue<ObjectGuid>("pull target")->Set(rtiTarget->GetGUID());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
botAI->TellError("I dont see my rti attack target");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AttackRtiTargetAction::isUseful()
|
||||
{
|
||||
if (botAI->ContainsStrategy(STRATEGY_TYPE_HEAL))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -69,6 +69,8 @@ public:
|
||||
AttackRtiTargetAction(PlayerbotAI* botAI) : AttackAction(botAI, "attack rti target") {}
|
||||
|
||||
std::string const GetTargetName() override { return "rti target"; }
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class AttackEnemyFlagCarrierAction : public AttackAction
|
||||
|
||||
@@ -68,6 +68,11 @@ bool CleanQuestLogAction::Execute(Event event)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!sPlayerbotAIConfig->dropObsoleteQuests)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only output this message if "debug rpg" strategy is enabled
|
||||
if (botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT))
|
||||
{
|
||||
|
||||
@@ -935,10 +935,10 @@ bool MovementAction::IsMovingAllowed()
|
||||
if (botAI->IsInVehicle() && !botAI->IsInVehicle(true))
|
||||
return false;
|
||||
|
||||
if (bot->isFrozen() || bot->IsPolymorphed() || (bot->isDead() && !bot->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) ||
|
||||
bot->IsBeingTeleported() || bot->isInRoots() || bot->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION) ||
|
||||
bot->HasAuraType(SPELL_AURA_MOD_CONFUSE) || bot->IsCharmed() || bot->HasAuraType(SPELL_AURA_MOD_STUN) ||
|
||||
bot->HasUnitState(UNIT_STATE_IN_FLIGHT) || bot->HasUnitState(UNIT_STATE_LOST_CONTROL))
|
||||
if (bot->isFrozen() || bot->IsPolymorphed() || (bot->isDead() && !bot->HasPlayerFlag(PLAYER_FLAGS_GHOST)) ||
|
||||
bot->IsBeingTeleported() || bot->HasRootAura() || bot->HasSpiritOfRedemptionAura() ||
|
||||
bot->HasConfuseAura() || bot->IsCharmed() || bot->HasStunAura() ||
|
||||
bot->IsInFlight() || bot->HasUnitState(UNIT_STATE_LOST_CONTROL))
|
||||
return false;
|
||||
|
||||
if (bot->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_CONTROLLED) != NULL_MOTION_TYPE)
|
||||
@@ -2564,9 +2564,9 @@ bool SetFacingTargetAction::isUseful() { return !AI_VALUE2(bool, "facing", "curr
|
||||
|
||||
bool SetFacingTargetAction::isPossible()
|
||||
{
|
||||
if (bot->isFrozen() || bot->IsPolymorphed() || (bot->isDead() && !bot->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) ||
|
||||
bot->IsBeingTeleported() || bot->HasAuraType(SPELL_AURA_MOD_CONFUSE) || bot->IsCharmed() ||
|
||||
bot->HasAuraType(SPELL_AURA_MOD_STUN) || bot->HasUnitState(UNIT_STATE_IN_FLIGHT) ||
|
||||
if (bot->isFrozen() || bot->IsPolymorphed() || (bot->isDead() && !bot->HasPlayerFlag(PLAYER_FLAGS_GHOST)) ||
|
||||
bot->IsBeingTeleported() || bot->HasConfuseAura() || bot->IsCharmed() ||
|
||||
bot->HasStunAura() || bot->IsInFlight() ||
|
||||
bot->HasUnitState(UNIT_STATE_LOST_CONTROL))
|
||||
return false;
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ bool ReleaseSpiritAction::Execute(Event event)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bot->GetCorpse() && bot->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
|
||||
if (bot->GetCorpse() && bot->HasPlayerFlag(PLAYER_FLAGS_GHOST))
|
||||
{
|
||||
botAI->TellMasterNoFacing("I am already a spirit");
|
||||
return false;
|
||||
@@ -149,7 +149,7 @@ bool AutoReleaseSpiritAction::isUseful()
|
||||
if (bot->InBattleground())
|
||||
return true;
|
||||
|
||||
if (bot->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
|
||||
if (bot->HasPlayerFlag(PLAYER_FLAGS_GHOST))
|
||||
return false;
|
||||
|
||||
if (!bot->GetGroup())
|
||||
|
||||
@@ -360,4 +360,4 @@ bool SpiritHealerAction::Execute(Event event)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SpiritHealerAction::isUseful() { return bot->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST); }
|
||||
bool SpiritHealerAction::isUseful() { return bot->HasPlayerFlag(PLAYER_FLAGS_GHOST); }
|
||||
|
||||
@@ -80,7 +80,7 @@ bool MarkRtiAction::Execute(Event event)
|
||||
for (uint8 i = 0; i < 8; i++)
|
||||
{
|
||||
ObjectGuid iconGUID = group->GetTargetIcon(i);
|
||||
if (guid == unit->GetGUID())
|
||||
if (iconGUID == unit->GetGUID())
|
||||
{
|
||||
marked = true;
|
||||
break;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "WorldBuffAction.h"
|
||||
|
||||
#include "AiFactory.h"
|
||||
#include "Event.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
@@ -39,6 +40,11 @@ std::vector<uint32> WorldBuffAction::NeedWorldBuffs(Unit* unit)
|
||||
if (wb.classId != 0 && wb.classId != unit->getClass())
|
||||
continue;
|
||||
|
||||
uint8 tab = AiFactory::GetPlayerSpecTab(unit->ToPlayer());
|
||||
|
||||
if (wb.specId != tab)
|
||||
continue;
|
||||
|
||||
if (wb.minLevel != 0 && wb.minLevel > unit->GetLevel())
|
||||
continue;
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "Strategy.h"
|
||||
#include "RaidBwlStrategy.h"
|
||||
#include "RaidNaxxStrategy.h"
|
||||
#include "RaidOsStrategy.h"
|
||||
#include "RaidMcStrategy.h"
|
||||
#include "RaidAq20Strategy.h"
|
||||
#include "RaidIccStrategy.h"
|
||||
@@ -21,6 +22,7 @@ public:
|
||||
creators["bwl"] = &RaidStrategyContext::bwl;
|
||||
creators["aq20"] = &RaidStrategyContext::aq20;
|
||||
creators["naxx"] = &RaidStrategyContext::naxx;
|
||||
creators["wotlk-os"] = &RaidStrategyContext::wotlk_os;
|
||||
creators["uld"] = &RaidStrategyContext::uld;
|
||||
creators["icc"] = &RaidStrategyContext::icc;
|
||||
}
|
||||
@@ -30,6 +32,7 @@ private:
|
||||
static Strategy* bwl(PlayerbotAI* botAI) { return new RaidBwlStrategy(botAI); }
|
||||
static Strategy* aq20(PlayerbotAI* botAI) { return new RaidAq20Strategy(botAI); }
|
||||
static Strategy* naxx(PlayerbotAI* botAI) { return new RaidNaxxStrategy(botAI); }
|
||||
static Strategy* wotlk_os(PlayerbotAI* botAI) { return new RaidOsStrategy(botAI); }
|
||||
static Strategy* uld(PlayerbotAI* botAI) { return new RaidUlduarStrategy(botAI); }
|
||||
static Strategy* icc(PlayerbotAI* botAI) { return new RaidIccStrategy(botAI); }
|
||||
};
|
||||
|
||||
30
src/strategy/raids/obsidiansanctum/RaidOsActionContext.h
Normal file
30
src/strategy/raids/obsidiansanctum/RaidOsActionContext.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef _PLAYERBOT_RAIDOSACTIONCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDOSACTIONCONTEXT_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidOsActions.h"
|
||||
|
||||
class RaidOsActionContext : public NamedObjectContext<Action>
|
||||
{
|
||||
public:
|
||||
RaidOsActionContext()
|
||||
{
|
||||
creators["sartharion tank position"] = &RaidOsActionContext::tank_position;
|
||||
creators["avoid twilight fissure"] = &RaidOsActionContext::avoid_twilight_fissure;
|
||||
creators["avoid flame tsunami"] = &RaidOsActionContext::avoid_flame_tsunami;
|
||||
creators["sartharion attack priority"] = &RaidOsActionContext::attack_priority;
|
||||
creators["enter twilight portal"] = &RaidOsActionContext::enter_twilight_portal;
|
||||
creators["exit twilight portal"] = &RaidOsActionContext::exit_twilight_portal;
|
||||
}
|
||||
|
||||
private:
|
||||
static Action* tank_position(PlayerbotAI* ai) { return new SartharionTankPositionAction(ai); }
|
||||
static Action* avoid_twilight_fissure(PlayerbotAI* ai) { return new AvoidTwilightFissureAction(ai); }
|
||||
static Action* avoid_flame_tsunami(PlayerbotAI* ai) { return new AvoidFlameTsunamiAction(ai); }
|
||||
static Action* attack_priority(PlayerbotAI* ai) { return new SartharionAttackPriorityAction(ai); }
|
||||
static Action* enter_twilight_portal(PlayerbotAI* ai) { return new EnterTwilightPortalAction(ai); }
|
||||
static Action* exit_twilight_portal(PlayerbotAI* ai) { return new ExitTwilightPortalAction(ai); }
|
||||
};
|
||||
|
||||
#endif
|
||||
246
src/strategy/raids/obsidiansanctum/RaidOsActions.cpp
Normal file
246
src/strategy/raids/obsidiansanctum/RaidOsActions.cpp
Normal file
@@ -0,0 +1,246 @@
|
||||
#include "RaidOsActions.h"
|
||||
#include "RaidOsTriggers.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
bool SartharionTankPositionAction::Execute(Event event)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion");
|
||||
if (!boss) { return false; }
|
||||
|
||||
// Unit* shadron = AI_VALUE2(Unit*, "find target", "shadron");
|
||||
// Unit* tenebron = AI_VALUE2(Unit*, "find target", "tenebron");
|
||||
// Unit* vesperon = AI_VALUE2(Unit*, "find target", "vesperon");
|
||||
Unit* shadron = nullptr;
|
||||
Unit* tenebron = nullptr;
|
||||
Unit* vesperon = nullptr;
|
||||
|
||||
// Detect incoming drakes before they are on aggro table
|
||||
GuidVector targets = AI_VALUE(GuidVector, "possible targets no los");
|
||||
for (auto& target : targets)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(target);
|
||||
if (!unit) { continue; }
|
||||
|
||||
switch (unit->GetEntry())
|
||||
{
|
||||
case NPC_SHADRON:
|
||||
shadron = unit;
|
||||
continue;
|
||||
case NPC_TENEBRON:
|
||||
tenebron = unit;
|
||||
continue;
|
||||
case NPC_VESPERON:
|
||||
vesperon = unit;
|
||||
continue;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Position currentPos = bot->GetPosition();
|
||||
// Adjustable, this is the acceptable distance to stack point that will be accepted as "safe"
|
||||
float looseDistance = 12.0f;
|
||||
|
||||
if (botAI->IsMainTank(bot))
|
||||
{
|
||||
if (bot->GetExactDist2d(SARTHARION_MAINTANK_POSITION.first, SARTHARION_MAINTANK_POSITION.second) > looseDistance)
|
||||
{
|
||||
return MoveTo(OS_MAP_ID, SARTHARION_MAINTANK_POSITION.first, SARTHARION_MAINTANK_POSITION.second, currentPos.GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
}
|
||||
// Offtank grab drakes
|
||||
else if (shadron || tenebron || vesperon)
|
||||
{
|
||||
float triggerDistance = 100.0f;
|
||||
// Prioritise threat before positioning
|
||||
if (tenebron && bot->GetExactDist2d(tenebron) < triggerDistance &&
|
||||
tenebron->GetTarget() != bot->GetGUID() && AI_VALUE(Unit*, "current target") != tenebron)
|
||||
{
|
||||
return Attack(tenebron);
|
||||
}
|
||||
if (shadron && bot->GetExactDist2d(shadron) < triggerDistance &&
|
||||
shadron->GetTarget() != bot->GetGUID() && AI_VALUE(Unit*, "current target") != shadron)
|
||||
{
|
||||
return Attack(shadron);
|
||||
}
|
||||
if (vesperon && bot->GetExactDist2d(vesperon) < triggerDistance &&
|
||||
vesperon->GetTarget() != bot->GetGUID() && AI_VALUE(Unit*, "current target") != vesperon)
|
||||
{
|
||||
return Attack(vesperon);
|
||||
}
|
||||
|
||||
bool drakeInCombat = (tenebron && bot->GetExactDist2d(tenebron) < triggerDistance) ||
|
||||
(shadron && bot->GetExactDist2d(shadron) < triggerDistance) ||
|
||||
(vesperon && bot->GetExactDist2d(vesperon) < triggerDistance);
|
||||
// Offtank has threat on drakes, check positioning
|
||||
if (drakeInCombat && bot->GetExactDist2d(SARTHARION_OFFTANK_POSITION.first, SARTHARION_OFFTANK_POSITION.second) > looseDistance)
|
||||
{
|
||||
return MoveTo(OS_MAP_ID, SARTHARION_OFFTANK_POSITION.first, SARTHARION_OFFTANK_POSITION.second, currentPos.GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AvoidTwilightFissureAction::Execute(Event event)
|
||||
{
|
||||
const float radius = 5.0f;
|
||||
|
||||
GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
|
||||
for (auto& npc : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(npc);
|
||||
if (unit && unit->GetEntry() == NPC_TWILIGHT_FISSURE)
|
||||
{
|
||||
float currentDistance = bot->GetDistance2d(unit);
|
||||
if (currentDistance < radius)
|
||||
{
|
||||
return MoveAway(unit, radius - currentDistance);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AvoidFlameTsunamiAction::Execute(Event event)
|
||||
{
|
||||
// Adjustable, this is the acceptable distance to stack point that will be accepted as "safe"
|
||||
float looseDistance = 4.0f;
|
||||
|
||||
GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
|
||||
for (auto& npc : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(npc);
|
||||
if (unit && unit->GetEntry() == NPC_FLAME_TSUNAMI)
|
||||
{
|
||||
Position currentPos = bot->GetPosition();
|
||||
|
||||
// I think these are centrepoints for the wave segments. Either way they uniquely identify the wave
|
||||
// direction as they have different coords for the left and right waves
|
||||
// int casting is not a mistake, need to avoid FP errors somehow.
|
||||
// I always saw these accurate to around 6 decimal places, but if there are issues,
|
||||
// can switch this to abs comparison of floats which would technically be more robust.
|
||||
int posY = (int) unit->GetPositionY();
|
||||
if (posY == 505 || posY == 555) // RIGHT WAVE
|
||||
{
|
||||
bool wavePassed = currentPos.GetPositionX() > unit->GetPositionX();
|
||||
if (wavePassed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bot->GetExactDist2d(currentPos.GetPositionX(), TSUNAMI_RIGHT_SAFE_ALL) > looseDistance)
|
||||
{
|
||||
return MoveTo(OS_MAP_ID, currentPos.GetPositionX(), TSUNAMI_RIGHT_SAFE_ALL, currentPos.GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
}
|
||||
else // LEFT WAVE
|
||||
{
|
||||
bool wavePassed = currentPos.GetPositionX() < unit->GetPositionX();
|
||||
if (wavePassed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (botAI->IsMelee(bot))
|
||||
{
|
||||
if (bot->GetExactDist2d(currentPos.GetPositionX(), TSUNAMI_LEFT_SAFE_MELEE) > looseDistance)
|
||||
{
|
||||
return MoveTo(OS_MAP_ID, currentPos.GetPositionX(), TSUNAMI_LEFT_SAFE_MELEE, currentPos.GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
}
|
||||
else // Ranged/healers
|
||||
{
|
||||
if (bot->GetExactDist2d(currentPos.GetPositionX(), TSUNAMI_LEFT_SAFE_RANGED) > looseDistance)
|
||||
{
|
||||
return MoveTo(OS_MAP_ID, currentPos.GetPositionX(), TSUNAMI_LEFT_SAFE_RANGED, currentPos.GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SartharionAttackPriorityAction::Execute(Event event)
|
||||
{
|
||||
Unit* sartharion = AI_VALUE2(Unit*, "find target", "sartharion");
|
||||
Unit* shadron = AI_VALUE2(Unit*, "find target", "shadron");
|
||||
Unit* tenebron = AI_VALUE2(Unit*, "find target", "tenebron");
|
||||
Unit* vesperon = AI_VALUE2(Unit*, "find target", "vesperon");
|
||||
Unit* acolyte = AI_VALUE2(Unit*, "find target", "acolyte of shadron");
|
||||
|
||||
Unit* target = nullptr;
|
||||
|
||||
if (acolyte)
|
||||
{
|
||||
target = acolyte;
|
||||
}
|
||||
else if (vesperon)
|
||||
{
|
||||
target = vesperon;
|
||||
}
|
||||
else if (tenebron)
|
||||
{
|
||||
target = tenebron;
|
||||
}
|
||||
else if (shadron)
|
||||
{
|
||||
target = shadron;
|
||||
}
|
||||
else if (sartharion)
|
||||
{
|
||||
target = sartharion;
|
||||
}
|
||||
|
||||
if (target && AI_VALUE(Unit*, "current target") != target)
|
||||
{
|
||||
return Attack(target);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EnterTwilightPortalAction::Execute(Event event)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion");
|
||||
if (!boss || !boss->HasAura(SPELL_GIFT_OF_TWILIGHT_FIRE)) { return false; }
|
||||
|
||||
GameObject* portal = bot->FindNearestGameObject(GO_TWILIGHT_PORTAL, 100.0f);
|
||||
if (!portal) { return false; }
|
||||
|
||||
if (!portal->IsAtInteractDistance(bot))
|
||||
{
|
||||
return MoveTo(portal, fmaxf(portal->GetInteractionDistance() - 1.0f, 0.0f));
|
||||
}
|
||||
|
||||
// Go through portal
|
||||
WorldPacket data1(CMSG_GAMEOBJ_USE);
|
||||
data1 << portal->GetGUID();
|
||||
bot->GetSession()->HandleGameObjectUseOpcode(data1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExitTwilightPortalAction::Execute(Event event)
|
||||
{
|
||||
GameObject* portal = bot->FindNearestGameObject(GO_NORMAL_PORTAL, 100.0f);
|
||||
if (!portal) { return false; }
|
||||
|
||||
if (!portal->IsAtInteractDistance(bot))
|
||||
{
|
||||
return MoveTo(portal, fmaxf(portal->GetInteractionDistance() - 1.0f, 0.0f));
|
||||
}
|
||||
|
||||
// Go through portal
|
||||
WorldPacket data1(CMSG_GAMEOBJ_USE);
|
||||
data1 << portal->GetGUID();
|
||||
bot->GetSession()->HandleGameObjectUseOpcode(data1);
|
||||
|
||||
return true;
|
||||
}
|
||||
64
src/strategy/raids/obsidiansanctum/RaidOsActions.h
Normal file
64
src/strategy/raids/obsidiansanctum/RaidOsActions.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#ifndef _PLAYERBOT_RAIDOSACTIONS_H
|
||||
#define _PLAYERBOT_RAIDOSACTIONS_H
|
||||
|
||||
#include "MovementActions.h"
|
||||
#include "AttackAction.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
const float TSUNAMI_LEFT_SAFE_MELEE = 552.0f;
|
||||
const float TSUNAMI_LEFT_SAFE_RANGED = 504.0f;
|
||||
const float TSUNAMI_RIGHT_SAFE_ALL = 529.0f;
|
||||
const std::pair<float, float> SARTHARION_MAINTANK_POSITION = {3258.5f, 532.5f};
|
||||
const std::pair<float, float> SARTHARION_OFFTANK_POSITION = {3230.0f, 526.0f};
|
||||
const std::pair<float, float> SARTHARION_RANGED_POSITION = {3248.0f, 507.0f};
|
||||
|
||||
class SartharionTankPositionAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
SartharionTankPositionAction(PlayerbotAI* botAI, std::string const name = "sartharion tank position")
|
||||
: AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class AvoidTwilightFissureAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
AvoidTwilightFissureAction(PlayerbotAI* botAI, std::string const name = "avoid twilight fissure")
|
||||
: MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class AvoidFlameTsunamiAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
AvoidFlameTsunamiAction(PlayerbotAI* botAI, std::string const name = "avoid flame tsunami")
|
||||
: MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class SartharionAttackPriorityAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
SartharionAttackPriorityAction(PlayerbotAI* botAI, std::string const name = "sartharion attack priority")
|
||||
: AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class EnterTwilightPortalAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
EnterTwilightPortalAction(PlayerbotAI* botAI, std::string const name = "enter twilight portal")
|
||||
: MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class ExitTwilightPortalAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
ExitTwilightPortalAction(PlayerbotAI* botAI, std::string const name = "exit twilight portal")
|
||||
: MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
49
src/strategy/raids/obsidiansanctum/RaidOsMultipliers.cpp
Normal file
49
src/strategy/raids/obsidiansanctum/RaidOsMultipliers.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#include "RaidOsMultipliers.h"
|
||||
|
||||
#include "ChooseTargetActions.h"
|
||||
#include "DKActions.h"
|
||||
#include "DruidActions.h"
|
||||
#include "DruidBearActions.h"
|
||||
#include "FollowActions.h"
|
||||
#include "GenericActions.h"
|
||||
#include "GenericSpellActions.h"
|
||||
#include "MovementActions.h"
|
||||
#include "PaladinActions.h"
|
||||
#include "RaidOsActions.h"
|
||||
#include "RaidOsTriggers.h"
|
||||
#include "ReachTargetActions.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "WarriorActions.h"
|
||||
|
||||
float SartharionMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion");
|
||||
if (!boss) { return 1.0f; }
|
||||
|
||||
Unit* target = action->GetTarget();
|
||||
|
||||
if (botAI->IsMainTank(bot) && dynamic_cast<TankFaceAction*>(action))
|
||||
{
|
||||
// return 0.0f;
|
||||
}
|
||||
|
||||
if (botAI->IsDps(bot) && dynamic_cast<DpsAssistAction*>(action))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (botAI->IsMainTank(bot) && target && target != boss &&
|
||||
(dynamic_cast<TankAssistAction*>(action) || dynamic_cast<CastTauntAction*>(action) || dynamic_cast<CastDarkCommandAction*>(action) ||
|
||||
dynamic_cast<CastHandOfReckoningAction*>(action) || dynamic_cast<CastGrowlAction*>(action)))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (botAI->IsAssistTank(bot) && target && target == boss &&
|
||||
(dynamic_cast<CastTauntAction*>(action) || dynamic_cast<CastDarkCommandAction*>(action) ||
|
||||
dynamic_cast<CastHandOfReckoningAction*>(action) || dynamic_cast<CastGrowlAction*>(action)))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
16
src/strategy/raids/obsidiansanctum/RaidOsMultipliers.h
Normal file
16
src/strategy/raids/obsidiansanctum/RaidOsMultipliers.h
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
#ifndef _PLAYERRBOT_RAIDOSMULTIPLIERS_H
|
||||
#define _PLAYERRBOT_RAIDOSMULTIPLIERS_H
|
||||
|
||||
#include "Multiplier.h"
|
||||
|
||||
class SartharionMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
SartharionMultiplier(PlayerbotAI* ai) : Multiplier(ai, "sartharion") {}
|
||||
|
||||
public:
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
#endif
|
||||
32
src/strategy/raids/obsidiansanctum/RaidOsStrategy.cpp
Normal file
32
src/strategy/raids/obsidiansanctum/RaidOsStrategy.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#include "RaidOsStrategy.h"
|
||||
#include "RaidOsMultipliers.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
void RaidOsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(
|
||||
new TriggerNode("sartharion tank",
|
||||
NextAction::array(0, new NextAction("sartharion tank position", ACTION_MOVE), nullptr)));
|
||||
triggers.push_back(
|
||||
new TriggerNode("twilight fissure",
|
||||
NextAction::array(0, new NextAction("avoid twilight fissure", ACTION_RAID + 2), nullptr)));
|
||||
triggers.push_back(
|
||||
new TriggerNode("flame tsunami",
|
||||
NextAction::array(0, new NextAction("avoid flame tsunami", ACTION_RAID + 1), nullptr)));
|
||||
triggers.push_back(
|
||||
new TriggerNode("sartharion dps",
|
||||
NextAction::array(0, new NextAction("sartharion attack priority", ACTION_RAID), nullptr)));
|
||||
// Flank dragon positioning
|
||||
triggers.push_back(new TriggerNode("sartharion melee positioning",
|
||||
NextAction::array(0, new NextAction("rear flank", ACTION_MOVE + 4), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode("twilight portal enter",
|
||||
NextAction::array(0, new NextAction("enter twilight portal", ACTION_RAID + 1), nullptr)));
|
||||
triggers.push_back(new TriggerNode("twilight portal exit",
|
||||
NextAction::array(0, new NextAction("exit twilight portal", ACTION_RAID + 1), nullptr)));
|
||||
}
|
||||
|
||||
void RaidOsStrategy::InitMultipliers(std::vector<Multiplier*> &multipliers)
|
||||
{
|
||||
multipliers.push_back(new SartharionMultiplier(botAI));
|
||||
}
|
||||
17
src/strategy/raids/obsidiansanctum/RaidOsStrategy.h
Normal file
17
src/strategy/raids/obsidiansanctum/RaidOsStrategy.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef _PLAYERBOT_RAIDOSSTRATEGY_H
|
||||
#define _PLAYERBOT_RAIDOSSTRATEGY_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "Multiplier.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
class RaidOsStrategy : public Strategy
|
||||
{
|
||||
public:
|
||||
RaidOsStrategy(PlayerbotAI* ai) : Strategy(ai) {}
|
||||
virtual std::string const getName() override { return "wotlk-os"; }
|
||||
virtual void InitTriggers(std::vector<TriggerNode*> &triggers) override;
|
||||
virtual void InitMultipliers(std::vector<Multiplier*> &multipliers) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
32
src/strategy/raids/obsidiansanctum/RaidOsTriggerContext.h
Normal file
32
src/strategy/raids/obsidiansanctum/RaidOsTriggerContext.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef _PLAYERBOT_RAIDOSTRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDOSTRIGGERCONTEXT_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidOsTriggers.h"
|
||||
|
||||
class RaidOsTriggerContext : public NamedObjectContext<Trigger>
|
||||
{
|
||||
public:
|
||||
RaidOsTriggerContext()
|
||||
{
|
||||
creators["sartharion tank"] = &RaidOsTriggerContext::sartharion_tank;
|
||||
creators["flame tsunami"] = &RaidOsTriggerContext::flame_tsunami;
|
||||
creators["twilight fissure"] = &RaidOsTriggerContext::twilight_fissure;
|
||||
creators["sartharion dps"] = &RaidOsTriggerContext::sartharion_dps;
|
||||
creators["sartharion melee positioning"] = &RaidOsTriggerContext::sartharion_melee;
|
||||
creators["twilight portal enter"] = &RaidOsTriggerContext::twilight_portal_enter;
|
||||
creators["twilight portal exit"] = &RaidOsTriggerContext::twilight_portal_exit;
|
||||
}
|
||||
|
||||
private:
|
||||
static Trigger* sartharion_tank(PlayerbotAI* ai) { return new SartharionTankTrigger(ai); }
|
||||
static Trigger* flame_tsunami(PlayerbotAI* ai) { return new FlameTsunamiTrigger(ai); }
|
||||
static Trigger* twilight_fissure(PlayerbotAI* ai) { return new TwilightFissureTrigger(ai); }
|
||||
static Trigger* sartharion_dps(PlayerbotAI* ai) { return new SartharionDpsTrigger(ai); }
|
||||
static Trigger* sartharion_melee(PlayerbotAI* ai) { return new SartharionMeleePositioningTrigger(ai); }
|
||||
static Trigger* twilight_portal_enter(PlayerbotAI* ai) { return new TwilightPortalEnterTrigger(ai); }
|
||||
static Trigger* twilight_portal_exit(PlayerbotAI* ai) { return new TwilightPortalExitTrigger(ai); }
|
||||
};
|
||||
|
||||
#endif
|
||||
128
src/strategy/raids/obsidiansanctum/RaidOsTriggers.cpp
Normal file
128
src/strategy/raids/obsidiansanctum/RaidOsTriggers.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
#include "RaidOsTriggers.h"
|
||||
|
||||
#include "SharedDefines.h"
|
||||
|
||||
bool SartharionTankTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion");
|
||||
if (!boss) { return false; }
|
||||
|
||||
return botAI->IsTank(bot);
|
||||
}
|
||||
|
||||
bool FlameTsunamiTrigger::IsActive()
|
||||
{
|
||||
if (botAI->IsTank(bot)) { return false; }
|
||||
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion");
|
||||
if (!boss) { return false; }
|
||||
|
||||
|
||||
GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
|
||||
for (auto& npc : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(npc);
|
||||
if (unit)
|
||||
{
|
||||
if (unit->GetEntry() == NPC_FLAME_TSUNAMI)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TwilightFissureTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion");
|
||||
if (!boss) { return false; }
|
||||
|
||||
|
||||
GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
|
||||
for (auto& npc : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(npc);
|
||||
if (unit)
|
||||
{
|
||||
if (unit->GetEntry() == NPC_TWILIGHT_FISSURE)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SartharionDpsTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion");
|
||||
if (!boss) { return false; }
|
||||
|
||||
return botAI->IsDps(bot);
|
||||
}
|
||||
|
||||
bool SartharionMeleePositioningTrigger::IsActive()
|
||||
{
|
||||
if (!botAI->IsMelee(bot) || !botAI->IsDps(bot)) { return false; }
|
||||
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion");
|
||||
if (!boss) { return false; }
|
||||
|
||||
Unit* shadron = AI_VALUE2(Unit*, "find target", "shadron");
|
||||
Unit* tenebron = AI_VALUE2(Unit*, "find target", "tenebron");
|
||||
Unit* vesperon = AI_VALUE2(Unit*, "find target", "vesperon");
|
||||
|
||||
return !(shadron || tenebron || vesperon);
|
||||
}
|
||||
|
||||
bool TwilightPortalEnterTrigger::IsActive()
|
||||
{
|
||||
if (botAI->IsMainTank(bot) || botAI->IsHealAssistantOfIndex(bot, 0)) { return false; }
|
||||
|
||||
// In 25-man, take two healers in. Otherwise just take one
|
||||
// if (bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL)
|
||||
// {
|
||||
// if (botAI->IsHealAssistantOfIndex(bot, 0) || botAI->IsHealAssistantOfIndex(bot, 1))
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// if (botAI->IsHealAssistantOfIndex(bot, 0))
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
// Don't enter portal until drakes are dead
|
||||
if (bot->HasAura(SPELL_POWER_OF_SHADRON) ||
|
||||
bot->HasAura(SPELL_POWER_OF_TENEBRON) ||
|
||||
bot->HasAura(SPELL_POWER_OF_VESPERON))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion");
|
||||
if (!boss) { return false; }
|
||||
|
||||
// GuidVector objects = AI_VALUE(GuidVector, "nearest game objects no los");
|
||||
// for (auto& object : objects)
|
||||
// {
|
||||
// GameObject* go = botAI->GetGameObject(object);
|
||||
// if (go && go->GetEntry() == GO_TWILIGHT_PORTAL)
|
||||
// {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
return bool(bot->FindNearestGameObject(GO_TWILIGHT_PORTAL, 100.0f));
|
||||
}
|
||||
|
||||
bool TwilightPortalExitTrigger::IsActive()
|
||||
{
|
||||
return bot->HasAura(SPELL_TWILIGHT_SHIFT) && !AI_VALUE2(Unit*, "find target", "acolyte of shadron");
|
||||
}
|
||||
120
src/strategy/raids/obsidiansanctum/RaidOsTriggers.h
Normal file
120
src/strategy/raids/obsidiansanctum/RaidOsTriggers.h
Normal file
@@ -0,0 +1,120 @@
|
||||
#ifndef _PLAYERBOT_RAIDOSTRIGGERS_H
|
||||
#define _PLAYERBOT_RAIDOSTRIGGERS_H
|
||||
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
#include "Trigger.h"
|
||||
|
||||
enum ObsidianSanctumIDs
|
||||
{
|
||||
// Bosses
|
||||
NPC_SARTHARION = 28860,
|
||||
NPC_SHADRON = 30451,
|
||||
NPC_TENEBRON = 30452,
|
||||
NPC_VESPERON = 30449,
|
||||
|
||||
// Mini-boss shared
|
||||
SPELL_SHADOW_BREATH = 57570,
|
||||
SPELL_SHADOW_FISSURE = 57579,
|
||||
SPELL_SUMMON_TWILIGHT_WHELP = 58035,
|
||||
SPELL_GIFT_OF_TWILIGHT_SHADOW = 57835,
|
||||
SPELL_TWILIGHT_TORMENT_VESPERON = 57935,
|
||||
|
||||
// Sartharion
|
||||
SPELL_SARTHARION_CLEAVE = 56909,
|
||||
SPELL_SARTHARION_FLAME_BREATH = 56908,
|
||||
SPELL_SARTHARION_TAIL_LASH = 56910,
|
||||
SPELL_CYCLONE_AURA_PERIODIC = 57598,
|
||||
SPELL_LAVA_STRIKE_DUMMY = 57578,
|
||||
SPELL_LAVA_STRIKE_DUMMY_TRIGGER = 57697,
|
||||
SPELL_LAVA_STRIKE_SUMMON = 57572,
|
||||
SPELL_SARTHARION_PYROBUFFET = 56916,
|
||||
SPELL_SARTHARION_BERSERK = 61632,
|
||||
SPELL_SARTHARION_TWILIGHT_REVENGE = 60639,
|
||||
|
||||
// Sartharion with drakes
|
||||
SPELL_WILL_OF_SARTHARION = 61254,
|
||||
SPELL_POWER_OF_TENEBRON = 61248,
|
||||
SPELL_POWER_OF_VESPERON = 61251,
|
||||
SPELL_POWER_OF_SHADRON = 58105,
|
||||
SPELL_GIFT_OF_TWILIGHT_FIRE = 58766,
|
||||
|
||||
// Visuals
|
||||
SPELL_EGG_MARKER_VISUAL = 58547,
|
||||
SPELL_FLAME_TSUNAMI_VISUAL = 57494,
|
||||
|
||||
// Misc
|
||||
SPELL_FADE_ARMOR = 60708,
|
||||
SPELL_FLAME_TSUNAMI_DAMAGE_AURA = 57492,
|
||||
SPELL_FLAME_TSUNAMI_LEAP = 60241,
|
||||
SPELL_SARTHARION_PYROBUFFET_TRIGGER = 57557,
|
||||
|
||||
NPC_TWILIGHT_EGG = 30882,
|
||||
NPC_TWILIGHT_WHELP = 30890,
|
||||
NPC_DISCIPLE_OF_SHADRON = 30688,
|
||||
NPC_DISCIPLE_OF_VESPERON = 30858,
|
||||
NPC_ACOLYTE_OF_SHADRON = 31218,
|
||||
NPC_ACOLYTE_OF_VESPERON = 31219,
|
||||
|
||||
// Sartharion fight
|
||||
NPC_LAVA_BLAZE = 30643,
|
||||
NPC_FLAME_TSUNAMI = 30616,
|
||||
NPC_SAFE_AREA_TRIGGER = 30494,
|
||||
NPC_TWILIGHT_FISSURE = 30641,
|
||||
GO_TWILIGHT_PORTAL = 193988,
|
||||
GO_NORMAL_PORTAL = 193989,
|
||||
SPELL_TWILIGHT_SHIFT = 57874,
|
||||
};
|
||||
|
||||
const uint32 OS_MAP_ID = 615;
|
||||
|
||||
class SartharionTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
SartharionTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "sartharion tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class FlameTsunamiTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
FlameTsunamiTrigger(PlayerbotAI* botAI) : Trigger(botAI, "flame tsunami") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class TwilightFissureTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
TwilightFissureTrigger(PlayerbotAI* botAI) : Trigger(botAI, "twilight fissure") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class SartharionDpsTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
SartharionDpsTrigger(PlayerbotAI* botAI) : Trigger(botAI, "sartharion dps") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class SartharionMeleePositioningTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
SartharionMeleePositioningTrigger(PlayerbotAI* botAI) : Trigger(botAI, "sartharion melee positioning") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class TwilightPortalEnterTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
TwilightPortalEnterTrigger(PlayerbotAI* botAI) : Trigger(botAI, "twilight portal enter") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class TwilightPortalExitTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
TwilightPortalExitTrigger(PlayerbotAI* botAI) : Trigger(botAI, "twilight portal exit") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -25,4 +25,8 @@ bool PartyMemberNeedCureTrigger::IsActive()
|
||||
return target && target->IsInWorld();
|
||||
}
|
||||
|
||||
bool NeedWorldBuffTrigger::IsActive() { return !WorldBuffAction::NeedWorldBuffs(bot).empty(); }
|
||||
bool NeedWorldBuffTrigger::IsActive()
|
||||
{
|
||||
std::any_of(WorldBuffAction::NeedWorldBuffs(bot).begin(), WorldBuffAction::NeedWorldBuffs(bot).end(),
|
||||
[](const auto& wb) { return true; });
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ bool AttackersValue::IsPossibleTarget(Unit* attacker, Player* bot, float range)
|
||||
// !((attacker->IsPolymorphed() || botAI->HasAura("sap", attacker) || /*attacker->IsCharmed() ||*/
|
||||
// attacker->isFeared()) && !rti) &&
|
||||
/*!sServerFacade->IsInRoots(attacker) &&*/
|
||||
!attacker->IsFriendlyTo(bot) && !attacker->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION) &&
|
||||
!attacker->IsFriendlyTo(bot) && !attacker->HasSpiritOfRedemptionAura() &&
|
||||
// !(attacker->GetGUID().IsPet() && enemy) &&
|
||||
!(attacker->GetCreatureType() == CREATURE_TYPE_CRITTER && !attacker->IsInCombat()) &&
|
||||
!attacker->HasUnitFlag(UNIT_FLAG_IMMUNE_TO_PC) && !attacker->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE) &&
|
||||
|
||||
@@ -18,7 +18,7 @@ bool NearestEnemyPlayersValue::AcceptUnit(Unit* unit)
|
||||
!enemy->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NON_ATTACKABLE_2) &&
|
||||
((inCannon || !enemy->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE))) &&
|
||||
/*!enemy->HasStealthAura() && !enemy->HasInvisibilityAura()*/ enemy->CanSeeOrDetect(bot) &&
|
||||
!(enemy->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION)))
|
||||
!(enemy->HasSpiritOfRedemptionAura()))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
||||
@@ -21,7 +21,7 @@ bool InvalidTargetValue::Calculate()
|
||||
return target->GetMapId() != bot->GetMapId() || target->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE) ||
|
||||
target->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE) || target->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE_2) ||
|
||||
!target->IsVisible() || !target->IsAlive() || target->IsPolymorphed() || target->IsCharmed() ||
|
||||
target->isFeared() || target->HasUnitState(UNIT_STATE_ISOLATED) || target->IsFriendlyTo(bot) ||
|
||||
target->HasFearAura() || target->HasUnitState(UNIT_STATE_ISOLATED) || target->IsFriendlyTo(bot) ||
|
||||
!AttackersValue::IsValidTarget(target, bot);
|
||||
}
|
||||
|
||||
|
||||
@@ -48,9 +48,6 @@ Unit* RtiTargetValue::Calculate()
|
||||
if (!guid)
|
||||
return nullptr;
|
||||
|
||||
if (!bot->IsInCombat())
|
||||
return nullptr;
|
||||
|
||||
//////////////////////////////////////////////////////begin: delete below check
|
||||
// Some units that need to be killed in battle are not on the list of attackers,
|
||||
// such as the Kor'kron Battle-Mage in Icecrown Citadel.
|
||||
|
||||
@@ -23,3 +23,5 @@ bool CastFearOnCcAction::isPossible() { return botAI->CanCastSpell("fear", GetTa
|
||||
bool CastFearOnCcAction::isUseful() { return true; }
|
||||
|
||||
bool CastLifeTapAction::isUseful() { return AI_VALUE2(uint8, "health", "self target") > sPlayerbotAIConfig->lowHealth; }
|
||||
|
||||
Unit* UseSoulstoneAction::GetTarget() { return botAI->GetMaster(); }
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#define _PLAYERBOT_WARLOCKACTIONS_H
|
||||
|
||||
#include "GenericSpellActions.h"
|
||||
#include "UseItemAction.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
class Unit;
|
||||
@@ -302,4 +303,12 @@ class CastIncinerateAction : public CastSpellAction
|
||||
public:
|
||||
CastIncinerateAction(PlayerbotAI* ai) : CastSpellAction(ai, "incinerate") {}
|
||||
};
|
||||
|
||||
class UseSoulstoneAction : public UseSpellItemAction
|
||||
{
|
||||
public:
|
||||
UseSoulstoneAction(PlayerbotAI* ai) : UseSpellItemAction(ai, "soulstone") {}
|
||||
|
||||
Unit* GetTarget() override;
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -184,6 +184,7 @@ public:
|
||||
creators["metamorphosis"] = &WarlockAiObjectContextInternal::metamorphosis;
|
||||
creators["soul fire"] = &WarlockAiObjectContextInternal::soul_fire;
|
||||
creators["incinerate"] = &WarlockAiObjectContextInternal::incinerate;
|
||||
creators["soulstone"] = &WarlockAiObjectContextInternal::soulstone;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -239,6 +240,7 @@ private:
|
||||
static Action* metamorphosis(PlayerbotAI* ai) { return new CastMetamorphosisAction(ai); }
|
||||
static Action* soul_fire(PlayerbotAI* ai) { return new CastSoulFireAction(ai); }
|
||||
static Action* incinerate(PlayerbotAI* ai) { return new CastIncinerateAction(ai); }
|
||||
static Action* soulstone(PlayerbotAI* ai) { return new UseSoulstoneAction(ai); }
|
||||
};
|
||||
|
||||
WarlockAiObjectContext::WarlockAiObjectContext(PlayerbotAI* botAI) : AiObjectContext(botAI)
|
||||
|
||||
Reference in New Issue
Block a user