mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-11-29 15:58:20 +08:00
smarter dps target and tank target
This commit is contained in:
@@ -292,9 +292,9 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
|
||||
if (tab == 2)
|
||||
engine->addStrategies("tank", "tank assist", "aoe", "mark rti", nullptr);
|
||||
else if (player->getLevel() < 30 || tab == 0)
|
||||
engine->addStrategies("arms", "aoe", "dps assist", "threat", "behind", nullptr);
|
||||
engine->addStrategies("arms", "aoe", "dps assist", "threat", /*"behind",*/ nullptr);
|
||||
else
|
||||
engine->addStrategies("fury", "aoe", "dps assist", "threat", "behind", nullptr);
|
||||
engine->addStrategies("fury", "aoe", "dps assist", "threat", /*"behind",*/ nullptr);
|
||||
break;
|
||||
case CLASS_SHAMAN:
|
||||
if (tab == 0)
|
||||
@@ -335,9 +335,9 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
|
||||
break;
|
||||
case CLASS_ROGUE:
|
||||
if (tab == ROGUE_TAB_ASSASSINATION) {
|
||||
engine->addStrategies("melee", "threat", "dps assist", "aoe", "behind", nullptr);
|
||||
engine->addStrategies("melee", "threat", "dps assist", "aoe", /*"behind",*/ nullptr);
|
||||
} else {
|
||||
engine->addStrategies("dps", "threat", "dps assist", "aoe", "behind", nullptr);
|
||||
engine->addStrategies("dps", "threat", "dps assist", "aoe", /*"behind",*/ nullptr);
|
||||
}
|
||||
break;
|
||||
case CLASS_WARLOCK:
|
||||
@@ -436,10 +436,10 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
|
||||
engine->addStrategies("caster", "caster aoe", nullptr);
|
||||
|
||||
if (player->getClass() == CLASS_DRUID && tab == 1)
|
||||
engine->addStrategies("behind", "dps", nullptr);
|
||||
engine->addStrategies(/*"behind",*/ "dps", nullptr);
|
||||
|
||||
if (player->getClass() == CLASS_ROGUE)
|
||||
engine->addStrategies("behind", "stealth", nullptr);
|
||||
engine->addStrategies(/*"behind",*/ "stealth", nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1399,6 +1399,19 @@ bool PlayerbotAI::IsRangedDpsAssistantOfIndex(Player* player, int index)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PlayerbotAI::HasAggro(Unit* unit)
|
||||
{
|
||||
if (!unit) {
|
||||
return false;
|
||||
}
|
||||
bool isMT = IsMainTank(bot);
|
||||
Unit* victim = unit->GetVictim();
|
||||
if (victim && (victim->GetGUID() == bot->GetGUID() || (!isMT && victim->ToPlayer() && IsTank(victim->ToPlayer())))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int32 PlayerbotAI::GetGroupSlotIndex(Player* player)
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
|
||||
@@ -338,6 +338,7 @@ class PlayerbotAI : public PlayerbotAIBase
|
||||
bool IsAssistTankOfIndex(Player* player, int index);
|
||||
bool IsHealAssistantOfIndex(Player* player, int index);
|
||||
bool IsRangedDpsAssistantOfIndex(Player* player, int index);
|
||||
bool HasAggro(Unit* unit);
|
||||
int32 GetGroupSlotIndex(Player* player);
|
||||
int32 GetRangedIndex(Player* player);
|
||||
int32 GetClassIndex(Player* player, uint8_t cls);
|
||||
|
||||
@@ -443,7 +443,7 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
|
||||
if (master && isRandomAccount && master->GetLevel() < bot->GetLevel()) {
|
||||
// PlayerbotFactory factory(bot, master->getLevel());
|
||||
// factory.Randomize(false);
|
||||
uint32 mixedGearScore = PlayerbotAI::GetMixedGearScore(master, false, false, 12) * sPlayerbotAIConfig->autoInitEquipLevelLimitRatio;
|
||||
uint32 mixedGearScore = PlayerbotAI::GetMixedGearScore(master, true, false, 12) * sPlayerbotAIConfig->autoInitEquipLevelLimitRatio;
|
||||
PlayerbotFactory factory(bot, master->getLevel(), ITEM_QUALITY_LEGENDARY, mixedGearScore);
|
||||
factory.Randomize(false);
|
||||
}
|
||||
@@ -584,14 +584,13 @@ std::string const PlayerbotHolder::ProcessBotCommand(std::string const cmd, Obje
|
||||
}
|
||||
else if (cmd == "init=auto")
|
||||
{
|
||||
uint32 mixedGearScore = PlayerbotAI::GetMixedGearScore(master, false, false, 12) * sPlayerbotAIConfig->autoInitEquipLevelLimitRatio;
|
||||
uint32 mixedGearScore = PlayerbotAI::GetMixedGearScore(master, true, false, 12) * sPlayerbotAIConfig->autoInitEquipLevelLimitRatio;
|
||||
PlayerbotFactory factory(bot, master->getLevel(), ITEM_QUALITY_LEGENDARY, mixedGearScore);
|
||||
factory.Randomize(false);
|
||||
return "ok, gear score limit: " + std::to_string(mixedGearScore / (ITEM_QUALITY_EPIC + 1)) + "(for epic)";
|
||||
}
|
||||
else if (cmd.starts_with("init=") && sscanf(cmd.c_str(), "init=%d", &gs) != -1)
|
||||
{
|
||||
// uint32 mixedGearScore = PlayerbotAI::GetMixedGearScore(master, false, false, 12) * sPlayerbotAIConfig->autoInitEquipLevelLimitRatio;
|
||||
PlayerbotFactory factory(bot, master->getLevel(), ITEM_QUALITY_LEGENDARY, gs);
|
||||
factory.Randomize(false);
|
||||
return "ok, gear score limit: " + std::to_string(gs / (ITEM_QUALITY_EPIC + 1)) + "(for epic)";
|
||||
|
||||
@@ -23,20 +23,34 @@ enum StrategyType : uint32
|
||||
STRATEGY_TYPE_MELEE = 64
|
||||
};
|
||||
|
||||
enum ActionPriority
|
||||
{
|
||||
ACTION_IDLE = 0,
|
||||
ACTION_NORMAL = 10,
|
||||
ACTION_HIGH = 20,
|
||||
ACTION_MOVE = 30,
|
||||
ACTION_INTERRUPT = 40,
|
||||
ACTION_DISPEL = 50,
|
||||
ACTION_RAID = 60,
|
||||
ACTION_LIGHT_HEAL = 10,
|
||||
ACTION_MEDIUM_HEAL = 20,
|
||||
ACTION_CRITICAL_HEAL = 30,
|
||||
ACTION_EMERGENCY = 90
|
||||
};
|
||||
// enum ActionPriority
|
||||
// {
|
||||
// ACTION_IDLE = 0,
|
||||
// ACTION_DEFAULT = 5,
|
||||
// ACTION_NORMAL = 10,
|
||||
// ACTION_HIGH = 20,
|
||||
// ACTION_MOVE = 30,
|
||||
// ACTION_INTERRUPT = 40,
|
||||
// ACTION_DISPEL = 50,
|
||||
// ACTION_RAID = 60,
|
||||
// ACTION_LIGHT_HEAL = 10,
|
||||
// ACTION_MEDIUM_HEAL = 20,
|
||||
// ACTION_CRITICAL_HEAL = 30,
|
||||
// ACTION_EMERGENCY = 90
|
||||
// };
|
||||
|
||||
static float ACTION_IDLE = 0.0f;
|
||||
static float ACTION_DEFAULT = 5.0f;
|
||||
static float ACTION_NORMAL = 10.0f;
|
||||
static float ACTION_HIGH = 20.0f;
|
||||
static float ACTION_MOVE = 30.0f;
|
||||
static float ACTION_INTERRUPT = 40.0f;
|
||||
static float ACTION_DISPEL = 50.0f;
|
||||
static float ACTION_RAID = 60.0f;
|
||||
static float ACTION_LIGHT_HEAL = 10.0f;
|
||||
static float ACTION_MEDIUM_HEAL = 20.0f;
|
||||
static float ACTION_CRITICAL_HEAL = 30.0f;
|
||||
static float ACTION_EMERGENCY = 90.0f;
|
||||
|
||||
class Strategy : public PlayerbotAIAware
|
||||
{
|
||||
|
||||
@@ -80,15 +80,15 @@ BloodDKStrategy::BloodDKStrategy(PlayerbotAI* botAI) : GenericDKStrategy(botAI)
|
||||
NextAction** BloodDKStrategy::getDefaultActions()
|
||||
{
|
||||
return NextAction::array(0,
|
||||
new NextAction("rune strike", ACTION_NORMAL + 8),
|
||||
new NextAction("icy touch", ACTION_NORMAL + 7),
|
||||
new NextAction("heart strike", ACTION_NORMAL + 6),
|
||||
new NextAction("blood strike", ACTION_NORMAL + 5),
|
||||
new NextAction("dancing rune weapon", ACTION_NORMAL + 4),
|
||||
new NextAction("death coil", ACTION_NORMAL + 3),
|
||||
new NextAction("plague strike", ACTION_NORMAL + 2),
|
||||
new NextAction("horn of winter", ACTION_NORMAL + 1),
|
||||
new NextAction("melee", ACTION_NORMAL),
|
||||
new NextAction("rune strike", ACTION_DEFAULT + 0.8f),
|
||||
new NextAction("icy touch", ACTION_DEFAULT + 0.7f),
|
||||
new NextAction("heart strike", ACTION_DEFAULT + 0.6f),
|
||||
new NextAction("blood strike", ACTION_DEFAULT + 0.5f),
|
||||
new NextAction("dancing rune weapon", ACTION_DEFAULT + 0.4f),
|
||||
new NextAction("death coil", ACTION_DEFAULT + 0.3f),
|
||||
new NextAction("plague strike", ACTION_DEFAULT + 0.2f),
|
||||
new NextAction("horn of winter", ACTION_DEFAULT + 0.1f),
|
||||
new NextAction("melee", ACTION_DEFAULT),
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
||||
@@ -78,12 +78,12 @@ FrostDKStrategy::FrostDKStrategy(PlayerbotAI* botAI) : GenericDKStrategy(botAI)
|
||||
NextAction** FrostDKStrategy::getDefaultActions()
|
||||
{
|
||||
return NextAction::array(0,
|
||||
new NextAction("obliterate", ACTION_NORMAL + 5),
|
||||
new NextAction("frost strike", ACTION_NORMAL + 4),
|
||||
new NextAction("obliterate", ACTION_DEFAULT + 0.5f),
|
||||
new NextAction("frost strike", ACTION_DEFAULT + 0.4f),
|
||||
// new NextAction("death strike", ACTION_NORMAL + 3),
|
||||
new NextAction("empower rune weapon", ACTION_NORMAL + 2),
|
||||
new NextAction("horn of winter", ACTION_NORMAL),
|
||||
new NextAction("melee", ACTION_NORMAL),
|
||||
new NextAction("empower rune weapon", ACTION_DEFAULT + 0.2f),
|
||||
new NextAction("horn of winter", ACTION_DEFAULT + 0.1f),
|
||||
new NextAction("melee", ACTION_DEFAULT),
|
||||
NULL
|
||||
);
|
||||
}
|
||||
|
||||
@@ -72,15 +72,15 @@ UnholyDKStrategy::UnholyDKStrategy(PlayerbotAI* botAI) : GenericDKStrategy(botAI
|
||||
NextAction** UnholyDKStrategy::getDefaultActions()
|
||||
{
|
||||
return NextAction::array(0,
|
||||
new NextAction("scourge strike", ACTION_NORMAL + 7),
|
||||
new NextAction("blood strike", ACTION_NORMAL + 6),
|
||||
new NextAction("ghoul frenzy", ACTION_NORMAL + 5),
|
||||
new NextAction("summon gargoyle", ACTION_NORMAL + 4),
|
||||
new NextAction("death coil", ACTION_NORMAL + 3),
|
||||
new NextAction("plague strike", ACTION_NORMAL + 2),
|
||||
new NextAction("icy touch", ACTION_NORMAL + 1),
|
||||
new NextAction("horn of winter", ACTION_NORMAL),
|
||||
new NextAction("melee", ACTION_NORMAL),
|
||||
new NextAction("scourge strike", ACTION_DEFAULT + 0.8f),
|
||||
new NextAction("blood strike", ACTION_DEFAULT + 0.7f),
|
||||
new NextAction("ghoul frenzy", ACTION_DEFAULT + 0.6f),
|
||||
new NextAction("summon gargoyle", ACTION_DEFAULT + 0.5f),
|
||||
new NextAction("death coil", ACTION_DEFAULT + 0.4f),
|
||||
new NextAction("plague strike", ACTION_DEFAULT + 0.3f),
|
||||
new NextAction("icy touch", ACTION_DEFAULT + 0.2f),
|
||||
new NextAction("horn of winter", ACTION_DEFAULT + 0.1f),
|
||||
new NextAction("melee", ACTION_DEFAULT),
|
||||
nullptr);
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ void DpsHunterStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
|
||||
triggers.push_back(new TriggerNode("black arrow", NextAction::array(0, new NextAction("black arrow", 15.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("viper sting", 23.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("hunter's mark", NextAction::array(0, new NextAction("hunter's mark", 11.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("hunter's mark", NextAction::array(0, new NextAction("hunter's mark", 31.0f), nullptr)));
|
||||
triggers.push_back(new TriggerNode("concussive shot on snare target", NextAction::array(0, new NextAction("concussive shot", 20.0f), nullptr)));
|
||||
// triggers.push_back(new TriggerNode("no pet", NextAction::array(0, new NextAction("call pet", 21.0f), NULL)));
|
||||
triggers.push_back(new TriggerNode("hunters pet low health", NextAction::array(0, new NextAction("mend pet", 21.0f), NULL)));
|
||||
|
||||
@@ -406,7 +406,7 @@ bool TankAssistTrigger::IsActive()
|
||||
if (!tankTarget || currentTarget == tankTarget)
|
||||
return false;
|
||||
|
||||
return currentTarget->GetVictim() == AI_VALUE(Unit*, "self target");
|
||||
return AI_VALUE2(bool, "has aggro", "current target");
|
||||
}
|
||||
|
||||
bool IsBehindTargetTrigger::IsActive()
|
||||
|
||||
@@ -21,7 +21,8 @@ bool HasAggroValue::Calculate()
|
||||
if (!victim) {
|
||||
return true;
|
||||
}
|
||||
if (victim && (victim->GetGUID() == bot->GetGUID() || (victim->ToPlayer() && botAI->IsMainTank(victim->ToPlayer())))) {
|
||||
bool isMT = botAI->IsMainTank(bot);
|
||||
if (victim && (victim->GetGUID() == bot->GetGUID() || (!isMT && victim->ToPlayer() && botAI->IsTank(victim->ToPlayer())))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -58,13 +58,70 @@ class FindMaxThreatGapTargetStrategy : public FindTargetStrategy
|
||||
float minThreat;
|
||||
};
|
||||
|
||||
class FindTargetSmartStrategy : public FindTargetStrategy
|
||||
{
|
||||
public:
|
||||
FindTargetSmartStrategy(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_time) > GetIntervalLevel(old_time)) {
|
||||
return true;
|
||||
}
|
||||
int32_t interval = GetIntervalLevel(new_time);
|
||||
if (interval == 2 || interval == 0) {
|
||||
return new_time < old_time;
|
||||
}
|
||||
// dont switch targets when all of them with low health
|
||||
if (botAI->GetAiObjectContext()->GetValue<Unit*>("current target")->Get() == old_unit) {
|
||||
return false;
|
||||
}
|
||||
return new_time > old_time;
|
||||
}
|
||||
int32_t GetIntervalLevel(float time) {
|
||||
if (time >= 5 && time <= 20) {
|
||||
return 2;
|
||||
}
|
||||
if (time < 5) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
float dps_;
|
||||
float targetExpectedLifeTime;
|
||||
};
|
||||
|
||||
Unit* DpsTargetValue::Calculate()
|
||||
{
|
||||
Unit* rti = RtiTargetValue::Calculate();
|
||||
if (rti)
|
||||
return rti;
|
||||
|
||||
FindLeastHpTargetStrategy strategy(botAI);
|
||||
// FindLeastHpTargetStrategy strategy(botAI);
|
||||
float dps = AI_VALUE(float, "expected group dps");
|
||||
FindTargetSmartStrategy strategy(botAI, dps);
|
||||
// FindMaxThreatGapTargetStrategy strategy(botAI);
|
||||
return TargetValue::FindTarget(&strategy);
|
||||
}
|
||||
|
||||
@@ -21,11 +21,11 @@ float ExpectedGroupDpsValue::Calculate()
|
||||
float dps_num;
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group) {
|
||||
dps_num = 1;
|
||||
dps_num = 0.7;
|
||||
} else {
|
||||
dps_num = group->GetMembersCount() * 0.7;
|
||||
}
|
||||
uint32 mixedGearScore = PlayerbotAI::GetMixedGearScore(bot, false, false, 12);
|
||||
uint32 mixedGearScore = PlayerbotAI::GetMixedGearScore(bot, true, false, 12);
|
||||
// efficiency record based on rare gear level, is there better calculation method?
|
||||
// float dps_efficiency = 1;
|
||||
float basic_dps;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
|
||||
#include "AttackersValue.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "TankTargetValue.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
@@ -41,8 +42,53 @@ class FindTargetForTankStrategy : public FindNonCcTargetStrategy
|
||||
float minThreat;
|
||||
};
|
||||
|
||||
class FindTankTargetSmartStrategy : public FindTargetStrategy
|
||||
{
|
||||
public:
|
||||
FindTankTargetSmartStrategy(PlayerbotAI* botAI) : FindTargetStrategy(botAI) { }
|
||||
|
||||
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 (!result || IsBetter(attacker, result)) {
|
||||
result = attacker;
|
||||
}
|
||||
}
|
||||
bool IsBetter(Unit* new_unit, Unit* old_unit) {
|
||||
Player* bot = botAI->GetBot();
|
||||
float new_threat = new_unit->GetThreatMgr().GetThreat(bot);
|
||||
float old_threat = old_unit->GetThreatMgr().GetThreat(bot);
|
||||
float new_dis = bot->GetDistance(new_unit);
|
||||
float old_dis = bot->GetDistance(old_unit);
|
||||
// hasAggro? -> withinMelee? -> threat
|
||||
if (GetIntervalLevel(new_unit) > GetIntervalLevel(old_unit)) {
|
||||
return true;
|
||||
}
|
||||
int32_t interval = GetIntervalLevel(new_unit);
|
||||
if (interval == 1) {
|
||||
return new_dis < old_dis;
|
||||
}
|
||||
return new_threat < old_threat;
|
||||
}
|
||||
int32_t GetIntervalLevel(Unit* unit) {
|
||||
if (!botAI->HasAggro(unit)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
Unit* TankTargetValue::Calculate()
|
||||
{
|
||||
FindTargetForTankStrategy strategy(botAI);
|
||||
// FindTargetForTankStrategy strategy(botAI);
|
||||
FindTankTargetSmartStrategy strategy(botAI);
|
||||
return FindTarget(&strategy);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user