From 037258fb1a0890054a77b79f7aaf0722bad430d9 Mon Sep 17 00:00:00 2001 From: EricksOliveira Date: Thu, 17 Oct 2024 21:19:00 -0300 Subject: [PATCH 01/17] Fix Bug Bot blocked in PT After leaving PT, the Bot was stuck in a Group, stopped and without receiving invites from other players. Ideally, use the LeaveGroupOnLogout.Enabled = 1 setting in Worldserver.conf so that after the player leaves the game, the Bots are not stuck in Raid. --- src/strategy/actions/LeaveGroupAction.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/strategy/actions/LeaveGroupAction.cpp b/src/strategy/actions/LeaveGroupAction.cpp index 39d0451d..b2594e0f 100644 --- a/src/strategy/actions/LeaveGroupAction.cpp +++ b/src/strategy/actions/LeaveGroupAction.cpp @@ -30,6 +30,8 @@ bool PartyCommandAction::Execute(Event event) Player* master = GetMaster(); if (master && member == master->GetName()) return Leave(bot); + + botAI->Reset(); return false; } @@ -62,6 +64,8 @@ bool UninviteAction::Execute(Event event) if (bot->GetGUID() == guid) return Leave(bot); } + + botAI->Reset(); return false; } @@ -160,6 +164,8 @@ bool LeaveFarAwayAction::isUseful() { return true; } + + botAI->Reset(); return false; } From f6987a80a51746ccb5641da165c81ce613e3e66d Mon Sep 17 00:00:00 2001 From: Bobblybook Date: Fri, 18 Oct 2024 20:42:36 +1100 Subject: [PATCH 02/17] Halls of Lightning implementation Halls of Lightning implementation --- src/strategy/AiObjectContext.cpp | 2 + .../dungeons/DungeonStrategyContext.h | 5 +- .../wotlk/WotlkDungeonActionContext.h | 2 +- .../wotlk/WotlkDungeonTriggerContext.h | 2 +- .../azjolnerub/AzjolNerubMultipliers.cpp | 16 +- .../HallsOfLightningActionContext.h | 34 ++++ .../HallsOfLightningActions.cpp | 163 ++++++++++++++++++ .../HallsOfLightningActions.h | 79 +++++++++ .../HallsOfLightningMultipliers.cpp | 88 ++++++++++ .../HallsOfLightningMultipliers.h | 42 +++++ .../HallsOfLightningStrategy.cpp | 41 +++++ .../HallsOfLightningStrategy.h | 18 ++ .../HallsOfLightningTriggerContext.h | 35 ++++ .../HallsOfLightningTriggers.cpp | 92 ++++++++++ .../HallsOfLightningTriggers.h | 95 ++++++++++ .../dungeons/wotlk/hallsoflightning/TODO | 0 16 files changed, 699 insertions(+), 15 deletions(-) create mode 100644 src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningActionContext.h create mode 100644 src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningActions.cpp create mode 100644 src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningActions.h create mode 100644 src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningMultipliers.cpp create mode 100644 src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningMultipliers.h create mode 100644 src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningStrategy.cpp create mode 100644 src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningStrategy.h create mode 100644 src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningTriggerContext.h create mode 100644 src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningTriggers.cpp create mode 100644 src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningTriggers.h delete mode 100644 src/strategy/dungeons/wotlk/hallsoflightning/TODO diff --git a/src/strategy/AiObjectContext.cpp b/src/strategy/AiObjectContext.cpp index c874f577..b8121320 100644 --- a/src/strategy/AiObjectContext.cpp +++ b/src/strategy/AiObjectContext.cpp @@ -58,6 +58,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) actionContexts.Add(new WotlkDungeonVHActionContext()); actionContexts.Add(new WotlkDungeonGDActionContext()); actionContexts.Add(new WotlkDungeonHoSActionContext()); + actionContexts.Add(new WotlkDungeonHoLActionContext()); triggerContexts.Add(new TriggerContext()); triggerContexts.Add(new ChatTriggerContext()); @@ -76,6 +77,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) triggerContexts.Add(new WotlkDungeonVHTriggerContext()); triggerContexts.Add(new WotlkDungeonGDTriggerContext()); triggerContexts.Add(new WotlkDungeonHoSTriggerContext()); + triggerContexts.Add(new WotlkDungeonHoLTriggerContext()); valueContexts.Add(new ValueContext()); diff --git a/src/strategy/dungeons/DungeonStrategyContext.h b/src/strategy/dungeons/DungeonStrategyContext.h index fa0d31c2..5ed70f6c 100644 --- a/src/strategy/dungeons/DungeonStrategyContext.h +++ b/src/strategy/dungeons/DungeonStrategyContext.h @@ -10,12 +10,11 @@ #include "wotlk/violethold/VioletHoldStrategy.h" #include "wotlk/gundrak/GundrakStrategy.h" #include "wotlk/hallsofstone/HallsOfStoneStrategy.h" +#include "wotlk/hallsoflightning/HallsOfLightningStrategy.h" /* Full list/TODO: -Halls of Lightning - HoL -General Bjarngrim, Volkhan, Ionar, Loken The Oculus - Occ Drakos the Interrogator, Varos Cloudstrider, Mage-Lord Urom, Ley-Guardian Eregos Utgarde Pinnacle - UP @@ -76,8 +75,8 @@ class DungeonStrategyContext : public NamedObjectContext static Strategy* wotlk_vh(PlayerbotAI* botAI) { return new WotlkDungeonVHStrategy(botAI); } static Strategy* wotlk_gd(PlayerbotAI* botAI) { return new WotlkDungeonGDStrategy(botAI); } static Strategy* wotlk_hos(PlayerbotAI* botAI) { return new WotlkDungeonHoSStrategy(botAI); } + static Strategy* wotlk_hol(PlayerbotAI* botAI) { return new WotlkDungeonHoLStrategy(botAI); } - static Strategy* wotlk_hol(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } static Strategy* wotlk_occ(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } static Strategy* wotlk_up(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } static Strategy* wotlk_cos(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } diff --git a/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h b/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h index 73fbecb4..b1301868 100644 --- a/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h +++ b/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h @@ -9,7 +9,7 @@ #include "violethold/VioletHoldActionContext.h" #include "gundrak/GundrakActionContext.h" #include "hallsofstone/HallsOfStoneActionContext.h" -// #include "hallsoflightning/HallsOfLightningActionContext.h" +#include "hallsoflightning/HallsOfLightningActionContext.h" // #include "oculus/OculusActionContext.h" // #include "utgardepinnacle/UtgardePinnacleActionContext.h" // #include "cullingofstratholme/CullingOfStratholmeActionContext.h" diff --git a/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h b/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h index 7e966e2f..cb819430 100644 --- a/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h +++ b/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h @@ -9,7 +9,7 @@ #include "violethold/VioletHoldTriggerContext.h" #include "gundrak/GundrakTriggerContext.h" #include "hallsofstone/HallsOfStoneTriggerContext.h" -// #include "hallsoflightning/HallsOfLightningTriggerContext.h" +#include "hallsoflightning/HallsOfLightningTriggerContext.h" // #include "oculus/OculusTriggerContext.h" // #include "utgardepinnacle/UtgardePinnacleTriggerContext.h" // #include "cullingofstratholme/CullingOfStratholmeTriggerContext.h" diff --git a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubMultipliers.cpp b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubMultipliers.cpp index 6c287591..38cb9e9a 100644 --- a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubMultipliers.cpp +++ b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubMultipliers.cpp @@ -38,19 +38,15 @@ float KrikthirMultiplier::GetValue(Action* action) if (boss && watcher) { // Do not target swap - // TODO: Need to suppress AoE actions but unsure how to identify them - // TODO: TEST AOE Avoid - if (dynamic_cast(action) - || dynamic_cast(action)) + if (dynamic_cast(action)) + { + return 0.0f; + } + + if (action->getThreatType() == Action::ActionThreatType::Aoe) { return 0.0f; } - // Doesn't seem to work - // if (action->getThreatType() == Action::ActionThreatType::Aoe) - // { - // bot->Yell("Suppressed AoE", LANG_UNIVERSAL); - // return 0.0f; - // } } return 1.0f; } diff --git a/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningActionContext.h b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningActionContext.h new file mode 100644 index 00000000..c3073d49 --- /dev/null +++ b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningActionContext.h @@ -0,0 +1,34 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONHOLACTIONCONTEXT_H +#define _PLAYERBOT_WOTLKDUNGEONHOLACTIONCONTEXT_H + +#include "Action.h" +#include "NamedObjectContext.h" +#include "HallsOfLightningActions.h" + +class WotlkDungeonHoLActionContext : public NamedObjectContext +{ + public: + WotlkDungeonHoLActionContext() { + creators["bjarngrim target"] = &WotlkDungeonHoLActionContext::bjarngrim_target; + creators["avoid whirlwind"] = &WotlkDungeonHoLActionContext::avoid_whirlwind; + creators["volkhan target"] = &WotlkDungeonHoLActionContext::volkhan_target; + creators["static overload spread"] = &WotlkDungeonHoLActionContext::static_overload_spread; + creators["ball lightning spread"] = &WotlkDungeonHoLActionContext::ball_lightning_spread; + creators["ionar tank position"] = &WotlkDungeonHoLActionContext::ionar_tank_position; + creators["disperse position"] = &WotlkDungeonHoLActionContext::disperse_position; + creators["loken stack"] = &WotlkDungeonHoLActionContext::loken_stack; + creators["avoid lightning nova"] = &WotlkDungeonHoLActionContext::avoid_lightning_nova; + } + private: + static Action* bjarngrim_target(PlayerbotAI* ai) { return new BjarngrimTargetAction(ai); } + static Action* avoid_whirlwind(PlayerbotAI* ai) { return new AvoidWhirlwindAction(ai); } + static Action* volkhan_target(PlayerbotAI* ai) { return new VolkhanTargetAction(ai); } + static Action* static_overload_spread(PlayerbotAI* ai) { return new StaticOverloadSpreadAction(ai); } + static Action* ball_lightning_spread(PlayerbotAI* ai) { return new BallLightningSpreadAction(ai); } + static Action* ionar_tank_position(PlayerbotAI* ai) { return new IonarTankPositionAction(ai); } + static Action* disperse_position(PlayerbotAI* ai) { return new DispersePositionAction(ai); } + static Action* loken_stack(PlayerbotAI* ai) { return new LokenStackAction(ai); } + static Action* avoid_lightning_nova(PlayerbotAI* ai) { return new AvoidLightningNovaAction(ai); } +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningActions.cpp b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningActions.cpp new file mode 100644 index 00000000..6ca64dd4 --- /dev/null +++ b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningActions.cpp @@ -0,0 +1,163 @@ +#include "Playerbots.h" +#include "HallsOfLightningActions.h" +#include "HallsOfLightningStrategy.h" + +bool BjarngrimTargetAction::Execute(Event event) +{ + Unit* target = nullptr; + + // Target is not findable from threat table using AI_VALUE2(), + // therefore need to search manually for the unit name + GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); + + for (auto i = targets.begin(); i != targets.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (unit && unit->GetEntry() == NPC_STORMFORGED_LIEUTENANT) + { + target = unit; + break; + } + } + // There are two, we don't want to ping-pong between them if we're attacking one already + if (!target || AI_VALUE(Unit*, "current target") == target) + { + return false; + } + + return Attack(target); +} + +bool AvoidWhirlwindAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "general bjarngrim"); + if (!boss) { return false; } + + float distance = bot->GetExactDist2d(boss->GetPosition()); + float radius = 8.0f; + float distanceExtra = 2.0f; + + if (distance < radius + distanceExtra) + { + return MoveAway(boss, radius + distanceExtra - distance); + } + + return false; +} + +bool VolkhanTargetAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "volkhan"); + if (!boss || AI_VALUE(Unit*, "current target") == boss) + { + return false; + } + + return Attack(boss); +} + +bool StaticOverloadSpreadAction::Execute(Event event) +{ + float radius = 8.0f; + float distanceExtra = 2.0f; + + GuidVector members = AI_VALUE(GuidVector, "group members"); + for (auto& member : members) + { + if (bot->GetGUID() == member) + { + continue; + } + + Unit* unit = botAI->GetUnit(member); + if (unit && unit->HasAura(SPELL_STATIC_OVERLOAD) + && bot->GetExactDist2d(unit) < radius) + { + return MoveAway(unit, radius + distanceExtra - bot->GetExactDist2d(unit)); + } + } + return false; +} + +bool BallLightningSpreadAction::Execute(Event event) +{ + float radius = 6.0f; + float distanceExtra = 1.0f; + + GuidVector members = AI_VALUE(GuidVector, "group members"); + for (auto& member : members) + { + if (bot->GetGUID() == member) + { + continue; + } + Unit* unit = botAI->GetUnit(member); + if (unit && bot->GetExactDist2d(unit) < radius + distanceExtra) + { + return MoveAway(unit, radius + distanceExtra - bot->GetExactDist2d(unit)); + } + } + return false; +} + +bool IonarTankPositionAction::isUseful() { return bot->GetExactDist2d(IONAR_TANK_POSITION) > 10.0f; } +bool IonarTankPositionAction::Execute(Event event) +{ + return MoveTo(bot->GetMapId(), IONAR_TANK_POSITION.GetPositionX(), IONAR_TANK_POSITION.GetPositionY(), IONAR_TANK_POSITION.GetPositionZ(), + false, false, false, true, MovementPriority::MOVEMENT_COMBAT); +} + +bool DispersePositionAction::isUseful() { return bot->GetExactDist2d(DISPERSE_POSITION) > 8.0f; } +bool DispersePositionAction::Execute(Event event) +{ + return MoveTo(bot->GetMapId(), DISPERSE_POSITION.GetPositionX(), DISPERSE_POSITION.GetPositionY(), DISPERSE_POSITION.GetPositionZ(), + false, false, false, true, MovementPriority::MOVEMENT_COMBAT); +} + +bool LokenStackAction::isUseful() +{ + // Minimum hunter range is 5, but values too close to this seem to cause issues.. + // Hunter bots will try and melee in between ranged attacks, or just melee entirely at 5 as they are in range. + // 6.5 or 7.0 solves this. + if(bot->getClass() == CLASS_HUNTER) + { + return AI_VALUE2(float, "distance", "current target") > 6.5f; + } + // else + return AI_VALUE2(float, "distance", "current target") > 2.0f; +} +bool LokenStackAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "loken"); + if (!boss) { return false; } + + if (!boss->HasUnitState(UNIT_STATE_CASTING)) + { + if(bot->getClass() == CLASS_HUNTER) + { + return Move(bot->GetAngle(boss), fmin(bot->GetExactDist2d(boss) - 6.5f, 10.0f)); + } + // else + return Move(bot->GetAngle(boss), fmin(bot->GetExactDist2d(boss), 10.0f)); + } + + return false; +} + + +bool AvoidLightningNovaAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "loken"); + if (!boss) { return false; } + + float distance = bot->GetExactDist2d(boss->GetPosition()); + float radius = 20.0f; + float distanceExtra = 2.0f; + + if (distance < radius + distanceExtra) + { + return MoveAway(boss, radius + distanceExtra - distance); + } + + return false; +} diff --git a/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningActions.h b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningActions.h new file mode 100644 index 00000000..fe81aa44 --- /dev/null +++ b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningActions.h @@ -0,0 +1,79 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONHOLACTIONS_H +#define _PLAYERBOT_WOTLKDUNGEONHOLACTIONS_H + +#include "Action.h" +#include "AttackAction.h" +#include "PlayerbotAI.h" +#include "Playerbots.h" +#include "HallsOfLightningTriggers.h" + +const Position IONAR_TANK_POSITION = Position(1078.860f, -261.928f, 61.226f); +const Position DISPERSE_POSITION = Position(1161.152f, -261.584f, 53.223f); + +class BjarngrimTargetAction : public AttackAction +{ +public: + BjarngrimTargetAction(PlayerbotAI* ai) : AttackAction(ai, "bjarngrim target") {} + bool Execute(Event event) override; +}; + +class AvoidWhirlwindAction : public MovementAction +{ +public: + AvoidWhirlwindAction(PlayerbotAI* ai) : MovementAction(ai, "avoid whirlwind") {} + bool Execute(Event event) override; +}; + +class VolkhanTargetAction : public AttackAction +{ +public: + VolkhanTargetAction(PlayerbotAI* ai) : AttackAction(ai, "volkhan target") {} + bool Execute(Event event) override; +}; + +class StaticOverloadSpreadAction : public MovementAction +{ +public: + StaticOverloadSpreadAction(PlayerbotAI* ai) : MovementAction(ai, "static overload spread") {} + bool Execute(Event event) override; +}; + +class BallLightningSpreadAction : public MovementAction +{ +public: + BallLightningSpreadAction(PlayerbotAI* ai) : MovementAction(ai, "ball lightning spread") {} + bool Execute(Event event) override; +}; + +class IonarTankPositionAction : public MovementAction +{ +public: + IonarTankPositionAction(PlayerbotAI* ai) : MovementAction(ai, "ionar tank position") {} + bool Execute(Event event) override; + bool isUseful() override; +}; + +class DispersePositionAction : public MovementAction +{ +public: + DispersePositionAction(PlayerbotAI* ai) : MovementAction(ai, "disperse position") {} + bool Execute(Event event) override; + bool isUseful() override; +}; + +class LokenStackAction : public MovementAction +{ +public: + LokenStackAction(PlayerbotAI* ai) : MovementAction(ai, "loken stack") {} + bool Execute(Event event) override; + bool isUseful() override; +}; + +class AvoidLightningNovaAction : public MovementAction +{ +public: + AvoidLightningNovaAction(PlayerbotAI* ai) : MovementAction(ai, "avoid lightning nova") {} + bool Execute(Event event) override; +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningMultipliers.cpp b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningMultipliers.cpp new file mode 100644 index 00000000..0250868d --- /dev/null +++ b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningMultipliers.cpp @@ -0,0 +1,88 @@ +#include "HallsOfLightningMultipliers.h" +#include "HallsOfLightningActions.h" +#include "GenericSpellActions.h" +#include "ChooseTargetActions.h" +#include "MovementActions.h" +#include "HallsOfLightningTriggers.h" +#include "Action.h" + +float BjarngrimMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "general bjarngrim"); + if (!boss || botAI->IsHeal(bot)) { return 1.0f; } + + if (boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_WHIRLWIND_BJARNGRIM)) + { + if (dynamic_cast(action) && !dynamic_cast(action)) + { + return 0.0f; + } + } + + Unit* boss_add = AI_VALUE2(Unit*, "find target", "stormforged lieutenant"); + if (!boss_add || botAI->IsTank(bot)) { return 1.0f; } + + if (dynamic_cast(action)) + { + return 0.0f; + } + + if (action->getThreatType() == Action::ActionThreatType::Aoe) + { + return 0.0f; + } + + return 1.0f; +} + +float VolkhanMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "volkhan"); + if (!boss || botAI->IsTank(bot) || botAI->IsHeal(bot)) { return 1.0f; } + + if (dynamic_cast(action)) + { + return 0.0f; + } + + if (action->getThreatType() == Action::ActionThreatType::Aoe) + { + return 0.0f; + } + + return 1.0f; +} + +float IonarMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "ionar"); + if (!boss) { return 1.0f; } + + if(!bot->CanSeeOrDetect(boss)) + { + if (dynamic_cast(action) + && !dynamic_cast(action) + && !dynamic_cast(action)) + { + return 0.0f; + } + } + return 1.0f; +} + +float LokenMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "loken"); + if (!boss) { return 1.0f; } + + if (dynamic_cast(action)) { return 0.0f; } + + if (boss->FindCurrentSpellBySpellId(SPELL_LIGHTNING_NOVA) + && dynamic_cast(action) + && !dynamic_cast(action)) + { + return 0.0f; + } + + return 1.0f; +} diff --git a/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningMultipliers.h b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningMultipliers.h new file mode 100644 index 00000000..8cc43967 --- /dev/null +++ b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningMultipliers.h @@ -0,0 +1,42 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONHOLMULTIPLIERS_H +#define _PLAYERBOT_WOTLKDUNGEONHOLMULTIPLIERS_H + +#include "Multiplier.h" + +class BjarngrimMultiplier : public Multiplier +{ + public: + BjarngrimMultiplier(PlayerbotAI* ai) : Multiplier(ai, "general bjarngrim") {} + + public: + virtual float GetValue(Action* action); +}; + +class VolkhanMultiplier : public Multiplier +{ + public: + VolkhanMultiplier(PlayerbotAI* ai) : Multiplier(ai, "volkhan") {} + + public: + virtual float GetValue(Action* action); +}; + +class IonarMultiplier : public Multiplier +{ + public: + IonarMultiplier(PlayerbotAI* ai) : Multiplier(ai, "ionar") {} + + public: + virtual float GetValue(Action* action); +}; + +class LokenMultiplier : public Multiplier +{ + public: + LokenMultiplier(PlayerbotAI* ai) : Multiplier(ai, "loken") {} + + public: + virtual float GetValue(Action* action); +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningStrategy.cpp b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningStrategy.cpp new file mode 100644 index 00000000..f438f17b --- /dev/null +++ b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningStrategy.cpp @@ -0,0 +1,41 @@ +#include "HallsOfLightningStrategy.h" +#include "HallsOfLightningMultipliers.h" + + +void WotlkDungeonHoLStrategy::InitTriggers(std::vector &triggers) +{ + // General Bjarngrim + triggers.push_back(new TriggerNode("stormforged lieutenant", + NextAction::array(0, new NextAction("bjarngrim target", ACTION_RAID + 5), nullptr))); + triggers.push_back(new TriggerNode("whirlwind", + NextAction::array(0, new NextAction("avoid whirlwind", ACTION_RAID + 4), nullptr))); + + // Volkhan + triggers.push_back(new TriggerNode("volkhan", + NextAction::array(0, new NextAction("volkhan target", ACTION_RAID + 5), nullptr))); + + // Ionar + triggers.push_back(new TriggerNode("ionar disperse", + NextAction::array(0, new NextAction("disperse position", ACTION_MOVE + 5), nullptr))); + triggers.push_back(new TriggerNode("ionar tank aggro", + NextAction::array(0, new NextAction("ionar tank position", ACTION_MOVE + 4), nullptr))); + triggers.push_back(new TriggerNode("static overload", + NextAction::array(0, new NextAction("static overload spread", ACTION_MOVE + 3), nullptr))); + // TODO: Targeted player can dodge the ball, but a single player soaking it isn't too bad to heal + triggers.push_back(new TriggerNode("ball lightning", + NextAction::array(0, new NextAction("ball lightning spread", ACTION_MOVE + 2), nullptr))); + + // Loken + triggers.push_back(new TriggerNode("lightning nova", + NextAction::array(0, new NextAction("avoid lightning nova", ACTION_MOVE + 5), nullptr))); + triggers.push_back(new TriggerNode("loken ranged", + NextAction::array(0, new NextAction("loken stack", ACTION_MOVE + 4), nullptr))); +} + +void WotlkDungeonHoLStrategy::InitMultipliers(std::vector &multipliers) +{ + multipliers.push_back(new BjarngrimMultiplier(botAI)); + multipliers.push_back(new VolkhanMultiplier(botAI)); + multipliers.push_back(new IonarMultiplier(botAI)); + multipliers.push_back(new LokenMultiplier(botAI)); +} diff --git a/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningStrategy.h b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningStrategy.h new file mode 100644 index 00000000..8ee265dd --- /dev/null +++ b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningStrategy.h @@ -0,0 +1,18 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONHOLSTRATEGY_H +#define _PLAYERBOT_WOTLKDUNGEONHOLSTRATEGY_H + +#include "Multiplier.h" +#include "AiObjectContext.h" +#include "Strategy.h" + + +class WotlkDungeonHoLStrategy : public Strategy +{ +public: + WotlkDungeonHoLStrategy(PlayerbotAI* ai) : Strategy(ai) {} + virtual std::string const getName() override { return "halls of lightning"; } + virtual void InitTriggers(std::vector &triggers) override; + virtual void InitMultipliers(std::vector &multipliers) override; +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningTriggerContext.h b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningTriggerContext.h new file mode 100644 index 00000000..5c8abc35 --- /dev/null +++ b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningTriggerContext.h @@ -0,0 +1,35 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONHOLTRIGGERCONTEXT_H +#define _PLAYERBOT_WOTLKDUNGEONHOLTRIGGERCONTEXT_H + +#include "NamedObjectContext.h" +#include "AiObjectContext.h" +#include "HallsOfLightningTriggers.h" + +class WotlkDungeonHoLTriggerContext : public NamedObjectContext +{ + public: + WotlkDungeonHoLTriggerContext() + { + creators["stormforged lieutenant"] = &WotlkDungeonHoLTriggerContext::stormforged_lieutenant; + creators["whirlwind"] = &WotlkDungeonHoLTriggerContext::bjarngrim_whirlwind; + creators["volkhan"] = &WotlkDungeonHoLTriggerContext::volkhan; + creators["static overload"] = &WotlkDungeonHoLTriggerContext::static_overload; + creators["ball lightning"] = &WotlkDungeonHoLTriggerContext::ball_lightning; + creators["ionar tank aggro"] = &WotlkDungeonHoLTriggerContext::ionar_tank_aggro; + creators["ionar disperse"] = &WotlkDungeonHoLTriggerContext::ionar_disperse; + creators["loken ranged"] = &WotlkDungeonHoLTriggerContext::loken_ranged; + creators["lightning nova"] = &WotlkDungeonHoLTriggerContext::lightning_nova; + } + private: + static Trigger* stormforged_lieutenant(PlayerbotAI* ai) { return new StormforgedLieutenantTrigger(ai); } + static Trigger* bjarngrim_whirlwind(PlayerbotAI* ai) { return new BjarngrimWhirlwindTrigger(ai); } + static Trigger* volkhan(PlayerbotAI* ai) { return new VolkhanTrigger(ai); } + static Trigger* static_overload(PlayerbotAI* ai) { return new IonarStaticOverloadTrigger(ai); } + static Trigger* ball_lightning(PlayerbotAI* ai) { return new IonarBallLightningTrigger(ai); } + static Trigger* ionar_tank_aggro(PlayerbotAI* ai) { return new IonarTankAggroTrigger(ai); } + static Trigger* ionar_disperse(PlayerbotAI* ai) { return new IonarDisperseTrigger(ai); } + static Trigger* loken_ranged(PlayerbotAI* ai) { return new LokenRangedTrigger(ai); } + static Trigger* lightning_nova(PlayerbotAI* ai) { return new LokenLightningNovaTrigger(ai); } +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningTriggers.cpp b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningTriggers.cpp new file mode 100644 index 00000000..6350acc5 --- /dev/null +++ b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningTriggers.cpp @@ -0,0 +1,92 @@ +#include "Playerbots.h" +#include "HallsOfLightningTriggers.h" +#include "AiObject.h" +#include "AiObjectContext.h" + +bool StormforgedLieutenantTrigger::IsActive() +{ + if (botAI->IsTank(bot) || botAI->IsHeal(bot)) { return false; } + + // Target is not findable from threat table using AI_VALUE2(), + // therefore need to search manually for the unit name + GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); + + for (auto i = targets.begin(); i != targets.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (unit && unit->GetEntry() == NPC_STORMFORGED_LIEUTENANT) + { + return true; + } + } + return false; +} + +bool BjarngrimWhirlwindTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "general bjarngrim"); + if (!boss) { return false; } + + return boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_WHIRLWIND_BJARNGRIM); +} + +bool VolkhanTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "volkhan"); + return boss && !botAI->IsTank(bot) && !botAI->IsHeal(bot); +} + +bool IonarStaticOverloadTrigger::IsActive() +{ + GuidVector members = AI_VALUE(GuidVector, "group members"); + for (auto& member : members) + { + Unit* unit = botAI->GetUnit(member); + if (unit && unit->HasAura(SPELL_STATIC_OVERLOAD)) + { + return true; + } + } + return false; +} + +bool IonarBallLightningTrigger::IsActive() +{ + if (botAI->IsMelee(bot)) { return false; } + + Unit* boss = AI_VALUE2(Unit*, "find target", "ionar"); + if (!boss) { return false; } + + return boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_BALL_LIGHTNING); +} + +bool IonarTankAggroTrigger::IsActive() +{ + if (!botAI->IsTank(bot)) { return false; } + + Unit* boss = AI_VALUE2(Unit*, "find target", "ionar"); + if (!boss) { return false; } + + return AI_VALUE2(bool, "has aggro", "current target"); +} + +bool IonarDisperseTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "ionar"); + if (!boss) { return false; } + + return !bot->CanSeeOrDetect(boss) || boss->FindCurrentSpellBySpellId(SPELL_DISPERSE); +} + +bool LokenRangedTrigger::IsActive() +{ + return !botAI->IsMelee(bot) && AI_VALUE2(Unit*, "find target", "loken"); +} + +bool LokenLightningNovaTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "loken"); + if (!boss) { return false; } + + return boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_LIGHTNING_NOVA); +} diff --git a/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningTriggers.h b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningTriggers.h new file mode 100644 index 00000000..7dbbc461 --- /dev/null +++ b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningTriggers.h @@ -0,0 +1,95 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONHOLTRIGGERS_H +#define _PLAYERBOT_WOTLKDUNGEONHOLTRIGGERS_H + +#include "Trigger.h" +#include "PlayerbotAIConfig.h" +#include "GenericTriggers.h" +#include "DungeonStrategyUtils.h" + +enum HallsOfLightningIDs +{ + // General Bjarngrim + NPC_STORMFORGED_LIEUTENANT = 29240, + SPELL_WHIRLWIND_BJARNGRIM = 52027, + + // Ionar + SPELL_STATIC_OVERLOAD_N = 52658, + SPELL_STATIC_OVERLOAD_H = 59795, + SPELL_BALL_LIGHTNING_N = 52780, + SPELL_BALL_LIGHTNING_H = 59800, + SPELL_DISPERSE = 52770, + NPC_SPARK_OF_IONAR = 28926, + + // Loken + SPELL_LIGHTNING_NOVA_N = 52960, + SPELL_LIGHTNING_NOVA_H = 59835, +}; + +#define SPELL_STATIC_OVERLOAD DUNGEON_MODE(bot, SPELL_STATIC_OVERLOAD_N, SPELL_STATIC_OVERLOAD_H) +#define SPELL_BALL_LIGHTNING DUNGEON_MODE(bot, SPELL_BALL_LIGHTNING_N, SPELL_BALL_LIGHTNING_H) +#define SPELL_LIGHTNING_NOVA DUNGEON_MODE(bot, SPELL_LIGHTNING_NOVA_N, SPELL_LIGHTNING_NOVA_H) + +class StormforgedLieutenantTrigger : public Trigger +{ +public: + StormforgedLieutenantTrigger(PlayerbotAI* ai) : Trigger(ai, "stormforged lieutenant") {} + bool IsActive() override; +}; + +class BjarngrimWhirlwindTrigger : public Trigger +{ +public: + BjarngrimWhirlwindTrigger(PlayerbotAI* ai) : Trigger(ai, "bjarngrim whirlwind") {} + bool IsActive() override; +}; + +class VolkhanTrigger : public Trigger +{ +public: + VolkhanTrigger(PlayerbotAI* ai) : Trigger(ai, "volkhan") {} + bool IsActive() override; +}; + +class IonarStaticOverloadTrigger : public Trigger +{ +public: + IonarStaticOverloadTrigger(PlayerbotAI* ai) : Trigger(ai, "ionar static overload") {} + bool IsActive() override; +}; + +class IonarBallLightningTrigger : public Trigger +{ +public: + IonarBallLightningTrigger(PlayerbotAI* ai) : Trigger(ai, "ionar ball lightning spread") {} + bool IsActive() override; +}; + +class IonarTankAggroTrigger : public Trigger +{ +public: + IonarTankAggroTrigger(PlayerbotAI* ai) : Trigger(ai, "ionar tank aggro") {} + bool IsActive() override; +}; + +class IonarDisperseTrigger : public Trigger +{ +public: + IonarDisperseTrigger(PlayerbotAI* ai) : Trigger(ai, "ionar disperse") {} + bool IsActive() override; +}; + +class LokenRangedTrigger : public Trigger +{ +public: + LokenRangedTrigger(PlayerbotAI* ai) : Trigger(ai, "loken ranged") {} + bool IsActive() override; +}; + +class LokenLightningNovaTrigger : public Trigger +{ +public: + LokenLightningNovaTrigger(PlayerbotAI* ai) : Trigger(ai, "lightning nova") {} + bool IsActive() override; +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/hallsoflightning/TODO b/src/strategy/dungeons/wotlk/hallsoflightning/TODO deleted file mode 100644 index e69de29b..00000000 From 66b7a3852d5b23a1068a2d51e3efd69f1db85b6f Mon Sep 17 00:00:00 2001 From: Bobblybook Date: Fri, 18 Oct 2024 20:57:48 +1100 Subject: [PATCH 03/17] HoL updates --- .../HallsOfLightningActions.cpp | 9 ++++++++- .../HallsOfLightningMultipliers.cpp | 17 ++++++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningActions.cpp b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningActions.cpp index 6ca64dd4..ac469e95 100644 --- a/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningActions.cpp +++ b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningActions.cpp @@ -19,8 +19,15 @@ bool BjarngrimTargetAction::Execute(Event event) break; } } + + Unit* currentTarget = AI_VALUE(Unit*, "current target"); // There are two, we don't want to ping-pong between them if we're attacking one already - if (!target || AI_VALUE(Unit*, "current target") == target) + if (target && currentTarget && currentTarget->GetEntry() == NPC_STORMFORGED_LIEUTENANT) + { + return false; + } + + if (AI_VALUE(Unit*, "current target") == target) { return false; } diff --git a/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningMultipliers.cpp b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningMultipliers.cpp index 0250868d..0ecb2e56 100644 --- a/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningMultipliers.cpp +++ b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningMultipliers.cpp @@ -19,7 +19,22 @@ float BjarngrimMultiplier::GetValue(Action* action) } } - Unit* boss_add = AI_VALUE2(Unit*, "find target", "stormforged lieutenant"); + // Detect boss adds this way as sometimes they don't get added to threat table on dps bots, + // and some dps just stand at range and don't engage the boss at all as they can't find the adds + // Unit* boss_add = AI_VALUE2(Unit*, "find target", "stormforged lieutenant"); + Unit* boss_add = nullptr; + GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); + + for (auto i = targets.begin(); i != targets.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (unit && unit->GetEntry() == NPC_STORMFORGED_LIEUTENANT) + { + boss_add = unit; + break; + } + } + if (!boss_add || botAI->IsTank(bot)) { return 1.0f; } if (dynamic_cast(action)) From 65b6e15ea1ff87db9e50463f93d74c4d6de340d3 Mon Sep 17 00:00:00 2001 From: EricksOliveira Date: Fri, 18 Oct 2024 09:27:46 -0300 Subject: [PATCH 04/17] Fix Crash - OnBotLogin 1. Improvements in error handling: Added detailed logs for cases where botAI or master are null, allowing better failure tracking. 2. Additional null pointer checks: Added checks to ensure that the botAI and master are valid before performing actions dependent on these objects, preventing potential crashes. 3. Optimization in bot login logic: Revised the bot input flow to ensure it is added to the group appropriately depending on its relationship to the master. Added logic for handling different types of groups (raid, LFG, etc.), including the possibility of automatic conversion to raid if necessary. --- src/PlayerbotMgr.cpp | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/src/PlayerbotMgr.cpp b/src/PlayerbotMgr.cpp index 34e107d1..3cae1645 100644 --- a/src/PlayerbotMgr.cpp +++ b/src/PlayerbotMgr.cpp @@ -100,6 +100,8 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con Player* bot = botSession->GetPlayer(); if (!bot) { + // Log para debug + LOG_ERROR("mod-playerbots", "Bot player could not be loaded for account ID: {}", botAccountId); botSession->LogoutPlayer(true); delete botSession; botLoading.erase(holder.GetGuid()); @@ -108,6 +110,14 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con uint32 masterAccount = holder.GetMasterAccountId(); WorldSession* masterSession = masterAccount ? sWorld->FindSession(masterAccount) : nullptr; + + // Check if masterSession->GetPlayer() is valid + Player* masterPlayer = masterSession ? masterSession->GetPlayer() : nullptr; + if (masterSession && !masterPlayer) + { + LOG_ERROR("mod-playerbots", "Master session found but no player is associated for master account ID: {}", masterAccount); + } + std::ostringstream out; bool allowed = false; if (botAccountId == masterAccount) @@ -115,7 +125,7 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con allowed = true; } else if (masterSession && sPlayerbotAIConfig->allowGuildBots && bot->GetGuildId() != 0 && - bot->GetGuildId() == masterSession->GetPlayer()->GetGuildId()) + bot->GetGuildId() == masterPlayer->GetGuildId()) { allowed = true; } @@ -129,10 +139,14 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con out << "Failure: You are not allowed to control bot " << bot->GetName().c_str(); } - if (allowed && masterSession) + if (allowed && masterSession && masterPlayer) { - Player* player = masterSession->GetPlayer(); - PlayerbotMgr* mgr = GET_PLAYERBOT_MGR(player); + PlayerbotMgr* mgr = GET_PLAYERBOT_MGR(masterPlayer); + if (!mgr) + { + LOG_ERROR("mod-playerbots", "PlayerbotMgr not found for master player with GUID: {}", masterPlayer->GetGUID().GetRawValue()); + } + uint32 count = mgr->GetPlayerbotsCount(); uint32 cls_count = mgr->GetPlayerbotsCountByClass(bot->getClass()); if (count >= sPlayerbotAIConfig->maxAddedBots) @@ -428,14 +442,17 @@ void PlayerbotHolder::OnBotLogin(Player* const bot) PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); if (!botAI) { + // Log a warning here to indicate that the botAI is null + LOG_ERROR("mod-playerbots", "PlayerbotAI is null for bot with GUID: {}", bot->GetGUID().GetRawValue()); return; } + Player* master = botAI->GetMaster(); - if (master) + if (!master) { - ObjectGuid masterGuid = master->GetGUID(); - if (master->GetGroup() && !master->GetGroup()->IsLeader(masterGuid)) - master->GetGroup()->ChangeLeader(masterGuid); + // Log a warning to indicate that the master is null + LOG_ERROR("mod-playerbots", "Master is null for bot with GUID: {}", bot->GetGUID().GetRawValue()); + return; } Group* group = bot->GetGroup(); From 8f2455b7663c935fb33d3552bb1371390d1106f8 Mon Sep 17 00:00:00 2001 From: EricksOliveira Date: Fri, 18 Oct 2024 14:37:54 -0300 Subject: [PATCH 05/17] Update PlayerbotCommandServer.cpp Protection against race conditions: Added a global mutex (session_mutex) to synchronize access to the session() function when multiple threads are running. This avoids concurrency problems in environments with many simultaneous connections. Thread pool usage: Replaced manual thread creation with boost::thread with a thread pool using boost::asio::thread_pool. The thread pool improves resource management and limits the number of simultaneous threads, optimizing performance. Checking for socket errors: Added check to ensure the socket is not null or closed before trying to read it. Implemented detailed error handling for readings and writings, including detailed logs to facilitate debugging. Secure socket closure: Now sockets are correctly closed at the end of the session, even in the event of an error, preventing resource leaks. --- src/PlayerbotCommandServer.cpp | 52 ++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/src/PlayerbotCommandServer.cpp b/src/PlayerbotCommandServer.cpp index 482c2348..c08f1d05 100644 --- a/src/PlayerbotCommandServer.cpp +++ b/src/PlayerbotCommandServer.cpp @@ -9,8 +9,10 @@ #include #include #include +#include #include #include +#include #include "IoContext.h" #include "Playerbots.h" @@ -18,21 +20,39 @@ using boost::asio::ip::tcp; typedef boost::shared_ptr socket_ptr; +std::mutex session_mutex; // Global mutex to protect sessions +boost::asio::thread_pool pool(4); // 4-thread thread pool + bool ReadLine(socket_ptr sock, std::string* buffer, std::string* line) { - // Do the real reading from fd until buffer has '\n'. + // Check if the socket is valid before using it + if (!sock || !sock->is_open()) + { + LOG_ERROR("playerbots", "Invalid or closed socket."); + return false; + } + + // Does the actual reading until the buffer has a '\n' std::string::iterator pos; while ((pos = find(buffer->begin(), buffer->end(), '\n')) == buffer->end()) { char buf[1025]; boost::system::error_code error; - size_t n = sock->read_some(boost::asio::buffer(buf), error); - if (n == -1 || error == boost::asio::error::eof) - return false; - else if (error) - throw boost::system::system_error(error); // Some other error. - buf[n] = 0; + // Read socket data + size_t n = sock->read_some(boost::asio::buffer(buf), error); + if (n == 0 || error == boost::asio::error::eof) + { + LOG_INFO("playerbots", "Connection closed by peer."); + return false; + } + else if (error) + { + LOG_ERROR("playerbots", "Socket read error: {}", error.message()); + return false; // Returns false in case of error. + } + + buf[n] = 0; // Ensures the buffer ends with null *buffer += buf; } @@ -45,6 +65,8 @@ void session(socket_ptr sock) { try { + std::lock_guard guard(session_mutex); // Protect session with mutex + std::string buffer, request; while (ReadLine(sock, &buffer, &request)) { @@ -55,7 +77,19 @@ void session(socket_ptr sock) } catch (std::exception& e) { - LOG_ERROR("playerbots", "{}", e.what()); + LOG_ERROR("playerbots", "Session error: {}", e.what()); + } + + // Make sure to close the socket at the end of the session + if (sock && sock->is_open()) + { + boost::system::error_code ec; + sock->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); + sock->close(ec); + if (ec) + { + LOG_ERROR("playerbots", "Error closing socket: {}", ec.message()); + } } } @@ -66,7 +100,7 @@ void server(Acore::Asio::IoContext& io_service, short port) { socket_ptr sock(new tcp::socket(io_service)); a.accept(*sock); - boost::thread t(boost::bind(session, sock)); + boost::asio::post(pool, boost::bind(session, sock)); // Use thread pool instead of creating new threads } } From eabad44d4bcc45a255fb7bdda409e6f7a1a47e64 Mon Sep 17 00:00:00 2001 From: EricksOliveira Date: Fri, 18 Oct 2024 14:40:25 -0300 Subject: [PATCH 06/17] Revert "Update PlayerbotCommandServer.cpp" This reverts commit 8f2455b7663c935fb33d3552bb1371390d1106f8. --- src/PlayerbotCommandServer.cpp | 46 +++++----------------------------- 1 file changed, 6 insertions(+), 40 deletions(-) diff --git a/src/PlayerbotCommandServer.cpp b/src/PlayerbotCommandServer.cpp index c08f1d05..482c2348 100644 --- a/src/PlayerbotCommandServer.cpp +++ b/src/PlayerbotCommandServer.cpp @@ -9,10 +9,8 @@ #include #include #include -#include #include #include -#include #include "IoContext.h" #include "Playerbots.h" @@ -20,39 +18,21 @@ using boost::asio::ip::tcp; typedef boost::shared_ptr socket_ptr; -std::mutex session_mutex; // Global mutex to protect sessions -boost::asio::thread_pool pool(4); // 4-thread thread pool - bool ReadLine(socket_ptr sock, std::string* buffer, std::string* line) { - // Check if the socket is valid before using it - if (!sock || !sock->is_open()) - { - LOG_ERROR("playerbots", "Invalid or closed socket."); - return false; - } - - // Does the actual reading until the buffer has a '\n' + // Do the real reading from fd until buffer has '\n'. std::string::iterator pos; while ((pos = find(buffer->begin(), buffer->end(), '\n')) == buffer->end()) { char buf[1025]; boost::system::error_code error; - - // Read socket data size_t n = sock->read_some(boost::asio::buffer(buf), error); - if (n == 0 || error == boost::asio::error::eof) - { - LOG_INFO("playerbots", "Connection closed by peer."); + if (n == -1 || error == boost::asio::error::eof) return false; - } else if (error) - { - LOG_ERROR("playerbots", "Socket read error: {}", error.message()); - return false; // Returns false in case of error. - } + throw boost::system::system_error(error); // Some other error. - buf[n] = 0; // Ensures the buffer ends with null + buf[n] = 0; *buffer += buf; } @@ -65,8 +45,6 @@ void session(socket_ptr sock) { try { - std::lock_guard guard(session_mutex); // Protect session with mutex - std::string buffer, request; while (ReadLine(sock, &buffer, &request)) { @@ -77,19 +55,7 @@ void session(socket_ptr sock) } catch (std::exception& e) { - LOG_ERROR("playerbots", "Session error: {}", e.what()); - } - - // Make sure to close the socket at the end of the session - if (sock && sock->is_open()) - { - boost::system::error_code ec; - sock->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); - sock->close(ec); - if (ec) - { - LOG_ERROR("playerbots", "Error closing socket: {}", ec.message()); - } + LOG_ERROR("playerbots", "{}", e.what()); } } @@ -100,7 +66,7 @@ void server(Acore::Asio::IoContext& io_service, short port) { socket_ptr sock(new tcp::socket(io_service)); a.accept(*sock); - boost::asio::post(pool, boost::bind(session, sock)); // Use thread pool instead of creating new threads + boost::thread t(boost::bind(session, sock)); } } From c313cdfa8eb6f6150e6b2df8a557ab2d46cbf54e Mon Sep 17 00:00:00 2001 From: Revision Date: Sat, 19 Oct 2024 03:10:46 +0200 Subject: [PATCH 07/17] Move tables into their own files --- ...layerbots_text.sql => ai_playerbot_texts.sql} | 16 ---------------- .../base/ai_playerbot_texts_chance.sql | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 16 deletions(-) rename sql/playerbots/base/{playerbots_text.sql => ai_playerbot_texts.sql} (99%) create mode 100644 sql/playerbots/base/ai_playerbot_texts_chance.sql diff --git a/sql/playerbots/base/playerbots_text.sql b/sql/playerbots/base/ai_playerbot_texts.sql similarity index 99% rename from sql/playerbots/base/playerbots_text.sql rename to sql/playerbots/base/ai_playerbot_texts.sql index 881e4671..718db731 100644 --- a/sql/playerbots/base/playerbots_text.sql +++ b/sql/playerbots/base/ai_playerbot_texts.sql @@ -1457,19 +1457,3 @@ INSERT INTO `ai_playerbot_texts` (`name`, `text`, `say_type`, `reply_type`, `tex ('dummy_end', 'dummy', 0, 0, '', '', '', '', '', '', '', ''); - -DROP TABLE IF EXISTS `ai_playerbot_texts_chance`; - -CREATE TABLE IF NOT EXISTS `ai_playerbot_texts_chance` ( - `id` bigint(20) NOT NULL AUTO_INCREMENT, - `name` varchar(255) NOT NULL, - `probability` bigint(20) NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=UTF8; - -/*!40000 ALTER TABLE `ai_playerbot_texts_chance` DISABLE KEYS */; -INSERT INTO `ai_playerbot_texts_chance` (`id`, `name`, `probability`) VALUES - (1, 'taunt', 30), - (2, 'aoe', 75), - (3, 'loot', 20); -/*!40000 ALTER TABLE `ai_playerbot_texts_chance` ENABLE KEYS */; \ No newline at end of file diff --git a/sql/playerbots/base/ai_playerbot_texts_chance.sql b/sql/playerbots/base/ai_playerbot_texts_chance.sql new file mode 100644 index 00000000..81218dba --- /dev/null +++ b/sql/playerbots/base/ai_playerbot_texts_chance.sql @@ -0,0 +1,14 @@ +DROP TABLE IF EXISTS `ai_playerbot_texts_chance`; +CREATE TABLE IF NOT EXISTS `ai_playerbot_texts_chance` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL, + `probability` bigint(20) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=UTF8; + +/*!40000 ALTER TABLE `ai_playerbot_texts_chance` DISABLE KEYS */; +INSERT INTO `ai_playerbot_texts_chance` (`id`, `name`, `probability`) VALUES + (1, 'taunt', 30), + (2, 'aoe', 75), + (3, 'loot', 20); +/*!40000 ALTER TABLE `ai_playerbot_texts_chance` ENABLE KEYS */; From 8d7a7aed2acf1c99409ecc4c5571ed0ebf2ca1cb Mon Sep 17 00:00:00 2001 From: Revision Date: Sat, 19 Oct 2024 03:11:40 +0200 Subject: [PATCH 08/17] Fix a typo --- sql/playerbots/base/ai_playerbot_texts.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/playerbots/base/ai_playerbot_texts.sql b/sql/playerbots/base/ai_playerbot_texts.sql index 718db731..e1c49907 100644 --- a/sql/playerbots/base/ai_playerbot_texts.sql +++ b/sql/playerbots/base/ai_playerbot_texts.sql @@ -464,7 +464,7 @@ INSERT INTO `ai_playerbot_texts` (`name`, `text`, `say_type`, `reply_type`, `tex -- %my_level ('suggest_something', 'Wanna party in %zone_name.', 0, 0, '', 'Je veux faire la fête dans %zone_name.', '', '', '', '¡Vamos a perrear a %zone_name!', '', 'Ищу группу в %zone_name.'), ('suggest_something', 'Anyone is looking for %my_role?', 0, 0, '', 'Quelqu\'un cherche un %my_role ?', '', '', '', '¿Alguien está buscando %my_role?', '', 'Кто-нибудь ищет %my_role?'), -('suggest_something', '%my_role is looking for quild.', 0, 0, '', '%my_role recherche une guilde.', '', '', '', '%my_role está buscando hermandad.', '', '%my_role ищу гильдию.'), +('suggest_something', '%my_role is looking for guild.', 0, 0, '', '%my_role recherche une guilde.', '', '', '', '%my_role está buscando hermandad.', '', '%my_role ищу гильдию.'), ('suggest_something', 'Looking for gold.', 0, 0, '', 'A la recherche de l\'or.', '', '', '', 'Buscando oro.', '', 'Дайте голды'), ('suggest_something', '%my_role wants to join a good guild.', 0, 0, '', '%my_role veut rejoindre une bonne guilde.', '', '', '', '%my_role quiere unirse a una buen hermandad.', '', '%my_role хочу в хорошую гильдию.'), ('suggest_something', 'Need a friend.', 0, 0, '', 'Besoin d\'un ami.', '', '', '', 'Necesito un amigo...', '', 'Ищу друга.'), From 11654425058bcddad0036cc96785556720ceaab5 Mon Sep 17 00:00:00 2001 From: Bobblybook Date: Sat, 19 Oct 2024 18:16:44 +1100 Subject: [PATCH 09/17] Winged Steed of the Ebon Blade northrend fix Winged Steed of the Ebon Blade gets incorrectly categorised as a ground mount, causes issues and bots will not be able to ground mount if they have this mount in the account spellbook. There may by other scaling mounts that have the same issue, either we blacklist them all or figure out a way to correctly read them as flyers. --- src/strategy/actions/CheckMountStateAction.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/strategy/actions/CheckMountStateAction.cpp b/src/strategy/actions/CheckMountStateAction.cpp index ce223d16..0300827b 100644 --- a/src/strategy/actions/CheckMountStateAction.cpp +++ b/src/strategy/actions/CheckMountStateAction.cpp @@ -239,9 +239,16 @@ bool CheckMountStateAction::Mount() // continue; uint32 index = (spellInfo->Effects[1].ApplyAuraName == SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED || - spellInfo->Effects[2].ApplyAuraName == SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) - ? 1 - : 0; + spellInfo->Effects[2].ApplyAuraName == SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED || + // Winged Steed of the Ebon Blade + // This mount is meant to autoscale from a 150% flyer + // up to a 280% as you train your flying skill up. + // This incorrectly gets categorised as a ground mount, force this to flyer only. + // TODO: Add other scaling mounts here if they have the same issue, or adjust above + // checks so that they are all correctly detected. + spellInfo->Id == 54729) + ? 1 // Flying Mount + : 0; // Ground Mount if (index == 0 && std::max(spellInfo->Effects[EFFECT_1].BasePoints, spellInfo->Effects[EFFECT_2].BasePoints) > 59) From 312c661311f6a4d25feb386323509fa7889e0596 Mon Sep 17 00:00:00 2001 From: Bobblybook Date: Sat, 19 Oct 2024 22:40:26 +1100 Subject: [PATCH 10/17] Preferred mount selection for bots Added a new table to store user-specified mount ids. Can specify flying and ground mounts for a character which will prioritise that mount. If multiple entries, a random entry will be selected from that list. Currently no way to add entries other than manual DB editing, but can be developed into a whisper command pretty easily. This should gracefully fail (if no entries exist for the character, or the db table does not exist) and fallback to the default random selection as usual. --- .../base/playerbots_preferred_mounts.sql | 10 ++++++ .../actions/CheckMountStateAction.cpp | 36 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 sql/playerbots/base/playerbots_preferred_mounts.sql diff --git a/sql/playerbots/base/playerbots_preferred_mounts.sql b/sql/playerbots/base/playerbots_preferred_mounts.sql new file mode 100644 index 00000000..6d904fb3 --- /dev/null +++ b/sql/playerbots/base/playerbots_preferred_mounts.sql @@ -0,0 +1,10 @@ +DROP TABLE IF EXISTS `playerbots_preferred_mounts`; +CREATE TABLE `playerbots_preferred_mounts` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `guid` INT(11) NOT NULL, + `type` TINYINT(3) NOT NULL COMMENT '0: Ground, 1: Flying', + `spellid` INT(11) NOT NULL, + PRIMARY KEY (`id`), + KEY `guid` (`guid`), + KEY `type` (`type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/src/strategy/actions/CheckMountStateAction.cpp b/src/strategy/actions/CheckMountStateAction.cpp index 0300827b..7ae52f74 100644 --- a/src/strategy/actions/CheckMountStateAction.cpp +++ b/src/strategy/actions/CheckMountStateAction.cpp @@ -270,6 +270,42 @@ bool CheckMountStateAction::Mount() : 0; } + // Check for preferred mounts table in db + QueryResult checkTable = PlayerbotsDatabase.Query( + "SELECT EXISTS(SELECT * FROM information_schema.tables WHERE table_schema = 'acore_playerbots' AND table_name = 'playerbots_preferred_mounts')"); + + if (checkTable) + { + uint32 tableExists = checkTable->Fetch()[0].Get(); + if (tableExists == 1) + { + // Check for preferred mount entry + QueryResult result = PlayerbotsDatabase.Query( + "SELECT spellid FROM playerbots_preferred_mounts WHERE guid = {} AND type = {}", + bot->GetGUID().GetCounter(), masterMountType); + + if (result) + { + std::vector mounts; + do + { + Field* fields = result->Fetch(); + uint32 spellId = fields[0].Get(); + mounts.push_back(spellId); + } while (result->NextRow()); + + uint32 index = urand(0, mounts.size() - 1); + // Validate spell ID + if (index < mounts.size() && sSpellMgr->GetSpellInfo(mounts[index])) + { + // TODO: May want to do checks for 'bot riding skill > skill required to ride the mount' + return botAI->CastSpell(mounts[index], bot); + } + } + } + } + + // No preferred mount found (or invalid), continue with random mount selection std::map>& spells = allSpells[masterMountType]; if (hasSwiftMount) { From eb5dd450cdcf3de57805f221d95faf8d4d8d5f89 Mon Sep 17 00:00:00 2001 From: Bobblybook Date: Sat, 19 Oct 2024 22:42:21 +1100 Subject: [PATCH 11/17] Revert "Preferred mount selection for bots" This reverts commit 312c661311f6a4d25feb386323509fa7889e0596. --- .../base/playerbots_preferred_mounts.sql | 10 ------ .../actions/CheckMountStateAction.cpp | 36 ------------------- 2 files changed, 46 deletions(-) delete mode 100644 sql/playerbots/base/playerbots_preferred_mounts.sql diff --git a/sql/playerbots/base/playerbots_preferred_mounts.sql b/sql/playerbots/base/playerbots_preferred_mounts.sql deleted file mode 100644 index 6d904fb3..00000000 --- a/sql/playerbots/base/playerbots_preferred_mounts.sql +++ /dev/null @@ -1,10 +0,0 @@ -DROP TABLE IF EXISTS `playerbots_preferred_mounts`; -CREATE TABLE `playerbots_preferred_mounts` ( - `id` INT(11) NOT NULL AUTO_INCREMENT, - `guid` INT(11) NOT NULL, - `type` TINYINT(3) NOT NULL COMMENT '0: Ground, 1: Flying', - `spellid` INT(11) NOT NULL, - PRIMARY KEY (`id`), - KEY `guid` (`guid`), - KEY `type` (`type`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/src/strategy/actions/CheckMountStateAction.cpp b/src/strategy/actions/CheckMountStateAction.cpp index 7ae52f74..0300827b 100644 --- a/src/strategy/actions/CheckMountStateAction.cpp +++ b/src/strategy/actions/CheckMountStateAction.cpp @@ -270,42 +270,6 @@ bool CheckMountStateAction::Mount() : 0; } - // Check for preferred mounts table in db - QueryResult checkTable = PlayerbotsDatabase.Query( - "SELECT EXISTS(SELECT * FROM information_schema.tables WHERE table_schema = 'acore_playerbots' AND table_name = 'playerbots_preferred_mounts')"); - - if (checkTable) - { - uint32 tableExists = checkTable->Fetch()[0].Get(); - if (tableExists == 1) - { - // Check for preferred mount entry - QueryResult result = PlayerbotsDatabase.Query( - "SELECT spellid FROM playerbots_preferred_mounts WHERE guid = {} AND type = {}", - bot->GetGUID().GetCounter(), masterMountType); - - if (result) - { - std::vector mounts; - do - { - Field* fields = result->Fetch(); - uint32 spellId = fields[0].Get(); - mounts.push_back(spellId); - } while (result->NextRow()); - - uint32 index = urand(0, mounts.size() - 1); - // Validate spell ID - if (index < mounts.size() && sSpellMgr->GetSpellInfo(mounts[index])) - { - // TODO: May want to do checks for 'bot riding skill > skill required to ride the mount' - return botAI->CastSpell(mounts[index], bot); - } - } - } - } - - // No preferred mount found (or invalid), continue with random mount selection std::map>& spells = allSpells[masterMountType]; if (hasSwiftMount) { From a430786133a4846eff6d4e1fa9fa7fa2b40e3390 Mon Sep 17 00:00:00 2001 From: Bobblybook Date: Sat, 19 Oct 2024 22:44:11 +1100 Subject: [PATCH 12/17] Reapply "Preferred mount selection for bots" This reverts commit eb5dd450cdcf3de57805f221d95faf8d4d8d5f89. --- .../base/playerbots_preferred_mounts.sql | 10 ++++++ .../actions/CheckMountStateAction.cpp | 36 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 sql/playerbots/base/playerbots_preferred_mounts.sql diff --git a/sql/playerbots/base/playerbots_preferred_mounts.sql b/sql/playerbots/base/playerbots_preferred_mounts.sql new file mode 100644 index 00000000..6d904fb3 --- /dev/null +++ b/sql/playerbots/base/playerbots_preferred_mounts.sql @@ -0,0 +1,10 @@ +DROP TABLE IF EXISTS `playerbots_preferred_mounts`; +CREATE TABLE `playerbots_preferred_mounts` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `guid` INT(11) NOT NULL, + `type` TINYINT(3) NOT NULL COMMENT '0: Ground, 1: Flying', + `spellid` INT(11) NOT NULL, + PRIMARY KEY (`id`), + KEY `guid` (`guid`), + KEY `type` (`type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/src/strategy/actions/CheckMountStateAction.cpp b/src/strategy/actions/CheckMountStateAction.cpp index 0300827b..7ae52f74 100644 --- a/src/strategy/actions/CheckMountStateAction.cpp +++ b/src/strategy/actions/CheckMountStateAction.cpp @@ -270,6 +270,42 @@ bool CheckMountStateAction::Mount() : 0; } + // Check for preferred mounts table in db + QueryResult checkTable = PlayerbotsDatabase.Query( + "SELECT EXISTS(SELECT * FROM information_schema.tables WHERE table_schema = 'acore_playerbots' AND table_name = 'playerbots_preferred_mounts')"); + + if (checkTable) + { + uint32 tableExists = checkTable->Fetch()[0].Get(); + if (tableExists == 1) + { + // Check for preferred mount entry + QueryResult result = PlayerbotsDatabase.Query( + "SELECT spellid FROM playerbots_preferred_mounts WHERE guid = {} AND type = {}", + bot->GetGUID().GetCounter(), masterMountType); + + if (result) + { + std::vector mounts; + do + { + Field* fields = result->Fetch(); + uint32 spellId = fields[0].Get(); + mounts.push_back(spellId); + } while (result->NextRow()); + + uint32 index = urand(0, mounts.size() - 1); + // Validate spell ID + if (index < mounts.size() && sSpellMgr->GetSpellInfo(mounts[index])) + { + // TODO: May want to do checks for 'bot riding skill > skill required to ride the mount' + return botAI->CastSpell(mounts[index], bot); + } + } + } + } + + // No preferred mount found (or invalid), continue with random mount selection std::map>& spells = allSpells[masterMountType]; if (hasSwiftMount) { From 72debb6e8365a8d47c4ac2ebd1a9b5a5b619f16e Mon Sep 17 00:00:00 2001 From: EricksOliveira Date: Sat, 19 Oct 2024 09:55:27 -0300 Subject: [PATCH 13/17] Fix Crash q [ link item ] This PR adds a null pointer check to the ChatHelper::FormatQuest function to avoid a crash that occurred when trying to format a null quest. The crash occurred when the quest pointer was nullptr and the code tried to access the GetQuestId(), GetQuestLevel() and GetTitle() methods. Changes: Added a nullptr check to the beginning of the ChatHelper::FormatQuest function. Return of a standard error message ("Invalid quest") if the quest is null. Impact: Prevents crashes when formatting null quests. Improves bot system stability when dealing with invalid quests. --- src/ChatHelper.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/ChatHelper.cpp b/src/ChatHelper.cpp index 8f464d5d..7bd8e590 100644 --- a/src/ChatHelper.cpp +++ b/src/ChatHelper.cpp @@ -304,9 +304,13 @@ ItemIds ChatHelper::parseItems(std::string const text) std::string const ChatHelper::FormatQuest(Quest const* quest) { + if (!quest) + { + return "Invalid quest"; + } + std::ostringstream out; - out << "|cFFFFFF00|Hquest:" << quest->GetQuestId() << ':' << quest->GetQuestLevel() << "|h[" << quest->GetTitle() - << "]|h|r"; + out << "|cFFFFFF00|Hquest:" << quest->GetQuestId() << ':' << quest->GetQuestLevel() << "|h[" << quest->GetTitle() << "]|h|r"; return out.str(); } From 8ac7d5823068e25b411263b5a9697c0f8b00f080 Mon Sep 17 00:00:00 2001 From: EricksOliveira Date: Sat, 19 Oct 2024 14:29:44 -0300 Subject: [PATCH 14/17] Fix Crash The Nexux Log Crash: 00007FF7750C05ED 00000028A47FEA10 FirebombSpreadAction::Execute+1AD E:\Server\Heroes_Azeroth\modules\mod-playerbots\src\strategy\dungeons\wotlk\nexus\NexusActions.cpp line 58 This PR fixes a possible crash in the FirebombSpreadAction::Execute function, located in the NexusActions.cpp file, which occurred due to the lack of checking for null pointers when accessing group members. Main changes: Added a null pointer check to ensure botAI->GetUnit(member) returns a valid unit before calling functions like GetExactDist2d and MoveAway. If botAI->GetUnit(member) returns nullptr, execution ignores the specific member, avoiding access to an invalid pointer that could cause a server crash. --- src/strategy/dungeons/wotlk/nexus/NexusActions.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/strategy/dungeons/wotlk/nexus/NexusActions.cpp b/src/strategy/dungeons/wotlk/nexus/NexusActions.cpp index 1a918614..e494251e 100644 --- a/src/strategy/dungeons/wotlk/nexus/NexusActions.cpp +++ b/src/strategy/dungeons/wotlk/nexus/NexusActions.cpp @@ -54,9 +54,16 @@ bool FirebombSpreadAction::Execute(Event event) { continue; } - if (bot->GetExactDist2d(botAI->GetUnit(member)) < targetDist) + + Unit* unit = botAI->GetUnit(member); + if (!unit) { - return MoveAway(botAI->GetUnit(member), targetDist); + continue; + } + + if (bot->GetExactDist2d(unit) < targetDist) + { + return MoveAway(unit, targetDist); } } return false; From d0a9e98801ac3a98ed477f58f4aee02cc74aa7b6 Mon Sep 17 00:00:00 2001 From: Bobblybook Date: Sun, 20 Oct 2024 15:49:10 +1100 Subject: [PATCH 15/17] Dungeon code cleanup Consistent code, easier to read logic flow and some missing nullptr checks --- .../hallsofstone/HallsOfStoneActions.cpp | 6 +-- .../hallsofstone/HallsOfStoneStrategy.cpp | 1 + .../dungeons/wotlk/nexus/NexusActions.cpp | 34 ++++++++------ .../dungeons/wotlk/nexus/NexusActions.h | 16 ------- .../dungeons/wotlk/nexus/NexusMultipliers.cpp | 6 +-- .../dungeons/wotlk/nexus/NexusMultipliers.h | 9 ---- .../dungeons/wotlk/nexus/NexusStrategy.cpp | 1 - .../wotlk/oldkingdom/OldKingdomActions.cpp | 10 ++-- .../oldkingdom/OldKingdomMultipliers.cpp | 14 +++--- .../wotlk/oldkingdom/OldKingdomTriggers.cpp | 18 ++++---- .../wotlk/utgardekeep/UtgardeKeepActions.cpp | 26 ++++------- .../utgardekeep/UtgardeKeepMultipliers.cpp | 32 ++++--------- .../wotlk/utgardekeep/UtgardeKeepTriggers.cpp | 46 ++++++------------- .../wotlk/violethold/VioletHoldActions.cpp | 12 +++-- .../wotlk/violethold/VioletHoldActions.h | 2 - .../wotlk/violethold/VioletHoldTriggers.cpp | 24 +++++++--- 16 files changed, 106 insertions(+), 151 deletions(-) diff --git a/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneActions.cpp b/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneActions.cpp index 25f19341..754d28df 100644 --- a/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneActions.cpp +++ b/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneActions.cpp @@ -12,12 +12,12 @@ bool ShatterSpreadAction::Execute(Event event) GuidVector members = AI_VALUE(GuidVector, "group members"); for (auto& member : members) { - if (bot->GetGUID() == member) + Unit* unit = botAI->GetUnit(member); + if (!unit || bot->GetGUID() == member) { continue; } - if (!closestMember || - bot->GetExactDist2d(botAI->GetUnit(member)) < bot->GetExactDist2d(closestMember)) + if (!closestMember || bot->GetExactDist2d(unit) < bot->GetExactDist2d(closestMember)) { closestMember = botAI->GetUnit(member); } diff --git a/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneStrategy.cpp b/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneStrategy.cpp index 09e77dc3..b1c66b66 100644 --- a/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneStrategy.cpp +++ b/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneStrategy.cpp @@ -26,4 +26,5 @@ void WotlkDungeonHoSStrategy::InitTriggers(std::vector &triggers) void WotlkDungeonHoSStrategy::InitMultipliers(std::vector &multipliers) { multipliers.push_back(new KrystallusMultiplier(botAI)); + multipliers.push_back(new SjonnirMultiplier(botAI)); } diff --git a/src/strategy/dungeons/wotlk/nexus/NexusActions.cpp b/src/strategy/dungeons/wotlk/nexus/NexusActions.cpp index 1a918614..775925e7 100644 --- a/src/strategy/dungeons/wotlk/nexus/NexusActions.cpp +++ b/src/strategy/dungeons/wotlk/nexus/NexusActions.cpp @@ -33,11 +33,12 @@ bool MoveFromWhirlwindAction::Execute(Event event) default: break; } - if (!boss || bot->GetExactDist2d(boss->GetPosition()) > targetDist) + float bossDistance = bot->GetExactDist2d(boss->GetPosition()); + if (!boss || bossDistance > targetDist) { return false; } - return MoveAway(boss, targetDist - bot->GetExactDist2d(boss->GetPosition())); + return MoveAway(boss, targetDist - bossDistance); } bool FirebombSpreadAction::Execute(Event event) @@ -50,13 +51,16 @@ bool FirebombSpreadAction::Execute(Event event) GuidVector members = AI_VALUE(GuidVector, "group members"); for (auto& member : members) { - if (bot->GetGUID() == member) + Unit* unit = botAI->GetUnit(member); + if (!unit || bot->GetGUID() == member) { continue; } - if (bot->GetExactDist2d(botAI->GetUnit(member)) < targetDist) + + if (bot->GetExactDist2d(unit) < targetDist) { - return MoveAway(botAI->GetUnit(member), targetDist); + float bossDistance = bot->GetExactDist2d(boss->GetPosition()); + return MoveAway(unit, targetDist - bossDistance); } } return false; @@ -70,24 +74,22 @@ bool TelestraSplitTargetAction::Execute(Event event) for (auto& attacker : attackers) { - Unit* npc = botAI->GetUnit(attacker); - if (!npc) - { - continue; - } - switch (npc->GetEntry()) + Unit* unit = botAI->GetUnit(attacker); + if (!unit) { continue; } + + switch (unit->GetEntry()) { // Focus arcane clone first case NPC_ARCANE_MAGUS: - splitTargets[0] = npc; + splitTargets[0] = unit; break; // Then the frost clone case NPC_FROST_MAGUS: - splitTargets[1] = npc; + splitTargets[1] = unit; break; // Fire clone last case NPC_FIRE_MAGUS: - splitTargets[2] = npc; + splitTargets[2] = unit; break; } } @@ -139,11 +141,15 @@ bool ChaoticRiftTargetAction::Execute(Event event) bool DodgeSpikesAction::isUseful() { Unit* boss = AI_VALUE2(Unit*, "find target", "ormorok the tree-shaper"); + if (!boss) { return false; } + return bot->GetExactDist2d(boss) > 0.5f; } bool DodgeSpikesAction::Execute(Event event) { Unit* boss = AI_VALUE2(Unit*, "find target", "ormorok the tree-shaper"); + if (!boss) { return false; } + return Move(bot->GetAngle(boss), bot->GetExactDist2d(boss) - 0.3f); } diff --git a/src/strategy/dungeons/wotlk/nexus/NexusActions.h b/src/strategy/dungeons/wotlk/nexus/NexusActions.h index 145efba7..96790c36 100644 --- a/src/strategy/dungeons/wotlk/nexus/NexusActions.h +++ b/src/strategy/dungeons/wotlk/nexus/NexusActions.h @@ -7,22 +7,6 @@ #include "Playerbots.h" #include "NexusTriggers.h" -#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) - -// Slice of the circle that we want melee dps to attack from. -// Measured from boss orientation, on one side. - -// Even though the breath cone is not the full 180 degrees, -// avoid melee dps from the front due to parry potential. -// Bots should learn good dps etiquette :) -#define DRAGON_MELEE_MIN_ANGLE ANGLE_90_DEG -// This leaves a danger zone of 60 degrees at the tail end on both sides. -// This is a total of 120 degrees tail arc that bots will avoid - -// number just happens to be the same in this case, but this is always measured from the front. -#define DRAGON_MELEE_MAX_ANGLE ANGLE_120_DEG - class MoveFromWhirlwindAction : public MovementAction { public: diff --git a/src/strategy/dungeons/wotlk/nexus/NexusMultipliers.cpp b/src/strategy/dungeons/wotlk/nexus/NexusMultipliers.cpp index 4b8ab97a..117be85c 100644 --- a/src/strategy/dungeons/wotlk/nexus/NexusMultipliers.cpp +++ b/src/strategy/dungeons/wotlk/nexus/NexusMultipliers.cpp @@ -77,10 +77,8 @@ float AnomalusMultiplier::GetValue(Action* action) float OrmorokMultiplier::GetValue(Action* action) { Unit* boss = AI_VALUE2(Unit*, "find target", "ormorok the tree-shaper"); - if (!boss) - { - return 1.0f; - } + if (!boss) { return 1.0f; } + // These are used for auto ranged repositioning, need to suppress so ranged dps don't ping-pong if (dynamic_cast(action)) { diff --git a/src/strategy/dungeons/wotlk/nexus/NexusMultipliers.h b/src/strategy/dungeons/wotlk/nexus/NexusMultipliers.h index c7321a40..00373ebd 100644 --- a/src/strategy/dungeons/wotlk/nexus/NexusMultipliers.h +++ b/src/strategy/dungeons/wotlk/nexus/NexusMultipliers.h @@ -39,13 +39,4 @@ class OrmorokMultiplier : public Multiplier virtual float GetValue(Action* action); }; -class KeristraszaMultiplier : public Multiplier -{ - public: - KeristraszaMultiplier(PlayerbotAI* ai) : Multiplier(ai, "keristrasza") {} - - public: - virtual float GetValue(Action* action); -}; - #endif diff --git a/src/strategy/dungeons/wotlk/nexus/NexusStrategy.cpp b/src/strategy/dungeons/wotlk/nexus/NexusStrategy.cpp index b86c1aa4..566a1381 100644 --- a/src/strategy/dungeons/wotlk/nexus/NexusStrategy.cpp +++ b/src/strategy/dungeons/wotlk/nexus/NexusStrategy.cpp @@ -50,5 +50,4 @@ void WotlkDungeonNexStrategy::InitMultipliers(std::vector &multipli multipliers.push_back(new TelestraMultiplier(botAI)); multipliers.push_back(new AnomalusMultiplier(botAI)); multipliers.push_back(new OrmorokMultiplier(botAI)); - // multipliers.push_back(new KeristraszaMultiplier(botAI)); } diff --git a/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomActions.cpp b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomActions.cpp index c5b27cca..41b410bf 100644 --- a/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomActions.cpp +++ b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomActions.cpp @@ -42,22 +42,22 @@ bool AvoidShadowCrashAction::Execute(Event event) { // Could check all enemy units in range as it's possible to pull multiple of these mobs. // They should really be killed 1 by 1, multipulls are messy so we just handle singles for now - Unit* npc = AI_VALUE2(Unit*, "find target", "forgotten one"); + Unit* unit = AI_VALUE2(Unit*, "find target", "forgotten one"); Unit* victim = nullptr; float radius = 10.0f; float targetDist = radius + 2.0f; - if (!npc) { return false; } + if (!unit) { return false; } // Actively move if targeted by a shadow crash. // Spell check not needed, they don't have any other non-instant casts - if (npc->HasUnitState(UNIT_STATE_CASTING)) // && npc->FindCurrentSpellBySpellId(SPELL_SHADOW_CRASH)) + if (unit->HasUnitState(UNIT_STATE_CASTING)) // && unit->FindCurrentSpellBySpellId(SPELL_SHADOW_CRASH)) { // This doesn't seem to avoid casts very well, perhaps because this isn't checked while allies are casting. // TODO: Revisit if this is an issue in heroics, otherwise ignore shadow crashes for the most part. - victim = botAI->GetUnit(npc->GetTarget()); + victim = botAI->GetUnit(unit->GetTarget()); if (victim && bot->GetExactDist2d(victim) < radius) { - return MoveAway(victim, targetDist - bot->GetExactDist2d(victim)); + return MoveAway(victim, targetDist - bot->GetExactDist2d(victim->GetPosition())); } } diff --git a/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomMultipliers.cpp b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomMultipliers.cpp index 9fc90802..73ce27bb 100644 --- a/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomMultipliers.cpp +++ b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomMultipliers.cpp @@ -9,9 +9,10 @@ float ElderNadoxMultiplier::GetValue(Action* action) { Unit* boss = AI_VALUE2(Unit*, "find target", "elder nadox"); - Unit* guardian = AI_VALUE2(Unit*, "find target", "ahn'kahar guardian"); + if (!boss) { return 1.0f; } - if (boss && guardian) + Unit* guardian = AI_VALUE2(Unit*, "find target", "ahn'kahar guardian"); + if (guardian) { if (dynamic_cast(action)) { @@ -24,7 +25,7 @@ float ElderNadoxMultiplier::GetValue(Action* action) float JedogaShadowseekerMultiplier::GetValue(Action* action) { Unit* boss = AI_VALUE2(Unit*, "find target", "jedoga shadowseeker"); - // Unit* volunteer = AI_VALUE2(Unit*, "find target", "twilight volunteer"); + if (!boss) { return 1.0f; } Unit* volunteer = nullptr; // Target is not findable from threat table using AI_VALUE2(), @@ -41,7 +42,7 @@ float JedogaShadowseekerMultiplier::GetValue(Action* action) } } - if (boss && volunteer) + if (volunteer) { if (dynamic_cast(action)) { @@ -53,9 +54,10 @@ float JedogaShadowseekerMultiplier::GetValue(Action* action) float ForgottenOneMultiplier::GetValue(Action* action) { - Unit* npc = AI_VALUE2(Unit*, "find target", "forgotten one"); + Unit* unit = AI_VALUE2(Unit*, "find target", "forgotten one"); + if (!unit) { return 1.0f; } - if (npc && bot->isMoving()) + if (bot->isMoving()) { if (dynamic_cast(action)) { diff --git a/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomTriggers.cpp b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomTriggers.cpp index 951ab755..04dcd590 100644 --- a/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomTriggers.cpp +++ b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomTriggers.cpp @@ -17,9 +17,9 @@ bool NadoxGuardianTrigger::IsActive() bool JedogaVolunteerTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "jedoga shadowseeker"); - // Unit* volunteer = AI_VALUE2(Unit*, "find target", "twilight volunteer"); - Unit* volunteer = nullptr; - // Target is not findable from threat table using AI_VALUE2(), + if (!boss) { return false; } + + // Volunteer is not findable from threat table using AI_VALUE2(), // therefore need to search manually for the unit name GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); @@ -28,16 +28,16 @@ bool JedogaVolunteerTrigger::IsActive() Unit* unit = botAI->GetUnit(*i); if (unit && unit->GetEntry() == NPC_TWILIGHT_VOLUNTEER) { - volunteer = unit; - break; + return true; } } - - return boss && volunteer; + return false; } bool ShadowCrashTrigger::IsActive() { - if (botAI->IsMelee(bot)) { return false; } - return !botAI->IsMelee(bot) && AI_VALUE2(Unit*, "find target", "forgotten one"); + Unit* unit = AI_VALUE2(Unit*, "find target", "forgotten one"); + if (!unit) { return false; } + + return !botAI->IsMelee(bot); } diff --git a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepActions.cpp b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepActions.cpp index f26823f4..a8966e8b 100644 --- a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepActions.cpp +++ b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepActions.cpp @@ -14,7 +14,7 @@ bool AttackFrostTombAction::Execute(Event event) for (auto i = targets.begin(); i != targets.end(); ++i) { Unit* unit = botAI->GetUnit(*i); - if (unit && unit->GetName() == "Frost Tomb") + if (unit && unit->GetEntry() == NPC_FROST_TOMB) { frostTomb = unit; break; @@ -31,7 +31,9 @@ bool AttackFrostTombAction::Execute(Event event) bool AttackDalronnAction::Execute(Event event) { Unit* boss = AI_VALUE2(Unit*, "find target", "dalronn the controller"); - if (!boss || AI_VALUE(Unit*, "current target") == boss) + if (!boss) { return false; } + + if (AI_VALUE(Unit*, "current target") == boss) { return false; } @@ -42,10 +44,7 @@ bool IngvarStopCastingAction::Execute(Event event) { // Doesn't work, this action gets queued behind the current spell instead of interrupting it Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); - if (!boss) - { - return false; - } + if (!boss) { return false; } int32 my_spell_id = AI_VALUE(uint32, "active spell"); if (!my_spell_id || my_spell_id == 0) @@ -54,10 +53,7 @@ bool IngvarStopCastingAction::Execute(Event event) } Spell* spell = bot->FindCurrentSpellBySpellId(my_spell_id); - if (!spell) - { - return false; - } + if (!spell) { return false; } // bot->Yell("cancelling spell="+std::to_string(my_spell_id), LANG_UNIVERSAL); bot->InterruptSpell(spell->GetCurrentContainer(), false, true, true); @@ -71,10 +67,7 @@ bool IngvarDodgeSmashAction::isUseful() { return !AI_VALUE2(bool, "behind", "cur bool IngvarDodgeSmashAction::Execute(Event event) { Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); - if (!boss) - { - return false; - } + if (!boss) { return false; } float distance = bot->GetExactDist2d(boss->GetPosition()); // Extra units to move into the boss, instead of being just 1 pixel past his midpoint. @@ -88,10 +81,7 @@ bool IngvarSmashReturnAction::isUseful() { return AI_VALUE2(bool, "behind", "cur bool IngvarSmashReturnAction::Execute(Event event) { Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); - if (!boss) - { - return false; - } + if (!boss) { return false; } float distance = bot->GetExactDist2d(boss->GetPosition()); return Move(bot->GetAngle(boss), distance + bot->GetMeleeReach()); diff --git a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepMultipliers.cpp b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepMultipliers.cpp index 31d310fa..c13f898d 100644 --- a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepMultipliers.cpp +++ b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepMultipliers.cpp @@ -7,10 +7,8 @@ float PrinceKelesethMultiplier::GetValue(Action* action) { Unit* boss = AI_VALUE2(Unit*, "find target", "prince keleseth"); - if (!boss) - { - return 1.0f; - } + if (!boss) { return 1.0f; } + if (dynamic_cast(action)) { return 0.0f; @@ -20,13 +18,9 @@ float PrinceKelesethMultiplier::GetValue(Action* action) float SkarvaldAndDalronnMultiplier::GetValue(Action* action) { - // Unit* skarvald = AI_VALUE2(Unit*, "find target", "skarvald the constructor"); - Unit* dalronn = AI_VALUE2(Unit*, "find target", "dalronn the controller"); - - if (!dalronn) - { - return 1.0f; - } + // Only need to deal with Dalronn here. If he's dead, just fall back to normal dps strat + Unit* boss = AI_VALUE2(Unit*, "find target", "dalronn the controller"); + if (!boss) { return 1.0f; } if (dynamic_cast(action)) { @@ -39,10 +33,7 @@ float IngvarThePlundererMultiplier::GetValue(Action* action) { Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); bool isTank = botAI->IsTank(bot); - if (!boss) - { - return 1.0f; - } + if (!boss) { return 1.0f; } // Prevent movement actions overriding current movement, we're probably dodging a slam if (isTank && bot->isMoving() && dynamic_cast(action)) @@ -60,10 +51,7 @@ float IngvarThePlundererMultiplier::GetValue(Action* action) { uint32 spellId = AI_VALUE2(uint32, "spell id", action->getName()); SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); - if (!spellInfo) - { - return 1.0f; - } + if (!spellInfo) { return 1.0f; } uint32 castTime = spellInfo->CalcCastTime(bot); if (castTime != 0) @@ -73,10 +61,8 @@ float IngvarThePlundererMultiplier::GetValue(Action* action) } } // Done with non-tank logic - if (!isTank) - { - return 1.0f; - } + if (!isTank) { return 1.0f; } + // TANK ONLY if (boss->FindCurrentSpellBySpellId(SPELL_SMASH) || boss->FindCurrentSpellBySpellId(SPELL_DARK_SMASH)) diff --git a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepTriggers.cpp b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepTriggers.cpp index ff07c78e..a3f03b94 100644 --- a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepTriggers.cpp +++ b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepTriggers.cpp @@ -9,7 +9,7 @@ bool KelesethFrostTombTrigger::IsActive() for (auto& member : members) { Unit* unit = botAI->GetUnit(member); - if (unit && unit->HasAura(DEBUFF_FROST_TOMB)) + if (unit && unit->HasAura(SPELL_FROST_TOMB)) { return true; } @@ -19,21 +19,17 @@ bool KelesethFrostTombTrigger::IsActive() bool DalronnNontankTrigger::IsActive() { - Unit* dalronn = AI_VALUE2(Unit*, "find target", "dalronn the controller"); - if (!dalronn) - { - return false; - } + Unit* boss = AI_VALUE2(Unit*, "find target", "dalronn the controller"); + if (!boss) { return false; } + return !botAI->IsTank(bot); } bool IngvarStaggeringRoarTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); - if (!boss) - { - return false; - } + if (!boss) { return false; } + if (boss->HasUnitState(UNIT_STATE_CASTING)) { if (boss->FindCurrentSpellBySpellId(SPELL_STAGGERING_ROAR)) @@ -47,16 +43,12 @@ bool IngvarStaggeringRoarTrigger::IsActive() bool IngvarDreadfulRoarTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); - if (!boss) + if (!boss) { return false; } + + if (boss->HasUnitState(UNIT_STATE_CASTING) && + boss->FindCurrentSpellBySpellId(SPELL_DREADFUL_ROAR)) { - return false; - } - if (boss->HasUnitState(UNIT_STATE_CASTING)) - { - if (boss->FindCurrentSpellBySpellId(SPELL_DREADFUL_ROAR)) - { - return true; - } + return true; } return false; } @@ -64,10 +56,7 @@ bool IngvarDreadfulRoarTrigger::IsActive() bool IngvarSmashTankTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); - if (!boss || !botAI->IsTank(bot)) - { - return false; - } + if (!boss || !botAI->IsTank(bot)) { return false; } if (boss->HasUnitState(UNIT_STATE_CASTING)) { @@ -86,20 +75,15 @@ bool IngvarSmashTankReturnTrigger::IsActive() // if (!boss || !botAI->IsTank(bot) || boss->HasUnitState(UNIT_STATE_CASTING)) // Ignore casting state as Ingvar will sometimes chain-cast a roar after a smash.. // We don't want this to prevent our tank from repositioning properly. - if (!boss || !botAI->IsTank(bot)) - { - return false; - } + if (!boss || !botAI->IsTank(bot)) { return false; } + return true; } bool NotBehindIngvarTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); - if (!boss || botAI->IsTank(bot)) - { - return false; - } + if (!boss || botAI->IsTank(bot)) { return false; } return AI_VALUE2(bool, "behind", "current target"); } diff --git a/src/strategy/dungeons/wotlk/violethold/VioletHoldActions.cpp b/src/strategy/dungeons/wotlk/violethold/VioletHoldActions.cpp index a108b323..df36baa1 100644 --- a/src/strategy/dungeons/wotlk/violethold/VioletHoldActions.cpp +++ b/src/strategy/dungeons/wotlk/violethold/VioletHoldActions.cpp @@ -7,6 +7,8 @@ bool AttackErekemAction::Execute(Event event) { // Focus boss first, adds after Unit* boss = AI_VALUE2(Unit*, "find target", "erekem"); + if (!boss) { return false; } + if (AI_VALUE(Unit*, "current target") != boss) { return Attack(boss); @@ -19,6 +21,8 @@ bool AttackIchorGlobuleAction::Execute(Event event) Unit* boss = AI_VALUE2(Unit*, "find target", "ichoron"); if (!boss) { return false; } + Unit* currentTarget = AI_VALUE(Unit*, "current target"); + // Tank prioritise boss if it's up if (botAI->IsTank(bot) && !boss->HasAura(SPELL_DRAINED)) { @@ -38,7 +42,6 @@ bool AttackIchorGlobuleAction::Execute(Event event) Unit* unit = botAI->GetUnit(*i); if (unit && unit->GetEntry() == NPC_ICHOR_GLOBULE) { - Unit* currentTarget = AI_VALUE(Unit*, "current target"); // Check IDs here, NOT Unit* pointers: // Don't keep swapping between sentries. // If we're already attacking one, don't retarget another @@ -50,7 +53,7 @@ bool AttackIchorGlobuleAction::Execute(Event event) } } // No ichor globules left alive, fall back to targeting boss - if (AI_VALUE(Unit*, "current target") != boss) + if (currentTarget != boss) { return Attack(boss); } @@ -62,6 +65,8 @@ bool AttackVoidSentryAction::Execute(Event event) { Unit* boss = AI_VALUE2(Unit*, "find target", "zuramat the obliterator"); if (!boss) { return false; } + + Unit* currentTarget = AI_VALUE(Unit*, "current target"); // Target is not findable from threat table using AI_VALUE2(), // therefore need to search manually for the unit name @@ -73,7 +78,6 @@ bool AttackVoidSentryAction::Execute(Event event) Unit* unit = botAI->GetUnit(*i); if (unit && unit->GetEntry() == NPC_VOID_SENTRY) { - Unit* currentTarget = AI_VALUE(Unit*, "current target"); // Check IDs here, NOT Unit* pointers: // Don't keep swapping between sentries. // If we're already attacking one, don't retarget another @@ -85,7 +89,7 @@ bool AttackVoidSentryAction::Execute(Event event) } } // No void sentries left alive, fall back to targeting boss - if (AI_VALUE(Unit*, "current target") != boss) + if (currentTarget != boss) { return Attack(boss); } diff --git a/src/strategy/dungeons/wotlk/violethold/VioletHoldActions.h b/src/strategy/dungeons/wotlk/violethold/VioletHoldActions.h index 2d2dada0..fc7ce9d2 100644 --- a/src/strategy/dungeons/wotlk/violethold/VioletHoldActions.h +++ b/src/strategy/dungeons/wotlk/violethold/VioletHoldActions.h @@ -8,8 +8,6 @@ #include "Playerbots.h" #include "VioletHoldTriggers.h" -// const Position NOVOS_PARTY_POSITION = Position(-378.852f, -760.349f, 28.587f); - class AttackErekemAction : public AttackAction { public: diff --git a/src/strategy/dungeons/wotlk/violethold/VioletHoldTriggers.cpp b/src/strategy/dungeons/wotlk/violethold/VioletHoldTriggers.cpp index cf9e9ab1..bd2eee76 100644 --- a/src/strategy/dungeons/wotlk/violethold/VioletHoldTriggers.cpp +++ b/src/strategy/dungeons/wotlk/violethold/VioletHoldTriggers.cpp @@ -6,30 +6,42 @@ bool ErekemTargetTrigger::IsActive() { - return AI_VALUE2(Unit*, "find target", "erekem") && botAI->IsDps(bot); + Unit* boss = AI_VALUE2(Unit*, "find target", "erekem"); + if (!boss) { return false; } + + return botAI->IsDps(bot); } bool IchoronTargetTrigger::IsActive() { - return AI_VALUE2(Unit*, "find target", "ichoron") && !botAI->IsHeal(bot); + Unit* boss = AI_VALUE2(Unit*, "find target", "ichoron"); + if (!boss) { return false; } + + return !botAI->IsHeal(bot); } bool VoidShiftTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "zuramat the obliterator"); - return boss && bot->HasAura(SPELL_VOID_SHIFTED) && !botAI->IsHeal(bot); + if (!boss) { return false; } + + return bot->HasAura(SPELL_VOID_SHIFTED) && !botAI->IsHeal(bot); } bool ShroudOfDarknessTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "zuramat the obliterator"); - return boss && boss->HasAura(SPELL_SHROUD_OF_DARKNESS); + if (!boss) { return false; } + + return boss->HasAura(SPELL_SHROUD_OF_DARKNESS); } bool CyanigosaPositioningTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "cyanigosa"); + if (!boss) { return false; } + // Include healers here for now, otherwise they stand in things - return boss && !botAI->IsTank(bot) && !botAI->IsRangedDps(bot); - // return boss && botAI->IsMelee(bot) && !botAI->IsTank(bot); + return !botAI->IsTank(bot) && !botAI->IsRangedDps(bot); + // return botAI->IsMelee(bot) && !botAI->IsTank(bot); } From 3f7f3061249771a145484de114071a31a0e9c09d Mon Sep 17 00:00:00 2001 From: Bobblybook Date: Sun, 20 Oct 2024 15:58:14 +1100 Subject: [PATCH 16/17] Revert "Dungeon code cleanup" This reverts commit d0a9e98801ac3a98ed477f58f4aee02cc74aa7b6. --- .../hallsofstone/HallsOfStoneActions.cpp | 6 +-- .../hallsofstone/HallsOfStoneStrategy.cpp | 1 - .../dungeons/wotlk/nexus/NexusActions.cpp | 34 ++++++-------- .../dungeons/wotlk/nexus/NexusActions.h | 16 +++++++ .../dungeons/wotlk/nexus/NexusMultipliers.cpp | 6 ++- .../dungeons/wotlk/nexus/NexusMultipliers.h | 9 ++++ .../dungeons/wotlk/nexus/NexusStrategy.cpp | 1 + .../wotlk/oldkingdom/OldKingdomActions.cpp | 10 ++-- .../oldkingdom/OldKingdomMultipliers.cpp | 14 +++--- .../wotlk/oldkingdom/OldKingdomTriggers.cpp | 18 ++++---- .../wotlk/utgardekeep/UtgardeKeepActions.cpp | 26 +++++++---- .../utgardekeep/UtgardeKeepMultipliers.cpp | 32 +++++++++---- .../wotlk/utgardekeep/UtgardeKeepTriggers.cpp | 46 +++++++++++++------ .../wotlk/violethold/VioletHoldActions.cpp | 12 ++--- .../wotlk/violethold/VioletHoldActions.h | 2 + .../wotlk/violethold/VioletHoldTriggers.cpp | 24 +++------- 16 files changed, 151 insertions(+), 106 deletions(-) diff --git a/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneActions.cpp b/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneActions.cpp index 754d28df..25f19341 100644 --- a/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneActions.cpp +++ b/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneActions.cpp @@ -12,12 +12,12 @@ bool ShatterSpreadAction::Execute(Event event) GuidVector members = AI_VALUE(GuidVector, "group members"); for (auto& member : members) { - Unit* unit = botAI->GetUnit(member); - if (!unit || bot->GetGUID() == member) + if (bot->GetGUID() == member) { continue; } - if (!closestMember || bot->GetExactDist2d(unit) < bot->GetExactDist2d(closestMember)) + if (!closestMember || + bot->GetExactDist2d(botAI->GetUnit(member)) < bot->GetExactDist2d(closestMember)) { closestMember = botAI->GetUnit(member); } diff --git a/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneStrategy.cpp b/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneStrategy.cpp index b1c66b66..09e77dc3 100644 --- a/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneStrategy.cpp +++ b/src/strategy/dungeons/wotlk/hallsofstone/HallsOfStoneStrategy.cpp @@ -26,5 +26,4 @@ void WotlkDungeonHoSStrategy::InitTriggers(std::vector &triggers) void WotlkDungeonHoSStrategy::InitMultipliers(std::vector &multipliers) { multipliers.push_back(new KrystallusMultiplier(botAI)); - multipliers.push_back(new SjonnirMultiplier(botAI)); } diff --git a/src/strategy/dungeons/wotlk/nexus/NexusActions.cpp b/src/strategy/dungeons/wotlk/nexus/NexusActions.cpp index 775925e7..1a918614 100644 --- a/src/strategy/dungeons/wotlk/nexus/NexusActions.cpp +++ b/src/strategy/dungeons/wotlk/nexus/NexusActions.cpp @@ -33,12 +33,11 @@ bool MoveFromWhirlwindAction::Execute(Event event) default: break; } - float bossDistance = bot->GetExactDist2d(boss->GetPosition()); - if (!boss || bossDistance > targetDist) + if (!boss || bot->GetExactDist2d(boss->GetPosition()) > targetDist) { return false; } - return MoveAway(boss, targetDist - bossDistance); + return MoveAway(boss, targetDist - bot->GetExactDist2d(boss->GetPosition())); } bool FirebombSpreadAction::Execute(Event event) @@ -51,16 +50,13 @@ bool FirebombSpreadAction::Execute(Event event) GuidVector members = AI_VALUE(GuidVector, "group members"); for (auto& member : members) { - Unit* unit = botAI->GetUnit(member); - if (!unit || bot->GetGUID() == member) + if (bot->GetGUID() == member) { continue; } - - if (bot->GetExactDist2d(unit) < targetDist) + if (bot->GetExactDist2d(botAI->GetUnit(member)) < targetDist) { - float bossDistance = bot->GetExactDist2d(boss->GetPosition()); - return MoveAway(unit, targetDist - bossDistance); + return MoveAway(botAI->GetUnit(member), targetDist); } } return false; @@ -74,22 +70,24 @@ bool TelestraSplitTargetAction::Execute(Event event) for (auto& attacker : attackers) { - Unit* unit = botAI->GetUnit(attacker); - if (!unit) { continue; } - - switch (unit->GetEntry()) + Unit* npc = botAI->GetUnit(attacker); + if (!npc) + { + continue; + } + switch (npc->GetEntry()) { // Focus arcane clone first case NPC_ARCANE_MAGUS: - splitTargets[0] = unit; + splitTargets[0] = npc; break; // Then the frost clone case NPC_FROST_MAGUS: - splitTargets[1] = unit; + splitTargets[1] = npc; break; // Fire clone last case NPC_FIRE_MAGUS: - splitTargets[2] = unit; + splitTargets[2] = npc; break; } } @@ -141,15 +139,11 @@ bool ChaoticRiftTargetAction::Execute(Event event) bool DodgeSpikesAction::isUseful() { Unit* boss = AI_VALUE2(Unit*, "find target", "ormorok the tree-shaper"); - if (!boss) { return false; } - return bot->GetExactDist2d(boss) > 0.5f; } bool DodgeSpikesAction::Execute(Event event) { Unit* boss = AI_VALUE2(Unit*, "find target", "ormorok the tree-shaper"); - if (!boss) { return false; } - return Move(bot->GetAngle(boss), bot->GetExactDist2d(boss) - 0.3f); } diff --git a/src/strategy/dungeons/wotlk/nexus/NexusActions.h b/src/strategy/dungeons/wotlk/nexus/NexusActions.h index 96790c36..145efba7 100644 --- a/src/strategy/dungeons/wotlk/nexus/NexusActions.h +++ b/src/strategy/dungeons/wotlk/nexus/NexusActions.h @@ -7,6 +7,22 @@ #include "Playerbots.h" #include "NexusTriggers.h" +#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) + +// Slice of the circle that we want melee dps to attack from. +// Measured from boss orientation, on one side. + +// Even though the breath cone is not the full 180 degrees, +// avoid melee dps from the front due to parry potential. +// Bots should learn good dps etiquette :) +#define DRAGON_MELEE_MIN_ANGLE ANGLE_90_DEG +// This leaves a danger zone of 60 degrees at the tail end on both sides. +// This is a total of 120 degrees tail arc that bots will avoid - +// number just happens to be the same in this case, but this is always measured from the front. +#define DRAGON_MELEE_MAX_ANGLE ANGLE_120_DEG + class MoveFromWhirlwindAction : public MovementAction { public: diff --git a/src/strategy/dungeons/wotlk/nexus/NexusMultipliers.cpp b/src/strategy/dungeons/wotlk/nexus/NexusMultipliers.cpp index 117be85c..4b8ab97a 100644 --- a/src/strategy/dungeons/wotlk/nexus/NexusMultipliers.cpp +++ b/src/strategy/dungeons/wotlk/nexus/NexusMultipliers.cpp @@ -77,8 +77,10 @@ float AnomalusMultiplier::GetValue(Action* action) float OrmorokMultiplier::GetValue(Action* action) { Unit* boss = AI_VALUE2(Unit*, "find target", "ormorok the tree-shaper"); - if (!boss) { return 1.0f; } - + if (!boss) + { + return 1.0f; + } // These are used for auto ranged repositioning, need to suppress so ranged dps don't ping-pong if (dynamic_cast(action)) { diff --git a/src/strategy/dungeons/wotlk/nexus/NexusMultipliers.h b/src/strategy/dungeons/wotlk/nexus/NexusMultipliers.h index 00373ebd..c7321a40 100644 --- a/src/strategy/dungeons/wotlk/nexus/NexusMultipliers.h +++ b/src/strategy/dungeons/wotlk/nexus/NexusMultipliers.h @@ -39,4 +39,13 @@ class OrmorokMultiplier : public Multiplier virtual float GetValue(Action* action); }; +class KeristraszaMultiplier : public Multiplier +{ + public: + KeristraszaMultiplier(PlayerbotAI* ai) : Multiplier(ai, "keristrasza") {} + + public: + virtual float GetValue(Action* action); +}; + #endif diff --git a/src/strategy/dungeons/wotlk/nexus/NexusStrategy.cpp b/src/strategy/dungeons/wotlk/nexus/NexusStrategy.cpp index 566a1381..b86c1aa4 100644 --- a/src/strategy/dungeons/wotlk/nexus/NexusStrategy.cpp +++ b/src/strategy/dungeons/wotlk/nexus/NexusStrategy.cpp @@ -50,4 +50,5 @@ void WotlkDungeonNexStrategy::InitMultipliers(std::vector &multipli multipliers.push_back(new TelestraMultiplier(botAI)); multipliers.push_back(new AnomalusMultiplier(botAI)); multipliers.push_back(new OrmorokMultiplier(botAI)); + // multipliers.push_back(new KeristraszaMultiplier(botAI)); } diff --git a/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomActions.cpp b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomActions.cpp index 41b410bf..c5b27cca 100644 --- a/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomActions.cpp +++ b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomActions.cpp @@ -42,22 +42,22 @@ bool AvoidShadowCrashAction::Execute(Event event) { // Could check all enemy units in range as it's possible to pull multiple of these mobs. // They should really be killed 1 by 1, multipulls are messy so we just handle singles for now - Unit* unit = AI_VALUE2(Unit*, "find target", "forgotten one"); + Unit* npc = AI_VALUE2(Unit*, "find target", "forgotten one"); Unit* victim = nullptr; float radius = 10.0f; float targetDist = radius + 2.0f; - if (!unit) { return false; } + if (!npc) { return false; } // Actively move if targeted by a shadow crash. // Spell check not needed, they don't have any other non-instant casts - if (unit->HasUnitState(UNIT_STATE_CASTING)) // && unit->FindCurrentSpellBySpellId(SPELL_SHADOW_CRASH)) + if (npc->HasUnitState(UNIT_STATE_CASTING)) // && npc->FindCurrentSpellBySpellId(SPELL_SHADOW_CRASH)) { // This doesn't seem to avoid casts very well, perhaps because this isn't checked while allies are casting. // TODO: Revisit if this is an issue in heroics, otherwise ignore shadow crashes for the most part. - victim = botAI->GetUnit(unit->GetTarget()); + victim = botAI->GetUnit(npc->GetTarget()); if (victim && bot->GetExactDist2d(victim) < radius) { - return MoveAway(victim, targetDist - bot->GetExactDist2d(victim->GetPosition())); + return MoveAway(victim, targetDist - bot->GetExactDist2d(victim)); } } diff --git a/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomMultipliers.cpp b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomMultipliers.cpp index 73ce27bb..9fc90802 100644 --- a/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomMultipliers.cpp +++ b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomMultipliers.cpp @@ -9,10 +9,9 @@ float ElderNadoxMultiplier::GetValue(Action* action) { Unit* boss = AI_VALUE2(Unit*, "find target", "elder nadox"); - if (!boss) { return 1.0f; } - Unit* guardian = AI_VALUE2(Unit*, "find target", "ahn'kahar guardian"); - if (guardian) + + if (boss && guardian) { if (dynamic_cast(action)) { @@ -25,7 +24,7 @@ float ElderNadoxMultiplier::GetValue(Action* action) float JedogaShadowseekerMultiplier::GetValue(Action* action) { Unit* boss = AI_VALUE2(Unit*, "find target", "jedoga shadowseeker"); - if (!boss) { return 1.0f; } + // Unit* volunteer = AI_VALUE2(Unit*, "find target", "twilight volunteer"); Unit* volunteer = nullptr; // Target is not findable from threat table using AI_VALUE2(), @@ -42,7 +41,7 @@ float JedogaShadowseekerMultiplier::GetValue(Action* action) } } - if (volunteer) + if (boss && volunteer) { if (dynamic_cast(action)) { @@ -54,10 +53,9 @@ float JedogaShadowseekerMultiplier::GetValue(Action* action) float ForgottenOneMultiplier::GetValue(Action* action) { - Unit* unit = AI_VALUE2(Unit*, "find target", "forgotten one"); - if (!unit) { return 1.0f; } + Unit* npc = AI_VALUE2(Unit*, "find target", "forgotten one"); - if (bot->isMoving()) + if (npc && bot->isMoving()) { if (dynamic_cast(action)) { diff --git a/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomTriggers.cpp b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomTriggers.cpp index 04dcd590..951ab755 100644 --- a/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomTriggers.cpp +++ b/src/strategy/dungeons/wotlk/oldkingdom/OldKingdomTriggers.cpp @@ -17,9 +17,9 @@ bool NadoxGuardianTrigger::IsActive() bool JedogaVolunteerTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "jedoga shadowseeker"); - if (!boss) { return false; } - - // Volunteer is not findable from threat table using AI_VALUE2(), + // Unit* volunteer = AI_VALUE2(Unit*, "find target", "twilight volunteer"); + Unit* volunteer = nullptr; + // Target is not findable from threat table using AI_VALUE2(), // therefore need to search manually for the unit name GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); @@ -28,16 +28,16 @@ bool JedogaVolunteerTrigger::IsActive() Unit* unit = botAI->GetUnit(*i); if (unit && unit->GetEntry() == NPC_TWILIGHT_VOLUNTEER) { - return true; + volunteer = unit; + break; } } - return false; + + return boss && volunteer; } bool ShadowCrashTrigger::IsActive() { - Unit* unit = AI_VALUE2(Unit*, "find target", "forgotten one"); - if (!unit) { return false; } - - return !botAI->IsMelee(bot); + if (botAI->IsMelee(bot)) { return false; } + return !botAI->IsMelee(bot) && AI_VALUE2(Unit*, "find target", "forgotten one"); } diff --git a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepActions.cpp b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepActions.cpp index a8966e8b..f26823f4 100644 --- a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepActions.cpp +++ b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepActions.cpp @@ -14,7 +14,7 @@ bool AttackFrostTombAction::Execute(Event event) for (auto i = targets.begin(); i != targets.end(); ++i) { Unit* unit = botAI->GetUnit(*i); - if (unit && unit->GetEntry() == NPC_FROST_TOMB) + if (unit && unit->GetName() == "Frost Tomb") { frostTomb = unit; break; @@ -31,9 +31,7 @@ bool AttackFrostTombAction::Execute(Event event) bool AttackDalronnAction::Execute(Event event) { Unit* boss = AI_VALUE2(Unit*, "find target", "dalronn the controller"); - if (!boss) { return false; } - - if (AI_VALUE(Unit*, "current target") == boss) + if (!boss || AI_VALUE(Unit*, "current target") == boss) { return false; } @@ -44,7 +42,10 @@ bool IngvarStopCastingAction::Execute(Event event) { // Doesn't work, this action gets queued behind the current spell instead of interrupting it Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); - if (!boss) { return false; } + if (!boss) + { + return false; + } int32 my_spell_id = AI_VALUE(uint32, "active spell"); if (!my_spell_id || my_spell_id == 0) @@ -53,7 +54,10 @@ bool IngvarStopCastingAction::Execute(Event event) } Spell* spell = bot->FindCurrentSpellBySpellId(my_spell_id); - if (!spell) { return false; } + if (!spell) + { + return false; + } // bot->Yell("cancelling spell="+std::to_string(my_spell_id), LANG_UNIVERSAL); bot->InterruptSpell(spell->GetCurrentContainer(), false, true, true); @@ -67,7 +71,10 @@ bool IngvarDodgeSmashAction::isUseful() { return !AI_VALUE2(bool, "behind", "cur bool IngvarDodgeSmashAction::Execute(Event event) { Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); - if (!boss) { return false; } + if (!boss) + { + return false; + } float distance = bot->GetExactDist2d(boss->GetPosition()); // Extra units to move into the boss, instead of being just 1 pixel past his midpoint. @@ -81,7 +88,10 @@ bool IngvarSmashReturnAction::isUseful() { return AI_VALUE2(bool, "behind", "cur bool IngvarSmashReturnAction::Execute(Event event) { Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); - if (!boss) { return false; } + if (!boss) + { + return false; + } float distance = bot->GetExactDist2d(boss->GetPosition()); return Move(bot->GetAngle(boss), distance + bot->GetMeleeReach()); diff --git a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepMultipliers.cpp b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepMultipliers.cpp index c13f898d..31d310fa 100644 --- a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepMultipliers.cpp +++ b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepMultipliers.cpp @@ -7,8 +7,10 @@ float PrinceKelesethMultiplier::GetValue(Action* action) { Unit* boss = AI_VALUE2(Unit*, "find target", "prince keleseth"); - if (!boss) { return 1.0f; } - + if (!boss) + { + return 1.0f; + } if (dynamic_cast(action)) { return 0.0f; @@ -18,9 +20,13 @@ float PrinceKelesethMultiplier::GetValue(Action* action) float SkarvaldAndDalronnMultiplier::GetValue(Action* action) { - // Only need to deal with Dalronn here. If he's dead, just fall back to normal dps strat - Unit* boss = AI_VALUE2(Unit*, "find target", "dalronn the controller"); - if (!boss) { return 1.0f; } + // Unit* skarvald = AI_VALUE2(Unit*, "find target", "skarvald the constructor"); + Unit* dalronn = AI_VALUE2(Unit*, "find target", "dalronn the controller"); + + if (!dalronn) + { + return 1.0f; + } if (dynamic_cast(action)) { @@ -33,7 +39,10 @@ float IngvarThePlundererMultiplier::GetValue(Action* action) { Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); bool isTank = botAI->IsTank(bot); - if (!boss) { return 1.0f; } + if (!boss) + { + return 1.0f; + } // Prevent movement actions overriding current movement, we're probably dodging a slam if (isTank && bot->isMoving() && dynamic_cast(action)) @@ -51,7 +60,10 @@ float IngvarThePlundererMultiplier::GetValue(Action* action) { uint32 spellId = AI_VALUE2(uint32, "spell id", action->getName()); SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); - if (!spellInfo) { return 1.0f; } + if (!spellInfo) + { + return 1.0f; + } uint32 castTime = spellInfo->CalcCastTime(bot); if (castTime != 0) @@ -61,8 +73,10 @@ float IngvarThePlundererMultiplier::GetValue(Action* action) } } // Done with non-tank logic - if (!isTank) { return 1.0f; } - + if (!isTank) + { + return 1.0f; + } // TANK ONLY if (boss->FindCurrentSpellBySpellId(SPELL_SMASH) || boss->FindCurrentSpellBySpellId(SPELL_DARK_SMASH)) diff --git a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepTriggers.cpp b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepTriggers.cpp index a3f03b94..ff07c78e 100644 --- a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepTriggers.cpp +++ b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepTriggers.cpp @@ -9,7 +9,7 @@ bool KelesethFrostTombTrigger::IsActive() for (auto& member : members) { Unit* unit = botAI->GetUnit(member); - if (unit && unit->HasAura(SPELL_FROST_TOMB)) + if (unit && unit->HasAura(DEBUFF_FROST_TOMB)) { return true; } @@ -19,17 +19,21 @@ bool KelesethFrostTombTrigger::IsActive() bool DalronnNontankTrigger::IsActive() { - Unit* boss = AI_VALUE2(Unit*, "find target", "dalronn the controller"); - if (!boss) { return false; } - + Unit* dalronn = AI_VALUE2(Unit*, "find target", "dalronn the controller"); + if (!dalronn) + { + return false; + } return !botAI->IsTank(bot); } bool IngvarStaggeringRoarTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); - if (!boss) { return false; } - + if (!boss) + { + return false; + } if (boss->HasUnitState(UNIT_STATE_CASTING)) { if (boss->FindCurrentSpellBySpellId(SPELL_STAGGERING_ROAR)) @@ -43,12 +47,16 @@ bool IngvarStaggeringRoarTrigger::IsActive() bool IngvarDreadfulRoarTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); - if (!boss) { return false; } - - if (boss->HasUnitState(UNIT_STATE_CASTING) && - boss->FindCurrentSpellBySpellId(SPELL_DREADFUL_ROAR)) + if (!boss) { - return true; + return false; + } + if (boss->HasUnitState(UNIT_STATE_CASTING)) + { + if (boss->FindCurrentSpellBySpellId(SPELL_DREADFUL_ROAR)) + { + return true; + } } return false; } @@ -56,7 +64,10 @@ bool IngvarDreadfulRoarTrigger::IsActive() bool IngvarSmashTankTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); - if (!boss || !botAI->IsTank(bot)) { return false; } + if (!boss || !botAI->IsTank(bot)) + { + return false; + } if (boss->HasUnitState(UNIT_STATE_CASTING)) { @@ -75,15 +86,20 @@ bool IngvarSmashTankReturnTrigger::IsActive() // if (!boss || !botAI->IsTank(bot) || boss->HasUnitState(UNIT_STATE_CASTING)) // Ignore casting state as Ingvar will sometimes chain-cast a roar after a smash.. // We don't want this to prevent our tank from repositioning properly. - if (!boss || !botAI->IsTank(bot)) { return false; } - + if (!boss || !botAI->IsTank(bot)) + { + return false; + } return true; } bool NotBehindIngvarTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); - if (!boss || botAI->IsTank(bot)) { return false; } + if (!boss || botAI->IsTank(bot)) + { + return false; + } return AI_VALUE2(bool, "behind", "current target"); } diff --git a/src/strategy/dungeons/wotlk/violethold/VioletHoldActions.cpp b/src/strategy/dungeons/wotlk/violethold/VioletHoldActions.cpp index df36baa1..a108b323 100644 --- a/src/strategy/dungeons/wotlk/violethold/VioletHoldActions.cpp +++ b/src/strategy/dungeons/wotlk/violethold/VioletHoldActions.cpp @@ -7,8 +7,6 @@ bool AttackErekemAction::Execute(Event event) { // Focus boss first, adds after Unit* boss = AI_VALUE2(Unit*, "find target", "erekem"); - if (!boss) { return false; } - if (AI_VALUE(Unit*, "current target") != boss) { return Attack(boss); @@ -21,8 +19,6 @@ bool AttackIchorGlobuleAction::Execute(Event event) Unit* boss = AI_VALUE2(Unit*, "find target", "ichoron"); if (!boss) { return false; } - Unit* currentTarget = AI_VALUE(Unit*, "current target"); - // Tank prioritise boss if it's up if (botAI->IsTank(bot) && !boss->HasAura(SPELL_DRAINED)) { @@ -42,6 +38,7 @@ bool AttackIchorGlobuleAction::Execute(Event event) Unit* unit = botAI->GetUnit(*i); if (unit && unit->GetEntry() == NPC_ICHOR_GLOBULE) { + Unit* currentTarget = AI_VALUE(Unit*, "current target"); // Check IDs here, NOT Unit* pointers: // Don't keep swapping between sentries. // If we're already attacking one, don't retarget another @@ -53,7 +50,7 @@ bool AttackIchorGlobuleAction::Execute(Event event) } } // No ichor globules left alive, fall back to targeting boss - if (currentTarget != boss) + if (AI_VALUE(Unit*, "current target") != boss) { return Attack(boss); } @@ -65,8 +62,6 @@ bool AttackVoidSentryAction::Execute(Event event) { Unit* boss = AI_VALUE2(Unit*, "find target", "zuramat the obliterator"); if (!boss) { return false; } - - Unit* currentTarget = AI_VALUE(Unit*, "current target"); // Target is not findable from threat table using AI_VALUE2(), // therefore need to search manually for the unit name @@ -78,6 +73,7 @@ bool AttackVoidSentryAction::Execute(Event event) Unit* unit = botAI->GetUnit(*i); if (unit && unit->GetEntry() == NPC_VOID_SENTRY) { + Unit* currentTarget = AI_VALUE(Unit*, "current target"); // Check IDs here, NOT Unit* pointers: // Don't keep swapping between sentries. // If we're already attacking one, don't retarget another @@ -89,7 +85,7 @@ bool AttackVoidSentryAction::Execute(Event event) } } // No void sentries left alive, fall back to targeting boss - if (currentTarget != boss) + if (AI_VALUE(Unit*, "current target") != boss) { return Attack(boss); } diff --git a/src/strategy/dungeons/wotlk/violethold/VioletHoldActions.h b/src/strategy/dungeons/wotlk/violethold/VioletHoldActions.h index fc7ce9d2..2d2dada0 100644 --- a/src/strategy/dungeons/wotlk/violethold/VioletHoldActions.h +++ b/src/strategy/dungeons/wotlk/violethold/VioletHoldActions.h @@ -8,6 +8,8 @@ #include "Playerbots.h" #include "VioletHoldTriggers.h" +// const Position NOVOS_PARTY_POSITION = Position(-378.852f, -760.349f, 28.587f); + class AttackErekemAction : public AttackAction { public: diff --git a/src/strategy/dungeons/wotlk/violethold/VioletHoldTriggers.cpp b/src/strategy/dungeons/wotlk/violethold/VioletHoldTriggers.cpp index bd2eee76..cf9e9ab1 100644 --- a/src/strategy/dungeons/wotlk/violethold/VioletHoldTriggers.cpp +++ b/src/strategy/dungeons/wotlk/violethold/VioletHoldTriggers.cpp @@ -6,42 +6,30 @@ bool ErekemTargetTrigger::IsActive() { - Unit* boss = AI_VALUE2(Unit*, "find target", "erekem"); - if (!boss) { return false; } - - return botAI->IsDps(bot); + return AI_VALUE2(Unit*, "find target", "erekem") && botAI->IsDps(bot); } bool IchoronTargetTrigger::IsActive() { - Unit* boss = AI_VALUE2(Unit*, "find target", "ichoron"); - if (!boss) { return false; } - - return !botAI->IsHeal(bot); + return AI_VALUE2(Unit*, "find target", "ichoron") && !botAI->IsHeal(bot); } bool VoidShiftTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "zuramat the obliterator"); - if (!boss) { return false; } - - return bot->HasAura(SPELL_VOID_SHIFTED) && !botAI->IsHeal(bot); + return boss && bot->HasAura(SPELL_VOID_SHIFTED) && !botAI->IsHeal(bot); } bool ShroudOfDarknessTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "zuramat the obliterator"); - if (!boss) { return false; } - - return boss->HasAura(SPELL_SHROUD_OF_DARKNESS); + return boss && boss->HasAura(SPELL_SHROUD_OF_DARKNESS); } bool CyanigosaPositioningTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "cyanigosa"); - if (!boss) { return false; } - // Include healers here for now, otherwise they stand in things - return !botAI->IsTank(bot) && !botAI->IsRangedDps(bot); - // return botAI->IsMelee(bot) && !botAI->IsTank(bot); + return boss && !botAI->IsTank(bot) && !botAI->IsRangedDps(bot); + // return boss && botAI->IsMelee(bot) && !botAI->IsTank(bot); } From c8dd44fe931beccc731dce1b1e1726dcd5204df8 Mon Sep 17 00:00:00 2001 From: EricksOliveira Date: Sun, 20 Oct 2024 11:46:16 -0300 Subject: [PATCH 17/17] Fix Logs LOG_ERROR for LOG_DEBUG --- src/PlayerbotMgr.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/PlayerbotMgr.cpp b/src/PlayerbotMgr.cpp index 3cae1645..444401a1 100644 --- a/src/PlayerbotMgr.cpp +++ b/src/PlayerbotMgr.cpp @@ -100,8 +100,8 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con Player* bot = botSession->GetPlayer(); if (!bot) { - // Log para debug - LOG_ERROR("mod-playerbots", "Bot player could not be loaded for account ID: {}", botAccountId); + // Debug log + LOG_DEBUG("mod-playerbots", "Bot player could not be loaded for account ID: {}", botAccountId); botSession->LogoutPlayer(true); delete botSession; botLoading.erase(holder.GetGuid()); @@ -115,7 +115,7 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con Player* masterPlayer = masterSession ? masterSession->GetPlayer() : nullptr; if (masterSession && !masterPlayer) { - LOG_ERROR("mod-playerbots", "Master session found but no player is associated for master account ID: {}", masterAccount); + LOG_DEBUG("mod-playerbots", "Master session found but no player is associated for master account ID: {}", masterAccount); } std::ostringstream out; @@ -144,7 +144,7 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con PlayerbotMgr* mgr = GET_PLAYERBOT_MGR(masterPlayer); if (!mgr) { - LOG_ERROR("mod-playerbots", "PlayerbotMgr not found for master player with GUID: {}", masterPlayer->GetGUID().GetRawValue()); + LOG_DEBUG("mod-playerbots", "PlayerbotMgr not found for master player with GUID: {}", masterPlayer->GetGUID().GetRawValue()); } uint32 count = mgr->GetPlayerbotsCount(); @@ -443,7 +443,7 @@ void PlayerbotHolder::OnBotLogin(Player* const bot) if (!botAI) { // Log a warning here to indicate that the botAI is null - LOG_ERROR("mod-playerbots", "PlayerbotAI is null for bot with GUID: {}", bot->GetGUID().GetRawValue()); + LOG_DEBUG("mod-playerbots", "PlayerbotAI is null for bot with GUID: {}", bot->GetGUID().GetRawValue()); return; } @@ -451,7 +451,7 @@ void PlayerbotHolder::OnBotLogin(Player* const bot) if (!master) { // Log a warning to indicate that the master is null - LOG_ERROR("mod-playerbots", "Master is null for bot with GUID: {}", bot->GetGUID().GetRawValue()); + LOG_DEBUG("mod-playerbots", "Master is null for bot with GUID: {}", bot->GetGUID().GetRawValue()); return; }