squad-proto/src/sim/enemy.h
z.kirill 0fb6e31c2b Свет как расходник: заряд тратится за путь, а не за время (M23)
Второй ресурс вылазки после патронов. Фонарь садится, погас — обзор падает
до 35%, а твари замечают отряд в 1.35 раза дальше: отряд не слепнет, он
меняется с тварями местами. Батарея встаёт сама, как бинт.

Главное решение — расход за ПРОЙДЕННЫЙ ПУТЬ, а не за время. Первая версия
считала по секундам, то есть была таймером, которого в игре нет по прямому
требованию к жанру: садящийся сам собой фонарь торопил бы игрока ровно как
секундомер, только без циферблата, и думать над этажом стало бы дорого.
За расстояние получается тот же размен, что и с весом, но с другой стороны:
вес делает дорогим «унести всё», свет — «обшарить всё».

Заряд общий на отряд и едет вниз вместе с ним: обновляйся он на этаже, это
был бы ресурс этажа, а не вылазки, и никуда бы не гнал. Чем глубже, тем
дороже шаг, а этажи внизу длиннее — глубина жмёт на свет дважды.

Проверка в --descend меряет правило целиком, и первая её строка не про
баланс, а про жанр: заряд на стоянке обязан стоять. Сам измеритель тоже
был неправ — делил потраченное на смещение отряда, а не на пройденный
путь, и показывал 1.55 за тайл при 1.10 в каталоге.

  свет: стоянка 0.2, ход 1.10 за тайл при 1.10 в каталоге
        глубина 8: заряда на 383 тайла, это 4.0 прямых пути до спуска
        обзор 1.00 -> 0.35 в темноте, батарея встала сама

Хватает ли заряда на этаж считается в этажах и по средней длине забега:
путь до спуска скачет вдвое от сида, и порог по одному этажу ловил бы
удачу, а не баланс.

--accept PASS без изменений: в песочнице света бесконечно, как и патронов.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 22:12:05 +03:00

45 lines
3.2 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#pragma once
// -----------------------------------------------------------------------------
// Поведение тварей. Отдельный файл, потому что здесь единственное место, где
// противник ЧИТАЕТ бойцов: sim/target.h про Agent не знает и знать не должен.
//
// Правила простые и намеренно читаемые с экрана:
// 1. заметил — идёт;
// 2. дошёл — замахивается, и замах ВИДНО (windupTimer);
// 3. довёл замах — бьёт по геометрии СВОЕЙ атаки (AttackKind).
// Между 2 и 3 у игрока есть время отойти или убить. Без телеграфа удар твари
// неотличим от случайной потери здоровья, и бой перестаёт быть боем.
//
// Пункт 3 — это пять разных задач для игрока, а не один урон:
// SWIPE — сектор перед тварью: уводи бойца вбок;
// LUNGE — рывок по прямой: освободи ОСЬ, а не точку;
// SPIT — снаряд: единственное, что перекрывается стеной;
// SLAM — круг вокруг твари: не толпитесь возле неё вдвоём;
// GRAB — захват: боец встал намертво, вытаскивать его придётся огнём.
// Числа этих форм — в EnemyDef (sim/target.cpp), общие множители — в tuning.h.
//
// ИИ не решает, кого атаковать, «по-умному»: ближайший живой боец в радиусе.
// Прототип проверяет отряд, а не противника, — и цена ошибки здесь должна быть
// в геометрии, а не в чужой хитрости.
// -----------------------------------------------------------------------------
#include <vector>
#include "ecs/ecs.h"
#include "sim/agent.h"
#include "sim/events.h"
#include "sim/target.h"
#include "engine/tilemap.h"
// Шевелит всех, у кого kind != DUMMY. Мишени приёмки не трогает вообще:
// --accept обязан считать ровно то же, что считал.
// aggroMul — во сколько раз дальше твари замечают отряд прямо сейчас. Больше
// единицы бывает ровно в одном случае: у отряда сел фонарь (sim/items.h).
// Радиус ЗАМЕТИЛ — единственное, что темнота меняет на стороне тварей: бьют они
// так же и ходят так же, просто находят раньше.
void UpdateEnemies(ecs::World& world, const Tilemap& map, Agent* agents, int agentCount,
std::vector<Target>& targets, float dt, CombatEventLog* events,
float aggroMul = 1.0f);
// Сколько тварей сейчас идёт к отряду. Для напряжения и HUD.
int CountAggroEnemies(const std::vector<Target>& targets);