Tank face and dps behind

This commit is contained in:
Yunfan Li
2024-09-24 11:46:39 +08:00
parent c710345e8b
commit eea652f5d5
10 changed files with 216 additions and 83 deletions

View File

@@ -279,11 +279,11 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
}
if (sPlayerbotAIConfig->autoSaveMana)
{
engine->addStrategy("smana", false);
engine->addStrategy("save mana", false);
}
if (sPlayerbotAIConfig->autoAvoidAoe && facade->HasRealPlayerMaster())
{
engine->addStrategy("aaoe", false);
engine->addStrategy("avoid aoe", false);
}
engine->addStrategy("formation", false);
switch (player->getClass())
@@ -388,6 +388,12 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
break;
}
if (PlayerbotAI::IsTank(player, true)) {
engine->addStrategy("tank face", false);
}
if (PlayerbotAI::IsMelee(player, true) && PlayerbotAI::IsDps(player, true)) {
engine->addStrategy("behind", false);
}
if (facade->IsRealPlayer() || sRandomPlayerbotMgr->IsRandomBot(player))
{
@@ -599,7 +605,7 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
if (sPlayerbotAIConfig->autoSaveMana)
{
nonCombatEngine->addStrategy("smana", false);
nonCombatEngine->addStrategy("save mana", false);
}
if ((sRandomPlayerbotMgr->IsRandomBot(player)) && !player->InBattleground())
{

View File

@@ -2023,7 +2023,7 @@ bool PlayerbotAI::IsDps(Player* player, bool bySpec)
{
return true;
}
if (tab == DRUID_TAB_FERAL && !IsTank(player))
if (tab == DRUID_TAB_FERAL && !IsTank(player, bySpec))
{
return true;
}

View File

@@ -60,8 +60,7 @@ public:
creators["gather"] = &StrategyContext::gather;
creators["emote"] = &StrategyContext::emote;
creators["passive"] = &StrategyContext::passive;
// creators["conserve mana"] = &StrategyContext::conserve_mana;
creators["smana"] = &StrategyContext::auto_save_mana;
creators["save mana"] = &StrategyContext::auto_save_mana;
creators["food"] = &StrategyContext::food;
creators["chat"] = &StrategyContext::chat;
creators["default"] = &StrategyContext::world_packet;
@@ -113,7 +112,8 @@ public:
creators["group"] = &StrategyContext::group;
creators["guild"] = &StrategyContext::guild;
creators["grind"] = &StrategyContext::grind;
creators["aaoe"] = &StrategyContext::avoid_aoe;
creators["avoid aoe"] = &StrategyContext::avoid_aoe;
creators["tank face"] = &StrategyContext::tank_face;
creators["move random"] = &StrategyContext::move_random;
creators["formation"] = &StrategyContext::combat_formation;
creators["move from group"] = &StrategyContext::move_from_group;
@@ -179,6 +179,7 @@ private:
static Strategy* guild (PlayerbotAI* botAI) { return new GuildStrategy(botAI); }
static Strategy* grind(PlayerbotAI* botAI) { return new GrindingStrategy(botAI); }
static Strategy* avoid_aoe(PlayerbotAI* botAI) { return new AvoidAoeStrategy(botAI); }
static Strategy* tank_face(PlayerbotAI* botAI) { return new TankFaceStrategy(botAI); }
static Strategy* move_random(PlayerbotAI* ai) { return new MoveRandomStrategy(ai); }
static Strategy* combat_formation(PlayerbotAI* ai) { return new CombatFormationStrategy(ai); }
static Strategy* move_from_group(PlayerbotAI* botAI) { return new MoveFromGroupStrategy(botAI); }

View File

@@ -91,8 +91,9 @@ public:
creators["reach party member to resurrect"] = &ActionContext::reach_party_member_to_resurrect;
creators["flee"] = &ActionContext::flee;
creators["flee with pet"] = &ActionContext::flee_with_pet;
creators["aaoe"] = &ActionContext::avoid_aoe;
creators["avoid aoe"] = &ActionContext::avoid_aoe;
creators["combat formation move"] = &ActionContext::combat_formation_move;
creators["tank face"] = &ActionContext::tank_face;
creators["disperse set"] = &ActionContext::disperse_set;
creators["gift of the naaru"] = &ActionContext::gift_of_the_naaru;
creators["shoot"] = &ActionContext::shoot;
@@ -276,6 +277,7 @@ private:
static Action* flee_with_pet(PlayerbotAI* botAI) { return new FleeWithPetAction(botAI); }
static Action* avoid_aoe(PlayerbotAI* botAI) { return new AvoidAoeAction(botAI); }
static Action* combat_formation_move(PlayerbotAI* botAI) { return new CombatFormationMoveAction(botAI); }
static Action* tank_face(PlayerbotAI* botAI) { return new TankFaceAction(botAI); }
static Action* disperse_set(PlayerbotAI* botAI) { return new DisperseSetAction(botAI); }
static Action* gift_of_the_naaru(PlayerbotAI* botAI) { return new CastGiftOfTheNaaruAction(botAI); }
static Action* lifeblood(PlayerbotAI* botAI) { return new CastLifeBloodAction(botAI); }

View File

@@ -24,6 +24,7 @@
#include "ObjectDefines.h"
#include "ObjectGuid.h"
#include "PathGenerator.h"
#include "PlayerbotAI.h"
#include "PlayerbotAIConfig.h"
#include "Playerbots.h"
#include "Position.h"
@@ -202,7 +203,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
return false;
float distance = vehicleBase->GetExactDist(x, y, z); // use vehicle distance, not bot
if (distance > sPlayerbotAIConfig->contactDistance)
if (distance > 0.01f)
{
MotionMaster& mm = *vehicleBase->GetMotionMaster(); // need to move vehicle, not bot
mm.Clear();
@@ -217,7 +218,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
else if (exact_waypoint || disableMoveSplinePath || !generatePath)
{
float distance = bot->GetExactDist(x, y, z);
if (distance > sPlayerbotAIConfig->contactDistance)
if (distance > 0.01f)
{
if (bot->IsSitState())
bot->SetStandState(UNIT_STAND_STATE_STAND);
@@ -247,7 +248,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
return false;
}
float distance = bot->GetExactDist(x, y, modifiedZ);
if (distance > sPlayerbotAIConfig->contactDistance)
if (distance > 0.01f)
{
if (bot->IsSitState())
bot->SetStandState(UNIT_STAND_STATE_STAND);
@@ -2140,7 +2141,7 @@ bool MovementAction::FleePosition(Position pos, float radius)
bool MovementAction::CheckLastFlee(float curAngle, std::list<FleeInfo>& infoList)
{
uint32 curTS = getMSTime();
curAngle = fmod(curAngle, 2 * M_PI);
curAngle = Position::NormalizeOrientation(curAngle);
while (!infoList.empty())
{
if (infoList.size() > 10 || infoList.front().timestamp + 5000 < curTS)
@@ -2159,7 +2160,7 @@ bool MovementAction::CheckLastFlee(float curAngle, std::list<FleeInfo>& infoList
{
continue;
}
float revAngle = fmod(info.angle + M_PI, 2 * M_PI);
float revAngle = Position::NormalizeOrientation(info.angle + M_PI);
// angle too close
if (fabs(revAngle - curAngle) < M_PI / 4)
{
@@ -2179,13 +2180,14 @@ bool CombatFormationMoveAction::isUseful()
{
return false;
}
float dis = AI_VALUE(float, "disperse distance");
return dis > 0.0f;
return true;
}
bool CombatFormationMoveAction::Execute(Event event)
{
float dis = AI_VALUE(float, "disperse distance");
if (dis <= 0.0f)
return false;
Player* playerToLeave = NearestGroupMember(dis);
if (playerToLeave && bot->GetExactDist(playerToLeave) < dis)
{
@@ -2197,7 +2199,7 @@ bool CombatFormationMoveAction::Execute(Event event)
return false;
}
Position CombatFormationMoveAction::AverageGroupPos(float dis)
Position CombatFormationMoveAction::AverageGroupPos(float dis, bool ranged, bool self)
{
float averageX = 0, averageY = 0, averageZ = 0;
int cnt = 0;
@@ -2210,10 +2212,19 @@ Position CombatFormationMoveAction::AverageGroupPos(float dis)
for (Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); itr++)
{
Player* member = ObjectAccessor::FindPlayer(itr->guid);
if (!member || !member->IsAlive() || member->GetMapId() != bot->GetMapId() || member->IsCharmed() ||
if (!member)
continue;
if (!self && member == bot)
continue;
if (ranged && !PlayerbotAI::IsRanged(member))
continue;
if (!member->IsAlive() || member->GetMapId() != bot->GetMapId() || member->IsCharmed() ||
sServerFacade->GetDistance2d(bot, member) > dis)
continue;
cnt++;
averageX += member->GetPositionX();
averageY += member->GetPositionY();
averageZ += member->GetPositionZ();
@@ -2224,6 +2235,51 @@ Position CombatFormationMoveAction::AverageGroupPos(float dis)
return Position(averageX, averageY, averageZ);
}
float CombatFormationMoveAction::AverageGroupAngle(Unit* from, bool ranged, bool self)
{
Group* group = bot->GetGroup();
if (!from || !group)
{
return 0.0f;
}
float average = 0.0f;
int cnt = 0;
Group::MemberSlotList const& groupSlot = group->GetMemberSlots();
for (Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); itr++)
{
Player* member = ObjectAccessor::FindPlayer(itr->guid);
if (!member)
continue;
if (!self && member == bot)
continue;
if (ranged && !PlayerbotAI::IsRanged(member))
continue;
if (!member->IsAlive() || member->GetMapId() != bot->GetMapId() || member->IsCharmed() ||
sServerFacade->GetDistance2d(bot, member) > sPlayerbotAIConfig->sightDistance)
continue;
cnt++;
average += from->GetAngle(member);
}
if (cnt)
average /= cnt;
return average;
}
Position CombatFormationMoveAction::GetNearestPosition(const std::vector<Position>& positions)
{
Position result;
for (const Position& pos : positions)
{
if (bot->GetExactDist(pos) < bot->GetExactDist(result))
result = pos;
}
return result;
}
Player* CombatFormationMoveAction::NearestGroupMember(float dis)
{
float nearestDis = 10000.0f;
@@ -2249,6 +2305,60 @@ Player* CombatFormationMoveAction::NearestGroupMember(float dis)
return result;
}
bool TankFaceAction::Execute(Event event)
{
Unit* target = AI_VALUE(Unit*, "current target");
if (!target)
return false;
if (!bot->GetGroup())
return false;
if (!bot->IsWithinMeleeRange(target))
return false;
if (!AI_VALUE2(bool, "has aggro", "current target"))
return false;
float averageAngle = AverageGroupAngle(target, true);
if (averageAngle == 0.0f)
return false;
float deltaAngle = Position::NormalizeOrientation(averageAngle - target->GetAngle(bot));
if (deltaAngle > M_PI)
deltaAngle -= 2.0f * M_PI; // -PI..PI
float tolerable = M_PI_2;
if (fabs(deltaAngle) > tolerable)
return false;
float goodAngle1 = Position::NormalizeOrientation(averageAngle + M_PI * 5 / 8);
float goodAngle2 = Position::NormalizeOrientation(averageAngle - M_PI * 5 / 8);
// if dist < bot->GetMeleeRange(target) / 2, target will move backward
float dist = std::max(bot->GetExactDist(target), bot->GetMeleeRange(target) / 2) - bot->GetCombatReach() - target->GetCombatReach();
std::vector<Position> availablePos;
float x, y, z;
target->GetNearPoint(bot, x, y, z, 0.0f, dist, goodAngle1);
if (bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(),
x, y, z))
{
availablePos.push_back(Position(x, y, z));
}
target->GetNearPoint(bot, x, y, z, 0.0f, dist, goodAngle2);
if (bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(),
x, y, z))
{
availablePos.push_back(Position(x, y, z));
}
if (availablePos.empty())
return false;
Position nearest = GetNearestPosition(availablePos);
return MoveTo(bot->GetMapId(), nearest.GetPositionX(), nearest.GetPositionY(), nearest.GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_COMBAT);
}
bool DisperseSetAction::Execute(Event event)
{
std::string const text = event.getParam();
@@ -2388,25 +2498,43 @@ bool SetBehindTargetAction::Execute(Event event)
if (!target)
return false;
float angle = GetFollowAngle() / 3 + target->GetOrientation() + M_PI;
if (target->GetVictim() == bot)
return false;
// return ChaseTo(target, 0.f, angle);
if (!bot->IsWithinMeleeRange(target))
return false;
float distance = sPlayerbotAIConfig->contactDistance;
float x = target->GetPositionX() + cos(angle) * distance;
float y = target->GetPositionY() + sin(angle) * distance;
float z = target->GetPositionZ();
bot->UpdateGroundPositionZ(x, y, z);
float deltaAngle = Position::NormalizeOrientation(target->GetOrientation() - target->GetAngle(bot));
if (deltaAngle > M_PI)
deltaAngle -= 2.0f * M_PI; // -PI..PI
return MoveTo(bot->GetMapId(), x, y, z);
}
float tolerable = M_PI_2;
bool SetBehindTargetAction::isUseful() { return !AI_VALUE2(bool, "behind", "current target"); }
if (fabs(deltaAngle) > tolerable)
return false;
bool SetBehindTargetAction::isPossible()
float goodAngle1 = Position::NormalizeOrientation(target->GetOrientation() + M_PI * 5 / 8);
float goodAngle2 = Position::NormalizeOrientation(target->GetOrientation() - M_PI * 5 / 8);
float dist = std::max(bot->GetExactDist(target), bot->GetMeleeRange(target) / 2) - bot->GetCombatReach() - target->GetCombatReach();
std::vector<Position> availablePos;
float x, y, z;
target->GetNearPoint(bot, x, y, z, 0.0f, dist, goodAngle1);
if (bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(),
x, y, z))
{
Unit* target = AI_VALUE(Unit*, "current target");
return target && !(target->GetVictim() && target->GetVictim()->GetGUID() == bot->GetGUID());
availablePos.push_back(Position(x, y, z));
}
target->GetNearPoint(bot, x, y, z, 0.0f, dist, goodAngle2);
if (bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(),
x, y, z))
{
availablePos.push_back(Position(x, y, z));
}
if (availablePos.empty())
return false;
Position nearest = GetNearestPosition(availablePos);
return MoveTo(bot->GetMapId(), nearest.GetPositionX(), nearest.GetPositionY(), nearest.GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_COMBAT);
}
bool MoveOutOfCollisionAction::Execute(Event event)

View File

@@ -99,7 +99,7 @@ class AvoidAoeAction : public MovementAction
{
public:
AvoidAoeAction(PlayerbotAI* botAI, int moveInterval = 1000)
: MovementAction(botAI, "aaoe"), moveInterval(moveInterval)
: MovementAction(botAI, "avoid aoe"), moveInterval(moveInterval)
{
}
@@ -115,11 +115,12 @@ protected:
int moveInterval;
};
class CombatFormationMoveAction : public MovementAction
{
public:
CombatFormationMoveAction(PlayerbotAI* botAI, int moveInterval = 1000)
: MovementAction(botAI, "combat formation move"), moveInterval(moveInterval)
CombatFormationMoveAction(PlayerbotAI* botAI, std::string name = "combat formation move", int moveInterval = 1000)
: MovementAction(botAI, name), moveInterval(moveInterval)
{
}
@@ -127,12 +128,22 @@ public:
bool Execute(Event event) override;
protected:
Position AverageGroupPos(float dis = sPlayerbotAIConfig->sightDistance);
Position AverageGroupPos(float dis = sPlayerbotAIConfig->sightDistance, bool ranged = false, bool self = false);
Player* NearestGroupMember(float dis = sPlayerbotAIConfig->sightDistance);
float AverageGroupAngle(Unit* from, bool ranged = false, bool self = false);
Position GetNearestPosition(const std::vector<Position>& positions);
int lastMoveTimer = 0;
int moveInterval;
};
class TankFaceAction : public CombatFormationMoveAction
{
public:
TankFaceAction(PlayerbotAI* botAI) : CombatFormationMoveAction(botAI, "tank face") {}
bool Execute(Event event) override;
};
class DisperseSetAction : public Action
{
public:
@@ -178,14 +189,12 @@ public:
bool isPossible() override;
};
class SetBehindTargetAction : public MovementAction
class SetBehindTargetAction : public CombatFormationMoveAction
{
public:
SetBehindTargetAction(PlayerbotAI* botAI) : MovementAction(botAI, "set behind") {}
SetBehindTargetAction(PlayerbotAI* botAI) : CombatFormationMoveAction(botAI, "set behind") {}
bool Execute(Event event) override;
bool isUseful() override;
bool isPossible() override;
};
class MoveOutOfCollisionAction : public MovementAction

View File

@@ -70,7 +70,7 @@ AvoidAoeStrategy::AvoidAoeStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
NextAction** AvoidAoeStrategy::getDefaultActions()
{
return NextAction::array(0, new NextAction("aaoe", ACTION_EMERGENCY), nullptr);
return NextAction::array(0, new NextAction("avoid aoe", ACTION_EMERGENCY), nullptr);
}
void AvoidAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
@@ -85,6 +85,17 @@ void AvoidAoeStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
// multipliers.push_back(new AvoidAoeStrategyMultiplier(botAI));
}
TankFaceStrategy::TankFaceStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
NextAction** TankFaceStrategy::getDefaultActions()
{
return NextAction::array(0, new NextAction("tank face", ACTION_MOVE), nullptr);
}
void TankFaceStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
}
NextAction** CombatFormationStrategy::getDefaultActions()
{
return NextAction::array(0, new NextAction("combat formation move", ACTION_NORMAL), nullptr);

View File

@@ -23,12 +23,21 @@ class AvoidAoeStrategy : public Strategy
{
public:
explicit AvoidAoeStrategy(PlayerbotAI* ai);
const std::string getName() override { return "aaoe"; }
const std::string getName() override { return "avoid aoe"; }
NextAction** getDefaultActions() override;
void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
};
class TankFaceStrategy : public Strategy
{
public:
explicit TankFaceStrategy(PlayerbotAI* ai);
const std::string getName() override { return "tank face"; }
NextAction** getDefaultActions() override;
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
};
class CombatFormationStrategy : public Strategy
{
public:

View File

@@ -10,46 +10,10 @@
class PlayerbotAI;
// Yunfan: deprecate old save mana method.
// class ConserveManaMultiplier : public Multiplier
// {
// public:
// ConserveManaMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "conserve mana") { }
// float GetValue(Action* action) override;
// };
// class SaveManaMultiplier : public Multiplier
// {
// public:
// SaveManaMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "save mana") { }
// float GetValue(Action* action) override;
// };
// class ConserveManaStrategy : public Strategy
// {
// public:
// ConserveManaStrategy(PlayerbotAI* botAI) : Strategy(botAI) { }
// void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
// std::string const getName() override { return "conserve mana"; }
// };
// class HealerSaveManaStrategy : public Strategy
// {
// public:
// HealerSaveManaStrategy(PlayerbotAI* botAI) : Strategy(botAI) { }
// void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
// std::string const getName() override { return "healer save mana"; }
// };
class HealerAutoSaveManaMultiplier : public Multiplier
{
public:
HealerAutoSaveManaMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "smana") {}
HealerAutoSaveManaMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "save mana") {}
float GetValue(Action* action) override;
};
@@ -60,7 +24,7 @@ public:
HealerAutoSaveManaStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
std::string const getName() override { return "smana"; }
std::string const getName() override { return "save mana"; }
};
#endif

View File

@@ -4,6 +4,7 @@
*/
#include "IsBehindValue.h"
#include <cmath>
#include "Playerbots.h"
@@ -14,8 +15,10 @@ bool IsBehindValue::Calculate()
return false;
float targetOrientation = target->GetOrientation();
float orientation = bot->GetOrientation();
float distance = bot->GetDistance(target);
return distance <= ATTACK_DISTANCE && abs(targetOrientation - orientation) < M_PI / 2;
float deltaAngle = Position::NormalizeOrientation(targetOrientation - target->GetAngle(bot));
if (deltaAngle > M_PI)
deltaAngle -= 2.0f * M_PI; // -PI..PI
return fabs(deltaAngle) > M_PI_2;
}