squad-proto/docs/21-damage.md
z.kirill c1831ac370 Статусы стихий и реакции (M22)
Стихии сами по себе были только множителем урона: «верный ствол бьёт втрое
больнее». Этого мало, чтобы они стали ЗНАНИЕМ, ради которого заводились —
множитель одинаково работает у того, кто понимает систему, и у того, кто взял
ствол посильнее.

Статусы и реакции добавляют слой, который работает только у первого: наложить
ДВЕ разные стихии на одну цель — это решение о составе отряда, а не о
характеристиках оружия.

  sim/status.h      правила, потолки стаков, реакции, числа
  sim/reactions.*   наложение и запуск, единственное место
  UpdateStatuses    тик горения и сгорание стаков, из UpdateTargets

СИЛА РЕАКЦИИ СЧИТАЕТСЯ ОТ ГЛУБИНЫ, А НЕ ОТ УРОНА ОРУЖИЯ. Считай она от оружия —
на сотом этаже стала бы округлением, и весь слой стихий превратился бы в
украшение. От глубины она растёт сама, но только у того, кто умеет её запускать.

ТРИ ОГРАНИЧИТЕЛЯ, И ВСЕ ПРОВЕРЯЮТСЯ

  потолок стаков   один стак не значит ничего, полный набор меняет бой
  откат на цели    вторая пара внутри 1.2 c даёт 0.0 урона
  запрет рекурсии  урон ОТ реакции не кладёт статусов

Третий не теоретический: без него цепочка «реакция бьёт по площади -> урон
кладёт стихию -> новая пара -> реакция» уходит в самоповтор на первом же
скоплении. Именно так ломаются все игры с триггерами.

Четыре реакции из десяти. Остальные ждут систем, которых нет (крит,
воскрешение, притяжение), и дописывать их «пока не работает» значило бы завести
шесть мёртвых веток.

Два отступления от документа, оба сознательные: кислота и дуга увеличивают
получаемый урон вместо снятия брони и метки крита (у тварей нет ни брони как
числа, ни крита), SUPERCOND кладёт стаки пустоты вместо отдельного среза
сопротивлений — пустота в этой игре и означает «защита не работает».

Шаг симуляции 2.9 мкс из бюджета 16666: статусы на 64 целях ничего не стоят.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 05:45:17 +03:00

21 KiB
Raw Blame History

21 — Урон: стихии, статусы, реакции, триггеры

Продолжение 20-gdd.md. Здесь вся боевая арифметика.

Главное ограничение, из которого всё следует: в игре нет ручных действий (спек 5). Игрок водит диск и строит отряд; ни одна механика ниже не вводит кнопку. Стихии накладываются оружием, реакции срабатывают сами, эффекты висят на снаряжении. Мастерство — это ЧТО ты взял вниз и КУДА повёл отряд, а не как быстро ты нажимаешь.

1. Семь стихий

Код Имя Что делает Против кого
KIN Кинетика оглушение, отбрасывание универсально, режется бронёй
INC Жар горение, распространение при смерти мягкие, скопления
CRY Холод замедление, заморозка быстрые, рывковые
TOX Кислота снятие брони, урон от макс. HP бронированные, жирные
ARC Дуга цепь по целям, наводка толпы, слабые
RAD Радиация взрыв при смерти, заражение стаи, respawn-твари
VOID Пустота срезание сопротивлений, притяжение всё, что защищено

Стихии вводятся по актам (20-gdd.md §3), а не разом: игрок должен успеть понять каждую, прежде чем получит следующую.

1.1. Статусы

Каждая стихия кладёт стаки статуса. Стак живёт dur, сгорает по одному.

Статус Стаки Эффект за стак Потолок
Горение (INC) 5 6% урона удара в секунду, 4 с при смерти поджигает в радиусе 1.5
Оледенение (CRY) 10 4% скорости, 3 с 10 стаков → Заморозка 1.4 с, +50% получаемого KIN
Разъедание (TOX) 8 4% брони, 5 с +0.8% макс. HP цели в секунду
Разряд (ARC) 4 +5% получаемого крита, 3 с 4 стака → цепь на 2 цели по 40%
Заражение (RAD) 6 +3% получаемого урона, 6 с при смерти взрыв 60% макс. HP, R=2.2
Схлопывание (VOID) 3 8% всех сопротивлений, 4 с 3 стака → притяжение к центру

