applications/PoP/docs: план порта loose floors (проваливающиеся полы)

Разбор SDLPoP (seg007 loose/trob/mob, seg008 draw_loose): хранение
состояния (tile 11 + curr_room_modif: 0 покой / 0x80.. тряска / 1..11
отсчёт падения), триггеры тряски (do_knock на приземлении → shake ряда)
и падения (make_loose_fall при стойке/зацепе на loose), падающий кусок
(mob → debris снизу, empty сверху), отрисовка по статусу (loose_fram_*
через get_loose_frame).  Порядок реализации L1..L5 + что нужно в нашем
движке (динамический тайловый слой: room_modif[], trob-очередь, mob).

ПЛАН — не реализация.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-18 12:31:23 +03:00
parent 3c0baacbf6
commit fce3e58830
@@ -0,0 +1,96 @@
# Loose floors (проваливающиеся полы) — план порта
Разбор SDLPoP (seg007 loose/trob/mob, seg008 draw_loose). ПЛАН, ещё не
реализовано. Тайл в комнате 1: `[2,6] = 0x0B = tiles_11_loose`.
## 1. Хранение состояния
- **Тип тайла**: `curr_room_tiles[tilepos] & 0x1F == 11` (tiles_11_loose).
Бит `0x20` = «solid» loose (авто-падающий вариант, ур.13 — от шага НЕ
падает). После падения тайл → `0` (tiles_0_empty).
- **Модификатор** `curr_room_modif[tilepos]` = состояние анимации:
- `0` — покой (обычный loose-пол);
- `0x80..0x83`**трясётся** (бит7); за ~4 кадра затухает обратно в 0;
- `1..11`**обратный отсчёт до падения** (на нём что-то стоит);
достигает `loose_floor_delay = 11` → падает.
## 2. Анимация тряски (shake) — когда включается
- **Триггер = do_knock** (seg007:0FE0): на ЖЁСТКОМ приземлении в кадрах
посадки играет `SEQ_KNOCK_DOWN` → взводит `knock``check_knock()`
`do_knock(room, curr_row (knock>0))`.
- `do_knock(room, row)`: по всем колонкам ряда — если тайл loose →
`loose_make_shake()`.
- `loose_make_shake()` (seg007:0FB4): если `modif==0` (и не ур.13) →
`modif = 0x80`, `add_trob(type 1)`.
- **Отсюда кейс пользователя**: Kid падает/приземляется на `[2,4]`
do_knock трясёт ВСЕ loose-тайлы ряда 2 → `[2,6]` трясётся. (Через
knock-смещение ряда может задеть и соседний ряд.)
- `animate_loose` (кадрово): `++modif`; при бите7 трясёт до `>=0x84`
сброс в 0, `trob.type=-1`. `loose_shake()` играет звук
(sound 20/21/22) по таблице `loose_sound[]`.
## 3. Анимация падения (fall) — когда включается
- **Триггер = make_loose_fall(1)** (seg007:0EF6), вызывается когда:
- Kid СТОИТ на loose-тайле — `check_press()` (seg006): кадр с
FRAME_NEEDS_FLOOR над loose → make_loose_fall(1);
- зацеп/подтягивание на loose (`check_grab`, `check_jump_up`);
- пробой сверху: кадр 79 (jumphang) над loose → make_loose_fall(1);
- авто-падающие (ур.13) — `make_loose_fall(-(prandom&0x0F))`.
- `make_loose_fall(modifier)`: если НЕ solid (`tiles & 0x20 == 0`) и
`(sbyte)modif <= 0``modif = modifier`, `add_trob(type 0)`.
- `animate_loose`: `++modif`; когда `modif >= 11` (loose_floor_delay) →
`remove_loose()` (тайл → empty) + `add_mob()` (спавн падающего куска).
## 4. Падающий кусок (mob)
- `add_mob()` кладёт `curmob` в `mobs[]` (до 14). `do_mobs()` каждый
кадр: `move_mob()` (гравитация, y растёт) + `check_loose_fall_on_kid()`
(урон Киду/страже, если попал).
- Приземление куска → тайл под ним `curr_room_tiles[...] = tiles_14_debris`
(seg007 move_mob:1053). Т.е. **loose(11) упал → сверху empty(0), снизу
debris(14)**.
## 5. Отрисовка по статусу
- Куски тайла: `loose_fram_left[]={41,69,41,70,70,41,41,41,70,70,70,0}`,
`loose_fram_right[]={42,71,...}`, `loose_fram_bottom[]={43,73,...}`
(env-спрайты, seg008:518/596/608).
- Индекс кадра = `get_loose_frame(modifier)` (seg008): `0` = ровный
(41/42/43); `1..10` = дрожащие варианты (69–74); при бите7/большой
задержке — низкие индексы.
- **До падения**: рисуем loose с `get_loose_frame(modif)` (0 = ровно,
иначе колеблется). **После**: сверху empty, снизу debris(14) — обычная
статическая отрисовка (у нас уже есть tile 0x0E/14 debris в tile_table).
## 6. Что нужно в нашем движке (сейчас НЕТ)
Наш `pop_bg` рисует комнату СТАТИЧЕСКИ один раз. Loose-полы требуют
**динамического тайлового слоя**:
1. **Массив модификаторов** `room_modif[30]` (у нас есть `bg[30]` — можно
переиспользовать/рядом) — состояние каждого тайла.
2. **Очередь trob** (список анимируемых тайлов) + `animate_loose` пер-кадр
→ перерисовка ТОЛЬКО изменившихся тайлов (как heal-прямоугольник Kid).
3. **make_loose_fall / do_knock / loose_make_shake** — триггеры (из
физики Kid: приземление→knock, стойка на loose→fall).
4. **mob-система** (падающий кусок): минимум 1–2 mob'а, гравитация,
приземление → debris. Урон Киду (`check_loose_fall_on_kid`) — можно
Фазой 2.
5. **Перерисовка тайла**: `draw_tile(row,col)` у нас уже умеет loose
(`code==11`, `loose_fram_*` в env) — нужно вызывать его выборочно с
текущим модификатором (сейчас draw_tile берёт статический bg).
**Порядок реализации (предложение):**
- L1: room_modif[] + выборочная перерисовка тайла по модификатору
(draw_loose с get_loose_frame) — статика→динамика одного тайла.
- L2: trob-очередь + animate_loose (тряска по do_knock на приземлении).
- L3: make_loose_fall (стойка на loose) + отсчёт + remove → empty.
- L4: mob (падающий кусок → debris снизу).
- L5: урон Киду от падающего куска.
## Связанные
Триггеры завязаны на физику Kid ([[pop_hang_state]] check_press/check_grab,
приземление land/SEQ_KNOCK_DOWN). Отрисовка — [[pop_fore_layer]] /
[[pop_background_strategy]] (draw_tile уже знает loose_fram_*).