/* * 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. */ #include "DpsTargetValue.h" #include "PlayerbotAIConfig.h" #include "Playerbots.h" class FindMaxThreatGapTargetStrategy : public FindTargetStrategy { public: FindMaxThreatGapTargetStrategy(PlayerbotAI* botAI) : FindTargetStrategy(botAI), minThreat(0) {} void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override { if (!attacker->IsAlive()) { return; } if (foundHighPriority) { return; } if (IsHighPriority(attacker)) { result = attacker; foundHighPriority = true; return; } Unit* victim = attacker->GetVictim(); if (!result || CalcThreatGap(attacker, threatMgr) > CalcThreatGap(result, &result->GetThreatMgr())) result = attacker; } float CalcThreatGap(Unit* attacker, ThreatMgr* threatMgr) { Unit* victim = attacker->GetVictim(); return threatMgr->GetThreat(victim) - threatMgr->GetThreat(attacker); } protected: float minThreat; }; // caster class CasterFindTargetSmartStrategy : public FindTargetStrategy { public: CasterFindTargetSmartStrategy(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; } if (foundHighPriority) { return; } if (IsHighPriority(attacker)) { result = attacker; foundHighPriority = true; 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; } int32_t level = GetIntervalLevel(new_unit); if (level % 10 == 2 || level % 10 == 1) { return new_time < old_time; } // dont switch targets when all of them with low health Unit* currentTarget = botAI->GetAiObjectContext()->GetValue("current target")->Get(); if (currentTarget == new_unit) { return true; } if (currentTarget == old_unit) { return false; } return new_time > old_time; } 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; if (time >= 3 && time <= 20) { return level + 2; } if (time > 20) { return level + 1; } return level; } protected: float dps_; float targetExpectedLifeTime; }; // non caster class NonCasterFindTargetSmartStrategy : public FindTargetStrategy { public: NonCasterFindTargetSmartStrategy(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; } if (foundHighPriority) { return; } if (IsHighPriority(attacker)) { result = attacker; foundHighPriority = true; 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); if (level == 10) { return new_time < old_time; } // all targets are far away, choose the closest one return botAI->GetBot()->GetDistance(new_unit) < botAI->GetBot()->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; }; // 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; } if (foundHighPriority) { return; } if (IsHighPriority(attacker)) { result = attacker; foundHighPriority = true; 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(); if (rti) return rti; // FindLeastHpTargetStrategy strategy(botAI); float dps = AI_VALUE(float, "estimated group dps"); 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); } class FindMaxHpTargetStrategy : public FindTargetStrategy { public: FindMaxHpTargetStrategy(PlayerbotAI* botAI) : FindTargetStrategy(botAI), maxHealth(0) {} 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 (!result || result->GetHealth() < attacker->GetHealth()) result = attacker; } protected: float maxHealth; }; Unit* DpsAoeTargetValue::Calculate() { Unit* rti = RtiTargetValue::Calculate(); if (rti) return rti; FindMaxHpTargetStrategy strategy(botAI); return TargetValue::FindTarget(&strategy); }