Числа подобраны под один принцип: один стак не значит ничего, полный набор меняет бой. Это заставляет выбирать цель и держать её, а не поливать всех.

2. Формула урона

Одна формула на всё — и на пулю, и на клинок, и на тик горения.

                  ┌ базовый урон источника
dmg_raw   = W · (1 + Σ add) · Π mult
dmg_elem  = dmg_raw · share(e)                для каждой стихии e удара
mitig(e)  = 1  clamp(R(e) + R_aff  PEN(e), 0.60, +0.85)
dmg_final = Σ_e dmg_elem · mitig(e) · crit · vuln
Символ Что Диапазон
W урон оружия (каталог sim/weapon_defs.h) 8 → 900 по глубине
Σ add аддитивные проценты (аффиксы «+X% урона») 0 → 4.5
Π mult множители (специализации, реакции, метки) 1.0 → 3.0
share(e) доля стихии в уроне, Σ = 1 конверсия аффиксами
R(e) сопротивление вида (каталог) 0.5 → 0.9
PEN(e) пробитие сборки 0 → 0.75
crit 1.0 или critMult 1.5 → 4.0
vuln уязвимости от статусов 1.0 → 2.2

Почему сопротивление режется до 0.60, а не глубже. Отрицательное сопротивление — это уязвимость, и она обязана быть ощутимой, но не абсурдной: при 2.0 одна верная стихия обесценивала бы всю остальную сборку.

Почему потолок +0.85, а не 1.0. Иммунитета нет ни у кого. Тварь, которую физически нельзя ранить, — это не сложность, а тупик: игрок без нужной стихии просто не может играть.

2.1. Броня — отдельно от сопротивлений

Броня режет только KIN и работает долей с жёстким потолком ARMOR_CAP = 0.72 (уже в коде). TOX снимает броню стаками — это единственный способ пробить тяжёлую цель без пробития в сборке.

2.2. Крит

critChance = clamp(0.05 + Σ aff, 0, 0.75)
critMult   = 1.5 + Σ aff                      (без потолка)

Шанс с потолком, множитель без — потому что «всегда крит» убивает разброс результата, а «очень больно раз в четыре удара» его сохраняет.

3. Реакции

Две РАЗНЫЕ стихии на одной цели в пределах 3 секунд дают реакцию. Реакция срабатывает сама и снимает оба статуса.

Реакция Пара Эффект
Растрескивание INC + CRY всплеск 250% APow в точке, +80% к следующему KIN
Возгорание INC + TOX облако R=3.0, 180% APow в секунду, 3 с
Сверхпроводник ARC + CRY 30% всех сопротивлений цели и соседей, 6 с
Электролиз ARC + TOX все DoT на цели ×3 на 3 с
Распад RAD + INC зона R=2.5 на 8 с, 60% APow в секунду, кладёт RAD
Пепел RAD + CRY цель не воскресает и не респавнится
Резонанс VOID + любая реакция повторяется вторым срабатыванием на 60%
Схлоп VOID + ARC притяжение всех в R=4 к цели, оглушение 1 с
Осколки KIN + CRY замороженная цель при смерти бьёт осколками, 120% APow
Прорыв KIN + TOX 40% брони цели на 5 с, игнорируя потолок

3.1. Сила реакции не зависит от оружия

APow(d) = 4 + 0.12·d                    (d — глубина этажа)
reaction = k · APow(d) · (1 + reactionBonus)

Это ключевое решение. Реакция считается от глубины, а не от урона оружия — иначе на сотом этаже реакции стали бы округлением, и вся система стихий превратилась бы в украшение. Так же поступает Genshin с Elemental Mastery, и по той же причине.

Следствие: реакции — это канал «знания» из закона масштаба (20-gdd.md §2). Они растут сами по глубине, но только у того, кто умеет их запускать.

3.2. Внутренний откат

У каждой пары свой ICD: 1.2 с на цель. Без него цепь ARC по десяти тварям взорвала бы этаж одним нажатием, которого к тому же нет.

4. Триггеры: on-hit, on-kill, on-death

