Решили: ввести четвёртое состояние цикла атаки — AttackPhase.CombatIdle (боевое ожидание), —
и провести границу между ним и Idle по досягаемости цели, а не по её наличию. Значение
дописано в конец enum: порядок существующих не меняем, он уезжает в снимок показа.
Почему: решение Макса 2026-07-30/10 существовало только на бумаге, а прежний Idle описывал две
несовместимые ситуации — «не дерётся вовсе» и «дерётся, но сейчас между ударами». Пока свинг занимал
весь интервал, разницы не было. С окном между атаками она принципиальна: парированию нужно знать, кто
именно на него нацелен, и бегущий мимо боец — не то же самое, что ждущий своего удара.
Развилка внутри: считать боевым ожиданием любого с выбранной целью или только дотянувшегося.
Взято второе — прямое уточнение Макса («бегущий к цели ещё не в бою»), и оно же дешевле: метрика та
же, что у гейта атаки и у движения (CombatPositioning.InAttackRange), поэтому «в бою» и «бью» не
могут разойтись на границе. Отдельно: под станом боевого ожидания не бывает — оглушённый выпал из
цикла целиком, иначе таймер Комбо (07-30/11, «время вне лупа») считал бы неверно.
Попутно правило «идёт ли удар» получило одного владельца — RuntimeUnit.IsSwinging и его зеркало
UnitSnapshot.IsSwinging. До этого три места перечисляли фазы руками (рут движения, смена боевой
формы, выбор клипа в показе), и четвёртая фаза заставила бы обойти каждое, ничего не забыв.
Грабли: три, все найдены тестами, а не глазами.
- Смена боевой формы сверялась с
Phase != Idle. С появлением боевого ожидания Десятина между ударами почти всегда стоит рядом с целью — то есть форма не менялась бы, пока враг не потерян, то есть никогда. Условие обязано звучать как «идёт ли удар», а не «равна ли фаза Idle». - Оборванный канал переводится в
Recoveryнамеренно (сворачивание потока — это и есть наказание контролем), поэтому безусловная простановкаIdleв ветке потери дееспособности его затирала. Фаза покоя ставится только тем, кто в ударе не остался. - У быстрого кита боевого ожидания не существует вовсе. Свинг сжимается до интервала
(
windup 21 + хвост 9 = интервал 30), хвост кончается ровно тогда, когда обнуляется кулдаун, и юнит уходит из доигрыша прямо в новый замах. Это не дефект, а «непрекращающийся град» из двух режимов темпа (07-30/15) — первая версия теста была написана на таком ките и падала справедливо.
Что из решения осталось незакрытым: пункт 07-30/14 «ожидание держит слот боевого контакта и линию» реализовать нечего — слотов контакта в коде нет, они приедут с позиционикой. Пункт вступит в силу вместе с ней.
Владелец правды: Combat/Units/AttackPhase.cs (смысл состояний и три следствия боевого ожидания),
RuntimeUnit.IsSwinging, AutoAttackSystem.SetRestingPhase (где проходит граница),
тесты WindupAutoAttackTests — группа «боевое ожидание» плюс FastKit_HasNoWaitingWindow_AtAll.