Решили: завести процентное пробивание брони разовым свойством удара (DamageRequest.BonusPctPen, плюс то же поле у DisplaceRequest), складывать его с процентом из статов умножением остатков, а циклы и периоды ударов считать по взмахам носителя (RuntimeUnit.AttacksMade), не по событиям урона. Разгон наездника сделан своим компонентом старта боя, а не веткой AbilityData.Displaces.

Почему: пробивание — вердикт Макса («заводить, а не подгонять плоское число под конкретных врагов»); плоским выражается только защита известной толщины, а карточка обещает «половину брони». Умножение остатков вместо сложения: 60% от статов и 50% от удара дают 80%, иначе два умеренных источника обнуляли бы любую броню и «броня вдвое меньше» перестало бы что-либо значить. Ветка Displaces занята семантикой Монаха — рывок ВПЛОТНУЮ к цели с выбором стороны по «наковальне»; наезднику нужно ровно обратное, проход сквозь строй, и два смысла в одной ветке означали бы двух хозяев.

Грабли: цикл голема 1-2-3 крутил первые две фазы и никогда не доходил до тяжёлого удара. Причина не в цикле: удар по площади присылает событие урона на КАЖДОГО задетого, и счётчик фаз считал их отдельными ударами, проскакивая фазу. Лечится единственным правильным счётчиком — тем же, что отмеряет слепоту. Следствие принято осознанно: промах цикл двигает, потому что цикл — ритм замахов, а не тальный счёт попаданий. Второе, помельче: живой снаряд, брошенный в край арены, получает и урон броска, и удар о стену — числа удваиваются законно, но в замере это читается как «швырнули дважды».

Владелец правды: DamageRequest.BonusPctPen (remarks про умножение остатков), DamagePipeline, ChargeThroughOnBattleStartComponent, AttackCycleComponent (remarks про взмахи), тест ChargeThroughTests.