From 7e1de0b9cf1ed32064cc5ac67adcc8fcc2a32324 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Mon, 4 Sep 2023 17:04:59 +0800 Subject: [PATCH] life time check for debuff spell & cast time spell --- src/strategy/generic/CastTimeStrategy.cpp | 36 +++++++++++----- src/strategy/mage/ArcaneMageStrategy.cpp | 16 +++---- src/strategy/mage/GenericMageStrategy.cpp | 8 ++++ src/strategy/triggers/GenericTriggers.cpp | 2 +- src/strategy/triggers/GenericTriggers.h | 6 +-- src/strategy/values/ExpectedLifetimeValue.cpp | 43 +++++++++++++++++++ src/strategy/values/ExpectedLifetimeValue.h | 36 ++++++++++++++++ src/strategy/values/ValueContext.h | 6 +++ src/strategy/warlock/WarlockTriggers.h | 10 ++++- 9 files changed, 138 insertions(+), 25 deletions(-) create mode 100644 src/strategy/values/ExpectedLifetimeValue.cpp create mode 100644 src/strategy/values/ExpectedLifetimeValue.h diff --git a/src/strategy/generic/CastTimeStrategy.cpp b/src/strategy/generic/CastTimeStrategy.cpp index 28359b58..9e362370 100644 --- a/src/strategy/generic/CastTimeStrategy.cpp +++ b/src/strategy/generic/CastTimeStrategy.cpp @@ -11,32 +11,46 @@ float CastTimeMultiplier::GetValue(Action* action) if (action == nullptr) return 1.0f; - uint8 targetHealth = AI_VALUE2(uint8, "health", "current target"); + // uint8 targetHealth = AI_VALUE2(uint8, "health", "current target"); std::string const name = action->getName(); - if (action->GetTarget() != AI_VALUE(Unit*, "current target")) + if (!action->GetTarget() || action->GetTarget() != AI_VALUE(Unit*, "current target")) return 1.0f; - if (targetHealth < sPlayerbotAIConfig->criticalHealth && dynamic_cast(action)) + if (/*targetHealth < sPlayerbotAIConfig->criticalHealth && */dynamic_cast(action)) { uint32 spellId = AI_VALUE2(uint32, "spell id", name); SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); if (!spellInfo) return 1.0f; - if ((spellInfo->Targets & TARGET_FLAG_DEST_LOCATION) != 0|| (spellInfo->Targets & TARGET_FLAG_SOURCE_LOCATION) != 0) + if ((spellInfo->Targets & TARGET_FLAG_DEST_LOCATION) != 0 || (spellInfo->Targets & TARGET_FLAG_SOURCE_LOCATION) != 0) return 1.0f; - + uint32 castTime = spellInfo->CalcCastTime(); - // if (castTime >= 3000) - // return 0.0f; - if (castTime >= 1500) - return 0.5f; + if (spellInfo->IsChanneled()) + { + int32 duration = spellInfo->GetDuration(); + bot->ApplySpellMod(spellInfo->Id, SPELLMOD_DURATION, duration); + duration = std::min(duration, 3000); + if (duration > 0) + castTime += duration; + } - if (castTime >= 1000) - return 0.25f; + if (castTime > (1000 * action->GetTarget()->GetHealth() / AI_VALUE(float, "expected group dps"))) { + return 0.0f; + } } + // if (castTime >= 3000) + // return 0.0f; + + // if (castTime >= 1500) + // return 0.5f; + + // if (castTime >= 1000) + // return 0.25f; + // // } return 1.0f; } diff --git a/src/strategy/mage/ArcaneMageStrategy.cpp b/src/strategy/mage/ArcaneMageStrategy.cpp index f5c5f634..4dce3368 100644 --- a/src/strategy/mage/ArcaneMageStrategy.cpp +++ b/src/strategy/mage/ArcaneMageStrategy.cpp @@ -13,7 +13,7 @@ class ArcaneMageStrategyActionNodeFactory : public NamedObjectFactory life_bound; + return BuffTrigger::IsActive() && GetTarget() && (1000 * GetTarget()->GetHealth() / AI_VALUE(float, "expected group dps")) >= needLifeTime; } bool DebuffOnBossTrigger::IsActive() diff --git a/src/strategy/triggers/GenericTriggers.h b/src/strategy/triggers/GenericTriggers.h index 9d4ad5e0..3376d92d 100644 --- a/src/strategy/triggers/GenericTriggers.h +++ b/src/strategy/triggers/GenericTriggers.h @@ -315,12 +315,12 @@ class TargetInSightTrigger : public Trigger class DebuffTrigger : public BuffTrigger { public: - DebuffTrigger(PlayerbotAI* botAI, std::string const spell, int32 checkInterval = 1, bool checkIsOwner = false, float life_bound = 0.25) : BuffTrigger(botAI, spell, checkInterval, checkIsOwner), life_bound(life_bound) { } + DebuffTrigger(PlayerbotAI* botAI, std::string const spell, int32 checkInterval = 1, bool checkIsOwner = false, float needLifeTime = 8000.0f) : BuffTrigger(botAI, spell, checkInterval, checkIsOwner), needLifeTime(needLifeTime) { } std::string const GetTargetName() override { return "current target"; } bool IsActive() override; protected: - float life_bound; + float needLifeTime; }; class DebuffOnBossTrigger : public DebuffTrigger @@ -333,7 +333,7 @@ class DebuffOnBossTrigger : public DebuffTrigger class DebuffOnAttackerTrigger : public DebuffTrigger { public: - DebuffOnAttackerTrigger(PlayerbotAI* botAI, std::string const spell, bool checkIsOwner = true) : DebuffTrigger(botAI, spell, 1, checkIsOwner) { } + DebuffOnAttackerTrigger(PlayerbotAI* botAI, std::string const spell, bool checkIsOwner = true, float needLifeTime = 8000.0f) : DebuffTrigger(botAI, spell, 1, checkIsOwner, needLifeTime) { } Value* GetTargetValue() override; std::string const getName() override { return spell + " on attacker"; } diff --git a/src/strategy/values/ExpectedLifetimeValue.cpp b/src/strategy/values/ExpectedLifetimeValue.cpp new file mode 100644 index 00000000..397f9bf7 --- /dev/null +++ b/src/strategy/values/ExpectedLifetimeValue.cpp @@ -0,0 +1,43 @@ +#include "ExpectedLifetimeValue.h" +#include "Playerbots.h" +#include "SharedDefines.h" + +float ExpectedLifetimeValue::Calculate() +{ + Unit* target = AI_VALUE(Unit*, qualifier); + if (!target || !target->IsAlive()) { + return 0.0f; + } + float dps = AI_VALUE(float, "expected group dps"); + float res = target->GetHealth() / dps * 1000; + // bot->Say(target->GetName() + " lifetime: " + std::to_string(res), LANG_UNIVERSAL); + return res; +} +float ExpectedGroupDpsValue::Calculate() +{ + float dps_num; + Group* group = bot->GetGroup(); + if (!group) { + dps_num = 1; + } else { + dps_num = group->GetMembersCount() * 0.7; + } + // efficiency record based on rare gear level, is there better calculation method? + float dps_efficiency = 1; + if (bot->GetLevel() < 30) { + dps_efficiency = 1.5; + } else if (bot->GetLevel() < 40) { + dps_efficiency = 2; + } else if (bot->GetLevel() < 50) { + dps_efficiency = 3; + } else if (bot->GetLevel() < 60) { + dps_efficiency = 4; + } else if (bot->GetLevel() < 70) { + dps_efficiency = 7; + } else if (bot->GetLevel() < 80) { + dps_efficiency = 12; + } else { + dps_efficiency = 30; + } + return dps_num * bot->GetLevel() * dps_efficiency; +} \ No newline at end of file diff --git a/src/strategy/values/ExpectedLifetimeValue.h b/src/strategy/values/ExpectedLifetimeValue.h new file mode 100644 index 00000000..5288b859 --- /dev/null +++ b/src/strategy/values/ExpectedLifetimeValue.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_EXPECTEDLIFETIMEVALUE_H +#define _PLAYERBOT_EXPECTEDLIFETIMEVALUE_H + +#include "NamedObjectContext.h" +#include "TargetValue.h" +#include "PossibleTargetsValue.h" +#include "Value.h" + +class PlayerbotAI; +class Unit; + +// [target health] / [expected group single target dps] = [expected lifetime] +class ExpectedLifetimeValue : public FloatCalculatedValue, public Qualified +{ + public: + ExpectedLifetimeValue(PlayerbotAI* botAI) : + FloatCalculatedValue(botAI, "expected lifetime") { } + + public: + float Calculate() override; +}; + +class ExpectedGroupDpsValue : public FloatCalculatedValue +{ + public: + ExpectedGroupDpsValue(PlayerbotAI* botAI) : + FloatCalculatedValue(botAI, "expected group dps") { } + + public: + float Calculate() override; +}; +#endif diff --git a/src/strategy/values/ValueContext.h b/src/strategy/values/ValueContext.h index 694bce5a..46d126ea 100644 --- a/src/strategy/values/ValueContext.h +++ b/src/strategy/values/ValueContext.h @@ -25,6 +25,7 @@ #include "DistanceValue.h" #include "EnemyHealerTargetValue.h" #include "EnemyPlayerValue.h" +#include "ExpectedLifetimeValue.h" #include "Formations.h" #include "GroupValues.h" #include "GrindTargetValue.h" @@ -293,6 +294,8 @@ class ValueContext : public NamedObjectContext creators["boss target"] = &ValueContext::boss_target; creators["nearest triggers"] = &ValueContext::nearest_triggers; creators["neglect threat"] = &ValueContext::neglect_threat; + creators["expected lifetime"] = &ValueContext::expected_lifetime; + creators["expected group dps"] = &ValueContext::expected_group_dps; } private: @@ -490,6 +493,9 @@ class ValueContext : public NamedObjectContext static UntypedValue* boss_target(PlayerbotAI* ai) { return new BossTargetValue(ai); } static UntypedValue* nearest_triggers(PlayerbotAI* ai) { return new NearestTriggersValue(ai); } static UntypedValue* neglect_threat(PlayerbotAI* ai) { return new NeglectThreatResetValue(ai); } + static UntypedValue* expected_lifetime(PlayerbotAI* ai) { return new ExpectedLifetimeValue(ai); } + static UntypedValue* expected_group_dps(PlayerbotAI* ai) { return new ExpectedGroupDpsValue(ai); } + }; #endif diff --git a/src/strategy/warlock/WarlockTriggers.h b/src/strategy/warlock/WarlockTriggers.h index 4a2ae9eb..3050012c 100644 --- a/src/strategy/warlock/WarlockTriggers.h +++ b/src/strategy/warlock/WarlockTriggers.h @@ -25,7 +25,13 @@ class SpellstoneTrigger : public BuffTrigger bool IsActive() override; }; -DEBUFF_CHECKISOWNER_TRIGGER(CurseOfAgonyTrigger, "curse of agony"); +// DEBUFF_CHECKISOWNER_TRIGGER(CurseOfAgonyTrigger, "curse of agony"); +class CurseOfAgonyTrigger : public DebuffTrigger +{ + public: + CurseOfAgonyTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "curse of agony", 1, true, 20000.0f) { } +}; + DEBUFF_CHECKISOWNER_TRIGGER(CorruptionTrigger, "corruption"); DEBUFF_CHECKISOWNER_TRIGGER(SiphonLifeTrigger, "siphon life"); @@ -38,7 +44,7 @@ class CorruptionOnAttackerTrigger : public DebuffOnAttackerTrigger class CastCurseOfAgonyOnAttackerTrigger : public DebuffOnAttackerTrigger { public: - CastCurseOfAgonyOnAttackerTrigger(PlayerbotAI* botAI) : DebuffOnAttackerTrigger(botAI, "curse of agony", true) { } + CastCurseOfAgonyOnAttackerTrigger(PlayerbotAI* botAI) : DebuffOnAttackerTrigger(botAI, "curse of agony", true, 20000.0f) { } }; class SiphonLifeOnAttackerTrigger : public DebuffOnAttackerTrigger