#include "sim/target.h" namespace { // Кучки: 5 на арене (рядом со стартом), 3 в северной комнате, // 3 в западной, 1 одиночная в восточной. Всего 12. // // Северная кучка стоит НАПРОТИВ прохода x=30..31: из коридора до неё есть // чистая линия. Это тот самый тест-кейс из спека — в узком проходе правило // «не стрелять сквозь своих» упирается в геометрию, и отряд обязан вытянуться // в колонну: стреляет передний, задние молчат. // // Кучка обязана быть В ПРЕДЕЛАХ ДАЛЬНОСТИ от середины прохода (y≈14), иначе // стрелять из коридора не по чему и тест-кейс проверяет пустоту. При weaponRange // 7.2 ближняя мишень стоит в ~6.5 юнита: запас есть, но небольшой — если менять // дальность, эти координаты надо пересчитывать вместе с ней. const float kTargetSpots[NUM_TARGETS][2] = { {30.5f, 20.5f}, {31.5f, 22.5f}, {29.5f, 23.5f}, {32.5f, 21.5f}, {30.5f, 24.5f}, {29.5f, 8.5f}, {31.5f, 7.5f}, {27.5f, 10.5f}, { 7.5f, 40.5f}, { 9.5f, 42.5f}, { 6.5f, 43.5f}, {43.5f, 35.5f}, }; // Сопротивление отбросу по слотам. Не случайное: в каждой кучке есть кто-то // тяжёлый, кого клинком не сдвинуть, и кто-то лёгкий, кто улетает от первого // же удара. Иначе «отброс» и «сопротивление» — два числа, которых не видно. const float kTargetKnockResist[NUM_TARGETS] = { 0.00f, 0.65f, 0.15f, 0.40f, 0.00f, 0.55f, 0.10f, 0.30f, 0.20f, 0.70f, 0.05f, 0.85f, }; } // namespace void SpawnTargets(ecs::World& world) { for (int i = 0; i < NUM_TARGETS; ++i) { Target t; t.self = world.Create(); t.pos = {kTargetSpots[i][0], kTargetSpots[i][1]}; t.home = t.pos; t.vel = {}; t.hp = g_tune.targetHP; t.maxHp = g_tune.targetHP; t.radius = g_tune.targetRadius; t.respawnTimer = 0.0f; t.alive = true; t.knockResist = kTargetKnockResist[i]; world.Add(t.self, t); } } void KnockTarget(Target& t, Vec2 dir, float speed) { t.vel += dir * (speed * t.KnockScale()); } void UpdateTargets(std::vector& targets, float dt) { // Отдача гасится и мишень возвращается на своё место: расстановка уровня // захардкожена, и «уползание» мишеней ломало бы все замеры разом. const float damp = std::exp(-g_tune.targetKnockDamp * dt); const float pull = 1.0f - std::exp(-g_tune.targetReturnRate * dt); for (Target& t : targets) { t.radius = g_tune.targetRadius; t.maxHp = g_tune.targetHP; t.pos += t.vel * dt; t.vel *= damp; t.pos += (t.home - t.pos) * pull; if (t.alive) continue; t.respawnTimer -= dt; if (t.respawnTimer <= 0.0f) { t.alive = true; t.hp = g_tune.targetHP; t.pos = t.home; t.vel = {}; } } } int CountAliveTargets(const std::vector& targets) { int n = 0; for (const Target& t : targets) if (t.alive) ++n; return n; }