Compare commits

..

2 Commits

Author SHA1 Message Date
bash
6620def962 auto-format 2025-11-24 11:59:06 +01:00
bash
cddd406b1c removed movementAction changed made due core movement changes 2025-11-24 11:55:03 +01:00
30 changed files with 206 additions and 270 deletions

View File

@@ -420,7 +420,7 @@ void PlayerbotAI::UpdateAIGroupAndMaster()
{
botAI->ChangeStrategy("+follow", BOT_STATE_NON_COMBAT);
if (botAI->GetMaster() == botAI->GetGroupLeader())
if (botAI->GetMaster() == botAI->GetGroupMaster())
botAI->TellMaster("Hello, I follow you!");
else
botAI->TellMaster(!urand(0, 2) ? "Hello!" : "Hi!");
@@ -1477,7 +1477,6 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
break;
case 544:
strategyName = "magtheridon"; // Magtheridon's Lair
break;
case 565:
strategyName = "gruulslair"; // Gruul's Lair
break;
@@ -4093,7 +4092,7 @@ Player* PlayerbotAI::FindNewMaster()
if (!group)
return nullptr;
Player* groupLeader = GetGroupLeader();
Player* groupLeader = GetGroupMaster();
PlayerbotAI* leaderBotAI = GET_PLAYERBOT_AI(groupLeader);
if (!leaderBotAI || leaderBotAI->IsRealPlayer())
return groupLeader;
@@ -4144,7 +4143,7 @@ bool PlayerbotAI::HasActivePlayerMaster() { return master && !GET_PLAYERBOT_AI(m
bool PlayerbotAI::IsAlt() { return HasRealPlayerMaster() && !sRandomPlayerbotMgr->IsRandomBot(bot); }
Player* PlayerbotAI::GetGroupLeader()
Player* PlayerbotAI::GetGroupMaster()
{
if (!bot->InBattleground())
if (Group* group = bot->GetGroup())

View File

@@ -540,7 +540,7 @@ public:
// Get the group leader or the master of the bot.
// Checks if the bot is summoned as alt of a player
bool IsAlt();
Player* GetGroupLeader();
Player* GetGroupMaster();
// Returns a semi-random (cycling) number that is fixed for each bot.
uint32 GetFixedBotNumer(uint32 maxNum = 100, float cyclePerMin = 1);
GrouperType GetGrouperType();

View File

@@ -251,9 +251,9 @@ bool PlayerbotSecurity::CheckLevelFor(PlayerbotSecurityLevel level, bool silent,
out << "I am currently leading a group. I can invite you if you want.";
break;
case PLAYERBOT_DENY_NOT_LEADER:
if (botAI->GetGroupLeader())
if (botAI->GetGroupMaster())
{
out << "I am in a group with " << botAI->GetGroupLeader()->GetName()
out << "I am in a group with " << botAI->GetGroupMaster()->GetName()
<< ". You can ask him for invite.";
}
else

View File

@@ -82,12 +82,12 @@ public:
PlayerbotsPlayerScript() : PlayerScript("PlayerbotsPlayerScript", {
PLAYERHOOK_ON_LOGIN,
PLAYERHOOK_ON_AFTER_UPDATE,
PLAYERHOOK_ON_CHAT,
PLAYERHOOK_ON_CHAT_WITH_CHANNEL,
PLAYERHOOK_ON_CHAT_WITH_GROUP,
PLAYERHOOK_ON_BEFORE_CRITERIA_PROGRESS,
PLAYERHOOK_ON_BEFORE_ACHI_COMPLETE,
PLAYERHOOK_CAN_PLAYER_USE_PRIVATE_CHAT,
PLAYERHOOK_CAN_PLAYER_USE_GROUP_CHAT,
PLAYERHOOK_CAN_PLAYER_USE_GUILD_CHAT,
PLAYERHOOK_CAN_PLAYER_USE_CHANNEL_CHAT,
PLAYERHOOK_ON_GIVE_EXP,
PLAYERHOOK_ON_BEFORE_TELEPORT
}) {}
@@ -164,17 +164,14 @@ public:
{
botAI->HandleCommand(type, msg, player);
// hotfix; otherwise the server will crash when whispering logout
// https://github.com/mod-playerbots/mod-playerbots/pull/1838
// TODO: find the root cause and solve it. (does not happen in party chat)
if (msg == "logout")
return false;
return false;
}
}
return true;
}
bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Group* group) override
void OnPlayerChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Group* group) override
{
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
{
@@ -186,10 +183,9 @@ public:
}
}
}
return true;
}
bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Guild* guild) override
void OnPlayerChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg) override
{
if (type == CHAT_MSG_GUILD)
{
@@ -208,10 +204,9 @@ public:
}
}
}
return true;
}
bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Channel* channel) override
void OnPlayerChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Channel* channel) override
{
if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player))
{
@@ -222,7 +217,6 @@ public:
}
sRandomPlayerbotMgr->HandleCommand(type, msg, player);
return true;
}
bool OnPlayerBeforeAchievementComplete(Player* player, AchievementEntry const* achievement) override

View File

