mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-11-29 15:58:20 +08:00
684 lines
21 KiB
C++
684 lines
21 KiB
C++
/*
|
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, 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 "Formations.h"
|
|
|
|
#include "Arrow.h"
|
|
#include "Event.h"
|
|
#include "Map.h"
|
|
#include "Playerbots.h"
|
|
#include "ServerFacade.h"
|
|
|
|
WorldLocation Formation::NullLocation = WorldLocation();
|
|
|
|
bool IsSameLocation(WorldLocation const& a, WorldLocation const& b)
|
|
{
|
|
return a.GetPositionX() == b.GetPositionX() && a.GetPositionY() == b.GetPositionY() &&
|
|
a.GetPositionZ() == b.GetPositionZ() && a.GetMapId() == b.GetMapId();
|
|
}
|
|
|
|
bool Formation::IsNullLocation(WorldLocation const& loc) { return IsSameLocation(loc, Formation::NullLocation); }
|
|
|
|
WorldLocation MoveAheadFormation::GetLocation()
|
|
{
|
|
Player* master = botAI->GetGroupMaster();
|
|
if (!master || master == bot)
|
|
return WorldLocation();
|
|
|
|
WorldLocation loc = GetLocationInternal();
|
|
if (Formation::IsNullLocation(loc))
|
|
return loc;
|
|
|
|
float x = loc.GetPositionX();
|
|
float y = loc.GetPositionY();
|
|
float z = loc.GetPositionZ();
|
|
|
|
// if (master->isMoving())
|
|
// {
|
|
// float ori = master->GetOrientation();
|
|
// float x1 = x + sPlayerbotAIConfig->tooCloseDistance * cos(ori);
|
|
// float y1 = y + sPlayerbotAIConfig->tooCloseDistance * sin(ori);
|
|
// float ground = master->GetMap()->GetHeight(x1, y1, z);
|
|
// if (ground > INVALID_HEIGHT)
|
|
// {
|
|
// x = x1;
|
|
// y = y1;
|
|
// }
|
|
// }
|
|
|
|
// float ground = master->GetMap()->GetHeight(x, y, z);
|
|
// if (ground <= INVALID_HEIGHT)
|
|
// return Formation::NullLocation;
|
|
|
|
// z += CONTACT_DISTANCE;
|
|
// bot->UpdateAllowedPositionZ(x, y, z);
|
|
return WorldLocation(master->GetMapId(), x, y, z);
|
|
}
|
|
|
|
class MeleeFormation : public FollowFormation
|
|
{
|
|
public:
|
|
MeleeFormation(PlayerbotAI* botAI) : FollowFormation(botAI, "melee") {}
|
|
|
|
std::string const GetTargetName() override { return "master target"; }
|
|
};
|
|
|
|
class QueueFormation : public FollowFormation
|
|
{
|
|
public:
|
|
QueueFormation(PlayerbotAI* botAI) : FollowFormation(botAI, "queue") {}
|
|
|
|
std::string const GetTargetName() override { return "line target"; }
|
|
};
|
|
|
|
class NearFormation : public MoveAheadFormation
|
|
{
|
|
public:
|
|
NearFormation(PlayerbotAI* botAI) : MoveAheadFormation(botAI, "near") {}
|
|
|
|
WorldLocation GetLocationInternal() override
|
|
{
|
|
Player* master = GetMaster();
|
|
if (!master)
|
|
return WorldLocation();
|
|
|
|
float range = sPlayerbotAIConfig->followDistance;
|
|
float angle = GetFollowAngle();
|
|
float x = master->GetPositionX() + cos(angle) * range;
|
|
float y = master->GetPositionY() + sin(angle) * range;
|
|
float z = master->GetPositionZ();
|
|
if (!master->GetMap()->CheckCollisionAndGetValidCoords(master, master->GetPositionX(), master->GetPositionY(),
|
|
master->GetPositionZ(), x, y, z))
|
|
{
|
|
x = master->GetPositionX() + cos(angle) * range;
|
|
y = master->GetPositionY() + sin(angle) * range;
|
|
z = master->GetPositionZ() + master->GetHoverHeight();
|
|
master->UpdateAllowedPositionZ(x, y, z);
|
|
}
|
|
return WorldLocation(master->GetMapId(), x, y, z);
|
|
}
|
|
|
|
float GetMaxDistance() override { return sPlayerbotAIConfig->followDistance; }
|
|
};
|
|
|
|
class ChaosFormation : public MoveAheadFormation
|
|
{
|
|
public:
|
|
ChaosFormation(PlayerbotAI* botAI) : MoveAheadFormation(botAI, "chaos"), lastChangeTime(0) {}
|
|
|
|
WorldLocation GetLocationInternal() override
|
|
{
|
|
Player* master = botAI->GetGroupMaster();
|
|
if (!master)
|
|
return WorldLocation();
|
|
|
|
float range = sPlayerbotAIConfig->followDistance;
|
|
float angle = GetFollowAngle();
|
|
|
|
time_t now = time(nullptr);
|
|
if (!lastChangeTime || now - lastChangeTime >= 3)
|
|
{
|
|
Player* master = botAI->GetGroupMaster();
|
|
if (!master)
|
|
return WorldLocation();
|
|
|
|
float range = sPlayerbotAIConfig->followDistance;
|
|
float angle = GetFollowAngle();
|
|
|
|
time_t now = time(nullptr);
|
|
if (!lastChangeTime || now - lastChangeTime >= 3)
|
|
{
|
|
lastChangeTime = now;
|
|
dx = (urand(0, 10) / 10.0 - 0.5) * sPlayerbotAIConfig->tooCloseDistance;
|
|
dy = (urand(0, 10) / 10.0 - 0.5) * sPlayerbotAIConfig->tooCloseDistance;
|
|
dr = sqrt(dx * dx + dy * dy);
|
|
}
|
|
|
|
float x = master->GetPositionX() + cos(angle) * range + dx;
|
|
float y = master->GetPositionY() + sin(angle) * range + dy;
|
|
float z = master->GetPositionZ();
|
|
z = bot->GetMapHeight(x, y, z + 5.0f);
|
|
if (!master->GetMap()->CheckCollisionAndGetValidCoords(master, master->GetPositionX(), master->GetPositionY(),
|
|
master->GetPositionZ(), x, y, z))
|
|
{
|
|
x = master->GetPositionX() + cos(angle) * range + dx;
|
|
y = master->GetPositionY() + sin(angle) * range + dy;
|
|
z = master->GetPositionZ() + master->GetHoverHeight();
|
|
z = master->GetMapHeight(x, y, z);
|
|
}
|
|
// bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(),
|
|
// bot->GetPositionZ(), x, y, z);
|
|
return WorldLocation(master->GetMapId(), x, y, z);
|
|
}
|
|
|
|
float x = master->GetPositionX() + cos(angle) * range + dx;
|
|
float y = master->GetPositionY() + sin(angle) * range + dy;
|
|
float z = master->GetPositionZ();
|
|
z = bot->GetMapHeight(x, y, z + 5.0f);
|
|
if (!master->GetMap()->CheckCollisionAndGetValidCoords(master, master->GetPositionX(), master->GetPositionY(),
|
|
master->GetPositionZ(), x, y, z))
|
|
{
|
|
x = master->GetPositionX() + cos(angle) * range + dx;
|
|
y = master->GetPositionY() + sin(angle) * range + dy;
|
|
z = master->GetPositionZ() + master->GetHoverHeight();
|
|
z = master->GetMapHeight(x, y, z);
|
|
}
|
|
return WorldLocation(master->GetMapId(), x, y, z);
|
|
}
|
|
|
|
float GetMaxDistance() override { return sPlayerbotAIConfig->followDistance + dr; }
|
|
|
|
private:
|
|
time_t lastChangeTime;
|
|
float dx = 0.f;
|
|
float dy = 0.f;
|
|
float dr = 0.f;
|
|
};
|
|
|
|
class CircleFormation : public MoveFormation
|
|
{
|
|
public:
|
|
CircleFormation(PlayerbotAI* botAI) : MoveFormation(botAI, "circle") {}
|
|
|
|
WorldLocation GetLocation() override
|
|
{
|
|
float range = 2.0f;
|
|
|
|
Unit* target = AI_VALUE(Unit*, "current target");
|
|
Player* master = botAI->GetGroupMaster();
|
|
if (!target && target != bot)
|
|
target = master;
|
|
|
|
if (!target)
|
|
return Formation::NullLocation;
|
|
|
|
switch (bot->getClass())
|
|
{
|
|
case CLASS_HUNTER:
|
|
case CLASS_MAGE:
|
|
case CLASS_PRIEST:
|
|
case CLASS_WARLOCK:
|
|
range = botAI->GetRange("flee");
|
|
break;
|
|
case CLASS_DRUID:
|
|
if (!botAI->IsTank(bot))
|
|
range = botAI->GetRange("flee");
|
|
break;
|
|
case CLASS_SHAMAN:
|
|
if (botAI->IsHeal(bot))
|
|
range = botAI->GetRange("flee");
|
|
break;
|
|
}
|
|
|
|
float angle = GetFollowAngle();
|
|
float x = target->GetPositionX() + cos(angle) * range;
|
|
float y = target->GetPositionY() + sin(angle) * range;
|
|
float z = target->GetPositionZ();
|
|
if (!master->GetMap()->CheckCollisionAndGetValidCoords(master, master->GetPositionX(), master->GetPositionY(),
|
|
master->GetPositionZ(), x, y, z))
|
|
{
|
|
x = target->GetPositionX() + cos(angle) * range;
|
|
y = target->GetPositionY() + sin(angle) * range;
|
|
z = target->GetPositionZ() + target->GetHoverHeight();
|
|
z = master->GetMapHeight(x, y, z);
|
|
}
|
|
return WorldLocation(bot->GetMapId(), x, y, z);
|
|
}
|
|
};
|
|
|
|
class LineFormation : public MoveAheadFormation
|
|
{
|
|
public:
|
|
LineFormation(PlayerbotAI* botAI) : MoveAheadFormation(botAI, "line") {}
|
|
|
|
WorldLocation GetLocationInternal() override
|
|
{
|
|
Group* group = bot->GetGroup();
|
|
if (!group)
|
|
return Formation::NullLocation;
|
|
|
|
float range = 2.0f;
|
|
|
|
Player* master = botAI->GetGroupMaster();
|
|
if (!master)
|
|
return Formation::NullLocation;
|
|
|
|
float x = master->GetPositionX();
|
|
float y = master->GetPositionY();
|
|
float z = master->GetPositionZ();
|
|
float orientation = master->GetOrientation();
|
|
|
|
std::vector<Player*> players;
|
|
GroupReference* gref = group->GetFirstMember();
|
|
while (gref)
|
|
{
|
|
Player* member = gref->GetSource();
|
|
if (member != master)
|
|
players.push_back(member);
|
|
|
|
gref = gref->next();
|
|
}
|
|
|
|
players.insert(players.begin() + group->GetMembersCount() / 2, master);
|
|
|
|
return MoveLine(players, 0.0f, x, y, z, orientation, range);
|
|
}
|
|
};
|
|
|
|
class ShieldFormation : public MoveFormation
|
|
{
|
|
public:
|
|
ShieldFormation(PlayerbotAI* botAI) : MoveFormation(botAI, "shield") {}
|
|
|
|
WorldLocation GetLocation() override
|
|
{
|
|
Group* group = bot->GetGroup();
|
|
if (!group)
|
|
return Formation::NullLocation;
|
|
|
|
float range = sPlayerbotAIConfig->followDistance;
|
|
|
|
Player* master = GetMaster();
|
|
if (!master)
|
|
return Formation::NullLocation;
|
|
|
|
float x = master->GetPositionX();
|
|
float y = master->GetPositionY();
|
|
float z = master->GetPositionZ();
|
|
float orientation = master->GetOrientation();
|
|
|
|
std::vector<Player*> tanks;
|
|
std::vector<Player*> dps;
|
|
GroupReference* gref = group->GetFirstMember();
|
|
while (gref)
|
|
{
|
|
Player* member = gref->GetSource();
|
|
if (member != master)
|
|
{
|
|
if (botAI->IsTank(member))
|
|
tanks.push_back(member);
|
|
else
|
|
dps.push_back(member);
|
|
}
|
|
|
|
gref = gref->next();
|
|
}
|
|
|
|
if (botAI->IsTank(master))
|
|
tanks.insert(tanks.begin() + (tanks.size() + 1) / 2, master);
|
|
else
|
|
dps.insert(dps.begin() + (dps.size() + 1) / 2, master);
|
|
|
|
if (botAI->IsTank(bot) && botAI->IsTank(master))
|
|
{
|
|
return MoveLine(tanks, 0.0f, x, y, z, orientation, range);
|
|
}
|
|
|
|
if (!botAI->IsTank(bot) && !botAI->IsTank(master))
|
|
{
|
|
return MoveLine(dps, 0.0f, x, y, z, orientation, range);
|
|
}
|
|
|
|
if (botAI->IsTank(bot) && !botAI->IsTank(master))
|
|
{
|
|
float diff = tanks.size() % 2 == 0 ? -sPlayerbotAIConfig->tooCloseDistance / 2.0f : 0.0f;
|
|
return MoveLine(tanks, diff, x + cos(orientation) * range, y + sin(orientation) * range, z, orientation,
|
|
range);
|
|
}
|
|
|
|
if (!botAI->IsTank(bot) && botAI->IsTank(master))
|
|
{
|
|
float diff = dps.size() % 2 == 0 ? -sPlayerbotAIConfig->tooCloseDistance / 2.0f : 0.0f;
|
|
return MoveLine(dps, diff, x - cos(orientation) * range, y - sin(orientation) * range, z, orientation,
|
|
range);
|
|
}
|
|
|
|
return Formation::NullLocation;
|
|
}
|
|
};
|
|
|
|
class FarFormation : public FollowFormation
|
|
{
|
|
public:
|
|
FarFormation(PlayerbotAI* botAI) : FollowFormation(botAI, "far") {}
|
|
|
|
WorldLocation GetLocation() override
|
|
{
|
|
float range = sPlayerbotAIConfig->farDistance;
|
|
float followRange = sPlayerbotAIConfig->followDistance;
|
|
|
|
Player* master = GetMaster();
|
|
if (!master)
|
|
return Formation::NullLocation;
|
|
|
|
if (sServerFacade->GetDistance2d(bot, master) <= range)
|
|
return Formation::NullLocation;
|
|
|
|
float angle = master->GetAngle(bot);
|
|
float followAngle = GetFollowAngle();
|
|
|
|
float x = master->GetPositionX() + cos(angle) * range + cos(followAngle) * followRange;
|
|
float y = master->GetPositionY() + sin(angle) * range + sin(followAngle) * followRange;
|
|
float z = master->GetPositionZ();
|
|
|
|
float ground = master->GetMapHeight(x, y, z + 30.0f);
|
|
if (ground <= INVALID_HEIGHT)
|
|
{
|
|
float minDist = 0, minX = 0, minY = 0;
|
|
for (float angle = 0.0f; angle <= 2 * M_PI; angle += M_PI / 16.0f)
|
|
{
|
|
x = master->GetPositionX() + cos(angle) * range + cos(followAngle) * followRange;
|
|
y = master->GetPositionY() + sin(angle) * range + sin(followAngle) * followRange;
|
|
float dist = sServerFacade->GetDistance2d(bot, x, y);
|
|
float ground = master->GetMapHeight(x, y, z + 30.0f);
|
|
if (ground > INVALID_HEIGHT && (!minDist || minDist > dist))
|
|
{
|
|
minDist = dist;
|
|
minX = x;
|
|
minY = y;
|
|
}
|
|
}
|
|
|
|
if (minDist)
|
|
{
|
|
if (!master->GetMap()->CheckCollisionAndGetValidCoords(master, master->GetPositionX(), master->GetPositionY(),
|
|
master->GetPositionZ(), x, y, z))
|
|
{
|
|
x = master->GetPositionX() + cos(angle) * range + cos(followAngle) * followRange;
|
|
y = master->GetPositionY() + sin(angle) * range + sin(followAngle) * followRange;
|
|
z = master->GetPositionZ() + master->GetHoverHeight();
|
|
z = master->GetMapHeight(x, y, z);
|
|
}
|
|
return WorldLocation(bot->GetMapId(), minX, minY, z);
|
|
}
|
|
|
|
return Formation::NullLocation;
|
|
}
|
|
|
|
if (!master->GetMap()->CheckCollisionAndGetValidCoords(master, master->GetPositionX(), master->GetPositionY(),
|
|
master->GetPositionZ(), x, y, z))
|
|
{
|
|
x = master->GetPositionX() + cos(angle) * range + cos(followAngle) * followRange;
|
|
y = master->GetPositionY() + sin(angle) * range + sin(followAngle) * followRange;
|
|
z = master->GetPositionZ() + master->GetHoverHeight();
|
|
z = master->GetMapHeight(x, y, z);
|
|
}
|
|
return WorldLocation(bot->GetMapId(), x, y, z);
|
|
}
|
|
};
|
|
|
|
float Formation::GetFollowAngle()
|
|
{
|
|
Player* master = GetMaster();
|
|
Group* group = bot->GetGroup();
|
|
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
|
|
|
uint32 index = 1;
|
|
uint32 total = 1;
|
|
|
|
PlayerbotMgr* masterBotMgr = nullptr;
|
|
if (master)
|
|
masterBotMgr = GET_PLAYERBOT_MGR(master);
|
|
if (!group && master && !GET_PLAYERBOT_AI(master) && masterBotMgr)
|
|
{
|
|
for (PlayerBotMap::const_iterator i = masterBotMgr->GetPlayerBotsBegin(); i != masterBotMgr->GetPlayerBotsEnd();
|
|
++i)
|
|
{
|
|
if (i->second == bot)
|
|
index = total;
|
|
|
|
++total;
|
|
}
|
|
}
|
|
else if (group)
|
|
{
|
|
std::vector<Player*> roster;
|
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
|
{
|
|
if (Player* member = ref->GetSource())
|
|
{
|
|
if (member != master && !botAI->IsTank(member) && !botAI->IsHeal(member))
|
|
{
|
|
roster.insert(roster.begin() + roster.size() / 2, member);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
|
{
|
|
if (Player* member = ref->GetSource())
|
|
{
|
|
if (member != master && botAI->IsHeal(member))
|
|
{
|
|
roster.insert(roster.begin() + roster.size() / 2, member);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool left = true;
|
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
|
{
|
|
if (Player* member = ref->GetSource())
|
|
{
|
|
if (member != master && botAI->IsTank(member))
|
|
{
|
|
if (left)
|
|
roster.push_back(member);
|
|
else
|
|
roster.insert(roster.begin(), member);
|
|
|
|
left = !left;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (Player* playerRoster : roster)
|
|
{
|
|
if (playerRoster == bot)
|
|
break;
|
|
|
|
++index;
|
|
}
|
|
|
|
total = roster.size() + 1;
|
|
}
|
|
|
|
float start = (master ? master->GetOrientation() : 0.0f);
|
|
return start + (0.125f + 1.75f * index / total + (total == 2 ? 0.125f : 0.0f)) * M_PI;
|
|
}
|
|
|
|
FormationValue::FormationValue(PlayerbotAI* botAI)
|
|
: ManualSetValue<Formation*>(botAI, new NearFormation(botAI), "formation")
|
|
{
|
|
}
|
|
|
|
FormationValue::~FormationValue()
|
|
{
|
|
if (value)
|
|
{
|
|
delete value;
|
|
value = nullptr;
|
|
}
|
|
}
|
|
|
|
std::string const FormationValue::Save() { return value ? value->getName() : "?"; }
|
|
|
|
bool FormationValue::Load(std::string const formation)
|
|
{
|
|
if (formation == "melee")
|
|
{
|
|
if (value)
|
|
delete value;
|
|
|
|
value = new MeleeFormation(botAI);
|
|
}
|
|
else if (formation == "queue")
|
|
{
|
|
if (value)
|
|
delete value;
|
|
|
|
value = new QueueFormation(botAI);
|
|
}
|
|
else if (formation == "chaos")
|
|
{
|
|
if (value)
|
|
delete value;
|
|
|
|
value = new ChaosFormation(botAI);
|
|
}
|
|
else if (formation == "circle")
|
|
{
|
|
if (value)
|
|
delete value;
|
|
|
|
value = new CircleFormation(botAI);
|
|
}
|
|
else if (formation == "line")
|
|
{
|
|
if (value)
|
|
delete value;
|
|
|
|
value = new LineFormation(botAI);
|
|
}
|
|
else if (formation == "shield")
|
|
{
|
|
if (value)
|
|
delete value;
|
|
|
|
value = new ShieldFormation(botAI);
|
|
}
|
|
else if (formation == "arrow")
|
|
{
|
|
if (value)
|
|
delete value;
|
|
|
|
value = new ArrowFormation(botAI);
|
|
}
|
|
else if (formation == "near" || formation == "default")
|
|
{
|
|
if (value)
|
|
delete value;
|
|
|
|
value = new NearFormation(botAI);
|
|
}
|
|
else if (formation == "far")
|
|
{
|
|
if (value)
|
|
delete value;
|
|
|
|
value = new FarFormation(botAI);
|
|
}
|
|
else
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SetFormationAction::Execute(Event event)
|
|
{
|
|
std::string const formation = event.getParam();
|
|
|
|
FormationValue* value = (FormationValue*)context->GetValue<Formation*>("formation");
|
|
if (formation == "?" || formation.empty())
|
|
{
|
|
std::ostringstream str;
|
|
str << "Formation: |cff00ff00" << value->Get()->getName();
|
|
botAI->TellMaster(str);
|
|
return true;
|
|
}
|
|
|
|
if (formation == "show")
|
|
{
|
|
WorldLocation loc = value->Get()->GetLocation();
|
|
if (!Formation::IsNullLocation(loc))
|
|
botAI->Ping(loc.GetPositionX(), loc.GetPositionY());
|
|
|
|
return true;
|
|
}
|
|
|
|
if (!value->Load(formation))
|
|
{
|
|
std::ostringstream str;
|
|
str << "Invalid formation: |cffff0000" << formation;
|
|
botAI->TellMaster(str);
|
|
botAI->TellMaster(
|
|
"Please set to any of:|cffffffff near (default), queue, chaos, circle, line, shield, arrow, melee, far");
|
|
return false;
|
|
}
|
|
|
|
std::ostringstream str;
|
|
str << "Formation set to: " << formation;
|
|
botAI->TellMaster(str);
|
|
return true;
|
|
}
|
|
|
|
WorldLocation MoveFormation::MoveLine(std::vector<Player*> line, float diff, float cx, float cy, float cz,
|
|
float orientation, float range)
|
|
{
|
|
if (line.size() < 5)
|
|
{
|
|
return MoveSingleLine(line, diff, cx, cy, cz, orientation, range);
|
|
}
|
|
|
|
uint32 lines = ceil((double)line.size() / 5.0);
|
|
for (uint32 i = 0; i < lines; i++)
|
|
{
|
|
float radius = range * i;
|
|
float x = cx + cos(orientation) * radius;
|
|
float y = cy + sin(orientation) * radius;
|
|
|
|
std::vector<Player*> singleLine;
|
|
for (uint32 j = 0; j < 5 && !line.empty(); j++)
|
|
{
|
|
singleLine.push_back(line[line.size() - 1]);
|
|
line.pop_back();
|
|
}
|
|
|
|
WorldLocation loc = MoveSingleLine(singleLine, diff, x, y, cz, orientation, range);
|
|
if (!Formation::IsNullLocation(loc))
|
|
return loc;
|
|
}
|
|
|
|
return Formation::NullLocation;
|
|
}
|
|
|
|
WorldLocation MoveFormation::MoveSingleLine(std::vector<Player*> line, float diff, float cx, float cy, float cz,
|
|
float orientation, float range)
|
|
{
|
|
float count = line.size();
|
|
float angle = orientation - M_PI / 2.0f;
|
|
float x = cx + cos(angle) * (range * floor(count / 2.0f) + diff);
|
|
float y = cy + sin(angle) * (range * floor(count / 2.0f) + diff);
|
|
|
|
uint32 index = 0;
|
|
for (Player* member : line)
|
|
{
|
|
if (member == bot)
|
|
{
|
|
float angle = orientation + M_PI / 2.0f;
|
|
float radius = range * index;
|
|
|
|
float lx = x + cos(angle) * radius;
|
|
float ly = y + sin(angle) * radius;
|
|
float lz = cz;
|
|
|
|
Player* master = botAI->GetMaster();
|
|
if (!master->GetMap()->CheckCollisionAndGetValidCoords(
|
|
master, master->GetPositionX(), master->GetPositionY(), master->GetPositionZ(), lx, ly, lz))
|
|
{
|
|
lx = x + cos(angle) * radius;
|
|
ly = y + sin(angle) * radius;
|
|
lz = cz;
|
|
}
|
|
|
|
return WorldLocation(bot->GetMapId(), lx, ly, lz);
|
|
}
|
|
|
|
++index;
|
|
}
|
|
|
|
return Formation::NullLocation;
|
|
}
|