mirror of
https://github.com/mod-playerbots/mod-playerbots.git
synced 2026-02-26 05:15:54 +00:00
Add aggressive non combat targeting strategy (#2117)
# Pull Request Tired of failing that escort quest because your bots stood and watched while the escort npc got swarmed and killed? Tired of your bots standing around doing nothing while the npc you are supposed to be guarding for 5 minutes is getting attacked? Don't want to use the grind strategy because it is too heavy-handed and has too many restrictions? Look no further! Just do "nc +aggressive" and your bots will pick a fight with anything they can in a 30 yard radius. The aggressive targetting is a stripped down version of the grind target. ## Feature Evaluation Please answer the following: - Describe the **minimum logic** required to achieve the intended behavior? Add a strategy, action, and targetting that will cause bots to attack nearby enemies when out of combat. - Describe the **cheapest implementation** that produces an acceptable result? Hopefully this is the cheapest. - Describe the **runtime cost** when this logic executes across many bots? Minimal runtime cost as this strategy needs to be added specifically to bots. --- ## How to Test the Changes - Add a bot to party, or use selfbot - Give them the aggressive strategy via "nc +aggressive" - They should attack anything within 30 yards. - If it is a bot with a master, the 30 yards should be centered around the master not the bot (prevent chaining from enemy to enemy) ## Complexity & Impact Does this change add new decision branches? ``` [] No [x] Yes (**explain below**) Only for bots that have the added strategy, adds decision to attack nearby targets when out of combat. ``` Does this change increase per-bot or per-tick processing? ``` [] No [x] Yes (**describe and justify impact**) Minimal increase to only bots that have this strategy added. ``` Could this logic scale poorly under load? ``` [x] No [ ] Yes (**explain why**) ``` --- ## Defaults & Configuration Does this change modify default bot behavior? ``` [x] No [ ] Yes (**explain why**) ``` If this introduces more advanced or AI-heavy logic: ``` [x] Lightweight mode remains the default [ ] More complex behavior is optional and thereby configurable ``` --- ## AI Assistance Was AI assistance (e.g. ChatGPT or similar tools) used while working on this change? ``` [ ] No [x] Yes (**explain below**) ``` Claude is used to explore the codebase to find similar implementations to be used for examples. --- ## Final Checklist - [x] Stability is not compromised - [x] Performance impact is understood, tested, and acceptable - [x] Added logic complexity is justified and explained - [x] Documentation updated if needed --- ## Notes for Reviewers Anything that significantly improves realism at the cost of stability or performance should be carefully discussed before merging.
This commit is contained in:
@@ -125,6 +125,7 @@ public:
|
||||
creators["runaway"] = &ActionContext::runaway;
|
||||
creators["stay"] = &ActionContext::stay;
|
||||
creators["sit"] = &ActionContext::sit;
|
||||
creators["aggressive target"] = &ActionContext::aggressive_target;
|
||||
creators["attack anything"] = &ActionContext::attack_anything;
|
||||
creators["attack least hp target"] = &ActionContext::attack_least_hp_target;
|
||||
creators["attack enemy player"] = &ActionContext::attack_enemy_player;
|
||||
@@ -315,6 +316,7 @@ private:
|
||||
static Action* suggest_what_to_do(PlayerbotAI* botAI) { return new SuggestWhatToDoAction(botAI); }
|
||||
static Action* suggest_trade(PlayerbotAI* botAI) { return new SuggestTradeAction(botAI); }
|
||||
static Action* suggest_dungeon(PlayerbotAI* botAI) { return new SuggestDungeonAction(botAI); }
|
||||
static Action* aggressive_target(PlayerbotAI* botAI) { return new AggressiveTargetAction(botAI); }
|
||||
static Action* attack_anything(PlayerbotAI* botAI) { return new AttackAnythingAction(botAI); }
|
||||
static Action* attack_least_hp_target(PlayerbotAI* botAI) { return new AttackLeastHpTargetAction(botAI); }
|
||||
static Action* attack_enemy_player(PlayerbotAI* botAI) { return new AttackEnemyPlayerAction(botAI); }
|
||||
|
||||
@@ -30,6 +30,14 @@ bool AttackEnemyFlagCarrierAction::isUseful()
|
||||
PlayerHasFlag::IsCapturingFlag(bot);
|
||||
}
|
||||
|
||||
bool AggressiveTargetAction::isUseful()
|
||||
{
|
||||
if (bot->IsInCombat())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DropTargetAction::Execute(Event /*event*/)
|
||||
{
|
||||
Unit* target = context->GetValue<Unit*>("current target")->Get();
|
||||
|
||||
@@ -35,6 +35,15 @@ public:
|
||||
std::string const GetTargetName() override { return "tank target"; }
|
||||
};
|
||||
|
||||
class AggressiveTargetAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
AggressiveTargetAction(PlayerbotAI* botAI) : AttackAction(botAI, "aggressive target") {}
|
||||
|
||||
std::string const GetTargetName() override { return "aggressive target"; }
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class AttackAnythingAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
|
||||
20
src/Ai/Base/Strategy/AggressiveStrategy.cpp
Normal file
20
src/Ai/Base/Strategy/AggressiveStrategy.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "AggressiveStrategy.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
void AggressiveStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"no target",
|
||||
{
|
||||
NextAction("aggressive target", 4.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
22
src/Ai/Base/Strategy/AggressiveStrategy.h
Normal file
22
src/Ai/Base/Strategy/AggressiveStrategy.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_AGGRESSIVESTRATEGY_H
|
||||
#define _PLAYERBOT_AGGRESSIVESTRATEGY_H
|
||||
|
||||
#include "NonCombatStrategy.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class AggressiveStrategy : public NonCombatStrategy
|
||||
{
|
||||
public:
|
||||
AggressiveStrategy(PlayerbotAI* botAI) : NonCombatStrategy(botAI) {}
|
||||
|
||||
std::string const getName() override { return "aggressive"; }
|
||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -6,6 +6,7 @@
|
||||
#ifndef _PLAYERBOT_STRATEGYCONTEXT_H
|
||||
#define _PLAYERBOT_STRATEGYCONTEXT_H
|
||||
|
||||
#include "AggressiveStrategy.h"
|
||||
#include "AttackEnemyPlayersStrategy.h"
|
||||
#include "BattlegroundStrategy.h"
|
||||
#include "CastTimeStrategy.h"
|
||||
@@ -61,6 +62,7 @@ public:
|
||||
creators["gather"] = &StrategyContext::gather;
|
||||
creators["emote"] = &StrategyContext::emote;
|
||||
creators["passive"] = &StrategyContext::passive;
|
||||
creators["aggressive"] = &StrategyContext::aggressive;
|
||||
creators["save mana"] = &StrategyContext::auto_save_mana;
|
||||
creators["food"] = &StrategyContext::food;
|
||||
creators["chat"] = &StrategyContext::chat;
|
||||
@@ -144,6 +146,7 @@ private:
|
||||
static Strategy* gather(PlayerbotAI* botAI) { return new GatherStrategy(botAI); }
|
||||
static Strategy* emote(PlayerbotAI* botAI) { return new EmoteStrategy(botAI); }
|
||||
static Strategy* passive(PlayerbotAI* botAI) { return new PassiveStrategy(botAI); }
|
||||
static Strategy* aggressive(PlayerbotAI* botAI) { return new AggressiveStrategy(botAI); }
|
||||
// static Strategy* conserve_mana(PlayerbotAI* botAI) { return new ConserveManaStrategy(botAI); }
|
||||
static Strategy* auto_save_mana(PlayerbotAI* botAI) { return new HealerAutoSaveManaStrategy(botAI); }
|
||||
static Strategy* food(PlayerbotAI* botAI) { return new UseFoodStrategy(botAI); }
|
||||
|
||||
66
src/Ai/Base/Value/AggressiveTargetValue.cpp
Normal file
66
src/Ai/Base/Value/AggressiveTargetValue.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "AggressiveTargetValue.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
#include "ServerFacade.h"
|
||||
#include "SharedDefines.h"
|
||||
|
||||
Unit* AggressiveTargetValue::Calculate()
|
||||
{
|
||||
Player* master = GetMaster();
|
||||
|
||||
if (master && (master == bot || master->GetMapId() != bot->GetMapId() || master->IsBeingTeleported() ||
|
||||
!GET_PLAYERBOT_AI(master)))
|
||||
master = nullptr;
|
||||
|
||||
GuidVector targets = AI_VALUE(GuidVector, "possible targets");
|
||||
if (targets.empty())
|
||||
return nullptr;
|
||||
|
||||
float aggroRange = sPlayerbotAIConfig.aggroDistance;
|
||||
float distance = 0;
|
||||
Unit* result = nullptr;
|
||||
|
||||
for (ObjectGuid const guid : targets)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(guid);
|
||||
if (!unit || !unit->IsAlive())
|
||||
continue;
|
||||
|
||||
if (!unit->IsInWorld() || unit->IsDuringRemoveFromWorld())
|
||||
continue;
|
||||
|
||||
if (unit->ToCreature() && !unit->ToCreature()->GetCreatureTemplate()->lootid &&
|
||||
bot->GetReactionTo(unit) >= REP_NEUTRAL)
|
||||
continue;
|
||||
|
||||
if (!bot->IsHostileTo(unit) && unit->GetNpcFlags() != UNIT_NPC_FLAG_NONE)
|
||||
continue;
|
||||
|
||||
if (abs(bot->GetPositionZ() - unit->GetPositionZ()) > INTERACTION_DISTANCE)
|
||||
continue;
|
||||
|
||||
if (!bot->InBattleground() && master && botAI->HasStrategy("follow", BotState::BOT_STATE_NON_COMBAT) &&
|
||||
ServerFacade::instance().GetDistance2d(master, unit) > aggroRange)
|
||||
continue;
|
||||
|
||||
if (!bot->IsWithinLOSInMap(unit))
|
||||
continue;
|
||||
|
||||
if (bot->GetDistance(unit) > aggroRange)
|
||||
continue;
|
||||
|
||||
float newdistance = bot->GetDistance(unit);
|
||||
if (!result || (newdistance < distance))
|
||||
{
|
||||
distance = newdistance;
|
||||
result = unit;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
22
src/Ai/Base/Value/AggressiveTargetValue.h
Normal file
22
src/Ai/Base/Value/AggressiveTargetValue.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_AGGRESSIVETARGETVALUE_H
|
||||
#define _PLAYERBOT_AGGRESSIVETARGETVALUE_H
|
||||
|
||||
#include "TargetValue.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
class Unit;
|
||||
|
||||
class AggressiveTargetValue : public TargetValue
|
||||
{
|
||||
public:
|
||||
AggressiveTargetValue(PlayerbotAI* botAI, std::string const name = "aggressive target") : TargetValue(botAI, name) {}
|
||||
|
||||
Unit* Calculate() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -7,6 +7,7 @@
|
||||
#define _PLAYERBOT_VALUECONTEXT_H
|
||||
|
||||
#include "ActiveSpellValue.h"
|
||||
#include "AggressiveTargetValue.h"
|
||||
#include "AlwaysLootListValue.h"
|
||||
#include "AoeHealValues.h"
|
||||
#include "AoeValues.h"
|
||||
@@ -144,6 +145,7 @@ public:
|
||||
creators["pet target"] = &ValueContext::pet_target;
|
||||
creators["old target"] = &ValueContext::old_target;
|
||||
creators["grind target"] = &ValueContext::grind_target;
|
||||
creators["aggressive target"] = &ValueContext::aggressive_target;
|
||||
creators["rti target"] = &ValueContext::rti_target;
|
||||
creators["rti cc target"] = &ValueContext::rti_cc_target;
|
||||
creators["duel target"] = &ValueContext::duel_target;
|
||||
@@ -459,6 +461,7 @@ private:
|
||||
static UntypedValue* current_cc_target(PlayerbotAI* botAI) { return new CurrentCcTargetValue(botAI); }
|
||||
static UntypedValue* pet_target(PlayerbotAI* botAI) { return new PetTargetValue(botAI); }
|
||||
static UntypedValue* grind_target(PlayerbotAI* botAI) { return new GrindTargetValue(botAI); }
|
||||
static UntypedValue* aggressive_target(PlayerbotAI* botAI) { return new AggressiveTargetValue(botAI); }
|
||||
static UntypedValue* rti_target(PlayerbotAI* botAI) { return new RtiTargetValue(botAI); }
|
||||
static UntypedValue* rti_cc_target(PlayerbotAI* botAI) { return new RtiCcTargetValue(botAI); }
|
||||
static UntypedValue* duel_target(PlayerbotAI* botAI) { return new DuelTargetValue(botAI); }
|
||||
|
||||
Reference in New Issue
Block a user