@@ -1480,10 +1480,10 @@ bool RandomPlayerbotMgr::ProcessBot(uint32 bot)
if (!sRandomPlayerbotMgr->IsRandomBot(player))
update = false;
if (player->GetGroup() && botAI->GetGroupLeader())
if (player->GetGroup() && botAI->GetGroupMaster())
{
PlayerbotAI* groupLeaderBotAI = GET_PLAYERBOT_AI(botAI->GetGroupLeader());
if (!groupLeaderBotAI || groupLeaderBotAI->IsRealPlayer())
PlayerbotAI* groupMasterBotAI = GET_PLAYERBOT_AI(botAI->GetGroupMaster());
if (!groupMasterBotAI || groupMasterBotAI->IsRealPlayer())
{
update = false;
}

View File

@@ -121,7 +121,7 @@ public:
creators["shoot"] = &ActionContext::shoot;
creators["follow"] = &ActionContext::follow;
creators["move from group"] = &ActionContext::move_from_group;
creators["flee to group leader"] = &ActionContext::flee_to_group_leader;
creators["flee to master"] = &ActionContext::flee_to_master;
creators["runaway"] = &ActionContext::runaway;
creators["stay"] = &ActionContext::stay;
creators["sit"] = &ActionContext::sit;
@@ -318,7 +318,7 @@ private:
static Action* runaway(PlayerbotAI* botAI) { return new RunAwayAction(botAI); }
static Action* follow(PlayerbotAI* botAI) { return new FollowAction(botAI); }
static Action* move_from_group(PlayerbotAI* botAI) { return new MoveFromGroupAction(botAI); }
static Action* flee_to_group_leader(PlayerbotAI* botAI) { return new FleeToGroupLeaderAction(botAI); }
static Action* flee_to_master(PlayerbotAI* botAI) { return new FleeToMasterAction(botAI); }
static Action* add_gathering_loot(PlayerbotAI* botAI) { return new AddGatheringLootAction(botAI); }
static Action* add_loot(PlayerbotAI* botAI) { return new AddLootAction(botAI); }
static Action* add_all_loot(PlayerbotAI* botAI) { return new AddAllLootAction(botAI); }

View File

@@ -311,7 +311,7 @@ bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldObject* target)
bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldPosition pos)
{
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
Player* groupLeader = botAI->GetGroupLeader();
Player* gmaster = botAI->GetGroupMaster();
Player* realMaster = botAI->GetMaster();
AiObjectContext* context = botAI->GetAiObjectContext();
@@ -327,30 +327,30 @@ bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldPosition pos)
return false;
}
if (!groupLeader || bot == groupLeader)
if (!gmaster || bot == gmaster)
return true;
if (!botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT))
return true;
if (bot->GetDistance(groupLeader) > sPlayerbotAIConfig->rpgDistance * 2)
if (bot->GetDistance(gmaster) > sPlayerbotAIConfig->rpgDistance * 2)
return false;
Formation* formation = AI_VALUE(Formation*, "formation");
float distance = groupLeader->GetDistance2d(pos.getX(), pos.getY());
float distance = gmaster->GetDistance2d(pos.getX(), pos.getY());
if (!botAI->HasActivePlayerMaster() && distance < 50.0f)
{
Player* player = groupLeader;
if (groupLeader && !groupLeader->isMoving() ||
Player* player = gmaster;
if (gmaster && !gmaster->isMoving() ||
PAI_VALUE(WorldPosition, "last long move").distance(pos) < sPlayerbotAIConfig->reactDistance)
return true;
}
if ((inDungeon || !groupLeader->HasPlayerFlag(PLAYER_FLAGS_RESTING)) && realMaster == groupLeader && distance > 5.0f)
if ((inDungeon || !gmaster->HasPlayerFlag(PLAYER_FLAGS_RESTING)) && realMaster == gmaster && distance > 5.0f)
return false;
if (!groupLeader->isMoving() && distance < 25.0f)
if (!gmaster->isMoving() && distance < 25.0f)
return true;
if (distance < formation->GetMaxDistance())

View File

@@ -180,7 +180,7 @@ void ChooseTravelTargetAction::getNewTarget(TravelTarget* newTarget, TravelTarge
void ChooseTravelTargetAction::setNewTarget(TravelTarget* newTarget, TravelTarget* oldTarget)
{
// Tell the master where we are going.
if (!bot->GetGroup() || (botAI->GetGroupLeader() == bot))
if (!bot->GetGroup() || (botAI->GetGroupMaster() == bot))
ReportTravelTarget(newTarget, oldTarget);
// If we are heading to a creature/npc clear it from the ignore list.

View File

@@ -70,7 +70,7 @@ bool FollowAction::isUseful()
if (!target.empty())
fTarget = AI_VALUE(Unit*, target);
else
fTarget = AI_VALUE(Unit*, "group leader");
fTarget = AI_VALUE(Unit*, "master target");
if (fTarget)
{
@@ -114,9 +114,9 @@ bool FollowAction::CanDeadFollow(Unit* target)
return true;
}
bool FleeToGroupLeaderAction::Execute(Event event)
bool FleeToMasterAction::Execute(Event event)
{
Unit* fTarget = AI_VALUE(Unit*, "group leader");
Unit* fTarget = AI_VALUE(Unit*, "master target");
bool canFollow = Follow(fTarget);
if (!canFollow)
{
@@ -146,22 +146,22 @@ bool FleeToGroupLeaderAction::Execute(Event event)
return true;
}
bool FleeToGroupLeaderAction::isUseful()
bool FleeToMasterAction::isUseful()
{
if (!botAI->GetGroupLeader())
if (!botAI->GetGroupMaster())
return false;
if (botAI->GetGroupLeader() == bot)
if (botAI->GetGroupMaster() == bot)
return false;
Unit* target = AI_VALUE(Unit*, "current target");
if (target && botAI->GetGroupLeader()->GetTarget() == target->GetGUID())
if (target && botAI->GetGroupMaster()->GetTarget() == target->GetGUID())
return false;
if (!botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT))
return false;
Unit* fTarget = AI_VALUE(Unit*, "group leader");
Unit* fTarget = AI_VALUE(Unit*, "master target");
if (!CanDeadFollow(fTarget))
return false;

View File

@@ -20,10 +20,10 @@ public:
bool CanDeadFollow(Unit* target);
};
class FleeToGroupLeaderAction : public FollowAction
class FleeToMasterAction : public FollowAction
{
public:
FleeToGroupLeaderAction(PlayerbotAI* botAI) : FollowAction(botAI, "flee to group leader") {}
FleeToMasterAction(PlayerbotAI* botAI) : FollowAction(botAI, "flee to master") {}
bool Execute(Event event) override;
bool isUseful() override;

View File

@@ -141,7 +141,7 @@ bool InviteNearbyToGroupAction::isUseful()
if (group->isRaidGroup() && group->IsFull())
return false;
if (botAI->GetGroupLeader() != bot)
if (botAI->GetGroupMaster() != bot)
return false;
uint32 memberCount = group->GetMembersCount();

View File

@@ -109,22 +109,22 @@ bool LeaveFarAwayAction::isUseful()
if (!bot->GetGroup())
return false;
Player* groupLeader = botAI->GetGroupLeader();
Player* master = botAI->GetGroupMaster();
Player* trueMaster = botAI->GetMaster();
if (!groupLeader || (bot == groupLeader && !botAI->IsRealPlayer()))
if (!master || (bot == master && !botAI->IsRealPlayer()))
return false;
PlayerbotAI* groupLeaderBotAI = nullptr;
if (groupLeader)
groupLeaderBotAI = GET_PLAYERBOT_AI(groupLeader);
if (groupLeader && !groupLeaderBotAI)
PlayerbotAI* masterBotAI = nullptr;
if (master)
masterBotAI = GET_PLAYERBOT_AI(master);
if (master && !masterBotAI)
return false;
if (trueMaster && !GET_PLAYERBOT_AI(trueMaster))
return false;
if (botAI->IsAlt() &&
(!groupLeaderBotAI || groupLeaderBotAI->IsRealPlayer())) // Don't leave group when alt grouped with player groupLeader.
(!masterBotAI || masterBotAI->IsRealPlayer())) // Don't leave group when alt grouped with player master.
return false;
if (botAI->GetGrouperType() == GrouperType::SOLO)
@@ -138,19 +138,19 @@ bool LeaveFarAwayAction::isUseful()
if (dCount > 4 && !botAI->HasRealPlayerMaster())
return true;
if (bot->GetGuildId() == groupLeader->GetGuildId())
if (bot->GetGuildId() == master->GetGuildId())
{
if (bot->GetLevel() > groupLeader->GetLevel() + 5)
if (bot->GetLevel() > master->GetLevel() + 5)
{
if (AI_VALUE(bool, "should get money"))
return false;
}
}
if (abs(int32(groupLeader->GetLevel() - bot->GetLevel())) > 4)
if (abs(int32(master->GetLevel() - bot->GetLevel())) > 4)
return true;
if (bot->GetMapId() != groupLeader->GetMapId() || bot->GetDistance2d(groupLeader) >= 2 * sPlayerbotAIConfig->rpgDistance)
if (bot->GetMapId() != master->GetMapId() || bot->GetDistance2d(master) >= 2 * sPlayerbotAIConfig->rpgDistance)
{
return true;
}

View File

@@ -18,7 +18,7 @@ bool MoveToTravelTargetAction::Execute(Event event)
WorldLocation location = *target->getPosition();
Group* group = bot->GetGroup();
if (group && !urand(0, 1) && bot == botAI->GetGroupLeader() && !bot->IsInCombat())
if (group && !urand(0, 1) && bot == botAI->GetGroupMaster() && !bot->IsInCombat())
{
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{

View File

@@ -192,23 +192,30 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
{
return false;
}
bool generatePath = !bot->IsFlying() && !bot->isSwimming();
bool disableMoveSplinePath =
sPlayerbotAIConfig->disableMoveSplinePath >= 2 ||
(sPlayerbotAIConfig->disableMoveSplinePath == 1 && bot->InBattleground());
bool disableMoveSplinePath = sPlayerbotAIConfig->disableMoveSplinePath >= 2 ||
(sPlayerbotAIConfig->disableMoveSplinePath == 1 && bot->InBattleground());
if (Vehicle* vehicle = bot->GetVehicle())
{
VehicleSeatEntry const* seat = vehicle->GetSeatForPassenger(bot);
Unit* vehicleBase = vehicle->GetBase();
generatePath = !vehicleBase || !vehicleBase->CanFly();
generatePath = vehicleBase->CanFly();
if (!vehicleBase || !seat || !seat->CanControl()) // is passenger and cant move anyway
return false;
float distance = vehicleBase->GetExactDist(x, y, z); // use vehicle distance, not bot
if (distance > 0.01f)
{
DoMovePoint(vehicleBase, x, y, z, generatePath, backwards);
MotionMaster& mm = *vehicleBase->GetMotionMaster(); // need to move vehicle, not bot
mm.Clear();
if (!backwards)
{
mm.MovePoint(0, x, y, z, generatePath);
}
else
{
mm.MovePointBackwards(0, x, y, z, generatePath);
}
float speed = backwards ? vehicleBase->GetSpeed(MOVE_RUN_BACK) : vehicleBase->GetSpeed(MOVE_RUN);
float delay = 1000.0f * (distance / speed);
if (lessDelay)
@@ -234,7 +241,16 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
// bot->CastStop();
// botAI->InterruptSpell();
// }
DoMovePoint(bot, x, y, z, generatePath, backwards);
MotionMaster& mm = *bot->GetMotionMaster();
mm.Clear();
if (!backwards)
{
mm.MovePoint(0, x, y, z, generatePath);
}
else
{
mm.MovePointBackwards(0, x, y, z, generatePath);
}
float delay = 1000.0f * MoveDelay(distance, backwards);
if (lessDelay)
{
@@ -252,7 +268,9 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
Movement::PointsArray path =
SearchForBestPath(x, y, z, modifiedZ, sPlayerbotAIConfig->maxMovementSearchTime, normal_only);
if (modifiedZ == INVALID_HEIGHT)
{
return false;
}
float distance = bot->GetExactDist(x, y, modifiedZ);
if (distance > 0.01f)
{
@@ -264,8 +282,17 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
// bot->CastStop();
// botAI->InterruptSpell();
// }
MotionMaster& mm = *bot->GetMotionMaster();
G3D::Vector3 endP = path.back();
DoMovePoint(bot, x, y, z, generatePath, backwards);
mm.Clear();
if (!backwards)
{
mm.MovePoint(0, x, y, z, generatePath);
}
else
{
mm.MovePointBackwards(0, x, y, z, generatePath);
}
float delay = 1000.0f * MoveDelay(distance, backwards);
if (lessDelay)
{
@@ -482,8 +509,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
// {
// AI_VALUE(LastMovement&, "last area trigger").lastAreaTrigger = entry;
// }
// else
// {
// else {
// LOG_DEBUG("playerbots", "!entry");
// return bot->TeleportTo(movePosition.getMapId(), movePosition.getX(), movePosition.getY(),
// movePosition.getZ(), movePosition.getO(), 0);
@@ -533,7 +559,9 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
// bool goTaxi = bot->ActivateTaxiPathTo({ tEntry->from, tEntry->to }, unit, 1);
// if (botAI->HasCheat(BotCheatMask::gold))
// {
// bot->SetMoney(botMoney);
// }
// LOG_DEBUG("playerbots", "goTaxi");
// return goTaxi;
// }
@@ -960,8 +988,7 @@ bool MovementAction::IsMovingAllowed()
return false;
}
// if (bot->HasUnitMovementFlag(MOVEMENTFLAG_FALLING))
// {
// if (bot->HasUnitMovementFlag(MOVEMENTFLAG_FALLING)) {
// return false;
// }
return bot->GetMotionMaster()->GetCurrentMovementGeneratorType() != FLIGHT_MOTION_TYPE;
@@ -971,95 +998,54 @@ bool MovementAction::Follow(Unit* target, float distance) { return Follow(target
void MovementAction::UpdateMovementState()
{
const bool isCurrentlyRestricted = // see if the bot is currently slowed, rooted, or otherwise unable to move
bot->isFrozen() ||
bot->IsPolymorphed() ||
bot->HasRootAura() ||
bot->HasStunAura() ||
bot->HasConfuseAura() ||
bot->HasUnitState(UNIT_STATE_LOST_CONTROL);
int8 botInLiquidState = bot->GetLiquidData().Status;
// no update movement flags while movement is current restricted.
if (!isCurrentlyRestricted && bot->IsAlive())
if (botInLiquidState == LIQUID_MAP_IN_WATER || botInLiquidState == LIQUID_MAP_UNDER_WATER)
{
// state flags
const auto master = botAI ? botAI->GetMaster() : nullptr; // real player or not
const bool masterIsFlying = master ? master->HasUnitMovementFlag(MOVEMENTFLAG_FLYING) : true;
const bool masterIsSwimming = master ? master->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING) : true;
const auto liquidState = bot->GetLiquidData().Status; // default LIQUID_MAP_NO_WATER
const float gZ = bot->GetMapWaterOrGroundLevel(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ());
const bool wantsToFly = bot->HasIncreaseMountedFlightSpeedAura() || bot->HasFlyAura();
const bool isFlying = bot->HasUnitMovementFlag(MOVEMENTFLAG_FLYING);
const bool isWaterArea = liquidState != LIQUID_MAP_NO_WATER;
const bool isUnderWater = liquidState == LIQUID_MAP_UNDER_WATER;
const bool isInWater = liquidState == LIQUID_MAP_IN_WATER;
const bool isWaterWalking = bot->HasUnitMovementFlag(MOVEMENTFLAG_WATERWALKING);
const bool isSwimming = bot->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING);
const bool wantsToWaterWalk = bot->HasWaterWalkAura();
const bool wantsToSwim = isInWater || isUnderWater;
const bool onGroundZ = (bot->GetPositionZ() < gZ + 1.f) && !isWaterArea;
bool movementFlagsUpdated = false;
// handle water state
if (isWaterArea && !isFlying)
{
// water walking
if (wantsToWaterWalk && !isWaterWalking && !masterIsSwimming)
{
bot->RemoveUnitMovementFlag(MOVEMENTFLAG_SWIMMING);
bot->AddUnitMovementFlag(MOVEMENTFLAG_WATERWALKING);
movementFlagsUpdated = true;
}
// swimming
else if (wantsToSwim && !isSwimming && masterIsSwimming)
{
bot->RemoveUnitMovementFlag(MOVEMENTFLAG_WATERWALKING);
bot->AddUnitMovementFlag(MOVEMENTFLAG_SWIMMING);
movementFlagsUpdated = true;
}
}
else if (isSwimming || isWaterWalking)
{
// reset water flags
bot->RemoveUnitMovementFlag(MOVEMENTFLAG_SWIMMING);
bot->RemoveUnitMovementFlag(MOVEMENTFLAG_WATERWALKING);
movementFlagsUpdated = true;
}
// handle flying state
if (wantsToFly && !isFlying && masterIsFlying)
{
bot->AddUnitMovementFlag(MOVEMENTFLAG_CAN_FLY);
bot->AddUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY);
bot->AddUnitMovementFlag(MOVEMENTFLAG_FLYING);
movementFlagsUpdated = true;
}
else if ((!wantsToFly || onGroundZ) && isFlying)
{
bot->RemoveUnitMovementFlag(MOVEMENTFLAG_CAN_FLY);
bot->RemoveUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY);
bot->RemoveUnitMovementFlag(MOVEMENTFLAG_FLYING);
movementFlagsUpdated = true;
}
// detect if movement restrictions have been lifted, CC just ended.
if (wasMovementRestricted)
movementFlagsUpdated = true; // refresh movement state to ensure animations play correctly
if (movementFlagsUpdated)
bot->SendMovementFlagUpdate();
bot->SetSwim(true);
}
else
{
bot->SetSwim(false);
}
// Save current state for the next check
bool onGround = bot->GetPositionZ() <
bot->GetMapWaterOrGroundLevel(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ()) + 1.0f;
// Keep bot->SendMovementFlagUpdate() withing the if statements to not intefere with bot behavior on
// ground/(shallow) waters
if (!bot->HasUnitMovementFlag(MOVEMENTFLAG_FLYING) &&
bot->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) && !onGround)
{
bot->AddUnitMovementFlag(MOVEMENTFLAG_FLYING);
bot->SendMovementFlagUpdate();
}
else if (bot->HasUnitMovementFlag(MOVEMENTFLAG_FLYING) &&
(!bot->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) || onGround))
{
bot->RemoveUnitMovementFlag(MOVEMENTFLAG_FLYING);
bot->SendMovementFlagUpdate();
}
// See if the bot is currently slowed, rooted, or otherwise unable to move
bool isCurrentlyRestricted = bot->isFrozen() || bot->IsPolymorphed() || bot->HasRootAura() || bot->HasStunAura() ||
bot->HasConfuseAura() || bot->HasUnitState(UNIT_STATE_LOST_CONTROL);
// Detect if movement restrictions have been lifted
if (wasMovementRestricted && !isCurrentlyRestricted && bot->IsAlive())
{
// CC just ended - refresh movement state to ensure animations play correctly
bot->SendMovementFlagUpdate();
}
// Save current state for the next check
wasMovementRestricted = isCurrentlyRestricted;
// Temporary speed increase in group
// if (botAI->HasRealPlayerMaster())
// {
// if (botAI->HasRealPlayerMaster()) {
// bot->SetSpeedRate(MOVE_RUN, 1.1f);
// }
// else
// {
// } else {
// bot->SetSpeedRate(MOVE_RUN, 1.0f);
// }
// check if target is not reachable (from Vmangos)
@@ -1068,7 +1054,7 @@ void MovementAction::UpdateMovementState()
// {
// if (Unit* pTarget = sServerFacade->GetChaseTarget(bot))
// {
// if (!bot->IsWithinMeleeRange(pTarget) && pTarget->IsCreature())
// if (!bot->IsWithinMeleeRange(pTarget) && pTarget->GetTypeId() == TYPEID_UNIT)
// {
// float angle = bot->GetAngle(pTarget);
// float distance = 5.0f;
@@ -1102,7 +1088,7 @@ void MovementAction::UpdateMovementState()
// {
// if (Unit* pTarget = sServerFacade->GetChaseTarget(bot))
// {
// if (pTarget != botAI->GetGroupLeader())
// if (pTarget != botAI->GetGroupMaster())
// return;
// if (!bot->IsWithinMeleeRange(pTarget))
@@ -1696,8 +1682,7 @@ bool MovementAction::MoveInside(uint32 mapId, float x, float y, float z, float d
// float MovementAction::SearchBestGroundZForPath(float x, float y, float z, bool generatePath, float range, bool
// normal_only, float step)
// {
// if (!generatePath)
// {
// if (!generatePath) {
// return z;
// }
// float min_length = 100000.0f;
@@ -1708,12 +1693,10 @@ bool MovementAction::MoveInside(uint32 mapId, float x, float y, float z, float d
// modified_z = bot->GetMapWaterOrGroundLevel(x, y, z + delta);
// PathGenerator gen(bot);
// gen.CalculatePath(x, y, modified_z);
// if (gen.GetPathType() == PATHFIND_NORMAL && gen.getPathLength() < min_length)
// {
// if (gen.GetPathType() == PATHFIND_NORMAL && gen.getPathLength() < min_length) {
// min_length = gen.getPathLength();
// current_z = modified_z;
// if (abs(current_z - z) < 0.5f)
// {
// if (abs(current_z - z) < 0.5f) {
// return current_z;
// }
// }
@@ -1722,34 +1705,30 @@ bool MovementAction::MoveInside(uint32 mapId, float x, float y, float z, float d
// modified_z = bot->GetMapWaterOrGroundLevel(x, y, z + delta);
// PathGenerator gen(bot);
// gen.CalculatePath(x, y, modified_z);
// if (gen.GetPathType() == PATHFIND_NORMAL && gen.getPathLength() < min_length)
// {
// if (gen.GetPathType() == PATHFIND_NORMAL && gen.getPathLength() < min_length) {
// min_length = gen.getPathLength();
// current_z = modified_z;
// if (abs(current_z - z) < 0.5f)
// if (abs(current_z - z) < 0.5f) {
// return current_z;
// }
// }
// }
// for (delta = range / 2 + step; delta <= range; delta += 2) {
// modified_z = bot->GetMapWaterOrGroundLevel(x, y, z + delta);
// PathGenerator gen(bot);
// gen.CalculatePath(x, y, modified_z);
// if (gen.GetPathType() == PATHFIND_NORMAL && gen.getPathLength() < min_length)
// {
// if (gen.GetPathType() == PATHFIND_NORMAL && gen.getPathLength() < min_length) {
// min_length = gen.getPathLength();
// current_z = modified_z;
// if (abs(current_z - z) < 0.5f)
// {
// if (abs(current_z - z) < 0.5f) {
// return current_z;
// }
// }
// }
// if (current_z == INVALID_HEIGHT && normal_only)
// {
// if (current_z == INVALID_HEIGHT && normal_only) {
// return INVALID_HEIGHT;
// }
// if (current_z == INVALID_HEIGHT && !normal_only)
// {
// if (current_z == INVALID_HEIGHT && !normal_only) {
// return z;
// }
// return current_z;
@@ -1824,46 +1803,6 @@ const Movement::PointsArray MovementAction::SearchForBestPath(float x, float y,
return result;
}
void MovementAction::DoMovePoint(Unit* unit, float x, float y, float z, bool generatePath, bool backwards)
{
if (!unit)
return;
MotionMaster* mm = unit->GetMotionMaster();
if (!mm)
return;
// enable water walking
if (unit->HasUnitMovementFlag(MOVEMENTFLAG_WATERWALKING))
{
float gZ = unit->GetMapWaterOrGroundLevel(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ());
unit->UpdatePosition(unit->GetPositionX(), unit->GetPositionY(), gZ, false);
// z = gZ; no overwrite Z axe otherwise you cant steer the bots into swimming when water walking.
}
mm->Clear();
if (backwards)
{
mm->MovePointBackwards(
/*id*/ 0,
/*coords*/ x, y, z,
/*generatePath*/ generatePath,
/*forceDestination*/ false);
return;
}
else
{
mm->MovePoint(
/*id*/ 0,
/*coords*/ x, y, z,
/*forcedMovement*/ FORCED_MOVEMENT_NONE,
/*speed*/ 0.f,
/*orientation*/ 0.f,
/*generatePath*/ generatePath, // true => terrain path (2d mmap); false => straight spline (3d vmap)
/*forceDestination*/ false);
}
}
bool FleeAction::Execute(Event event)
{
return MoveAway(AI_VALUE(Unit*, "current target"), sPlayerbotAIConfig->fleeDistance, true);
@@ -2124,8 +2063,8 @@ Position MovementAction::BestPositionForMeleeToFlee(Position pos, float radius)
if (currentTarget)
{
// Normally, move to left or right is the best position
bool isTanking = (!currentTarget->isFrozen()
&& !currentTarget->HasRootAura()) && (currentTarget->GetVictim() == bot);
bool isTanking =
(!currentTarget->isFrozen() && !currentTarget->HasRootAura()) && (currentTarget->GetVictim() == bot);
float angle = bot->GetAngle(currentTarget);
float angleLeft = angle + (float)M_PI / 2;
float angleRight = angle - (float)M_PI / 2;
@@ -2541,7 +2480,9 @@ bool RearFlankAction::isUseful()
{
Unit* target = AI_VALUE(Unit*, "current target");
if (!target)
{
return false;
}
// Need to double the front angle check to account for mirrored angle.
bool inFront = target->HasInArc(2.f * minAngle, bot);
@@ -2556,7 +2497,9 @@ bool RearFlankAction::Execute(Event event)
{
Unit* target = AI_VALUE(Unit*, "current target");
if (!target)
{
return false;
}
float angle = frand(minAngle, maxAngle);
float baseDistance = bot->GetMeleeRange(target) * 0.5f;
@@ -2663,7 +2606,7 @@ bool DisperseSetAction::Execute(Event event)
return true;
}
bool RunAwayAction::Execute(Event event) { return Flee(AI_VALUE(Unit*, "group leader")); }
bool RunAwayAction::Execute(Event event) { return Flee(AI_VALUE(Unit*, "master target")); }
bool MoveToLootAction::Execute(Event event)
{
@@ -2850,7 +2793,7 @@ bool MoveAwayFromCreatureAction::Execute(Event event)
// Find all creatures with the specified Id
std::vector<Unit*> creatures;
for (auto const& guid : targets)
for (const auto& guid : targets)
{
Unit* unit = botAI->GetUnit(guid);
if (unit && (alive && unit->IsAlive()) && unit->GetEntry() == creatureId)

View File

@@ -13,10 +13,10 @@ bool RandomBotUpdateAction::Execute(Event event)
if (!sRandomPlayerbotMgr->IsRandomBot(bot))
return false;
if (bot->GetGroup() && botAI->GetGroupLeader())
if (bot->GetGroup() && botAI->GetGroupMaster())
{
PlayerbotAI* groupLeaderBotAI = GET_PLAYERBOT_AI(botAI->GetGroupLeader());
if (!groupLeaderBotAI || groupLeaderBotAI->IsRealPlayer())
PlayerbotAI* groupMasterBotAI = GET_PLAYERBOT_AI(botAI->GetGroupMaster());
if (!groupMasterBotAI || groupMasterBotAI->IsRealPlayer())
return true;
}

View File

@@ -168,15 +168,15 @@ bool AutoReleaseSpiritAction::ShouldAutoRelease() const
if (!bot->GetGroup())
return true;
Player* groupLeader = botAI->GetGroupLeader();
if (!groupLeader || groupLeader == bot)
Player* groupMaster = botAI->GetGroupMaster();
if (!groupMaster || groupMaster == bot)
return true;
if (!botAI->HasActivePlayerMaster())
return true;
if (botAI->HasActivePlayerMaster() &&
groupLeader->GetMapId() == bot->GetMapId() &&
groupMaster->GetMapId() == bot->GetMapId() &&
bot->GetMap() &&
(bot->GetMap()->IsRaid() || bot->GetMap()->IsDungeon()))
{
@@ -184,7 +184,7 @@ bool AutoReleaseSpiritAction::ShouldAutoRelease() const
}
return sServerFacade->IsDistanceGreaterThan(
AI_VALUE2(float, "distance", "group leader"),
AI_VALUE2(float, "distance", "master target"),
sPlayerbotAIConfig->sightDistance);
}

View File

@@ -16,4 +16,4 @@ bool ResetInstancesAction::Execute(Event event)
return true;
}
bool ResetInstancesAction::isUseful() { return botAI->GetGroupLeader() == bot; };
bool ResetInstancesAction::isUseful() { return botAI->GetGroupMaster() == bot; };

View File

@@ -17,14 +17,14 @@
bool ReviveFromCorpseAction::Execute(Event event)
{
Player* groupLeader = botAI->GetGroupLeader();
Player* master = botAI->GetGroupMaster();
Corpse* corpse = bot->GetCorpse();
// follow group Leader when group Leader revives
// follow master when master revives
WorldPacket& p = event.getPacket();
if (!p.empty() && p.GetOpcode() == CMSG_RECLAIM_CORPSE && groupLeader && !corpse && bot->IsAlive())
if (!p.empty() && p.GetOpcode() == CMSG_RECLAIM_CORPSE && master && !corpse && bot->IsAlive())
{
if (sServerFacade->IsDistanceLessThan(AI_VALUE2(float, "distance", "group leader"),
if (sServerFacade->IsDistanceLessThan(AI_VALUE2(float, "distance", "master target"),
sPlayerbotAIConfig->farDistance))
{
if (!botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT))
@@ -43,10 +43,10 @@ bool ReviveFromCorpseAction::Execute(Event event)
// time(nullptr))
// return false;
if (groupLeader)
if (master)
{
if (!GET_PLAYERBOT_AI(groupLeader) && groupLeader->isDead() && groupLeader->GetCorpse() &&
sServerFacade->IsDistanceLessThan(AI_VALUE2(float, "distance", "group leader"),
if (!GET_PLAYERBOT_AI(master) && master->isDead() && master->GetCorpse() &&
sServerFacade->IsDistanceLessThan(AI_VALUE2(float, "distance", "master target"),
sPlayerbotAIConfig->farDistance))
return false;
}
@@ -79,15 +79,15 @@ bool FindCorpseAction::Execute(Event event)
if (bot->InBattleground())
return false;
Player* groupLeader = botAI->GetGroupLeader();
Player* master = botAI->GetGroupMaster();
Corpse* corpse = bot->GetCorpse();
if (!corpse)
return false;
// if (groupLeader)
// if (master)
// {
// if (!GET_PLAYERBOT_AI(groupLeader) &&
// sServerFacade->IsDistanceLessThan(AI_VALUE2(float, "distance", "group leader"),
// if (!GET_PLAYERBOT_AI(master) &&
// sServerFacade->IsDistanceLessThan(AI_VALUE2(float, "distance", "master target"),
// sPlayerbotAIConfig->farDistance)) return false;
// }
@@ -110,20 +110,20 @@ bool FindCorpseAction::Execute(Event event)
WorldPosition botPos(bot);
WorldPosition corpsePos(corpse);
WorldPosition moveToPos = corpsePos;
WorldPosition leaderPos(groupLeader);
WorldPosition masterPos(master);
float reclaimDist = CORPSE_RECLAIM_RADIUS - 5.0f;
float corpseDist = botPos.distance(corpsePos);
int64 deadTime = time(nullptr) - corpse->GetGhostTime();
bool moveToLeader = groupLeader && groupLeader != bot && leaderPos.fDist(corpsePos) < reclaimDist;
bool moveToMaster = master && master != bot && masterPos.fDist(corpsePos) < reclaimDist;
// Should we ressurect? If so, return false.
if (corpseDist < reclaimDist)
{
if (moveToLeader) // We are near group leader.
if (moveToMaster) // We are near master.
{
if (botPos.fDist(leaderPos) < sPlayerbotAIConfig->spellDistance)
if (botPos.fDist(masterPos) < sPlayerbotAIConfig->spellDistance)
return false;
}
else if (deadTime > 8 * MINUTE) // We have walked too long already.
@@ -140,8 +140,8 @@ bool FindCorpseAction::Execute(Event event)
// If we are getting close move to a save ressurrection spot instead of just the corpse.
if (corpseDist < sPlayerbotAIConfig->reactDistance)
{
if (moveToLeader)
moveToPos = leaderPos;
if (moveToMaster)
moveToPos = masterPos;
else
{
FleeManager manager(bot, reclaimDist, 0.0, urand(0, 1), moveToPos);
@@ -215,12 +215,12 @@ GraveyardStruct const* SpiritHealerAction::GetGrave(bool startZone)
if (!startZone && ClosestGrave)
return ClosestGrave;
if (botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT) && botAI->GetGroupLeader() && botAI->GetGroupLeader() != bot)
if (botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT) && botAI->GetGroupMaster() && botAI->GetGroupMaster() != bot)
{
Player* groupLeader = botAI->GetGroupLeader();
if (groupLeader && groupLeader != bot)
Player* master = botAI->GetGroupMaster();
if (master && master != bot)
{
ClosestGrave = sGraveyard->GetClosestGraveyard(groupLeader, bot->GetTeamId());
ClosestGrave = sGraveyard->GetClosestGraveyard(master, bot->GetTeamId());
if (ClosestGrave)
return ClosestGrave;

View File

@@ -35,8 +35,8 @@ bool RewardAction::Execute(Event event)
return true;
}
Unit* groupLeaderUnit = AI_VALUE(Unit*, "group leader");
if (groupLeaderUnit && Reward(itemId, groupLeaderUnit))
Unit* mtar = AI_VALUE(Unit*, "master target");
if (mtar && Reward(itemId, mtar))
return true;
botAI->TellError("Cannot talk to quest giver");

View File

@@ -76,7 +76,7 @@ void RpgHelper::setFacing(GuidPosition guidPosition)
void RpgHelper::setDelay(bool waitForGroup)
{
if (!botAI->HasRealPlayerMaster() || (waitForGroup && botAI->GetGroupLeader() == bot && bot->GetGroup()))
if (!botAI->HasRealPlayerMaster() || (waitForGroup && botAI->GetGroupMaster() == bot && bot->GetGroup()))
botAI->SetNextCheckDelay(sPlayerbotAIConfig->rpgDelay);
else
botAI->SetNextCheckDelay(sPlayerbotAIConfig->rpgDelay / 5);

View File

@@ -22,8 +22,8 @@ bool SecurityCheckAction::Execute(Event event)
ItemQualities threshold = group->GetLootThreshold();
if (method == MASTER_LOOT || method == FREE_FOR_ALL || threshold > ITEM_QUALITY_UNCOMMON)
{
if ((botAI->GetGroupLeader()->GetSession()->GetSecurity() == SEC_PLAYER) &&
(!bot->GetGuildId() || bot->GetGuildId() != botAI->GetGroupLeader()->GetGuildId()))
if ((botAI->GetGroupMaster()->GetSession()->GetSecurity() == SEC_PLAYER) &&
(!bot->GetGuildId() || bot->GetGuildId() != botAI->GetGroupMaster()->GetGuildId()))
{
botAI->TellError("I will play with this loot type only if I'm in your guild :/");
botAI->ChangeStrategy("+passive,+stay", BOT_STATE_NON_COMBAT);

View File

@@ -22,7 +22,7 @@ bool OutOfReactRangeAction::Execute(Event event)
bool OutOfReactRangeAction::isUseful()
{
bool canFollow = Follow(AI_VALUE(Unit*, "group leader"));
bool canFollow = Follow(AI_VALUE(Unit*, "master target"));
if (!canFollow)
{
return false;

View File

@@ -64,7 +64,7 @@ bool MoveToDarkPortalAction::Execute(Event event)
{
if (bot->GetGroup())
if (bot->GetGroup()->GetLeaderGUID() != bot->GetGUID() &&
!GET_PLAYERBOT_AI(GET_PLAYERBOT_AI(bot)->GetGroupLeader()))
!GET_PLAYERBOT_AI(GET_PLAYERBOT_AI(bot)->GetGroupMaster()))
return false;
if (bot->GetLevel() > 57)

View File

@@ -22,8 +22,8 @@ bool UnstealthTrigger::IsActive()
return botAI->HasAura("stealth", bot) && !AI_VALUE(uint8, "attacker count") &&
(AI_VALUE2(bool, "moving", "self target") &&
((botAI->GetMaster() &&
sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "group leader"), 10.0f) &&
AI_VALUE2(bool, "moving", "group leader")) ||
sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "master target"), 10.0f) &&
AI_VALUE2(bool, "moving", "master target")) ||
!AI_VALUE(uint8, "attacker count")));
}

View File

@@ -213,7 +213,7 @@ PartyMemberToHealOutOfSpellRangeTrigger::PartyMemberToHealOutOfSpellRangeTrigger
bool FarFromMasterTrigger::IsActive()
{
return sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "group leader"), distance);
return sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "master target"), distance);
}
bool TooCloseToCreatureTrigger::TooCloseToCreature(uint32 creatureId, float range, bool alive)

View File

@@ -62,7 +62,7 @@ class MeleeFormation : public FollowFormation
public:
MeleeFormation(PlayerbotAI* botAI) : FollowFormation(botAI, "melee") {}
std::string const GetTargetName() override { return "group leader"; }
std::string const GetTargetName() override { return "master target"; }
};
class QueueFormation : public FollowFormation

View File

@@ -28,7 +28,7 @@ GuidVector GroupMembersValue::Calculate()
bool IsFollowingPartyValue::Calculate()
{
if (botAI->GetGroupLeader() == bot)
if (botAI->GetGroupMaster() == bot)
return true;
if (botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT))
@@ -39,15 +39,15 @@ bool IsFollowingPartyValue::Calculate()
bool IsNearLeaderValue::Calculate()
{
Player* groupLeader = botAI->GetGroupLeader();
Player* groupMaster = botAI->GetGroupMaster();
if (!groupLeader)
if (!groupMaster)
return false;
if (groupLeader == bot)
if (groupMaster == bot)
return true;
return sServerFacade->GetDistance2d(bot, botAI->GetGroupLeader()) < sPlayerbotAIConfig->sightDistance;
return sServerFacade->GetDistance2d(bot, botAI->GetGroupMaster()) < sPlayerbotAIConfig->sightDistance;
}
bool BoolANDValue::Calculate()
@@ -154,8 +154,8 @@ bool GroupReadyValue::Calculate()
// We only wait for members that are in range otherwise we might be waiting for bots stuck in dead loops
// forever.
if (botAI->GetGroupLeader() &&
sServerFacade->GetDistance2d(member, botAI->GetGroupLeader()) > sPlayerbotAIConfig->sightDistance)
if (botAI->GetGroupMaster() &&
sServerFacade->GetDistance2d(member, botAI->GetGroupMaster()) > sPlayerbotAIConfig->sightDistance)
continue;
if (member->GetHealthPct() < sPlayerbotAIConfig->almostFullHealth)

View File

@@ -3,8 +3,8 @@
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "GroupLeaderValue.h"
#include "MasterTargetValue.h"
#include "Playerbots.h"
Unit* GroupLeaderValue::Calculate() { return botAI->GetGroupLeader(); }
Unit* MasterTargetValue::Calculate() { return botAI->GetGroupMaster(); }

View File

@@ -3,18 +3,18 @@
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_GROUPLEADERVALUE_H
#define _PLAYERBOT_GROUPLEADERVALUE_H
#ifndef _PLAYERBOT_MASTERTARGETVALUE_H
#define _PLAYERBOT_MASTERTARGETVALUE_H
#include "Value.h"
class PlayerbotAI;
class Unit;
class GroupLeaderValue : public UnitCalculatedValue
class MasterTargetValue : public UnitCalculatedValue
{
public:
GroupLeaderValue(PlayerbotAI* botAI, std::string const name = "group leader") : UnitCalculatedValue(botAI, name)
MasterTargetValue(PlayerbotAI* botAI, std::string const name = "master target") : UnitCalculatedValue(botAI, name)
{
}

View File

@@ -30,7 +30,6 @@
#include "Formations.h"
#include "GrindTargetValue.h"
#include "GroupValues.h"
#include "GroupLeaderValue.h"
#include "GuildValues.h"
#include "HasAvailableLootValue.h"
#include "HasTotemValue.h"
@@ -52,6 +51,7 @@
#include "LootStrategyValue.h"
#include "MaintenanceValues.h"
#include "ManaSaveLevelValue.h"
#include "MasterTargetValue.h"
#include "NearestAdsValue.h"
#include "NearestCorpsesValue.h"
#include "NearestFriendlyPlayersValue.h"
@@ -130,7 +130,7 @@ public:
creators["party member to resurrect"] = &ValueContext::party_member_to_resurrect;
creators["current target"] = &ValueContext::current_target;
creators["self target"] = &ValueContext::self_target;
creators["group leader"] = &ValueContext::group_leader;
creators["master target"] = &ValueContext::master;
creators["line target"] = &ValueContext::line_target;
creators["tank target"] = &ValueContext::tank_target;
creators["dps target"] = &ValueContext::dps_target;
@@ -439,7 +439,7 @@ private:
static UntypedValue* current_target(PlayerbotAI* botAI) { return new CurrentTargetValue(botAI); }
static UntypedValue* old_target(PlayerbotAI* botAI) { return new CurrentTargetValue(botAI); }
static UntypedValue* self_target(PlayerbotAI* botAI) { return new SelfTargetValue(botAI); }
static UntypedValue* group_leader(PlayerbotAI* botAI) { return new GroupLeaderValue(botAI); }
static UntypedValue* master(PlayerbotAI* botAI) { return new MasterTargetValue(botAI); }
static UntypedValue* line_target(PlayerbotAI* botAI) { return new LineTargetValue(botAI); }
static UntypedValue* tank_target(PlayerbotAI* botAI) { return new TankTargetValue(botAI); }
static UntypedValue* dps_target(PlayerbotAI* botAI) { return new DpsTargetValue(botAI); }