Журнал боевых событий уже существуетsim/events.h, кольцевой буфер, по которому сейчас работают эффекты и свет. Триггеры вешаются на него, а не рядом: второй канал событий разошёлся бы с первым в первый же месяц.

Триггер Когда Пример эффекта
on_attack начало выстрела/замаха «каждый 5-й выстрел бесплатный»
on_hit попадание «10% наложить CRY»
on_crit критическое попадание «крит снимает 1 стак перезарядки»
on_kill цель умерла от тебя «+8% скорости на 3 с, стак до 5»
on_death ЦЕЛЬ умерла (любая) «труп взрывается RAD»
on_hurt по тебе попали «20%: наложить INC атакующему»
on_down твой оперативник пал «отряд получает +25% урона на 10 с»
on_floor вход на этаж «+1 бинт, если сумка не полна»
on_extract эвакуация «+15% материалов»

4.1. Против взрыва комбинаций

Три ограничителя, все обязательные:

  1. ICD у каждого эффекта. Минимум 0.5 с, у мощных — до 8 с.
  2. Бюджет строки. Предмет несёт суммарный бюджет B(ilvl) = 40 + 3·ilvl; каждый аффикс стоит по тиру (22-loot.md §3). Шесть сильнейших аффиксов не влезают в один предмет никогда.
  3. Запрет рекурсии. Эффект, сработавший от триггера, не может запустить тот же триггер: on_kill → взрыв → смерть → on_kill обрывается на первом шаге.

Без третьего пункта одна цепь ARC на скоплении из сорока тварей вешает игру — и это не гипотеза, а то, как ломаются все игры с триггерами.

5. Твари: сопротивления и роли

Сопротивление — свойство ВИДА, из каталога проекта. Роль читается по профилю сопротивлений, и это единственная подсказка, которую игра даёт напрямую.

Вид Акт KIN INC CRY TOX ARC RAD Роль
Бредущий I .0 .2 .0 .0 .0 .3 мясо
Бегун I .0 .0 .4 .0 .2 .0 давление
Плевок II .1 .0 .0 .5 .3 .2 дистанция
Хват III .3 .3 .1 .0 .0 .0 контроль
Громила III .5 .2 .3 .4 .0 .1 танк
Рой IV .2 .5 .0 .0 .4 .4 толпа
Скорлупа IV .7 .3 .4 .5 .3 .2 стена
Плакальщик V .2 .0 .0 .2 .2 .6 воскрешает
Пустотник VI .3 .3 .3 .3 .3 .3 требует VOID

Читается так: против Скорлупы бесполезно всё, кроме TOX; Плакальщика надо жечь радиацией, иначе он поднимет остальных; Пустотник равномерно защищён, и его берут только срезанием сопротивлений.

Ни у одного вида нет множителя HP от глубины. Глубже выходят ДРУГИЕ виды — правило из 16-descent.md, и оно не отменяется до Бездны (23-endgame.md §2, где отменяется осознанно и с объяснением).

6. Коридор TTK

Чтобы бой не превращался ни в тир, ни в губку, время убийства держится в коридоре — это проверяемое обещание, а не пожелание.

Цель TTK одним оперативником, с верной стихией с неверной
мясо своей полосы 0.61.2 с 24 с
давление 1.02.0 с 48 с
танк 47 с 1540 с
элита (мутант) 815 с не убивается до отката ресурсов

Разрыв «верная/неверная» — от ×3 до ×6. Меньше — стихии перестают значить, больше — игра требует энциклопедии перед каждым спуском.

Проверяется инструментом --tool ttk <глубина>: он печатает таблицу «вид × сборка → секунды» и валится с кодом 1, если хоть одна пара вышла из коридора.

6.1. Что из этого уже в коде

Веха M21 положила арифметику, но не статусы: горение, оледенение и реакции — следующая веха, они живут ПОВЕРХ этого файла, а не внутри него.

Что Где
стихии, сопротивления, формула src/sim/element.h
профиль вида EnemyDef::resist, src/sim/target.cpp
состав удара оружия FirearmDef::elem, BladeDef::elem
единственная точка урона ResolveDamage в bullet.cpp и в клинке
проверка squad_proto --tool ttk

