From b526a07303a0c4cc822a7732558922eb512bf157 Mon Sep 17 00:00:00 2001 From: Bobblybook Date: Sat, 12 Oct 2024 23:51:00 +1100 Subject: [PATCH] Generalise dragon flanking action --- src/strategy/actions/ActionContext.h | 2 ++ src/strategy/actions/MovementActions.cpp | 40 ++++++++++++++++++++++++ src/strategy/actions/MovementActions.h | 25 +++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/src/strategy/actions/ActionContext.h b/src/strategy/actions/ActionContext.h index e195e2f9..c13f7162 100644 --- a/src/strategy/actions/ActionContext.h +++ b/src/strategy/actions/ActionContext.h @@ -94,6 +94,7 @@ public: creators["avoid aoe"] = &ActionContext::avoid_aoe; creators["combat formation move"] = &ActionContext::combat_formation_move; creators["tank face"] = &ActionContext::tank_face; + creators["rear flank"] = &ActionContext::rear_flank; creators["disperse set"] = &ActionContext::disperse_set; creators["gift of the naaru"] = &ActionContext::gift_of_the_naaru; creators["shoot"] = &ActionContext::shoot; @@ -278,6 +279,7 @@ private: static Action* avoid_aoe(PlayerbotAI* botAI) { return new AvoidAoeAction(botAI); } static Action* combat_formation_move(PlayerbotAI* botAI) { return new CombatFormationMoveAction(botAI); } static Action* tank_face(PlayerbotAI* botAI) { return new TankFaceAction(botAI); } + static Action* rear_flank(PlayerbotAI* botAI) { return new RearFlankAction(botAI); } static Action* disperse_set(PlayerbotAI* botAI) { return new DisperseSetAction(botAI); } static Action* gift_of_the_naaru(PlayerbotAI* botAI) { return new CastGiftOfTheNaaruAction(botAI); } static Action* lifeblood(PlayerbotAI* botAI) { return new CastLifeBloodAction(botAI); } diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index 569d7fcc..791f0990 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -2372,6 +2372,46 @@ bool TankFaceAction::Execute(Event event) return MoveTo(bot->GetMapId(), nearest.GetPositionX(), nearest.GetPositionY(), nearest.GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_COMBAT); } +bool RearFlankAction::isUseful() +{ + Unit* target = AI_VALUE(Unit*, "current target"); + if (!target) { return false; } + + // Need to double the front angle check to account for mirrored angle. + bool inFront = target->HasInArc(2.f * minAngle, bot); + // Rear check does not need to double this angle as the logic is inverted + // and we are subtracting from 2pi. + bool inRear = !target->HasInArc((2.f * M_PI) - maxAngle, bot); + + return inFront || inRear; +} + +bool RearFlankAction::Execute(Event event) +{ + Unit* target = AI_VALUE(Unit*, "current target"); + if (!target) { return false; } + + float angle = frand(minAngle, maxAngle); + float baseDistance = bot->GetMeleeRange(target) * 0.5f; + Position leftFlank = target->GetPosition(); + Position rightFlank = target->GetPosition(); + Position* destination = nullptr; + leftFlank.RelocatePolarOffset(angle, baseDistance + distance); + rightFlank.RelocatePolarOffset(-angle, baseDistance + distance); + + if (bot->GetExactDist2d(leftFlank) < bot->GetExactDist2d(rightFlank)) + { + destination = &leftFlank; + } + else + { + destination = &rightFlank; + } + + return MoveTo(bot->GetMapId(), destination->GetPositionX(), destination->GetPositionY(), destination->GetPositionZ(), + false, false, false, true, MovementPriority::MOVEMENT_COMBAT); +} + bool DisperseSetAction::Execute(Event event) { std::string const text = event.getParam(); diff --git a/src/strategy/actions/MovementActions.h b/src/strategy/actions/MovementActions.h index 7b03f034..76929427 100644 --- a/src/strategy/actions/MovementActions.h +++ b/src/strategy/actions/MovementActions.h @@ -18,6 +18,9 @@ class Unit; class WorldObject; class Position; +#define ANGLE_45_DEG (static_cast(M_PI) / 4.f) +#define ANGLE_90_DEG M_PI_2 +#define ANGLE_120_DEG (2.f * static_cast(M_PI) / 3.f) class MovementAction : public Action { @@ -144,6 +147,27 @@ public: bool Execute(Event event) override; }; +class RearFlankAction : public MovementAction +{ +// 90 degree minimum angle prevents any frontal cleaves/breaths and avoids parry-hasting the boss. +// 120 degree maximum angle leaves a 120 degree symmetrical cone at the tail end which is usually enough to avoid tail swipes. +// Some dragons or mobs may have different danger zone angles, override if needed. +public: + RearFlankAction(PlayerbotAI* botAI, float distance = 0.0f, float minAngle = ANGLE_90_DEG, float maxAngle = ANGLE_120_DEG) + : MovementAction(botAI, "rear flank") + { + this->distance = distance; + this->minAngle = minAngle; + this->maxAngle = maxAngle; + } + + bool Execute(Event event) override; + bool isUseful() override; + +protected: + float distance, minAngle, maxAngle; +}; + class DisperseSetAction : public Action { public: @@ -268,4 +292,5 @@ public: bool Execute(Event event) override; }; + #endif