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:
@@ -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