Решили: событие ленты несёт долю тика (TapeEvent.SubTick), PumpTo берёт её вместе с долей
кадра показа, а запись держит ленту отсортированной по паре (тик, доля) вставкой, а не слепым
Add.
Почему сортировка при ЗАПИСИ, а не разбор при подаче: подача идёт линейным курсором, и событие с
долей 0.9, записанное раньше события с долей 0.1 того же тика, задержало бы второе до своей доли — то
есть sub-tick точность съела бы сама себя, причём именно в самом частом случае (порядок записи внутри
тика — это порядок обхода юнитов, а не порядок моментов). Альтернатива «оставить Add, а при подаче
пропускать незрелые и возвращаться к ним» требует признака «отдано» на каждом событии вместо одного
курсора: дороже и на каждом кадре, ради случая, который лечится единицами сдвигов при записи (события
приходят по возрастанию тика, сдвиг возможен только среди событий последнего тика).
Вставка стабильна намеренно: при равных долях порядок записи сохраняется, поэтому у событий без доли — смерть, наложение эффекта, периодика — поведение осталось прежним. Без этого правка ради точности удара незаметно переставила бы всё остальное.
Сказано: «Давай саб тики, а потом пошли фазу А» (01.08.2026, сессия 9e6eef1b). Само решение делать sub-tick подачу принято им 31.07 отдельным заходом, к коопу не привязанным.
Грабли: доля обнуляется, если момент контакта склампился (телеграф-пол MinWindupTicks или
потолок «интервал − 1»): кламп сдвигает момент искусственно, и дробь от сдвинутого числа была бы
точностью, которой нет. Второе — PumpTo обязан отдавать прошлые тики целиком, независимо от
alpha: доля показываемого тика к ним не относится, и сравнение по паре без этого условия теряло бы
события на долгом кадре насовсем.
Владелец правды: Combat/Tape/TapeEvent.cs, Combat/Tape/BattleTape.Record,
Combat/Tape/BattleTapeDispatcher.PumpTo; инварианты — тест TapeSubTickTests (сортировка внутри
тика, стабильность для событий без доли, прошлые тики целиком, дефолт alpha = 1).