From f09d8d72f406f9376dd01f4fb235a8ef6b238805 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sat, 10 Feb 2024 22:03:33 +0800 Subject: [PATCH] Rogue stealth spell & Target selection for combo class --- src/PlayerbotAI.cpp | 7 ++ src/PlayerbotAI.h | 1 + .../rogue/AssassinationRogueStrategy.cpp | 10 ++- src/strategy/rogue/DpsRogueStrategy.cpp | 82 ++++++++++--------- src/strategy/values/DpsTargetValue.cpp | 61 ++++++++++++++ 5 files changed, 119 insertions(+), 42 deletions(-) diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 8be4ee5f..a7d7c1d8 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -1378,6 +1378,13 @@ bool PlayerbotAI::IsCaster(Player* player) return IsRanged(player) && player->getClass() != CLASS_HUNTER; } +bool PlayerbotAI::IsCombo(Player* player) +{ + int tab = AiFactory::GetPlayerSpecTab(player); + return player->getClass() == CLASS_ROGUE || + (player->getClass() == CLASS_DRUID && tab == DRUID_TAB_FERAL && !IsTank(bot)); +} + bool PlayerbotAI::IsRangedDps(Player* player) { return IsRanged(player) && IsDps(player); diff --git a/src/PlayerbotAI.h b/src/PlayerbotAI.h index 2952f43c..7011f012 100644 --- a/src/PlayerbotAI.h +++ b/src/PlayerbotAI.h @@ -333,6 +333,7 @@ class PlayerbotAI : public PlayerbotAIBase bool IsDps(Player* player); bool IsRanged(Player* player); bool IsCaster(Player* player); + bool IsCombo(Player* player); bool IsRangedDps(Player* player); bool IsMainTank(Player* player); bool IsAssistTank(Player* player); diff --git a/src/strategy/rogue/AssassinationRogueStrategy.cpp b/src/strategy/rogue/AssassinationRogueStrategy.cpp index 1da854ce..e5ae8c1b 100644 --- a/src/strategy/rogue/AssassinationRogueStrategy.cpp +++ b/src/strategy/rogue/AssassinationRogueStrategy.cpp @@ -34,9 +34,7 @@ AssassinationRogueStrategy::AssassinationRogueStrategy(PlayerbotAI* ai) : MeleeC NextAction** AssassinationRogueStrategy::getDefaultActions() { - return NextAction::array(0, - new NextAction("garrote", ACTION_DEFAULT + 0.2f), - new NextAction("ambush", ACTION_DEFAULT + 0.1f), + return NextAction::array(0, new NextAction("melee", ACTION_DEFAULT), NULL); } @@ -44,6 +42,12 @@ NextAction** AssassinationRogueStrategy::getDefaultActions() void AssassinationRogueStrategy::InitTriggers(std::vector &triggers) { MeleeCombatStrategy::InitTriggers(triggers); + + triggers.push_back(new TriggerNode( + "high energy available", + NextAction::array(0, + new NextAction("garrote", ACTION_HIGH + 0.3f), + new NextAction("ambush", ACTION_HIGH + 0.2f), nullptr))); triggers.push_back(new TriggerNode( "high energy available", diff --git a/src/strategy/rogue/DpsRogueStrategy.cpp b/src/strategy/rogue/DpsRogueStrategy.cpp index 6abf6a0b..69873f94 100644 --- a/src/strategy/rogue/DpsRogueStrategy.cpp +++ b/src/strategy/rogue/DpsRogueStrategy.cpp @@ -23,51 +23,51 @@ class DpsRogueStrategyActionNodeFactory : public NamedObjectFactory static ActionNode* melee(PlayerbotAI* botAI) { return new ActionNode ("melee", - /*P*/ NULL, - /*A*/ NextAction::array(0, new NextAction("mutilate"), NULL), - /*C*/ NULL); + /*P*/ nullptr, + /*A*/ NextAction::array(0, new NextAction("mutilate"), nullptr), + /*C*/ nullptr); } static ActionNode* mutilate(PlayerbotAI* botAI) { return new ActionNode ("mutilate", - /*P*/ NULL, - /*A*/ NextAction::array(0, new NextAction("sinister strike"), NULL), - /*C*/ NULL); + /*P*/ nullptr, + /*A*/ NextAction::array(0, new NextAction("sinister strike"), nullptr), + /*C*/ nullptr); } static ActionNode* sinister_strike(PlayerbotAI* botAI) { return new ActionNode ("sinister strike", - /*P*/ NULL, - /*A*/ NextAction::array(0, new NextAction("melee"), NULL), - /*C*/ NULL); + /*P*/ nullptr, + /*A*/ NextAction::array(0, new NextAction("melee"), nullptr), + /*C*/ nullptr); } static ActionNode* kick(PlayerbotAI* botAI) { return new ActionNode ("kick", - /*P*/ NULL, - /*A*/ NextAction::array(0, new NextAction("kidney shot"), NULL), - /*C*/ NULL); + /*P*/ nullptr, + /*A*/ NextAction::array(0, new NextAction("kidney shot"), nullptr), + /*C*/ nullptr); } static ActionNode* kidney_shot(PlayerbotAI* botAI) { return new ActionNode ("kidney shot", - /*P*/ NULL, - /*A*/ NULL, - /*C*/ NULL); + /*P*/ nullptr, + /*A*/ nullptr, + /*C*/ nullptr); } static ActionNode* backstab(PlayerbotAI* botAI) { return new ActionNode ("backstab", - /*P*/ NULL, - /*A*/ NextAction::array(0, new NextAction("mutilate"), NULL), - /*C*/ NULL); + /*P*/ nullptr, + /*A*/ NextAction::array(0, new NextAction("mutilate"), nullptr), + /*C*/ nullptr); } static ActionNode* rupture(PlayerbotAI* botAI) { return new ActionNode ("rupture", - /*P*/ NULL, - /*A*/ NextAction::array(0, new NextAction("eviscerate"), NULL), - /*C*/ NULL); + /*P*/ nullptr, + /*A*/ NextAction::array(0, new NextAction("eviscerate"), nullptr), + /*C*/ nullptr); } }; @@ -79,11 +79,9 @@ DpsRogueStrategy::DpsRogueStrategy(PlayerbotAI* botAI) : MeleeCombatStrategy(bot NextAction** DpsRogueStrategy::getDefaultActions() { - return NextAction::array(0, - new NextAction("garrote", ACTION_DEFAULT + 0.3f), - new NextAction("ambush", ACTION_DEFAULT + 0.2f), + return NextAction::array(0, new NextAction("killing spree", ACTION_DEFAULT + 0.1f), - new NextAction("melee", ACTION_DEFAULT), NULL); + new NextAction("melee", ACTION_DEFAULT), nullptr); } void DpsRogueStrategy::InitTriggers(std::vector& triggers) @@ -92,45 +90,51 @@ void DpsRogueStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode( "high energy available", - NextAction::array(0, new NextAction("sinister strike", ACTION_NORMAL + 3), NULL))); + NextAction::array(0, + new NextAction("garrote", ACTION_HIGH + 0.3f), + new NextAction("ambush", ACTION_HIGH + 0.2f), nullptr))); + + triggers.push_back(new TriggerNode( + "high energy available", + NextAction::array(0, new NextAction("sinister strike", ACTION_NORMAL + 3), nullptr))); triggers.push_back(new TriggerNode( "slice and dice", - NextAction::array(0, new NextAction("slice and dice", ACTION_HIGH + 2), NULL))); + NextAction::array(0, new NextAction("slice and dice", ACTION_HIGH + 2), nullptr))); triggers.push_back(new TriggerNode( "combo points available", NextAction::array(0, - new NextAction("rupture", ACTION_HIGH + 1), NULL))); + new NextAction("rupture", ACTION_HIGH + 1), nullptr))); triggers.push_back(new TriggerNode( "target with combo points almost dead", NextAction::array(0, - new NextAction("eviscerate", ACTION_HIGH + 1), NULL))); + new NextAction("eviscerate", ACTION_HIGH + 1), nullptr))); triggers.push_back(new TriggerNode( "medium threat", - NextAction::array(0, new NextAction("vanish", ACTION_HIGH), NULL))); + NextAction::array(0, new NextAction("vanish", ACTION_HIGH), nullptr))); triggers.push_back(new TriggerNode( "low health", - NextAction::array(0, new NextAction("evasion", ACTION_EMERGENCY), new NextAction("feint", ACTION_EMERGENCY), NULL))); + NextAction::array(0, new NextAction("evasion", ACTION_EMERGENCY), new NextAction("feint", ACTION_EMERGENCY), nullptr))); triggers.push_back(new TriggerNode( "kick", - NextAction::array(0, new NextAction("kick", ACTION_INTERRUPT + 2), NULL))); + NextAction::array(0, new NextAction("kick", ACTION_INTERRUPT + 2), nullptr))); triggers.push_back(new TriggerNode( "kick on enemy healer", - NextAction::array(0, new NextAction("kick on enemy healer", ACTION_INTERRUPT + 1), NULL))); + NextAction::array(0, new NextAction("kick on enemy healer", ACTION_INTERRUPT + 1), nullptr))); // triggers.push_back(new TriggerNode( // "behind target", - // NextAction::array(0, new NextAction("backstab", ACTION_NORMAL), NULL))); + // NextAction::array(0, new NextAction("backstab", ACTION_NORMAL), nullptr))); triggers.push_back(new TriggerNode( "light aoe", - NextAction::array(0, new NextAction("blade flurry", ACTION_HIGH + 3), NULL))); + NextAction::array(0, new NextAction("blade flurry", ACTION_HIGH + 3), nullptr))); triggers.push_back(new TriggerNode( "enemy out of melee", @@ -138,15 +142,15 @@ void DpsRogueStrategy::InitTriggers(std::vector& triggers) new NextAction("stealth", ACTION_NORMAL + 9), new NextAction("sprint", ACTION_NORMAL + 8), new NextAction("reach melee", ACTION_NORMAL + 7), - NULL))); + nullptr))); triggers.push_back(new TriggerNode( "expose armor", - NextAction::array(0, new NextAction("expose armor", ACTION_HIGH + 3), NULL))); + NextAction::array(0, new NextAction("expose armor", ACTION_HIGH + 3), nullptr))); triggers.push_back(new TriggerNode( "tricks of the trade on main tank", - NextAction::array(0, new NextAction("tricks of the trade on main tank", ACTION_HIGH + 7), NULL))); + NextAction::array(0, new NextAction("tricks of the trade on main tank", ACTION_HIGH + 7), nullptr))); } class StealthedRogueStrategyActionNodeFactory : public NamedObjectFactory @@ -239,7 +243,7 @@ void RogueAoeStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode("light aoe", NextAction::array(0, new NextAction("blade flurry", ACTION_HIGH), nullptr))); triggers.push_back(new TriggerNode( "medium aoe", - NextAction::array(0, new NextAction("fan of knives", ACTION_NORMAL + 5), NULL))); + NextAction::array(0, new NextAction("fan of knives", ACTION_NORMAL + 5), nullptr))); } void RogueBoostStrategy::InitTriggers(std::vector& triggers) diff --git a/src/strategy/values/DpsTargetValue.cpp b/src/strategy/values/DpsTargetValue.cpp index e98d72e2..e26c1545 100644 --- a/src/strategy/values/DpsTargetValue.cpp +++ b/src/strategy/values/DpsTargetValue.cpp @@ -177,6 +177,64 @@ class NonCasterFindTargetSmartStrategy : public FindTargetStrategy float targetExpectedLifeTime; }; +// combo +class ComboFindTargetSmartStrategy : public FindTargetStrategy +{ + public: + ComboFindTargetSmartStrategy(PlayerbotAI* botAI, float dps) : FindTargetStrategy(botAI), dps_(dps), targetExpectedLifeTime(1000000) { } + + void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override + { + if (Group* group = botAI->GetBot()->GetGroup()) + { + ObjectGuid guid = group->GetTargetIcon(4); + if (guid && attacker->GetGUID() == guid) + return; + } + if (!attacker->IsAlive()) { + return; + } + float expectedLifeTime = attacker->GetHealth() / dps_; + // Unit* victim = attacker->GetVictim(); + if (!result || IsBetter(attacker, result)) { + targetExpectedLifeTime = expectedLifeTime; + result = attacker; + } + } + bool IsBetter(Unit* new_unit, Unit* old_unit) { + float new_time = new_unit->GetHealth() / dps_; + float old_time = old_unit->GetHealth() / dps_; + // [5-20] > (5-0] > (20-inf) + if (GetIntervalLevel(new_unit) > GetIntervalLevel(old_unit)) { + return true; + } + // attack enemy in range and with lowest health + int level = GetIntervalLevel(new_unit); + Player* bot = botAI->GetBot(); + if (level == 10) { + Unit* combo_unit = bot->GetComboTarget(); + if (new_unit == combo_unit) { + return true; + } + return new_time < old_time; + } + // all targets are far away, choose the closest one + return bot->GetDistance(new_unit) < bot->GetDistance(old_unit); + } + int32_t GetIntervalLevel(Unit* unit) { + float time = unit->GetHealth() / dps_; + float dis = unit->GetDistance(botAI->GetBot()); + float attackRange = botAI->IsRanged(botAI->GetBot()) ? sPlayerbotAIConfig->spellDistance : sPlayerbotAIConfig->meleeDistance; + attackRange += 5.0f; + int level = dis < attackRange ? 10 : 0; + return level; + } + + protected: + float dps_; + float targetExpectedLifeTime; +}; + Unit* DpsTargetValue::Calculate() { Unit* rti = RtiTargetValue::Calculate(); @@ -188,6 +246,9 @@ Unit* DpsTargetValue::Calculate() if (botAI->IsCaster(bot)) { CasterFindTargetSmartStrategy strategy(botAI, dps); return TargetValue::FindTarget(&strategy); + } else if (botAI->IsCombo(bot)) { + ComboFindTargetSmartStrategy strategy(botAI, dps); + return TargetValue::FindTarget(&strategy); } NonCasterFindTargetSmartStrategy strategy(botAI, dps); return TargetValue::FindTarget(&strategy);