diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index bf6b0a343a..5a545ce93a 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -1531,6 +1531,9 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster) case 532: strategyName = "karazhan"; // Karazhan break; + case 531: + strategyName = "aq40"; + break; case 533: strategyName = "naxx"; // Naxxramas break; diff --git a/src/strategy/AiObjectContext.cpp b/src/strategy/AiObjectContext.cpp index 4c49fe4eac..d2cf149cb1 100644 --- a/src/strategy/AiObjectContext.cpp +++ b/src/strategy/AiObjectContext.cpp @@ -27,6 +27,27 @@ #include "WarriorAiObjectContext.h" #include "WorldPacketActionContext.h" #include "WorldPacketTriggerContext.h" +#include "raids/RaidStrategyContext.h" +#include "raids/blackwinglair/RaidBwlActionContext.h" +#include "raids/blackwinglair/RaidBwlTriggerContext.h" +#include "raids/naxxramas/RaidNaxxActionContext.h" +#include "raids/naxxramas/RaidNaxxTriggerContext.h" +#include "raids/icecrown/RaidIccActionContext.h" +#include "raids/icecrown/RaidIccTriggerContext.h" +#include "raids/obsidiansanctum/RaidOsActionContext.h" +#include "raids/obsidiansanctum/RaidOsTriggerContext.h" +#include "raids/eyeofeternity/RaidEoEActionContext.h" +#include "raids/vaultofarchavon/RaidVoATriggerContext.h" +#include "raids/onyxia/RaidOnyxiaActionContext.h" +#include "raids/onyxia/RaidOnyxiaTriggerContext.h" +#include "raids/vaultofarchavon/RaidVoAActionContext.h" +#include "raids/eyeofeternity/RaidEoETriggerContext.h" +#include "raids/moltencore/RaidMcActionContext.h" +#include "raids/moltencore/RaidMcTriggerContext.h" +#include "raids/aq20/RaidAq20ActionContext.h" +#include "raids/aq20/RaidAq20TriggerContext.h" +#include "raids/aq40/RaidAq40ActionContext.h" +#include "raids/aq40/RaidAq40TriggerContext.h" #include "dungeons/DungeonStrategyContext.h" #include "dungeons/wotlk/WotlkDungeonActionContext.h" #include "dungeons/wotlk/WotlkDungeonTriggerContext.h" @@ -109,11 +130,13 @@ void AiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList public: RaidStrategyContext() : NamedObjectContext(false, true) { - creators["aq20"] = &RaidStrategyContext::aq20; creators["mc"] = &RaidStrategyContext::mc; creators["bwl"] = &RaidStrategyContext::bwl; creators["karazhan"] = &RaidStrategyContext::karazhan; creators["gruulslair"] = &RaidStrategyContext::gruulslair; + creators["aq20"] = &RaidStrategyContext::aq20; + creators["aq40"] = &RaidStrategyContext::aq40; creators["naxx"] = &RaidStrategyContext::naxx; creators["wotlk-os"] = &RaidStrategyContext::wotlk_os; creators["wotlk-eoe"] = &RaidStrategyContext::wotlk_eoe; @@ -35,11 +40,12 @@ class RaidStrategyContext : public NamedObjectContext } private: - static Strategy* aq20(PlayerbotAI* botAI) { return new RaidAq20Strategy(botAI); } static Strategy* mc(PlayerbotAI* botAI) { return new RaidMcStrategy(botAI); } static Strategy* bwl(PlayerbotAI* botAI) { return new RaidBwlStrategy(botAI); } static Strategy* karazhan(PlayerbotAI* botAI) { return new RaidKarazhanStrategy(botAI); } static Strategy* gruulslair(PlayerbotAI* botAI) { return new RaidGruulsLairStrategy(botAI); } + static Strategy* aq20(PlayerbotAI* botAI) { return new RaidAq20Strategy(botAI); } + static Strategy* aq40(PlayerbotAI* botAI) { return new RaidAq40Strategy(botAI); } static Strategy* naxx(PlayerbotAI* botAI) { return new RaidNaxxStrategy(botAI); } static Strategy* wotlk_os(PlayerbotAI* botAI) { return new RaidOsStrategy(botAI); } static Strategy* wotlk_eoe(PlayerbotAI* botAI) { return new RaidEoEStrategy(botAI); } diff --git a/src/strategy/raids/aq40/RaidAq40ActionContext.h b/src/strategy/raids/aq40/RaidAq40ActionContext.h new file mode 100644 index 0000000000..f235820aa8 --- /dev/null +++ b/src/strategy/raids/aq40/RaidAq40ActionContext.h @@ -0,0 +1,44 @@ +#ifndef _PLAYERBOT_RAIDAQ40ACTIONCONTEXT_H +#define _PLAYERBOT_RAIDAQ40ACTIONCONTEXT_H + +#include "Action.h" +#include "NamedObjectContext.h" +#include "RaidAq40Actions.h" + +class RaidAq40ActionContext : public NamedObjectContext +{ +public: + RaidAq40ActionContext() + { + creators["aq40 use resistance buffs"] = &RaidAq40ActionContext::use_resistance_buffs; + creators["aq40 move from other emperor"] = &RaidAq40ActionContext::move_from_other_emperor; + creators["aq40 attack emperor vek'lor"] = &RaidAq40ActionContext::attack_emperor_veklor; + creators["aq40 attack emperor vek'nilash"] = &RaidAq40ActionContext::attack_emperor_veknilash; + creators["aq40 attack emperor pests"] = &RaidAq40ActionContext::attack_emperor_pests; + creators["aq40 move towards emperor vek'lor"] = &RaidAq40ActionContext::move_towards_emperor_veklor; + creators["aq40 move towards emperor vek'nilash"] = &RaidAq40ActionContext::move_towards_emperor_veknilash; + creators["searing pain"] = &RaidAq40ActionContext::warlock_searing_pain; + creators["frostbolt"] = &RaidAq40ActionContext::mage_cast_frostbolt; + creators["aq40 melee viscidus"] = &RaidAq40ActionContext::melee_viscidus; + creators["aq40 ouro burrowed flee"] = &RaidAq40ActionContext::ouro_burrowed_flee; + creators["aq40 cthun1 get positioned"] = &RaidAq40ActionContext::cthun1_get_positioned; + creators["aq40 cthun2 get positioned"] = &RaidAq40ActionContext::cthun2_get_positioned; + } + +private: + static Action* use_resistance_buffs(PlayerbotAI *ai) { return new Aq40UseResistanceBuffsAction(ai); } + static Action* move_from_other_emperor(PlayerbotAI* ai) { return new Aq40MoveFromOtherEmperorAction(ai); } + static Action* attack_emperor_veklor(PlayerbotAI* ai) { return new Aq40AttackEmperorVekLorAction(ai); } + static Action* attack_emperor_veknilash(PlayerbotAI* ai) { return new Aq40AttackEmperorVekNilashAction(ai); } + static Action* attack_emperor_pests(PlayerbotAI* ai) { return new Aq40AttackEmperorPestsAction(ai); } + static Action* move_towards_emperor_veklor(PlayerbotAI* ai) { return (Action *)(new Aq40MoveTowardsEmperorVekLorAction(ai)); } + static Action* move_towards_emperor_veknilash(PlayerbotAI* ai) { return (Action *)(new Aq40MoveTowardsEmperorVekNilashAction(ai)); } + static Action* warlock_searing_pain(PlayerbotAI* ai) { return new Aq40WarlockCastSearingPainAction(ai); } + static Action* mage_cast_frostbolt(PlayerbotAI* ai) { return new Aq40MageCastFrostboltAction(ai); } + static Action* melee_viscidus(PlayerbotAI* ai) { return new Aq40MeleeViscidusAction(ai); } + static Action* ouro_burrowed_flee(PlayerbotAI* ai) { return new Aq40OuroBurrowedFleeAction(ai); } + static Action* cthun1_get_positioned(PlayerbotAI* ai) { return new Aq40Cthun1PositionAction(ai); } + static Action* cthun2_get_positioned(PlayerbotAI* ai) { return new Aq40Cthun2PositionAction(ai); } +}; + +#endif diff --git a/src/strategy/raids/aq40/RaidAq40Actions.cpp b/src/strategy/raids/aq40/RaidAq40Actions.cpp new file mode 100644 index 0000000000..bce8230a76 --- /dev/null +++ b/src/strategy/raids/aq40/RaidAq40Actions.cpp @@ -0,0 +1,696 @@ +#include "RaidAq40Actions.h" + +#include "Playerbots.h" +#include "AttackAction.h" + +bool Aq40UseResistanceBuffsAction::Execute(Event event) +{ + switch(bot->getClass()) + { + case CLASS_HUNTER: + { + bool isNatureBoss = false; + Unit* boss; + + if ((boss = AI_VALUE2(Unit*, "find target", "viscidus")) && boss->IsInCombat()) + isNatureBoss = true; + + else if ((boss = AI_VALUE2(Unit*, "find target", "princess huhuran")) && boss->IsInCombat()) + isNatureBoss = true; + + if (isNatureBoss) + { + if (!botAI->HasStrategy("rnature", BotState::BOT_STATE_COMBAT)) + { + botAI->ChangeStrategy("+rnature", BOT_STATE_NON_COMBAT); + botAI->ChangeStrategy("+rnature", BOT_STATE_COMBAT); + return true; + } + } + else if (botAI->HasStrategy("rnature", BotState::BOT_STATE_COMBAT)) + { + botAI->ChangeStrategy("-rnature", BOT_STATE_NON_COMBAT); + botAI->ChangeStrategy("-rnature", BOT_STATE_COMBAT); + return true; + } + } + break; + case CLASS_PRIEST: + { + // paladin aura seems like a waste when priests have buffs that don't have a radius limitation + if (!botAI->HasStrategy("rshadow", BotState::BOT_STATE_NON_COMBAT)) + { + botAI->ChangeStrategy("+rshadow", BOT_STATE_NON_COMBAT); + botAI->ChangeStrategy("+rshadow", BOT_STATE_COMBAT); + return true; + } + } + break; + case default; + break; + } + + return false; +} + + + +// 88072: The Master's Eye for positioning maybe + +bool Aq40MoveFromOtherEmperorAction::Execute(Event event) +{ + const float radius = 120.0f; // emperors' heal range is 60 + + if (Unit* boss1 = AI_VALUE2(Unit*, "find target", "emperor vek'lor")) + if (Unit* boss2 = AI_VALUE2(Unit*, "find target", "emperor vek'nilash")) + { + ObjectGuid botGuid = bot->GetGUID(); + ObjectGuid petGuid = (ObjectGuid)0UL; + if (Pet* pet = bot->GetPet()) + petGuid = pet->GetGUID(); + + Unit* moveAwayFrom = NULL; + + if (boss1->GetTarget() == botGuid || boss1->GetTarget() == petGuid) + { + moveAwayFrom = boss2; + if (petGuid) + botAI->PetFollow(); + } + else if (boss2->GetTarget() == botGuid || boss2->GetTarget() == petGuid) + { + moveAwayFrom = boss1; + if (petGuid) + botAI->PetFollow(); + } + + if (moveAwayFrom != NULL) + { + long distToTravel = radius - bot->GetDistance(moveAwayFrom); + + if (distToTravel > 0) + return MoveAway(moveAwayFrom, distToTravel); + } + } + + return false; +} + +bool Aq40MeleeViscidusAction::Execute(Event event) +{ + if (Unit* boss = AI_VALUE2(Unit*, "find target", "viscidus")) + { + if (!bot->HasUnitState(UNIT_STATE_MELEE_ATTACKING)) + { + // this might not work properly, didn't have problems with boss anyway + ObjectGuid guid = boss->GetGUID(); + + botAI->GetAiObjectContext()->GetValue("prioritized targets")->Set({guid}); + bool result = Attack(boss); + if (result) + { + bot->AddUnitState(UNIT_STATE_MELEE_ATTACKING); + context->GetValue("pull target")->Set(guid); + } + + return result; + } + } + + return false; +} + +bool Aq40AttackTargetByNameAction::Execute(Event event) +{ + if (Unit* boss = AI_VALUE2(Unit*, "find target", WhichEmperor())) + { + if (bot->GetTarget() != boss->GetGUID()) + { + ObjectGuid guid = boss->GetGUID(); + + botAI->GetAiObjectContext()->GetValue("prioritized targets")->Set({guid}); + bool result = Attack(boss); + if (result) + context->GetValue("pull target")->Set(guid); + + return result; + } + } + return false; +} + +bool Aq40AttackEmperorPestsAction::Execute(Event event) +{ + Unit* current = AI_VALUE(Unit*, "current target"); + + if (current && (current->GetName() == "qiraji scarab" || current->GetName() == "qiraji scorpion")) + return false; + + + Unit* pest1 = AI_VALUE2(Unit*, "find target", "qiraji scarab"); + Unit* pest2 = AI_VALUE2(Unit*, "find target", "qiraji scorpion"); + Unit* pest; + + if (pest1 && pest2) + { + if (pest1->GetDistance(bot) < pest2->GetDistance(bot)) + pest = pest1; + + else + pest = pest2; + } + else if (pest1) + pest = pest1; + + else if (pest2) + pest = pest2; + + else + return false; + + + ObjectGuid guid = pest->GetGUID(); + botAI->GetAiObjectContext()->GetValue("prioritized targets")->Set({guid}); + bool result = Attack(pest); + if (result) + context->GetValue("pull target")->Set(guid); + + return result; +} + +bool Aq40MoveTowardsEmperorAction::Execute(Event event) +{ + const float radius = 20.0f; // assume general healing range of 40, try to keep healers near the tanks/victims + + if (Unit* boss = AI_VALUE2(Unit*, "find target", WhichEmperor())) + { + if (ObjectGuid bossTarget = boss->GetTarget()) + { + Unit* target = botAI->GetUnit(bossTarget); + + long travelTarger = -1;//radius - bot->GetDistance(target); + long travelBoss = radius - bot->GetDistance(boss); + + if (travelTarger > travelBoss) + return MoveTo(target, travelTarger); + + else + return MoveTo(boss, travelBoss); + } + } + + return false; +} + +bool Aq40OuroBurrowedFleeAction::Execute(Event event) +{ + bool doFlee = false; + + if (Unit* boss = AI_VALUE2(Unit*, "find target", "ouro")) + { + if (boss->IsInCombat() && (boss->GetUnitFlags() & UNIT_FLAG_NOT_SELECTABLE) == UNIT_FLAG_NOT_SELECTABLE) + { + doFlee = true; + + // todo: if ankle-biter scarab is near, doFlee = false + } + + } + /* + else if (Unit* boss = AI_VALUE2(Unit*, "find target", "eye of c'thun")) + { + if (boss->IsInCombat()) + { + float dist = bot->GetDistance(boss); + + //printf("eyedist %f\n",dist); + + doFlee = dist < 13.0; + + if (bot->GetName() == "Snusnu") + { + printf("eyedist %f doFlee %d\n",dist,doFlee); + } + } + } + */ + + + if (doFlee) + { + if (!botAI->HasStrategy("move from group", BotState::BOT_STATE_COMBAT)) + { + // add/remove from both for now as it will make it more obvious to + // player if this strat remains on after fight somehow + // (which it will in this case, todo: remove after relevant, or just issue 'follow' command to bots) + botAI->ChangeStrategy("+move from group", BOT_STATE_NON_COMBAT); + botAI->ChangeStrategy("+move from group", BOT_STATE_COMBAT); + } + } + else if (botAI->HasStrategy("move from group", BotState::BOT_STATE_COMBAT)) + { + // add/remove from both for now as it will make it more obvious to + // player if this strat remains on after fight somehow + // (which it will in this case, todo: remove after relevant, or just issue 'follow' command to bots) + botAI->ChangeStrategy("-move from group", BOT_STATE_NON_COMBAT); + botAI->ChangeStrategy("-move from group", BOT_STATE_COMBAT); + } + + return true; +} + +int Aq40Cthun1PositionAction::WrappingDistanceBetween(int first, int second, int scope) +{ + int retVal = first - second; + if (abs(retVal) > scope / 2) + { + if (retVal > scope / 2) + retVal = retVal - scope; + + else + retVal = scope + retVal; + } + return retVal; +} + +int Aq40Cthun1PositionAction::GetNearestPoint(int excludeouter, bool doinner) +{ + int retVal = -1; + float retValDist = 0.0F; + + for (int n = 0; n < outerPointsCount; n++) + { + float dist = bot->GetDistance(outerPoints[n]->GetPositionX(), outerPoints[n]->GetPositionY(), outerPoints[n]->GetPositionZ()); + + if (retVal < 0 || retValDist > dist) + { + if (excludeouter != n) + { + retVal = n; + retValDist = dist; + } + } + } + + if (doinner) + { + for (int n = 0; n < innerPointsCount; n++) + { + float dist = bot->GetDistance(innerPoints[n]->GetPositionX(), innerPoints[n]->GetPositionY(), innerPoints[n]->GetPositionZ()); + + if (retVal < 0 || retValDist > dist) + { + retVal = n + outerPointsCount; + retValDist = dist; + } + } + } + + return retVal; +} + +bool Aq40Cthun1PositionAction::Execute(Event event) +{ + // boss_cthun.cpp + const int SPELL_GREEN_BEAM = 26134; + const int SPELL_RED_COLORATION = 22518; //Probably not the right spell but looks similar + + + ObjectGuid botGuid = bot->GetGUID(); + + if (Unit* boss = AI_VALUE2(Unit*, "find target", "eye of c'thun")) + { + Position* point = NULL; + + ObjectGuid bossTargetguid = boss->GetTarget(); + if (boss->HasAura(SPELL_RED_COLORATION)) + { + int bossd = (int)(boss->GetOrientation() * outerPointsCount / (M_PI * 2.0)); + if (bossd < 0) + { + bossd = bossd + outerPointsCount * ((-bossd) / outerPointsCount + 1); + } + bossd %= outerPointsCount; + + int nearest = GetNearestPoint(bossd, false); + int dist = WrappingDistanceBetween(bossd, nearest, outerPointsCount); + + if (abs(dist) <= outerPointsCount / 6) + { + nearest = (nearest + outerPointsCount - dist * (outerPointsCount / 6) / abs(dist)) % outerPointsCount; + } + + point = outerPoints[nearest]; + + if (bot->GetDistance(*point) < 0.5) + { + return false; + } + } + else + { + if (bossTargetguid == botGuid) + { + bot->StopMoving(); + return true; + } + else + { + Spell* spell = boss->GetCurrentSpell(CURRENT_GENERIC_SPELL); + + if (spell and spell->m_spellInfo->Id == SPELL_GREEN_BEAM) + { + Unit* bossTarget = botAI->GetUnit(bossTargetguid); + float bdist = bot->GetDistance(bossTarget); + + if (bdist < 20.0) + { + if (bdist <= 10.0) + { + bot->AttackStop(); + //printf("%s: still %f away from %s\n",bot->GetName().c_str(),bdist,bossTarget->GetName().c_str()); + } + + return MoveAway(bossTarget, 20.0); + } + } + } + } + + if (!point) + { + Group* group = bot->GetGroup(); + if (bot->GetDistance(boss) < 60.0F && group) + { + Player* closest = NULL; + float closestDist = 0.0F; + + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + + if (member->GetGUID() != botGuid && member->IsAlive()) + { + float dist = bot->GetDistance(member); + + if (dist < 10.0 && (closest == NULL || closestDist < dist)) + { + closest = member; + closestDist = dist; + } + } + } + + if (closest) + { + return MoveAway(closest, 10.0); + } + } + + int nearest = GetNearestPoint(); + + if (nearest >= outerPointsCount) + { + point = innerPoints[nearest - outerPointsCount]; + } + else + { + point = outerPoints[nearest]; + } + + if (bot->GetDistance(*point) < 0.5) + { + return false; + } + } + + if (point) + { + return MoveTo(bot->GetMapId(), point->GetPositionX(), point->GetPositionY(), point->GetPositionZ(), + false, false, false, true, MovementPriority::MOVEMENT_COMBAT); + } + } + + return false; +} + +bool Aq40Cthun2PositionAction::Execute(Event event) +{ + // boss_cthun.cpp + const int NPC_TRIGGER = 15384; + const int NPC_EXIT_TRIGGER = 15800; + + const int SPELL_DIGESTIVE_ACID = 26476; + + // Areatriggers + const int SPELL_SPIT_OUT = 25383; + const int SPELL_EXIT_STOMACH = 26221; + const int SPELL_RUBBLE_ROCKY = 26271; + + const int SPELL_CARAPACE_CTHUN = 26156; // Server-side + + + // temple_of_ahn_quiraj.h + const int NPC_CLAW_TENTACLE = 15725; + const int NPC_EYE_TENTACLE = 15726; + const int NPC_GIANT_CLAW_TENTACLE = 15728; + const int NPC_GIANT_EYE_TENTACLE = 15334; + const int NPC_FLESH_TENTACLE = 15802; + + ObjectGuid botGuid = bot->GetGUID(); + + if (Unit* boss = AI_VALUE2(Unit*, "find target", "c'thun")) + { + Position* point = NULL; + + if (bot->GetPositionZ() > 0.0) + { + Creature* tentacle1 = bot->FindNearestCreature(NPC_GIANT_EYE_TENTACLE, 100.0f, true); + + if (tentacle1) + { + Group* group = bot->GetGroup(); + if (group) + { + Player* closest = NULL; + float closestDist = 0.0F; + + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + + if (member->GetGUID() != botGuid && member->IsAlive()) + { + float dist = bot->GetDistance(member); + + if (dist < 11.0 && (closest == NULL || closestDist < dist)) + { + closest = member; + closestDist = dist; + } + } + } + + if (closest) + { + return MoveAway(closest, 11.0); + } + } + } + + Unit* attackTarget = NULL; + + if (!boss->HasAura(SPELL_CARAPACE_CTHUN)) + { + attackTarget = boss; + } + else if (tentacle1) + { + attackTarget = tentacle1; + } + else if (Creature* tentacle3 = bot->FindNearestCreature(NPC_EYE_TENTACLE, 100.0f, true)) + { + attackTarget = tentacle3; + } + else if (Creature* tentacle2 = bot->FindNearestCreature(NPC_GIANT_CLAW_TENTACLE, 100.0f, true)) + { + attackTarget = tentacle2; + } + else if (Creature* tentacle4 = bot->FindNearestCreature(NPC_CLAW_TENTACLE, 100.0f, true)) + { + attackTarget = tentacle4; + } + + if (attackTarget) + { + ObjectGuid guid = attackTarget->GetGUID(); + + botAI->GetAiObjectContext()->GetValue("prioritized targets")->Set({guid}); + bot->Attack(attackTarget, botAI->IsMelee(bot)); + + return false; + } + + outTriggered = false; + attackPositioned = false; + } + else + { + if (bot->GetPositionZ() > -50.0) + { + // flying around out of bounds where they shouldn't be... + // teleport them to splashdown at -8562.1 2037.0 -99.58 + bot->TeleportTo(bot->GetMapId(), -8562.1, 2037.0, -99.58, 5.03); + } + + bool doGetOut = false; + + if (!attackPositioned) + { + point = insideattack; + + if (bot->GetDistance(*point) < 1.0) + { + attackPositioned = true; + } + } + else + { + int auraStack = bot->GetAuraCount(SPELL_DIGESTIVE_ACID); + int auraDamage = (auraStack + (auraStack + 1) + (auraStack + 2)) * 150; // 150 per stack, 5 second tick + + if (auraDamage >= bot->GetHealth()) + { + point = getOut; + doGetOut = true; + //printf("%s: low health, get out\n",bot->GetName().c_str()); + } + else if (auraStack < 1) // bugged state check + { + // immediately leave + point = getOut; + doGetOut = true; + //printf("%s: bugged state, get out\n",bot->GetName().c_str()); + } + else if (botAI->IsTank(bot, true)) + { + // immediately leave + point = getOut; + doGetOut = true; + //printf("%s: tank, get out\n",bot->GetName().c_str()); + } + else if (botAI->IsHeal(bot, true)) + { + // stack to 5 and leave, lower tolerance for healers + if (bot->GetAuraCount(SPELL_DIGESTIVE_ACID) >= 5 || bot->GetHealthPct() <= 50.0) + { + point = getOut; + doGetOut = true; + } + } + else + { + // search completely failed with AI_VALUE2 here, but the above AI_VALUE2 started + // working after the below statement was used here instead? + if (Creature* tboss = bot->FindNearestCreature(NPC_FLESH_TENTACLE, 100.0f, true)) + { + bool hasTarget = false; + + ObjectGuid targetguid = bot->GetTarget(); + if (Unit* target = botAI->GetUnit(targetguid)) + { + if (target->GetEntry() == NPC_FLESH_TENTACLE) + { + hasTarget = true; + } + } + + if (!hasTarget) + { + ObjectGuid guid = tboss->GetGUID(); + + botAI->GetAiObjectContext()->GetValue("prioritized targets")->Set({guid}); + bot->SetTarget(guid); + + //printf("%s: attack flesh tentacle\n",bot->GetName().c_str()); + + if (botAI->IsMelee(bot)) + { + // bots not picking up on these tentacles very effectively + return MoveTo(tboss, -1.0); + } + + return false; + } + else + { + //printf("%s: tentacle already target\n",bot->GetName().c_str()); + } + } + else + { + //printf("%s: no flesh tentacle, get out\n",bot->GetName().c_str()); + point = getOut; + doGetOut = true; + } + } + } + + if (doGetOut && !outTriggered) + { + if (bot->GetDistance(*point) < 10.0) + { + // at_cthun_stomach_exit::OnTrigger from boss_cthun.cpp + Player* player = bot; + Unit* cthun = boss; + + if (Creature* trigger = player->FindNearestCreature(NPC_TRIGGER, 15.0f)) + { + if (!trigger->GetCurrentSpell(CurrentSpellTypes::CURRENT_GENERIC_SPELL)) + { + trigger->CastSpell(player, SPELL_EXIT_STOMACH, true); + + if (Creature* exittrigger = player->FindNearestCreature(NPC_EXIT_TRIGGER, 15.0f)) + { + exittrigger->CastSpell(player, SPELL_RUBBLE_ROCKY, true); + } + + outTriggered = true; + } + } + + if (outTriggered) + { + player->m_Events.AddEventAtOffset([player, cthun]() + { + if (player->FindNearestCreature(NPC_EXIT_TRIGGER, 10.0f)) + { + player->JumpTo(0.0f, 80.0f, false); + + player->m_Events.AddEventAtOffset([player, cthun]() + { + if (cthun) + player->NearTeleportTo(cthun->GetPositionX(), cthun->GetPositionY(), cthun->GetPositionZ() + 10, float(rand32() % 6)); + + player->RemoveAurasDueToSpell(SPELL_DIGESTIVE_ACID); + }, 1s); + } + else + { + player->m_Events.KillAllEvents(false); + } + }, 3s); + } + } + } + } + + if (point) + { + return MoveTo(bot->GetMapId(), point->GetPositionX(), point->GetPositionY(), point->GetPositionZ(), + false, false, false, true, MovementPriority::MOVEMENT_COMBAT); + } + } + + return false; +} diff --git a/src/strategy/raids/aq40/RaidAq40Actions.h b/src/strategy/raids/aq40/RaidAq40Actions.h new file mode 100644 index 0000000000..31adcef2f1 --- /dev/null +++ b/src/strategy/raids/aq40/RaidAq40Actions.h @@ -0,0 +1,178 @@ +#ifndef _PLAYERBOT_RAIDAQ40ACTIONS_H +#define _PLAYERBOT_RAIDAQ40ACTIONS_H + +#include "MovementActions.h" +#include "GenericSpellActions.h" +#include "AttackAction.h" +#include "PlayerbotAI.h" +#include "Playerbots.h" + +class Aq40UseResistanceBuffsAction : public Action +{ +public: + Aq40UseResistanceBuffsAction(PlayerbotAI* botAI, std::string const name = "aq40 use resistance buffs") + : Action(botAI, name) {} + bool Execute(Event event) override; +}; + +class Aq40MoveFromOtherEmperorAction : public MovementAction +{ +public: + Aq40MoveFromOtherEmperorAction(PlayerbotAI* botAI, std::string const name = "aq40 move from other emperor") + : MovementAction(botAI, name) {} + bool Execute(Event event) override; +protected: + const Position* torch_left = new Position(-8894.3, 1285.5, -112.25); + const Position* torch_right = new Position(-9029.1, 1261.8, -112.25); + float lastdist = 0.0F; +}; + +BEGIN_RANGED_SPELL_ACTION(Aq40WarlockCastSearingPainAction, "searing pain") +END_SPELL_ACTION() + +BEGIN_RANGED_SPELL_ACTION(Aq40MageCastFrostboltAction, "frostbolt") +END_SPELL_ACTION() + +class Aq40MeleeViscidusAction : public AttackAction +{ +public: + Aq40MeleeViscidusAction(PlayerbotAI* botAI) : AttackAction(botAI, "aq40 melee viscidus") {} + bool Execute(Event event) override; +}; + +class Aq40AttackTargetByNameAction : public AttackAction +{ +public: + Aq40AttackTargetByNameAction(PlayerbotAI* botAI, std::string const name) : AttackAction(botAI, name) {} + bool Execute(Event event) override; + + virtual std::string const WhichEmperor() { return NULL; } +}; + +class Aq40AttackEmperorVekLorAction : public Aq40AttackTargetByNameAction +{ +public: + Aq40AttackEmperorVekLorAction(PlayerbotAI* botAI) : Aq40AttackTargetByNameAction(botAI, "aq40 attack emperor vek'lor") {} + + std::string const WhichEmperor() override { return "emperor vek'lor"; } +}; + +class Aq40AttackEmperorVekNilashAction : public Aq40AttackTargetByNameAction +{ +public: + Aq40AttackEmperorVekNilashAction(PlayerbotAI* botAI) : Aq40AttackTargetByNameAction(botAI, "aq40 attack emperor vek'nilash") {} + + std::string const WhichEmperor() override { return "emperor vek'nilash"; } +}; + +class Aq40AttackEmperorPestsAction : public AttackAction +{ +public: + Aq40AttackEmperorPestsAction(PlayerbotAI* botAI) : AttackAction(botAI, "aq40 attack emperor pests") {} + bool Execute(Event event) override; +}; + +class Aq40MoveTowardsEmperorAction : public MovementAction +{ +public: + Aq40MoveTowardsEmperorAction(PlayerbotAI* botAI, std::string const name) + : MovementAction(botAI, name) {} + bool Execute(Event event) override; + + virtual std::string const WhichEmperor() { return NULL; } +}; + +class Aq40MoveTowardsEmperorVekLorAction : Aq40MoveTowardsEmperorAction +{ +public: + Aq40MoveTowardsEmperorVekLorAction(PlayerbotAI* botAI) + : Aq40MoveTowardsEmperorAction(botAI, "aq40 move towards emperor vek'lor") {} + + std::string const WhichEmperor() override { return "emperor vek'lor"; } +}; + +class Aq40MoveTowardsEmperorVekNilashAction : Aq40MoveTowardsEmperorAction +{ +public: + Aq40MoveTowardsEmperorVekNilashAction(PlayerbotAI* botAI) + : Aq40MoveTowardsEmperorAction(botAI, "aq40 move towards emperor vek'nilash") {} + + std::string const WhichEmperor() override { return "emperor vek'nilash"; } +}; + +class Aq40OuroBurrowedFleeAction : public Action +{ +public: + Aq40OuroBurrowedFleeAction(PlayerbotAI* botAI, std::string const name = "aq40 ouro burrowed flee") + : Action(botAI, name) {} + bool Execute(Event event) override; +}; + +class Aq40Cthun1PositionAction : public MovementAction +{ +public: + Aq40Cthun1PositionAction(PlayerbotAI* botAI, std::string const name = "aq40 cthun1 get positioned") + : MovementAction(botAI, name) + { + /* + x=[-8620.924,-8534.281] + y=[1943.8806,2029.8264] + + -8578.6025,1986.8535 radius 43 = circumference 270, or room for about 20 bots spread 13.5 units apart + so.. two circles, one at radius 50, one at radius 36? + center at -8579.0,1987.0 + rings 14 units apart + outer ring 23 bots about 13.7 units apart + inner ring 17 bots about 13.3 units apart + */ + for (int n = 0; n < outerpointscount; n++) + { + Position* what = new Position(-8579.0F, 1987.0F, 101.0F); + // 45.0: outside of 25 yard range, inside of 30 yard range + what->RelocatePolarOffset(n * M_PI * 2.0 / outerpointscount, 43.0); + outerpoints[n] = what; + //printf("build outerpoint %d: x=%f y=%f z=%f\n",n,what->GetPositionX(),what->GetPositionY(),what->GetPositionZ()); + } + + for (int n = 0; n < innerpointscount; n++) + { + Position* what = new Position(-8579.0F, 1987.0F, 101.0F); + // 45.0: outside of 25 yard range, inside of 30 yard range + what->RelocatePolarOffset(n * M_PI * 2.0 / outerpointscount, 31.0); + innerpoints[n] = what; + //printf("build innerpoint %d: x=%f y=%f z=%f\n",n,what->GetPositionX(),what->GetPositionY(),what->GetPositionZ()); + } + } + bool Execute(Event event) override; + +protected: + static int WrappingDistanceBetween(int src, int dst, int scope); + int GetNearestPoint(int excludeouter = -1, bool doinner = true); + + static const int outerPointsCount = 23; + static const int innerPointsCount = 17; + + Position* outerPoints[outerPointsCount]; + Position* innerPoints[innerPointsCount]; +}; + +class Aq40Cthun2PositionAction : public Aq40Cthun1PositionAction +{ +public: + Aq40Cthun2PositionAction(PlayerbotAI* botAI, std::string const name = "aq40 cthun2 get positioned") + : Aq40Cthun1PositionAction(botAI, name) + { + // oldworld trigger 87646 entry 15384 + getOut = new Position(-8546.2, 1987.2, -96.52); + insideAttack = new Position(-8550.7, 2000.4, -97.495); + } + bool Execute(Event event) override; + +protected: + Position* getOut; + Position* insideAttack; + bool outTriggered = false; + bool attackPositioned = false; +}; + +#endif diff --git a/src/strategy/raids/aq40/RaidAq40Strategy.cpp b/src/strategy/raids/aq40/RaidAq40Strategy.cpp new file mode 100644 index 0000000000..a1d28e297c --- /dev/null +++ b/src/strategy/raids/aq40/RaidAq40Strategy.cpp @@ -0,0 +1,42 @@ +#include "RaidAq40Strategy.h" + +#include "Strategy.h" + +void RaidAq40Strategy::InitTriggers(std::vector& triggers) +{ + triggers.push_back( + new TriggerNode("aq40 should use resistance buffs", + NextAction::array(0, new NextAction("aq40 use resistance buffs", ACTION_RAID), nullptr))); + + triggers.push_back( + new TriggerNode("aq40 has emperor aggro", + NextAction::array(0, new NextAction("aq40 move from other emperor", ACTION_EMERGENCY), nullptr))); + + triggers.push_back( + new TriggerNode("aq40 warlock tank emperor", + NextAction::array(0, new NextAction("searing pain", ACTION_RAID), nullptr))); + + triggers.push_back( + new TriggerNode("aq40 mage frostbolt viscidus", // would be rank 1, ideally.. supplying "frostbolt(rank 1)" seems to not work + NextAction::array(0, new NextAction("frostbolt", ACTION_RAID), nullptr))); + + triggers.push_back( + new TriggerNode("aq40 melee viscidus", + NextAction::array(0, new NextAction("aq40 melee viscidus", ACTION_RAID + 1), nullptr))); + + triggers.push_back( + new TriggerNode("aq40 emperor fight", + NextAction::array(0, new NextAction("aq40 decide emperor action", ACTION_RAID), nullptr))); + + triggers.push_back( + new TriggerNode("aq40 ouro burrowed", + NextAction::array(0, new NextAction("aq40 ouro burrowed flee", ACTION_RAID), nullptr))); + + triggers.push_back( + new TriggerNode("aq40 cthun1 started", + NextAction::array(0, new NextAction("aq40 cthun1 get positioned", ACTION_RAID), nullptr))); + + triggers.push_back( + new TriggerNode("aq40 cthun2 started", + NextAction::array(0, new NextAction("aq40 cthun2 get positioned", ACTION_RAID), nullptr))); +} diff --git a/src/strategy/raids/aq40/RaidAq40Strategy.h b/src/strategy/raids/aq40/RaidAq40Strategy.h new file mode 100644 index 0000000000..939cf730d5 --- /dev/null +++ b/src/strategy/raids/aq40/RaidAq40Strategy.h @@ -0,0 +1,17 @@ +#ifndef _PLAYERBOT_RAIDAQ40STRATEGY_H +#define _PLAYERBOT_RAIDAQ40STRATEGY_H + +#include "AiObjectContext.h" +#include "Multiplier.h" +#include "Strategy.h" + +class RaidAq40Strategy : public Strategy +{ +public: + RaidAq40Strategy(PlayerbotAI* ai) : Strategy(ai) {} + virtual std::string const getName() override { return "aq40"; } + virtual void InitTriggers(std::vector& triggers) override; + // virtual void InitMultipliers(std::vector &multipliers) override; +}; + +#endif diff --git a/src/strategy/raids/aq40/RaidAq40TriggerContext.h b/src/strategy/raids/aq40/RaidAq40TriggerContext.h new file mode 100644 index 0000000000..e0e3721ada --- /dev/null +++ b/src/strategy/raids/aq40/RaidAq40TriggerContext.h @@ -0,0 +1,44 @@ +#ifndef _PLAYERBOT_RAIDAQ40TRIGGERCONTEXT_H +#define _PLAYERBOT_RAIDAQ40TRIGGERCONTEXT_H + +#include "AiObjectContext.h" +#include "NamedObjectContext.h" +#include "RaidAq40Triggers.h" + +class RaidAq40TriggerContext : public NamedObjectContext +{ +public: + RaidAq40TriggerContext() + { + creators["aq40 should use resistance buffs"] = &RaidAq40TriggerContext::should_use_resistance_buffs; + creators["aq40 has emperor aggro"] = &RaidAq40TriggerContext::has_emperor_aggro; + creators["aq40 warlock tank emperor"] = &RaidAq40TriggerContext::warlock_tank_emperor; + creators["aq40 mage frostbolt viscidus"] = &RaidAq40TriggerContext::mage_frostbolt_viscidus; + creators["aq40 melee viscidus"] = &RaidAq40TriggerContext::melee_viscidus; + creators["aq40 target emperor vek'lor"] = &RaidAq40TriggerContext::target_emperor_veklor; + creators["aq40 target emperor vek'nilash"] = &RaidAq40TriggerContext::target_emperor_veknilash; + creators["aq40 target emperor pests"] = &RaidAq40TriggerContext::target_emperor_pests; + creators["aq40 approach emperor vek'lor"] = &RaidAq40TriggerContext::approach_emperor_veklor; + creators["aq40 approach emperor vek'nilash"] = &RaidAq40TriggerContext::approach_emperor_veknilash; + creators["aq40 ouro burrowed"] = &RaidAq40TriggerContext::ouro_burrowed; + creators["aq40 cthun1 started"] = &RaidAq40TriggerContext::cthun1_started; + creators["aq40 cthun2 started"] = &RaidAq40TriggerContext::cthun2_started; + } + +private: + static Trigger* should_use_resistance_buffs(PlayerbotAI* ai) { return new Aq40ShouldUseResistanceBuffsTrigger(ai); } + static Trigger* has_emperor_aggro(PlayerbotAI* ai) { return new Aq40HasEmperorAggroTrigger(ai); } + static Trigger* warlock_tank_emperor(PlayerbotAI* ai) { return new Aq40WarlockTankEmperorTrigger(ai); } + static Trigger* mage_frostbolt_viscidus(PlayerbotAI* ai) { return new Aq40MageFrostboltViscidusTrigger(ai); } + static Trigger* melee_viscidus(PlayerbotAI* ai) { return new Aq40MeleeViscidusTrigger(ai); } + static Trigger* target_emperor_veklor(PlayerbotAI* ai) { return new Aq40TargetEmperorVekLorTrigger(ai); } + static Trigger* target_emperor_veknilash(PlayerbotAI* ai) { return new Aq40TargetEmperorVekNilashTrigger(ai); } + static Trigger* target_emperor_pests(PlayerbotAI* ai) { return new Aq40TargetEmperorPestsTrigger(ai); } + static Trigger* approach_emperor_veklor(PlayerbotAI* ai) { return new Aq40ApproachEmperorVekLorTrigger(ai); } + static Trigger* approach_emperor_veknilash(PlayerbotAI* ai) { return new Aq40ApproachEmperorVekNilashTrigger(ai); } + static Trigger* ouro_burrowed(PlayerbotAI* ai) { return new Aq40OuroBurrowedTrigger(ai); } + static Trigger* cthun1_started(PlayerbotAI* ai) { return new Aq40Cthun1StartedTrigger(ai); } + static Trigger* cthun2_started(PlayerbotAI* ai) { return new Aq40Cthun2StartedTrigger(ai); } +}; + +#endif diff --git a/src/strategy/raids/aq40/RaidAq40Triggers.cpp b/src/strategy/raids/aq40/RaidAq40Triggers.cpp new file mode 100644 index 0000000000..c7d4b5e901 --- /dev/null +++ b/src/strategy/raids/aq40/RaidAq40Triggers.cpp @@ -0,0 +1,438 @@ +#include "RaidAq40Triggers.h" + +#include "SharedDefines.h" + +bool Aq40HasEmperorAggroTrigger::IsActive() +{ + // temple_of_ahnquiraj.h + const int NPC_VEKLOR = 15276; + const int NPC_VEKNILASH = 15275; + + ObjectGuid botguid = bot->GetGUID(); + ObjectGuid petguid = (ObjectGuid)0UL; + if (Unit* pet = bot->GetPet()) + { + petguid = pet->GetGUID(); + } + + if (Creature* boss1 = bot->FindNearestCreature(NPC_VEKLOR, 200.0f)) + if (Creature* boss2 = bot->FindNearestCreature(NPC_VEKNILASH, 200.0f)) + { + ObjectGuid boss1target = boss1->GetTarget(); + + if (boss1target && (boss1target == botguid || (petguid && boss1target == petguid))) + { + return true; + } + + ObjectGuid boss2target = boss2->GetTarget(); + + if (boss2target && (boss2target == botguid || (petguid && boss2target == petguid))) + { + return true; + } + } + return false; +} + +bool Aq40WarlockTankEmperorTrigger::IsActive() +{ + if (bot->getClass() == CLASS_WARLOCK) + { + if (Unit* boss1 = AI_VALUE2(Unit*, "find target", "emperor vek'lor")) + { + ObjectGuid bottarget = bot->GetTarget(); + ObjectGuid bossguid = boss1->GetGUID(); + ObjectGuid bosstargetguid = boss1->GetTarget(); + if (bosstargetguid) + { + Player* bosstarget = botAI->GetPlayer(bosstargetguid); + + if (!bosstarget || bosstarget->getClass() != CLASS_WARLOCK || !botAI->IsTank(bosstarget, true)) + { + //std::ostringstream out; + //out << "want tank? target="<GetTarget(); + ObjectGuid bossguid = boss1->GetGUID(); + + //std::ostringstream out; + //out << "want tank? target="<HasAura(SPELL_VISCIDUS_FREEZE)) + { + return true; + } + } + + return false; +} + +bool Aq40EmperorTrigger::IsActive() +{ + Unit* boss1 = AI_VALUE2(Unit*, "find target", "emperor vek'lor"); + Unit* boss2 = AI_VALUE2(Unit*, "find target", "emperor vek'nilash"); + + if (boss1 && boss2 && (boss1->IsInCombat() || boss2->IsInCombat())) + { + Group* group = bot->GetGroup(); + if (!group) + { + return false; + } + + ObjectGuid self = bot->GetGUID(); + ObjectGuid masterguid = botAI->GetMaster()->GetGUID(); + + std::vector memberlists[4]; + for (int n = 0; n < 4; n++) + { + memberlists[n].reserve(group->GetMembersCount()); + } + + int selftype = -1; + + std::vector members; + members.reserve(group->GetMembersCount()); + + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + auto memberAI = GET_PLAYERBOT_AI(member); + if (memberAI) + { + Player* submaster = memberAI->GetMaster(); + if (submaster && submaster->GetGUID() == masterguid) + { + int assignedtype; + if (member->getClass() == CLASS_WARLOCK) + { + // lol warlock tanks + assignedtype = 0; + } + else if (memberAI->IsTank(member)) + { + assignedtype = 1; + } + else if (memberAI->IsHeal(member)) + { + assignedtype = 2; + } + else + { + assignedtype = 3; + } + + memberlists[assignedtype].push_back(member); + if (member->GetGUID() == self) + { + selftype = assignedtype; + } + } + } + } + + if (selftype < 0 || (IsForHealers() != (selftype == 2))) + { + return false; + } + + + int sametypecount = memberlists[selftype].size(); + + float dist1[sametypecount]; + ObjectGuid dist1who[sametypecount]; + Unit* dist1unit[sametypecount]; + + float dist2[sametypecount]; + ObjectGuid dist2who[sametypecount]; + Unit* dist2unit[sametypecount]; + + for (int n = 0; n < sametypecount; n++) + { + auto member = memberlists[selftype][n]; + + dist1[n] = boss1->GetDistance(member); + dist2[n] = boss2->GetDistance(member); + dist1who[n] = member->GetGUID(); + dist2who[n] = member->GetGUID(); + dist1unit[n] = member; + dist2unit[n] = member; + } + + for (int n = 0; n < sametypecount; n++) + { + for (int subn = n + 1; subn < sametypecount; subn++) + { + if (dist1[n] > dist1[subn]) + { + float fswap = dist1[n]; + dist1[n] = dist1[subn]; + dist1[subn] = fswap; + + ObjectGuid gswap = dist1who[n]; + dist1who[n] = dist1who[subn]; + dist1who[subn] = gswap; + + Unit* uswap = dist1unit[n]; + dist1unit[n] = dist1unit[subn]; + dist1unit[subn] = uswap; + } + + if (dist2[n] > dist2[subn]) + { + float fswap = dist2[n]; + dist2[n] = dist2[subn]; + dist2[subn] = fswap; + + ObjectGuid gswap = dist2who[n]; + dist2who[n] = dist2who[subn]; + dist2who[subn] = gswap; + + Unit* uswap = dist2unit[n]; + dist2unit[n] = dist2unit[subn]; + dist2unit[subn] = uswap; + } + } + } + + ObjectGuid boss1assigned[sametypecount]; + int boss1assignedindex=0; + ObjectGuid boss2assigned[sametypecount]; + int boss2assignedindex=0; + + if (selftype == 3) + { + sametypecount -= 4; + } + else if (selftype == 1) + { + if (sametypecount > 3) + { + sametypecount -= 2; + } + else if (sametypecount > 2) + { + sametypecount--; + } + } + else if (selftype == 2) + { + if (sametypecount > 4) + { + sametypecount = 4; + } + } + + int lastcount = 0; + while (boss1assignedindex + boss2assignedindex < sametypecount) + { + for (int n = 0; n < sametypecount; n++) + { + if (dist1who[n] && boss1assignedindex <= boss2assignedindex) + { + if (selftype != 3 || (dist1unit[n]->getClass() != CLASS_ROGUE && dist1unit[n]->getClass() != CLASS_WARRIOR && dist1unit[n]->getClass() != CLASS_HUNTER)) + { + boss1assigned[boss1assignedindex] = dist1who[n]; + boss1assignedindex++; + + for (int subn = 0; subn < sametypecount; subn++) + { + if (dist2who[subn] == dist1who[n]) + { + dist2who[subn] = (ObjectGuid)0UL; + break; + } + } + dist1who[n] = (ObjectGuid)0UL; + } + } + + if (boss1assignedindex + boss2assignedindex >= sametypecount) + { + break; + } + + if (dist2who[n] && boss2assignedindex <= boss1assignedindex) + { + if (selftype != 3 || (dist2unit[n]->getClass() != CLASS_WARLOCK && dist2unit[n]->getClass() != CLASS_MAGE && dist2unit[n]->getClass() != CLASS_PRIEST)) + { + boss2assigned[boss2assignedindex] = dist2who[n]; + boss2assignedindex++; + + for (int subn = 0; subn < sametypecount; subn++) + { + if (dist1who[subn] == dist2who[n]) + { + dist1who[subn] = (ObjectGuid)0UL; + break; + } + } + dist2who[n] = (ObjectGuid)0UL; + } + } + } + + if (lastcount == boss1assignedindex + boss2assignedindex) + { + // failure to keep filling slots, abort + break; + } + + lastcount = boss1assignedindex + boss2assignedindex; + } + + for (int n = 0; n < boss1assignedindex; n++) + { + if (boss1assigned[n] == self) + { + if (IsVekLor()) + { + std::ostringstream out; + out << "attack vek'lor"; + botAI->TellError(out.str()); + + return true; + } + + return false; + } + } + + for (int n = 0; n < boss2assignedindex; n++) + { + if (boss2assigned[n] == self) + { + if (IsVekNilash()) + { + std::ostringstream out; + out << "attack vek'nilash"; + botAI->TellError(out.str()); + + return true; + } + + return false; + } + } + + if (IsPestControl()) + { + std::ostringstream out; + out << "attack pests"; + botAI->TellError(out.str()); + + return true; + } + } + + return false; +} + +bool Aq40OuroBurrowedTrigger::IsActive() +{ + if (Unit* boss = AI_VALUE2(Unit*, "find target", "ouro")) + { + if (boss->IsInCombat() && (boss->GetUnitFlags() & UNIT_FLAG_NOT_SELECTABLE) == UNIT_FLAG_NOT_SELECTABLE) + { + return true; + } + } + /* + else if (Unit* boss = AI_VALUE2(Unit*, "find target", "eye of c'thun")) + { + if (boss->IsInCombat()) + { + return true; + } + } + */ + + return botAI->HasStrategy("move from group", BotState::BOT_STATE_COMBAT); +} + +bool Aq40Cthun1StartedTrigger::IsActive() +{ + if (Unit* boss = AI_VALUE2(Unit*, "find target", "c'thun")) + { + if (boss->IsAlive() && boss->IsInCombat() && (boss->GetUnitFlags() & UNIT_FLAG_NOT_SELECTABLE) == 0) + { + return false; + } + } + if (Unit* boss = AI_VALUE2(Unit*, "find target", "eye of c'thun")) + { + if (boss->IsAlive() && boss->IsInCombat()) + { + if (!wasactive) + { + //printf("start cthun 1\n"); + wasactive = true; + } + return true; + } + } + + if (wasactive) + { + //printf("end cthun 1\n"); + wasactive = false; + return true; + } + return false; +} + +bool Aq40Cthun2StartedTrigger::IsActive() +{ + if (Unit* boss = AI_VALUE2(Unit*, "find target", "c'thun")) + { + if (boss->IsAlive() && boss->IsInCombat() && (boss->GetUnitFlags() & UNIT_FLAG_NOT_SELECTABLE) == 0) + { + if (!wasactive) + { + //printf("start cthun 2\n"); + wasactive = true; + } + return true; + } + } + + if (wasactive) + { + //printf("end cthun 2\n"); + wasactive = false; + return true; + } + return false; +} diff --git a/src/strategy/raids/aq40/RaidAq40Triggers.h b/src/strategy/raids/aq40/RaidAq40Triggers.h new file mode 100644 index 0000000000..1188eb200b --- /dev/null +++ b/src/strategy/raids/aq40/RaidAq40Triggers.h @@ -0,0 +1,135 @@ +#ifndef _PLAYERBOT_RAIDAQ40TRIGGERS_H +#define _PLAYERBOT_RAIDAQ40TRIGGERS_H + +#include "PlayerbotAI.h" +#include "Playerbots.h" +#include "Trigger.h" + +class Aq40ShouldUseResistanceBuffsTrigger : public Trigger +{ +public: + Aq40ShouldUseResistanceBuffsTrigger(PlayerbotAI* botAI) : Trigger(botAI, "aq40 should use resistance buffs") {} + bool IsActive() override { return true; } +}; + +class Aq40HasEmperorAggroTrigger : public Trigger +{ +public: + Aq40HasEmperorAggroTrigger(PlayerbotAI* botAI) : Trigger(botAI, "aq40 has emperor aggro") {} + bool IsActive() override; +}; + +class Aq40WarlockTankEmperorTrigger : public Trigger +{ +public: + Aq40WarlockTankEmperorTrigger(PlayerbotAI* botAI) : Trigger(botAI, "aq40 warlock tank emperor") {} + bool IsActive() override; +}; + +class Aq40MageFrostboltViscidusTrigger : public Trigger +{ +public: + Aq40MageFrostboltViscidusTrigger(PlayerbotAI* botAI) : Trigger(botAI, "aq40 mage frostbolt viscidus") {} + bool IsActive() override; +}; + +class Aq40MeleeViscidusTrigger : public Trigger +{ +public: + Aq40MeleeViscidusTrigger(PlayerbotAI* botAI) : Trigger(botAI, "aq40 melee viscidus") {} + bool IsActive() override; +}; + +class Aq40EmperorTrigger : public Trigger +{ +public: + Aq40EmperorTrigger(PlayerbotAI* botAI, std::string const name) : Trigger(botAI, name) {} + bool IsActive() override; +protected: + virtual bool IsVekLor() { return false; } + virtual bool IsVekNilash() { return false; } + virtual bool IsForHealers() { return false; } + virtual bool IsPestControl() { return false; } +}; + +class Aq40TargetEmperorVekLorTrigger : public Aq40EmperorTrigger +{ +public: + Aq40TargetEmperorVekLorTrigger(PlayerbotAI* botAI) : Aq40EmperorTrigger(botAI, "aq40 target emperor vek'lor") {} +protected: + bool IsVekLor() override { return true; } + bool IsVekNilash() override { return false; } + bool IsForHealers() override { return false; } + bool IsPestControl() override { return false; } +}; + +class Aq40TargetEmperorVekNilashTrigger : public Aq40EmperorTrigger +{ +public: + Aq40TargetEmperorVekNilashTrigger(PlayerbotAI* botAI) : Aq40EmperorTrigger(botAI, "aq40 target emperor vek'nilash") {} +protected: + bool IsVekLor() override { return false; } + bool IsVekNilash() override { return true; } + bool IsForHealers() override { return false; } + bool IsPestControl() override { return false; } +}; + +class Aq40TargetEmperorPestsTrigger : public Aq40EmperorTrigger +{ +public: + Aq40TargetEmperorPestsTrigger(PlayerbotAI* botAI) : Aq40EmperorTrigger(botAI, "aq40 target emperor pests") {} +protected: + bool IsVekLor() override { return false; } + bool IsVekNilash() override { return false; } + bool IsForHealers() override { return false; } + bool IsPestControl() override { return true; } +}; + +class Aq40ApproachEmperorVekLorTrigger : public Aq40EmperorTrigger +{ +public: + Aq40ApproachEmperorVekLorTrigger(PlayerbotAI* botAI) : Aq40EmperorTrigger(botAI, "aq40 approach emperor vek'lor") {} +protected: + bool IsVekLor() override { return true; } + bool IsVekNilash() override { return false; } + bool IsForHealers() override { return true; } + bool IsPestControl() override { return false; } +}; + +class Aq40ApproachEmperorVekNilashTrigger : public Aq40EmperorTrigger +{ +public: + Aq40ApproachEmperorVekNilashTrigger(PlayerbotAI* botAI) : Aq40EmperorTrigger(botAI, "aq40 approach emperor vek'nilash") {} +protected: + bool IsVekLor() override { return false; } + bool IsVekNilash() override { return true; } + bool IsForHealers() override { return true; } + bool IsPestControl() override { return false; } +}; + +class Aq40OuroBurrowedTrigger : public Trigger +{ +public: + Aq40OuroBurrowedTrigger(PlayerbotAI* botAI) : Trigger(botAI, "aq40 ouro burrowed") {} + bool IsActive() override; +}; + +class Aq40Cthun1StartedTrigger : public Trigger +{ +public: + Aq40Cthun1StartedTrigger(PlayerbotAI* botAI) : Trigger(botAI, "aq40 cthun1 started") {} + bool IsActive() override; +private: + bool wasactive = false; +}; + +class Aq40Cthun2StartedTrigger : public Trigger +{ +public: + Aq40Cthun2StartedTrigger(PlayerbotAI* botAI) : Trigger(botAI, "aq40 cthun2 started") {} + bool IsActive() override; +private: + bool wasactive = false; +}; + +#endif