L1-START + L1-EXIT: старт по данным уровня и выход через дверь уровня
L1-START. Старт и оба рестарта (смерть, выпадение из уровня) сведены в pop_start_level() — порт start_level + do_startpos + set_start_pos (seg003). Комната/тайл/направление берутся из pop_level_start_*, направление инвертируется (~start_dir), поза входа — из tbl_entry_pose: у уровня 1 это падение внутрь (seq_7_fall) плюс нажатие кнопки room5(0,2), то самое, что захлопывает решётку за спиной. Жёсткие START_ROOM/COL/ROW убраны. Проверено в MAME: старт даёт room 1, col 0, падение на row 1 — как по данным. L1-EXIT. Ветка двери уровня из up_pressed + go_up_leveldoor (seg005:0482/ 0574): тайлы и геометрия — pop_leveldoor_enter() в pop_map, последовательность seq_70 — в pop_ctrl. Опкод 0xF1 END_LEVEL в play_seq инкрементит pop_next_level (порт next_level), главный цикл по нему перезапускает уровень — ровно та точка, куда levels_plan §2.2 подключит загрузку уровня 2. Открытость двери проверяется по modifier >= 42 (ветка fix_exit_door), а не по ванильному leveldoor_open: иначе можно войти в ещё ползущую створку. Отдельно стоило разбора: go_up_leveldoor сначала писал Char.x/Char.direction, и оба присваивания молча терялись — окно Char вокруг диспетчера возвращает назад только curr_seq и sword (pop_savekid_state). Направление оставалось «вправо», а все DX в seq_70 отрицательные, поэтому Кид уходил ИЗ проёма влево (поймано стоп-кадром). Геометрию персонажа в этом порте меняет pop_map, пишет в Kid — как pop_down_action и pop_jump_up_seq. Известный остаток — BUG-DOOR-CLIP в bug_list.md: нет обрезки силуэта правым косяком проёма (недопортирован obj_clip_right в clip_char); нужен вариант колоночного блита с ограничением ширины. ROOMNAV пока оставлен включённым — он нужен, чтобы попадать в комнату 9 для этой работы. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,11 +6,11 @@
|
||||
а то, **что берём в работу сейчас и в каком порядке**. Каждая запись: что
|
||||
сделать, почему именно сейчас, чем подтверждать результат.
|
||||
|
||||
**Открытых багов нет** (ревизия 2026-08-01): в [`bug_list.md`](bug_list.md)
|
||||
остались только незакрытые оптимизации [T-1](bug_list.md#t-1),
|
||||
**Открытые баги** ([`bug_list.md`](bug_list.md)):
|
||||
[BUG-DOOR-CLIP](bug_list.md#bug-door-clip) (Кид не обрезается правым косяком
|
||||
двери уровня) + незакрытые оптимизации [T-1](bug_list.md#t-1),
|
||||
[T-2](bug_list.md#t-2) и незаконченная
|
||||
[таблица обхода 24 комнат](bug_list.md#обход-всех-24-комнат-уровня-1).
|
||||
Следующая порция багов придёт оттуда и из L1-PASS.
|
||||
|
||||
Правило проекта в силе: механику сверять с `../SDLPoP/src/` ДО кодинга;
|
||||
диагноз платформы подтверждать артефактом (брейкпоинт/дамп/.asm), а не
|
||||
@@ -364,19 +364,41 @@
|
||||
|
||||
## P1 — сразу после P0 (закрываем уровень 1 как ИГРУ, а не стенд)
|
||||
|
||||
### L1-START. Старт по данным уровня
|
||||
`roomtest.c` жёстко стартует `START_ROOM 1 / COL 3 / ROW 0`, хотя
|
||||
`pop_level_start_room()` / `pop_level_start_pos()` / `pop_level_start_dir()`
|
||||
в `pop_level.h` уже реализованы и НИКЕМ не вызываются. Перевести старт и
|
||||
респавн на них; `#define ROOMNAV` оставить, но выключенным по умолчанию.
|
||||
### L1-START. Старт по данным уровня — **СДЕЛАНО 2026-08-01**
|
||||
Старт и оба рестарта (смерть, выпадение из уровня) сведены в один
|
||||
`pop_start_level()` — порт `start_level` + `do_startpos` + `set_start_pos`
|
||||
(seg003): комната/тайл/направление берутся из `pop_level_start_*`, направление
|
||||
инвертируется (`~start_dir`), поза входа — из `tbl_entry_pose`. У уровня 1
|
||||
это «падение внутрь» плюс нажатие кнопки room5(0,2) — то самое, что
|
||||
захлопывает решётку за спиной. Проверено: старт даёт room 1, col 0, падение
|
||||
на row 1 — как по данным уровня.
|
||||
|
||||
### L1-EXIT. Выход с уровня (дверь уровня)
|
||||
Сейчас: дверь открывается (`animate_leveldoor`), но войти в неё нельзя —
|
||||
`up_pressed()` (`pop_ctrl.c:188`) не проверяет `tiles_16_level_door_left`, а
|
||||
опкод `0xF1 END_LEVEL` в `play_seq` (`pop_kid.c:418`) пустой. Портировать
|
||||
`up_pressed`-ветку + `go_up_leveldoor()` (`seg005.c:410..500`) и завести
|
||||
`pop_next_level`, который взводит `END_LEVEL` (`seg006.c:662`). **Это же
|
||||
первый шаг плана следующих уровней** — см. `../docs/levels_plan.md`.
|
||||
⏳ **Остаток:** `#define ROOMNAV` пока ВКЛЮЧЁН — он нужен, чтобы попадать в
|
||||
комнату 9 для работы над [BUG-DOOR-CLIP](bug_list.md#bug-door-clip).
|
||||
Выключить перед L1-PASS.
|
||||
|
||||
### L1-EXIT. Выход с уровня (дверь уровня) — **СДЕЛАНО 2026-08-01**
|
||||
Портированы: ветка двери уровня из `up_pressed` + `go_up_leveldoor`
|
||||
(seg005:0482/0574) — в `pop_leveldoor_enter()` (`pop_map.c`, тайлы и
|
||||
геометрия) и `up_pressed()` (`pop_ctrl.c`, только последовательность);
|
||||
опкод `0xF1 END_LEVEL` в `play_seq` теперь инкрементит `pop_next_level`
|
||||
(порт `next_level`), а главный цикл по нему перезапускает уровень — ровно
|
||||
та точка, куда `levels_plan.md` §2.2 подключит загрузку уровня 2.
|
||||
|
||||
Открытость двери проверяем по `modifier >= 42` (ветка `fix_exit_door`), а не
|
||||
по ванильному `leveldoor_open`: с ванильным условием можно войти в ещё
|
||||
ползущую створку.
|
||||
|
||||
**Грабли, которые стоили отдельного разбора:** `go_up_leveldoor` сначала
|
||||
писал `Char.x`/`Char.direction`, и оба присваивания молча терялись — окно
|
||||
`Char` вокруг диспетчера возвращает назад ТОЛЬКО `curr_seq` и `sword`
|
||||
(`pop_savekid_state`). Направление оставалось «вправо», а все `DX`
|
||||
последовательности seq_70 отрицательные, поэтому Кид уходил ИЗ проёма влево.
|
||||
Вывод на будущее: геометрию персонажа в этом порте меняет `pop_map` (пишет в
|
||||
`Kid`), а не диспетчер.
|
||||
|
||||
⏳ Косметика: [BUG-DOOR-CLIP](bug_list.md#bug-door-clip) — нет обрезки силуэта
|
||||
правым косяком проёма.
|
||||
|
||||
### L1-TRIAGE. Ревизия багов — **ЧАСТЬ 1 СДЕЛАНА 2026-08-01**
|
||||
|
||||
|
||||
@@ -14,17 +14,61 @@ odd-pixel арифметика `char_x`, подстановка тайла на
|
||||
починена кодом ещё в июле, список просто отстал — чем именно закрыта каждая,
|
||||
расписано в [`bug_closed.md`](bug_closed.md)).
|
||||
|
||||
**Открытых багов сейчас нет** — только две незакрытые оптимизации и
|
||||
незаконченная ревизия комнат:
|
||||
|
||||
| ID | что | тип |
|
||||
|----|-----|-----|
|
||||
| [BUG-DOOR-CLIP](#bug-door-clip) | Кид не обрезается правым косяком двери уровня | Medium |
|
||||
| [T-1](#t-1) | пики перерисовываются безусловно | оптимизация |
|
||||
| [T-2](#t-2) | Кид перерисовывается в покое | оптимизация |
|
||||
| [обход 24 комнат](#обход-всех-24-комнат-уровня-1) | таблица заполнена на 5 строк из 24 | ревизия |
|
||||
|
||||
Это НЕ значит «багов нет» — значит, что известные найдены и закрыты, а
|
||||
следующая порция придёт из обхода 24 комнат и сквозного прохождения (L1-PASS).
|
||||
---
|
||||
|
||||
<a id="bug-door-clip"></a>
|
||||
## BUG-DOOR-CLIP. Подъём по лестнице двери уровня: нет обрезки по правому косяку — **Medium**
|
||||
|
||||
**Симптом (2026-08-01, найден пользователем сразу после L1-EXIT):** при
|
||||
подъёме по лестнице за дверью уровня (seq_70, кадры 224..228) силуэт Кида
|
||||
вылезает ПРАВЕЕ правого косяка проёма — должен уходить за него. По ВЫСОТЕ
|
||||
обрезка корректна.
|
||||
|
||||
**Причина — недопортированная половина `clip_char`** (seg006:1231). Для
|
||||
кадров двери уровня оригинал ставит ДВА клипа, а мы портировали только
|
||||
первый:
|
||||
|
||||
```c
|
||||
if (frame >= frame_224_exit_stairs_8 && frame < 229) {
|
||||
obj_clip_top = leveldoor_ybottom + 1; // ЕСТЬ (pop_clip_char_top)
|
||||
obj_clip_right = leveldoor_right; // НЕТ
|
||||
}
|
||||
```
|
||||
|
||||
`leveldoor_right = (draw_xh << 3) + 48` считается в `draw_leveldoor`
|
||||
(seg008:1D29) от колонки ЛЕВОЙ половины двери; для palace-уровней (+8,
|
||||
`tbl_level_type`) — на уровне 1 не применяется.
|
||||
|
||||
Обратить внимание: комментарий в SDLPoP говорит «frames 217..228», а КОД
|
||||
проверяет `>= frame_224_exit_stairs_8`, то есть **224..228**. Портировать по
|
||||
коду.
|
||||
|
||||
**Чего не хватает в движке:** обрезки СПРАВА у колоночного блита.
|
||||
`gfx_blit_cols_part` (libbgi) умеет только обрезку сверху (`skip`/`rows`) плюс
|
||||
клип по краю ЭКРАНА (`w = _bgi_maxx + 1 - x`). Для column-major спрайта
|
||||
обрезка справа — это ровно уменьшение `w`, то есть внутри ядра она уже есть,
|
||||
не хватает способа задать её снаружи.
|
||||
|
||||
**План (не начат):**
|
||||
1. libbgi: тело `gfx_blit_cols_part` вынести в вариант с ограничением
|
||||
ширины (напр. `gfx_blit_cols_part_w(..., uint8_t maxw)`), а нынешнюю
|
||||
функцию оставить тонкой обёрткой — тем же приёмом, каким `gfx_blit_cols`
|
||||
уже обёрнут вокруг `gfx_blit_cols_part`. Флип тут не мешает: в кадрах
|
||||
двери направление всегда dir_FF_left, то есть flip=0.
|
||||
2. PoP: `pop_clip_char_top` дополнить правым клипом (или завести
|
||||
`pop_clip_char_right`), считать `leveldoor_right` от колонки двери.
|
||||
3. `make size-check` (трогаем libbgi) + проверка стоп-кадром в MAME:
|
||||
поймать кадр 224..228 клавишей `1` и сверить кромку.
|
||||
|
||||
**Пока не починено, дефект косметический** — вход на следующий уровень
|
||||
работает.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
#define SEQ_14_JUMP_INTO_CEILING 14 /* прыжок вверх: сверху стена/пол */
|
||||
#define SEQ_28_JUMP_NOTHING_ABOVE 28 /* прыжок вверх: сверху пусто */
|
||||
#define SEQ_25_HANG_AGAINST_WALL 25 /* вис у стены (Shift в висе) */
|
||||
#define SEQ_GO_UP_LEVELDOOR 70 /* seq_70_go_up_on_level_door */
|
||||
|
||||
#define FRAME_7_RUN 7
|
||||
#define FRAME_11_RUN 11
|
||||
@@ -187,8 +188,15 @@ static void jump_up(void)
|
||||
|
||||
static void up_pressed(void)
|
||||
{
|
||||
/* (дверь уровня/телепорт — нет в тесте.) ↑+forward = прыжок с места;
|
||||
* иначе вертикальный прыжок вверх. */
|
||||
/* Дверь уровня рядом и открыта → войти в неё: постановку в проём
|
||||
* (go_up_leveldoor) делает pop_leveldoor_enter, здесь остаётся только
|
||||
* последовательность подъёма по лестнице. Телепортов у нас нет — это
|
||||
* модификация SDLPoP, а не оригинал.
|
||||
* Иначе: ↑+forward = прыжок с места, иначе вертикальный прыжок вверх. */
|
||||
if (pop_leveldoor_enter()) {
|
||||
seqtbl_offset_char(SEQ_GO_UP_LEVELDOOR);
|
||||
return;
|
||||
}
|
||||
if (control_x == CONTROL_HELD_FORWARD)
|
||||
standing_jump();
|
||||
else
|
||||
|
||||
@@ -23,6 +23,13 @@ pop_char_t Kid;
|
||||
* в play_seq, потребляется pop_check_knock (pop_map) — трясёт loose-полы. */
|
||||
int8_t knock;
|
||||
|
||||
/* Порт глобала next_level: seqtbl-команда END_LEVEL инкрементит его в конце
|
||||
* seq_70 (подъём по лестнице за дверью уровня), а главный цикл сравнивает с
|
||||
* текущим уровнем и грузит следующий. Счётчик, а не флаг — ровно как в
|
||||
* оригинале, чтобы читы «следующий/предыдущий уровень» (levels_plan.md §4)
|
||||
* легли сюда же без переделки. */
|
||||
uint8_t pop_next_level;
|
||||
|
||||
|
||||
/* ---- Атласы Kid (прямая адресация image>>3) ------------------------ */
|
||||
#define KID_MAXPAGES 28
|
||||
@@ -418,7 +425,12 @@ void play_seq(void)
|
||||
case 0xF2: /* SOUND — потребить 1 байт */
|
||||
Char.curr_seq++;
|
||||
break;
|
||||
case 0xF1: /* END_LEVEL */
|
||||
case 0xF1: /* END_LEVEL: seq_70 доиграл — уровень пройден.
|
||||
* В оригинале это `++next_level`, а главный цикл
|
||||
* (play_level_2, seg003:0386) выходит, когда next_level
|
||||
* разошёлся с current_level. У нас так же: счётчик, а
|
||||
* решение — снаружи (roomtest.c). */
|
||||
pop_next_level++;
|
||||
break;
|
||||
case 0xF3: /* GET_ITEM: аргумент 1 — применить эффект предмета
|
||||
* (порт play_seq -> proc_get_object, seg006) */
|
||||
|
||||
@@ -21,6 +21,11 @@ extern pop_char_t Kid;
|
||||
* (свой ряд). Ставит play_seq, читает/сбрасывает pop_check_knock. */
|
||||
extern int8_t knock;
|
||||
|
||||
/* Порт next_level (seg000): END_LEVEL в play_seq инкрементит — «уровень
|
||||
* пройден». Читает и обнуляет главный цикл. Пока уровень один, поэтому
|
||||
* там рестарт того же уровня; загрузка следующего — levels_plan.md. */
|
||||
extern uint8_t pop_next_level;
|
||||
|
||||
/* Загрузить npages атласов Kid (kid0..kid{npages-1}.atl). ДО initgraph.
|
||||
* 0 — OK, -1 — ошибка. */
|
||||
int pop_kid_load(uint8_t npages);
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "pop_bg.h"
|
||||
#include "pop_state.h"
|
||||
#include "pop_trob.h" /* room_modif + trob-список (пики/ворота) */
|
||||
#include "pop_level.h" /* pop_level_start_room — вход в дверь уровня */
|
||||
|
||||
/* ---- Тайлы / действия / seq id (подмножество PoP) ------------------ */
|
||||
#define TILE_EMPTY 0
|
||||
@@ -46,6 +47,7 @@
|
||||
#define TILE_POTION 10 /* зелье */
|
||||
#define TILE_SWORD 22 /* меч на полу */
|
||||
#define TILE_OPENER 15 /* кнопка-открывалка (RAISE, 0x0F) */
|
||||
#define TILE_LEVELDOOR_LEFT 16 /* ЛЕВАЯ половина двери уровня (на ней modif) */
|
||||
#define ACT_STAND 0
|
||||
#define ACT_RUNJUMP 1
|
||||
#define ACT_HANGCLIMB 2
|
||||
@@ -777,6 +779,58 @@ uint8_t pop_hang_fall_seq(void) __banked
|
||||
* либо спуск seq_68 через зацеп, либо отступ от передней кромки), 0 =
|
||||
* приседать. Меняет Kid.x. Kid развёрнут от края (край сзади) у кромки
|
||||
* пола + под соседним тайлом есть за что зацепиться → спуск на этаж вниз. */
|
||||
/* Ветка «войти в дверь уровня» из up_pressed (seg005:0482) вместе с
|
||||
* go_up_leveldoor (seg005:0574). Дверь рядом и открыта → поставить Кида в
|
||||
* проём, вернуть 1 (последовательность seq_70 ставит вызывающий — это его
|
||||
* слой); иначе 0 и Кид не тронут.
|
||||
*
|
||||
* Геометрию персонажа меняем в Kid, а НЕ в Char: окно Char вокруг
|
||||
* диспетчера возвращает назад ТОЛЬКО curr_seq и sword
|
||||
* (pop_savekid_state), поэтому во всём порте позицию правит pop_map — как
|
||||
* в pop_down_action и pop_jump_up_seq. Запись в Char.x/Char.direction
|
||||
* отсюда молча теряется (проверено: Кид уходил из проёма влево).
|
||||
*
|
||||
* Порядок проб как в оригинале и с той же семантикой `else if`: берём
|
||||
* ПЕРВУЮ клетку, в которой стоит дверь (под персонажем → за спиной → перед
|
||||
* лицом), и уже её проверяем на открытость — а не ищем «первую открытую».
|
||||
*
|
||||
* Открытость: `modifier >= 42`, то есть створка доехала до верха. Это
|
||||
* ветка fix_exit_door, а не ванильный глобал leveldoor_open: с ванильным
|
||||
* условием в дверь можно войти, пока она ещё ползёт вверх, и персонаж
|
||||
* проходит сквозь опущенную створку.
|
||||
*
|
||||
* Стартовая комната исключена (`level.start_room != drawn_room`): в первой
|
||||
* комнате уровня дверь — та, через которую вошли, и обратно в неё нельзя. */
|
||||
uint8_t pop_leveldoor_enter(void) __banked
|
||||
{
|
||||
int8_t cols[3];
|
||||
uint8_t i;
|
||||
|
||||
if (pop_level_start_room() == g_room) return 0;
|
||||
|
||||
cols[0] = Kid.curr_col;
|
||||
cols[1] = (int8_t)(dir_behind[Kid.direction + 1] + Kid.curr_col);
|
||||
cols[2] = (int8_t)(dir_front[Kid.direction + 1] + Kid.curr_col);
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (get_tile(cols[i], Kid.curr_row) != TILE_LEVELDOOR_LEFT)
|
||||
continue;
|
||||
/* дверь найдена — дальше решает только её modif (см. выше) */
|
||||
if (cols[i] < 0 || cols[i] > 9) return 0; /* дверь в шве: не наша */
|
||||
if (pop_trob_modif(g_room)[Kid.curr_row * 10 + cols[i]] < 42)
|
||||
return 0;
|
||||
/* go_up_leveldoor (seg005:0574): встать РОВНО в проём и развернуться
|
||||
* ВЛЕВО. Направление тут не косметика: все DX последовательности
|
||||
* seq_70 ОТРИЦАТЕЛЬНЫЕ (−5,−4,−4,−2,−3,−1,−3,−1,−2), а
|
||||
* char_dx_forward при взгляде влево меняет им знак — то есть именно
|
||||
* dir_FF_left и гонит персонажа ВПРАВО-ВВЕРХ по лестнице. При
|
||||
* взгляде вправо тот же seq уводит его влево, ИЗ проёма. */
|
||||
Kid.x = (uint8_t)(pop_x_bump[cols[i] + FIRST_ONSCREEN_COLUMN] + 10);
|
||||
Kid.direction = -1;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t pop_down_action(void) __banked
|
||||
{
|
||||
if (!tile_is_floor(get_tile_infrontof_char()) && distance_to_edge_weight() < 3) {
|
||||
|
||||
@@ -151,4 +151,10 @@ int8_t pop_hang_wall(void) __banked; /* Shift в висе: 1=у сте
|
||||
uint8_t pop_hang_fall_seq(void) __banked; /* seq_23 / seq_11 (сорваться), +Kid.x */
|
||||
uint8_t pop_down_action(void) __banked; /* 1=обработано(спуск/отступ), 0=присед */
|
||||
|
||||
/* Дверь уровня рядом и ОТКРЫТА ПОЛНОСТЬЮ? → поставить Кида в проём (x +
|
||||
* разворот влево, go_up_leveldoor) и вернуть 1; иначе 0, Кид не тронут.
|
||||
* Ветка двери уровня из up_pressed (seg005:0482). Последовательность
|
||||
* seq_70 ставит вызывающий (pop_ctrl) — тайлы наши, управление его. */
|
||||
uint8_t pop_leveldoor_enter(void) __banked;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -89,12 +89,11 @@ static void kbd_idle(void)
|
||||
#define ROOM_1 1
|
||||
#define LINK_DOWN 3 /* сторона links: 0=L,1=R,2=U,3=D */
|
||||
|
||||
/* Тест-старт для отладки пик (Фаза S): комната 6 = тестовая (пики в col3/col4
|
||||
* row2, шахта col3). Kid на полу row0 col1, идёт вправо → падает в шахту →
|
||||
* пики. TODO: вернуть на pop_level_start_room (комната 1) после отладки. */
|
||||
#define START_ROOM 1 /* обход комнат: старт с 1-й, дальше +/- (ROOMNAV) */
|
||||
#define START_COL 3
|
||||
#define START_ROW 0
|
||||
/* Поза входа на уровне (tbl_entry_pose, data.h:848): у уровня 1 — падение
|
||||
* внутрь плюс нажатие кнопки. Уровень пока один, поэтому константа; когда
|
||||
* появятся уровни 2+ (levels_plan.md), это станет таблицей в pop_level. */
|
||||
#define LEVEL_ENTRY_POSE ENTRY_POSE_FALL
|
||||
|
||||
#define RESPAWN_DELAY 400 /* авто-респавн (фолбэк), если не нажали ↑ */
|
||||
#define RESPAWN_SETTLE 20 /* тиков до приёма ↑ (труп успел лечь) */
|
||||
|
||||
@@ -262,6 +261,60 @@ static void enter_room(uint8_t room)
|
||||
pop_leave_timer = 2; /* exit_room_timer: 2 кадра блокировки leave (анти-ping-pong/#4) */
|
||||
}
|
||||
|
||||
/* start_level (seg003:0055) + do_startpos (seg003:01A3) + set_start_pos
|
||||
* (seg003:028A) — поставить Кида в стартовую позицию УРОВНЯ. Один вход и
|
||||
* для запуска, и для рестарта после смерти / выпадения из уровня: в
|
||||
* оригинале это буквально один и тот же путь (play_level_2 зовёт
|
||||
* start_level заново), поэтому и у нас не три копии четырёх строк.
|
||||
*
|
||||
* Позиция берётся ИЗ ДАННЫХ уровня (pop_level_start_*), а не из #define:
|
||||
* start_pos — тайл 0..29 (row*10+col), start_dir — направление, в котором
|
||||
* персонаж должен ОКАЗАТЬСЯ.
|
||||
*
|
||||
* Направление ИНВЕРТИРУЕТСЯ (`Char.direction = ~level.start_dir`): персонаж
|
||||
* ставится спиной и доворачивается кадрами позы входа. Сама поза — из
|
||||
* tbl_entry_pose (data.h:848 = {0,1,0,…,2,…}):
|
||||
* 1 (уровень 1) — падение внутрь (seq_7_fall) + нажатие кнопки room5(0,2);
|
||||
* это оно захлопывает решётку за спиной в первой комнате;
|
||||
* 2 (уровень 13) — вбегание (seq_84_run);
|
||||
* иначе — разворот на месте (seq_5_turn).
|
||||
* Таблица индексируется НОМЕРОМ УРОВНЯ, поэтому появится в pop_level, когда
|
||||
* дойдём до уровней 2+ (levels_plan.md); пока уровень один. */
|
||||
#define SEQ_TURN 5
|
||||
#define SEQ_FALL 7
|
||||
#define SEQ_RUN 84
|
||||
#define ENTRY_POSE_FALL 1 /* tbl_entry_pose[1] */
|
||||
#define ENTRY_POSE_RUN 2 /* tbl_entry_pose[13] */
|
||||
|
||||
static void pop_start_level(uint8_t entry_pose)
|
||||
{
|
||||
uint8_t pos = pop_level_start_pos();
|
||||
int8_t dir = (int8_t)~pop_level_start_dir();
|
||||
uint8_t seq = SEQ_TURN;
|
||||
|
||||
pop_trob_reset(); /* рестарт уровня: пики/ворота в исходное */
|
||||
|
||||
/* Кнопку жмём ДО отрисовки комнаты — как в оригинале (do_startpos
|
||||
* отрабатывает раньше первого draw_room), иначе шов кадр показывал бы
|
||||
* ещё не тронутое состояние решётки. */
|
||||
if (entry_pose == ENTRY_POSE_FALL) {
|
||||
/* get_tile(5,2,0) + trigger_button(0,0,-1) — «нажать» кнопку room5,
|
||||
* col2, row0 её же типом и модификатором (в оригинале и то и другое
|
||||
* берётся из curr_* после get_tile). */
|
||||
uint8_t tp = 2; /* row0*10 + col2 */
|
||||
pop_trigger_button(5, tp, pop_level_tile(5, tp), pop_trob_modif(5)[tp]);
|
||||
seq = SEQ_FALL;
|
||||
} else if (entry_pose == ENTRY_POSE_RUN) {
|
||||
seq = SEQ_RUN;
|
||||
}
|
||||
|
||||
enter_room(pop_level_start_room());
|
||||
kid_init(seq, (int8_t)(pos % 10), (int8_t)(pos / 10), dir);
|
||||
Kid.sword = 0; /* set_start_pos: sword_0_sheathed */
|
||||
pop_have_sword = 0; /* start_level: have_sword с уровня 2 */
|
||||
pop_kid_hp_reset();
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* Дабл-буфер: front — видимая страница, back — куда рисуем. dbuf=1
|
||||
@@ -308,15 +361,11 @@ int main(void)
|
||||
* ТОЛЬКО прямоугольник Кида (kid_heal), а не весь экран. */
|
||||
/* Извлечь и отрисовать стартовую комнату (в обе страницы). P0: сбросить
|
||||
* trob/room_modif (ленивая инициализация из уровня при первом входе). */
|
||||
pop_trob_reset();
|
||||
enter_room(START_ROOM);
|
||||
/* Старт — ПО ДАННЫМ УРОВНЯ (комната/тайл/направление/поза входа), см.
|
||||
* pop_start_level. Управление: ←/→ бег, ↓ присед, ↑+→ прыжок с места,
|
||||
* Shift шаг, ESC — выход. Карта коллизии задана внутри enter_room. */
|
||||
pop_start_level(LEVEL_ENTRY_POSE);
|
||||
gfx_set_draw_page(0);
|
||||
|
||||
/* K2b: Kid стоит на уступе (row1), управляется с клавиатуры.
|
||||
* ←/→ бег, ↓ присед, ↑+→ прыжок с места, Shift шаг (K3). ESC — выход.
|
||||
* Карта коллизии (pop_map_set) уже задана в enter_room. */
|
||||
kid_init(SEQ_STAND, START_COL, START_ROW, 0);
|
||||
pop_kid_hp_reset();
|
||||
kid_draw();
|
||||
pop_fore_over_kid(kid_fp_obj_x(), kid_fp_obj_y(),
|
||||
kid_fp_width(), kid_fp_height(),
|
||||
@@ -428,12 +477,9 @@ int main(void)
|
||||
if (down) { /* L2: переход в комнату снизу — продолжает падать */
|
||||
enter_room(down); /* координаты Kid уже репроецированы pop_map */
|
||||
gfx_set_draw_page(back); /* вернуть текущую back для kid_draw */
|
||||
} else { /* выпал из уровня (нет комнаты снизу) — респавн */
|
||||
pop_trob_reset(); /* рестарт уровня: пики/ворота в исходное */
|
||||
enter_room(START_ROOM);
|
||||
} else { /* выпал из уровня (нет комнаты снизу) — рестарт */
|
||||
pop_start_level(LEVEL_ENTRY_POSE);
|
||||
gfx_set_draw_page(back);
|
||||
kid_init(SEQ_STAND, START_COL, START_ROW, 0);
|
||||
pop_kid_hp_reset();
|
||||
}
|
||||
} else if (pop_leave_dir) { /* L3: уход вбок/вверх (координаты уже репроецированы) */
|
||||
uint8_t nb = pop_room_link(cur_room, (uint8_t)(pop_leave_dir - 1));
|
||||
@@ -468,16 +514,24 @@ int main(void)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pop_next_level) { /* END_LEVEL: доиграл seq_70 в двери уровня */
|
||||
/* Порт play_level_2 (seg003:0386): цикл уровня кончается, когда
|
||||
* next_level разошёлся с current_level, и start_game грузит
|
||||
* следующий. Уровень у нас пока один, поэтому «следующий» = тот
|
||||
* же самый — зато вся цепочка (дверь → seq_70 → END_LEVEL →
|
||||
* конец уровня) работает целиком и проверяема. Загрузка уровня
|
||||
* 2 подключается ровно сюда, см. ../docs/levels_plan.md §2.2. */
|
||||
pop_next_level = 0;
|
||||
pop_start_level(LEVEL_ENTRY_POSE);
|
||||
gfx_set_draw_page(back);
|
||||
}
|
||||
if (pop_kid_dead) { /* напоролся на пики: труп лежит; респавн по ↑ */
|
||||
dead_frames++;
|
||||
if (dead_frames > RESPAWN_SETTLE &&
|
||||
(kbd_raw_down(KBD_UP) || dead_frames > RESPAWN_DELAY)) {
|
||||
dead_frames = 0;
|
||||
pop_trob_reset(); /* рестарт уровня: пики/ворота в исходное */
|
||||
enter_room(START_ROOM);
|
||||
pop_start_level(LEVEL_ENTRY_POSE);
|
||||
gfx_set_draw_page(back);
|
||||
kid_init(SEQ_STAND, START_COL, START_ROW, 0);
|
||||
pop_kid_hp_reset();
|
||||
}
|
||||
}
|
||||
if (pop_loose_fell) { /* упавший кусок → debris в комнате снизу */
|
||||
|
||||
Reference in New Issue
Block a user