Прототип боевого ядра: отряд, хоррор-слой, ближний бой
Тактический отряд с автоматическим огнём в изометрии, собственный
софтверный растеризатор (raylib только окно, ввод и финальный блит).
Ядро (спек, раздел 6):
- LaneClear: проверка линии огня по своим, стенам и дальности;
- решатель огневых позиций с гистерезисом и коммитом;
- резервирование линий огня и расступание из чужих секторов;
- пули-снаряды, дружественный урон физически возможен.
Хоррор-слой (render-only): свет и туман войны с памятью карты,
светящиеся трассеры, кровь, виньетка, зерно, напряжение.
Ближний бой: сектор удара, три фазы, связка из трёх ударов,
своя дисциплина «не бить сквозь своего».
Проверка: squad_proto.exe --accept прогоняет критерии приёмки
раздела 11 плюс блок M7 по ближнему бою — все PASS,
FRIENDLY_HITS = 0 за 3 минуты боя.
Сборка: cmake -S . -B build && cmake --build build --config Release
raylib 6.0 подтягивается через FetchContent.
Документация — CLAUDE.md как оглавление, содержание в docs/.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 17:00:49 +07:00
|
|
|
|
# 11 — Ближний бой
|
|
|
|
|
|
|
|
|
|
|
|
Второй класс оружия. Файлы: [src/sim/loadout.h](../src/sim/loadout.h) (типы и
|
|
|
|
|
|
профили), [src/ai/melee.cpp](../src/ai/melee.cpp) (цель, подход, дисциплина
|
|
|
|
|
|
сектора), `Game::UpdateMelee` в [src/game.cpp](../src/game.cpp) (машина
|
|
|
|
|
|
состояний и урон), [src/render/fx.cpp](../src/render/fx.cpp) (дуга).
|
|
|
|
|
|
|
|
|
|
|
|
## Главная мысль
|
|
|
|
|
|
|
|
|
|
|
|
Ближний бой — это **то же правило, что и стрельба, в угловой форме**. Ничего
|
|
|
|
|
|
нового в этику отряда он не приносит:
|
|
|
|
|
|
|
|
|
|
|
|
| | стрелок | боец ближнего боя |
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
| зона поражения | отрезок «дуло → цель» | сектор `reach` × `arcDeg` вокруг оси удара |
|
|
|
|
|
|
| правило безопасности | свой в отрезке → не стрелять | свой в секторе → не бить |
|
|
|
|
|
|
| проверка вперёд | `AllyInterceptsShot` (успеет ли влезть за время полёта) | экстраполяция союзника на `windup + strike` |
|
|
|
|
|
|
| срыв в последний момент | пуля уже летит, срывать нечего | взмах **обрывается** |
|
|
|
|
|
|
| резерв в реестре линий (6.6) | отрезок до цели | ось удара с явной полушириной |
|
|
|
|
|
|
| свой круг вокруг диска | `cohesionRadius` | `meleeCohesionRadius`, заметно больший |
|
|
|
|
|
|
| выбор позиции | решатель огневых позиций | подход вплотную к цели |
|
|
|
|
|
|
|
|
|
|
|
|
Из этого следует главное свойство: **клинок физически не может поднять
|
|
|
|
|
|
`FRIENDLY_HITS`**. Союзникам он урона не наносит вообще, а правило сектора
|
|
|
|
|
|
существует не ради урона, а ради того, чтобы отряд выглядел как отряд.
|
|
|
|
|
|
|
|
|
|
|
|
## Состав отряда
|
|
|
|
|
|
|
|
|
|
|
|
`Squad::meleeCount` (по умолчанию `DEFAULT_MELEE_COUNT` = 2) — сколько **первых
|
|
|
|
|
|
слотов строя** держат клинок. В клине это остриё и левое крыло, то есть те, кто
|
|
|
|
|
|
и так упирается в противника первым. Ставится до `Squad::Init`; из командной
|
|
|
|
|
|
строки — `--melee N`.
|
|
|
|
|
|
|
|
|
|
|
|
## Три фазы удара
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
IDLE ──цель в reach, корпус довёрнут, сектор чист──> WINDUP ──> STRIKE ──> RECOVER ──> IDLE
|
|
|
|
|
|
0.17 с 0.13 с 0.24 с
|
|
|
|
|
|
интервал между НАЧАЛАМИ замахов: 0.60 с
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
- **WINDUP** — замах. Ось удара уже зафиксирована и больше не доворачивается:
|
|
|
|
|
|
доворот на этой фазе — это самонаведение, только клинком. Скорость ×0.20,
|
|
|
|
|
|
ноги вкопаны. Игрок обязан успевать читать замах, иначе удар выглядит не
|
|
|
|
|
|
ударом, а случайным списанием HP.
|
|
|
|
|
|
- **STRIKE** — активное окно. Лезвие метёт сектор от одного края к другому,
|
|
|
|
|
|
сторона захода чередуется от взмаха к взмаху. Здесь же **рывок**: seek,
|
|
|
|
|
|
separation и расступание молчат, боец едет по оси удара со своей скоростью
|
|
|
|
|
|
и своим ускорением. Стены — единственное, что его останавливает.
|
|
|
|
|
|
- **RECOVER** — боец открыт и медленный (×0.55).
|
|
|
|
|
|
|
|
|
|
|
|
## Связка ударов
|
|
|
|
|
|
|
|
|
|
|
|
Чередование «влево-вправо» — это не связка, а метроном: два удара, между которыми
|
|
|
|
|
|
нет разницы ни на глаз, ни по последствиям. Связка обязана иметь **форму**.
|
|
|
|
|
|
|
|
|
|
|
|
Таблица — `MELEE_COMBO` в [src/tuning.h](../src/tuning.h), множители к базовым
|
|
|
|
|
|
характеристикам клинка:
|
|
|
|
|
|
|
|
|
|
|
|
| # | Удар | Сектор | Урон | Отброс | Замах | Пауза | Смысл |
|
|
|
|
|
|
|---|---|---|---|---|---|---|---|
|
|
|
|
|
|
| 1 | `slash` | ×1.00 | ×1.00 | ×1.00 | ×1.00 | ×1.00 | косой заход, с него всё начинается |
|
|
|
|
|
|
| 2 | `backhand` | ×0.85 | ×1.00 | ×0.85 | ×0.55 | ×0.50 | возврат с обратной стороны, быстро и почти без паузы |
|
|
|
|
|
|
| 3 | `spin` | ×1.70 | ×1.75 | ×2.30 | ×1.50 | ×1.35 | раскрутка: широкая, тяжёлая, с долгой расплатой |
|
|
|
|
|
|
|
|
|
|
|
|
Серия держится `meleeComboWindow` (1.1 с) после удара. Не продолжил вовремя —
|
|
|
|
|
|
начинаешь с первого. Итог по времени: три удара за 1.71 с вместо трёх за 1.80 с,
|
|
|
|
|
|
но суммарный урон выше в 1.25 раза — связка выгоднее ровного метронома, если её
|
|
|
|
|
|
успевают довести.
|
|
|
|
|
|
|
|
|
|
|
|
Раскрутку в тесноте **просто не дадут начать**: её сектор проверяется той же
|
|
|
|
|
|
дисциплиной, что и обычный удар, а он шире почти вдвое. Это не ограничение, а
|
|
|
|
|
|
цена: широкий замах требует места. В замере связки: `slash 21 / backhand 19 /
|
|
|
|
|
|
spin 15` — финиш доходит реже, и это видно в цифрах, а не на словах.
|
|
|
|
|
|
|
|
|
|
|
|
Параметры взмаха **запекаются на старте замаха** (`MeleeWeapon::BakeSwing`).
|
|
|
|
|
|
Причина техническая и важная: базовые поля переписываются со снаряжения каждый
|
|
|
|
|
|
шаг (`SyncLoadout`), и без запекания удар менял бы ширину и урон прямо в полёте.
|
|
|
|
|
|
|
|
|
|
|
|
## Удар на ходу
|
|
|
|
|
|
|
|
|
|
|
|
Бой на ходу — нормальный режим, а не исключение. Ограничение ровно одно: в
|
|
|
|
|
|
**спринте** боец не гоняется за целями (`Squad::GoalFor` возвращает слот строя),
|
|
|
|
|
|
но если цель сама оказалась под рукой — бьёт. Отдельного запрета «на бегу не
|
|
|
|
|
|
драться» нет.
|
|
|
|
|
|
|
|
|
|
|
|
При этом на текущих числах поводка удержание направления спринт и не включает:
|
|
|
|
|
|
отряд идёт быстрым шагом и работает (см. [05-squad-ai.md](05-squad-ai.md),
|
|
|
|
|
|
равновесие поводка).
|
|
|
|
|
|
|
|
|
|
|
|
## Урон идёт по пройденной полосе, а не по факту удара
|
|
|
|
|
|
|
|
|
|
|
|
Каждый шаг считаются два угла лезвия: где оно было в начале шага и где стало в
|
|
|
|
|
|
конце. Цель получает урон, если полоса шириной `meleeBladeDeg` прошла через неё
|
|
|
|
|
|
**за этот шаг** (`BladeSweepHits`). Проверяется и «стоит на цели прямо сейчас»,
|
|
|
|
|
|
и «проскочило насквозь за 1/60 с» — за шаг дуга проходит заметный угол.
|
|
|
|
|
|
|
|
|
|
|
|
### Почему это точная геометрия, а не угловой допуск
|
|
|
|
|
|
|
|
|
|
|
|
Лезвие — **отрезок** от бойца длиной `reach`, повернувшийся за шаг. Проверка
|
|
|
|
|
|
работает с ним напрямую: попало ли направление на цель в заметённую полосу, а
|
|
|
|
|
|
если нет — не задел ли цель край отрезка в крайних положениях.
|
|
|
|
|
|
|
|
|
|
|
|
Раньше здесь стоял угловой допуск `asin(r / dist)`. Он корректно описывает
|
|
|
|
|
|
угловой размер тела, но как **допуск к сектору** вырождается: при `dist -> r`
|
|
|
|
|
|
`asin` даёт 90°, сектор разрастается в полуплоскость, и боец рубит за спину.
|
|
|
|
|
|
В фазе рывка он как раз проезжает вплотную — отсюда и брались удары «на 360
|
|
|
|
|
|
градусов». Отрезок такого не допускает: для цели позади ближайшая точка лезвия —
|
|
|
|
|
|
рукоять, а до неё ровно `dist`, что больше радиуса цели.
|
|
|
|
|
|
|
|
|
|
|
|
Инвариант закреплён критерием **M7.4**: замеряется, на сколько градусов
|
|
|
|
|
|
попадание вышло за разрешённый сектор ЭТОГО взмаха. Норма — ноль.
|
|
|
|
|
|
|
|
|
|
|
|
Смысл: то, что видно на экране, и то, что получило урон, — одна и та же дуга.
|
|
|
|
|
|
Разовый расчёт «в начале взмаха по всему сектору» дал бы попадания раньше, чем
|
|
|
|
|
|
лезвие туда доедет, и это чувствуется.
|
|
|
|
|
|
|
|
|
|
|
|
Одна мишень получает урон **один раз за взмах** (`hitMask`), но один взмах может
|
|
|
|
|
|
задеть несколько — мишени стоят кучками, и это ровно то, ради чего клинок нужен.
|
|
|
|
|
|
Сквозь стену не рубит: перед уроном идёт `RaycastBlocked`.
|
|
|
|
|
|
|
|
|
|
|
|
## Дисциплина сектора
|
|
|
|
|
|
|
|
|
|
|
|
Проверяется дважды, как и у ствола:
|
|
|
|
|
|
|
|
|
|
|
|
1. **на старте замаха** — сектор `reach` × `arcDeg` обязан быть чист. Союзник
|
|
|
|
|
|
проверяется в двух точках: где он сейчас и где будет через `windup + strike`
|
|
|
|
|
|
(`meleeStartPredict`). Без этого треть взмахов начиналась впустую;
|
|
|
|
|
|
2. **каждый шаг активного окна** — если свой всё-таки влез под лезвие, взмах
|
|
|
|
|
|
**обрывается**: серая вспышка, `MELEE_ABORT`, сразу в `RECOVER`.
|
|
|
|
|
|
|
|
|
|
|
|
Оборванные взмахи считаются метрикой (`meleeAborts`) и видны в оверлее. Это не
|
|
|
|
|
|
баг, а видимая цена скученности: примерно каждый пятый взмах в плотной свалке.
|
|
|
|
|
|
|
|
|
|
|
|
Угловой размер тела союзника считается честно (`asin(r / dist)`), поэтому
|
|
|
|
|
|
вплотную союзник перекрывает почти весь сектор — так и должно быть.
|
|
|
|
|
|
|
|
|
|
|
|
## Свой, больший круг
|
|
|
|
|
|
|
|
|
|
|
|
Стрелок держится в `cohesionRadius` (2.1) от диска, и решатель **жёстко**
|
|
|
|
|
|
отбраковывает огневые позиции дальше `cohesionRadius + postLeash`. Боец ближнего
|
|
|
|
|
|
боя берёт цель, только если она внутри `meleeCohesionRadius` (5.6) от диска и не
|
|
|
|
|
|
дальше `meleeSeekRadius` от него самого. Нет такой цели — возвращается в слот
|
|
|
|
|
|
строя.
|
|
|
|
|
|
|
|
|
|
|
|
Поэтому классы живут в двух разных кругах вокруг одного диска: стрелки кучкой,
|
|
|
|
|
|
клинки — на вылазке, но на поводке. Замеряется критерием M7.3.
|
|
|
|
|
|
|
|
|
|
|
|
Двое с клинками не вцепляются в одну мишень: цена цели растёт на
|
|
|
|
|
|
`meleeClaimPenalty` за каждого своего, уже её выбравшего. Ближняя цель всё ещё
|
|
|
|
|
|
выигрывает, но не любой ценой — вдвоём на одну это оборванные взмахи друг об
|
|
|
|
|
|
друга и вдвое меньше урона (замер: 37 взмахов / 31 попадание против 74 / 75).
|
|
|
|
|
|
|
|
|
|
|
|
## Мишени получают отдачу
|
|
|
|
|
|
|
|
|
|
|
|
Удар отбрасывает мишень (`meleeKnockback`), импульс гасится и мишень возвращается
|
|
|
|
|
|
на своё место (`home`). Без отдачи попадание в упор ничем не отличается от
|
|
|
|
|
|
промаха. Расстановка уровня захардкожена, поэтому «уползание» мишеней исключено
|
|
|
|
|
|
конструктивно: возврат идёт всегда, а респавн ставит `pos = home`.
|
|
|
|
|
|
|
|
|
|
|
|
Отдельно появился слой движения `TargetPushForce`: мишень — тело, а не пустое
|
|
|
|
|
|
место. Без него боец стоял **внутри** того, кого рубит.
|
|
|
|
|
|
|
|
|
|
|
|
## Взаимодействие со стрельбой
|
|
|
|
|
|
|
|
|
|
|
|
- Боец ближнего боя — обычный союзник для `LaneClearFrom`. Стрелки сквозь него
|
|
|
|
|
|
не стреляют, и решатель разводит их так, чтобы работать мимо него.
|
|
|
|
|
|
- Пока идёт замах или удар, боец **не расступается** из чужих линий. Смысла нет:
|
|
|
|
|
|
стрелок всё равно не выстрелит (правило 6.3 запрещает), а удар сорвётся.
|
|
|
|
|
|
Расступается он до и после.
|
|
|
|
|
|
- Рывок учтён в `AllyInterceptsShot` отдельной экстраполяцией: на замахе боец
|
|
|
|
|
|
почти неподвижен и через мгновение выстреливает по уже известной оси, о чём
|
|
|
|
|
|
его текущая скорость ничего не говорит. Проверяются обе точки — где он есть и
|
|
|
|
|
|
куда прыгнет. Именно поэтому `MaxAgentSpeed()` намеренно **не** включает
|
|
|
|
|
|
скорость рывка: она бы раздула запас для всех выстрелов сразу.
|
|
|
|
|
|
|
|
|
|
|
|
## Как это выглядит
|
|
|
|
|
|
|
|
|
|
|
|
[src/render/fx.cpp](../src/render/fx.cpp), `DrawMeleeArcs`. Дуга читается прямо
|
|
|
|
|
|
из состояния агентов, а не из журнала событий: фаза живёт десятые доли секунды,
|
|
|
|
|
|
её видно в каждом кадре, и привязка к живой позиции важнее — боец в момент удара
|
|
|
|
|
|
едет вперёд.
|
|
|
|
|
|
|
|
|
|
|
|
- **WINDUP** — холодная стальная дуга уводится назад за край сектора и
|
|
|
|
|
|
разгорается. Это телеграф.
|
|
|
|
|
|
- **STRIKE** — серп из четырёх концентрических дуг: внешний край раскалён,
|
|
|
|
|
|
внутренние тускнеют к рукояти. Одна линия на 480×270 читается палкой, четыре —
|
|
|
|
|
|
взмахом. Хвост — доля пройденной дуги (`bladeTrailFrac`), а не вся: серп, а не
|
|
|
|
|
|
кольцо. На голове — раскалённая радиальная черта и точка.
|
|
|
|
|
|
- **RECOVER** — серп стоит на месте и быстро гаснет.
|
|
|
|
|
|
|
|
|
|
|
|
В изометрии круг — эллипс, поэтому дуга рисуется сэмплами мировых точек, а не
|
|
|
|
|
|
экранной окружностью.
|
|
|
|
|
|
|
|
|
|
|
|
События `MELEE_SWING` / `MELEE_HIT` / `MELEE_ABORT` идут по тому же каналу
|
|
|
|
|
|
`sim/events.h`, что и выстрелы: искры, вспышки света и тряска камеры (`meleeShake`
|
|
|
|
|
|
заметно сильнее, чем на выстрел — удар всегда в упор).
|
|
|
|
|
|
|
|
|
|
|
|
Класс читается силуэтом: у бойца капсула шире и со стальным клинком. Цвет занят
|
|
|
|
|
|
номером агента, поэтому классом он быть не может.
|
|
|
|
|
|
|
|
|
|
|
|
## Метрики
|
|
|
|
|
|
|
|
|
|
|
|
`FIRE UPTIME` усредняется **только по стрелкам**: линии огня у клинка не бывает
|
|
|
|
|
|
по определению, и его ноль занижал бы метрику стрельбы. Отдельно считается
|
|
|
|
|
|
`MELEE reach` — доля времени в зоне удара. Для `SETTLE TIME` «в бою» = стрелок
|
|
|
|
|
|
при линии **или** клинок при цели.
|
|
|
|
|
|
|
|
|
|
|
|
Критерии M7.1–M7.3 в `--accept` — [09-acceptance.md](09-acceptance.md).
|
|
|
|
|
|
|
|
|
|
|
|
## Куда приедут пушки и апгрейды
|
|
|
|
|
|
|
|
|
|
|
|
[src/sim/loadout.h](../src/sim/loadout.h). Сейчас профиль ровно один на класс и
|
|
|
|
|
|
целиком копируется из `tuning.h` каждый шаг (`SyncLoadoutFromTuning`) — так
|
|
|
|
|
|
работает runtime-подкрутка. Когда профилей станет несколько, здесь встанет
|
|
|
|
|
|
таблица и модификаторы поверх неё, а весь остальной код продолжит читать
|
|
|
|
|
|
`Agent::weapon` / `Agent::melee` и не заметит разницы.
|
|
|
|
|
|
|
|
|
|
|
|
Что уже вынесено в параметры и годится под модификаторы: досягаемость, ширина
|
|
|
|
|
|
сектора и ширина лезвия, длительности всех трёх фаз, интервал, урон, отбрасывание,
|
|
|
|
|
|
скорость и ускорение рывка, множители скорости на замахе и восстановлении,
|
|
|
|
|
|
радиус своего круга.
|
Личинки, парирование и инструмент --wall (M30)
--WALL. Новый прогон отвечает на вопрос, появившийся вместе с чекпоинтами
через пятьдесят этажей: растёт ли достижимая глубина от вылазки к вылазке.
Отряд из одного, трёх и пяти, одетый по глубине, ставится в скопление
тварей и живёт полторы минуты без управления. Первая стена: одиночка D7,
трое D30, пятеро D30 - петля работает.
И он же нашёл настоящую ошибку: PlaceSquad воскрешал ВСЕ пять слотов, в
том числе незанятые. Строка писалась, когда отряд всегда был впятером;
после появления ростера одиночка выходил на этаж с четырьмя невидимыми
телохранителями. Из-за них прежний замер "одиночка выжил на первом этаже,
100 -> 41" был боем впятером.
ЛИЧИНКИ. С исправленным замером выяснилось, что одиночка с ножом
проигрывает двум бредущим: 210 здоровья против ножа это девять секунд
работы под ответным уроном. Первые этажи населены личинками (55 здоровья,
7 урона, медленные), бредущий уехал на третий этаж, бегун на четвёртый.
Новый ВИД, а не ослабленный бредущий с множителем: глубина в этой игре
меняет, кто выходит, а не числа у одного и того же.
ПАРИРОВАНИЕ. Отдельная характеристика клинка, и чем оружие легче, тем она
выше: нож 34%, сабля 20%, тесак 8%. Не выведена из веса намеренно - её
двигает аффикс PARRY, и лёгкий тесак обязан быть возможен как находка.
Отбить можно, только если удары СОПРИКОСНУЛИСЬ: боец сам должен быть в
замахе или в ударе. Иначе клинок стал бы щитом, работающим, пока стоишь.
Удавшийся отбив снимает урон, бьёт тварь ударом бойца целиком и запускает
повторную атаку с начала фазы, НЕ сбрасывая откат. Последнее и есть суть
размена: сбрасывай отбив откат, выгоднее было бы подставляться под удары.
Плевок отбивается тоже: летит назад и несёт урон бойца, а не твари. Это
его удар, просто по летящей цели.
Проверка меряет размен целиком, включая обратную половину правила:
"стоя столбом не отбивает". Без неё условие про соприкосновение
существовало бы только в комментарии.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 09:04:35 +07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Парирование
|
|
|
|
|
|
|
|
|
|
|
|
Клинок умеет отбивать. Шанс — отдельная характеристика оружия (`BladeDef::parry`),
|
|
|
|
|
|
и **чем оружие легче, тем он выше**: нож 34%, сабля 20%, тесак 8%. Тесаком не
|
|
|
|
|
|
защищаются, им бьют первым.
|
|
|
|
|
|
|
|
|
|
|
|
Характеристика именно отдельная, а не выведенная из веса: её двигают аффиксы
|
|
|
|
|
|
(`PARRY`), и «лёгкий тесак» обязан быть возможен как находка, а не запрещён
|
|
|
|
|
|
формулой.
|
|
|
|
|
|
|
|
|
|
|
|
**Отбить можно, только если удары соприкоснулись.** Боец должен сам быть в замахе
|
|
|
|
|
|
или в ударе (`MeleeWeapon::Committed()`). Парирование «просто так» превратило бы
|
|
|
|
|
|
клинок в щит, который работает, пока стоишь, — и ближний бой перестал бы быть
|
|
|
|
|
|
разменом.
|
|
|
|
|
|
|
|
|
|
|
|
Удавшийся отбив:
|
|
|
|
|
|
|
|
|
|
|
|
| | |
|
|
|
|
|
|
|---|---|
|
|
|
|
|
|
| урон бойцу | не проходит |
|
|
|
|
|
|
| тварь | получает удар бойца целиком, со стихиями и пробитием |
|
|
|
|
|
|
| боец | бьёт ЗАНОВО с начала фазы удара |
|
|
|
|
|
|
| откат | **не сбрасывается** |
|
|
|
|
|
|
|
|
|
|
|
|
Последняя строка — то, ради чего всё и делалось. Сбрасывай отбив откат, это была
|
|
|
|
|
|
бы бесплатная атака, и выгоднее стало бы подставляться под удары. Не сбрасывая,
|
|
|
|
|
|
парирование даёт ровно один лишний удар в текущем размене.
|
|
|
|
|
|
|
|
|
|
|
|
**Пуля тоже отбивается.** Плевок при удачном парировании летит назад и несёт урон
|
|
|
|
|
|
БОЙЦА, а не твари: это его удар, просто по летящей цели. Отражённый снаряд
|
|
|
|
|
|
становится дружественным, поэтому дальше по коду работает как обычный выстрел и
|
|
|
|
|
|
в своих не попадёт.
|
|
|
|
|
|
|
|
|
|
|
|
Проверяется в `--descend` — размен целиком, а не факт срабатывания:
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
парирование: нож 34%, тесак 8%; отбив снял урон, ответ прошёл,
|
|
|
|
|
|
откат не сброшен, стоя столбом не отбивает
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Последняя половина строки важна не меньше первой: без неё правило «удары должны
|
|
|
|
|
|
соприкоснуться» существовало бы только в комментарии.
|