Решили: реактив, требующий ДЕЙСТВИЯ носителя, помечается маркером IRequiresAgencyComponent и не
срабатывает, пока юнит выведен контролем; гейт читает СНИМОК дееспособности на начало тика
(RuntimeUnit.CanActAtTickStart), а не живой CanAct.
Почему: оглушённый поднимал щит «Оплота» и уходил в кувырок «Изворотливости» — проверялся только
IsDead. Отвергли список эффектов, отключающих реактивы: дееспособность уже имеет владельца (CanAct,
который EffectSystem собирает из компонентов контроля), и второй список разошёлся бы с ним на первом же
новом виде контроля. Маркер на компоненте ставит решение туда, где объявлено поведение, и одна проверка в
диспатче заменяет пятнадцать проверок в компонентах. Шипы и вампиризм не помечены намеренно: это свойства,
а не действия, и под станом они обязаны работать.
Грабли: две, и обе стоили прогонов. Первая — живой CanAct меняется ПОСРЕДИ тика: RecomputeControl
зовётся синхронно при наложении эффекта, а урон проходит раньше фазы эффектов, поэтому реакция стала зависеть
от того, чей атакующий раньше в списке юнитов, и зеркальные команды разошлись. Отсюда снимок. Побочное
следствие принято как верное: стан, прилетевший тем же тиком, что удар, поднять щит не мешает — как «Оплот»
ловит тот самый удар, который его разбудил. Вторая — маркер СЛОМАЛ комбо монаха: его рывок сделан смещением,
а смещение снимает дееспособность у летящего, так что гейт отменял фазы собственной способности. Правило:
если недееспособность вызвана своей же механикой, маркера быть не должно.
Владелец правды: Assets/_Project/Scripts/Combat/Effects/EffectSystem.cs (гейт в RunPreDamage и в
реактивной очереди), Assets/_Project/Scripts/Combat/Effects/IRuntimeEffectComponent.cs (маркер),
тесты DefenderSliceTests.Bulwark_DoesNotRaiseTheShieldWhileIncapacitated,
DefenderSliceTests.Thorns_StillBiteWhileIncapacitated,
AssassinSliceTests.Dodge_DoesNotWorkWhileIncapacitated.