56 lines
2.3 KiB
C++
56 lines
2.3 KiB
C++
|
|
#include "ai/lane_registry.h"
|
|||
|
|
|
|||
|
|
void LaneRegistry::Add(int shooter, Vec2 from, Vec2 to, float reserve)
|
|||
|
|
{
|
|||
|
|
if (count >= NUM_AGENTS) return;
|
|||
|
|
lanes[count++] = FireLane{from, to, shooter, reserve};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Vec2 LaneRegistry::AvoidanceForce(int agentIndex, Vec2 pos, float bodyRadius, float maxSpeed) const
|
|||
|
|
{
|
|||
|
|
// Резерв шире, чем «тело уже в линии»: агент должен отходить ЗАРАНЕЕ,
|
|||
|
|
// иначе он успевает влезть под пулю, которая уже летит.
|
|||
|
|
const float wide = BlockRadius(bodyRadius) * g_tune.laneReserveScale;
|
|||
|
|
Vec2 force{};
|
|||
|
|
|
|||
|
|
for (int i = 0; i < count; ++i)
|
|||
|
|
{
|
|||
|
|
const FireLane& lane = lanes[i];
|
|||
|
|
if (lane.shooter == agentIndex) continue;
|
|||
|
|
|
|||
|
|
const float threshold = (lane.reserve > 0.0f) ? (bodyRadius + lane.reserve) : wide;
|
|||
|
|
const float d = DistancePointToSegment(pos, lane.from, lane.to);
|
|||
|
|
if (d >= threshold) continue;
|
|||
|
|
|
|||
|
|
const Vec2 axis = Normalized(lane.to - lane.from);
|
|||
|
|
if (LengthSq(axis) < 0.5f) continue;
|
|||
|
|
|
|||
|
|
// В сторону ближайшего края линии.
|
|||
|
|
const Vec2 n = Perp(axis);
|
|||
|
|
const float side = Dot(pos - lane.from, n);
|
|||
|
|
Vec2 away = (side >= 0.0f) ? n : Vec2{-n.x, -n.y};
|
|||
|
|
if (std::fabs(side) < 1e-4f) // ровно на оси — разводим детерминированно
|
|||
|
|
away = (agentIndex & 1) ? n : Vec2{-n.x, -n.y};
|
|||
|
|
|
|||
|
|
// Чем ближе к оси, тем сильнее; laneAvoidStrength делает отклик резче.
|
|||
|
|
const float strength = Clampf((1.0f - d / threshold) * g_tune.laneAvoidStrength, 0.0f, 1.0f);
|
|||
|
|
force += away * (maxSpeed * strength);
|
|||
|
|
}
|
|||
|
|
return force;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int LaneRegistry::CountBlocked(int exceptShooter, Vec2 pos, float bodyRadius,
|
|||
|
|
bool shootingOnly) const
|
|||
|
|
{
|
|||
|
|
const float narrow = BlockRadius(bodyRadius);
|
|||
|
|
int n = 0;
|
|||
|
|
for (int i = 0; i < count; ++i)
|
|||
|
|
{
|
|||
|
|
if (lanes[i].shooter == exceptShooter) continue;
|
|||
|
|
if (shootingOnly && lanes[i].reserve > 0.0f) continue; // это сектор клинка
|
|||
|
|
const float threshold = (lanes[i].reserve > 0.0f) ? (bodyRadius + lanes[i].reserve) : narrow;
|
|||
|
|
if (DistancePointToSegment(pos, lanes[i].from, lanes[i].to) < threshold) ++n;
|
|||
|
|
}
|
|||
|
|
return n;
|
|||
|
|
}
|