Решили: рекаст — это ускорение фаз, а не их снятие. Доигрыш прерванной атаки и замах вышедшего
вне очереди удара остаются на месте и проигрываются вдвое быстрее; ожидание интервала по-прежнему
снимается целиком. Множитель во всех API стал скоростью (2 = вдвое быстрее), а не долей, в
которую превращается длительность, и живёт в двух ручках SimTuningConfig — «Ускорение доигрыша» и
«Ускорение замаха».
Почему: снятая фаза убирает окно, в которое противник успевает ответить, — ускоренная лишь
сокращает его. Отвергнутая альтернатива (recoveryMult: 0, «хвост снят целиком») делала рекаст
бесплатным: у бурста не оставалось ни телеграфа, ни паузы, в которую можно вклиниться, и парированию,
которое делается следующим, было бы не во что целиться. Числа вынесены в SO, а не в константы, по
прямому требованию: их будут крутить «с позиции визуала и ощущения», а это правка ассета, не сборки.
Ручек две, хотя пока обе равны 2 — доигрыш и замах подкручиваются под разное, и одна ручка на двоих
означала бы, что настроить можно только оба сразу.
Грабли: ускоренный замах обязан сокращать весь свинг, а не переливать сэкономленное в доигрыш.
Хвост считается от контакта (duration − lastContact), поэтому наивная формула выдавала удару,
вышедшему вдвое быстрее, вдвое более длинный доигрыш — весь выигрыш возвращался обратно тем же тиком.
Лечится расчётом доигрыша от базовой длины замаха (AttackTiming.WindupTicksFor(unit, ignoreRecast: true)). Поймал тест RecoveryCut_IsSpentByOneSwing, причём поймал не тем, что проверял:
он ждал равных хвостов у двух подряд идущих атак и упёрся в 9 против 19.
Второе: узкий фильтр тестов не показывает соседние падения. Прогон по одному классу был зелёным, а по
всему боевому неймспейсу — три красных: тест на удалённый авто-рекаст, переименованное поле
_bonusOnHitEffect → _bonusOnHitEffects и ассет «Решительного удара», снимавший заряд по тегу
Stealth вместо Empowered. Лестница из CLAUDE.md («узко локально, целиком — в CI») означает, что
блок сдаётся прогоном по неймспейсу, а не по классу.
Владелец правды: RuntimeUnit.RecastAttack (семантика скорости и конверсия «скорость → длина»),
SimTuning.RecastRecoverySpeed / RecastWindupSpeed (числа), AutoAttackSystem.EnterRecovery и
EnterWindup (применение), тесты WindupAutoAttackTests группы «Рекаст» — в том числе
Recast_FasterWindup_ShortensTheSwing_DoesNotLengthenTheFollowThrough и
Recast_KeepsAtLeastOneTickOfRecovery.