Решили: парирование живёт на том же pre-damage-шве, что Блок и «Изворотливость», и триггерится
прилетевшим ближним ударом (вердикт Макса). Сработав, оно гасит удар-триггер, открывает окно
effect.parry_window на 0.3 с, вешает общий микро-стан на всех, кто целился в носителя спереди, и
отвечает рекастом с зарядом effect.riposte_charge (×2). Парировавшего того, у кого дальность
больше своей, дополнительно разгоняет effect.parry_haste (+30% скорости передвижения на 1 с).
Почему: альтернатива — триггер по намерению атакующего («кто выбрал парирующего целью») —
позволяла бы отбивать до попадания, но требовала бы нового шва «кто в кого целится» прямо в
pre-damage. Со шва по прилетевшему удару вся механика собирается из уже написанного: заряды
(RuntimeEffect.TryConsumeCharge), гейт дееспособности (IRequiresAgencyComponent), отмена урона
(PreDamageResult.Negated) и рекаст. Ответ намеренно не свой путь урона, а заряд плюс
RecastAttack: тогда его можно сбить контролем, от него можно уклониться и он уходит в промах —
контригра работает та же, что у любого удара.
Окно — отдельный эффект, а не таймер внутри компонента. Его видит показ, его снимает диспел, его
длительность крутится в ассете. Таймер внутри ParryComponent был бы состоянием, о котором снаружи
не знает никто.
Грабли:
- «Перед ним» не из чего вычислить. Направления взгляда в симуляции нет вовсе — его выводит показ
по цели и движению. Поэтому фронтальный сектор считается от того же правила (
CurrentTarget, иначе вектор движения), чтобы «спереди» в бою совпадало со «спереди» на экране; неизвестное направление сектор не сужает. - Пока окно висит, второй заряд не тратится. Без этого замка серия из трёх Ударов сожгла бы все заряды разом, хотя первое же парирование их и так отбило. Проверка — по ссылке на тот же ассет окна, без служебных тегов.
Stats.Commit()в тесте обязателен. Статы эффектов ложатся отложенно (конец боевого тика), иEffectSystem.Tickих не публикует — разгон читался как «не применился».RuntimeUnit.AttackTypeне выводится из реликвии сам. Он заполняется вBind, а тест собирает юнит инициализатором — стрелок в тесте оказался ближним, и парирование «отбило стрелу».
Что осталось незакрытым: носителя у примитива пока нет — ни один кит парирование не объявляет
(механика ждёт вердикта, кому её выдать). Плавный вход и выход разгона (дизайн: «не рывком») сейчас
не реализован: ParryHaste даёт плоскую прибавку, рампа есть только у спринта. Оба долга записаны в
tech-debt §3.10.
Владелец правды: ParryComponent (триггер, стан по целящимся, ответ), ParryWindowComponent
(что гасит окно), ассеты Effects/Parry, ParryWindow, RiposteCharge, ParryHaste, MicroStun,
тесты ParryTests.