#pragma once // ----------------------------------------------------------------------------- // Ядро прототипа (спек 6.3–6.5): проверка линии огня, выбор цели, // решатель огневых позиций с гистерезисом. // // Всё здесь — чистые функции над состоянием мира. Модуль ai/ НЕ знает // ни про рендер, ни про изометрию, ни про ввод. // ----------------------------------------------------------------------------- #include #include "ai/lane_registry.h" #include "sim/agent.h" #include "sim/target.h" #include "world/tilemap.h" struct SolverContext { const Tilemap* map = nullptr; const Agent* agents = nullptr; int agentCount = 0; const std::vector* targets = nullptr; const LaneRegistry* lanes = nullptr; Vec2 anchor{}; int assignedTargetId = -1; // ручной режим (6.8); -1 = авто }; // --- 6.3 --------------------------------------------------------------------- // Линия от дула стрелка (в точке from, ствол смотрит на цель) до targetPos. // Порядок проверок как в спеке: свои -> геометрия -> дистанция. bool LaneClearFrom(Vec2 from, int shooterIndex, Vec2 targetPos, const SolverContext& ctx); // Успеет ли союзник влезть под пулю за время её полёта. // // 6.3 проверяет линию в МОМЕНТ выстрела, а 6.9 делает пулю настоящим снарядом: // до 0.4 с полёта, за которые союзник проходит больше метра. Реактивного // расступания (6.6) тут не хватает — союзник может быть чист в момент нажатия // и войти в коридор уже потом. Поэтому спуск дополнительно проверяет // сближение пули и союзника как двух равномерно движущихся точек. // Само правило 6.3 не ослабляется: см. docs/08-decisions.md. bool AllyInterceptsShot(Vec2 muzzle, Vec2 targetPos, int shooterIndex, const SolverContext& ctx); // --- выбор цели (6.4 / 6.8) -------------------------------------------------- // Заполняет currentTargetId, hasLane, priorityLane и диагностику для оверлея. void EvaluateAgentLane(Agent& a, int index, const SolverContext& ctx); // Ближайшая живая мишень в радиусе searchRadius (или -1). int NearestAliveTarget(Vec2 from, const SolverContext& ctx, float searchRadius); // --- 6.5 --------------------------------------------------------------------- struct SolveResult { Vec2 candidates[SOLVER_CANDIDATES]{}; float scores[SOLVER_CANDIDATES]{}; bool valid[SOLVER_CANDIDATES]{}; int count = 0; int bestIndex = -1; float bestScore = 0.0f; float currentScore = 0.0f; bool committed = false; int bestTargetId = -1; }; float ScoreCandidate(Vec2 cand, const Agent& a, int index, const SolverContext& ctx, int bestTargetId); // true, если решатель отработал (нашёл bestTarget и оценил кандидатов). bool SolveFiringPosition(Agent& a, int index, const SolverContext& ctx, SolveResult& out);