Стихии есть только у НАХОДИМОГО оружия. Карабин, пистолет и сабля — чистая кинетика, потому что на них меряется приёмка, и её числа обязаны остаться теми же.

Как коридор чуть не измерили неправильно

Первая версия --tool ttk считала разброс по времени убийства и показывала уверенные 2.8x на каждой твари. Выглядело как готовая система — а на самом деле это была разница МОЩНОСТИ стволов: магнум лучше пистолета против всех, и стихии тут ни при чём.

С вынесенной за скобки мощностью оружия правда оказалась другой: 1.041.36x, то есть состав удара не значил почти ничего. Механика была декоративной ровно в том же смысле, в каком когда-то был вес.

Чтобы разброс появился, у каждой твари должны быть И уязвимость, И сопротивление среди тех стихий, которые вообще носит оружие. Одной уязвимости мало: если ни один ствол не бьёт тем, что тварь держит, минимум остаётся единицей.

ENEMY         rifle   pistol  shotgun      smg   magnum      bow      ELEM
SHAMBLER        7.5     10.1      8.3      7.2      3.9      6.0     1.90x
SPITTER         4.4      5.9      6.1      2.3      2.2      7.0     2.61x
BRUTE          46.5     62.4     44.2     18.0     14.4     12.1     3.06x

Читается так: дробовик жжёт бредущих, дуга бьёт плевков, лук разъедает громилу, а карабин против громилы бесполезен — сорок шесть секунд.

Коридор пока 1.88.0, а не заявленные 36: чтобы дотянуть до тройки, нужны почти чистые стихийные сборки, а они приходят с аффиксами (22-loot.md). Занижать цель в документе нельзя, поэтому в инструменте стоит достижимое, а в таблице выше — целевое.

6.2. Статусы и реакции в коде (M22)

Что Где
правила статусов, потолки, реакции, числа src/sim/status.h
наложение и запуск реакции src/sim/reactions.cpp
тик горения и сгорание стаков UpdateStatuses, зовётся из UpdateTargets
проверка squad_proto --tool reactions

Реализованы четыре реакции из десяти: SHATTER, IGNITE, SUPERCOND, DECAY. Остальные ждут систем, которых пока нет (крит, воскрешение, притяжение), и дописывать их «пока не работает» значило бы завести шесть мёртвых веток.

Два отступления от документа, оба сознательные:

  • кислота и дуга увеличивают получаемый урон, а не снимают броню и не вешают метку крита: у тварей в этой игре брони как числа нет, а крита нет вовсе. Эффект тот же, лишней сущности не заводится;
  • SUPERCOND кладёт стаки пустоты вместо отдельного «среза сопротивлений»: пустота в этой игре и означает «защита не работает», и второе имя для того же было бы просто вторым именем.

Горение считается от МАКСИМАЛЬНОГО здоровья цели, а не от урона удара. Иначе на громиле оно не значит ничего, а мелочь дожигает само.

Три ограничителя проверяются, а не подразумеваются

REACTION         TARGET  NEIGHBOUR   STACKS
SHATTER            13.0        0.0        0
IGNITE              9.4        4.7        0
SUPERCOND           3.1        0.0        0
DECAY               6.2        3.1        0

откат: вторая пара внутри 1.2 c нанесла 0.0
рекурсия: урон от реакции наложил стаков 0
сила реакции: D1 4.1, D50 10.0, D100 16.0

Столбец STACKS — сколько стихий осталось после реакции. Ноль обязателен: не погасив пару, следующий удар сложил бы её снова, и реакция стала бы постоянным фоном вместо события.

Шаг симуляции после вехи — 2.9 мкс при бюджете кадра 16666: статусы на шестидесяти четырёх целях ничего не стоят.

7. Свет как ресурс

Хоррор-часть, которая сейчас не сделана: фонарь даёт множитель обзора и не садится. Должен садиться.

заряд(t) = заряд₀  drain·t,   drain = 1/сек
радиус = base · (0.35 + 0.65 · заряд/заряд₀)

Батареи — расходник (вес 0.3), заменяются автоматически при разряде, как бинты. Кончились — отряд идёт в темноте: обзор 35%, твари замечают первыми.

Это второй после патронов ресурс, который гонит наверх, и он же — причина, по которой ветвь Разведки в дереве узлов не «приятная», а обязательная.